diff --git a/app/Services/AdultSyncService.php b/app/Services/AdultSyncService.php index 7e2ae2e..06903ca 100644 --- a/app/Services/AdultSyncService.php +++ b/app/Services/AdultSyncService.php @@ -16,9 +16,9 @@ class AdultSyncService extends BaseSyncService private int $newCount = 0; private int $updatedCount = 0; - public function __construct(PDO $pdo, array $source) + public function __construct(PDO $pdo, array $source, ?int $existingSyncLogId = null) { - parent::__construct($pdo, $source); + parent::__construct($pdo, $source, $existingSyncLogId); // Find XBVR and Stash sources $this->xbvrSource = $this->findSourceByName('xbvr'); diff --git a/app/Services/BaseSyncService.php b/app/Services/BaseSyncService.php index d973bbb..ef72d6a 100644 --- a/app/Services/BaseSyncService.php +++ b/app/Services/BaseSyncService.php @@ -2,20 +2,18 @@ namespace App\Services; -use App\Models\SyncLog; use Exception; abstract class BaseSyncService { protected \PDO $pdo; protected array $source; - protected SyncLog $syncLog; protected int $sourceId; protected $logFileHandle; protected $logFilePath; - public function __construct(\PDO $pdo, array $source) + public function __construct(\PDO $pdo, array $source, ?int $existingSyncLogId = null) { $this->pdo = $pdo; $this->source = $source; @@ -25,6 +23,7 @@ abstract class BaseSyncService } $this->sourceId = (int) $source['id']; + $this->currentSyncLogId = $existingSyncLogId; // Create log file for this sync operation $this->initializeLogFile(); @@ -62,8 +61,19 @@ abstract class BaseSyncService ini_set('max_execution_time', 3600); // 1 hour ini_set('memory_limit', '512M'); - // Create sync log entry - $syncLogId = $this->createSyncLog($syncType, 'started'); + // Use existing sync log ID if provided, otherwise create a new one + if ($this->currentSyncLogId) { + $syncLogId = $this->currentSyncLogId; + // Update the existing log to 'started' status + $this->updateSyncLog($syncLogId, 'started', [ + 'sync_type' => $syncType, + 'started_at' => date('Y-m-d H:i:s') + ]); + } else { + // Create sync log entry + $syncLogId = $this->createSyncLog($syncType, 'started'); + } + $this->currentSyncLogId = $syncLogId; try { diff --git a/app/Services/ExophaseSyncService.php b/app/Services/ExophaseSyncService.php index 9f334d9..3a7053f 100644 --- a/app/Services/ExophaseSyncService.php +++ b/app/Services/ExophaseSyncService.php @@ -15,9 +15,9 @@ class ExophaseSyncService extends BaseSyncService private int $newCount = 0; private int $updatedCount = 0; - public function __construct(\PDO $pdo, array $source) + public function __construct(\PDO $pdo, array $source, ?int $existingSyncLogId = null) { - parent::__construct($pdo, $source); + parent::__construct($pdo, $source, $existingSyncLogId); $this->httpClient = new Client([ 'timeout' => 30, 'headers' => [ diff --git a/app/Services/JellyfinSyncService.php b/app/Services/JellyfinSyncService.php index 39fbb47..8ad2356 100644 --- a/app/Services/JellyfinSyncService.php +++ b/app/Services/JellyfinSyncService.php @@ -18,9 +18,9 @@ class JellyfinSyncService extends BaseSyncService private int $newCount = 0; private int $updatedCount = 0; - public function __construct(\PDO $pdo, array $source) + public function __construct(\PDO $pdo, array $source, ?int $existingSyncLogId = null) { - parent::__construct($pdo, $source); + parent::__construct($pdo, $source, $existingSyncLogId); $this->httpClient = new Client([ 'timeout' => 30, 'headers' => [ diff --git a/app/Services/StashSyncService.php b/app/Services/StashSyncService.php index 3485252..130420f 100644 --- a/app/Services/StashSyncService.php +++ b/app/Services/StashSyncService.php @@ -18,9 +18,9 @@ class StashSyncService extends BaseSyncService private int $newCount = 0; private int $updatedCount = 0; - public function __construct(PDO $pdo, array $source) + public function __construct(PDO $pdo, array $source, ?int $existingSyncLogId = null) { - parent::__construct($pdo, $source); + parent::__construct($pdo, $source, $existingSyncLogId); // Initialize properties first before using them $this->apiKey = $source['api_key']; diff --git a/app/Services/SteamSyncService.php b/app/Services/SteamSyncService.php index b054ef8..5d2d4a0 100644 --- a/app/Services/SteamSyncService.php +++ b/app/Services/SteamSyncService.php @@ -15,9 +15,9 @@ class SteamSyncService extends BaseSyncService private int $newCount = 0; private int $updatedCount = 0; - public function __construct(\PDO $pdo, array $source) + public function __construct(\PDO $pdo, array $source, ?int $existingSyncLogId = null) { - parent::__construct($pdo, $source); + parent::__construct($pdo, $source, $existingSyncLogId); $this->httpClient = new Client([ 'timeout' => 30, 'headers' => [ diff --git a/app/Services/XbvrSyncService.php b/app/Services/XbvrSyncService.php index 48025c8..83c03a4 100644 --- a/app/Services/XbvrSyncService.php +++ b/app/Services/XbvrSyncService.php @@ -16,9 +16,9 @@ class XbvrSyncService extends BaseSyncService private int $newCount = 0; private int $updatedCount = 0; - public function __construct(\PDO $pdo, array $source) + public function __construct(\PDO $pdo, array $source, ?int $existingSyncLogId = null) { - parent::__construct($pdo, $source); + parent::__construct($pdo, $source, $existingSyncLogId); // Initialize properties first before using them $this->baseUrl = rtrim($source['api_url'], '/'); diff --git a/resources/views/admin/index.twig b/resources/views/admin/index.twig index 5fcceb8..3415b9d 100644 --- a/resources/views/admin/index.twig +++ b/resources/views/admin/index.twig @@ -119,8 +119,11 @@ Type Status Progress + Items Started Duration + Message + Actions @@ -142,13 +145,27 @@ {% if sync.total_items > 0 %} {{ sync.processed_items }} / {{ sync.total_items }} + {% if sync.total_items > 0 %} +
+
+
+ {% endif %} {% else %} - {% endif %} + + + {% if sync.new_items > 0 %}+{{ sync.new_items }}{% endif %} + {% if sync.updated_items > 0 %} {{ sync.updated_items }} updated{% endif %} + {% if sync.deleted_items > 0 %} {{ sync.deleted_items }} deleted{% endif %} + {% if sync.new_items == 0 and sync.updated_items == 0 and sync.deleted_items == 0 %}No changes{% endif %} + + {% if sync.started_at %} - {{ sync.started_at|date('M j, H:i') }} + {{ sync.started_at|date('M j, H:i') }} {% else %} - {% endif %} @@ -157,16 +174,34 @@ {% if sync.started_at and sync.completed_at %} {% set duration = sync.completed_at|date('U') - sync.started_at|date('U') %} {% if duration < 60 %} - {{ duration }}s + {{ duration }}s {% elseif duration < 3600 %} - {{ (duration / 60)|round }}m + {{ (duration / 60)|round }}m {% else %} - {{ (duration / 3600)|round }}h + {{ (duration / 3600)|round }}h {% endif %} {% else %} - {% endif %} + + {% if sync.message %} + + {{ sync.message|length > 50 ? sync.message|slice(0, 50) ~ '...' : sync.message }} + + {% else %} + - + {% endif %} + + + {% if sync.errors %} + + {% endif %} + {% endfor %} @@ -280,16 +315,118 @@ function getStatusMessage(data) { if (data.status === 'completed') { - return `Completed: ${data.new_items} new, ${data.updated_items} updated`; + let message = `Completed: ${data.new_items || 0} new`; + if (data.updated_items > 0) message += `, ${data.updated_items} updated`; + if (data.deleted_items > 0) message += `, ${data.deleted_items} deleted`; + return message; } else if (data.status === 'failed') { - return 'Failed: ' + (data.errors.join(', ') || 'Unknown error'); + return 'Failed: ' + (data.message || 'Unknown error'); } else if (data.status === 'running') { - return `Processing: ${data.processed_items}/${data.total_items} items`; + let progress = 'Processing'; + if (data.total_items > 0) { + progress += `: ${data.processed_items}/${data.total_items} items`; + } + if (data.message) { + progress += ` - ${data.message}`; + } + return progress; } else { return data.message || 'Unknown status'; } } + function showSyncDetails(syncId, sourceName, syncType) { + // Fetch detailed sync information + fetch(`/admin/sync/status/${syncId}`) + .then(response => response.json()) + .then(data => { + const modal = document.createElement('div'); + modal.className = 'modal fade show'; + modal.style.display = 'block'; + modal.innerHTML = ` + + `; + document.body.appendChild(modal); + }) + .catch(error => { + console.error('Error fetching sync details:', error); + alert('Error loading sync details'); + }); + } + + function closeModal() { + const modal = document.querySelector('.modal.show'); + if (modal) { + modal.remove(); + } + } + + function formatDuration(ms) { + const seconds = Math.floor(ms / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + + if (hours > 0) { + return `${hours}h ${minutes % 60}m`; + } else if (minutes > 0) { + return `${minutes}m ${seconds % 60}s`; + } else { + return `${seconds}s`; + } + } + // Cleanup intervals on page unload window.addEventListener('beforeunload', () => { Object.values(syncIntervals).forEach(interval => clearInterval(interval)); diff --git a/sync-runner.php b/sync-runner.php index d3f558c..9aa0edb 100644 --- a/sync-runner.php +++ b/sync-runner.php @@ -94,22 +94,22 @@ if ($source['name'] === 'jellyfin') { $syncService = null; switch ($source['name']) { case 'steam': - $syncService = new SteamSyncService($pdo, $sourceData); + $syncService = new SteamSyncService($pdo, $sourceData, $syncLogId); break; case 'jellyfin': - $syncService = new JellyfinSyncService($pdo, $sourceData); + $syncService = new JellyfinSyncService($pdo, $sourceData, $syncLogId); break; case 'stash': - $syncService = new StashSyncService($pdo, $sourceData); + $syncService = new StashSyncService($pdo, $sourceData, $syncLogId); break; case 'adult': - $syncService = new AdultSyncService($pdo, $sourceData); + $syncService = new AdultSyncService($pdo, $sourceData, $syncLogId); break; case 'xbvr': - $syncService = new XbvrSyncService($pdo, $sourceData); + $syncService = new XbvrSyncService($pdo, $sourceData, $syncLogId); break; case 'exophase': - $syncService = new ExophaseSyncService($pdo, $sourceData); + $syncService = new ExophaseSyncService($pdo, $sourceData, $syncLogId); break; default: echo "Unsupported source type: " . $source['name'] . "\n"; @@ -134,11 +134,11 @@ $syncLogModel->update($syncLogId, [ // Execute the sync try { - $syncLogId = $syncService->startSync($syncType); + $returnedSyncLogId = $syncService->startSync($syncType); if (!$noOutput) { - echo "Sync started with log ID: {$syncLogId}\n"; - echo "Monitor progress at: /admin/sync/status/{$syncLogId}\n"; + echo "Sync started with log ID: {$returnedSyncLogId}\n"; + echo "Monitor progress at: /admin/sync/status/{$returnedSyncLogId}\n"; echo "Sync completed successfully.\n"; } @@ -150,8 +150,10 @@ try { echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n"; } - // Update sync log as failed - $syncLogModel->update($syncLogId, [ + // Update sync log as failed (use the returned ID or fall back to original) + $failedSyncLogId = $returnedSyncLogId ?? $syncLogId; + $syncLogModel = new SyncLog($pdo); + $syncLogModel->update($failedSyncLogId, [ 'status' => 'failed', 'completed_at' => date('Y-m-d H:i:s'), 'message' => $e->getMessage(),