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:
ghassanandClaude Opus 4.7 committed 2026-07-31 02:42:25 +03:00
1 parent af276f2cd1
commit ba8ab11ce4
12 files changed
+547 -49

No files matched your search

+26 -2
View File
@@ -67,7 +67,9 @@ class Video extends Model
public function audioTracks()
{
return $this->hasMany(\App\Models\VideoAudioTrack::class)->orderBy('id');
return $this->hasMany(\App\Models\VideoAudioTrack::class)
->orderBy('position')
->orderBy('id');
}
public function hasSlideshow(): bool
@@ -252,7 +254,29 @@ class Video extends Model
if ($realId === null || $realId <= 0) {
abort(404);
}
return $this->where('id', $realId)->firstOrFail();
$video = $this->where('id', $realId)->first();
if ($video) {
return $video;
}
// Video not found — check the merge-redirect table so old links still work.
$redirect = \DB::table('video_redirects')->where('source_id', $realId)->first();
if ($redirect) {
$target = static::find($redirect->target_id);
if ($target) {
$newValue = self::encodeId($target->id);
$newUrl = preg_replace(
'#/'.preg_quote((string) $value, '#').'(?=/|\?|$)#',
'/'.$newValue,
request()->fullUrl(),
1
);
throw new \Illuminate\Http\Exceptions\HttpResponseException(
redirect()->to($newUrl, 302)
);
}
}
abort(404);
}
// All share URLs use the unguessable token route