Download pause/resume özelliği eklendi.
This commit is contained in:
209
server/server.js
209
server/server.js
@@ -2091,7 +2091,7 @@ function broadcastSnapshot() {
|
||||
// --- Snapshot (thumbnail dahil, tracker + tarih eklendi) ---
|
||||
function snapshot() {
|
||||
return Array.from(torrents.values()).map(
|
||||
({ torrent, selectedIndex, savePath, added }) => {
|
||||
({ torrent, selectedIndex, savePath, added, paused }) => {
|
||||
const rootFolder = path.basename(savePath);
|
||||
const bestVideoIndex = pickBestVideoFile(torrent);
|
||||
const bestVideo = torrent.files[bestVideoIndex];
|
||||
@@ -2110,12 +2110,13 @@ function snapshot() {
|
||||
name: torrent.name,
|
||||
progress: torrent.progress,
|
||||
downloaded: torrent.downloaded,
|
||||
downloadSpeed: torrent.downloadSpeed,
|
||||
uploadSpeed: torrent.uploadSpeed,
|
||||
numPeers: torrent.numPeers,
|
||||
downloadSpeed: paused ? 0 : torrent.downloadSpeed, // Pause durumunda hız 0
|
||||
uploadSpeed: paused ? 0 : torrent.uploadSpeed, // Pause durumunda hız 0
|
||||
numPeers: paused ? 0 : torrent.numPeers, // Pause durumunda peer sayısı 0
|
||||
tracker: torrent.announce?.[0] || null,
|
||||
added,
|
||||
savePath, // 🆕 BURASI!
|
||||
paused: paused || false, // Pause durumunu ekle
|
||||
files: torrent.files.map((f, i) => ({
|
||||
index: i,
|
||||
name: f.name,
|
||||
@@ -2171,7 +2172,8 @@ app.post("/api/transfer", requireAuth, upload.single("torrent"), (req, res) => {
|
||||
torrent,
|
||||
selectedIndex: 0,
|
||||
savePath,
|
||||
added
|
||||
added,
|
||||
paused: false
|
||||
});
|
||||
|
||||
// --- Metadata geldiğinde ---
|
||||
@@ -2181,7 +2183,8 @@ app.post("/api/transfer", requireAuth, upload.single("torrent"), (req, res) => {
|
||||
torrent,
|
||||
selectedIndex,
|
||||
savePath,
|
||||
added
|
||||
added,
|
||||
paused: false
|
||||
});
|
||||
const rootFolder = path.basename(savePath);
|
||||
upsertInfoFile(savePath, {
|
||||
@@ -2422,6 +2425,200 @@ app.delete("/api/torrents/:hash", requireAuth, (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
function getPieceCount(torrent) {
|
||||
if (!torrent) return 0;
|
||||
if (Array.isArray(torrent.pieces)) return torrent.pieces.length;
|
||||
const pieces = torrent.pieces;
|
||||
if (pieces && typeof pieces.length === "number") return pieces.length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function pauseTorrentEntry(entry) {
|
||||
const torrent = entry?.torrent;
|
||||
if (!torrent || torrent._destroyed || entry.paused) return false;
|
||||
|
||||
entry.previousSelection = entry.selectedIndex;
|
||||
|
||||
const pieceCount = getPieceCount(torrent);
|
||||
if (pieceCount > 0 && typeof torrent.deselect === "function") {
|
||||
try {
|
||||
torrent.deselect(0, pieceCount - 1, 0);
|
||||
} catch (err) {
|
||||
console.warn("Torrent deselect failed during pause:", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(torrent.files)) {
|
||||
for (const file of torrent.files) {
|
||||
if (file && typeof file.deselect === "function") {
|
||||
try {
|
||||
file.deselect();
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`File deselect failed during pause (${torrent.infoHash}):`,
|
||||
err.message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof torrent.pause === "function") {
|
||||
try {
|
||||
torrent.pause();
|
||||
} catch (err) {
|
||||
console.warn("Torrent pause method failed:", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
entry.paused = true;
|
||||
entry.pausedAt = Date.now();
|
||||
return true;
|
||||
}
|
||||
|
||||
function resumeTorrentEntry(entry) {
|
||||
const torrent = entry?.torrent;
|
||||
if (!torrent || torrent._destroyed || !entry.paused) return false;
|
||||
|
||||
const pieceCount = getPieceCount(torrent);
|
||||
if (pieceCount > 0 && typeof torrent.select === "function") {
|
||||
try {
|
||||
torrent.select(0, pieceCount - 1, 0);
|
||||
} catch (err) {
|
||||
console.warn("Torrent select failed during resume:", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(torrent.files)) {
|
||||
const preferredIndex =
|
||||
entry.previousSelection !== undefined
|
||||
? entry.previousSelection
|
||||
: entry.selectedIndex ?? 0;
|
||||
const targetFile =
|
||||
torrent.files[preferredIndex] || torrent.files[0] || null;
|
||||
if (targetFile && typeof targetFile.select === "function") {
|
||||
try {
|
||||
targetFile.select();
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`File select failed during resume (${torrent.infoHash}):`,
|
||||
err.message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof torrent.resume === "function") {
|
||||
try {
|
||||
torrent.resume();
|
||||
} catch (err) {
|
||||
console.warn("Torrent resume method failed:", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
entry.paused = false;
|
||||
delete entry.pausedAt;
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- Tüm torrentleri durdur/devam ettir ---
|
||||
app.post("/api/torrents/toggle-all", requireAuth, (req, res) => {
|
||||
try {
|
||||
const { action } = req.body; // 'pause' veya 'resume'
|
||||
|
||||
if (!action || (action !== 'pause' && action !== 'resume')) {
|
||||
return res.status(400).json({ error: "action 'pause' veya 'resume' olmalı" });
|
||||
}
|
||||
|
||||
let updatedCount = 0;
|
||||
const pausedTorrents = new Set();
|
||||
|
||||
for (const [infoHash, entry] of torrents.entries()) {
|
||||
if (!entry?.torrent || entry.torrent._destroyed) continue;
|
||||
|
||||
try {
|
||||
const changed =
|
||||
action === "pause"
|
||||
? pauseTorrentEntry(entry)
|
||||
: resumeTorrentEntry(entry);
|
||||
if (changed) updatedCount++;
|
||||
if (entry.paused) pausedTorrents.add(infoHash);
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`⚠️ Torrent ${infoHash} ${action} işleminde hata:`,
|
||||
err.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
global.pausedTorrents = pausedTorrents;
|
||||
|
||||
broadcastSnapshot();
|
||||
res.json({
|
||||
ok: true,
|
||||
action,
|
||||
updatedCount,
|
||||
totalCount: torrents.size
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("❌ Toggle all torrents error:", err.message);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// --- Tek torrent'i durdur/devam ettir ---
|
||||
app.post("/api/torrents/:hash/toggle", requireAuth, (req, res) => {
|
||||
try {
|
||||
const { action } = req.body; // 'pause' veya 'resume'
|
||||
const infoHash = req.params.hash;
|
||||
|
||||
if (!action || (action !== 'pause' && action !== 'resume')) {
|
||||
return res.status(400).json({ error: "action 'pause' veya 'resume' olmalı" });
|
||||
}
|
||||
|
||||
const entry = torrents.get(infoHash);
|
||||
if (!entry || !entry.torrent || entry.torrent._destroyed) {
|
||||
return res.status(404).json({ error: "torrent bulunamadı" });
|
||||
}
|
||||
|
||||
const changed =
|
||||
action === "pause"
|
||||
? pauseTorrentEntry(entry)
|
||||
: resumeTorrentEntry(entry);
|
||||
|
||||
if (!changed) {
|
||||
const message =
|
||||
action === "pause"
|
||||
? "Torrent zaten durdurulmuş"
|
||||
: "Torrent zaten devam ediyor";
|
||||
return res.json({
|
||||
ok: true,
|
||||
action,
|
||||
infoHash,
|
||||
paused: entry.paused,
|
||||
message
|
||||
});
|
||||
}
|
||||
|
||||
const pausedTorrents = new Set();
|
||||
for (const [hash, item] of torrents.entries()) {
|
||||
if (item?.paused) pausedTorrents.add(hash);
|
||||
}
|
||||
global.pausedTorrents = pausedTorrents;
|
||||
|
||||
broadcastSnapshot();
|
||||
res.json({
|
||||
ok: true,
|
||||
action,
|
||||
infoHash,
|
||||
paused: entry.paused
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("❌ Toggle single torrent error:", err.message);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// --- GENEL MEDYA SUNUMU (🆕 resimler + videolar) ---
|
||||
app.get("/media/:path(*)", requireAuth, (req, res) => {
|
||||
const relPath = req.params.path;
|
||||
|
||||
Reference in New Issue
Block a user