-
+
+ {#key encName}
+
+
+ {/key}
{/if}
+{#if showImageModal && selectedImage}
+
+
+
(showImageModal = false)}>
+
+
+
+
+
+
+
}`})
+
+
+{/if}
diff --git a/client/src/routes/Transfers.svelte b/client/src/routes/Transfers.svelte
index b29334a..8e56268 100644
--- a/client/src/routes/Transfers.svelte
+++ b/client/src/routes/Transfers.svelte
@@ -428,7 +428,13 @@
{/if}
+
\ No newline at end of file
diff --git a/client/src/styles/main.css b/client/src/styles/main.css
index b76d4a8..a5d2710 100644
--- a/client/src/styles/main.css
+++ b/client/src/styles/main.css
@@ -1,3 +1,6 @@
+/* =======================================================
+ 🎨 RENK DEĞİŞKENLERİ VE TEMEL STİLLER
+ ======================================================= */
:root {
--yellow: #f5b333;
--yellow-dark: #e2a62f;
@@ -6,9 +9,11 @@
--muted: #666;
--green: #4caf50;
}
+
* {
box-sizing: border-box;
}
+
html,
body,
#app {
@@ -19,27 +24,43 @@ body,
color: #222;
background: #fff;
}
+
+/* =======================================================
+ 📐 GENEL YERLEŞİM
+ ======================================================= */
.app {
display: grid;
grid-template-columns: 220px 1fr;
height: 100%;
}
-/* Sidebar */
+
+.content {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+/* =======================================================
+ 🧭 SIDEBAR
+ ======================================================= */
.sidebar {
background: var(--sidebar);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
}
+
.sidebar .logo {
padding: 12px 16px;
font-weight: 900;
font-size: 28px;
letter-spacing: 0.5px;
}
+
.sidebar .menu {
padding-top: 6px;
}
+
.sidebar .menu .item {
display: flex;
align-items: center;
@@ -48,22 +69,37 @@ body,
color: #222;
cursor: pointer;
text-decoration: none;
+ transition: background 0.2s ease, color 0.2s ease;
}
-.sidebar .menu .item.active {
- background: #fff;
- border-left: 3px solid var(--yellow);
-}
+
.sidebar .menu .item .icon {
width: 18px;
text-align: center;
color: #333;
}
-/* Content */
-.content {
- display: flex;
- flex-direction: column;
- height: 100%;
+
+/* Hover efekti */
+.sidebar .menu .item:hover {
+ background: #f0f0f0;
+ color: #000;
}
+
+/* Aktif menü öğesi */
+.sidebar .menu .item.active {
+ background: #e5e5e5;
+ border-left: 3px solid var(--yellow);
+ font-weight: 600;
+ color: #000;
+}
+
+.sidebar .menu .item:hover .icon,
+.sidebar .menu .item.active .icon {
+ color: #000;
+}
+
+/* =======================================================
+ 🔍 TOPBAR VE ARAMA
+ ======================================================= */
.topbar {
display: flex;
align-items: center;
@@ -71,6 +107,7 @@ body,
padding: 12px 16px;
border-bottom: 1px solid var(--border);
}
+
.search {
flex: 1;
display: flex;
@@ -81,12 +118,17 @@ body,
border-radius: 6px;
padding: 8px 12px;
}
+
.search input {
border: none;
outline: none;
background: transparent;
flex: 1;
}
+
+/* =======================================================
+ 🟨 BUTONLAR
+ ======================================================= */
.btn-primary {
background: var(--yellow);
border: 1px solid var(--yellow-dark);
@@ -95,20 +137,37 @@ body,
padding: 10px 14px;
border-radius: 6px;
cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ text-transform: uppercase;
+ gap: 6px;
+ height: 36px;
+ transition: background 0.2s;
}
+
+.btn-primary:hover {
+ background: var(--yellow-dark);
+}
+
.btn-primary:active {
transform: translateY(1px);
}
-/* Files */
+
+/* =======================================================
+ 📂 FILES SAYFASI
+ ======================================================= */
.files {
margin: 0 16px 16px 16px;
flex: 1;
border-top: 2px solid #f0c24d;
padding-top: 14px;
}
+
.files h2 {
margin: 0 0 10px 0;
}
+
.empty {
display: flex;
flex-direction: column;
@@ -119,6 +178,7 @@ body,
border: 2px dashed var(--border);
border-radius: 8px;
}
+
.create-folder {
background: var(--yellow);
border: 1px solid var(--yellow-dark);
@@ -127,7 +187,10 @@ body,
font-weight: 700;
cursor: pointer;
}
-/* Transfers Page */
+
+/* =======================================================
+ 📦 TRANSFERS SAYFASI
+ ======================================================= */
.torrent {
display: flex;
align-items: center;
@@ -135,9 +198,11 @@ body,
padding: 10px 12px;
border-bottom: 1px solid var(--border);
}
+
.torrent:last-child {
border-bottom: none;
}
+
.progress {
height: 8px;
background: #eee;
@@ -145,28 +210,155 @@ body,
overflow: hidden;
flex: 1;
}
+
.progress > div {
height: 100%;
background: var(--green);
transition: width 0.3s;
}
+
.small {
color: var(--muted);
font-size: 12px;
}
-/* ====== Responsive & Off-Canvas Sidebar (EKLENDİ) ====== */
-/* Hamburger butonunu varsayılan gizle; mobilde göstereceğiz */
-.menu-toggle {
- display: none;
- background: none;
- border: none;
- font-size: 20px;
- color: #333;
- cursor: pointer;
+/* =======================================================
+ 🎞️ MODAL & PLAYER (ORTAK)
+ ======================================================= */
+.modal-overlay {
+ position: fixed;
+ inset: 0;
+ backdrop-filter: blur(10px);
+ background: rgba(0, 0, 0, 0.8);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 999;
+}
+
+.modal-content {
+ width: 70%;
+ height: 70%;
+ background: #1a1a1a;
+ border-radius: 12px;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ box-shadow: 0 0 30px rgba(0, 0, 0, 0.8);
+}
+
+/* === Video Player === */
+.custom-player {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ background: #000;
+}
+
+.video-element {
+ flex: 1;
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ background: #000;
+ border: none;
+ outline: none;
+}
+
+.video-element:focus {
+ outline: none !important;
+ box-shadow: none !important;
+}
+
+/* === Kontroller === */
+.controls {
+ background: #1c1c1c;
+ padding: 10px 16px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ border-top: 1px solid #333;
+}
+
+.top-controls {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.control-btn {
+ background: none;
+ border: none;
+ color: #fff;
+ font-size: 18px;
+ cursor: pointer;
+ transition: opacity 0.2s;
+}
+
+.control-btn:hover {
+ opacity: 0.7;
+}
+
+.right-controls {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+/* === Ses Kaydırıcısı === */
+.volume-slider {
+ -webkit-appearance: none;
+ width: 100px;
+ height: 4px;
+ border-radius: 2px;
+ background: linear-gradient(
+ to right,
+ #ff3b30 calc(var(--fill, 100%) * 1%),
+ rgba(255, 255, 255, 0.3) calc(var(--fill, 100%) * 1%)
+ );
+ outline: none;
+ cursor: pointer;
+ transition: background 0.2s ease;
+}
+
+.volume-slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ background: #fff;
+ cursor: pointer;
+ margin-top: -4px;
+ transition: transform 0.2s ease;
+}
+
+.volume-slider::-webkit-slider-thumb:hover {
+ transform: scale(1.3);
+}
+
+/* === Alt Kontroller === */
+.bottom-controls {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.progress-slider {
+ flex: 1;
+ cursor: pointer;
+ accent-color: #27ae60;
+}
+
+.time {
+ color: #ccc;
+ font-size: 13px;
+ min-width: 90px;
+ text-align: right;
+ white-space: nowrap;
}
-/* Sidebar arkası için tıklanabilir backdrop (mobilde sidebar açıkken) */
.backdrop {
position: fixed;
inset: 0;
@@ -174,14 +366,95 @@ body,
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease;
- z-index: 999; /* sidebar’ın üstünde */
+ z-index: 999;
}
+
.backdrop.show {
opacity: 1;
pointer-events: auto;
}
-/* Tablet ve aşağısında grid tek sütun; sidebar off-canvas olur */
+/* === Global Close Button (Resim + Video) === */
+.global-close-btn,
+.image-close-btn {
+ position: fixed;
+ top: 20px;
+ right: 30px;
+ background: rgba(0, 0, 0, 0.6);
+ border: none;
+ color: #fff;
+ font-size: 36px;
+ cursor: pointer;
+ z-index: 2100;
+ border-radius: 50%;
+ width: 46px;
+ height: 46px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: background 0.2s ease, transform 0.2s ease;
+}
+
+.global-close-btn:hover,
+.image-close-btn:hover {
+ background: rgba(255, 255, 255, 0.15);
+ transform: scale(1.05);
+}
+
+/* =======================================================
+ 🖼️ RESİM MODALI
+ ======================================================= */
+.image-modal-overlay {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.8);
+ backdrop-filter: blur(10px);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 2000;
+}
+
+.image-modal-content {
+ position: relative;
+ max-width: 90%;
+ max-height: 90%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.image-modal-img {
+ max-width: 75vw;
+ max-height: 75vh;
+ object-fit: contain;
+ border-radius: 8px;
+ box-shadow: 0 0 25px rgba(0, 0, 0, 0.6);
+}
+
+/* === Modal Başlığı (Ortak: Files + Transfers) === */
+.modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: #2a2a2a;
+ padding: 10px 16px;
+ color: #fff;
+ font-size: 16px;
+ font-weight: 500;
+}
+
+.video-title {
+ flex: 1;
+ text-align: center;
+ font-weight: 600;
+}
+
+/* =======================================================
+ 📱 RESPONSIVE
+ ======================================================= */
+
+/* Tablet ve aşağısı */
@media (max-width: 768px) {
.app {
grid-template-columns: 1fr;
@@ -193,6 +466,11 @@ body,
justify-content: center;
width: 36px;
height: 36px;
+ background: none;
+ border: none;
+ font-size: 20px;
+ color: #333;
+ cursor: pointer;
}
.sidebar {
@@ -206,48 +484,16 @@ body,
transition: left 0.25s ease;
z-index: 1000;
}
+
.sidebar.open {
left: 0;
}
- /* Genel içerik kenar boşluklarını sıkılaştır */
.files {
margin: 0 10px 14px 10px;
padding-top: 12px;
}
- /* Transfers ve diğer sayfalar için liste öğelerini dikeyleştir */
- .torrent {
- /* Transfers’teki kutular */
- grid-template-columns: 1fr !important;
- gap: 10px;
- }
- .thumb {
- width: 100% !important;
- height: 180px !important;
- }
- .torrent-header {
- flex-direction: column;
- align-items: flex-start;
- gap: 4px;
- }
- .torrent-hash {
- word-break: break-word;
- white-space: normal;
- font-size: 12px;
- line-height: 1.3;
- }
- .file-row {
- flex-direction: column;
- align-items: flex-start;
- gap: 4px;
- }
- .progress-text {
- text-align: left;
- font-size: 12px;
- }
-
- /* Butonlar eş görünsün ve kolay dokunulsun */
.btn-primary {
flex: 1 1 auto;
justify-content: center;
@@ -256,64 +502,48 @@ body,
font-size: 13px;
}
- /* Modal video oynatıcı mobil uyum */
.modal-content {
width: 95% !important;
height: 72% !important;
border-radius: 10px;
}
+
.controls {
padding: 8px 12px;
gap: 8px;
}
+
.volume-slider {
width: 70px;
}
+
.time {
font-size: 12px;
min-width: 78px;
}
}
-/* === Sidebar Hover & Active Effects === */
-
-/* Hover efekti: hafif gri arka plan */
-.sidebar .menu .item:hover {
- background: #f0f0f0;
- color: #000;
- transition: background 0.2s ease, color 0.2s ease;
-}
-
-/* Aktif item: Daha koyu gri arka plan */
-.sidebar .menu .item.active {
- background: #e5e5e5; /* aktif olan menü item */
- font-weight: 600;
- color: #000;
-}
-
-/* Hover ve aktif durumlarda ikon da koyulaşsın */
-.sidebar .menu .item:hover .icon,
-.sidebar .menu .item.active .icon {
- color: #000;
-}
-
-/* Daha küçük telefonlar */
+/* Küçük telefonlar */
@media (max-width: 480px) {
.btn-primary {
font-size: 12px;
padding: 8px 10px;
height: 34px;
}
+
.torrent-hash {
font-size: 11px;
}
+
.modal-content {
width: 98% !important;
height: 76% !important;
}
+
.volume-slider {
width: 56px;
}
+
.bottom-controls {
flex-direction: column;
align-items: stretch;
diff --git a/client/src/utils/filename.js b/client/src/utils/filename.js
index 9bfb3c4..240d34b 100644
--- a/client/src/utils/filename.js
+++ b/client/src/utils/filename.js
@@ -1,10 +1,10 @@
-// utils/filename.js
-
/**
* Dosya adını temizler ve sadeleştirir.
* Örnek:
* The.Astronaut.2025.1080p.WEBRip.x265-KONTRAST
* → "The Astronaut (2025)"
+ * 1761244874124/Gen.V.S02E08.Cavallo.di.Troia.ITA.ENG.1080p.AMZN.WEB-DL.DDP5.1.H.264-MeM.GP.mkv
+ * → "Gen V S02E08 Cavallo Di Troia"
*/
export function cleanFileName(fullPath) {
if (!fullPath) return "";
@@ -15,8 +15,8 @@ export function cleanFileName(fullPath) {
// 2️⃣ Uzantıyı kaldır
name = name.replace(/\.[^.]+$/, "");
- // 3️⃣ Noktaları boşluğa çevir
- name = name.replace(/\./g, " ");
+ // 3️⃣ Noktaları ve alt tireleri boşluğa çevir
+ name = name.replace(/[._]+/g, " ");
// 4️⃣ Gereksiz etiketleri kaldır
const trashWords = [
@@ -25,7 +25,7 @@ export function cleanFileName(fullPath) {
"2160p",
"4k",
"bluray",
- "web-dl",
+ "web[- ]?dl",
"webrip",
"hdrip",
"x264",
@@ -34,6 +34,7 @@ export function cleanFileName(fullPath) {
"aac",
"h264",
"h265",
+ "ddp5",
"dvdrip",
"brrip",
"remux",
@@ -41,6 +42,8 @@ export function cleanFileName(fullPath) {
"sub",
"subs",
"turkce",
+ "ita",
+ "eng",
"dublado",
"dubbed",
"extended",
@@ -54,57 +57,42 @@ export function cleanFileName(fullPath) {
"hdtv",
"amzn",
"nf",
- "netflix"
+ "netflix",
+ "mem",
+ "gp"
];
const trashRegex = new RegExp(`\\b(${trashWords.join("|")})\\b`, "gi");
name = name.replace(trashRegex, " ");
- // 5️⃣ Köşeli parantez içindekileri kaldır
- name = name.replace(/\[[^\]]*\]/g, "");
+ // 5️⃣ Parantez veya köşeli parantez içindekileri kaldır
+ name = name.replace(/[\[\(].*?[\]\)]/g, " ");
- // 6️⃣ Parantez içindeki tarihleri kaldır
+ // 6️⃣ Fazla tireleri ve sayıları temizle
name = name
- .replace(/\(\d{2}\.\d{2}\.\d{2,4}\)/g, "")
- .replace(/\(\d{4}(-\d{2})?(-\d{2})?\)/g, "");
-
- // 7️⃣ Fazla boşlukları temizle
- name = name.replace(/\s{2,}/g, " ").trim();
-
- // 8️⃣ Yılı tespit et (ör. 2024, 1999)
- const yearMatch = name.match(/\b(19|20)\d{2}\b/);
- let year = "";
- if (yearMatch) {
- year = yearMatch[0];
- name = name.replace(year, "").trim();
- }
-
- // 9️⃣ Dizi formatı (S03E01) varsa koru
- const match = name.match(/(.+?)\s*-\s*(S\d{2}E\d{2})/i);
- if (match) {
- const formatted = `${match[1].trim()} - ${match[2].toUpperCase()}`;
- return year ? `${formatted} (${year})` : formatted;
- }
-
- // 🔟 Fazla tireleri ve tire + parantez boşluklarını düzelt
- name = name
- .replace(/[-_]+/g, " ") // birden fazla tireyi temizle
- .replace(/\s-\s*\(/g, " (") // " - (" → " ("
+ .replace(/[-]+/g, " ")
+ .replace(/\b\d{3,4}\b/g, " ") // tek başına 1080, 2025 gibi
+ .replace(/\s{2,}/g, " ")
.trim();
- // 11️⃣ Baş harfleri büyüt
+ // 7️⃣ Dizi formatını (S02E08) koru
+ const episodeMatch = name.match(/(S\d{1,2}E\d{1,2})/i);
+ if (episodeMatch) {
+ const epTag = episodeMatch[0].toUpperCase();
+ // örnek: Gen V S02E08 Cavallo di Troia
+ name = name.replace(episodeMatch[0], epTag);
+ }
+
+ // 8️⃣ Baş harfleri büyüt (küçük kelimeleri koruyarak)
name = name
.split(" ")
- .map(
- (w) =>
- w.length > 1
- ? w[0].toUpperCase() + w.slice(1).toLowerCase()
- : w.toUpperCase()
- )
+ .filter((w) => w.length > 0)
+ .map((w) => {
+ if (["di", "da", "de", "of", "and", "the"].includes(w.toLowerCase()))
+ return w.toLowerCase();
+ return w[0].toUpperCase() + w.slice(1).toLowerCase();
+ })
.join(" ")
.trim();
- // 12️⃣ Yıl varsa sonuna ekle
- if (year) name += ` (${year})`;
-
- return name.trim();
+ return name;
}
diff --git a/server/server.js b/server/server.js
index c466bb9..3e6a284 100644
--- a/server/server.js
+++ b/server/server.js
@@ -29,7 +29,6 @@ app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use("/downloads", express.static(DOWNLOAD_DIR));
-
// --- En uygun video dosyasını seç ---
function pickBestVideoFile(torrent) {
const videoExts = [".mp4", ".webm", ".mkv", ".mov", ".m4v"];
@@ -70,8 +69,8 @@ function snapshot() {
}
// --- Basit kimlik doğrulama sistemi ---
-const USERNAME = process.env.USERNAME
-const PASSWORD = process.env.PASSWORD
+const USERNAME = process.env.USERNAME;
+const PASSWORD = process.env.PASSWORD;
let activeTokens = new Set();
app.post("/api/login", (req, res) => {
@@ -85,14 +84,12 @@ app.post("/api/login", (req, res) => {
});
function requireAuth(req, res, next) {
- const token =
- req.headers.authorization?.split(" ")[1] || req.query.token;
+ const token = req.headers.authorization?.split(" ")[1] || req.query.token;
if (!token || !activeTokens.has(token))
return res.status(401).json({ error: "Unauthorized" });
next();
}
-
// --- Torrent veya magnet ekleme ---
app.post("/api/transfer", requireAuth, upload.single("torrent"), (req, res) => {
try {
@@ -131,8 +128,8 @@ app.post("/api/transfer", requireAuth, upload.single("torrent"), (req, res) => {
infoHash: torrent.infoHash,
name: torrent.name,
selectedIndex,
- tracker: torrent.announce?.[0] || null, // 🆕
- added, // 🆕
+ tracker: torrent.announce?.[0] || null,
+ added,
files: torrent.files.map((f, i) => ({
index: i,
name: f.name,
@@ -206,15 +203,19 @@ app.delete("/api/torrents/:hash", requireAuth, (req, res) => {
});
});
+// --- GENEL MEDYA SUNUMU (🆕 resimler + videolar) ---
app.get("/media/:path(*)", requireAuth, (req, res) => {
- const fullPath = path.join(DOWNLOAD_DIR, req.params.path);
+ const relPath = req.params.path;
+ const fullPath = path.join(DOWNLOAD_DIR, relPath);
if (!fs.existsSync(fullPath)) return res.status(404).send("File not found");
const stat = fs.statSync(fullPath);
const fileSize = stat.size;
+ const type = mime.lookup(fullPath) || "application/octet-stream";
+ const isVideo = String(type).startsWith("video/");
const range = req.headers.range;
- if (range) {
+ if (isVideo && range) {
const [startStr, endStr] = range.replace(/bytes=/, "").split("-");
const start = parseInt(startStr, 10);
const end = endStr ? parseInt(endStr, 10) : fileSize - 1;
@@ -224,21 +225,22 @@ app.get("/media/:path(*)", requireAuth, (req, res) => {
"Content-Range": `bytes ${start}-${end}/${fileSize}`,
"Accept-Ranges": "bytes",
"Content-Length": chunkSize,
- "Content-Type": "video/mp4"
+ "Content-Type": type
};
res.writeHead(206, head);
file.pipe(res);
} else {
const head = {
"Content-Length": fileSize,
- "Content-Type": "video/mp4"
+ "Content-Type": type,
+ "Accept-Ranges": isVideo ? "bytes" : "none"
};
res.writeHead(200, head);
fs.createReadStream(fullPath).pipe(res);
}
});
-// --- 📁 Dosya gezgini: /downloads altındaki dosyaları listele ---
+// --- 📁 Dosya gezgini (🆕 type ve url alanları eklendi; resim thumb'ı) ---
app.get("/api/files", requireAuth, (req, res) => {
const walk = (dir) => {
let result = [];
@@ -251,21 +253,35 @@ app.get("/api/files", requireAuth, (req, res) => {
if (entry.isDirectory()) {
result = result.concat(walk(full));
} else {
- // thumbnail.jpg dosyasını listeleme
if (entry.name.toLowerCase() === "thumbnail.jpg") continue;
- const size = fs.statSync(full).size;
- const parts = rel.split(path.sep);
- const rootHash = parts[0]; // ilk klasör adı
- const thumbPath = path.join(DOWNLOAD_DIR, rootHash, "thumbnail.jpg");
- // ✅ Thumbnail dosyası gerçekten varsa ekle
- const thumb = fs.existsSync(thumbPath)
+ const size = fs.statSync(full).size;
+ const type = mime.lookup(full) || "application/octet-stream";
+
+ // kök klasör (thumbnail varsa video kartlarında kullanıyoruz)
+ const parts = rel.split(path.sep);
+ const rootHash = parts[0];
+ const videoThumbPath = path.join(DOWNLOAD_DIR, rootHash, "thumbnail.jpg");
+ const hasVideoThumb = fs.existsSync(videoThumbPath);
+
+ // URL (segment bazlı encode → / işaretlerini koru)
+ const urlPath = encodeURIComponent(rel).replace(/%2F/g, "/");
+ const url = `/media/${urlPath}`;
+
+ // Resimler için küçük önizleme: kendi dosyasını thumbnail yap
+ const isImage = String(type).startsWith("image/");
+ const isVideo = String(type).startsWith("video/");
+ const thumb = isImage
+ ? url
+ : hasVideoThumb
? `/downloads/${rootHash}/thumbnail.jpg`
: null;
result.push({
name: rel,
size,
+ type, // 🆕 "image/jpeg", "video/mp4", vs.
+ url, // 🆕 doğrudan görüntüleme/oynatma için
thumbnail: thumb
});
}
@@ -283,7 +299,7 @@ app.get("/api/files", requireAuth, (req, res) => {
}
});
-// --- Stream endpoint ---
+// --- Stream endpoint (torrent içinden) ---
app.get("/stream/:hash", requireAuth, (req, res) => {
const entry = torrents.get(req.params.hash);
if (!entry) return res.status(404).end();
@@ -331,8 +347,6 @@ const server = app.listen(PORT, () =>
const publicDir = path.join(__dirname, "public");
if (fs.existsSync(publicDir)) {
app.use(express.static(publicDir));
-
- // Frontend route'larını index.html'e yönlendir
app.get("*", (req, res, next) => {
if (req.path.startsWith("/api")) return next();
res.sendFile(path.join(publicDir, "index.html"));