beginning of adding Jellyfin support

This commit is contained in:
Drew Weymouth
2023-11-14 09:05:02 -08:00
parent e841793318
commit d948d2d725
8 changed files with 108 additions and 20 deletions
+8
View File
@@ -8,7 +8,15 @@ import (
"github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2"
) )
type ServerType string
const (
ServerTypeSubsonic ServerType = "Subsonic"
ServerTypeJellyfin ServerType = "Jellyfin"
)
type ServerConnection struct { type ServerConnection struct {
ServerType ServerType
Hostname string Hostname string
AltHostname string AltHostname string
Username 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)
}
+7
View File
@@ -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
View File
@@ -43,7 +43,7 @@ func (s *ServerManager) ConnectToServer(conf *ServerConfig, password string) err
if err != nil { if err != nil {
return err return err
} }
s.Server = subsonicMP.SubsonicMediaProvider(cli) s.Server = cli.MediaProvider()
s.Server.SetPrefetchCoverCallback(s.prefetchCoverCB) s.Server.SetPrefetchCoverCallback(s.prefetchCoverCB)
s.LoggedInUser = conf.Username s.LoggedInUser = conf.Username
s.ServerID = conf.ID 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) return keyring.Set(s.appName, server.ID.String(), password)
} }
func (s *ServerManager) connect(connection ServerConnection, password string) (*subsonic.Client, error) { func (s *ServerManager) connect(connection ServerConnection, password string) (mediaprovider.Server, error) {
cli := &subsonic.Client{ var cli, altCli mediaprovider.Server
Client: &http.Client{Timeout: 10 * time.Second},
BaseUrl: connection.Hostname, cli = &subsonicMP.SubsonicServer{
User: connection.Username, Client: subsonic.Client{
PasswordAuth: connection.LegacyAuth, Client: &http.Client{Timeout: 10 * time.Second},
ClientName: "supersonic", BaseUrl: connection.Hostname,
User: connection.Username,
PasswordAuth: connection.LegacyAuth,
ClientName: "supersonic",
},
} }
altCli := &subsonic.Client{ altCli = &subsonicMP.SubsonicServer{
Client: &http.Client{Timeout: 10 * time.Second}, Client: subsonic.Client{
BaseUrl: connection.AltHostname, Client: &http.Client{Timeout: 10 * time.Second},
User: connection.Username, BaseUrl: connection.AltHostname,
PasswordAuth: connection.LegacyAuth, User: connection.Username,
ClientName: "supersonic", PasswordAuth: connection.LegacyAuth,
ClientName: "supersonic",
},
} }
pingChan := make(chan bool, 2) // false for primary hostname, true for alternate 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) <-time.After(delay)
if err := cli.Authenticate(password); err == nil { if err := cli.Login(connection.Username, password); err == nil {
pingChan <- val pingChan <- val
} }
} }
+3 -2
View File
@@ -22,6 +22,7 @@ require (
github.com/alessio/shellescape v1.4.1 // indirect github.com/alessio/shellescape v1.4.1 // indirect
github.com/danieljoos/wincred v1.1.0 // indirect github.com/danieljoos/wincred v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // 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/fredbi/uri v1.0.0 // indirect
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // 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 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/stretchr/testify v1.8.4 // indirect
github.com/tevino/abool v1.2.0 // indirect github.com/tevino/abool v1.2.0 // indirect
github.com/yuin/goldmark v1.5.5 // 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/mobile v0.0.0-20230531173138-3c911d8e3eda // indirect
golang.org/x/sys v0.11.0 // 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 gopkg.in/yaml.v3 v3.0.1 // indirect
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 // indirect honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 // indirect
) )
+6
View File
@@ -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/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 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/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 h1:ZGyJ6wp7CAfT31BugypcF/TPKEy2RrGR9JFq1JOjOpY=
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee/go.mod h1:Ov0ieN90M7i+0k3OxhA/g1dozGs+UcPHDsMKqPgRDk0= 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= 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.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 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= 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-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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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.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 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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-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-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+19 -2
View File
@@ -14,6 +14,7 @@ import (
type AddEditServerDialog struct { type AddEditServerDialog struct {
widget.BaseWidget widget.BaseWidget
ServerType backend.ServerType
Nickname string Nickname string
Host string Host string
AltHost string AltHost string
@@ -35,6 +36,7 @@ func NewAddEditServerDialog(title string, cancelable bool, prefillServer *backen
a := &AddEditServerDialog{} a := &AddEditServerDialog{}
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
if prefillServer != nil { if prefillServer != nil {
a.ServerType = prefillServer.ServerType
a.Nickname = prefillServer.Nickname a.Nickname = prefillServer.Nickname
a.Host = prefillServer.Hostname a.Host = prefillServer.Hostname
a.AltHost = prefillServer.AltHostname a.AltHost = prefillServer.AltHostname
@@ -44,6 +46,21 @@ func NewAddEditServerDialog(title string, cancelable bool, prefillServer *backen
titleLabel := widget.NewLabel(title) titleLabel := widget.NewLabel(title)
titleLabel.TextStyle.Bold = true 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 = widget.NewPasswordEntry()
a.passField.OnSubmitted = func(_ string) { a.doSubmit() } a.passField.OnSubmitted = func(_ string) { a.doSubmit() }
userField := widget.NewEntryWithData(binding.BindString(&a.Username)) 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 = widget.NewRichTextWithText("")
a.promptText.Hidden = true a.promptText.Hidden = true
legacyAuthCheck := widget.NewCheckWithData("Use legacy authentication", binding.BindBool(&a.LegacyAuth))
var bottomRow *fyne.Container var bottomRow *fyne.Container
if cancelable { if cancelable {
bottomRow = container.NewHBox( bottomRow = container.NewHBox(
@@ -81,6 +96,8 @@ func NewAddEditServerDialog(title string, cancelable bool, prefillServer *backen
a.container = container.NewVBox( a.container = container.NewVBox(
container.NewHBox(layout.NewSpacer(), titleLabel, layout.NewSpacer()), container.NewHBox(layout.NewSpacer(), titleLabel, layout.NewSpacer()),
container.New(layout.NewFormLayout(), container.New(layout.NewFormLayout(),
widget.NewLabel("Type"),
serverTypeChoice,
widget.NewLabel("Nickname"), widget.NewLabel("Nickname"),
nickField, nickField,
widget.NewLabel("Hostname"), widget.NewLabel("Hostname"),