mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
feat: add per-playlist cron sync schedules
Each playlist now has its own cron schedule for syncing with Spotify. Default is 0 8 * * 1 (Monday 8 AM). Removed global MatchingIntervalHours in favor of per-playlist scheduling.
This commit is contained in:
@@ -2274,12 +2274,19 @@ public class AdminController : ControllerBase
|
|||||||
Name = request.Name,
|
Name = request.Name,
|
||||||
Id = request.SpotifyPlaylistId,
|
Id = request.SpotifyPlaylistId,
|
||||||
JellyfinId = jellyfinPlaylistId,
|
JellyfinId = jellyfinPlaylistId,
|
||||||
LocalTracksPosition = LocalTracksPosition.First // Use Spotify order
|
LocalTracksPosition = LocalTracksPosition.First, // Use Spotify order
|
||||||
|
SyncSchedule = request.SyncSchedule ?? "0 8 * * 1" // Default to Monday 8 AM
|
||||||
});
|
});
|
||||||
|
|
||||||
// Convert to JSON format for env var: [["Name","SpotifyId","JellyfinId","first|last"],...]
|
// Convert to JSON format for env var: [["Name","SpotifyId","JellyfinId","first|last","cronSchedule"],...]
|
||||||
var playlistsJson = JsonSerializer.Serialize(
|
var playlistsJson = JsonSerializer.Serialize(
|
||||||
currentPlaylists.Select(p => new[] { p.Name, p.Id, p.JellyfinId, p.LocalTracksPosition.ToString().ToLower() }).ToArray()
|
currentPlaylists.Select(p => new[] {
|
||||||
|
p.Name,
|
||||||
|
p.Id,
|
||||||
|
p.JellyfinId,
|
||||||
|
p.LocalTracksPosition.ToString().ToLower(),
|
||||||
|
p.SyncSchedule ?? "0 8 * * 1"
|
||||||
|
}).ToArray()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update .env file
|
// Update .env file
|
||||||
@@ -2335,7 +2342,7 @@ public class AdminController : ControllerBase
|
|||||||
return playlists;
|
return playlists;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse JSON array format: [["Name","SpotifyId","JellyfinId","first|last"],...]
|
// Parse JSON array format: [["Name","SpotifyId","JellyfinId","first|last","cronSchedule"],...]
|
||||||
var playlistArrays = JsonSerializer.Deserialize<string[][]>(value);
|
var playlistArrays = JsonSerializer.Deserialize<string[][]>(value);
|
||||||
if (playlistArrays != null)
|
if (playlistArrays != null)
|
||||||
{
|
{
|
||||||
@@ -2351,7 +2358,8 @@ public class AdminController : ControllerBase
|
|||||||
LocalTracksPosition = arr.Length >= 4 &&
|
LocalTracksPosition = arr.Length >= 4 &&
|
||||||
arr[3].Trim().Equals("last", StringComparison.OrdinalIgnoreCase)
|
arr[3].Trim().Equals("last", StringComparison.OrdinalIgnoreCase)
|
||||||
? LocalTracksPosition.Last
|
? LocalTracksPosition.Last
|
||||||
: LocalTracksPosition.First
|
: LocalTracksPosition.First,
|
||||||
|
SyncSchedule = arr.Length >= 5 ? arr[4].Trim() : "0 8 * * 1"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3406,6 +3414,7 @@ public class LinkPlaylistRequest
|
|||||||
{
|
{
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string SpotifyPlaylistId { get; set; } = string.Empty;
|
public string SpotifyPlaylistId { get; set; } = string.Empty;
|
||||||
|
public string SyncSchedule { get; set; } = "0 8 * * 1"; // Default: 8 AM every Monday
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -45,6 +45,14 @@ public class SpotifyPlaylistConfig
|
|||||||
/// Where to position local tracks: "first" or "last"
|
/// Where to position local tracks: "first" or "last"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LocalTracksPosition LocalTracksPosition { get; set; } = LocalTracksPosition.First;
|
public LocalTracksPosition LocalTracksPosition { get; set; } = LocalTracksPosition.First;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cron schedule for syncing this playlist with Spotify
|
||||||
|
/// Format: minute hour day month dayofweek
|
||||||
|
/// Example: "0 8 * * 1" = 8 AM every Monday
|
||||||
|
/// Default: "0 8 * * 1" (weekly on Monday at 8 AM)
|
||||||
|
/// </summary>
|
||||||
|
public string SyncSchedule { get; set; } = "0 8 * * 1";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1254,6 +1254,17 @@
|
|||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Sync Schedule -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Sync Schedule (Cron)</label>
|
||||||
|
<input type="text" id="link-sync-schedule" placeholder="0 8 * * 1" value="0 8 * * 1" style="font-family: monospace;">
|
||||||
|
<small style="color: var(--text-secondary); display: block; margin-top: 4px;">
|
||||||
|
Cron format: <code>minute hour day month dayofweek</code><br>
|
||||||
|
Default: <code>0 8 * * 1</code> = 8 AM every Monday<br>
|
||||||
|
Examples: <code>0 6 * * *</code> = daily at 6 AM, <code>0 20 * * 5</code> = Fridays at 8 PM
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button onclick="closeModal('link-playlist-modal')">Cancel</button>
|
<button onclick="closeModal('link-playlist-modal')">Cancel</button>
|
||||||
<button class="primary" onclick="linkPlaylist()">Link Playlist</button>
|
<button class="primary" onclick="linkPlaylist()">Link Playlist</button>
|
||||||
@@ -2080,6 +2091,19 @@
|
|||||||
async function linkPlaylist() {
|
async function linkPlaylist() {
|
||||||
const jellyfinId = document.getElementById('link-jellyfin-id').value;
|
const jellyfinId = document.getElementById('link-jellyfin-id').value;
|
||||||
const name = document.getElementById('link-jellyfin-name').value;
|
const name = document.getElementById('link-jellyfin-name').value;
|
||||||
|
const syncSchedule = document.getElementById('link-sync-schedule').value.trim();
|
||||||
|
|
||||||
|
// Validate sync schedule (basic cron format check)
|
||||||
|
if (!syncSchedule) {
|
||||||
|
showToast('Sync schedule is required', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cronParts = syncSchedule.split(/\s+/);
|
||||||
|
if (cronParts.length !== 5) {
|
||||||
|
showToast('Invalid cron format. Expected: minute hour day month dayofweek', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Get Spotify ID based on current mode
|
// Get Spotify ID based on current mode
|
||||||
let spotifyId = '';
|
let spotifyId = '';
|
||||||
@@ -2119,7 +2143,11 @@
|
|||||||
const res = await fetch(`/api/admin/jellyfin/playlists/${encodeURIComponent(jellyfinId)}/link`, {
|
const res = await fetch(`/api/admin/jellyfin/playlists/${encodeURIComponent(jellyfinId)}/link`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ name, spotifyPlaylistId: cleanSpotifyId })
|
body: JSON.stringify({
|
||||||
|
name,
|
||||||
|
spotifyPlaylistId: cleanSpotifyId,
|
||||||
|
syncSchedule: syncSchedule
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|||||||
Reference in New Issue
Block a user