ghassan 66fd78c10f Add multi-language audio tracks and self-hosted flag-icons
Introduce per-video language support and multiple audio tracks
(VideoAudioTrack model + migrations for language, description, title),
a reusable language-select component, and a track-editor form. Bundle
the self-hosted flag-icons v7.2.3 library and a NAS auto-sync command.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:32:52 +03:00

76 lines
2.7 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Jobs\NasSyncVideoJob;
use App\Models\User;
use App\Models\Video;
use App\Services\NasSyncService;
use Illuminate\Console\Command;
class NasAutoSync extends Command
{
protected $signature = 'nas:auto-sync';
protected $description = 'Push locally-stored files to NAS when NAS is available. Runs as a scheduled task.';
public function handle(NasSyncService $nas): int
{
if (! $nas->isEnabled()) {
return 0; // NAS down or disabled — nothing to do
}
// Videos whose file OR thumbnail/slides are still on local disk
$synced = 0;
Video::with(['user', 'slides'])
->where('status', 'ready')
->where('path', 'like', 'users/%')
->get()
->each(function (Video $video) use ($nas, &$synced) {
$hasLocalVideo = file_exists(storage_path('app/' . $video->path));
// Check for locally-stored thumbnail (NAS push failed during edit)
$hasLocalThumb = $video->thumbnail
&& str_starts_with($video->thumbnail, 'users/')
&& file_exists(storage_path('app/' . $video->thumbnail));
// Check for locally-stored slides
$hasLocalSlide = false;
foreach ($video->slides as $slide) {
if (str_starts_with($slide->filename, 'users/')
&& file_exists(storage_path('app/' . $slide->filename))) {
$hasLocalSlide = true;
break;
}
}
if ($hasLocalVideo || $hasLocalThumb || $hasLocalSlide) {
NasSyncVideoJob::dispatch($video);
$synced++;
}
});
// Avatars and banners stuck locally
User::whereNotNull('avatar')->orWhereNotNull('banner')->get()
->each(function (User $user) use ($nas) {
if ($user->avatar
&& str_starts_with($user->avatar, 'users/')
&& file_exists(storage_path('app/' . $user->avatar))) {
$nas->syncAvatar($user, storage_path('app/' . $user->avatar));
$nas->deleteLocalAvatar($user);
}
if ($user->banner
&& str_starts_with($user->banner, 'users/')
&& file_exists(storage_path('app/' . $user->banner))) {
$nas->syncCover($user, storage_path('app/' . $user->banner));
$nas->deleteLocalBanner($user);
}
});
if ($synced > 0) {
$this->info("Queued {$synced} video(s) for NAS sync.");
}
return 0;
}
}