mirror of
https://github.com/simon-ding/polaris.git
synced 2026-06-09 11:39:46 +08:00
feat: download per media feature
This commit is contained in:
@@ -57,13 +57,13 @@ func ParseMovie(name string) *MovieMetadata {
|
|||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Pirated_movie_release_types
|
// https://en.wikipedia.org/wiki/Pirated_movie_release_types
|
||||||
func isQiangban(name string) bool {
|
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`)
|
re := regexp.MustCompile(`\W`)
|
||||||
name = re.ReplaceAllString(strings.ToLower(name), " ")
|
name = re.ReplaceAllString(strings.ToLower(name), " ")
|
||||||
fields := strings.Fields(name)
|
fields := strings.Fields(name)
|
||||||
for _, q := range qiangbanFilter {
|
for _, q := range qiangbanFilter {
|
||||||
for _, f := range fields {
|
for _, f := range fields {
|
||||||
if strings.ToLower(q) == strings.ToLower(f) {
|
if strings.EqualFold(q, f) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import (
|
|||||||
func (c *Client) addSysCron() {
|
func (c *Client) addSysCron() {
|
||||||
c.mustAddCron("@every 1m", c.checkTasks)
|
c.mustAddCron("@every 1m", c.checkTasks)
|
||||||
c.mustAddCron("0 0 * * * *", func() {
|
c.mustAddCron("0 0 * * * *", func() {
|
||||||
c.downloadTvSeries()
|
c.downloadAllTvSeries()
|
||||||
c.downloadMovie()
|
c.downloadAllMovies()
|
||||||
})
|
})
|
||||||
c.mustAddCron("0 0 */12 * * *", c.checkAllSeriesNewSeason)
|
c.mustAddCron("0 0 */12 * * *", c.checkAllSeriesNewSeason)
|
||||||
c.cron.Start()
|
c.cron.Start()
|
||||||
@@ -206,79 +206,97 @@ type Task struct {
|
|||||||
pkg.Torrent
|
pkg.Torrent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) downloadTvSeries() {
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ep.Status != episode.StatusMissing {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, err := c.SearchAndDownload(id, ep.SeasonNumber, ep.EpisodeNumber)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "find resource to download")
|
||||||
|
} else {
|
||||||
|
allNames = append(allNames, *name)
|
||||||
|
log.Infof("begin download torrent resource: %v", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return allNames, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) downloadAllTvSeries() {
|
||||||
log.Infof("begin check all tv series resources")
|
log.Infof("begin check all tv series resources")
|
||||||
allSeries := c.db.GetMediaWatchlist(media.MediaTypeTv)
|
allSeries := c.db.GetMediaWatchlist(media.MediaTypeTv)
|
||||||
for _, series := range allSeries {
|
for _, series := range allSeries {
|
||||||
tvDetail := c.db.GetMediaDetails(series.ID)
|
if _, err := c.DownloadSeriesAllEpisodes(series.ID); err != nil {
|
||||||
m := make(map[int][]*ent.Episode)
|
return
|
||||||
for _, ep := range tvDetail.Episodes {
|
|
||||||
m[ep.SeasonNumber] = append(m[ep.SeasonNumber], ep)
|
|
||||||
}
|
|
||||||
for seasonNum, epsides := range m {
|
|
||||||
wantedSeasonPack := true
|
|
||||||
for _, ep := range epsides {
|
|
||||||
if !ep.Monitored {
|
|
||||||
wantedSeasonPack = false
|
|
||||||
}
|
|
||||||
if ep.Status != episode.StatusMissing {
|
|
||||||
wantedSeasonPack = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if wantedSeasonPack {
|
|
||||||
name, err := c.SearchAndDownload(series.ID, seasonNum, -1)
|
|
||||||
if err != nil {
|
|
||||||
log.Infof("cannot find resource to download : %v", err)
|
|
||||||
} else {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) downloadMovie() {
|
func (c *Client) downloadAllMovies() {
|
||||||
log.Infof("begin check all movie resources")
|
log.Infof("begin check all movie resources")
|
||||||
allSeries := c.db.GetMediaWatchlist(media.MediaTypeMovie)
|
allSeries := c.db.GetMediaWatchlist(media.MediaTypeMovie)
|
||||||
|
|
||||||
for _, series := range allSeries {
|
for _, series := range allSeries {
|
||||||
detail := c.db.GetMediaDetails(series.ID)
|
if _, err := c.DownloadMovieByID(series.ID); err != nil {
|
||||||
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 {
|
|
||||||
log.Errorf("download movie error: %v", err)
|
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()
|
trc, dlc, err := c.getDownloadClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "connect transmission")
|
return "", errors.Wrap(err, "connect transmission")
|
||||||
}
|
}
|
||||||
qiangban := c.db.GetSetting(db.SettingAllowQiangban)
|
qiangban := c.db.GetSetting(db.SettingAllowQiangban)
|
||||||
allowQiangban := false
|
allowQiangban := false
|
||||||
@@ -294,13 +312,13 @@ func (c *Client) downloadMovieSingleEpisode(ep *ent.Episode, targetDir string) e
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
return errors.Wrap(err, "search movie")
|
return "", errors.Wrap(err, "search movie")
|
||||||
}
|
}
|
||||||
r1 := res[0]
|
r1 := res[0]
|
||||||
log.Infof("begin download torrent resource: %v", r1.Name)
|
log.Infof("begin download torrent resource: %v", r1.Name)
|
||||||
torrent, err := trc.Download(r1.Link, c.db.GetDownloadDir())
|
torrent, err := trc.Download(r1.Link, c.db.GetDownloadDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "downloading")
|
return "", errors.Wrap(err, "downloading")
|
||||||
}
|
}
|
||||||
torrent.Start()
|
torrent.Start()
|
||||||
|
|
||||||
@@ -322,7 +340,7 @@ func (c *Client) downloadMovieSingleEpisode(ep *ent.Episode, targetDir string) e
|
|||||||
c.tasks[history.ID] = &Task{Torrent: torrent}
|
c.tasks[history.ID] = &Task{Torrent: torrent}
|
||||||
|
|
||||||
c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
|
c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
|
||||||
return nil
|
return r1.Name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) checkAllSeriesNewSeason() {
|
func (c *Client) checkAllSeriesNewSeason() {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"polaris/log"
|
"polaris/log"
|
||||||
"polaris/pkg/torznab"
|
"polaris/pkg/torznab"
|
||||||
"polaris/server/core"
|
"polaris/server/core"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/pkg/errors"
|
"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
|
||||||
|
}
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ func (s *Server) Serve() error {
|
|||||||
tv.DELETE("/record/:id", HttpHandler(s.DeleteFromWatchlist))
|
tv.DELETE("/record/:id", HttpHandler(s.DeleteFromWatchlist))
|
||||||
tv.GET("/suggest/tv/:tmdb_id", HttpHandler(s.SuggestedSeriesFolderName))
|
tv.GET("/suggest/tv/:tmdb_id", HttpHandler(s.SuggestedSeriesFolderName))
|
||||||
tv.GET("/suggest/movie/:tmdb_id", HttpHandler(s.SuggestedMovieFolderName))
|
tv.GET("/suggest/movie/:tmdb_id", HttpHandler(s.SuggestedMovieFolderName))
|
||||||
|
tv.GET("/downloadall/:id", HttpHandler(s.DownloadAll))
|
||||||
}
|
}
|
||||||
indexer := api.Group("/indexer")
|
indexer := api.Group("/indexer")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class APIs {
|
|||||||
static final _baseUrl = baseUrl();
|
static final _baseUrl = baseUrl();
|
||||||
static final searchUrl = "$_baseUrl/api/v1/media/search";
|
static final searchUrl = "$_baseUrl/api/v1/media/search";
|
||||||
static final editMediaUrl = "$_baseUrl/api/v1/media/edit";
|
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 settingsUrl = "$_baseUrl/api/v1/setting/do";
|
||||||
static final settingsGeneralUrl = "$_baseUrl/api/v1/setting/general";
|
static final settingsGeneralUrl = "$_baseUrl/api/v1/setting/general";
|
||||||
static final watchlistTvUrl = "$_baseUrl/api/v1/media/tv/watchlist";
|
static final watchlistTvUrl = "$_baseUrl/api/v1/media/tv/watchlist";
|
||||||
|
|||||||
@@ -81,6 +81,16 @@ class SeriesDetailData
|
|||||||
}
|
}
|
||||||
ref.invalidateSelf();
|
ref.invalidateSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> 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 {
|
class SeriesDetails {
|
||||||
|
|||||||
@@ -303,9 +303,13 @@ class _DetailCardState extends ConsumerState<DetailCard> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget downloadButton() {
|
Widget downloadButton() {
|
||||||
return IconButton(
|
return LoadingIconButton(
|
||||||
tooltip: widget.details.mediaType == "tv" ? "查找并下载所有监控剧集" : "查找并下载此电影",
|
tooltip: widget.details.mediaType == "tv" ? "查找并下载所有监控剧集" : "查找并下载此电影",
|
||||||
onPressed: () {},
|
onPressed: () async{
|
||||||
icon: const Icon(Icons.download_rounded));
|
await ref
|
||||||
|
.read(mediaDetailsProvider(widget.details.id.toString()).notifier)
|
||||||
|
.downloadall();
|
||||||
|
},
|
||||||
|
icon: Icons.download_rounded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user