mirror of
https://github.com/simon-ding/polaris.git
synced 2026-02-06 15:10:49 +08:00
263 lines
6.9 KiB
Go
263 lines
6.9 KiB
Go
package engine
|
||
|
||
import (
|
||
"bytes"
|
||
"fmt"
|
||
"polaris/ent"
|
||
"polaris/ent/episode"
|
||
"polaris/ent/history"
|
||
"polaris/ent/media"
|
||
"polaris/log"
|
||
"polaris/pkg/metadata"
|
||
"polaris/pkg/notifier/message"
|
||
"polaris/pkg/torznab"
|
||
"polaris/pkg/utils"
|
||
|
||
"github.com/pkg/errors"
|
||
)
|
||
|
||
func (c *Engine) DownloadEpisodeTorrent(r1 torznab.Result, op DownloadOptions) (*string, error) {
|
||
|
||
series, err := c.db.GetMedia(op.MediaId)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("no tv series of id %v", op.MediaId)
|
||
}
|
||
|
||
return c.downloadTorrent(series, r1, op)
|
||
}
|
||
|
||
/*
|
||
tmdb 校验获取的资源名,如果用资源名在tmdb搜索出来的结果能匹配上想要的资源,则认为资源有效,否则无效
|
||
解决名称过于简单的影视会匹配过多资源的问题, 例如:梦魇绝镇 FROM
|
||
*/
|
||
func (c *Engine) checkBtReourceWithTmdb(r *torznab.Result, seriesId int) bool {
|
||
m := metadata.ParseTv(r.Name)
|
||
se, err := c.MustTMDB().SearchMedia(m.NameEn, "", 1)
|
||
if err != nil {
|
||
log.Warnf("tmdb search error, consider this torrent ok: %v", err)
|
||
return true
|
||
} else {
|
||
if len(se.Results) == 0 {
|
||
log.Debugf("tmdb search no result, consider this torrent ok: %s", r.Name) //because tv name parse is not accurate
|
||
return true
|
||
}
|
||
series, err := c.db.GetMediaDetails(seriesId)
|
||
if err != nil {
|
||
log.Warnf("get media details error: %v", err)
|
||
return false
|
||
}
|
||
|
||
se0 := se.Results[0]
|
||
if se0.ID != int64(series.TmdbID) {
|
||
log.Warnf("bt reosurce name not match tmdb id: %s", r.Name)
|
||
return false
|
||
} else { //resource tmdb id match
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
|
||
func (c *Engine) SearchAndDownload(seriesId, seasonNum int, episodeNums ...int) ([]string, error) {
|
||
|
||
res, err := SearchTvSeries(c.db, &SearchParam{
|
||
MediaId: seriesId,
|
||
SeasonNum: seasonNum,
|
||
Episodes: episodeNums,
|
||
CheckFileSize: true,
|
||
CheckResolution: true,
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
wanted := make(map[int]bool, len(episodeNums))
|
||
for _, ep := range episodeNums {
|
||
wanted[ep] = true
|
||
}
|
||
var torrentNames []string
|
||
lo:
|
||
for _, r := range res {
|
||
if !c.checkBtReourceWithTmdb(&r, seriesId) {
|
||
continue
|
||
}
|
||
m := metadata.ParseTv(r.Name)
|
||
m.ParseExtraDescription(r.Description)
|
||
if len(episodeNums) == 0 { //want season pack
|
||
if m.IsSeasonPack {
|
||
name, err := c.DownloadEpisodeTorrent(r, DownloadOptions{
|
||
SeasonNum: seasonNum,
|
||
MediaId: seriesId,
|
||
HashFilterFn: c.hashInBlacklist,
|
||
})
|
||
if err != nil {
|
||
log.Warnf("download season pack error, continue next item: %v", err)
|
||
continue lo
|
||
}
|
||
torrentNames = append(torrentNames, *name)
|
||
break lo
|
||
}
|
||
} else {
|
||
torrentEpisodes := make([]int, 0)
|
||
for i := m.StartEpisode; i <= m.EndEpisode; i++ {
|
||
if !wanted[i] { //torrent has episode not wanted
|
||
continue lo
|
||
}
|
||
torrentEpisodes = append(torrentEpisodes, i)
|
||
}
|
||
name, err := c.DownloadEpisodeTorrent(r, DownloadOptions{
|
||
SeasonNum: seasonNum,
|
||
MediaId: seriesId,
|
||
EpisodeNums: torrentEpisodes,
|
||
HashFilterFn: c.hashInBlacklist,
|
||
})
|
||
if err != nil {
|
||
log.Warnf("download episode error, continue next item: %v", err)
|
||
continue lo
|
||
}
|
||
torrentNames = append(torrentNames, *name)
|
||
|
||
for _, num := range torrentEpisodes {
|
||
delete(wanted, num) //delete downloaded episode from wanted
|
||
}
|
||
}
|
||
}
|
||
if len(wanted) > 0 {
|
||
log.Warnf("still wanted but not downloaded episodes: %v", wanted)
|
||
}
|
||
return torrentNames, nil
|
||
}
|
||
|
||
func (c *Engine) DownloadMovie(m *ent.Media, r1 torznab.Result) (*string, error) {
|
||
return c.downloadTorrent(m, r1, DownloadOptions{
|
||
SeasonNum: 0,
|
||
MediaId: m.ID,
|
||
})
|
||
}
|
||
|
||
func (c *Engine) hashInBlacklist(hash string) bool {
|
||
blacklist, err := c.db.GetTorrentBlacklist()
|
||
if err != nil {
|
||
log.Warnf("get torrent blacklist error: %v", err)
|
||
return false
|
||
}
|
||
for _, b := range blacklist {
|
||
if b.TorrentHash == hash {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func (c *Engine) downloadTorrent(m *ent.Media, r1 torznab.Result, op DownloadOptions) (s *string, err1 error) {
|
||
trc, dlc, err := c.GetDownloadClient()
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "get download client")
|
||
}
|
||
|
||
downloadDir := c.db.GetDownloadDir()
|
||
|
||
//due to reported bug by user, this will be temporarily disabled
|
||
// size := utils.AvailableSpace(downloadDir)
|
||
// if size < uint64(r1.Size) {
|
||
// log.Errorf("space available %v, space needed %v", size, r1.Size)
|
||
// return nil, errors.New("not enough space")
|
||
// }
|
||
|
||
var name = r1.Name
|
||
var targetDir = m.TargetDir
|
||
if m.MediaType == media.MediaTypeTv { //tv download
|
||
targetDir = fmt.Sprintf("%s/Season %02d/", m.TargetDir, op.SeasonNum)
|
||
|
||
if len(op.EpisodeNums) > 0 {
|
||
for _, epNum := range op.EpisodeNums {
|
||
ep, err := c.db.GetEpisode(m.ID, op.SeasonNum, epNum)
|
||
if err != nil {
|
||
return nil, errors.Errorf("no episode of season %d episode %d", op.SeasonNum, epNum)
|
||
|
||
}
|
||
if ep.Status == episode.StatusMissing {
|
||
c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
|
||
defer func(episodeId int) {
|
||
if err1 != nil {
|
||
c.db.SetEpisodeStatus(episodeId, episode.StatusMissing)
|
||
}
|
||
}(ep.ID)
|
||
}
|
||
}
|
||
buff := &bytes.Buffer{}
|
||
for i, ep := range op.EpisodeNums {
|
||
if i != 0 {
|
||
buff.WriteString(",")
|
||
|
||
}
|
||
buff.WriteString(fmt.Sprint(ep))
|
||
}
|
||
name = fmt.Sprintf("第%s集 (%s)", buff.String(), name)
|
||
|
||
} else { //season package download
|
||
name = fmt.Sprintf("全集 (%s)", name)
|
||
c.db.SetSeasonAllEpisodeStatus(m.ID, op.SeasonNum, episode.StatusDownloading)
|
||
defer func(mediaId int, seasonNum int) {
|
||
if err1 != nil {
|
||
c.db.SetSeasonAllEpisodeStatus(mediaId, seasonNum, episode.StatusMissing)
|
||
}
|
||
|
||
}(m.ID, op.SeasonNum)
|
||
}
|
||
|
||
} else { //movie download
|
||
ep, _ := c.db.GetMovieDummyEpisode(m.ID)
|
||
if ep.Status == episode.StatusMissing {
|
||
c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
|
||
defer func(episodeId int) {
|
||
if err1 != nil {
|
||
c.db.SetEpisodeStatus(episodeId, episode.StatusMissing)
|
||
}
|
||
}(ep.ID)
|
||
}
|
||
|
||
}
|
||
|
||
link, hash, err := utils.GetRealLinkAndHash(r1.Link)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "get hash")
|
||
}
|
||
|
||
if op.HashFilterFn != nil && op.HashFilterFn(hash) {
|
||
return nil, errors.Errorf("hash is filtered: %s", hash)
|
||
}
|
||
|
||
r1.Link = link
|
||
|
||
history, err := c.db.SaveHistoryRecord(ent.History{
|
||
MediaID: m.ID,
|
||
EpisodeNums: op.EpisodeNums,
|
||
SeasonNum: op.SeasonNum,
|
||
SourceTitle: r1.Name,
|
||
TargetDir: targetDir,
|
||
Status: history.StatusRunning,
|
||
Size: int(r1.Size),
|
||
//Saved: torrent.Save(),
|
||
Link: r1.Link,
|
||
Hash: hash,
|
||
DownloadClientID: dlc.ID,
|
||
IndexerID: r1.IndexerId,
|
||
})
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "save record")
|
||
}
|
||
|
||
torrent, err := trc.Download(r1.Link, hash, downloadDir)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "downloading")
|
||
}
|
||
torrent.Start()
|
||
|
||
c.tasks.Store(history.ID, &Task{Torrent: torrent})
|
||
|
||
c.sendMsg(fmt.Sprintf(message.BeginDownload, name))
|
||
|
||
log.Infof("success add %s to download task", r1.Name)
|
||
|
||
return &r1.Name, nil
|
||
}
|