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
@@ -323,7 +323,8 @@ class PlaylistController extends Controller
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'video_id' => 'required|exists:videos,id',
|
||||
'video_id' => 'required|exists:videos,id',
|
||||
'audio_track_id' => 'nullable|integer|exists:video_audio_tracks,id',
|
||||
]);
|
||||
|
||||
$video = Video::findOrFail($request->video_id);
|
||||
@@ -333,7 +334,13 @@ class PlaylistController extends Controller
|
||||
abort(403, 'You cannot add this video to your playlist.');
|
||||
}
|
||||
|
||||
$added = $playlist->addVideo($video);
|
||||
// Track must actually belong to this video (avoid arbitrary FK pinning).
|
||||
$audioTrackId = $request->input('audio_track_id');
|
||||
if ($audioTrackId && ! $video->audioTracks()->whereKey($audioTrackId)->exists()) {
|
||||
$audioTrackId = null;
|
||||
}
|
||||
|
||||
$added = $playlist->addVideo($video, $audioTrackId ? (int) $audioTrackId : null);
|
||||
|
||||
if ($request->expectsJson() || $request->ajax()) {
|
||||
return response()->json([
|
||||
@@ -353,9 +360,30 @@ class PlaylistController extends Controller
|
||||
*/
|
||||
public function removeVideoByBody(Request $request, Playlist $playlist)
|
||||
{
|
||||
$request->validate(['video_id' => 'required|exists:videos,id']);
|
||||
$request->validate([
|
||||
'video_id' => 'required|exists:videos,id',
|
||||
'audio_track_id' => 'nullable|integer',
|
||||
]);
|
||||
|
||||
if (! $playlist->canEdit(Auth::user())) {
|
||||
abort(403, 'You do not have permission to edit this playlist.');
|
||||
}
|
||||
|
||||
$video = Video::findOrFail($request->video_id);
|
||||
return $this->removeVideo($request, $playlist, $video);
|
||||
$trackId = $request->filled('audio_track_id')
|
||||
? (int) $request->input('audio_track_id')
|
||||
: null;
|
||||
|
||||
$removed = $playlist->removeVideoWithTrack($video, $trackId);
|
||||
|
||||
if ($request->expectsJson() || $request->ajax()) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => $removed ? 'Video removed from playlist.' : 'Video was not in the playlist.',
|
||||
'video_count' => $playlist->video_count,
|
||||
]);
|
||||
}
|
||||
return back()->with('success', 'Video removed from playlist.');
|
||||
}
|
||||
|
||||
// Remove video from playlist
|
||||
@@ -431,6 +459,17 @@ class PlaylistController extends Controller
|
||||
'visibility' => $p->visibility,
|
||||
'thumbnail_url' => $p->thumbnail_url,
|
||||
'video_ids' => $p->videos()->pluck('videos.id')->toArray(),
|
||||
// Track-aware entries: each row = {video_id, audio_track_id or null}.
|
||||
// Consumers that need per-track granularity use this; older callers
|
||||
// can still rely on video_ids above.
|
||||
'entries' => \DB::table('playlist_videos')
|
||||
->where('playlist_id', $p->id)
|
||||
->select('video_id', 'audio_track_id')
|
||||
->get()
|
||||
->map(fn ($r) => [
|
||||
'video_id' => (int) $r->video_id,
|
||||
'audio_track_id' => $r->audio_track_id ? (int) $r->audio_track_id : null,
|
||||
])->all(),
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user