mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-10 07:58:39 -05:00
feat: playlist implementation
This commit is contained in:
@@ -4,6 +4,7 @@ using octo_fiesta.Models.Download;
|
||||
using octo_fiesta.Models.Search;
|
||||
using octo_fiesta.Models.Subsonic;
|
||||
using octo_fiesta.Services.Local;
|
||||
using octo_fiesta.Services.Subsonic;
|
||||
using TagLib;
|
||||
using IOFile = System.IO.File;
|
||||
|
||||
@@ -21,6 +22,7 @@ public abstract class BaseDownloadService : IDownloadService
|
||||
protected readonly IMusicMetadataService MetadataService;
|
||||
protected readonly SubsonicSettings SubsonicSettings;
|
||||
protected readonly ILogger Logger;
|
||||
protected readonly IServiceProvider ServiceProvider;
|
||||
|
||||
protected readonly string DownloadPath;
|
||||
protected readonly string CachePath;
|
||||
@@ -38,12 +40,14 @@ public abstract class BaseDownloadService : IDownloadService
|
||||
ILocalLibraryService localLibraryService,
|
||||
IMusicMetadataService metadataService,
|
||||
SubsonicSettings subsonicSettings,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger logger)
|
||||
{
|
||||
Configuration = configuration;
|
||||
LocalLibraryService = localLibraryService;
|
||||
MetadataService = metadataService;
|
||||
SubsonicSettings = subsonicSettings;
|
||||
ServiceProvider = serviceProvider;
|
||||
Logger = logger;
|
||||
|
||||
DownloadPath = configuration["Library:DownloadPath"] ?? "./downloads";
|
||||
@@ -79,6 +83,30 @@ public abstract class BaseDownloadService : IDownloadService
|
||||
return info;
|
||||
}
|
||||
|
||||
public async Task<string?> GetLocalPathIfExistsAsync(string externalProvider, string externalId)
|
||||
{
|
||||
if (externalProvider != ProviderName)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check local library
|
||||
var localPath = await LocalLibraryService.GetLocalPathForExternalSongAsync(externalProvider, externalId);
|
||||
if (localPath != null && IOFile.Exists(localPath))
|
||||
{
|
||||
return localPath;
|
||||
}
|
||||
|
||||
// Check cache directory
|
||||
var cachedPath = GetCachedFilePath(externalProvider, externalId);
|
||||
if (cachedPath != null && IOFile.Exists(cachedPath))
|
||||
{
|
||||
return cachedPath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract Task<bool> IsAvailableAsync();
|
||||
|
||||
public void DownloadRemainingAlbumTracksInBackground(string externalProvider, string albumExternalId, string excludeTrackExternalId)
|
||||
@@ -240,6 +268,25 @@ public abstract class BaseDownloadService : IDownloadService
|
||||
|
||||
song.LocalPath = localPath;
|
||||
|
||||
// Check if this track belongs to a playlist and update M3U
|
||||
try
|
||||
{
|
||||
var playlistSyncService = ServiceProvider.GetService(typeof(PlaylistSyncService)) as PlaylistSyncService;
|
||||
if (playlistSyncService != null)
|
||||
{
|
||||
var playlistId = playlistSyncService.GetPlaylistIdForTrack(songId);
|
||||
if (playlistId != null)
|
||||
{
|
||||
Logger.LogInformation("Track {SongId} belongs to playlist {PlaylistId}, adding to M3U", songId, playlistId);
|
||||
await playlistSyncService.AddTrackToM3UAsync(playlistId, song, localPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Failed to update playlist M3U for track {SongId}", songId);
|
||||
}
|
||||
|
||||
// Only register and scan if NOT in cache mode
|
||||
if (!isCache)
|
||||
{
|
||||
|
||||
76
octo-fiesta/Services/Common/PlaylistIdHelper.cs
Normal file
76
octo-fiesta/Services/Common/PlaylistIdHelper.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
namespace octo_fiesta.Services.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for handling external playlist IDs.
|
||||
/// Playlist IDs use the format: "pl-{provider}-{externalId}"
|
||||
/// Example: "pl-deezer-123456", "pl-qobuz-789"
|
||||
/// </summary>
|
||||
public static class PlaylistIdHelper
|
||||
{
|
||||
private const string PlaylistPrefix = "pl-";
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an ID represents an external playlist.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID to check</param>
|
||||
/// <returns>True if the ID starts with "pl-", false otherwise</returns>
|
||||
public static bool IsExternalPlaylist(string? id)
|
||||
{
|
||||
return !string.IsNullOrEmpty(id) && id.StartsWith(PlaylistPrefix, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a playlist ID to extract provider and external ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The playlist ID in format "pl-{provider}-{externalId}"</param>
|
||||
/// <returns>A tuple containing (provider, externalId)</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if the ID format is invalid</exception>
|
||||
public static (string provider, string externalId) ParsePlaylistId(string id)
|
||||
{
|
||||
if (!IsExternalPlaylist(id))
|
||||
{
|
||||
throw new ArgumentException($"Invalid playlist ID format. Expected 'pl-{{provider}}-{{externalId}}', got '{id}'", nameof(id));
|
||||
}
|
||||
|
||||
// Remove "pl-" prefix
|
||||
var withoutPrefix = id.Substring(PlaylistPrefix.Length);
|
||||
|
||||
// Split by first dash to get provider and externalId
|
||||
var dashIndex = withoutPrefix.IndexOf('-');
|
||||
if (dashIndex == -1)
|
||||
{
|
||||
throw new ArgumentException($"Invalid playlist ID format. Expected 'pl-{{provider}}-{{externalId}}', got '{id}'", nameof(id));
|
||||
}
|
||||
|
||||
var provider = withoutPrefix.Substring(0, dashIndex);
|
||||
var externalId = withoutPrefix.Substring(dashIndex + 1);
|
||||
|
||||
if (string.IsNullOrEmpty(provider) || string.IsNullOrEmpty(externalId))
|
||||
{
|
||||
throw new ArgumentException($"Invalid playlist ID format. Provider or external ID is empty in '{id}'", nameof(id));
|
||||
}
|
||||
|
||||
return (provider, externalId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a playlist ID from provider and external ID.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider name (e.g., "deezer", "qobuz")</param>
|
||||
/// <param name="externalId">The external ID from the provider</param>
|
||||
/// <returns>A playlist ID in format "pl-{provider}-{externalId}"</returns>
|
||||
public static string CreatePlaylistId(string provider, string externalId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(provider))
|
||||
{
|
||||
throw new ArgumentException("Provider cannot be null or empty", nameof(provider));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(externalId))
|
||||
{
|
||||
throw new ArgumentException("External ID cannot be null or empty", nameof(externalId));
|
||||
}
|
||||
|
||||
return $"{PlaylistPrefix}{provider.ToLowerInvariant()}-{externalId}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user