Chunked video upload, persistent track order, per-track playlist entries
Chunked uploads: bypass Cloudflare's 100 MB request cap by slicing the video/audio into 8 MB chunks and reassembling server-side before the normal store flow runs. Track reorder: audio tracks gain a `position` column so a user's edit- modal ordering survives reload. `Video::audioTracks()` now orders by position, `promote_track_id` remembers the swapped row, and freshly uploaded tracks append to the end. Playlist per-track pinning: `playlist_videos.audio_track_id` remembers which language the user pinned when adding a song. Same song with a different track becomes a separate row (Spotify-style duplicates). Player-data exposes `active_stream_url` + `active_track_id`, sidebar cards render `?track=` from the pivot, and both `plTransitionTo` and initial-load code activate the corresponding language option. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
1 parent
af276f2cd1
commit
ba8ab11ce4
12 files changed
+547
-49
No files matched your search
@@ -237,9 +237,22 @@
|
||||
.then(data => {
|
||||
if (data.success && data.playlists && data.playlists.length > 0) {
|
||||
let html = '';
|
||||
// Language-track the modal is scoped to (0 = primary).
|
||||
// Read once here so both the "already in?" check and the
|
||||
// toggle call use the same value.
|
||||
const currentTrackId = parseInt(window._ytpTrackId || 0);
|
||||
data.playlists.forEach(function(playlist) {
|
||||
const isInPlaylist = playlist.video_ids && playlist.video_ids.includes(parseInt(
|
||||
videoId));
|
||||
// Prefer entries[] (per-track granularity) when present.
|
||||
// Fall back to video_ids for older API responses.
|
||||
let isInPlaylist = false;
|
||||
if (Array.isArray(playlist.entries)) {
|
||||
isInPlaylist = playlist.entries.some(function (e) {
|
||||
return e.video_id === parseInt(videoId)
|
||||
&& (e.audio_track_id || 0) === currentTrackId;
|
||||
});
|
||||
} else if (playlist.video_ids) {
|
||||
isInPlaylist = playlist.video_ids.includes(parseInt(videoId));
|
||||
}
|
||||
const visibilityText = playlist.visibility === 'public' ? 'Public' : 'Private';
|
||||
const durationText = playlist.formatted_duration || '0m';
|
||||
html += `
|
||||
@@ -328,7 +341,11 @@
|
||||
// of the encoded route key used by GET /videos/{video}.
|
||||
var url = `/playlists/${playlistId}/videos`;
|
||||
var method = currentlyIn ? 'DELETE' : 'POST';
|
||||
var body = JSON.stringify({ video_id: videoId });
|
||||
// Pin the row to the currently-active audio track (0 = primary/null).
|
||||
var trackId = parseInt(window._ytpTrackId || 0);
|
||||
var payload = { video_id: videoId };
|
||||
if (trackId > 0) payload.audio_track_id = trackId;
|
||||
var body = JSON.stringify(payload);
|
||||
|
||||
fetch(url, {
|
||||
method,
|
||||
|
||||
Reference in new issue
Block a user