beginning of adding Jellyfin support
This commit is contained in:
@@ -8,7 +8,15 @@ import (
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
type ServerType string
|
||||
|
||||
const (
|
||||
ServerTypeSubsonic ServerType = "Subsonic"
|
||||
ServerTypeJellyfin ServerType = "Jellyfin"
|
||||
)
|
||||
|
||||
type ServerConnection struct {
|
||||
ServerType ServerType
|
||||
Hostname string
|
||||
AltHostname string
|
||||
Username string
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package jellyfin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/dweymouth/go-jellyfin"
|
||||
)
|
||||
|
||||
type jellyfinMediaProvider struct {
|
||||
client *jellyfin.Client
|
||||
prefetchCoverCB func(coverArtID string)
|
||||
}
|
||||
|
||||
func (j *jellyfinMediaProvider) SetPrefetchCoverCallback(cb func(coverArtID string)) {
|
||||
j.prefetchCoverCB = cb
|
||||
}
|
||||
|
||||
func (jellyfinMediaProvider) CreatePlaylist(name string, trackIDs []string) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (j *jellyfinMediaProvider) DeletePlaylist(id string) error {
|
||||
return j.client.DeletePlaylist(id)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package mediaprovider
|
||||
|
||||
type Server interface {
|
||||
Ping() bool
|
||||
Login(username, password string) error
|
||||
MediaProvider() MediaProvider
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package subsonic
|
||||
|
||||
import (
|
||||
subsonicCli "github.com/dweymouth/go-subsonic/subsonic"
|
||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||
)
|
||||
|
||||
type SubsonicServer struct {
|
||||
subsonicCli.Client
|
||||
}
|
||||
|
||||
func (s *SubsonicServer) Login(username, password string) error {
|
||||
s.User = username
|
||||
return s.Client.Authenticate(password)
|
||||
}
|
||||
|
||||
func (s *SubsonicServer) MediaProvider() mediaprovider.MediaProvider {
|
||||
return SubsonicMediaProvider(&s.Client)
|
||||
}
|
||||
+22
-16
@@ -43,7 +43,7 @@ func (s *ServerManager) ConnectToServer(conf *ServerConfig, password string) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Server = subsonicMP.SubsonicMediaProvider(cli)
|
||||
s.Server = cli.MediaProvider()
|
||||
s.Server.SetPrefetchCoverCallback(s.prefetchCoverCB)
|
||||
s.LoggedInUser = conf.Username
|
||||
s.ServerID = conf.ID
|
||||
@@ -156,25 +156,31 @@ func (s *ServerManager) SetServerPassword(server *ServerConfig, password string)
|
||||
return keyring.Set(s.appName, server.ID.String(), password)
|
||||
}
|
||||
|
||||
func (s *ServerManager) connect(connection ServerConnection, password string) (*subsonic.Client, error) {
|
||||
cli := &subsonic.Client{
|
||||
Client: &http.Client{Timeout: 10 * time.Second},
|
||||
BaseUrl: connection.Hostname,
|
||||
User: connection.Username,
|
||||
PasswordAuth: connection.LegacyAuth,
|
||||
ClientName: "supersonic",
|
||||
func (s *ServerManager) connect(connection ServerConnection, password string) (mediaprovider.Server, error) {
|
||||
var cli, altCli mediaprovider.Server
|
||||
|
||||
cli = &subsonicMP.SubsonicServer{
|
||||
Client: subsonic.Client{
|
||||
Client: &http.Client{Timeout: 10 * time.Second},
|
||||
BaseUrl: connection.Hostname,
|
||||
User: connection.Username,
|
||||
PasswordAuth: connection.LegacyAuth,
|
||||
ClientName: "supersonic",
|
||||
},
|
||||
}
|
||||
altCli := &subsonic.Client{
|
||||
Client: &http.Client{Timeout: 10 * time.Second},
|
||||
BaseUrl: connection.AltHostname,
|
||||
User: connection.Username,
|
||||
PasswordAuth: connection.LegacyAuth,
|
||||
ClientName: "supersonic",
|
||||
altCli = &subsonicMP.SubsonicServer{
|
||||
Client: subsonic.Client{
|
||||
Client: &http.Client{Timeout: 10 * time.Second},
|
||||
BaseUrl: connection.AltHostname,
|
||||
User: connection.Username,
|
||||
PasswordAuth: connection.LegacyAuth,
|
||||
ClientName: "supersonic",
|
||||
},
|
||||
}
|
||||
pingChan := make(chan bool, 2) // false for primary hostname, true for alternate
|
||||
pingFunc := func(delay time.Duration, cli *subsonic.Client, val bool) {
|
||||
pingFunc := func(delay time.Duration, cli mediaprovider.Server, val bool) {
|
||||
<-time.After(delay)
|
||||
if err := cli.Authenticate(password); err == nil {
|
||||
if err := cli.Login(connection.Username, password); err == nil {
|
||||
pingChan <- val
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ require (
|
||||
github.com/alessio/shellescape v1.4.1 // indirect
|
||||
github.com/danieljoos/wincred v1.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dweymouth/go-jellyfin v0.0.0-20231112010253-d93b76137091 // indirect
|
||||
github.com/fredbi/uri v1.0.0 // indirect
|
||||
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect
|
||||
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 // indirect
|
||||
@@ -38,10 +39,10 @@ require (
|
||||
github.com/stretchr/testify v1.8.4 // indirect
|
||||
github.com/tevino/abool v1.2.0 // indirect
|
||||
github.com/yuin/goldmark v1.5.5 // indirect
|
||||
golang.org/x/image v0.13.0 // indirect
|
||||
golang.org/x/image v0.14.0 // indirect
|
||||
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 // indirect
|
||||
)
|
||||
|
||||
@@ -71,6 +71,8 @@ github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1 h1:mGvOb3zxl4vCLv+
|
||||
github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1/go.mod h1:ZNCLJfehvEf34B7BbLKjgpsL9lyW7q938w/GY1XgV4E=
|
||||
github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20231110162149-a0e470497555 h1:8S+d0LuwdTUEipEzFeXp8rNwTQD47dBdDOg9+FI1+Vw=
|
||||
github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20231110162149-a0e470497555/go.mod h1:AWM1iPM2YfliduZ4u/kQzP9E6ARIWm0gg+57GpYzWro=
|
||||
github.com/dweymouth/go-jellyfin v0.0.0-20231112010253-d93b76137091 h1:UiyWNmeig46pkNCj7Pqc0Ta4JdxYKeXnBwuX+NOFCyM=
|
||||
github.com/dweymouth/go-jellyfin v0.0.0-20231112010253-d93b76137091/go.mod h1:BMwS4vdjEYf1gmjPGSKCzWP/I6YlI6fkefJ9nsjBjaU=
|
||||
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee h1:ZGyJ6wp7CAfT31BugypcF/TPKEy2RrGR9JFq1JOjOpY=
|
||||
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee/go.mod h1:Ov0ieN90M7i+0k3OxhA/g1dozGs+UcPHDsMKqPgRDk0=
|
||||
github.com/dweymouth/go-subsonic v0.0.0-20231105161622-54b5aec28363 h1:MIH7MAWWPPVRKEKxz+RJubn+ycyQPimHn1Zvoxs1KRI=
|
||||
@@ -337,6 +339,8 @@ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMx
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
|
||||
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
|
||||
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
|
||||
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -497,6 +501,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
type AddEditServerDialog struct {
|
||||
widget.BaseWidget
|
||||
|
||||
ServerType backend.ServerType
|
||||
Nickname string
|
||||
Host string
|
||||
AltHost string
|
||||
@@ -35,6 +36,7 @@ func NewAddEditServerDialog(title string, cancelable bool, prefillServer *backen
|
||||
a := &AddEditServerDialog{}
|
||||
a.ExtendBaseWidget(a)
|
||||
if prefillServer != nil {
|
||||
a.ServerType = prefillServer.ServerType
|
||||
a.Nickname = prefillServer.Nickname
|
||||
a.Host = prefillServer.Hostname
|
||||
a.AltHost = prefillServer.AltHostname
|
||||
@@ -44,6 +46,21 @@ func NewAddEditServerDialog(title string, cancelable bool, prefillServer *backen
|
||||
|
||||
titleLabel := widget.NewLabel(title)
|
||||
titleLabel.TextStyle.Bold = true
|
||||
legacyAuthCheck := widget.NewCheckWithData("Use legacy authentication", binding.BindBool(&a.LegacyAuth))
|
||||
serverTypeChoice := widget.NewRadioGroup([]string{"Subsonic", "Jellyfin"}, func(s string) {
|
||||
a.ServerType = backend.ServerType(s)
|
||||
if s == string(backend.ServerTypeSubsonic) {
|
||||
legacyAuthCheck.Show()
|
||||
} else {
|
||||
legacyAuthCheck.Hide()
|
||||
}
|
||||
})
|
||||
serverTypeChoice.Horizontal = true
|
||||
selected := backend.ServerTypeSubsonic
|
||||
if a.ServerType == backend.ServerTypeJellyfin {
|
||||
selected = backend.ServerTypeJellyfin
|
||||
}
|
||||
serverTypeChoice.Selected = string(selected)
|
||||
a.passField = widget.NewPasswordEntry()
|
||||
a.passField.OnSubmitted = func(_ string) { a.doSubmit() }
|
||||
userField := widget.NewEntryWithData(binding.BindString(&a.Username))
|
||||
@@ -62,8 +79,6 @@ func NewAddEditServerDialog(title string, cancelable bool, prefillServer *backen
|
||||
a.promptText = widget.NewRichTextWithText("")
|
||||
a.promptText.Hidden = true
|
||||
|
||||
legacyAuthCheck := widget.NewCheckWithData("Use legacy authentication", binding.BindBool(&a.LegacyAuth))
|
||||
|
||||
var bottomRow *fyne.Container
|
||||
if cancelable {
|
||||
bottomRow = container.NewHBox(
|
||||
@@ -81,6 +96,8 @@ func NewAddEditServerDialog(title string, cancelable bool, prefillServer *backen
|
||||
a.container = container.NewVBox(
|
||||
container.NewHBox(layout.NewSpacer(), titleLabel, layout.NewSpacer()),
|
||||
container.New(layout.NewFormLayout(),
|
||||
widget.NewLabel("Type"),
|
||||
serverTypeChoice,
|
||||
widget.NewLabel("Nickname"),
|
||||
nickField,
|
||||
widget.NewLabel("Hostname"),
|
||||
|
||||
Reference in New Issue
Block a user