Files
polaris/server/core/torrent.go
2024-08-03 12:31:53 +08:00

230 lines
5.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package core
import (
"fmt"
"polaris/db"
"polaris/log"
"polaris/pkg/metadata"
"polaris/pkg/torznab"
"polaris/pkg/utils"
"slices"
"sort"
"strconv"
"strings"
"sync"
"github.com/pkg/errors"
)
func SearchTvSeries(db1 *db.Client, seriesId, seasonNum int, episodes []int, checkResolution bool, checkFileSize bool) ([]torznab.Result, error) {
series := db1.GetMediaDetails(seriesId)
if series == nil {
return nil, fmt.Errorf("no tv series of id %v", seriesId)
}
slices.Contains(episodes, 1)
res := searchWithTorznab(db1, series.NameEn)
resCn := searchWithTorznab(db1, series.NameCn)
res = append(res, resCn...)
var filtered []torznab.Result
for _, r := range res {
//log.Infof("torrent resource: %+v", r)
meta := metadata.ParseTv(r.Name)
if meta == nil { //cannot parse name
continue
}
if !isNumberedSeries(series) { //do not check season on series that only rely on episode number
if meta.Season != seasonNum {
continue
}
}
if isNumberedSeries(series) && len(episodes) == 0 {
//should not want season
continue
}
if len(episodes) > 0 && slices.Contains(episodes, meta.Episode) { //not season pack, episode number equals
continue
}else if len(episodes) == 0 && !meta.IsSeasonPack { //want season pack, but not season pack
continue
}
if checkResolution && meta.Resolution != series.Resolution.String() {
continue
}
if !utils.IsNameAcceptable(meta.NameEn, series.NameEn) && !utils.IsNameAcceptable(meta.NameCn, series.NameCn) {
continue
}
if checkFileSize && series.Limiter != nil {
if series.Limiter.SizeMin > 0 && r.Size < series.Limiter.SizeMin {
//min size not satified
continue
}
if series.Limiter.SizeMax > 0 && r.Size > series.Limiter.SizeMax {
//max size not satified
continue
}
}
filtered = append(filtered, r)
}
if len(filtered) == 0 {
return nil, errors.New("no resource found")
}
filtered = dedup(filtered)
return filtered, nil
}
func isNumberedSeries(detail *db.MediaDetails) bool {
hasSeason2 := false
season2HasEpisode1 := false
for _, ep := range detail.Episodes {
if ep.SeasonNumber == 2 {
hasSeason2 = true
if ep.EpisodeNumber == 1 {
season2HasEpisode1 = true
}
}
}
return hasSeason2 && !season2HasEpisode1 //only one 1st episode
}
func SearchMovie(db1 *db.Client, movieId int, checkResolution bool, checkFileSize bool) ([]torznab.Result, error) {
movieDetail := db1.GetMediaDetails(movieId)
if movieDetail == nil {
return nil, errors.New("no media found of id")
}
res := searchWithTorznab(db1, movieDetail.NameEn)
res1 := searchWithTorznab(db1, movieDetail.NameCn)
res = append(res, res1...)
if len(res) == 0 {
return nil, fmt.Errorf("no resource found")
}
var filtered []torznab.Result
for _, r := range res {
meta := metadata.ParseMovie(r.Name)
if !utils.IsNameAcceptable(meta.NameEn, movieDetail.NameEn) {
continue
}
if checkResolution && meta.Resolution != movieDetail.Resolution.String() {
continue
}
if checkFileSize && movieDetail.Limiter != nil {
if movieDetail.Limiter.SizeMin > 0 && r.Size < movieDetail.Limiter.SizeMin {
//min size not satified
continue
}
if movieDetail.Limiter.SizeMax > 0 && r.Size > movieDetail.Limiter.SizeMax {
//max size not satified
continue
}
}
ss := strings.Split(movieDetail.AirDate, "-")[0]
year, _ := strconv.Atoi(ss)
if meta.Year != year && meta.Year != year-1 && meta.Year != year+1 { //year not match
continue
}
filtered = append(filtered, r)
}
if len(filtered) == 0 {
return nil, errors.New("no resource found")
}
filtered = dedup(filtered)
return filtered, nil
}
func searchWithTorznab(db *db.Client, q string) []torznab.Result {
var res []torznab.Result
allTorznab := db.GetAllTorznabInfo()
resChan := make(chan []torznab.Result)
var wg sync.WaitGroup
for _, tor := range allTorznab {
if tor.Disabled {
continue
}
wg.Add(1)
go func() {
log.Debugf("search torznab %v with %v", tor.Name, q)
defer wg.Done()
resp, err := torznab.Search(tor, tor.ApiKey, q)
if err != nil {
log.Errorf("search %s error: %v", tor.Name, err)
return
}
resChan <- resp
}()
}
go func() {
wg.Wait()
close(resChan) // 在所有的worker完成后关闭Channel
}()
for result := range resChan {
res = append(res, result...)
}
//res = dedup(res)
sort.SliceStable(res, func(i, j int) bool { //先按做种人数排序
var s1 = res[i]
var s2 = res[j]
return s1.Seeders > s2.Seeders
})
sort.SliceStable(res, func(i, j int) bool { //再按优先级排序,优先级高的种子排前面
var s1 = res[i]
var s2 = res[j]
return s1.Priority > s2.Priority
})
//pt资源中同一indexer内部优先下载free的资源
sort.SliceStable(res, func(i, j int) bool {
var s1 = res[i]
var s2 = res[j]
if s1.IndexerId == s2.IndexerId && s1.IsPrivate {
return s1.DownloadVolumeFactor < s2.DownloadVolumeFactor
}
return false
})
//同一indexer内部如果下载消耗一样则优先下载上传奖励较多的
sort.SliceStable(res, func(i, j int) bool {
var s1 = res[i]
var s2 = res[j]
if s1.IndexerId == s2.IndexerId && s1.IsPrivate && s1.DownloadVolumeFactor == s2.DownloadVolumeFactor{
return s1.UploadVolumeFactor > s2.UploadVolumeFactor
}
return false
})
return res
}
func dedup(list []torznab.Result) []torznab.Result {
var res = make([]torznab.Result, 0, len(list))
seen := make(map[string]bool, 0)
for _, r := range list {
key := fmt.Sprintf("%s%s%d%d", r.Name, r.Source, r.Seeders,r.Peers)
if seen[key] {
continue
}
seen[key] = true
res = append(res, r)
}
return res
}