From 9eb9535c83493d8ca9914c34391ebcee51bbf118 Mon Sep 17 00:00:00 2001 From: ghassan Date: Wed, 21 Oct 2026 10:20:44 +0300 Subject: [PATCH] fix(audio): dedupe language menu by (lang, label) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A video with two audio tracks sharing the same language (common when an upload was retried before the first attempt finished syncing) used to show both rows in the player's language picker, leaving the viewer guessing which one to pick. Dedupe keeps the first occurrence per (language, label) pair — so legit variants like "en — Original" and "en — Director's cut" still coexist, but bare duplicates collapse. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/Http/Controllers/VideoController.php | 28 +++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/app/Http/Controllers/VideoController.php b/app/Http/Controllers/VideoController.php index 9498dbd..0b1efd5 100644 --- a/app/Http/Controllers/VideoController.php +++ b/app/Http/Controllers/VideoController.php @@ -956,16 +956,24 @@ class VideoController extends Controller : $slideMap['0']; $allLangData = \App\Data\Languages::all(); - $audioTracks = $video->audioTracks->map(fn ($t) => [ - 'id' => $t->id, - 'language' => $t->language, - 'label' => $t->label, - 'flag' => $allLangData[$t->language]['flag'] ?? null, - 'stream_url' => route('videos.audio-track', ['video' => $video, 'track' => $t->id]) . '?v=' . $t->updated_at->timestamp, - 'title' => $t->title ?? '', - 'description' => $t->description ?? '', - 'dl_url' => route('videos.audio-track', ['video' => $video, 'track' => $t->id]) . '?download=1&v=' . $t->updated_at->timestamp, - ])->values()->all(); + // Dedupe the language picker by (language + label). A video with two + // tracks sharing the same language (e.g. "ar" uploaded twice while + // retrying) used to list both and leave the viewer guessing. Keeping + // the first occurrence preserves the primary when it exists; using + // (lang, label) as the key still lets legit variants coexist (e.g. + // "en — Original" and "en — Director's cut"). + $audioTracks = $video->audioTracks + ->unique(fn ($t) => mb_strtolower((string) $t->language) . '|' . mb_strtolower((string) ($t->label ?? ''))) + ->map(fn ($t) => [ + 'id' => $t->id, + 'language' => $t->language, + 'label' => $t->label, + 'flag' => $allLangData[$t->language]['flag'] ?? null, + 'stream_url' => route('videos.audio-track', ['video' => $video, 'track' => $t->id]) . '?v=' . $t->updated_at->timestamp, + 'title' => $t->title ?? '', + 'description' => $t->description ?? '', + 'dl_url' => route('videos.audio-track', ['video' => $video, 'track' => $t->id]) . '?download=1&v=' . $t->updated_at->timestamp, + ])->values()->all(); // Synced lyrics embedded inline (no separate request), keyed by track id; "0" = primary. // Local mirror only — must not block this hot path on NAS I/O.