func (d *Downloader) downloadMissingMovies(wl *polochon.Wishlist, log *logrus.Entry) { log = log.WithField("function", "download_movies") for _, wantedMovie := range wl.Movies { ok, err := d.library.HasMovie(wantedMovie.ImdbID) if err != nil { log.Error(err) continue } if ok { log.Debugf("movie %q already in the video store", wantedMovie.ImdbID) continue } m := polochon.NewMovie(d.config.Movie) m.ImdbID = wantedMovie.ImdbID log = log.WithField("imdb_id", m.ImdbID) if err := m.GetDetails(log); err != nil { errors.LogErrors(log, err) if errors.IsFatal(err) { continue } } log = log.WithField("title", m.Title) if err := m.GetTorrents(log); err != nil && err != polochon.ErrMovieTorrentNotFound { errors.LogErrors(log, err) if errors.IsFatal(err) { continue } } // Keep the torrent URL var torrentURL string quality_loop: for _, q := range wantedMovie.Qualities { for _, t := range m.Torrents { if t.Quality == q { torrentURL = t.URL break quality_loop } } } if torrentURL == "" { log.Debug("no torrent found") continue } if err := d.config.Downloader.Client.Download(torrentURL, log); err != nil { log.Error(err) continue } } }
// Start statrs a sub app in its own goroutine func (a *App) subAppStart(app subapp.App, log *logrus.Entry) { log.Debugf("starting sub app %q", app.Name()) a.wg.Add(1) go func() { defer a.wg.Done() if err := app.Run(log); err != nil { // Check the error type, if it comes from a panic recovery // reload the app switch e := err.(type) { case *errors.Error: errors.LogErrors(log.WithField("app", app.Name()), e) // Notify the safeguard of the error a.safeguard.Event() // Write to the reload channel in a goroutine to prevent deadlocks go func() { a.reload <- app }() // Only log the error default: log.Error(err) go a.Stop(log) } } }() log.Debugf("sub app %q started", app.Name()) }
func (d *Downloader) downloadMissingShows(wl *polochon.Wishlist, log *logrus.Entry) { log = log.WithField("function", "download_shows") for _, wishedShow := range wl.Shows { s := polochon.NewShow(d.config.Show) s.ImdbID = wishedShow.ImdbID log = log.WithField("imdb_id", s.ImdbID) if err := s.GetDetails(log); err != nil { errors.LogErrors(log, err) if errors.IsFatal(err) { continue } } calendar, err := s.GetCalendar(log) if err != nil { errors.LogErrors(log, err) if errors.IsFatal(err) { continue } } for _, calEpisode := range calendar.Episodes { // Check if the episode should be downloaded if calEpisode.IsOlder(wishedShow) { continue } // Check if the episode has already been downloaded ok, err := d.library.HasShowEpisode(wishedShow.ImdbID, calEpisode.Season, calEpisode.Episode) if err != nil { log.Error(err) continue } if ok { continue } // Setup the episode e := polochon.NewShowEpisode(d.config.Show) e.ShowImdbID = wishedShow.ImdbID e.ShowTitle = s.Title e.Season = calEpisode.Season e.Episode = calEpisode.Episode log = log.WithFields(logrus.Fields{ "show_imdb_id": e.ShowImdbID, "show_title": e.ShowTitle, "season": e.Season, "episode": e.Episode, }) if err := e.GetTorrents(log); err != nil && err != polochon.ErrShowEpisodeTorrentNotFound { errors.LogErrors(log, err) if errors.IsFatal(err) { continue } } // Keep the torrent URL var torrentURL string quality_loop: for _, q := range wishedShow.Qualities { for _, t := range e.Torrents { if t.Quality == q { torrentURL = t.URL break quality_loop } } } if torrentURL == "" { log.Debug("no torrent found") continue } if err := d.config.Downloader.Client.Download(torrentURL, log); err != nil { log.Error(err) continue } } } }
// OrganizeFile stores the videos in the video library func (o *Organizer) organizeFile(filePath string, log *logrus.Entry) error { log = log.WithField("file_path", filePath) log.Debug("organize file") // Create a file file := polochon.NewFileWithConfig(filePath, o.config.File) // Check if file really exists if !file.Exists() { log.Warning("the file has been removed") return nil } // Check if file is a video if !file.IsVideo() { log.Debug("the file is not a video") return nil } // Check if file is ignored if file.IsIgnored() { log.Debug("the file is ignored") return nil } // Check if file is symlink if file.IsSymlink() { log.Debug("the file is a symlink") return nil } // Check if file is ignored if file.IsExcluded() { log.Debug("the file is excluded") return file.Ignore() } // Guess the video inforamtion video, err := file.Guess(o.config.Movie, o.config.Show, log) if err != nil { errors.LogErrors(log, err) return file.Ignore() } // Get video details if err := video.GetDetails(log); err != nil { errors.LogErrors(log, err) if errors.IsFatal(err) { return file.Ignore() } } // Store the video if err := o.library.Add(video, log); err != nil { errors.LogErrors(log, err) return file.Ignore() } // Get subtitle if err := video.GetSubtitle(log); err != nil { errors.LogErrors(log, err) } // Notify o.Notify(video, log) return nil }
func (l *Library) addShow(ep *polochon.ShowEpisode, log *logrus.Entry) error { dir := l.getShowDir(ep) nfoPath := l.showNFOPath(dir) if exists(nfoPath) { return nil } s := ep.Show if s == nil { s = polochon.NewShowFromEpisode(ep) if err := s.GetDetails(log); err != nil { errors.LogErrors(log, err) if errors.IsFatal(err) { return err } } } // Create show dir if necessary if !exists(dir) { if err := os.Mkdir(dir, os.ModePerm); err != nil { return err } } // Write NFO into the file if err := writeNFOFile(nfoPath, s); err != nil { return err } // Download show images if s.Fanart == "" || s.Banner == "" || s.Poster == "" { return ErrMissingShowImageURL } // Download images for _, img := range []struct { url string name string }{ { url: s.Fanart, name: "fanart.jpg", }, { url: s.Poster, name: "poster.jpg", }, { url: s.Banner, name: "banner.jpg", }, } { savePath := filepath.Join(dir, img.name) if err := download(img.url, savePath); err != nil { return err } } return nil }
// Run launches the app func (a *App) Run() { // Hangle os signals osSig := make(chan os.Signal, 1) signal.Notify(osSig, syscall.SIGINT, syscall.SIGKILL, syscall.SIGHUP) log := logrus.NewEntry(a.logger) log.Info("starting the app") // Panic loop safeguard go func() { if err := a.safeguard.Run(log); err != nil { errors.LogErrors(log, err) go a.Stop(log) } }() // Start all the apps a.startSubApps(log) // Handle graceful shutdown var forceShutdown bool // Main loop for { select { case <-a.done: log.Info("all done, exiting") return case subApp := <-a.reload: log.Infof("reloading sub app %q", subApp.Name()) a.wg.Add(1) go func() { defer a.wg.Done() subApp.BlockingStop(log) a.subAppStart(subApp, log) }() case sig := <-osSig: log.WithField("os_event", sig).Info("got an os event") switch sig { case syscall.SIGINT, syscall.SIGKILL: if forceShutdown { log.Warn("forced shutdown") os.Exit(1) } log.Info("graceful shutdown") // stop the app go a.Stop(log) // Next time it won't be so gentle forceShutdown = true case syscall.SIGHUP: log.Info("reloading app") a.stopApps(log) if err := a.init(); err != nil { log.Fatal(err) } a.startSubApps(log) log.Info("app reloaded") } } } }