package historyimport import ( "context" "fmt" "log/slog" ) type PlexServerProvider struct { client *PlexClient baseURL string token string } func NewPlexServerProvider(client *PlexClient, baseURL, token string) *PlexServerProvider { return &PlexServerProvider{client: client, baseURL: baseURL, token: token} } func (p *PlexServerProvider) Fetch(ctx context.Context) ([]Record, []string, error) { sections, err := p.client.FetchLibrarySections(ctx, p.baseURL, p.token) if err != nil { return nil, nil, err } var allItems []PlexItem var warnings []string for _, section := range sections { switch section.Type { case "movie": items, err := p.client.FetchWatchedItems(ctx, p.baseURL, p.token, section.Key, 1) if err != nil { warnings = append(warnings, fmt.Sprintf("failed to fetch movies from section %q: %v", section.Title, err)) continue } allItems = append(allItems, items...) case "show": items, err := p.client.FetchWatchedItems(ctx, p.baseURL, p.token, section.Key, 4) if err != nil { warnings = append(warnings, fmt.Sprintf("failed to fetch episodes from section %q: %v", section.Title, err)) continue } allItems = append(allItems, items...) } } onDeck, err := p.client.FetchOnDeck(ctx, p.baseURL, p.token) if err != nil { warnings = append(warnings, fmt.Sprintf("failed to fetch on-deck items: %v", err)) } else { allItems = append(allItems, onDeck...) } seriesMeta := p.fetchSeriesMetadata(ctx, allItems, &warnings) merged := make(map[string]Record, len(allItems)) for _, item := range allItems { record := NormalizePlexItem(item, seriesMeta[item.GrandparentRatingKey]) existing, ok := merged[record.ExternalID] if !ok { merged[record.ExternalID] = record continue } merged[record.ExternalID] = mergeRecords(existing, record) } records := make([]Record, 0, len(merged)) for _, record := range merged { records = append(records, record) } return records, warnings, nil } func (p *PlexServerProvider) fetchSeriesMetadata(ctx context.Context, items []PlexItem, warnings *[]string) map[string]*PlexItem { seen := make(map[string]struct{}) var seriesKeys []string for _, item := range items { if item.Type != "episode" || item.GrandparentRatingKey == "" { continue } if _, ok := seen[item.GrandparentRatingKey]; ok { continue } seen[item.GrandparentRatingKey] = struct{}{} seriesKeys = append(seriesKeys, item.GrandparentRatingKey) } result := make(map[string]*PlexItem, len(seriesKeys)) for _, key := range seriesKeys { meta, err := p.client.FetchMetadata(ctx, p.baseURL, p.token, key) if err != nil { slog.Warn("plex history import: failed to fetch series metadata", "rating_key", key, "error", err) *warnings = append(*warnings, fmt.Sprintf("failed to fetch series metadata for %s: %v", key, err)) continue } if meta != nil { result[key] = meta } } return result }