mirror of
https://github.com/simon-ding/polaris.git
synced 2026-06-09 11:39:46 +08:00
code refactor
This commit is contained in:
@@ -21,6 +21,7 @@ func (Episode) Fields() []ent.Field {
|
|||||||
field.String("overview"),
|
field.String("overview"),
|
||||||
field.String("air_date"),
|
field.String("air_date"),
|
||||||
field.Enum("status").Values("missing", "downloading", "downloaded").Default("missing"),
|
field.Enum("status").Values("missing", "downloading", "downloaded").Default("missing"),
|
||||||
|
field.Bool("monitored").Default(true).StructTag("json:\"monitored\""), //whether this episode is monitored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func (s *Server) GetAllActivities(c *gin.Context) (interface{}, error) {
|
|||||||
a := Activity{
|
a := Activity{
|
||||||
History: h,
|
History: h,
|
||||||
}
|
}
|
||||||
for id, task := range s.tasks {
|
for id, task := range s.core.GetTasks() {
|
||||||
if h.ID == id && task.Exists() {
|
if h.ID == id && task.Exists() {
|
||||||
a.Progress = task.Progress()
|
a.Progress = task.Progress()
|
||||||
}
|
}
|
||||||
@@ -54,13 +54,11 @@ func (s *Server) RemoveActivity(c *gin.Context) (interface{}, error) {
|
|||||||
log.Errorf("no record of id: %d", id)
|
log.Errorf("no record of id: %d", id)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
torrent := s.tasks[his.ID]
|
|
||||||
if torrent != nil {
|
if err := s.core.RemoveTaskAndTorrent(his.ID); err != nil {
|
||||||
if err := torrent.Remove(); err != nil {
|
return nil, errors.Wrap(err, "remove torrent")
|
||||||
return nil, errors.Wrap(err, "remove torrent")
|
|
||||||
}
|
|
||||||
delete(s.tasks, his.ID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if his.EpisodeID != 0 {
|
if his.EpisodeID != 0 {
|
||||||
s.db.SetEpisodeStatus(his.EpisodeID, episode.StatusMissing)
|
s.db.SetEpisodeStatus(his.EpisodeID, episode.StatusMissing)
|
||||||
|
|
||||||
@@ -96,7 +94,7 @@ func (s *Server) GetMediaDownloadHistory(c *gin.Context) (interface{}, error) {
|
|||||||
|
|
||||||
type TorrentInfo struct {
|
type TorrentInfo struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
SeedRatio float32 `json:"seed_ratio"`
|
SeedRatio float32 `json:"seed_ratio"`
|
||||||
Progress int `json:"progress"`
|
Progress int `json:"progress"`
|
||||||
}
|
}
|
||||||
@@ -116,8 +114,8 @@ func (s *Server) GetAllTorrents(c *gin.Context) (interface{}, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
infos = append(infos, TorrentInfo{
|
infos = append(infos, TorrentInfo{
|
||||||
Name: t.Name(),
|
Name: t.Name(),
|
||||||
ID: t.ID,
|
ID: t.ID,
|
||||||
Progress: t.Progress(),
|
Progress: t.Progress(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
94
server/core/client.go
Normal file
94
server/core/client.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"polaris/db"
|
||||||
|
"polaris/ent"
|
||||||
|
"polaris/log"
|
||||||
|
"polaris/pkg/tmdb"
|
||||||
|
"polaris/pkg/transmission"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/robfig/cron"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewClient(db *db.Client, language string) *Client {
|
||||||
|
return &Client{
|
||||||
|
db: db,
|
||||||
|
cron: cron.New(),
|
||||||
|
tasks: make(map[int]*Task, 0),
|
||||||
|
language: language,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
db *db.Client
|
||||||
|
cron *cron.Cron
|
||||||
|
tasks map[int]*Task
|
||||||
|
language string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Init() {
|
||||||
|
c.reloadTasks()
|
||||||
|
c.addSysCron()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) reloadTasks() {
|
||||||
|
allTasks := c.db.GetHistories()
|
||||||
|
for _, t := range allTasks {
|
||||||
|
torrent, err := transmission.ReloadTorrent(t.Saved)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("relaod task %s failed: %v", t.SourceTitle, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !torrent.Exists() { //只要种子还存在于客户端中,就重新加载,有可能是还在做种中
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Infof("reloading task: %d %s", t.ID, t.SourceTitle)
|
||||||
|
c.tasks[t.ID] = &Task{Torrent: torrent}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getDownloadClient() (*transmission.Client, *ent.DownloadClients, error) {
|
||||||
|
tr := c.db.GetTransmission()
|
||||||
|
trc, err := transmission.NewClient(transmission.Config{
|
||||||
|
URL: tr.URL,
|
||||||
|
User: tr.User,
|
||||||
|
Password: tr.Password,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrap(err, "connect transmission")
|
||||||
|
}
|
||||||
|
return trc, tr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) TMDB() (*tmdb.Client, error) {
|
||||||
|
api := c.db.GetSetting(db.SettingTmdbApiKey)
|
||||||
|
if api == "" {
|
||||||
|
return nil, errors.New("TMDB apiKey not set")
|
||||||
|
}
|
||||||
|
return tmdb.NewClient(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) MustTMDB() *tmdb.Client {
|
||||||
|
t, err := c.TMDB()
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("get tmdb: %v", err)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (c *Client) RemoveTaskAndTorrent(id int)error {
|
||||||
|
torrent := c.tasks[id]
|
||||||
|
if torrent != nil {
|
||||||
|
if err := torrent.Remove(); err != nil {
|
||||||
|
return errors.Wrap(err, "remove torrent")
|
||||||
|
}
|
||||||
|
delete(c.tasks, id)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetTasks() map[int]*Task {
|
||||||
|
return c.tasks
|
||||||
|
}
|
||||||
121
server/core/integration.go
Normal file
121
server/core/integration.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"polaris/db"
|
||||||
|
"polaris/ent/media"
|
||||||
|
storage1 "polaris/ent/storage"
|
||||||
|
"polaris/log"
|
||||||
|
"polaris/pkg/notifier"
|
||||||
|
"polaris/pkg/storage"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) writePlexmatch(seriesId int, episodeId int, targetDir, name string) error {
|
||||||
|
|
||||||
|
if !c.plexmatchEnabled() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
series, err := c.db.GetMedia(seriesId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if series.MediaType != media.MediaTypeTv {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
st, err := c.getStorage(series.StorageID, media.MediaTypeTv)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "get storage")
|
||||||
|
}
|
||||||
|
|
||||||
|
//series plexmatch file
|
||||||
|
_, err = st.ReadFile(filepath.Join(series.TargetDir, ".plexmatch"))
|
||||||
|
if err != nil {
|
||||||
|
//create new
|
||||||
|
log.Warnf(".plexmatch file not found, create new one: %s", series.NameEn)
|
||||||
|
if err := st.WriteFile(filepath.Join(series.TargetDir, ".plexmatch"),
|
||||||
|
[]byte(fmt.Sprintf("tmdbid: %d\n", series.TmdbID))); err != nil {
|
||||||
|
return errors.Wrap(err, "series plexmatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//season plexmatch file
|
||||||
|
ep, err := c.db.GetEpisodeByID(episodeId)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "query episode")
|
||||||
|
}
|
||||||
|
buff := bytes.Buffer{}
|
||||||
|
seasonPlex := filepath.Join(targetDir, ".plexmatch")
|
||||||
|
data, err := st.ReadFile(seasonPlex)
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("read season plexmatch: %v", err)
|
||||||
|
} else {
|
||||||
|
buff.Write(data)
|
||||||
|
}
|
||||||
|
buff.WriteString(fmt.Sprintf("\nep: %d: %s\n", ep.EpisodeNumber, name))
|
||||||
|
log.Infof("write season plexmatch file content: %s", buff.String())
|
||||||
|
return st.WriteFile(seasonPlex, buff.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) plexmatchEnabled() bool {
|
||||||
|
return c.db.GetSetting(db.SettingPlexMatchEnabled) == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getStorage(storageId int, mediaType media.MediaType) (storage.Storage, error) {
|
||||||
|
st := c.db.GetStorage(storageId)
|
||||||
|
targetPath := st.TvPath
|
||||||
|
if mediaType == media.MediaTypeMovie {
|
||||||
|
targetPath = st.MoviePath
|
||||||
|
}
|
||||||
|
|
||||||
|
switch st.Implementation {
|
||||||
|
case storage1.ImplementationLocal:
|
||||||
|
|
||||||
|
storageImpl1, err := storage.NewLocalStorage(targetPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "new local")
|
||||||
|
}
|
||||||
|
return storageImpl1, nil
|
||||||
|
|
||||||
|
case storage1.ImplementationWebdav:
|
||||||
|
ws := st.ToWebDavSetting()
|
||||||
|
storageImpl1, err := storage.NewWebdavStorage(ws.URL, ws.User, ws.Password, targetPath, ws.ChangeFileHash == "true")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "new webdav")
|
||||||
|
}
|
||||||
|
return storageImpl1, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("no storage found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendMsg(msg string) {
|
||||||
|
clients, err := c.db.GetAllNotificationClients2()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("query notification clients: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, cl := range clients {
|
||||||
|
if !cl.Enabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
handler, ok := notifier.Gethandler(cl.Service)
|
||||||
|
if !ok {
|
||||||
|
log.Errorf("no notification implementation of service %s", cl.Service)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
noCl, err := handler(cl.Settings)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("handle setting for name %s error: %v", cl.Name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = noCl.SendMsg(msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("send message error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Debugf("send message to %s success, msg is %s", cl.Name, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
165
server/core/resources.go
Normal file
165
server/core/resources.go
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"polaris/ent"
|
||||||
|
"polaris/ent/episode"
|
||||||
|
"polaris/ent/history"
|
||||||
|
"polaris/log"
|
||||||
|
"polaris/pkg/notifier/message"
|
||||||
|
"polaris/pkg/torznab"
|
||||||
|
"polaris/pkg/utils"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) DownloadSeasonPackage(r1 torznab.Result, seriesId, seasonNum int) (*string, error) {
|
||||||
|
trc, dlClient, err := c.getDownloadClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "connect transmission")
|
||||||
|
}
|
||||||
|
downloadDir := c.db.GetDownloadDir()
|
||||||
|
size := utils.AvailableSpace(downloadDir)
|
||||||
|
if size < uint64(r1.Size) {
|
||||||
|
log.Errorf("space available %v, space needed %v", size, r1.Size)
|
||||||
|
return nil, errors.New("no enough space")
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent, err := trc.Download(r1.Link, c.db.GetDownloadDir())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "downloading")
|
||||||
|
}
|
||||||
|
torrent.Start()
|
||||||
|
|
||||||
|
series := c.db.GetMediaDetails(seriesId)
|
||||||
|
if series == nil {
|
||||||
|
return nil, fmt.Errorf("no tv series of id %v", seriesId)
|
||||||
|
}
|
||||||
|
dir := fmt.Sprintf("%s/Season %02d/", series.TargetDir, seasonNum)
|
||||||
|
|
||||||
|
history, err := c.db.SaveHistoryRecord(ent.History{
|
||||||
|
MediaID: seriesId,
|
||||||
|
EpisodeID: 0,
|
||||||
|
SourceTitle: r1.Name,
|
||||||
|
TargetDir: dir,
|
||||||
|
Status: history.StatusRunning,
|
||||||
|
Size: r1.Size,
|
||||||
|
Saved: torrent.Save(),
|
||||||
|
DownloadClientID: dlClient.ID,
|
||||||
|
IndexerID: r1.IndexerId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "save record")
|
||||||
|
}
|
||||||
|
c.db.SetSeasonAllEpisodeStatus(seriesId, seasonNum, episode.StatusDownloading)
|
||||||
|
|
||||||
|
c.tasks[history.ID] = &Task{Torrent: torrent}
|
||||||
|
|
||||||
|
c.sendMsg(fmt.Sprintf(message.BeginDownload, r1.Name))
|
||||||
|
return &r1.Name, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (c *Client) DownloadEpisodeTorrent(r1 torznab.Result, seriesId, seasonNum, episodeNum int) (*string, error) {
|
||||||
|
trc, dlc, err := c.getDownloadClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "connect transmission")
|
||||||
|
}
|
||||||
|
series := c.db.GetMediaDetails(seriesId)
|
||||||
|
if series == nil {
|
||||||
|
return nil, fmt.Errorf("no tv series of id %v", seriesId)
|
||||||
|
}
|
||||||
|
var ep *ent.Episode
|
||||||
|
for _, e := range series.Episodes {
|
||||||
|
if e.SeasonNumber == seasonNum && e.EpisodeNumber == episodeNum {
|
||||||
|
ep = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ep == nil {
|
||||||
|
return nil, errors.Errorf("no episode of season %d episode %d", seasonNum, episodeNum)
|
||||||
|
}
|
||||||
|
torrent, err := trc.Download(r1.Link, c.db.GetDownloadDir())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "downloading")
|
||||||
|
}
|
||||||
|
torrent.Start()
|
||||||
|
|
||||||
|
dir := fmt.Sprintf("%s/Season %02d/", series.TargetDir, seasonNum)
|
||||||
|
|
||||||
|
history, err := c.db.SaveHistoryRecord(ent.History{
|
||||||
|
MediaID: ep.MediaID,
|
||||||
|
EpisodeID: ep.ID,
|
||||||
|
SourceTitle: r1.Name,
|
||||||
|
TargetDir: dir,
|
||||||
|
Status: history.StatusRunning,
|
||||||
|
Size: r1.Size,
|
||||||
|
Saved: torrent.Save(),
|
||||||
|
DownloadClientID: dlc.ID,
|
||||||
|
IndexerID: r1.IndexerId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "save record")
|
||||||
|
}
|
||||||
|
c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
|
||||||
|
|
||||||
|
c.tasks[history.ID] = &Task{Torrent: torrent}
|
||||||
|
c.sendMsg(fmt.Sprintf(message.BeginDownload, r1.Name))
|
||||||
|
|
||||||
|
log.Infof("success add %s to download task", r1.Name)
|
||||||
|
return &r1.Name, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
func (c *Client) SearchAndDownload(seriesId, seasonNum, episodeNum int) (*string, error) {
|
||||||
|
|
||||||
|
res, err := SearchEpisode(c.db, seriesId, seasonNum, episodeNum, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r1 := res[0]
|
||||||
|
log.Infof("found resource to download: %+v", r1)
|
||||||
|
return c.DownloadEpisodeTorrent(r1, seriesId, seasonNum, episodeNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) DownloadMovie(m *ent.Media,link, name string, size int, indexerID int) (*string, error) {
|
||||||
|
trc, dlc, err := c.getDownloadClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "connect transmission")
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent, err := trc.Download(link, c.db.GetDownloadDir())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "downloading")
|
||||||
|
}
|
||||||
|
torrent.Start()
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
name = m.OriginalName
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
ep, _ := c.db.GetMovieDummyEpisode(m.ID)
|
||||||
|
history, err := c.db.SaveHistoryRecord(ent.History{
|
||||||
|
MediaID: m.ID,
|
||||||
|
EpisodeID: ep.ID,
|
||||||
|
SourceTitle: name,
|
||||||
|
TargetDir: m.TargetDir,
|
||||||
|
Status: history.StatusRunning,
|
||||||
|
Size: size,
|
||||||
|
Saved: torrent.Save(),
|
||||||
|
DownloadClientID: dlc.ID,
|
||||||
|
IndexerID: indexerID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("save history error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.tasks[history.ID] = &Task{Torrent: torrent}
|
||||||
|
|
||||||
|
c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.sendMsg(fmt.Sprintf(message.BeginDownload, name))
|
||||||
|
log.Infof("success add %s to download task", name)
|
||||||
|
return &name, nil
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package server
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -11,44 +11,43 @@ import (
|
|||||||
"polaris/pkg"
|
"polaris/pkg"
|
||||||
"polaris/pkg/notifier/message"
|
"polaris/pkg/notifier/message"
|
||||||
"polaris/pkg/utils"
|
"polaris/pkg/utils"
|
||||||
"polaris/server/core"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) scheduler() {
|
func (c *Client) addSysCron() {
|
||||||
s.mustAddCron("@every 1m", s.checkTasks)
|
c.mustAddCron("@every 1m", c.checkTasks)
|
||||||
s.mustAddCron("0 0 * * * *", func() {
|
c.mustAddCron("0 0 * * * *", func() {
|
||||||
s.downloadTvSeries()
|
c.downloadTvSeries()
|
||||||
s.downloadMovie()
|
c.downloadMovie()
|
||||||
})
|
})
|
||||||
s.mustAddCron("0 0 */12 * * *", s.checkAllSeriesNewSeason)
|
c.mustAddCron("0 0 */12 * * *", c.checkAllSeriesNewSeason)
|
||||||
s.cron.Start()
|
c.cron.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) mustAddCron(spec string, cmd func()) {
|
func (c *Client) mustAddCron(spec string, cmd func()) {
|
||||||
if err := s.cron.AddFunc(spec, cmd); err != nil {
|
if err := c.cron.AddFunc(spec, cmd); err != nil {
|
||||||
log.Errorf("add func error: %v", err)
|
log.Errorf("add func error: %v", err)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) checkTasks() {
|
func (c *Client) checkTasks() {
|
||||||
log.Debug("begin check tasks...")
|
log.Debug("begin check tasks...")
|
||||||
for id, t := range s.tasks {
|
for id, t := range c.tasks {
|
||||||
if !t.Exists() {
|
if !t.Exists() {
|
||||||
log.Infof("task no longer exists: %v", id)
|
log.Infof("task no longer exists: %v", id)
|
||||||
delete(s.tasks, id)
|
delete(c.tasks, id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Infof("task (%s) percentage done: %d%%", t.Name(), t.Progress())
|
log.Infof("task (%s) percentage done: %d%%", t.Name(), t.Progress())
|
||||||
if t.Progress() == 100 {
|
if t.Progress() == 100 {
|
||||||
r := s.db.GetHistory(id)
|
r := c.db.GetHistory(id)
|
||||||
if r.Status == history.StatusSuccess {
|
if r.Status == history.StatusSuccess {
|
||||||
//task already success, check seed ratio
|
//task already success, check seed ratio
|
||||||
torrent := s.tasks[id]
|
torrent := c.tasks[id]
|
||||||
ok, err := s.isSeedRatioLimitReached(r.IndexerID, torrent)
|
ok, err := c.isSeedRatioLimitReached(r.IndexerID, torrent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("getting torrent seed ratio : %v", err)
|
log.Warnf("getting torrent seed ratio : %v", err)
|
||||||
ok = false
|
ok = false
|
||||||
@@ -56,16 +55,16 @@ func (s *Server) checkTasks() {
|
|||||||
if ok {
|
if ok {
|
||||||
log.Infof("torrent file seed ratio reached, remove: %v", torrent.Name())
|
log.Infof("torrent file seed ratio reached, remove: %v", torrent.Name())
|
||||||
torrent.Remove()
|
torrent.Remove()
|
||||||
delete(s.tasks, id)
|
delete(c.tasks, id)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("torrent file still sedding: %v", torrent.Name())
|
log.Infof("torrent file still sedding: %v", torrent.Name())
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Infof("task is done: %v", t.Name())
|
log.Infof("task is done: %v", t.Name())
|
||||||
s.sendMsg(fmt.Sprintf(message.DownloadComplete, t.Name()))
|
c.sendMsg(fmt.Sprintf(message.DownloadComplete, t.Name()))
|
||||||
go func() {
|
go func() {
|
||||||
if err := s.moveCompletedTask(id); err != nil {
|
if err := c.moveCompletedTask(id); err != nil {
|
||||||
log.Infof("post tasks for id %v fail: %v", id, err)
|
log.Infof("post tasks for id %v fail: %v", id, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -73,20 +72,20 @@ func (s *Server) checkTasks() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) moveCompletedTask(id int) (err1 error) {
|
func (c *Client) moveCompletedTask(id int) (err1 error) {
|
||||||
torrent := s.tasks[id]
|
torrent := c.tasks[id]
|
||||||
r := s.db.GetHistory(id)
|
r := c.db.GetHistory(id)
|
||||||
if r.Status == history.StatusUploading {
|
if r.Status == history.StatusUploading {
|
||||||
log.Infof("task %d is already uploading, skip", id)
|
log.Infof("task %d is already uploading, skip", id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
s.db.SetHistoryStatus(r.ID, history.StatusUploading)
|
c.db.SetHistoryStatus(r.ID, history.StatusUploading)
|
||||||
seasonNum, err := utils.SeasonId(r.TargetDir)
|
seasonNum, err := utils.SeasonId(r.TargetDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("no season id: %v", r.TargetDir)
|
log.Errorf("no season id: %v", r.TargetDir)
|
||||||
seasonNum = -1
|
seasonNum = -1
|
||||||
}
|
}
|
||||||
downloadclient, err := s.db.GetDownloadClient(r.DownloadClientID)
|
downloadclient, err := c.db.GetDownloadClient(r.DownloadClientID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("get task download client error: %v, use default one", err)
|
log.Errorf("get task download client error: %v, use default one", err)
|
||||||
downloadclient = &ent.DownloadClients{RemoveCompletedDownloads: true, RemoveFailedDownloads: true}
|
downloadclient = &ent.DownloadClients{RemoveCompletedDownloads: true, RemoveFailedDownloads: true}
|
||||||
@@ -96,59 +95,59 @@ func (s *Server) moveCompletedTask(id int) (err1 error) {
|
|||||||
defer func() {
|
defer func() {
|
||||||
|
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
s.db.SetHistoryStatus(r.ID, history.StatusFail)
|
c.db.SetHistoryStatus(r.ID, history.StatusFail)
|
||||||
if r.EpisodeID != 0 {
|
if r.EpisodeID != 0 {
|
||||||
s.db.SetEpisodeStatus(r.EpisodeID, episode.StatusMissing)
|
c.db.SetEpisodeStatus(r.EpisodeID, episode.StatusMissing)
|
||||||
} else {
|
} else {
|
||||||
s.db.SetSeasonAllEpisodeStatus(r.MediaID, seasonNum, episode.StatusMissing)
|
c.db.SetSeasonAllEpisodeStatus(r.MediaID, seasonNum, episode.StatusMissing)
|
||||||
}
|
}
|
||||||
s.sendMsg(fmt.Sprintf(message.ProcessingFailed, err))
|
c.sendMsg(fmt.Sprintf(message.ProcessingFailed, err))
|
||||||
if downloadclient.RemoveFailedDownloads {
|
if downloadclient.RemoveFailedDownloads {
|
||||||
log.Debugf("task failed, remove failed torrent and files related")
|
log.Debugf("task failed, remove failed torrent and files related")
|
||||||
delete(s.tasks, r.ID)
|
delete(c.tasks, r.ID)
|
||||||
torrent.Remove()
|
torrent.Remove()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
series := s.db.GetMediaDetails(r.MediaID)
|
series := c.db.GetMediaDetails(r.MediaID)
|
||||||
if series == nil {
|
if series == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
st := s.db.GetStorage(series.StorageID)
|
st := c.db.GetStorage(series.StorageID)
|
||||||
log.Infof("move task files to target dir: %v", r.TargetDir)
|
log.Infof("move task files to target dir: %v", r.TargetDir)
|
||||||
stImpl, err := s.getStorage(st.ID, series.MediaType)
|
stImpl, err := c.getStorage(st.ID, series.MediaType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//如果种子是路径,则会把路径展开,只移动文件,类似 move dir/* dir2/, 如果种子是文件,则会直接移动文件,类似 move file dir/
|
//如果种子是路径,则会把路径展开,只移动文件,类似 move dir/* dir2/, 如果种子是文件,则会直接移动文件,类似 move file dir/
|
||||||
if err := stImpl.Copy(filepath.Join(s.db.GetDownloadDir(), torrentName), r.TargetDir); err != nil {
|
if err := stImpl.Copy(filepath.Join(c.db.GetDownloadDir(), torrentName), r.TargetDir); err != nil {
|
||||||
return errors.Wrap(err, "move file")
|
return errors.Wrap(err, "move file")
|
||||||
}
|
}
|
||||||
|
|
||||||
// .plexmatch file
|
// .plexmatch file
|
||||||
if err := s.writePlexmatch(r.MediaID, r.EpisodeID, r.TargetDir, torrentName); err != nil {
|
if err := c.writePlexmatch(r.MediaID, r.EpisodeID, r.TargetDir, torrentName); err != nil {
|
||||||
log.Errorf("create .plexmatch file error: %v", err)
|
log.Errorf("create .plexmatch file error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.db.SetHistoryStatus(r.ID, history.StatusSuccess)
|
c.db.SetHistoryStatus(r.ID, history.StatusSuccess)
|
||||||
if r.EpisodeID != 0 {
|
if r.EpisodeID != 0 {
|
||||||
s.db.SetEpisodeStatus(r.EpisodeID, episode.StatusDownloaded)
|
c.db.SetEpisodeStatus(r.EpisodeID, episode.StatusDownloaded)
|
||||||
} else {
|
} else {
|
||||||
s.db.SetSeasonAllEpisodeStatus(r.MediaID, seasonNum, episode.StatusDownloaded)
|
c.db.SetSeasonAllEpisodeStatus(r.MediaID, seasonNum, episode.StatusDownloaded)
|
||||||
}
|
}
|
||||||
s.sendMsg(fmt.Sprintf(message.ProcessingComplete, torrentName))
|
c.sendMsg(fmt.Sprintf(message.ProcessingComplete, torrentName))
|
||||||
|
|
||||||
//判断是否需要删除本地文件
|
//判断是否需要删除本地文件
|
||||||
ok, err := s.isSeedRatioLimitReached(r.IndexerID, torrent)
|
ok, err := c.isSeedRatioLimitReached(r.IndexerID, torrent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("getting torrent seed ratio %s: %v", torrent.Name(), err)
|
log.Warnf("getting torrent seed ratio %s: %v", torrent.Name(), err)
|
||||||
ok = false
|
ok = false
|
||||||
}
|
}
|
||||||
if downloadclient.RemoveCompletedDownloads && ok {
|
if downloadclient.RemoveCompletedDownloads && ok {
|
||||||
log.Debugf("download complete,remove torrent and files related")
|
log.Debugf("download complete,remove torrent and files related")
|
||||||
delete(s.tasks, r.ID)
|
delete(c.tasks, r.ID)
|
||||||
torrent.Remove()
|
torrent.Remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,13 +155,13 @@ func (s *Server) moveCompletedTask(id int) (err1 error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) checkDownloadedSeriesFiles(m *ent.Media) error {
|
func (c *Client) CheckDownloadedSeriesFiles(m *ent.Media) error {
|
||||||
if m.MediaType != media.MediaTypeTv {
|
if m.MediaType != media.MediaTypeTv {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Infof("check files in directory: %s", m.TargetDir)
|
log.Infof("check files in directory: %s", m.TargetDir)
|
||||||
|
|
||||||
var storageImpl, err = s.getStorage(m.StorageID, media.MediaTypeTv)
|
var storageImpl, err = c.getStorage(m.StorageID, media.MediaTypeTv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -190,12 +189,12 @@ func (s *Server) checkDownloadedSeriesFiles(m *ent.Media) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Infof("found match, season num %d, episode num %d", seNum, epNum)
|
log.Infof("found match, season num %d, episode num %d", seNum, epNum)
|
||||||
ep, err := s.db.GetEpisode(m.ID, seNum, epNum)
|
ep, err := c.db.GetEpisode(m.ID, seNum, epNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("update episode: %v", err)
|
log.Error("update episode: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = s.db.SetEpisodeStatus(ep.ID, episode.StatusDownloaded)
|
err = c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloaded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("update episode: %v", err)
|
log.Error("update episode: %v", err)
|
||||||
continue
|
continue
|
||||||
@@ -212,11 +211,11 @@ type Task struct {
|
|||||||
pkg.Torrent
|
pkg.Torrent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) downloadTvSeries() {
|
func (c *Client) downloadTvSeries() {
|
||||||
log.Infof("begin check all tv series resources")
|
log.Infof("begin check all tv series resources")
|
||||||
allSeries := s.db.GetMediaWatchlist(media.MediaTypeTv)
|
allSeries := c.db.GetMediaWatchlist(media.MediaTypeTv)
|
||||||
for _, series := range allSeries {
|
for _, series := range allSeries {
|
||||||
tvDetail := s.db.GetMediaDetails(series.ID)
|
tvDetail := c.db.GetMediaDetails(series.ID)
|
||||||
for _, ep := range tvDetail.Episodes {
|
for _, ep := range tvDetail.Episodes {
|
||||||
if !series.DownloadHistoryEpisodes { //设置不下载历史已播出剧集,只下载将来剧集
|
if !series.DownloadHistoryEpisodes { //设置不下载历史已播出剧集,只下载将来剧集
|
||||||
t, err := time.Parse("2006-01-02", ep.AirDate)
|
t, err := time.Parse("2006-01-02", ep.AirDate)
|
||||||
@@ -232,7 +231,7 @@ func (s *Server) downloadTvSeries() {
|
|||||||
if ep.Status != episode.StatusMissing { //已经下载的不去下载
|
if ep.Status != episode.StatusMissing { //已经下载的不去下载
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name, err := s.searchAndDownload(series.ID, ep.SeasonNumber, ep.EpisodeNumber)
|
name, err := c.SearchAndDownload(series.ID, ep.SeasonNumber, ep.EpisodeNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("cannot find resource to download for %s: %v", ep.Title, err)
|
log.Infof("cannot find resource to download for %s: %v", ep.Title, err)
|
||||||
} else {
|
} else {
|
||||||
@@ -244,12 +243,12 @@ func (s *Server) downloadTvSeries() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) downloadMovie() {
|
func (c *Client) downloadMovie() {
|
||||||
log.Infof("begin check all movie resources")
|
log.Infof("begin check all movie resources")
|
||||||
allSeries := s.db.GetMediaWatchlist(media.MediaTypeMovie)
|
allSeries := c.db.GetMediaWatchlist(media.MediaTypeMovie)
|
||||||
|
|
||||||
for _, series := range allSeries {
|
for _, series := range allSeries {
|
||||||
detail := s.db.GetMediaDetails(series.ID)
|
detail := c.db.GetMediaDetails(series.ID)
|
||||||
if len(detail.Episodes) == 0 {
|
if len(detail.Episodes) == 0 {
|
||||||
log.Errorf("no related dummy episode: %v", detail.NameEn)
|
log.Errorf("no related dummy episode: %v", detail.NameEn)
|
||||||
continue
|
continue
|
||||||
@@ -259,32 +258,32 @@ func (s *Server) downloadMovie() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.downloadMovieSingleEpisode(ep); err != nil {
|
if err := c.downloadMovieSingleEpisode(ep); err != nil {
|
||||||
log.Errorf("download movie error: %v", err)
|
log.Errorf("download movie error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) downloadMovieSingleEpisode(ep *ent.Episode) error {
|
func (c *Client) downloadMovieSingleEpisode(ep *ent.Episode) error {
|
||||||
trc, dlc, err := s.getDownloadClient()
|
trc, dlc, err := c.getDownloadClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "connect transmission")
|
return errors.Wrap(err, "connect transmission")
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := core.SearchMovie(s.db, ep.MediaID, true)
|
res, err := SearchMovie(c.db, ep.MediaID, true)
|
||||||
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, s.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()
|
||||||
|
|
||||||
history, err := s.db.SaveHistoryRecord(ent.History{
|
history, err := c.db.SaveHistoryRecord(ent.History{
|
||||||
MediaID: ep.MediaID,
|
MediaID: ep.MediaID,
|
||||||
EpisodeID: ep.ID,
|
EpisodeID: ep.ID,
|
||||||
SourceTitle: r1.Name,
|
SourceTitle: r1.Name,
|
||||||
@@ -298,36 +297,36 @@ func (s *Server) downloadMovieSingleEpisode(ep *ent.Episode) error {
|
|||||||
log.Errorf("save history error: %v", err)
|
log.Errorf("save history error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.tasks[history.ID] = &Task{Torrent: torrent}
|
c.tasks[history.ID] = &Task{Torrent: torrent}
|
||||||
|
|
||||||
s.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
|
c.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) checkAllSeriesNewSeason() {
|
func (c *Client) checkAllSeriesNewSeason() {
|
||||||
log.Infof("begin checking series all new season")
|
log.Infof("begin checking series all new season")
|
||||||
allSeries := s.db.GetMediaWatchlist(media.MediaTypeTv)
|
allSeries := c.db.GetMediaWatchlist(media.MediaTypeTv)
|
||||||
for _, series := range allSeries {
|
for _, series := range allSeries {
|
||||||
err := s.checkSeiesNewSeason(series)
|
err := c.checkSeiesNewSeason(series)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("check series new season error: series name %v, error: %v", series.NameEn, err)
|
log.Errorf("check series new season error: series name %v, error: %v", series.NameEn, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) checkSeiesNewSeason(media *ent.Media) error {
|
func (c *Client) checkSeiesNewSeason(media *ent.Media) error {
|
||||||
d, err := s.MustTMDB().GetTvDetails(media.TmdbID, s.language)
|
d, err := c.MustTMDB().GetTvDetails(media.TmdbID, c.language)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "tmdb")
|
return errors.Wrap(err, "tmdb")
|
||||||
}
|
}
|
||||||
lastsSason := d.NumberOfSeasons
|
lastsSason := d.NumberOfSeasons
|
||||||
seasonDetail, err := s.MustTMDB().GetSeasonDetails(media.TmdbID, lastsSason, s.language)
|
seasonDetail, err := c.MustTMDB().GetSeasonDetails(media.TmdbID, lastsSason, c.language)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "tmdb season")
|
return errors.Wrap(err, "tmdb season")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ep := range seasonDetail.Episodes {
|
for _, ep := range seasonDetail.Episodes {
|
||||||
epDb, err := s.db.GetEpisode(media.ID, ep.SeasonNumber, ep.EpisodeNumber)
|
epDb, err := c.db.GetEpisode(media.ID, ep.SeasonNumber, ep.EpisodeNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ent.IsNotFound(err) {
|
if ent.IsNotFound(err) {
|
||||||
log.Infof("add new episode: %+v", ep)
|
log.Infof("add new episode: %+v", ep)
|
||||||
@@ -340,20 +339,20 @@ func (s *Server) checkSeiesNewSeason(media *ent.Media) error {
|
|||||||
AirDate: ep.AirDate,
|
AirDate: ep.AirDate,
|
||||||
Status: episode.StatusMissing,
|
Status: episode.StatusMissing,
|
||||||
}
|
}
|
||||||
s.db.SaveEposideDetail2(episode)
|
c.db.SaveEposideDetail2(episode)
|
||||||
}
|
}
|
||||||
} else { //update episode
|
} else { //update episode
|
||||||
if ep.Name != epDb.Title || ep.Overview != epDb.Overview || ep.AirDate != epDb.AirDate {
|
if ep.Name != epDb.Title || ep.Overview != epDb.Overview || ep.AirDate != epDb.AirDate {
|
||||||
log.Infof("update new episode: %+v", ep)
|
log.Infof("update new episode: %+v", ep)
|
||||||
s.db.UpdateEpiode2(epDb.ID, ep.Name, ep.Overview, ep.AirDate)
|
c.db.UpdateEpiode2(epDb.ID, ep.Name, ep.Overview, ep.AirDate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) isSeedRatioLimitReached(indexId int, t pkg.Torrent) (bool, error) {
|
func (c *Client) isSeedRatioLimitReached(indexId int, t pkg.Torrent) (bool, error) {
|
||||||
indexer, err := s.db.GetIndexer(indexId)
|
indexer, err := c.db.GetIndexer(indexId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
|
||||||
"polaris/db"
|
|
||||||
"polaris/ent/media"
|
|
||||||
"polaris/log"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Server) writePlexmatch(seriesId int, episodeId int, targetDir, name string) error {
|
|
||||||
|
|
||||||
if !s.plexmatchEnabled() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
series, err := s.db.GetMedia(seriesId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if series.MediaType != media.MediaTypeTv {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
st, err := s.getStorage(series.StorageID, media.MediaTypeTv)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "get storage")
|
|
||||||
}
|
|
||||||
|
|
||||||
//series plexmatch file
|
|
||||||
_, err = st.ReadFile(filepath.Join(series.TargetDir, ".plexmatch"))
|
|
||||||
if err != nil {
|
|
||||||
//create new
|
|
||||||
log.Warnf(".plexmatch file not found, create new one: %s", series.NameEn)
|
|
||||||
if err := st.WriteFile(filepath.Join(series.TargetDir, ".plexmatch"),
|
|
||||||
[]byte(fmt.Sprintf("tmdbid: %d\n",series.TmdbID))); err != nil {
|
|
||||||
return errors.Wrap(err, "series plexmatch")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//season plexmatch file
|
|
||||||
ep, err := s.db.GetEpisodeByID(episodeId)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "query episode")
|
|
||||||
}
|
|
||||||
buff := bytes.Buffer{}
|
|
||||||
seasonPlex := filepath.Join(targetDir, ".plexmatch")
|
|
||||||
data, err := st.ReadFile(seasonPlex)
|
|
||||||
if err != nil {
|
|
||||||
log.Infof("read season plexmatch: %v", err)
|
|
||||||
} else {
|
|
||||||
buff.Write(data)
|
|
||||||
}
|
|
||||||
buff.WriteString(fmt.Sprintf("\nep: %d: %s\n", ep.EpisodeNumber, name))
|
|
||||||
log.Infof("write season plexmatch file content: %s", buff.String())
|
|
||||||
return st.WriteFile(seasonPlex, buff.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) plexmatchEnabled() bool {
|
|
||||||
return s.db.GetSetting(db.SettingPlexMatchEnabled) == "true"
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,6 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"polaris/ent"
|
"polaris/ent"
|
||||||
"polaris/log"
|
|
||||||
"polaris/pkg/notifier"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -44,32 +42,3 @@ func (s *Server) AddNotificationClient(c *gin.Context) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) sendMsg(msg string) {
|
|
||||||
clients, err := s.db.GetAllNotificationClients2()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("query notification clients: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, cl := range clients {
|
|
||||||
if !cl.Enabled {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
handler, ok := notifier.Gethandler(cl.Service)
|
|
||||||
if !ok {
|
|
||||||
log.Errorf("no notification implementation of service %s", cl.Service)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
noCl, err := handler(cl.Settings)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("handle setting for name %s error: %v", cl.Name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = noCl.SendMsg(msg)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("send message error: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Debugf("send message to %s success, msg is %s", cl.Name, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,14 +2,9 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"polaris/ent"
|
|
||||||
"polaris/ent/episode"
|
|
||||||
"polaris/ent/history"
|
|
||||||
"polaris/ent/media"
|
"polaris/ent/media"
|
||||||
"polaris/log"
|
"polaris/log"
|
||||||
"polaris/pkg/notifier/message"
|
|
||||||
"polaris/pkg/torznab"
|
"polaris/pkg/torznab"
|
||||||
"polaris/pkg/utils"
|
|
||||||
"polaris/server/core"
|
"polaris/server/core"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -25,117 +20,10 @@ func (s *Server) searchAndDownloadSeasonPackage(seriesId, seasonNum int) (*strin
|
|||||||
|
|
||||||
r1 := res[0]
|
r1 := res[0]
|
||||||
log.Infof("found resource to download: %+v", r1)
|
log.Infof("found resource to download: %+v", r1)
|
||||||
return s.downloadSeasonPackage(r1, seriesId, seasonNum)
|
return s.core.DownloadSeasonPackage(r1, seriesId, seasonNum)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) downloadSeasonPackage(r1 torznab.Result, seriesId, seasonNum int) (*string, error) {
|
|
||||||
trc, dlClient, err := s.getDownloadClient()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "connect transmission")
|
|
||||||
}
|
|
||||||
downloadDir := s.db.GetDownloadDir()
|
|
||||||
size := utils.AvailableSpace(downloadDir)
|
|
||||||
if size < uint64(r1.Size) {
|
|
||||||
log.Errorf("space available %v, space needed %v", size, r1.Size)
|
|
||||||
return nil, errors.New("no enough space")
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent, err := trc.Download(r1.Link, s.db.GetDownloadDir())
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "downloading")
|
|
||||||
}
|
|
||||||
torrent.Start()
|
|
||||||
|
|
||||||
series := s.db.GetMediaDetails(seriesId)
|
|
||||||
if series == nil {
|
|
||||||
return nil, fmt.Errorf("no tv series of id %v", seriesId)
|
|
||||||
}
|
|
||||||
dir := fmt.Sprintf("%s/Season %02d/", series.TargetDir, seasonNum)
|
|
||||||
|
|
||||||
history, err := s.db.SaveHistoryRecord(ent.History{
|
|
||||||
MediaID: seriesId,
|
|
||||||
EpisodeID: 0,
|
|
||||||
SourceTitle: r1.Name,
|
|
||||||
TargetDir: dir,
|
|
||||||
Status: history.StatusRunning,
|
|
||||||
Size: r1.Size,
|
|
||||||
Saved: torrent.Save(),
|
|
||||||
DownloadClientID: dlClient.ID,
|
|
||||||
IndexerID: r1.IndexerId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "save record")
|
|
||||||
}
|
|
||||||
s.db.SetSeasonAllEpisodeStatus(seriesId, seasonNum, episode.StatusDownloading)
|
|
||||||
|
|
||||||
s.tasks[history.ID] = &Task{Torrent: torrent}
|
|
||||||
|
|
||||||
s.sendMsg(fmt.Sprintf(message.BeginDownload, r1.Name))
|
|
||||||
return &r1.Name, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) downloadEpisodeTorrent(r1 torznab.Result, seriesId, seasonNum, episodeNum int) (*string, error) {
|
|
||||||
trc, dlc, err := s.getDownloadClient()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "connect transmission")
|
|
||||||
}
|
|
||||||
series := s.db.GetMediaDetails(seriesId)
|
|
||||||
if series == nil {
|
|
||||||
return nil, fmt.Errorf("no tv series of id %v", seriesId)
|
|
||||||
}
|
|
||||||
var ep *ent.Episode
|
|
||||||
for _, e := range series.Episodes {
|
|
||||||
if e.SeasonNumber == seasonNum && e.EpisodeNumber == episodeNum {
|
|
||||||
ep = e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ep == nil {
|
|
||||||
return nil, errors.Errorf("no episode of season %d episode %d", seasonNum, episodeNum)
|
|
||||||
}
|
|
||||||
torrent, err := trc.Download(r1.Link, s.db.GetDownloadDir())
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "downloading")
|
|
||||||
}
|
|
||||||
torrent.Start()
|
|
||||||
|
|
||||||
dir := fmt.Sprintf("%s/Season %02d/", series.TargetDir, seasonNum)
|
|
||||||
|
|
||||||
history, err := s.db.SaveHistoryRecord(ent.History{
|
|
||||||
MediaID: ep.MediaID,
|
|
||||||
EpisodeID: ep.ID,
|
|
||||||
SourceTitle: r1.Name,
|
|
||||||
TargetDir: dir,
|
|
||||||
Status: history.StatusRunning,
|
|
||||||
Size: r1.Size,
|
|
||||||
Saved: torrent.Save(),
|
|
||||||
DownloadClientID: dlc.ID,
|
|
||||||
IndexerID: r1.IndexerId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "save record")
|
|
||||||
}
|
|
||||||
s.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
|
|
||||||
|
|
||||||
s.tasks[history.ID] = &Task{Torrent: torrent}
|
|
||||||
s.sendMsg(fmt.Sprintf(message.BeginDownload, r1.Name))
|
|
||||||
|
|
||||||
log.Infof("success add %s to download task", r1.Name)
|
|
||||||
return &r1.Name, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
func (s *Server) searchAndDownload(seriesId, seasonNum, episodeNum int) (*string, error) {
|
|
||||||
|
|
||||||
res, err := core.SearchEpisode(s.db, seriesId, seasonNum, episodeNum, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r1 := res[0]
|
|
||||||
log.Infof("found resource to download: %+v", r1)
|
|
||||||
return s.downloadEpisodeTorrent(r1, seriesId, seasonNum, episodeNum)
|
|
||||||
}
|
|
||||||
|
|
||||||
type searchAndDownloadIn struct {
|
type searchAndDownloadIn struct {
|
||||||
ID int `json:"id" binding:"required"`
|
ID int `json:"id" binding:"required"`
|
||||||
Season int `json:"season"`
|
Season int `json:"season"`
|
||||||
@@ -203,7 +91,7 @@ func (s *Server) SearchTvAndDownload(c *gin.Context) (interface{}, error) {
|
|||||||
name = *name1
|
name = *name1
|
||||||
} else {
|
} else {
|
||||||
log.Infof("season episode search")
|
log.Infof("season episode search")
|
||||||
name1, err := s.searchAndDownload(in.ID, in.Season, in.Episode)
|
name1, err := s.core.SearchAndDownload(in.ID, in.Season, in.Episode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "download")
|
return nil, errors.Wrap(err, "download")
|
||||||
}
|
}
|
||||||
@@ -241,55 +129,17 @@ func (s *Server) DownloadTorrent(c *gin.Context) (interface{}, error) {
|
|||||||
name = fmt.Sprintf("%v S%02d", m.OriginalName, in.Season)
|
name = fmt.Sprintf("%v S%02d", m.OriginalName, in.Season)
|
||||||
}
|
}
|
||||||
res := torznab.Result{Name: name, Link: in.Link, Size: in.Size}
|
res := torznab.Result{Name: name, Link: in.Link, Size: in.Size}
|
||||||
return s.downloadSeasonPackage(res, in.MediaID, in.Season)
|
return s.core.DownloadSeasonPackage(res, in.MediaID, in.Season)
|
||||||
}
|
}
|
||||||
name := in.Name
|
name := in.Name
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = fmt.Sprintf("%v S%02dE%02d", m.OriginalName, in.Season, in.Episode)
|
name = fmt.Sprintf("%v S%02dE%02d", m.OriginalName, in.Season, in.Episode)
|
||||||
}
|
}
|
||||||
res := torznab.Result{Name: name, Link: in.Link, Size: in.Size, IndexerId: in.IndexerId}
|
res := torznab.Result{Name: name, Link: in.Link, Size: in.Size, IndexerId: in.IndexerId}
|
||||||
return s.downloadEpisodeTorrent(res, in.MediaID, in.Season, in.Episode)
|
return s.core.DownloadEpisodeTorrent(res, in.MediaID, in.Season, in.Episode)
|
||||||
} else {
|
} else {
|
||||||
//movie
|
//movie
|
||||||
trc, dlc, err := s.getDownloadClient()
|
return s.core.DownloadMovie(m, in.Link, in.Name, in.Size, in.IndexerId)
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "connect transmission")
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent, err := trc.Download(in.Link, s.db.GetDownloadDir())
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "downloading")
|
|
||||||
}
|
|
||||||
torrent.Start()
|
|
||||||
name := in.Name
|
|
||||||
if name == "" {
|
|
||||||
name = m.OriginalName
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
ep, _ := s.db.GetMovieDummyEpisode(m.ID)
|
|
||||||
history, err := s.db.SaveHistoryRecord(ent.History{
|
|
||||||
MediaID: m.ID,
|
|
||||||
EpisodeID: ep.ID,
|
|
||||||
SourceTitle: name,
|
|
||||||
TargetDir: m.TargetDir,
|
|
||||||
Status: history.StatusRunning,
|
|
||||||
Size: in.Size,
|
|
||||||
Saved: torrent.Save(),
|
|
||||||
DownloadClientID: dlc.ID,
|
|
||||||
IndexerID: in.IndexerId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("save history error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.tasks[history.ID] = &Task{Torrent: torrent}
|
|
||||||
|
|
||||||
s.db.SetEpisodeStatus(ep.ID, episode.StatusDownloading)
|
|
||||||
}()
|
|
||||||
|
|
||||||
s.sendMsg(fmt.Sprintf(message.BeginDownload, in.Name))
|
|
||||||
log.Infof("success add %s to download task", in.Name)
|
|
||||||
return in.Name, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,12 @@ import (
|
|||||||
"polaris/db"
|
"polaris/db"
|
||||||
"polaris/log"
|
"polaris/log"
|
||||||
"polaris/pkg/tmdb"
|
"polaris/pkg/tmdb"
|
||||||
"polaris/pkg/transmission"
|
"polaris/server/core"
|
||||||
"polaris/ui"
|
"polaris/ui"
|
||||||
|
|
||||||
ginzap "github.com/gin-contrib/zap"
|
ginzap "github.com/gin-contrib/zap"
|
||||||
|
|
||||||
"github.com/gin-contrib/static"
|
"github.com/gin-contrib/static"
|
||||||
"github.com/robfig/cron"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -22,26 +21,25 @@ import (
|
|||||||
|
|
||||||
func NewServer(db *db.Client) *Server {
|
func NewServer(db *db.Client) *Server {
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
return &Server{
|
s := &Server{
|
||||||
r: r,
|
r: r,
|
||||||
db: db,
|
db: db,
|
||||||
cron: cron.New(),
|
|
||||||
tasks: make(map[int]*Task),
|
|
||||||
}
|
}
|
||||||
|
s.core = core.NewClient(db, s.language)
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
r *gin.Engine
|
r *gin.Engine
|
||||||
db *db.Client
|
db *db.Client
|
||||||
cron *cron.Cron
|
core *core.Client
|
||||||
language string
|
language string
|
||||||
tasks map[int]*Task
|
|
||||||
jwtSerect string
|
jwtSerect string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Serve() error {
|
func (s *Server) Serve() error {
|
||||||
s.scheduler()
|
s.core.Init()
|
||||||
s.reloadTasks()
|
|
||||||
s.restoreProxy()
|
s.restoreProxy()
|
||||||
|
|
||||||
s.jwtSerect = s.db.GetSetting(db.JwtSerectKey)
|
s.jwtSerect = s.db.GetSetting(db.JwtSerectKey)
|
||||||
@@ -142,22 +140,6 @@ func (s *Server) MustTMDB() *tmdb.Client {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) reloadTasks() {
|
|
||||||
allTasks := s.db.GetHistories()
|
|
||||||
for _, t := range allTasks {
|
|
||||||
torrent, err := transmission.ReloadTorrent(t.Saved)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("relaod task %s failed: %v", t.SourceTitle, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !torrent.Exists() { //只要种子还存在于客户端中,就重新加载,有可能是还在做种中
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Infof("reloading task: %d %s", t.ID, t.SourceTitle)
|
|
||||||
s.tasks[t.ID] = &Task{Torrent: torrent}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) proxyPosters(c *gin.Context) {
|
func (s *Server) proxyPosters(c *gin.Context) {
|
||||||
remote, _ := url.Parse("https://image.tmdb.org")
|
remote, _ := url.Parse("https://image.tmdb.org")
|
||||||
proxy := httputil.NewSingleHostReverseProxy(remote)
|
proxy := httputil.NewSingleHostReverseProxy(remote)
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"polaris/db"
|
"polaris/db"
|
||||||
"polaris/ent/media"
|
|
||||||
storage1 "polaris/ent/storage"
|
|
||||||
"polaris/log"
|
"polaris/log"
|
||||||
"polaris/pkg/storage"
|
"polaris/pkg/storage"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -113,30 +112,3 @@ func (s *Server) SuggestedMovieFolderName(c *gin.Context) (interface{}, error) {
|
|||||||
log.Infof("tv series of tmdb id %v suggestting name is %v", id, name)
|
log.Infof("tv series of tmdb id %v suggestting name is %v", id, name)
|
||||||
return gin.H{"name": name}, nil
|
return gin.H{"name": name}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getStorage(storageId int, mediaType media.MediaType) (storage.Storage, error) {
|
|
||||||
st := s.db.GetStorage(storageId)
|
|
||||||
targetPath := st.TvPath
|
|
||||||
if mediaType == media.MediaTypeMovie {
|
|
||||||
targetPath = st.MoviePath
|
|
||||||
}
|
|
||||||
|
|
||||||
switch st.Implementation {
|
|
||||||
case storage1.ImplementationLocal:
|
|
||||||
|
|
||||||
storageImpl1, err := storage.NewLocalStorage(targetPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "new local")
|
|
||||||
}
|
|
||||||
return storageImpl1, nil
|
|
||||||
|
|
||||||
case storage1.ImplementationWebdav:
|
|
||||||
ws := st.ToWebDavSetting()
|
|
||||||
storageImpl1, err := storage.NewWebdavStorage(ws.URL, ws.User, ws.Password, targetPath, ws.ChangeFileHash == "true")
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "new webdav")
|
|
||||||
}
|
|
||||||
return storageImpl1, nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("no storage found")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ func (s *Server) AddTv2Watchlist(c *gin.Context) (interface{}, error) {
|
|||||||
if err := s.downloadBackdrop(detail.BackdropPath, r.ID); err != nil {
|
if err := s.downloadBackdrop(detail.BackdropPath, r.ID); err != nil {
|
||||||
log.Errorf("download poster error: %v", err)
|
log.Errorf("download poster error: %v", err)
|
||||||
}
|
}
|
||||||
if err := s.checkDownloadedSeriesFiles(r); err != nil {
|
if err := s.core.CheckDownloadedSeriesFiles(r); err != nil {
|
||||||
log.Errorf("check downloaded files error: %v", err)
|
log.Errorf("check downloaded files error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user