takeone-youtube-clone/app/Jobs/NasToLocalMigrationJob.php
ghassan c160242dbc WIP: storage-fix-local-nas work before playlist controls feature
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 11:15:20 +03:00

184 lines
8.0 KiB
PHP

<?php
namespace App\Jobs;
use App\Models\Setting;
use App\Models\User;
use App\Models\Video;
use App\Services\NasSyncService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
class NasToLocalMigrationJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 1;
public int $timeout = 7200; // 2 hours
private function updateProgress(array $data): void
{
Cache::put('nas_disable_progress', json_encode($data), 3600);
}
public function handle(NasSyncService $nas): void
{
$progress = [
'current' => 0,
'total' => 0,
'phase' => 'Counting files...',
'done' => false,
'error' => null,
];
$this->updateProgress($progress);
try {
// ── Count all items ───────────────────────────────────────────────
$videos = Video::where('path', 'like', 'users/%')->get();
$slides = \DB::table('video_slides')
->where('filename', 'like', 'users/%')
->get();
$usersWithAvatar = User::where('avatar', 'like', 'users/%')->get();
$usersWithBanner = User::where('banner', 'like', 'users/%')->get();
$postImages = \DB::table('post_images')
->where('filename', 'like', 'users/%')
->get();
// Count thumbnails separately (they're additional per-video downloads)
$videoThumbs = $videos->filter(fn($v) => $v->thumbnail && str_starts_with($v->thumbnail, 'users/'));
$total = $videos->count()
+ $videoThumbs->count()
+ $slides->count()
+ $usersWithAvatar->count()
+ $usersWithBanner->count()
+ $postImages->count();
$progress['total'] = $total;
$progress['phase'] = 'Downloading files from NAS...';
$this->updateProgress($progress);
// ── Download videos ───────────────────────────────────────────────
foreach ($videos as $video) {
$nasPath = $video->path;
$localPath = storage_path('app/' . $nasPath);
try {
$nas->ensureLocalAsset($localPath, $nasPath);
} catch (\Throwable $e) {
Log::error('NasToLocalMigrationJob: failed to download video #' . $video->id . ' path=' . $nasPath . ': ' . $e->getMessage());
}
$progress['current']++;
$progress['phase'] = 'Downloading videos... (' . $progress['current'] . '/' . $total . ')';
$this->updateProgress($progress);
}
// ── Download thumbnails ───────────────────────────────────────────
foreach ($videoThumbs as $video) {
$nasPath = $video->thumbnail;
$localPath = storage_path('app/' . $nasPath);
try {
$nas->ensureLocalAsset($localPath, $nasPath);
} catch (\Throwable $e) {
Log::error('NasToLocalMigrationJob: failed to download thumbnail for video #' . $video->id . ' path=' . $nasPath . ': ' . $e->getMessage());
}
$progress['current']++;
$progress['phase'] = 'Downloading thumbnails... (' . $progress['current'] . '/' . $total . ')';
$this->updateProgress($progress);
}
// ── Download slides ───────────────────────────────────────────────
foreach ($slides as $slide) {
$nasPath = $slide->filename;
$localPath = storage_path('app/' . $nasPath);
try {
$nas->ensureLocalAsset($localPath, $nasPath);
} catch (\Throwable $e) {
Log::error('NasToLocalMigrationJob: failed to download slide id=' . $slide->id . ' path=' . $nasPath . ': ' . $e->getMessage());
}
$progress['current']++;
$progress['phase'] = 'Downloading audio slides... (' . $progress['current'] . '/' . $total . ')';
$this->updateProgress($progress);
}
// ── Download user avatars ─────────────────────────────────────────
foreach ($usersWithAvatar as $user) {
$nasPath = $user->avatar;
$localPath = storage_path('app/' . $nasPath);
try {
$nas->ensureLocalAsset($localPath, $nasPath);
} catch (\Throwable $e) {
Log::error('NasToLocalMigrationJob: failed to download avatar for user #' . $user->id . ' path=' . $nasPath . ': ' . $e->getMessage());
}
$progress['current']++;
$progress['phase'] = 'Downloading avatars... (' . $progress['current'] . '/' . $total . ')';
$this->updateProgress($progress);
}
// ── Download user banners ─────────────────────────────────────────
foreach ($usersWithBanner as $user) {
$nasPath = $user->banner;
$localPath = storage_path('app/' . $nasPath);
try {
$nas->ensureLocalAsset($localPath, $nasPath);
} catch (\Throwable $e) {
Log::error('NasToLocalMigrationJob: failed to download banner for user #' . $user->id . ' path=' . $nasPath . ': ' . $e->getMessage());
}
$progress['current']++;
$progress['phase'] = 'Downloading banners... (' . $progress['current'] . '/' . $total . ')';
$this->updateProgress($progress);
}
// ── Download post images ──────────────────────────────────────────
foreach ($postImages as $img) {
$nasPath = $img->filename;
$localPath = storage_path('app/' . $nasPath);
try {
$nas->ensureLocalAsset($localPath, $nasPath);
} catch (\Throwable $e) {
Log::error('NasToLocalMigrationJob: failed to download post image id=' . $img->id . ' path=' . $nasPath . ': ' . $e->getMessage());
}
$progress['current']++;
$progress['phase'] = 'Downloading post images... (' . $progress['current'] . '/' . $total . ')';
$this->updateProgress($progress);
}
// ── Disable NAS ───────────────────────────────────────────────────
Setting::set('nas_sync_enabled', 'false');
$progress['done'] = true;
$progress['phase'] = 'Complete';
$this->updateProgress($progress);
Log::info('NasToLocalMigrationJob: completed. ' . $total . ' files migrated. NAS disabled.');
} catch (\Throwable $e) {
Log::error('NasToLocalMigrationJob: fatal error: ' . $e->getMessage(), [
'trace' => $e->getTraceAsString(),
]);
$progress['error'] = $e->getMessage();
$this->updateProgress($progress);
}
}
}