mirror of
https://github.com/simon-ding/polaris.git
synced 2026-06-09 19:47:47 +08:00
feat: support prowlarr connection
This commit is contained in:
@@ -17,6 +17,7 @@ const (
|
|||||||
SetttingSizeLimiter = "size_limiter"
|
SetttingSizeLimiter = "size_limiter"
|
||||||
SettingTvNamingFormat = "tv_naming_format"
|
SettingTvNamingFormat = "tv_naming_format"
|
||||||
SettingMovieNamingFormat = "movie_naming_format"
|
SettingMovieNamingFormat = "movie_naming_format"
|
||||||
|
SettingProwlarrInfo = "prowlarr_info"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -60,3 +61,8 @@ type Limiter struct {
|
|||||||
Max int `json:"max"`
|
Max int `json:"max"`
|
||||||
Min int `json:"min"`
|
Min int `json:"min"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProwlarrSetting struct {
|
||||||
|
ApiKey string `json:"api_key"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|||||||
21
db/db.go
21
db/db.go
@@ -660,3 +660,24 @@ func (c *Client) CleanAllDanglingEpisodes() error {
|
|||||||
func (c *Client) AddBlacklistItem(item *ent.Blacklist) error {
|
func (c *Client) AddBlacklistItem(item *ent.Blacklist) error {
|
||||||
return c.ent.Blacklist.Create().SetType(item.Type).SetValue(item.Value).SetNotes(item.Notes).Exec(context.Background())
|
return c.ent.Blacklist.Create().SetType(item.Type).SetValue(item.Value).SetNotes(item.Notes).Exec(context.Background())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (c *Client) GetProwlarrSetting() (*ProwlarrSetting, error) {
|
||||||
|
s := c.GetSetting(SettingProwlarrInfo)
|
||||||
|
if s == "" {
|
||||||
|
return nil, errors.New("prowlarr setting not set")
|
||||||
|
}
|
||||||
|
var se ProwlarrSetting
|
||||||
|
if err := json.Unmarshal([]byte(s), &se); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &se, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) SaveProwlarrSetting(se *ProwlarrSetting) error {
|
||||||
|
data, err := json.Marshal(se)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.SetSetting(SettingProwlarrInfo, string(data))
|
||||||
|
}
|
||||||
1
go.mod
1
go.mod
@@ -21,6 +21,7 @@ require (
|
|||||||
github.com/ncruces/go-sqlite3 v0.18.4
|
github.com/ncruces/go-sqlite3 v0.18.4
|
||||||
github.com/nikoksr/notify v1.0.0
|
github.com/nikoksr/notify v1.0.0
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
|
golift.io/starr v1.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
10
go.sum
10
go.sum
@@ -216,8 +216,6 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
|
|||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
@@ -251,8 +249,6 @@ github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt
|
|||||||
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||||
github.com/nikoksr/notify v1.0.0 h1:qe9/6FRsWdxBgQgWcpvQ0sv8LRGJZDpRB4TkL2uNdO8=
|
github.com/nikoksr/notify v1.0.0 h1:qe9/6FRsWdxBgQgWcpvQ0sv8LRGJZDpRB4TkL2uNdO8=
|
||||||
github.com/nikoksr/notify v1.0.0/go.mod h1:hPaaDt30d6LAA7/5nb0e48Bp/MctDfycCSs8VEgN29I=
|
github.com/nikoksr/notify v1.0.0/go.mod h1:hPaaDt30d6LAA7/5nb0e48Bp/MctDfycCSs8VEgN29I=
|
||||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
@@ -310,8 +306,6 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
|||||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
|
||||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||||
@@ -453,10 +447,10 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3
|
|||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
|
||||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golift.io/starr v1.0.0 h1:IDSaSL+ZYxdLT/Lg//dg/iwZ39LHO3D5CmbLCOgSXbI=
|
||||||
|
golift.io/starr v1.0.0/go.mod h1:xnUwp4vK62bDvozW9QHUYc08m6kjwaZnGw3Db65fQHw=
|
||||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ func ParseDoulist(doulistUrl string) (*importlist.Response, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var items []importlist.Item
|
||||||
doc.Find("div[class=doulist-item]").Each(func(i int, selection *goquery.Selection) {
|
doc.Find("div[class=doulist-item]").Each(func(i int, selection *goquery.Selection) {
|
||||||
titleDiv := selection.Find("div[class=title]")
|
titleDiv := selection.Find("div[class=title]")
|
||||||
link := titleDiv.Find("div>a")
|
link := titleDiv.Find("div>a")
|
||||||
@@ -64,18 +65,26 @@ func ParseDoulist(doulistUrl string) (*importlist.Response, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_, err := parseDetailPage(strings.TrimSpace(href))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("get detail page: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
item := importlist.Item{
|
item := importlist.Item{
|
||||||
Title: strings.TrimSpace(link.Text()),
|
Title: strings.TrimSpace(link.Text()),
|
||||||
Year: year,
|
Year: year,
|
||||||
}
|
}
|
||||||
|
items = append(items, item)
|
||||||
_ = item
|
_ = item
|
||||||
println(link.Text(), href)
|
//println(link.Text(), href)
|
||||||
})
|
})
|
||||||
return nil, nil
|
|
||||||
|
return &importlist.Response{Items: items}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDetailPage(url string) (string, error) {
|
func parseDetailPage(url string) (string, error) {
|
||||||
|
println(url)
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -95,6 +104,14 @@ func parseDetailPage(url string) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doc.Find("div[class='subject clearfix']").Each(func(i int, se *goquery.Selection) {
|
||||||
|
println(se.Text())
|
||||||
|
se.Children().Get(1)
|
||||||
|
imdb := se.Find("div[class='info']").First().Children().Last()
|
||||||
|
println(imdb.Text())
|
||||||
|
})
|
||||||
|
|
||||||
_ = doc
|
_ = doc
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestParseDoulist(t *testing.T) {
|
func TestParseDoulist(t *testing.T) {
|
||||||
r, err := ParseDoulist("https://www.douban.com/doulist/166422/")
|
r, err := ParseDoulist("https://www.douban.com/doulist/81580/")
|
||||||
log.Info(r, err)
|
log.Info(r, err)
|
||||||
}
|
}
|
||||||
|
|||||||
65
pkg/prowlarr/prowlarr.go
Normal file
65
pkg/prowlarr/prowlarr.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package prowlarr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"polaris/db"
|
||||||
|
"polaris/ent"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golift.io/starr"
|
||||||
|
"golift.io/starr/prowlarr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
p *prowlarr.Prowlarr
|
||||||
|
apiKey string
|
||||||
|
url string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(apiKey, url string) *Client {
|
||||||
|
c := starr.New(apiKey, url, 10*time.Second)
|
||||||
|
p := prowlarr.New(c)
|
||||||
|
return &Client{p: p, apiKey: apiKey, url: url}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetIndexers() ([]*db.TorznabInfo, error) {
|
||||||
|
ins, err := c.p.GetIndexers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var indexers []*db.TorznabInfo
|
||||||
|
for _, in := range ins {
|
||||||
|
if !in.Enable {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seedRatio := 0.0
|
||||||
|
for _, f := range in.Fields {
|
||||||
|
if f.Name == "torrentBaseSettings.seedRatio" && f.Value != nil {
|
||||||
|
if r, ok := f.Value.(float64); ok {
|
||||||
|
seedRatio = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setting := db.TorznabSetting{
|
||||||
|
URL: fmt.Sprintf("%s/%d/api", strings.TrimSuffix(c.url, "/"), in.ID),
|
||||||
|
ApiKey: c.apiKey,
|
||||||
|
}
|
||||||
|
data, _ := json.Marshal(&setting)
|
||||||
|
|
||||||
|
entIndexer := ent.Indexers{
|
||||||
|
Name: in.Name,
|
||||||
|
Implementation: "torznab",
|
||||||
|
Priority: int(in.Priority),
|
||||||
|
SeedRatio: float32(seedRatio),
|
||||||
|
Settings: string(data),
|
||||||
|
}
|
||||||
|
|
||||||
|
indexers = append(indexers, &db.TorznabInfo{
|
||||||
|
Indexers: &entIndexer,
|
||||||
|
TorznabSetting: setting,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return indexers, nil
|
||||||
|
}
|
||||||
13
pkg/prowlarr/prowlarr_test.go
Normal file
13
pkg/prowlarr/prowlarr_test.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package prowlarr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"polaris/log"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test111(t *testing.T) {
|
||||||
|
c := New("", "http://10.0.0.8:9696/")
|
||||||
|
apis , err := c.GetIndexers()
|
||||||
|
log.Infof("errors: %v", err)
|
||||||
|
log.Infof("indexers: %+v", apis[0])
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"polaris/ent/media"
|
"polaris/ent/media"
|
||||||
"polaris/log"
|
"polaris/log"
|
||||||
"polaris/pkg/metadata"
|
"polaris/pkg/metadata"
|
||||||
|
"polaris/pkg/prowlarr"
|
||||||
"polaris/pkg/torznab"
|
"polaris/pkg/torznab"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -136,7 +137,7 @@ func torrentSizeOk(detail *db.MediaDetails, torrentSize int, param *SearchParam)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return torrentSize > defaultMinSize * multiplier
|
return torrentSize > defaultMinSize*multiplier
|
||||||
}
|
}
|
||||||
|
|
||||||
func seasonEpisodeCount(detail *db.MediaDetails, seasonNum int) int {
|
func seasonEpisodeCount(detail *db.MediaDetails, seasonNum int) int {
|
||||||
@@ -230,6 +231,17 @@ func searchWithTorznab(db *db.Client, queries ...string) []torznab.Result {
|
|||||||
|
|
||||||
var res []torznab.Result
|
var res []torznab.Result
|
||||||
allTorznab := db.GetAllTorznabInfo()
|
allTorznab := db.GetAllTorznabInfo()
|
||||||
|
|
||||||
|
p, err := db.GetProwlarrSetting()
|
||||||
|
if err == nil { //prowlarr exists
|
||||||
|
c := prowlarr.New(p.ApiKey, p.URL)
|
||||||
|
all, err := c.GetIndexers()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("get prowlarr all indexer error: %v", err)
|
||||||
|
} else {
|
||||||
|
allTorznab = append(allTorznab, all...)
|
||||||
|
}
|
||||||
|
}
|
||||||
resChan := make(chan []torznab.Result)
|
resChan := make(chan []torznab.Result)
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ func (s *Server) Serve() error {
|
|||||||
setting.POST("/parse/movie", HttpHandler(s.ParseMovie))
|
setting.POST("/parse/movie", HttpHandler(s.ParseMovie))
|
||||||
setting.POST("/monitoring", HttpHandler(s.ChangeEpisodeMonitoring))
|
setting.POST("/monitoring", HttpHandler(s.ChangeEpisodeMonitoring))
|
||||||
setting.POST("/cron/trigger", HttpHandler(s.TriggerCronJob))
|
setting.POST("/cron/trigger", HttpHandler(s.TriggerCronJob))
|
||||||
|
setting.GET("/prowlarr", HttpHandler(s.GetProwlarrSetting))
|
||||||
|
setting.POST("/prowlarr", HttpHandler(s.SaveProwlarrSetting))
|
||||||
}
|
}
|
||||||
activity := api.Group("/activity")
|
activity := api.Group("/activity")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"polaris/ent"
|
"polaris/ent"
|
||||||
"polaris/ent/downloadclients"
|
"polaris/ent/downloadclients"
|
||||||
"polaris/log"
|
"polaris/log"
|
||||||
|
"polaris/pkg/prowlarr"
|
||||||
"polaris/pkg/qbittorrent"
|
"polaris/pkg/qbittorrent"
|
||||||
"polaris/pkg/torznab"
|
"polaris/pkg/torznab"
|
||||||
"polaris/pkg/transmission"
|
"polaris/pkg/transmission"
|
||||||
@@ -303,3 +304,26 @@ func (s *Server) TriggerCronJob(c *gin.Context) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
return "success", nil
|
return "success", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetProwlarrSetting(c *gin.Context) (interface{}, error) {
|
||||||
|
se, err :=s.db.GetProwlarrSetting()
|
||||||
|
if err != nil {
|
||||||
|
return &db.ProwlarrSetting{}, nil
|
||||||
|
}
|
||||||
|
return se, nil
|
||||||
|
}
|
||||||
|
func (s *Server) SaveProwlarrSetting(c *gin.Context) (interface{}, error) {
|
||||||
|
var in db.ProwlarrSetting
|
||||||
|
if err := c.ShouldBindJSON(&in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client := prowlarr.New(in.ApiKey, in.URL)
|
||||||
|
if _, err := client.GetIndexers(); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "connect to prowlarr error")
|
||||||
|
}
|
||||||
|
err := s.db.SaveProwlarrSetting(&in)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return "success", nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ class APIs {
|
|||||||
static final addImportlistUrl = "$_baseUrl/api/v1/importlist/add";
|
static final addImportlistUrl = "$_baseUrl/api/v1/importlist/add";
|
||||||
static final deleteImportlistUrl = "$_baseUrl/api/v1/importlist/delete";
|
static final deleteImportlistUrl = "$_baseUrl/api/v1/importlist/delete";
|
||||||
static final getAllImportlists = "$_baseUrl/api/v1/importlist/";
|
static final getAllImportlists = "$_baseUrl/api/v1/importlist/";
|
||||||
|
static final prowlarrUrl = "$_baseUrl/api/v1/setting/prowlarr";
|
||||||
|
|
||||||
static final notifierAllUrl = "$_baseUrl/api/v1/notifier/all";
|
static final notifierAllUrl = "$_baseUrl/api/v1/notifier/all";
|
||||||
static final notifierDeleteUrl = "$_baseUrl/api/v1/notifier/id/";
|
static final notifierDeleteUrl = "$_baseUrl/api/v1/notifier/id/";
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ var importlistProvider =
|
|||||||
AsyncNotifierProvider.autoDispose<ImportListData, List<ImportList>>(
|
AsyncNotifierProvider.autoDispose<ImportListData, List<ImportList>>(
|
||||||
ImportListData.new);
|
ImportListData.new);
|
||||||
|
|
||||||
|
var prowlarrSettingDataProvider =
|
||||||
|
AsyncNotifierProvider.autoDispose<ProwlarrSettingData, ProwlarrSetting>(
|
||||||
|
ProwlarrSettingData.new);
|
||||||
|
|
||||||
class EditSettingData extends AutoDisposeAsyncNotifier<GeneralSetting> {
|
class EditSettingData extends AutoDisposeAsyncNotifier<GeneralSetting> {
|
||||||
@override
|
@override
|
||||||
FutureOr<GeneralSetting> build() async {
|
FutureOr<GeneralSetting> build() async {
|
||||||
@@ -503,3 +507,38 @@ class ImportListData extends AutoDisposeAsyncNotifier<List<ImportList>> {
|
|||||||
ref.invalidateSelf();
|
ref.invalidateSelf();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ProwlarrSetting {
|
||||||
|
final String apiKey;
|
||||||
|
final String url;
|
||||||
|
ProwlarrSetting({required this.apiKey, required this.url});
|
||||||
|
factory ProwlarrSetting.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ProwlarrSetting(apiKey: json["api_key"], url: json["url"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> tojson() => {"api_key": apiKey, "url": url};
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProwlarrSettingData extends AutoDisposeAsyncNotifier<ProwlarrSetting> {
|
||||||
|
@override
|
||||||
|
FutureOr<ProwlarrSetting> build() async {
|
||||||
|
final dio = APIs.getDio();
|
||||||
|
var resp = await dio.get(APIs.prowlarrUrl);
|
||||||
|
var sp = ServerResponse.fromJson(resp.data);
|
||||||
|
if (sp.code != 0) {
|
||||||
|
throw sp.message;
|
||||||
|
}
|
||||||
|
var se = ProwlarrSetting.fromJson(sp.data);
|
||||||
|
return se;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> save(ProwlarrSetting ps) async {
|
||||||
|
final dio = APIs.getDio();
|
||||||
|
var resp = await dio.post(APIs.prowlarrUrl, data: ps.tojson());
|
||||||
|
var sp = ServerResponse.fromJson(resp.data);
|
||||||
|
if (sp.code != 0) {
|
||||||
|
throw sp.message;
|
||||||
|
}
|
||||||
|
ref.invalidateSelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
72
ui/lib/settings/prowlarr.dart
Normal file
72
ui/lib/settings/prowlarr.dart
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||||
|
import 'package:ui/providers/settings.dart';
|
||||||
|
import 'package:ui/widgets/progress_indicator.dart';
|
||||||
|
import 'package:ui/widgets/utils.dart';
|
||||||
|
import 'package:ui/widgets/widgets.dart';
|
||||||
|
|
||||||
|
class ProwlarrSettingPage extends ConsumerStatefulWidget {
|
||||||
|
const ProwlarrSettingPage({super.key});
|
||||||
|
@override
|
||||||
|
ConsumerState<ConsumerStatefulWidget> createState() {
|
||||||
|
return ProwlarrSettingState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProwlarrSettingState extends ConsumerState<ProwlarrSettingPage> {
|
||||||
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var ps = ref.watch(prowlarrSettingDataProvider);
|
||||||
|
return ps.when(
|
||||||
|
data: (v) => FormBuilder(
|
||||||
|
key: _formKey, //设置globalKey,用于后面获取FormState
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
initialValue: {"api_key": v.apiKey, "url": v.url},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "url",
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: "Prowlarr URL",
|
||||||
|
icon: Icon(Icons.web),
|
||||||
|
hintText: "http://10.0.0.8:9696"),
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: "api_key",
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: "API Key",
|
||||||
|
icon: Icon(Icons.web),
|
||||||
|
helperText: "Prowlarr 设置 -> 通用设置 -> 接口密钥"),
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (_formKey.currentState!.saveAndValidate()) {
|
||||||
|
var values = _formKey.currentState!.value;
|
||||||
|
var f = ref
|
||||||
|
.read(prowlarrSettingDataProvider.notifier)
|
||||||
|
.save(ProwlarrSetting(
|
||||||
|
apiKey: values["api_key"],
|
||||||
|
url: values["url"]))
|
||||||
|
.then((v) => showSnakeBar("更新成功"));
|
||||||
|
showLoadingWithFuture(f);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Padding(padding: EdgeInsets.all(10), child: Text("保存"),)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
error: (err, trace) => Text("$err"),
|
||||||
|
loading: () => const MyProgressIndicator());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import 'package:ui/settings/general.dart';
|
|||||||
import 'package:ui/settings/importlist.dart';
|
import 'package:ui/settings/importlist.dart';
|
||||||
import 'package:ui/settings/indexer.dart';
|
import 'package:ui/settings/indexer.dart';
|
||||||
import 'package:ui/settings/notifier.dart';
|
import 'package:ui/settings/notifier.dart';
|
||||||
|
import 'package:ui/settings/prowlarr.dart';
|
||||||
import 'package:ui/settings/storage.dart';
|
import 'package:ui/settings/storage.dart';
|
||||||
|
|
||||||
class SystemSettingsPage extends ConsumerStatefulWidget {
|
class SystemSettingsPage extends ConsumerStatefulWidget {
|
||||||
@@ -25,6 +26,7 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
|||||||
children: [
|
children: [
|
||||||
getExpansionTile("常规", const GeneralSettings()),
|
getExpansionTile("常规", const GeneralSettings()),
|
||||||
getExpansionTile("索引器", const IndexerSettings()),
|
getExpansionTile("索引器", const IndexerSettings()),
|
||||||
|
getExpansionTile("Prowlarr 设置", const ProwlarrSettingPage()),
|
||||||
getExpansionTile("下载器", const DownloaderSettings()),
|
getExpansionTile("下载器", const DownloaderSettings()),
|
||||||
getExpansionTile("存储", const StorageSettings()),
|
getExpansionTile("存储", const StorageSettings()),
|
||||||
getExpansionTile("通知客户端", const NotifierSettings()),
|
getExpansionTile("通知客户端", const NotifierSettings()),
|
||||||
|
|||||||
Reference in New Issue
Block a user