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 = `
+
+
+
+
+
+
+ Status:
+
+ ${data.status}
+
+
+
+ Started: ${data.started_at ? new Date(data.started_at).toLocaleString() : 'N/A'}
+
+
+
+
+ Completed: ${data.completed_at ? new Date(data.completed_at).toLocaleString() : 'N/A'}
+
+
+ Duration: ${data.started_at && data.completed_at ?
+ formatDuration(new Date(data.completed_at) - new Date(data.started_at)) : 'N/A'}
+
+
+
+
Total Items: ${data.total_items || 0}
+
Processed: ${data.processed_items || 0}
+
New: ${data.new_items || 0}
+
Updated: ${data.updated_items || 0}
+
+
+
Message:
+
${data.message || 'No message available'}
+
+ ${data.errors && data.errors.length > 0 ? `
+
+
Errors:
+
+
+ ${data.errors.map(error => `- ${error}
`).join('')}
+
+
+
+ ` : ''}
+
+
+
+
+ `;
+ 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(),