diff --git a/public/index.html b/public/index.html index a8f6973..b62a257 100644 --- a/public/index.html +++ b/public/index.html @@ -57,34 +57,20 @@
-
Add New Torrent
+
+
+
Add New Torrent
+
+
-
+
- - - + + +
- +
diff --git a/public/style.css b/public/style.css index 63e43fe..c54fd24 100644 --- a/public/style.css +++ b/public/style.css @@ -241,9 +241,10 @@ } .card-header { - padding: 1rem 1.25rem; /* Match Bootstrap default padding */ - background-color: #f8f9fa; /* Light gray background, consistent with Bootstrap cards */ - border-bottom: 1px solid rgba(0, 0, 0, 0.125); /* Default card border */ + padding: 1rem 1.25rem; + background-color: #f8f9fa; + border-bottom: 1px solid rgba(0, 0, 0, 0.125); + min-height: 70px; /* Adjust based on your design */ } .card-header h5 { diff --git a/server.js b/server.js index 6c55255..d4b657a 100644 --- a/server.js +++ b/server.js @@ -10,7 +10,6 @@ import path from "path"; import { fileURLToPath } from "url"; import rateLimit from "express-rate-limit"; import winston from "winston"; -import { exec } from "child_process"; import JSONStream from "JSONStream"; import { lock, unlock } from "proper-lockfile"; import os from "os"; @@ -110,35 +109,26 @@ async function loadSavedTorrentsFromFile() { } await loadSavedTorrentsFromFile(); -// **Batch Save Torrents with File Locking** -let pendingChanges = false; - +// **Asynchronous Save Torrents with File Locking** async function saveTorrents() { - pendingChanges = true; -} - -setInterval(async () => { - if (pendingChanges) { - await lock(TORRENTS_FILE); - try { - const stream = JSONStream.stringify("[\n", ",\n", "\n]"); - const fileStream = fs.createWriteStream(TORRENTS_FILE); - stream.pipe(fileStream); - savedTorrents.forEach((torrent) => stream.write(torrent)); - stream.end(); - await new Promise((resolve, reject) => { - fileStream.on("finish", resolve); - fileStream.on("error", reject); - }); - pendingChanges = false; - logger.info("Torrents saved to file"); - } catch (err) { - logger.error("Error saving torrents: " + err); - } finally { - await unlock(TORRENTS_FILE); - } + await lock(TORRENTS_FILE); + try { + const stream = JSONStream.stringify("[\n", ",\n", "\n]"); + const fileStream = fs.createWriteStream(TORRENTS_FILE); + stream.pipe(fileStream); + savedTorrents.forEach((torrent) => stream.write(torrent)); + stream.end(); + await new Promise((resolve, reject) => { + fileStream.on("finish", resolve); + fileStream.on("error", reject); + }); + logger.info("Torrents saved to file"); + } catch (err) { + logger.error("Error saving torrents: " + err); + } finally { + await unlock(TORRENTS_FILE); } -}, 5000); +} // **Ensure Storage Directory Exists** let STORAGE_DIR = config.storageDir; @@ -262,7 +252,7 @@ app.get("/torrents", (req, res) => { } }); -// Add after existing routes in server.js +// Download route app.get("/download/:infoHash", async (req, res) => { const infoHash = req.params.infoHash; const savedData = savedTorrents.find((t) => t.infoHash === infoHash); @@ -284,22 +274,33 @@ app.get("/download/:infoHash", async (req, res) => { // Pause a torrent app.post("/pause/:infoHash", async (req, res) => { const infoHash = req.params.infoHash; + logger.debug(`Pausing torrent with infoHash: ${infoHash}`); // Added logging + // Validate infoHash + if (!infoHash || typeof infoHash !== "string") { + return res.status(400).json({ error: "Invalid infoHash" }); + } const torrent = client.get(infoHash); if (!torrent) { return res .status(400) .json({ error: "Torrent not active or already paused" }); } - client.remove(infoHash, { destroyStore: false }, async (err) => { - if (err) { - logger.error("Error pausing torrent: " + err); - return res.status(500).json({ error: "Error pausing torrent" }); - } + try { + // Wrap client.remove in a promise to handle both synchronous and asynchronous errors + await new Promise((resolve, reject) => { + client.remove(infoHash, { destroyStore: false }, (err) => { + if (err) reject(err); + else resolve(); + }); + }); const entry = savedTorrents.find((s) => s.infoHash === infoHash); if (entry) entry.paused = true; - saveTorrents(); + await saveTorrents(); // Await the save operation res.json({ message: "Torrent paused" }); - }); + } catch (err) { + logger.error("Error pausing torrent: " + err); + res.status(500).json({ error: "Error pausing torrent" }); + } }); // Resume a torrent @@ -310,7 +311,7 @@ app.post("/resume/:infoHash", async (req, res) => { return res.status(400).json({ error: "Torrent not found in saved data" }); } entry.paused = false; - saveTorrents(); + await saveTorrents(); // Await the save operation const torrentPath = path.join(STORAGE_DIR, entry.name); const exists = await pathExists(torrentPath); try { @@ -384,7 +385,7 @@ app.post("/add", (req, res) => { paused: false, }; savedTorrents.push(torrentData); - saveTorrents(); + saveTorrents(); // Call saveTorrents res.json(torrentData); }); } catch (err) { @@ -457,7 +458,7 @@ app.post("/upload", upload.single("video"), async (req, res) => { }; savedTorrents.push(torrentData); - saveTorrents(); + saveTorrents(); // Call saveTorrents res.json(torrentData); }); } catch (err) { @@ -488,7 +489,7 @@ app.delete("/remove/:infoHash", (req, res) => { return res.status(500).json({ error: "Error removing torrent" }); } savedTorrents = savedTorrents.filter((t) => t.infoHash !== infoHash); - saveTorrents(); + await saveTorrents(); // Await the save operation const torrentPath = path.join(STORAGE_DIR, torrentName); if (await pathExists(torrentPath)) { try {