prematch on startup

This commit is contained in:
2026-02-02 12:31:09 -05:00
parent 82b480c47e
commit 4111b5228d
3 changed files with 79 additions and 20 deletions

View File

@@ -3096,6 +3096,54 @@ public class JellyfinController : ControllerBase
return Ok(results); return Ok(results);
} }
/// <summary>
/// Manually trigger track matching for all Spotify playlists.
/// GET /spotify/match?api_key=YOUR_KEY
/// </summary>
[HttpGet("spotify/match", Order = 1)]
[ServiceFilter(typeof(ApiKeyAuthFilter))]
public async Task<IActionResult> TriggerSpotifyMatch([FromServices] IEnumerable<IHostedService> hostedServices)
{
if (!_spotifySettings.Enabled)
{
return BadRequest(new { error = "Spotify Import is not enabled" });
}
_logger.LogInformation("Manual Spotify track matching triggered");
// Find the SpotifyTrackMatchingService
var matchingService = hostedServices
.OfType<allstarr.Services.Spotify.SpotifyTrackMatchingService>()
.FirstOrDefault();
if (matchingService == null)
{
return StatusCode(500, new { error = "SpotifyTrackMatchingService not found" });
}
// Trigger matching asynchronously
_ = Task.Run(async () =>
{
try
{
await matchingService.TriggerMatchingAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during manual track matching");
}
});
return Ok(new
{
status = "started",
message = "Track matching started in background. Check logs for progress.",
playlists = _spotifySettings.PlaylistNames.Count > 0
? _spotifySettings.PlaylistNames
: _spotifySettings.PlaylistIds
});
}
private List<allstarr.Models.Spotify.MissingTrack> ParseMissingTracksJson(string json) private List<allstarr.Models.Spotify.MissingTrack> ParseMissingTracksJson(string json)
{ {
var tracks = new List<allstarr.Models.Spotify.MissingTrack>(); var tracks = new List<allstarr.Models.Spotify.MissingTrack>();

View File

@@ -72,13 +72,10 @@ public class SpotifyMissingTracksFetcher : BackgroundService
} }
_logger.LogInformation("========================================"); _logger.LogInformation("========================================");
// Run once on startup if we haven't run in the last 24 hours // Always run once on startup to ensure we have missing tracks
if (!_hasRunOnce) if (!_hasRunOnce)
{ {
var shouldRunOnStartup = await ShouldRunOnStartupAsync(); _logger.LogInformation("Running initial fetch on startup");
if (shouldRunOnStartup)
{
_logger.LogInformation("Running initial fetch on startup (bypassing sync window check)");
try try
{ {
await FetchMissingTracksAsync(stoppingToken, bypassSyncWindowCheck: true); await FetchMissingTracksAsync(stoppingToken, bypassSyncWindowCheck: true);
@@ -89,12 +86,6 @@ public class SpotifyMissingTracksFetcher : BackgroundService
_logger.LogError(ex, "Error during startup fetch"); _logger.LogError(ex, "Error during startup fetch");
} }
} }
else
{
_logger.LogInformation("Skipping startup fetch - already have recent cache");
_hasRunOnce = true;
}
}
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
{ {

View File

@@ -44,6 +44,17 @@ public class SpotifyTrackMatchingService : BackgroundService
// Wait a bit for the fetcher to run first // Wait a bit for the fetcher to run first
await Task.Delay(TimeSpan.FromMinutes(2), stoppingToken); await Task.Delay(TimeSpan.FromMinutes(2), stoppingToken);
// Run once on startup to match any existing missing tracks
try
{
_logger.LogInformation("Running initial track matching on startup");
await MatchAllPlaylistsAsync(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during startup track matching");
}
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
{ {
try try
@@ -60,6 +71,15 @@ public class SpotifyTrackMatchingService : BackgroundService
} }
} }
/// <summary>
/// Public method to trigger matching manually (called from controller).
/// </summary>
public async Task TriggerMatchingAsync()
{
_logger.LogInformation("Manual track matching triggered");
await MatchAllPlaylistsAsync(CancellationToken.None);
}
private async Task MatchAllPlaylistsAsync(CancellationToken cancellationToken) private async Task MatchAllPlaylistsAsync(CancellationToken cancellationToken)
{ {
_logger.LogInformation("=== STARTING TRACK MATCHING ==="); _logger.LogInformation("=== STARTING TRACK MATCHING ===");
@@ -103,7 +123,7 @@ public class SpotifyTrackMatchingService : BackgroundService
var existingMatched = await _cache.GetAsync<List<Song>>(matchedTracksKey); var existingMatched = await _cache.GetAsync<List<Song>>(matchedTracksKey);
if (existingMatched != null && existingMatched.Count > 0) if (existingMatched != null && existingMatched.Count > 0)
{ {
_logger.LogDebug("Playlist {Playlist} already has {Count} matched tracks cached, skipping", _logger.LogInformation("Playlist {Playlist} already has {Count} matched tracks cached, skipping",
playlistName, existingMatched.Count); playlistName, existingMatched.Count);
return; return;
} }
@@ -112,7 +132,7 @@ public class SpotifyTrackMatchingService : BackgroundService
var missingTracks = await _cache.GetAsync<List<MissingTrack>>(missingTracksKey); var missingTracks = await _cache.GetAsync<List<MissingTrack>>(missingTracksKey);
if (missingTracks == null || missingTracks.Count == 0) if (missingTracks == null || missingTracks.Count == 0)
{ {
_logger.LogDebug("No missing tracks found for {Playlist}, skipping matching", playlistName); _logger.LogInformation("No missing tracks found for {Playlist}, skipping matching", playlistName);
return; return;
} }
@@ -158,7 +178,7 @@ public class SpotifyTrackMatchingService : BackgroundService
if (matchCount % 10 == 0) if (matchCount % 10 == 0)
{ {
_logger.LogDebug("Matched {Count}/{Total} tracks for {Playlist}", _logger.LogInformation("Matched {Count}/{Total} tracks for {Playlist}",
matchCount, missingTracks.Count, playlistName); matchCount, missingTracks.Count, playlistName);
} }
} }