Files
sugarr/internal/client/jellyfin.go
2026-04-22 18:23:19 +02:00

134 lines
3.6 KiB
Go

package client
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"github.com/fjurmanovic/sugarr/internal/model"
"github.com/fjurmanovic/sugarr/internal/utl/configs"
)
type JellyfinClient struct {
url string
apiKey string
userID string
http *http.Client
}
func NewJellyfinClient(cfg *configs.Config) *JellyfinClient {
return &JellyfinClient{
url: cfg.JellyfinURL,
apiKey: cfg.JellyfinAPIKey,
userID: cfg.JellyfinUserID,
http: &http.Client{},
}
}
type jellyfinItemsResponse struct {
Items []jellyfinItem `json:"Items"`
}
type jellyfinItem struct {
Name string `json:"Name"`
ProductionYear int `json:"ProductionYear"`
ProviderIds map[string]string `json:"ProviderIds"`
Genres []string `json:"Genres"`
CommunityRating float64 `json:"CommunityRating"`
UserData jellyfinUserData `json:"UserData"`
}
type jellyfinUserData struct {
IsFavorite bool `json:"IsFavorite"`
PlayCount int `json:"PlayCount"`
}
func (c *JellyfinClient) get(path string, params url.Values) (*jellyfinItemsResponse, error) {
u := fmt.Sprintf("%s%s?%s", c.url, path, params.Encode())
req, err := http.NewRequest(http.MethodGet, u, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("MediaBrowser Token=%q", c.apiKey))
req.Header.Set("Accept", "application/json")
resp, err := c.http.Do(req)
if err != nil {
return nil, fmt.Errorf("jellyfin request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("jellyfin returned status %d for %s", resp.StatusCode, path)
}
var result jellyfinItemsResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("decoding jellyfin response: %w", err)
}
return &result, nil
}
func toMovies(items []jellyfinItem) []model.Movie {
movies := make([]model.Movie, 0, len(items))
for _, item := range items {
movies = append(movies, model.Movie{
Name: item.Name,
Year: item.ProductionYear,
IMDbID: item.ProviderIds["Imdb"],
TMDbID: item.ProviderIds["Tmdb"],
Genres: item.Genres,
CommunityRating: item.CommunityRating,
IsFavorite: item.UserData.IsFavorite,
PlayCount: item.UserData.PlayCount,
})
}
return movies
}
func (c *JellyfinClient) GetFavoriteMovies() ([]model.Movie, error) {
params := url.Values{
"IncludeItemTypes": {"Movie"},
"Recursive": {"true"},
"IsFavorite": {"true"},
"Fields": {"ProviderIds,Genres,CommunityRating,UserData"},
}
resp, err := c.get(fmt.Sprintf("/Users/%s/Items", c.userID), params)
if err != nil {
return nil, err
}
return toMovies(resp.Items), nil
}
func (c *JellyfinClient) GetRecentlyWatched() ([]model.Movie, error) {
params := url.Values{
"IncludeItemTypes": {"Movie"},
"Recursive": {"true"},
"Filters": {"IsPlayed"},
"SortBy": {"DatePlayed"},
"SortOrder": {"Descending"},
"Limit": {"50"},
"Fields": {"ProviderIds,Genres,CommunityRating,UserData"},
}
resp, err := c.get(fmt.Sprintf("/Users/%s/Items", c.userID), params)
if err != nil {
return nil, err
}
return toMovies(resp.Items), nil
}
func (c *JellyfinClient) GetAllMovies() ([]model.Movie, error) {
params := url.Values{
"IncludeItemTypes": {"Movie"},
"Recursive": {"true"},
"Fields": {"ProviderIds"},
"Limit": {"10000"},
}
resp, err := c.get(fmt.Sprintf("/Users/%s/Items", c.userID), params)
if err != nil {
return nil, err
}
return toMovies(resp.Items), nil
}