diff --git a/go.mod b/go.mod index ec420128..606a5cf2 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl/v2 v2.13.0 // indirect github.com/hekmon/cunits/v2 v2.1.0 // indirect + // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -65,7 +66,7 @@ require ( require ( github.com/cyruzin/golang-tmdb v1.6.3 github.com/gin-gonic/gin v1.10.0 - github.com/hekmon/transmissionrpc v1.1.0 + github.com/hekmon/transmissionrpc/v3 v3.0.0 github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/go.sum b/go.sum index 82e7ed53..5655d9fa 100644 --- a/go.sum +++ b/go.sum @@ -72,6 +72,8 @@ github.com/hekmon/cunits/v2 v2.1.0 h1:k6wIjc4PlacNOHwKEMBgWV2/c8jyD4eRMs5mR1BBhI github.com/hekmon/cunits/v2 v2.1.0/go.mod h1:9r1TycXYXaTmEWlAIfFV8JT+Xo59U96yUJAYHxzii2M= github.com/hekmon/transmissionrpc v1.1.0 h1:58xY27x2JYxaMlIj7ycKnxqgCm3IjvTxfB7cHPLxOfs= github.com/hekmon/transmissionrpc v1.1.0/go.mod h1:qkwhsyD/MQSlWvOE1AC92xajwEveAuGsOvTuOBZEuHc= +github.com/hekmon/transmissionrpc/v3 v3.0.0 h1:0Fb11qE0IBh4V4GlOwHNYpqpjcYDp5GouolwrpmcUDQ= +github.com/hekmon/transmissionrpc/v3 v3.0.0/go.mod h1:38SlNhFzinVUuY87wGj3acOmRxeYZAZfrj6Re7UgCDg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= diff --git a/pkg/transmission/transmission.go b/pkg/transmission/transmission.go index aa5c156d..a9f36286 100644 --- a/pkg/transmission/transmission.go +++ b/pkg/transmission/transmission.go @@ -1,10 +1,12 @@ package transmission import ( + "context" "net/url" + "polaris/log" "strconv" - "github.com/hekmon/transmissionrpc" + "github.com/hekmon/transmissionrpc/v3" "github.com/pkg/errors" ) @@ -13,7 +15,13 @@ func NewClient(url1, user, password string) (*Client, error) { if err != nil { return nil, errors.Wrap(err, "parse url") } - tbt, err := transmissionrpc.New(u.Hostname(), user, password, nil) + if user != "" { + log.Info("transmission login with user: ", user) + u.User = url.UserPassword(user, password) + } + u.Path = "/transmission/rpc" + + tbt, err := transmissionrpc.New(u, nil) if err != nil { return nil, errors.Wrap(err, "connect transmission") } @@ -25,47 +33,61 @@ type Client struct { } func (c *Client) Download(magnet, dir string) (*Torrent, error) { - t, err := c.c.TorrentAdd(&transmissionrpc.TorrentAddPayload{ - Filename: &magnet, + t, err := c.c.TorrentAdd(context.TODO(), transmissionrpc.TorrentAddPayload{ + Filename: &magnet, DownloadDir: &dir, }) + log.Infof("get torrent info: %+v", t) + return &Torrent{ - t: t, - c: c.c, + id: *t.ID, + c: c.c, }, err } type Torrent struct { - t *transmissionrpc.Torrent - c *transmissionrpc.Client + //t *transmissionrpc.Torrent + c *transmissionrpc.Client + id int64 +} + +func (t *Torrent) getTorrent() transmissionrpc.Torrent { + r, err := t.c.TorrentGetAllFor(context.TODO(), []int64{t.id}) + if err != nil { + log.Errorf("get torrent info for error: %v", err) + } + return r[0] } func (t *Torrent) Name() string { - return *t.t.Name + return *t.getTorrent().Name } func (t *Torrent) Progress() int { - if *t.t.IsFinished { + if t.getTorrent().IsFinished != nil && *t.getTorrent().IsFinished { return 100 } - return int(*t.t.PercentDone*100) + if t.getTorrent().PercentDone != nil { + return int(*t.getTorrent().PercentDone * 100) + } + return 0 } func (t *Torrent) Stop() error { - return t.c.TorrentStopIDs([]int64{*t.t.ID}) + return t.c.TorrentStopIDs(context.TODO(), []int64{t.id}) } func (t *Torrent) Start() error { - return t.c.TorrentStartIDs([]int64{*t.t.ID}) + return t.c.TorrentStartIDs(context.TODO(), []int64{t.id}) } func (t *Torrent) Remove() error { - return t.c.TorrentRemove(&transmissionrpc.TorrentRemovePayload{ - IDs: []int64{*t.t.ID}, + return t.c.TorrentRemove(context.TODO(), transmissionrpc.TorrentRemovePayload{ + IDs: []int64{t.id}, DeleteLocalData: true, }) } func (t *Torrent) Save() string { - return strconv.Itoa(int(*t.t.ID)) -} \ No newline at end of file + return strconv.Itoa(int(t.id)) +} diff --git a/server/resources.go b/server/resources.go index 16232969..0be26a5c 100644 --- a/server/resources.go +++ b/server/resources.go @@ -75,34 +75,29 @@ type searchAndDownloadIn struct { Episode int `json:"episode"` } -func (s *Server) SearchAndDownload(c *gin.Context) (interface{}, error) { - var in searchAndDownloadIn - if err := c.ShouldBindJSON(&in); err != nil { - return nil, errors.Wrap(err, "bind json") - } +func (s *Server) searchAndDownload(seriesId, seasonNum, episodeNum int) (*string, error) { tr := s.db.GetTransmission() trc, err := transmission.NewClient(tr.URL, tr.User, tr.Password) if err != nil { return nil, errors.Wrap(err, "connect transmission") } - log.Infof("search episode resources link: %v", in) - series := s.db.GetSeriesDetails(in.ID) + series := s.db.GetSeriesDetails(seriesId) if series == nil { - return nil, fmt.Errorf("no tv series of id %v", in.ID) + return nil, fmt.Errorf("no tv series of id %v", seriesId) } var ep *ent.Episode for _, e := range series.Episodes { - if e.SeasonNumber == in.Season && e.EpisodeNumber == in.Episode { + if e.SeasonNumber == seasonNum && e.EpisodeNumber == episodeNum { ep = e } } if ep == nil { - return nil, errors.Errorf("no episode of season %d episode %d", in.Season, in.Episode) + return nil, errors.Errorf("no episode of season %d episode %d", seasonNum, episodeNum) } - res := s.searchTvWithTorznab(series.OriginalName, in.Season, in.Episode) + res := s.searchTvWithTorznab(series.OriginalName, seasonNum, episodeNum) if len(res) == 0 { - return "", fmt.Errorf("no resource found") + return nil, fmt.Errorf("no resource found") } r1 := res[0] log.Infof("found resource to download: %v", r1) @@ -138,8 +133,22 @@ func (s *Server) SearchAndDownload(c *gin.Context) (interface{}, error) { // return nil, errors.Wrap(err, "download torrent") // } log.Errorf("success add %s to download task", r1.Name) + return &r1.Name, nil +} + +func (s *Server) SearchAndDownload(c *gin.Context) (interface{}, error) { + var in searchAndDownloadIn + if err := c.ShouldBindJSON(&in); err != nil { + return nil, errors.Wrap(err, "bind json") + } + log.Infof("search episode resources link: %v", in) + name, err := s.searchAndDownload(in.ID, in.Season, in.Episode) + if err != nil { + return nil, errors.Wrap(err, "download") + } + return gin.H{ - "name": r1.Name, + "name": *name, }, nil } diff --git a/server/scheduler.go b/server/scheduler.go index 8fcde28a..3dcb2643 100644 --- a/server/scheduler.go +++ b/server/scheduler.go @@ -10,12 +10,21 @@ import ( ) func (s *Server) scheduler() { - s.cron.AddFunc("@every 1m", s.checkTasks) + s.mustAddCron("@every 1m", s.checkTasks) + s.cron.Start() +} + +func (s *Server) mustAddCron(spec string, cmd func()) { + if err := s.cron.AddFunc(spec, cmd); err != nil { + log.Errorf("add func error: %v", err) + panic(err) + } } func (s *Server) checkTasks() { + log.Infof("begin check tasks...") for id, t := range s.tasks { - 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 { log.Infof("task is done: %v", t.Name()) s.moveCompletedTask(id) diff --git a/server/watchlist.go b/server/watchlist.go index 6da2c98c..ec56ada0 100644 --- a/server/watchlist.go +++ b/server/watchlist.go @@ -28,8 +28,8 @@ func (s *Server) SearchTvSeries(c *gin.Context) (interface{}, error) { } type addWatchlistIn struct { - TmdbID int `json:"id" binding:"required"` - StorageID int `json:"folder"` + TmdbID int `json:"id" binding:"required"` + StorageID int `json:"storage_id"` } func (s *Server) AddWatchlist(c *gin.Context) (interface{}, error) { @@ -53,8 +53,6 @@ func (s *Server) AddWatchlist(c *gin.Context) (interface{}, error) { } } - - var epIds []int for _, season := range detail.Seasons { seasonId := season.SeasonNumber @@ -92,7 +90,6 @@ func (s *Server) GetWatchlist(c *gin.Context) (interface{}, error) { return list, nil } - func (s *Server) GetTvDetails(c *gin.Context) (interface{}, error) { ids := c.Param("id") id, err := strconv.Atoi(ids) @@ -101,4 +98,4 @@ func (s *Server) GetTvDetails(c *gin.Context) (interface{}, error) { } detail := s.db.GetSeriesDetails(id) return detail, nil -} \ No newline at end of file +} diff --git a/ui/lib/system_settings.dart b/ui/lib/system_settings.dart index a4705b0f..63754c1b 100644 --- a/ui/lib/system_settings.dart +++ b/ui/lib/system_settings.dart @@ -66,6 +66,7 @@ class _SystemSettingsPageState extends ConsumerState { padding: EdgeInsets.all(16.0), child: Text("保存"), ), + onPressed: () { // 通过_formKey.currentState 获取FormState后, // 调用validate()方法校验用户名密码是否合法,校验