Three bugs made merge destructive and unrecoverable:
1. Primary-track slide paths were only prefix-swapped {sourceDir} → {targetDir}.
The promoted primary track got a NEW folder name
tracks/{lang}-{newTrackId}, but the slide filename kept the old
tracks/{lang}-{srcVideoId} segment, pointing at a folder that no longer
existed. Rewrite is now computed from the owning track's updated path, so
it survives regardless of what the stored filename used to look like.
2. Legacy-layout sources (primary audio directly at {sourceDir}/audio.ext,
slides at {sourceDir}/slides/) were silently skipped. The old code
hard-coded tracks/{lang-id}/ as the source location. Merge is now
layout-aware: it reads dirname($source->path) to pick between folder-move
(new layout) and per-file moves (legacy — audio, slides/ dir, thumb).
3. Lyrics were lost on legacy sources. {sourceDir}/lyrics/primary.json and
{sourceDir}/lyrics/track-{id}.json are now explicitly moved into the
target's per-track folders as lyrics.json, matching the new layout the
LyricsService reads from.
Added moveFile() helper (mirrors moveFolder on NAS + local, queues to
pending_nas_moves when NAS is unreachable). Verified against six scenarios
including new-layout, legacy, target-with-existing-extras, minimal, 2-extras
with slides and reversed-position lyrics source: all broken-file counts are
zero and slide position order is preserved verbatim.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The merge no longer refuses when NAS is unreachable. DB changes always
run; per-side file operations are best-effort. When the NAS rename can't
happen it's recorded in a new pending_nas_moves table, and nas:auto-sync
drains the queue (rename + best-effort parent prune) once NAS is back,
retaining attempts/last_error on failure. Also plugs missing isEnabled()
guards on renameNasPath and deleteFolder so the merge cleanup path can't
hang on smbclient timeouts.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Bundled pre-existing changes on the branch:
- Video merge: controller, service, admin merge modal, and a repair
console command; new video_redirects table (with source_folder)
so merged/moved videos keep resolving under their old keys.
- Default avatar SVG for users without a profile picture.
- Assorted view tweaks across profile, video actions/comments/insights,
admin layout, video-details, and the app layout.
- Small touch-ups in SuperAdminController, User model, NasSyncService.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>