mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-10 07:58:39 -05:00
706 lines
26 KiB
C#
706 lines
26 KiB
C#
using octo_fiesta.Models.Domain;
|
|
using octo_fiesta.Models.Settings;
|
|
using octo_fiesta.Models.Download;
|
|
using octo_fiesta.Models.Search;
|
|
using octo_fiesta.Models.Subsonic;
|
|
using System.Text.Json;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace octo_fiesta.Services.Deezer;
|
|
|
|
/// <summary>
|
|
/// Metadata service implementation using the Deezer API (free, no key required)
|
|
/// </summary>
|
|
public class DeezerMetadataService : IMusicMetadataService
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
private readonly SubsonicSettings _settings;
|
|
private const string BaseUrl = "https://api.deezer.com";
|
|
|
|
public DeezerMetadataService(IHttpClientFactory httpClientFactory, IOptions<SubsonicSettings> settings)
|
|
{
|
|
_httpClient = httpClientFactory.CreateClient();
|
|
_settings = settings.Value;
|
|
}
|
|
|
|
public async Task<List<Song>> SearchSongsAsync(string query, int limit = 20)
|
|
{
|
|
try
|
|
{
|
|
var url = $"{BaseUrl}/search/track?q={Uri.EscapeDataString(query)}&limit={limit}";
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode) return new List<Song>();
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var result = JsonDocument.Parse(json);
|
|
|
|
var songs = new List<Song>();
|
|
if (result.RootElement.TryGetProperty("data", out var data))
|
|
{
|
|
foreach (var track in data.EnumerateArray())
|
|
{
|
|
var song = ParseDeezerTrack(track);
|
|
if (ShouldIncludeSong(song))
|
|
{
|
|
songs.Add(song);
|
|
}
|
|
}
|
|
}
|
|
|
|
return songs;
|
|
}
|
|
catch
|
|
{
|
|
return new List<Song>();
|
|
}
|
|
}
|
|
|
|
public async Task<List<Album>> SearchAlbumsAsync(string query, int limit = 20)
|
|
{
|
|
try
|
|
{
|
|
var url = $"{BaseUrl}/search/album?q={Uri.EscapeDataString(query)}&limit={limit}";
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode) return new List<Album>();
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var result = JsonDocument.Parse(json);
|
|
|
|
var albums = new List<Album>();
|
|
if (result.RootElement.TryGetProperty("data", out var data))
|
|
{
|
|
foreach (var album in data.EnumerateArray())
|
|
{
|
|
albums.Add(ParseDeezerAlbum(album));
|
|
}
|
|
}
|
|
|
|
return albums;
|
|
}
|
|
catch
|
|
{
|
|
return new List<Album>();
|
|
}
|
|
}
|
|
|
|
public async Task<List<Artist>> SearchArtistsAsync(string query, int limit = 20)
|
|
{
|
|
try
|
|
{
|
|
var url = $"{BaseUrl}/search/artist?q={Uri.EscapeDataString(query)}&limit={limit}";
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode) return new List<Artist>();
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var result = JsonDocument.Parse(json);
|
|
|
|
var artists = new List<Artist>();
|
|
if (result.RootElement.TryGetProperty("data", out var data))
|
|
{
|
|
foreach (var artist in data.EnumerateArray())
|
|
{
|
|
artists.Add(ParseDeezerArtist(artist));
|
|
}
|
|
}
|
|
|
|
return artists;
|
|
}
|
|
catch
|
|
{
|
|
return new List<Artist>();
|
|
}
|
|
}
|
|
|
|
public async Task<SearchResult> SearchAllAsync(string query, int songLimit = 20, int albumLimit = 20, int artistLimit = 20)
|
|
{
|
|
// Execute searches in parallel
|
|
var songsTask = SearchSongsAsync(query, songLimit);
|
|
var albumsTask = SearchAlbumsAsync(query, albumLimit);
|
|
var artistsTask = SearchArtistsAsync(query, artistLimit);
|
|
|
|
await Task.WhenAll(songsTask, albumsTask, artistsTask);
|
|
|
|
return new SearchResult
|
|
{
|
|
Songs = await songsTask,
|
|
Albums = await albumsTask,
|
|
Artists = await artistsTask
|
|
};
|
|
}
|
|
|
|
public async Task<Song?> GetSongAsync(string externalProvider, string externalId)
|
|
{
|
|
if (externalProvider != "deezer") return null;
|
|
|
|
var url = $"{BaseUrl}/track/{externalId}";
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode) return null;
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var track = JsonDocument.Parse(json).RootElement;
|
|
|
|
if (track.TryGetProperty("error", out _)) return null;
|
|
|
|
// For an individual track, get full metadata
|
|
var song = ParseDeezerTrackFull(track);
|
|
|
|
// Get additional info from album (genre, total track count, label, copyright)
|
|
if (track.TryGetProperty("album", out var albumRef) &&
|
|
albumRef.TryGetProperty("id", out var albumIdEl))
|
|
{
|
|
var albumId = albumIdEl.GetInt64().ToString();
|
|
try
|
|
{
|
|
var albumUrl = $"{BaseUrl}/album/{albumId}";
|
|
var albumResponse = await _httpClient.GetAsync(albumUrl);
|
|
if (albumResponse.IsSuccessStatusCode)
|
|
{
|
|
var albumJson = await albumResponse.Content.ReadAsStringAsync();
|
|
var albumData = JsonDocument.Parse(albumJson).RootElement;
|
|
|
|
// Genre
|
|
if (albumData.TryGetProperty("genres", out var genres) &&
|
|
genres.TryGetProperty("data", out var genresData) &&
|
|
genresData.GetArrayLength() > 0 &&
|
|
genresData[0].TryGetProperty("name", out var genreName))
|
|
{
|
|
song.Genre = genreName.GetString();
|
|
}
|
|
|
|
// Total track count
|
|
if (albumData.TryGetProperty("nb_tracks", out var nbTracks))
|
|
{
|
|
song.TotalTracks = nbTracks.GetInt32();
|
|
}
|
|
|
|
// Label
|
|
if (albumData.TryGetProperty("label", out var label))
|
|
{
|
|
song.Label = label.GetString();
|
|
}
|
|
|
|
// Cover art XL if not already set
|
|
if (string.IsNullOrEmpty(song.CoverArtUrlLarge))
|
|
{
|
|
if (albumData.TryGetProperty("cover_xl", out var coverXl))
|
|
{
|
|
song.CoverArtUrlLarge = coverXl.GetString();
|
|
}
|
|
else if (albumData.TryGetProperty("cover_big", out var coverBig))
|
|
{
|
|
song.CoverArtUrlLarge = coverBig.GetString();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// If we can't get the album, continue with track info only
|
|
}
|
|
}
|
|
|
|
return song;
|
|
}
|
|
|
|
public async Task<Album?> GetAlbumAsync(string externalProvider, string externalId)
|
|
{
|
|
if (externalProvider != "deezer") return null;
|
|
|
|
var url = $"{BaseUrl}/album/{externalId}";
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode) return null;
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var albumElement = JsonDocument.Parse(json).RootElement;
|
|
|
|
if (albumElement.TryGetProperty("error", out _)) return null;
|
|
|
|
var album = ParseDeezerAlbum(albumElement);
|
|
|
|
// Get album songs
|
|
if (albumElement.TryGetProperty("tracks", out var tracks) &&
|
|
tracks.TryGetProperty("data", out var tracksData))
|
|
{
|
|
int trackIndex = 1;
|
|
foreach (var track in tracksData.EnumerateArray())
|
|
{
|
|
// Pass the album artist to ensure proper folder organization
|
|
var song = ParseDeezerTrack(track, trackIndex, album.Artist);
|
|
|
|
// Ensure album metadata is set (tracks in album response may not have full album object)
|
|
song.Album = album.Title;
|
|
song.AlbumId = album.Id;
|
|
song.AlbumArtist = album.Artist;
|
|
|
|
if (ShouldIncludeSong(song))
|
|
{
|
|
album.Songs.Add(song);
|
|
}
|
|
trackIndex++;
|
|
}
|
|
}
|
|
|
|
return album;
|
|
}
|
|
|
|
public async Task<Artist?> GetArtistAsync(string externalProvider, string externalId)
|
|
{
|
|
if (externalProvider != "deezer") return null;
|
|
|
|
var url = $"{BaseUrl}/artist/{externalId}";
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode) return null;
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var artist = JsonDocument.Parse(json).RootElement;
|
|
|
|
if (artist.TryGetProperty("error", out _)) return null;
|
|
|
|
return ParseDeezerArtist(artist);
|
|
}
|
|
|
|
public async Task<List<Album>> GetArtistAlbumsAsync(string externalProvider, string externalId)
|
|
{
|
|
if (externalProvider != "deezer") return new List<Album>();
|
|
|
|
var url = $"{BaseUrl}/artist/{externalId}/albums";
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode) return new List<Album>();
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var result = JsonDocument.Parse(json);
|
|
|
|
var albums = new List<Album>();
|
|
if (result.RootElement.TryGetProperty("data", out var data))
|
|
{
|
|
foreach (var album in data.EnumerateArray())
|
|
{
|
|
albums.Add(ParseDeezerAlbum(album));
|
|
}
|
|
}
|
|
|
|
return albums;
|
|
}
|
|
|
|
private Song ParseDeezerTrack(JsonElement track, int? fallbackTrackNumber = null, string? albumArtist = null)
|
|
{
|
|
var externalId = track.GetProperty("id").GetInt64().ToString();
|
|
|
|
// Try to get track_position from API, fallback to provided index
|
|
int? trackNumber = track.TryGetProperty("track_position", out var trackPos)
|
|
? trackPos.GetInt32()
|
|
: fallbackTrackNumber;
|
|
|
|
// Explicit content lyrics value
|
|
int? explicitContentLyrics = track.TryGetProperty("explicit_content_lyrics", out var ecl)
|
|
? ecl.GetInt32()
|
|
: null;
|
|
|
|
return new Song
|
|
{
|
|
Id = $"ext-deezer-song-{externalId}",
|
|
Title = track.GetProperty("title").GetString() ?? "",
|
|
Artist = track.TryGetProperty("artist", out var artist)
|
|
? artist.GetProperty("name").GetString() ?? ""
|
|
: "",
|
|
ArtistId = track.TryGetProperty("artist", out var artistForId)
|
|
? $"ext-deezer-artist-{artistForId.GetProperty("id").GetInt64()}"
|
|
: null,
|
|
Album = track.TryGetProperty("album", out var album)
|
|
? album.GetProperty("title").GetString() ?? ""
|
|
: "",
|
|
AlbumId = track.TryGetProperty("album", out var albumForId)
|
|
? $"ext-deezer-album-{albumForId.GetProperty("id").GetInt64()}"
|
|
: null,
|
|
Duration = track.TryGetProperty("duration", out var duration)
|
|
? duration.GetInt32()
|
|
: null,
|
|
Track = trackNumber,
|
|
CoverArtUrl = track.TryGetProperty("album", out var albumForCover) &&
|
|
albumForCover.TryGetProperty("cover_medium", out var cover)
|
|
? cover.GetString()
|
|
: null,
|
|
AlbumArtist = albumArtist,
|
|
IsLocal = false,
|
|
ExternalProvider = "deezer",
|
|
ExternalId = externalId,
|
|
ExplicitContentLyrics = explicitContentLyrics
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses a Deezer track with all available metadata
|
|
/// Used for GetSongAsync which returns complete data
|
|
/// </summary>
|
|
private Song ParseDeezerTrackFull(JsonElement track)
|
|
{
|
|
var externalId = track.GetProperty("id").GetInt64().ToString();
|
|
|
|
// Track position et disc number
|
|
int? trackNumber = track.TryGetProperty("track_position", out var trackPos)
|
|
? trackPos.GetInt32()
|
|
: null;
|
|
int? discNumber = track.TryGetProperty("disk_number", out var diskNum)
|
|
? diskNum.GetInt32()
|
|
: null;
|
|
|
|
// BPM
|
|
int? bpm = track.TryGetProperty("bpm", out var bpmVal) && bpmVal.ValueKind == JsonValueKind.Number
|
|
? (int)bpmVal.GetDouble()
|
|
: null;
|
|
|
|
// ISRC
|
|
string? isrc = track.TryGetProperty("isrc", out var isrcVal)
|
|
? isrcVal.GetString()
|
|
: null;
|
|
|
|
// Release date from album
|
|
string? releaseDate = null;
|
|
int? year = null;
|
|
if (track.TryGetProperty("release_date", out var relDate))
|
|
{
|
|
releaseDate = relDate.GetString();
|
|
if (!string.IsNullOrEmpty(releaseDate) && releaseDate.Length >= 4)
|
|
{
|
|
if (int.TryParse(releaseDate.Substring(0, 4), out var y))
|
|
year = y;
|
|
}
|
|
}
|
|
else if (track.TryGetProperty("album", out var albumForDate) &&
|
|
albumForDate.TryGetProperty("release_date", out var albumRelDate))
|
|
{
|
|
releaseDate = albumRelDate.GetString();
|
|
if (!string.IsNullOrEmpty(releaseDate) && releaseDate.Length >= 4)
|
|
{
|
|
if (int.TryParse(releaseDate.Substring(0, 4), out var y))
|
|
year = y;
|
|
}
|
|
}
|
|
|
|
// Contributors
|
|
var contributors = new List<string>();
|
|
if (track.TryGetProperty("contributors", out var contribs))
|
|
{
|
|
foreach (var contrib in contribs.EnumerateArray())
|
|
{
|
|
if (contrib.TryGetProperty("name", out var contribName))
|
|
{
|
|
var name = contribName.GetString();
|
|
if (!string.IsNullOrEmpty(name))
|
|
contributors.Add(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Album artist (first artist from album, or main track artist)
|
|
string? albumArtist = null;
|
|
if (track.TryGetProperty("album", out var albumForArtist) &&
|
|
albumForArtist.TryGetProperty("artist", out var albumArtistEl))
|
|
{
|
|
albumArtist = albumArtistEl.TryGetProperty("name", out var aName)
|
|
? aName.GetString()
|
|
: null;
|
|
}
|
|
|
|
// Cover art URLs (different sizes)
|
|
string? coverMedium = null;
|
|
string? coverLarge = null;
|
|
if (track.TryGetProperty("album", out var albumForCover))
|
|
{
|
|
coverMedium = albumForCover.TryGetProperty("cover_medium", out var cm)
|
|
? cm.GetString()
|
|
: null;
|
|
coverLarge = albumForCover.TryGetProperty("cover_xl", out var cxl)
|
|
? cxl.GetString()
|
|
: (albumForCover.TryGetProperty("cover_big", out var cb) ? cb.GetString() : null);
|
|
}
|
|
|
|
// Explicit content lyrics value
|
|
int? explicitContentLyrics = track.TryGetProperty("explicit_content_lyrics", out var ecl)
|
|
? ecl.GetInt32()
|
|
: null;
|
|
|
|
return new Song
|
|
{
|
|
Id = $"ext-deezer-song-{externalId}",
|
|
Title = track.GetProperty("title").GetString() ?? "",
|
|
Artist = track.TryGetProperty("artist", out var artist)
|
|
? artist.GetProperty("name").GetString() ?? ""
|
|
: "",
|
|
ArtistId = track.TryGetProperty("artist", out var artistForId)
|
|
? $"ext-deezer-artist-{artistForId.GetProperty("id").GetInt64()}"
|
|
: null,
|
|
Album = track.TryGetProperty("album", out var album)
|
|
? album.GetProperty("title").GetString() ?? ""
|
|
: "",
|
|
AlbumId = track.TryGetProperty("album", out var albumForId)
|
|
? $"ext-deezer-album-{albumForId.GetProperty("id").GetInt64()}"
|
|
: null,
|
|
Duration = track.TryGetProperty("duration", out var duration)
|
|
? duration.GetInt32()
|
|
: null,
|
|
Track = trackNumber,
|
|
DiscNumber = discNumber,
|
|
Year = year,
|
|
Bpm = bpm,
|
|
Isrc = isrc,
|
|
ReleaseDate = releaseDate,
|
|
AlbumArtist = albumArtist,
|
|
Contributors = contributors,
|
|
CoverArtUrl = coverMedium,
|
|
CoverArtUrlLarge = coverLarge,
|
|
IsLocal = false,
|
|
ExternalProvider = "deezer",
|
|
ExternalId = externalId,
|
|
ExplicitContentLyrics = explicitContentLyrics
|
|
};
|
|
}
|
|
|
|
private Album ParseDeezerAlbum(JsonElement album)
|
|
{
|
|
var externalId = album.GetProperty("id").GetInt64().ToString();
|
|
|
|
return new Album
|
|
{
|
|
Id = $"ext-deezer-album-{externalId}",
|
|
Title = album.GetProperty("title").GetString() ?? "",
|
|
Artist = album.TryGetProperty("artist", out var artist)
|
|
? artist.GetProperty("name").GetString() ?? ""
|
|
: "",
|
|
ArtistId = album.TryGetProperty("artist", out var artistForId)
|
|
? $"ext-deezer-artist-{artistForId.GetProperty("id").GetInt64()}"
|
|
: null,
|
|
Year = album.TryGetProperty("release_date", out var releaseDate)
|
|
? int.TryParse(releaseDate.GetString()?.Split('-')[0], out var year) ? year : null
|
|
: null,
|
|
SongCount = album.TryGetProperty("nb_tracks", out var nbTracks)
|
|
? nbTracks.GetInt32()
|
|
: null,
|
|
CoverArtUrl = album.TryGetProperty("cover_medium", out var cover)
|
|
? cover.GetString()
|
|
: null,
|
|
Genre = album.TryGetProperty("genres", out var genres) &&
|
|
genres.TryGetProperty("data", out var genresData) &&
|
|
genresData.GetArrayLength() > 0
|
|
? genresData[0].GetProperty("name").GetString()
|
|
: null,
|
|
IsLocal = false,
|
|
ExternalProvider = "deezer",
|
|
ExternalId = externalId
|
|
};
|
|
}
|
|
|
|
private Artist ParseDeezerArtist(JsonElement artist)
|
|
{
|
|
var externalId = artist.GetProperty("id").GetInt64().ToString();
|
|
|
|
return new Artist
|
|
{
|
|
Id = $"ext-deezer-artist-{externalId}",
|
|
Name = artist.GetProperty("name").GetString() ?? "",
|
|
ImageUrl = artist.TryGetProperty("picture_medium", out var picture)
|
|
? picture.GetString()
|
|
: null,
|
|
AlbumCount = artist.TryGetProperty("nb_album", out var nbAlbum)
|
|
? nbAlbum.GetInt32()
|
|
: null,
|
|
IsLocal = false,
|
|
ExternalProvider = "deezer",
|
|
ExternalId = externalId
|
|
};
|
|
}
|
|
|
|
public async Task<List<ExternalPlaylist>> SearchPlaylistsAsync(string query, int limit = 20)
|
|
{
|
|
try
|
|
{
|
|
var url = $"{BaseUrl}/search/playlist?q={Uri.EscapeDataString(query)}&limit={limit}";
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode) return new List<ExternalPlaylist>();
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var result = JsonDocument.Parse(json);
|
|
|
|
var playlists = new List<ExternalPlaylist>();
|
|
if (result.RootElement.TryGetProperty("data", out var data))
|
|
{
|
|
foreach (var playlist in data.EnumerateArray())
|
|
{
|
|
playlists.Add(ParseDeezerPlaylist(playlist));
|
|
}
|
|
}
|
|
|
|
return playlists;
|
|
}
|
|
catch
|
|
{
|
|
return new List<ExternalPlaylist>();
|
|
}
|
|
}
|
|
|
|
public async Task<ExternalPlaylist?> GetPlaylistAsync(string externalProvider, string externalId)
|
|
{
|
|
if (externalProvider != "deezer") return null;
|
|
|
|
try
|
|
{
|
|
var url = $"{BaseUrl}/playlist/{externalId}";
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode) return null;
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var playlistElement = JsonDocument.Parse(json).RootElement;
|
|
|
|
if (playlistElement.TryGetProperty("error", out _)) return null;
|
|
|
|
return ParseDeezerPlaylist(playlistElement);
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public async Task<List<Song>> GetPlaylistTracksAsync(string externalProvider, string externalId)
|
|
{
|
|
if (externalProvider != "deezer") return new List<Song>();
|
|
|
|
try
|
|
{
|
|
var url = $"{BaseUrl}/playlist/{externalId}";
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode) return new List<Song>();
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var playlistElement = JsonDocument.Parse(json).RootElement;
|
|
|
|
if (playlistElement.TryGetProperty("error", out _)) return new List<Song>();
|
|
|
|
var songs = new List<Song>();
|
|
|
|
// Get playlist name for album field
|
|
var playlistName = playlistElement.TryGetProperty("title", out var titleEl)
|
|
? titleEl.GetString() ?? "Unknown Playlist"
|
|
: "Unknown Playlist";
|
|
|
|
if (playlistElement.TryGetProperty("tracks", out var tracks) &&
|
|
tracks.TryGetProperty("data", out var tracksData))
|
|
{
|
|
int trackIndex = 1;
|
|
foreach (var track in tracksData.EnumerateArray())
|
|
{
|
|
// For playlists, use the track's own artist (not a single album artist)
|
|
var song = ParseDeezerTrack(track, trackIndex);
|
|
|
|
// Override album name to be the playlist name
|
|
song.Album = playlistName;
|
|
|
|
if (ShouldIncludeSong(song))
|
|
{
|
|
songs.Add(song);
|
|
}
|
|
trackIndex++;
|
|
}
|
|
}
|
|
|
|
return songs;
|
|
}
|
|
catch
|
|
{
|
|
return new List<Song>();
|
|
}
|
|
}
|
|
|
|
private ExternalPlaylist ParseDeezerPlaylist(JsonElement playlist)
|
|
{
|
|
var externalId = playlist.GetProperty("id").GetInt64().ToString();
|
|
|
|
// Get curator/creator name
|
|
string? curatorName = null;
|
|
if (playlist.TryGetProperty("user", out var user) &&
|
|
user.TryGetProperty("name", out var userName))
|
|
{
|
|
curatorName = userName.GetString();
|
|
}
|
|
else if (playlist.TryGetProperty("creator", out var creator) &&
|
|
creator.TryGetProperty("name", out var creatorName))
|
|
{
|
|
curatorName = creatorName.GetString();
|
|
}
|
|
|
|
// Get creation date
|
|
DateTime? createdDate = null;
|
|
if (playlist.TryGetProperty("creation_date", out var creationDateEl))
|
|
{
|
|
var dateStr = creationDateEl.GetString();
|
|
if (!string.IsNullOrEmpty(dateStr) && DateTime.TryParse(dateStr, out var date))
|
|
{
|
|
createdDate = date;
|
|
}
|
|
}
|
|
|
|
return new ExternalPlaylist
|
|
{
|
|
Id = Common.PlaylistIdHelper.CreatePlaylistId("deezer", externalId),
|
|
Name = playlist.GetProperty("title").GetString() ?? "",
|
|
Description = playlist.TryGetProperty("description", out var desc)
|
|
? desc.GetString()
|
|
: null,
|
|
CuratorName = curatorName,
|
|
Provider = "deezer",
|
|
ExternalId = externalId,
|
|
TrackCount = playlist.TryGetProperty("nb_tracks", out var nbTracks)
|
|
? nbTracks.GetInt32()
|
|
: 0,
|
|
Duration = playlist.TryGetProperty("duration", out var duration)
|
|
? duration.GetInt32()
|
|
: 0,
|
|
CoverUrl = playlist.TryGetProperty("picture_medium", out var picture)
|
|
? picture.GetString()
|
|
: (playlist.TryGetProperty("picture_big", out var pictureBig)
|
|
? pictureBig.GetString()
|
|
: null),
|
|
CreatedDate = createdDate
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether a song should be included based on the explicit content filter setting
|
|
/// </summary>
|
|
/// <param name="song">The song to check</param>
|
|
/// <returns>True if the song should be included, false otherwise</returns>
|
|
private bool ShouldIncludeSong(Song song)
|
|
{
|
|
// If no explicit content info, include the song
|
|
if (song.ExplicitContentLyrics == null)
|
|
return true;
|
|
|
|
return _settings.ExplicitFilter switch
|
|
{
|
|
// All: No filtering, include everything
|
|
ExplicitFilter.All => true,
|
|
|
|
// ExplicitOnly: Exclude clean/edited versions (value 3)
|
|
// Include: 0 (naturally clean), 1 (explicit), 2 (not applicable), 6/7 (unknown)
|
|
ExplicitFilter.ExplicitOnly => song.ExplicitContentLyrics != 3,
|
|
|
|
// CleanOnly: Only show clean content
|
|
// Include: 0 (naturally clean), 3 (clean/edited version)
|
|
// Exclude: 1 (explicit)
|
|
ExplicitFilter.CleanOnly => song.ExplicitContentLyrics != 1,
|
|
|
|
_ => true
|
|
};
|
|
}
|
|
}
|