From 09ff67fef76c9a26bde3f31b5e0b52f56cef4963 Mon Sep 17 00:00:00 2001 From: Simon Ding Date: Mon, 12 Aug 2024 10:16:36 +0800 Subject: [PATCH] feat: download per media feature --- pkg/metadata/movie.go | 4 +- server/core/scheduler.go | 120 +++++++++++++++------------ server/resources.go | 19 +++++ server/server.go | 1 + ui/lib/providers/APIs.dart | 1 + ui/lib/providers/series_details.dart | 10 +++ ui/lib/widgets/detail_card.dart | 10 ++- 7 files changed, 109 insertions(+), 56 deletions(-) diff --git a/pkg/metadata/movie.go b/pkg/metadata/movie.go index 23acf4f..4fc57cc 100644 --- a/pkg/metadata/movie.go +++ b/pkg/metadata/movie.go @@ -57,13 +57,13 @@ func ParseMovie(name string) *MovieMetadata { // https://en.wikipedia.org/wiki/Pirated_movie_release_types func isQiangban(name string) bool { - qiangbanFilter := []string{"CAM-Rip", "CAM", "HDCAM", "TS", "HDTS", "TELESYNC", "PDVD", "PreDVDRip", "TC", "HDTC", "TELECINE", "WP", "WORKPRINT"} + qiangbanFilter := []string{"CAMRip","CAM-Rip", "CAM", "HDCAM", "TS","TSRip", "HDTS", "TELESYNC", "PDVD", "PreDVDRip", "TC", "HDTC", "TELECINE", "WP", "WORKPRINT"} re := regexp.MustCompile(`\W`) name = re.ReplaceAllString(strings.ToLower(name), " ") fields := strings.Fields(name) for _, q := range qiangbanFilter { for _, f := range fields { - if strings.ToLower(q) == strings.ToLower(f) { + if strings.EqualFold(q, f) { return true } } diff --git a/server/core/scheduler.go b/server/core/scheduler.go index 74f65c8..214ce36 100644 --- a/server/core/scheduler.go +++ b/server/core/scheduler.go @@ -19,8 +19,8 @@ import ( func (c *Client) addSysCron() { c.mustAddCron("@every 1m", c.checkTasks) c.mustAddCron("0 0 * * * *", func() { - c.downloadTvSeries() - c.downloadMovie() + c.downloadAllTvSeries() + c.downloadAllMovies() }) c.mustAddCron("0 0 */12 * * *", c.checkAllSeriesNewSeason) c.cron.Start() @@ -206,79 +206,97 @@ type Task struct { pkg.Torrent } -func (c *Client) downloadTvSeries() { - log.Infof("begin check all tv series resources") - allSeries := c.db.GetMediaWatchlist(media.MediaTypeTv) - for _, series := range allSeries { - tvDetail := c.db.GetMediaDetails(series.ID) - m := make(map[int][]*ent.Episode) - for _, ep := range tvDetail.Episodes { - m[ep.SeasonNumber] = append(m[ep.SeasonNumber], ep) +func (c *Client) DownloadSeriesAllEpisodes(id int) ([]string, error) { + tvDetail := c.db.GetMediaDetails(id) + m := make(map[int][]*ent.Episode) + for _, ep := range tvDetail.Episodes { + m[ep.SeasonNumber] = append(m[ep.SeasonNumber], ep) + } + var allNames []string + for seasonNum, epsides := range m { + wantedSeasonPack := true + for _, ep := range epsides { + if !ep.Monitored { + wantedSeasonPack = false + } + if ep.Status != episode.StatusMissing { + wantedSeasonPack = false + } } - for seasonNum, epsides := range m { - wantedSeasonPack := true + if wantedSeasonPack { + name, err := c.SearchAndDownload(id, seasonNum, -1) + if err != nil { + return nil, errors.Wrap(err, "find resource") + } else { + allNames = append(allNames, *name) + log.Infof("begin download torrent resource: %v", name) + } + + } else { for _, ep := range epsides { if !ep.Monitored { - wantedSeasonPack = false + continue } if ep.Status != episode.StatusMissing { - wantedSeasonPack = false + continue } - } - if wantedSeasonPack { - name, err := c.SearchAndDownload(series.ID, seasonNum, -1) + name, err := c.SearchAndDownload(id, ep.SeasonNumber, ep.EpisodeNumber) if err != nil { - log.Infof("cannot find resource to download : %v", err) + return nil, errors.Wrap(err, "find resource to download") } else { + allNames = append(allNames, *name) log.Infof("begin download torrent resource: %v", name) } + } - } else { - for _, ep := range epsides { - if !ep.Monitored { - continue - } - if ep.Status != episode.StatusMissing { - continue - } - name, err := c.SearchAndDownload(series.ID, ep.SeasonNumber, ep.EpisodeNumber) - if err != nil { - log.Infof("cannot find resource to download for %s: %v", ep.Title, err) - } else { - log.Infof("begin download torrent resource: %v", name) - } - } + } - } + } + return allNames, nil +} + +func (c *Client) downloadAllTvSeries() { + log.Infof("begin check all tv series resources") + allSeries := c.db.GetMediaWatchlist(media.MediaTypeTv) + for _, series := range allSeries { + if _, err := c.DownloadSeriesAllEpisodes(series.ID); err != nil { + return } } } -func (c *Client) downloadMovie() { +func (c *Client) downloadAllMovies() { log.Infof("begin check all movie resources") allSeries := c.db.GetMediaWatchlist(media.MediaTypeMovie) for _, series := range allSeries { - detail := c.db.GetMediaDetails(series.ID) - if len(detail.Episodes) == 0 { - log.Errorf("no related dummy episode: %v", detail.NameEn) - continue - } - ep := detail.Episodes[0] - if ep.Status != episode.StatusMissing { - continue - } - - if err := c.downloadMovieSingleEpisode(ep, series.TargetDir); err != nil { + if _, err := c.DownloadMovieByID(series.ID); err != nil { log.Errorf("download movie error: %v", err) } } } -func (c *Client) downloadMovieSingleEpisode(ep *ent.Episode, targetDir string) error { +func (c *Client) DownloadMovieByID(id int) (string, error) { + detail := c.db.GetMediaDetails(id) + if len(detail.Episodes) == 0 { + return "", fmt.Errorf("no related dummy episode: %v", detail.NameEn) + } + ep := detail.Episodes[0] + if ep.Status != episode.StatusMissing { + return "", nil + } + + if name, err := c.downloadMovieSingleEpisode(ep, detail.TargetDir); err != nil { + return "", errors.Wrap(err, "download movie") + } else { + return name, nil + } +} + +func (c *Client) downloadMovieSingleEpisode(ep *ent.Episode, targetDir string) (string, error) { trc, dlc, err := c.getDownloadClient() if err != nil { - return errors.Wrap(err, "connect transmission") + return "", errors.Wrap(err, "connect transmission") } qiangban := c.db.GetSetting(db.SettingAllowQiangban) allowQiangban := false @@ -294,13 +312,13 @@ func (c *Client) downloadMovieSingleEpisode(ep *ent.Episode, targetDir string) e }) if err != nil { - return errors.Wrap(err, "search movie") + return "", errors.Wrap(err, "search movie") } r1 := res[0] log.Infof("begin download torrent resource: %v", r1.Name) torrent, err := trc.Download(r1.Link, c.db.GetDownloadDir()) if err != nil { - return errors.Wrap(err, "downloading") + return "", errors.Wrap(err, "downloading") } torrent.Start() @@ -322,7 +340,7 @@ func (c *Client) downloadMovieSingleEpisode(ep *ent.Episode, targetDir string) e c.tasks[history.ID] = &Task{Torrent: torrent} c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading) - return nil + return r1.Name, nil } func (c *Client) checkAllSeriesNewSeason() { diff --git a/server/resources.go b/server/resources.go index 8900fb9..c33dbc3 100644 --- a/server/resources.go +++ b/server/resources.go @@ -7,6 +7,7 @@ import ( "polaris/log" "polaris/pkg/torznab" "polaris/server/core" + "strconv" "github.com/gin-gonic/gin" "github.com/pkg/errors" @@ -167,3 +168,21 @@ func (s *Server) DownloadTorrent(c *gin.Context) (interface{}, error) { } } + +func (s *Server) DownloadAll(c *gin.Context) (interface{}, error) { + ids := c.Param("id") + id, err := strconv.Atoi(ids) + if err != nil { + return nil, errors.Wrap(err, "convert") + } + m, err := s.db.GetMedia(id) + if err != nil { + return nil, errors.Wrap(err, "get media") + } + if m.MediaType == media.MediaTypeTv { + return s.core.DownloadSeriesAllEpisodes(m.ID) + } + name, err := s.core.DownloadMovieByID(m.ID) + + return []string{name}, err +} diff --git a/server/server.go b/server/server.go index 8fe2492..c950be8 100644 --- a/server/server.go +++ b/server/server.go @@ -91,6 +91,7 @@ func (s *Server) Serve() error { tv.DELETE("/record/:id", HttpHandler(s.DeleteFromWatchlist)) tv.GET("/suggest/tv/:tmdb_id", HttpHandler(s.SuggestedSeriesFolderName)) tv.GET("/suggest/movie/:tmdb_id", HttpHandler(s.SuggestedMovieFolderName)) + tv.GET("/downloadall/:id", HttpHandler(s.DownloadAll)) } indexer := api.Group("/indexer") { diff --git a/ui/lib/providers/APIs.dart b/ui/lib/providers/APIs.dart index f8f66fd..9d94917 100644 --- a/ui/lib/providers/APIs.dart +++ b/ui/lib/providers/APIs.dart @@ -8,6 +8,7 @@ class APIs { static final _baseUrl = baseUrl(); static final searchUrl = "$_baseUrl/api/v1/media/search"; static final editMediaUrl = "$_baseUrl/api/v1/media/edit"; + static final downloadAllUrl = "$_baseUrl/api/v1/media/downloadall/"; static final settingsUrl = "$_baseUrl/api/v1/setting/do"; static final settingsGeneralUrl = "$_baseUrl/api/v1/setting/general"; static final watchlistTvUrl = "$_baseUrl/api/v1/media/tv/watchlist"; diff --git a/ui/lib/providers/series_details.dart b/ui/lib/providers/series_details.dart index 75742c2..f4d740c 100644 --- a/ui/lib/providers/series_details.dart +++ b/ui/lib/providers/series_details.dart @@ -81,6 +81,16 @@ class SeriesDetailData } ref.invalidateSelf(); } + + Future downloadall() async { + final dio = APIs.getDio(); + var resp = await dio.get(APIs.downloadAllUrl + id!); + var sp = ServerResponse.fromJson(resp.data); + if (sp.code != 0) { + throw sp.message; + } + ref.invalidateSelf(); + } } class SeriesDetails { diff --git a/ui/lib/widgets/detail_card.dart b/ui/lib/widgets/detail_card.dart index e61889e..b717bb4 100644 --- a/ui/lib/widgets/detail_card.dart +++ b/ui/lib/widgets/detail_card.dart @@ -303,9 +303,13 @@ class _DetailCardState extends ConsumerState { } Widget downloadButton() { - return IconButton( + return LoadingIconButton( tooltip: widget.details.mediaType == "tv" ? "查找并下载所有监控剧集" : "查找并下载此电影", - onPressed: () {}, - icon: const Icon(Icons.download_rounded)); + onPressed: () async{ + await ref + .read(mediaDetailsProvider(widget.details.id.toString()).notifier) + .downloadall(); + }, + icon: Icons.download_rounded); } }