mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Add lyrics prefetch endpoint and UI button: prefetch lyrics for individual playlists with progress feedback
This commit is contained in:
@@ -38,6 +38,7 @@ public class AdminController : ControllerBase
|
|||||||
private readonly RedisCacheService _cache;
|
private readonly RedisCacheService _cache;
|
||||||
private readonly HttpClient _jellyfinHttpClient;
|
private readonly HttpClient _jellyfinHttpClient;
|
||||||
private readonly IWebHostEnvironment _environment;
|
private readonly IWebHostEnvironment _environment;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly string _envFilePath;
|
private readonly string _envFilePath;
|
||||||
private const string CacheDirectory = "/app/cache/spotify";
|
private const string CacheDirectory = "/app/cache/spotify";
|
||||||
|
|
||||||
@@ -56,6 +57,7 @@ public class AdminController : ControllerBase
|
|||||||
SpotifyPlaylistFetcher playlistFetcher,
|
SpotifyPlaylistFetcher playlistFetcher,
|
||||||
RedisCacheService cache,
|
RedisCacheService cache,
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
|
IServiceProvider serviceProvider,
|
||||||
SpotifyTrackMatchingService? matchingService = null)
|
SpotifyTrackMatchingService? matchingService = null)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -73,6 +75,7 @@ public class AdminController : ControllerBase
|
|||||||
_matchingService = matchingService;
|
_matchingService = matchingService;
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
_jellyfinHttpClient = httpClientFactory.CreateClient();
|
_jellyfinHttpClient = httpClientFactory.CreateClient();
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
|
||||||
// .env file path is always /app/.env in Docker (mounted from host)
|
// .env file path is always /app/.env in Docker (mounted from host)
|
||||||
// In development, it's in the parent directory of ContentRootPath
|
// In development, it's in the parent directory of ContentRootPath
|
||||||
@@ -2621,6 +2624,46 @@ public class AdminController : ControllerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prefetch lyrics for a specific playlist
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("playlists/{name}/prefetch-lyrics")]
|
||||||
|
public async Task<IActionResult> PrefetchPlaylistLyrics(string name)
|
||||||
|
{
|
||||||
|
var decodedName = Uri.UnescapeDataString(name);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var lyricsPrefetchService = _serviceProvider.GetService<allstarr.Services.Lyrics.LyricsPrefetchService>();
|
||||||
|
|
||||||
|
if (lyricsPrefetchService == null)
|
||||||
|
{
|
||||||
|
return StatusCode(500, new { error = "Lyrics prefetch service not available" });
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Starting lyrics prefetch for playlist: {Playlist}", decodedName);
|
||||||
|
|
||||||
|
var (fetched, cached, missing) = await lyricsPrefetchService.PrefetchPlaylistLyricsAsync(
|
||||||
|
decodedName,
|
||||||
|
HttpContext.RequestAborted);
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
message = "Lyrics prefetch complete",
|
||||||
|
playlist = decodedName,
|
||||||
|
fetched,
|
||||||
|
cached,
|
||||||
|
missing,
|
||||||
|
total = fetched + cached + missing
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to prefetch lyrics for playlist {Playlist}", decodedName);
|
||||||
|
return StatusCode(500, new { error = $"Failed to prefetch lyrics: {ex.Message}" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ public class LyricsPrefetchService : BackgroundService
|
|||||||
totalFetched, totalCached, totalMissing);
|
totalFetched, totalCached, totalMissing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(int Fetched, int Cached, int Missing)> PrefetchPlaylistLyricsAsync(
|
public async Task<(int Fetched, int Cached, int Missing)> PrefetchPlaylistLyricsAsync(
|
||||||
string playlistName,
|
string playlistName,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1274,6 +1274,7 @@
|
|||||||
<td class="cache-age">${p.cacheAge || '-'}</td>
|
<td class="cache-age">${p.cacheAge || '-'}</td>
|
||||||
<td>
|
<td>
|
||||||
<button onclick="matchPlaylistTracks('${escapeJs(p.name)}')">Match Tracks</button>
|
<button onclick="matchPlaylistTracks('${escapeJs(p.name)}')">Match Tracks</button>
|
||||||
|
<button onclick="prefetchLyrics('${escapeJs(p.name)}')">Prefetch Lyrics</button>
|
||||||
<button onclick="viewTracks('${escapeJs(p.name)}')">View</button>
|
<button onclick="viewTracks('${escapeJs(p.name)}')">View</button>
|
||||||
<button class="danger" onclick="removePlaylist('${escapeJs(p.name)}')">Remove</button>
|
<button class="danger" onclick="removePlaylist('${escapeJs(p.name)}')">Remove</button>
|
||||||
</td>
|
</td>
|
||||||
@@ -1568,6 +1569,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function prefetchLyrics(name) {
|
||||||
|
try {
|
||||||
|
showToast(`Prefetching lyrics for ${name}...`, 'info', 5000);
|
||||||
|
const res = await fetch(`/api/admin/playlists/${encodeURIComponent(name)}/prefetch-lyrics`, { method: 'POST' });
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
const summary = `Fetched: ${data.fetched}, Cached: ${data.cached}, Missing: ${data.missing}`;
|
||||||
|
showToast(`✓ Lyrics prefetch complete for ${name}. ${summary}`, 'success', 8000);
|
||||||
|
} else {
|
||||||
|
showToast(data.error || 'Failed to prefetch lyrics', 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showToast('Failed to prefetch lyrics', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function searchProvider(query, provider) {
|
function searchProvider(query, provider) {
|
||||||
// Provider-specific search URLs
|
// Provider-specific search URLs
|
||||||
const searchUrls = {
|
const searchUrls = {
|
||||||
|
|||||||
Reference in New Issue
Block a user