perf(server): websocket yayınlarında önbellekleme ve debouncing ekle
This commit is contained in:
269
server/server.js
269
server/server.js
@@ -8,7 +8,7 @@ import mime from "mime-types";
|
|||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { exec, spawn } from "child_process";
|
import { exec, spawn } from "child_process";
|
||||||
import crypto from "crypto"; // 🔒 basit token üretimi için
|
import crypto from "crypto"; // 🔒 basit token üretimi için
|
||||||
import { getSystemDiskInfo } from "./utils/diskSpace.js";
|
import { getDiskSpace, getDownloadsSize } from "./utils/diskSpace.js";
|
||||||
import { createAuth } from "./modules/auth.js";
|
import { createAuth } from "./modules/auth.js";
|
||||||
import { buildHealthReport, healthRouter } from "./modules/health.js";
|
import { buildHealthReport, healthRouter } from "./modules/health.js";
|
||||||
import { restoreTorrentsFromDisk } from "./modules/state.js";
|
import { restoreTorrentsFromDisk } from "./modules/state.js";
|
||||||
@@ -735,7 +735,7 @@ function startYoutubeDownload(url) {
|
|||||||
youtubeJobs.set(job.id, job);
|
youtubeJobs.set(job.id, job);
|
||||||
launchYoutubeJob(job);
|
launchYoutubeJob(job);
|
||||||
console.log(`▶️ YouTube indirmesi başlatıldı: ${job.url}`);
|
console.log(`▶️ YouTube indirmesi başlatıldı: ${job.url}`);
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -888,7 +888,7 @@ function launchYoutubeJob(job) {
|
|||||||
binary,
|
binary,
|
||||||
args
|
args
|
||||||
});
|
});
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -922,7 +922,7 @@ function startYoutubeStage(job, fileName) {
|
|||||||
};
|
};
|
||||||
job.currentStage = stage;
|
job.currentStage = stage;
|
||||||
job.stages.push(stage);
|
job.stages.push(stage);
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateYoutubeProgress(job, match) {
|
function updateYoutubeProgress(job, match) {
|
||||||
@@ -959,7 +959,7 @@ function updateYoutubeProgress(job, match) {
|
|||||||
if (Number.isFinite(speedValue) && speedUnit) {
|
if (Number.isFinite(speedValue) && speedUnit) {
|
||||||
job.downloadSpeed = bytesFromHuman(speedValue, speedUnit);
|
job.downloadSpeed = bytesFromHuman(speedValue, speedUnit);
|
||||||
}
|
}
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function finalizeYoutubeJob(job, exitCode) {
|
async function finalizeYoutubeJob(job, exitCode) {
|
||||||
@@ -979,7 +979,7 @@ async function finalizeYoutubeJob(job, exitCode) {
|
|||||||
args: job.debug?.args,
|
args: job.debug?.args,
|
||||||
lastLines: tail
|
lastLines: tail
|
||||||
});
|
});
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (exitCode !== 0 && fallbackMedia) {
|
if (exitCode !== 0 && fallbackMedia) {
|
||||||
@@ -1007,7 +1007,7 @@ async function finalizeYoutubeJob(job, exitCode) {
|
|||||||
savePath: job.savePath,
|
savePath: job.savePath,
|
||||||
lastLines: job.debug?.logs?.slice(-8) || []
|
lastLines: job.debug?.logs?.slice(-8) || []
|
||||||
});
|
});
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1065,13 +1065,13 @@ async function finalizeYoutubeJob(job, exitCode) {
|
|||||||
primaryMediaInfo: mediaInfo
|
primaryMediaInfo: mediaInfo
|
||||||
});
|
});
|
||||||
broadcastFileUpdate(job.folderId);
|
broadcastFileUpdate(job.folderId);
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
broadcastDiskSpace();
|
broadcastDiskSpace();
|
||||||
console.log(`✅ YouTube indirmesi tamamlandı: ${job.title}`);
|
console.log(`✅ YouTube indirmesi tamamlandı: ${job.title}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
job.state = "error";
|
job.state = "error";
|
||||||
job.error = err?.message || "YouTube indirimi tamamlanamadı";
|
job.error = err?.message || "YouTube indirimi tamamlanamadı";
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1248,7 +1248,7 @@ function removeYoutubeJob(jobId, { removeFiles = true } = {}) {
|
|||||||
console.warn("YT cache silinemedi:", err.message);
|
console.warn("YT cache silinemedi:", err.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
if (filesRemoved) {
|
if (filesRemoved) {
|
||||||
broadcastFileUpdate(job.folderId);
|
broadcastFileUpdate(job.folderId);
|
||||||
broadcastDiskSpace();
|
broadcastDiskSpace();
|
||||||
@@ -4104,25 +4104,108 @@ function broadcastFileUpdate(rootFolder) {
|
|||||||
wss.clients.forEach((c) => c.readyState === 1 && c.send(data));
|
wss.clients.forEach((c) => c.readyState === 1 && c.send(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
function broadcastDiskSpace() {
|
const DISK_SPACE_CACHE_TTL_MS = 30000;
|
||||||
if (!wss) return;
|
const DOWNLOADS_SIZE_CACHE_TTL_MS = 5 * 60 * 1000;
|
||||||
getSystemDiskInfo(DOWNLOAD_DIR).then(diskInfo => {
|
let diskSpaceCache = { value: null, fetchedAt: 0 };
|
||||||
const data = JSON.stringify({
|
let downloadsSizeCache = { value: null, fetchedAt: 0 };
|
||||||
type: "diskSpace",
|
let diskInfoInFlight = null;
|
||||||
data: diskInfo
|
let lastDiskSpacePayload = null;
|
||||||
});
|
|
||||||
wss.clients.forEach((c) => c.readyState === 1 && c.send(data));
|
async function getCachedDiskInfo({ force = false } = {}) {
|
||||||
}).catch(err => {
|
const now = Date.now();
|
||||||
console.error("❌ Disk space broadcast error:", err.message);
|
const diskFresh =
|
||||||
});
|
!force && diskSpaceCache.value && now - diskSpaceCache.fetchedAt < DISK_SPACE_CACHE_TTL_MS;
|
||||||
|
const downloadsFresh =
|
||||||
|
!force &&
|
||||||
|
downloadsSizeCache.value &&
|
||||||
|
now - downloadsSizeCache.fetchedAt < DOWNLOADS_SIZE_CACHE_TTL_MS;
|
||||||
|
|
||||||
|
if (diskFresh && downloadsFresh) {
|
||||||
|
return {
|
||||||
|
...diskSpaceCache.value,
|
||||||
|
downloads: downloadsSizeCache.value,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diskInfoInFlight) return diskInfoInFlight;
|
||||||
|
|
||||||
|
diskInfoInFlight = (async () => {
|
||||||
|
const diskSpace = diskFresh
|
||||||
|
? diskSpaceCache.value
|
||||||
|
: await getDiskSpace(DOWNLOAD_DIR);
|
||||||
|
if (!diskFresh) {
|
||||||
|
diskSpaceCache = { value: diskSpace, fetchedAt: now };
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadsSize = downloadsFresh
|
||||||
|
? downloadsSizeCache.value
|
||||||
|
: await getDownloadsSize(DOWNLOAD_DIR);
|
||||||
|
if (!downloadsFresh) {
|
||||||
|
downloadsSizeCache = { value: downloadsSize, fetchedAt: now };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...diskSpace,
|
||||||
|
downloads: downloadsSize,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await diskInfoInFlight;
|
||||||
|
} finally {
|
||||||
|
diskInfoInFlight = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function broadcastDiskSpace() {
|
||||||
|
if (!wss || !hasActiveWsClients()) return;
|
||||||
|
getCachedDiskInfo()
|
||||||
|
.then((diskInfo) => {
|
||||||
|
const data = JSON.stringify({
|
||||||
|
type: "diskSpace",
|
||||||
|
data: diskInfo
|
||||||
|
});
|
||||||
|
if (data === lastDiskSpacePayload) return;
|
||||||
|
lastDiskSpacePayload = data;
|
||||||
|
wss.clients.forEach((c) => c.readyState === 1 && c.send(data));
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("❌ Disk space broadcast error:", err.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastSnapshotPayload = null;
|
||||||
|
const SNAPSHOT_DEBOUNCE_MS = 1000;
|
||||||
|
let snapshotTimer = null;
|
||||||
|
let lastSnapshotAt = 0;
|
||||||
|
|
||||||
function broadcastSnapshot() {
|
function broadcastSnapshot() {
|
||||||
if (!wss) return;
|
if (!wss || !hasActiveWsClients()) return;
|
||||||
const data = JSON.stringify({ type: "progress", torrents: snapshot() });
|
const data = JSON.stringify({ type: "progress", torrents: snapshot() });
|
||||||
|
if (data === lastSnapshotPayload) return;
|
||||||
|
lastSnapshotPayload = data;
|
||||||
wss.clients.forEach((c) => c.readyState === 1 && c.send(data));
|
wss.clients.forEach((c) => c.readyState === 1 && c.send(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scheduleSnapshotBroadcast() {
|
||||||
|
if (!wss || !hasActiveWsClients()) return;
|
||||||
|
const now = Date.now();
|
||||||
|
const remaining = SNAPSHOT_DEBOUNCE_MS - (now - lastSnapshotAt);
|
||||||
|
if (remaining <= 0) {
|
||||||
|
lastSnapshotAt = now;
|
||||||
|
broadcastSnapshot();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (snapshotTimer) return;
|
||||||
|
snapshotTimer = setTimeout(() => {
|
||||||
|
snapshotTimer = null;
|
||||||
|
lastSnapshotAt = Date.now();
|
||||||
|
broadcastSnapshot();
|
||||||
|
}, remaining);
|
||||||
|
}
|
||||||
|
|
||||||
let mediaRescanTask = null;
|
let mediaRescanTask = null;
|
||||||
let pendingMediaRescan = { movies: false, tv: false };
|
let pendingMediaRescan = { movies: false, tv: false };
|
||||||
let lastMediaRescanReason = "manual";
|
let lastMediaRescanReason = "manual";
|
||||||
@@ -4252,22 +4335,73 @@ function inferMediaFlagsFromTrashEntry(entry) {
|
|||||||
return { movies: true, tv: false };
|
return { movies: true, tv: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const THUMBNAIL_CHECK_INTERVAL_MS = 15000;
|
||||||
|
|
||||||
|
function hasActiveWsClients() {
|
||||||
|
if (!wss) return false;
|
||||||
|
let hasActive = false;
|
||||||
|
wss.clients.forEach((c) => {
|
||||||
|
if (c.readyState === 1) hasActive = true;
|
||||||
|
});
|
||||||
|
return hasActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureTorrentSnapshotCache(entry) {
|
||||||
|
if (!entry) return entry;
|
||||||
|
const { torrent, savePath } = entry;
|
||||||
|
if (!entry.rootFolder && savePath) entry.rootFolder = path.basename(savePath);
|
||||||
|
|
||||||
|
if (!entry.filesSnapshot && Array.isArray(torrent?.files)) {
|
||||||
|
entry.filesSnapshot = torrent.files.map((f, i) => ({
|
||||||
|
index: i,
|
||||||
|
name: f.name,
|
||||||
|
length: f.length
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
entry.bestVideoIndex === undefined ||
|
||||||
|
entry.bestVideoIndex === null
|
||||||
|
) {
|
||||||
|
entry.bestVideoIndex = pickBestVideoFile(torrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bestVideo =
|
||||||
|
torrent?.files?.[entry.bestVideoIndex] || torrent?.files?.[0];
|
||||||
|
if (!bestVideo || !savePath || !entry.rootFolder) return entry;
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
const shouldCheckThumbnail =
|
||||||
|
!entry.thumbnail ||
|
||||||
|
!entry.thumbnailCheckedAt ||
|
||||||
|
now - entry.thumbnailCheckedAt > THUMBNAIL_CHECK_INTERVAL_MS;
|
||||||
|
|
||||||
|
if (shouldCheckThumbnail) {
|
||||||
|
const relPath = path.join(entry.rootFolder, bestVideo.path);
|
||||||
|
const { relThumb, absThumb } = getVideoThumbnailPaths(relPath);
|
||||||
|
entry.thumbnailCheckedAt = now;
|
||||||
|
entry.thumbnailRelPath = relThumb;
|
||||||
|
|
||||||
|
if (fs.existsSync(absThumb)) {
|
||||||
|
entry.thumbnail = thumbnailUrl(relThumb);
|
||||||
|
entry.thumbnailQueued = false;
|
||||||
|
} else if (torrent?.progress === 1 || torrent?.done) {
|
||||||
|
if (!entry.thumbnailQueued) {
|
||||||
|
queueVideoThumbnail(path.join(savePath, bestVideo.path), relPath);
|
||||||
|
entry.thumbnailQueued = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
// --- Snapshot (thumbnail dahil, tracker + tarih eklendi) ---
|
// --- Snapshot (thumbnail dahil, tracker + tarih eklendi) ---
|
||||||
function snapshot() {
|
function snapshot() {
|
||||||
const torrentEntries = Array.from(torrents.values()).map(
|
const torrentEntries = Array.from(torrents.values()).map((entry) => {
|
||||||
({ torrent, selectedIndex, savePath, added, paused }) => {
|
const { torrent, selectedIndex, savePath, added, paused } = entry;
|
||||||
const rootFolder = path.basename(savePath);
|
ensureTorrentSnapshotCache(entry);
|
||||||
const bestVideoIndex = pickBestVideoFile(torrent);
|
const rootFolder = entry?.rootFolder || path.basename(savePath);
|
||||||
const bestVideo = torrent.files[bestVideoIndex];
|
|
||||||
let thumbnail = null;
|
|
||||||
|
|
||||||
if (bestVideo) {
|
|
||||||
const relPath = path.join(rootFolder, bestVideo.path);
|
|
||||||
const { relThumb, absThumb } = getVideoThumbnailPaths(relPath);
|
|
||||||
if (fs.existsSync(absThumb)) thumbnail = thumbnailUrl(relThumb);
|
|
||||||
else if (torrent.progress === 1)
|
|
||||||
queueVideoThumbnail(path.join(savePath, bestVideo.path), relPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
infoHash: torrent.infoHash,
|
infoHash: torrent.infoHash,
|
||||||
@@ -4282,13 +4416,9 @@ function snapshot() {
|
|||||||
added,
|
added,
|
||||||
savePath, // 🆕 BURASI!
|
savePath, // 🆕 BURASI!
|
||||||
paused: paused || false, // Pause durumunu ekle
|
paused: paused || false, // Pause durumunu ekle
|
||||||
files: torrent.files.map((f, i) => ({
|
files: entry?.filesSnapshot || [],
|
||||||
index: i,
|
|
||||||
name: f.name,
|
|
||||||
length: f.length
|
|
||||||
})),
|
|
||||||
selectedIndex,
|
selectedIndex,
|
||||||
thumbnail
|
thumbnail: entry?.thumbnail || null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -4308,9 +4438,24 @@ function wireTorrent(torrent, { savePath, added, respond, restored = false }) {
|
|||||||
selectedIndex: 0,
|
selectedIndex: 0,
|
||||||
savePath,
|
savePath,
|
||||||
added,
|
added,
|
||||||
paused: false
|
paused: false,
|
||||||
|
filesSnapshot: null,
|
||||||
|
thumbnail: null,
|
||||||
|
thumbnailCheckedAt: 0,
|
||||||
|
thumbnailQueued: false,
|
||||||
|
bestVideoIndex: null,
|
||||||
|
rootFolder: savePath ? path.basename(savePath) : null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const scheduleTorrentSnapshot = () => scheduleSnapshotBroadcast();
|
||||||
|
torrent.on("download", scheduleTorrentSnapshot);
|
||||||
|
torrent.on("upload", scheduleTorrentSnapshot);
|
||||||
|
torrent.on("wire", scheduleTorrentSnapshot);
|
||||||
|
torrent.on("noPeers", scheduleTorrentSnapshot);
|
||||||
|
torrent.on("metadata", scheduleTorrentSnapshot);
|
||||||
|
torrent.on("warning", scheduleTorrentSnapshot);
|
||||||
|
torrent.on("error", scheduleTorrentSnapshot);
|
||||||
|
|
||||||
torrent.on("ready", () => {
|
torrent.on("ready", () => {
|
||||||
onTorrentReady({ torrent, savePath, added, respond, restored });
|
onTorrentReady({ torrent, savePath, added, respond, restored });
|
||||||
});
|
});
|
||||||
@@ -4322,12 +4467,19 @@ function wireTorrent(torrent, { savePath, added, respond, restored = false }) {
|
|||||||
|
|
||||||
function onTorrentReady({ torrent, savePath, added, respond }) {
|
function onTorrentReady({ torrent, savePath, added, respond }) {
|
||||||
const selectedIndex = pickBestVideoFile(torrent);
|
const selectedIndex = pickBestVideoFile(torrent);
|
||||||
|
const existing = torrents.get(torrent.infoHash) || {};
|
||||||
torrents.set(torrent.infoHash, {
|
torrents.set(torrent.infoHash, {
|
||||||
torrent,
|
torrent,
|
||||||
selectedIndex,
|
selectedIndex,
|
||||||
savePath,
|
savePath,
|
||||||
added,
|
added,
|
||||||
paused: false
|
paused: false,
|
||||||
|
filesSnapshot: existing.filesSnapshot || null,
|
||||||
|
thumbnail: existing.thumbnail || null,
|
||||||
|
thumbnailCheckedAt: existing.thumbnailCheckedAt || 0,
|
||||||
|
thumbnailQueued: existing.thumbnailQueued || false,
|
||||||
|
bestVideoIndex: selectedIndex,
|
||||||
|
rootFolder: savePath ? path.basename(savePath) : existing.rootFolder || null
|
||||||
});
|
});
|
||||||
const rootFolder = path.basename(savePath);
|
const rootFolder = path.basename(savePath);
|
||||||
upsertInfoFile(savePath, {
|
upsertInfoFile(savePath, {
|
||||||
@@ -4356,7 +4508,7 @@ function onTorrentReady({ torrent, savePath, added, respond }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (typeof respond === "function") respond(payload);
|
if (typeof respond === "function") respond(payload);
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onTorrentDone({ torrent }) {
|
async function onTorrentDone({ torrent }) {
|
||||||
@@ -4594,7 +4746,7 @@ async function onTorrentDone({ torrent }) {
|
|||||||
infoUpdate.files[bestVideoPath].type || rootType;
|
infoUpdate.files[bestVideoPath].type || rootType;
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth router ve middleware createAuth ile yüklendi
|
// Auth router ve middleware createAuth ile yüklendi
|
||||||
@@ -4741,7 +4893,7 @@ app.delete("/api/torrents/:hash", requireAuth, (req, res) => {
|
|||||||
`ℹ️ ${req.params.hash} torrent'i tamamlandığı için yalnızca Transfers listesinden kaldırıldı; dosyalar tutuldu.`
|
`ℹ️ ${req.params.hash} torrent'i tamamlandığı için yalnızca Transfers listesinden kaldırıldı; dosyalar tutuldu.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
res.json({
|
res.json({
|
||||||
ok: true,
|
ok: true,
|
||||||
filesRemoved: !isComplete
|
filesRemoved: !isComplete
|
||||||
@@ -4877,7 +5029,7 @@ app.post("/api/torrents/toggle-all", requireAuth, (req, res) => {
|
|||||||
|
|
||||||
global.pausedTorrents = pausedTorrents;
|
global.pausedTorrents = pausedTorrents;
|
||||||
|
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
res.json({
|
res.json({
|
||||||
ok: true,
|
ok: true,
|
||||||
action,
|
action,
|
||||||
@@ -4935,7 +5087,7 @@ app.post("/api/torrents/:hash/toggle", requireAuth, (req, res) => {
|
|||||||
}
|
}
|
||||||
global.pausedTorrents = pausedTorrents;
|
global.pausedTorrents = pausedTorrents;
|
||||||
|
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
res.json({
|
res.json({
|
||||||
ok: true,
|
ok: true,
|
||||||
action,
|
action,
|
||||||
@@ -5106,16 +5258,16 @@ app.delete("/api/file", requireAuth, (req, res) => {
|
|||||||
entry?.torrent?.destroy(() => {
|
entry?.torrent?.destroy(() => {
|
||||||
torrents.delete(matchedInfoHash);
|
torrents.delete(matchedInfoHash);
|
||||||
console.log(`🧹 Torrent kaydı da temizlendi: ${matchedInfoHash}`);
|
console.log(`🧹 Torrent kaydı da temizlendi: ${matchedInfoHash}`);
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
|
|
||||||
// Torrent silindiğinde disk space bilgisini güncelle
|
// Torrent silindiğinde disk space bilgisini güncelle
|
||||||
broadcastDiskSpace();
|
broadcastDiskSpace();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -6958,13 +7110,6 @@ wss.on("connection", (ws) => {
|
|||||||
broadcastDiskSpace();
|
broadcastDiskSpace();
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- ⏱️ Her 2 saniyede bir aktif torrent durumu yayınla ---
|
|
||||||
setInterval(() => {
|
|
||||||
if (torrents.size > 0) {
|
|
||||||
broadcastSnapshot();
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
// --- ⏱️ Her 30 saniyede bir disk space bilgisi yayınla ---
|
// --- ⏱️ Her 30 saniyede bir disk space bilgisi yayınla ---
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
broadcastDiskSpace();
|
broadcastDiskSpace();
|
||||||
@@ -6978,7 +7123,9 @@ app.get("/api/disk-space", requireAuth, async (req, res) => {
|
|||||||
fs.mkdirSync(DOWNLOAD_DIR, { recursive: true });
|
fs.mkdirSync(DOWNLOAD_DIR, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const diskInfo = await getSystemDiskInfo(DOWNLOAD_DIR);
|
const diskInfo = await getCachedDiskInfo({
|
||||||
|
force: req.query?.fresh === "1"
|
||||||
|
});
|
||||||
res.json(diskInfo);
|
res.json(diskInfo);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("❌ Disk space error:", err.message);
|
console.error("❌ Disk space error:", err.message);
|
||||||
@@ -7568,7 +7715,7 @@ app.patch("/api/folder", requireAuth, (req, res) => {
|
|||||||
console.log(`📁 Kök klasör yeniden adlandırıldı: ${rootFolder} -> ${newRootFolder}`);
|
console.log(`📁 Kök klasör yeniden adlandırıldı: ${rootFolder} -> ${newRootFolder}`);
|
||||||
broadcastFileUpdate(rootFolder);
|
broadcastFileUpdate(rootFolder);
|
||||||
broadcastFileUpdate(newRootFolder);
|
broadcastFileUpdate(newRootFolder);
|
||||||
broadcastSnapshot();
|
scheduleSnapshotBroadcast();
|
||||||
return res.json({
|
return res.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: "Klasör yeniden adlandırıldı",
|
message: "Klasör yeniden adlandırıldı",
|
||||||
|
|||||||
Reference in New Issue
Block a user