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) <[email protected]>
This commit is contained in:
1 parent
f8d13457fa
commit
66fd78c10f
314 files changed
+16507
-3172
No files matched your search
@@ -0,0 +1,75 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
+18
-1
@@ -24,7 +24,24 @@ class Kernel extends ConsoleKernel
|
||||
if ($nas->isEnabled()) {
|
||||
$nas->clearNasCache(24);
|
||||
}
|
||||
})->daily()->name('nas-cache-evict')->withoutOverlapping();
|
||||
})->daily()->name('nas-cache-evict');
|
||||
|
||||
// Clean up stale temp files in public/tmp older than 1 hour
|
||||
$schedule->call(function () {
|
||||
$tmpDir = storage_path('app/public/tmp');
|
||||
if (! is_dir($tmpDir)) return;
|
||||
$cutoff = time() - 3600;
|
||||
foreach (glob("{$tmpDir}/*") as $file) {
|
||||
if (is_file($file) && filemtime($file) < $cutoff) @unlink($file);
|
||||
}
|
||||
})->hourly()->name('tmp-cleanup');
|
||||
|
||||
// Auto-sync local files to NAS when NAS comes back online.
|
||||
// Runs every 10 minutes; exits immediately when NAS is unreachable.
|
||||
$schedule->command('nas:auto-sync')
|
||||
->everyTenMinutes()
|
||||
->withoutOverlapping()
|
||||
->runInBackground();
|
||||
|
||||
// Weekly activity digest — every Monday at 9:00 AM (Bahrain time)
|
||||
$schedule->command('digest:weekly')
|
||||
|
||||
Reference in new issue
Block a user