Files

140 lines
4.1 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const { sanitizeFolderName, THUMBNAILS_DIR } = require('./paths');
class AutoSyncManager {
constructor(ftpManager, scanner, config) {
this.ftp = ftpManager;
this.scanner = scanner;
this.config = config;
this.isRunning = false;
this.timer = null;
this.status = {
active: false,
lastSync: null,
currentProgress: null,
error: null,
syncedCount: 0,
};
}
start() {
if (!this.config.autoSync || !this.config.autoSync.enabled) return;
this.stop();
const intervalMs = Math.max(1, this.config.autoSync.intervalMinutes || 10) * 60 * 1000;
console.log(`[AutoSync] Background sync enabled (checking every ${this.config.autoSync.intervalMinutes} min).`);
// Initial run after 5 seconds
setTimeout(() => this.runSync(), 5000);
this.timer = setInterval(() => {
this.runSync();
}, intervalMs);
}
stop() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
this.isRunning = false;
}
async runSync() {
if (this.isRunning) return;
this.isRunning = true;
this.status.active = true;
this.status.error = null;
try {
// 1. Check if PS5 is reachable
const online = await this.ftp.checkOnline(2000);
if (!online) {
this.status.error = 'PS5 is unreachable';
return;
}
// 2. Scan captures
const scanResult = await this.scanner.scan();
if (!scanResult.success) {
this.status.error = scanResult.error;
return;
}
const items = scanResult.library.items || [];
const downloadRoot = this.config.autoSync.downloadPath;
if (!fs.existsSync(downloadRoot)) {
fs.mkdirSync(downloadRoot, { recursive: true });
}
let downloadedThisRun = 0;
const totalToSync = items.filter((item) => {
if (item.type === 'photo' && !this.config.autoSync.syncPhotos) return false;
if (item.type === 'video' && !this.config.autoSync.syncVideos) return false;
const gameFolder = sanitizeFolderName(item.gameTitle || item.titleId);
const targetPath = path.join(downloadRoot, gameFolder, item.filename);
return !fs.existsSync(targetPath);
});
console.log(`[AutoSync] Found ${totalToSync.length} new items to download.`);
for (let i = 0; i < totalToSync.length; i++) {
const item = totalToSync[i];
const gameFolder = sanitizeFolderName(item.gameTitle || item.titleId);
const targetDir = path.join(downloadRoot, gameFolder);
const targetPath = path.join(targetDir, item.filename);
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
this.status.currentProgress = {
current: i + 1,
total: totalToSync.length,
filename: item.filename,
};
try {
await this.ftp.downloadFile(item.remotePath, targetPath);
downloadedThisRun++;
// Also cache thumbnail if not cached
const thumbKey = crypto.createHash('md5').update(item.remotePath).digest('hex') + '.thumb';
const thumbPath = path.join(THUMBNAILS_DIR, thumbKey);
if (!fs.existsSync(thumbPath)) {
if (item.type === 'photo') {
try { fs.copyFileSync(targetPath, thumbPath); } catch (e) {}
}
}
} catch (e) {
console.error(`[AutoSync] Failed to download ${item.remotePath}:`, e.message);
}
}
this.status.syncedCount += downloadedThisRun;
this.status.lastSync = new Date().toISOString();
console.log(`[AutoSync] Sync finished. Downloaded ${downloadedThisRun} items.`);
} catch (err) {
console.error('[AutoSync] Sync error:', err.message);
this.status.error = err.message;
} finally {
this.isRunning = false;
this.status.active = false;
this.status.currentProgress = null;
}
}
getStatus() {
return {
enabled: this.config.autoSync.enabled,
...this.status,
};
}
}
module.exports = AutoSyncManager;