fix(client-server): rclone ve miniplayer düzeltmeleri
This commit is contained in:
@@ -723,6 +723,16 @@ function readInfoForRoot(rootFolder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function getInfoPathForRoot(rootFolder) {
|
||||
const safe = sanitizeRelative(rootFolder);
|
||||
if (!safe) return null;
|
||||
const localPath = path.join(DOWNLOAD_DIR, safe, INFO_FILENAME);
|
||||
if (fs.existsSync(localPath)) return localPath;
|
||||
const gdrivePath = path.join(GDRIVE_ROOT, safe, INFO_FILENAME);
|
||||
if (fs.existsSync(gdrivePath)) return gdrivePath;
|
||||
return localPath;
|
||||
}
|
||||
|
||||
function sanitizeRelative(relPath) {
|
||||
return relPath.replace(/^[\\/]+/, "");
|
||||
}
|
||||
@@ -1109,7 +1119,27 @@ async function runRcloneCacheClean() {
|
||||
const body = await resp.text();
|
||||
return { ok: false, error: `Rclone RC hata: ${body || resp.status}` };
|
||||
}
|
||||
return { ok: true, method: "rc", restarted: false };
|
||||
// RC refresh sonrası cache dizinini temizle (mount düşmeden)
|
||||
try {
|
||||
if (fs.existsSync(RCLONE_VFS_CACHE_DIR)) {
|
||||
const entries = fs.readdirSync(RCLONE_VFS_CACHE_DIR);
|
||||
for (const entry of entries) {
|
||||
const target = path.join(RCLONE_VFS_CACHE_DIR, entry);
|
||||
fs.rmSync(target, { recursive: true, force: true });
|
||||
}
|
||||
} else {
|
||||
fs.mkdirSync(RCLONE_VFS_CACHE_DIR, { recursive: true });
|
||||
}
|
||||
} catch (cleanupErr) {
|
||||
return {
|
||||
ok: false,
|
||||
error: cleanupErr?.message || String(cleanupErr)
|
||||
};
|
||||
}
|
||||
const remaining = fs.existsSync(RCLONE_VFS_CACHE_DIR)
|
||||
? fs.readdirSync(RCLONE_VFS_CACHE_DIR).length
|
||||
: 0;
|
||||
return { ok: true, method: "rc+fs", restarted: false, remaining };
|
||||
}
|
||||
|
||||
if (wasRunning && !RCLONE_RC_ENABLED) {
|
||||
@@ -1119,7 +1149,8 @@ async function runRcloneCacheClean() {
|
||||
// RC kapalıysa dosya sisteminden temizle
|
||||
fs.rmSync(RCLONE_VFS_CACHE_DIR, { recursive: true, force: true });
|
||||
fs.mkdirSync(RCLONE_VFS_CACHE_DIR, { recursive: true });
|
||||
return { ok: true, method: "fs", restarted: false };
|
||||
const remaining = fs.readdirSync(RCLONE_VFS_CACHE_DIR).length;
|
||||
return { ok: true, method: "fs", restarted: false, remaining };
|
||||
} catch (err) {
|
||||
return { ok: false, error: err?.message || String(err) };
|
||||
}
|
||||
@@ -1294,6 +1325,10 @@ function startRcloneMount(settings) {
|
||||
rcloneProcess.stderr.on("data", (data) => {
|
||||
const msg = data.toString().trim();
|
||||
if (msg) {
|
||||
if (msg.includes("Dir.Remove not empty")) {
|
||||
console.info(`ℹ️ rclone: ${msg}`);
|
||||
return;
|
||||
}
|
||||
rcloneLastLogMessage = msg;
|
||||
// NOTICE ve INFO seviyesindeki loglar hata değil
|
||||
// Sadece ERROR, FATAL, CRITICAL seviyesindekileri "son hata" olarak işaretle
|
||||
@@ -5934,7 +5969,8 @@ function writeInfoForRoot(rootFolder, info) {
|
||||
if (!rootFolder || !info) return;
|
||||
const safe = sanitizeRelative(rootFolder);
|
||||
if (!safe) return;
|
||||
const target = path.join(DOWNLOAD_DIR, safe, INFO_FILENAME);
|
||||
const target = getInfoPathForRoot(safe);
|
||||
if (!target) return;
|
||||
try {
|
||||
info.updatedAt = Date.now();
|
||||
fs.writeFileSync(target, JSON.stringify(info, null, 2), "utf-8");
|
||||
@@ -8319,7 +8355,8 @@ app.get("/api/files", requireAuth, (req, res) => {
|
||||
const seriesEpisodeInfo = relWithinRoot
|
||||
? info.seriesEpisodes?.[relWithinRoot] || null
|
||||
: null;
|
||||
let mediaCategory = fileMeta?.type || null;
|
||||
let mediaCategory =
|
||||
fileMeta?.categoryOverride || fileMeta?.type || null;
|
||||
if (!mediaCategory) {
|
||||
const canInheritFromInfo = !relWithinRoot || isVideo;
|
||||
if (canInheritFromInfo && info.type) {
|
||||
@@ -8370,6 +8407,45 @@ app.get("/api/files", requireAuth, (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// --- 🏷️ Dosya kategori override ---
|
||||
app.post("/api/file/category", requireAuth, (req, res) => {
|
||||
try {
|
||||
const relPath = sanitizeRelative(String(req.body?.path || ""));
|
||||
const category = String(req.body?.category || "").toLowerCase();
|
||||
if (!relPath) return res.status(400).json({ error: "Geçersiz yol" });
|
||||
if (!category) return res.status(400).json({ error: "Geçersiz kategori" });
|
||||
const rootFolder = rootFromRelPath(relPath);
|
||||
if (!rootFolder) return res.status(400).json({ error: "Kök bulunamadı" });
|
||||
const info = readInfoForRoot(rootFolder);
|
||||
if (!info) return res.status(404).json({ error: "info.json bulunamadı" });
|
||||
const segments = relPathToSegments(relPath);
|
||||
const relWithinRoot = segments.slice(1).join("/");
|
||||
|
||||
if (!info.files) info.files = {};
|
||||
if (!relWithinRoot) {
|
||||
if (category === "auto") delete info.type;
|
||||
else info.type = category;
|
||||
writeInfoForRoot(rootFolder, info);
|
||||
return res.json({ ok: true });
|
||||
}
|
||||
|
||||
if (!info.files[relWithinRoot]) {
|
||||
return res.status(404).json({ error: "Dosya kaydı bulunamadı" });
|
||||
}
|
||||
|
||||
if (category === "auto") {
|
||||
delete info.files[relWithinRoot].categoryOverride;
|
||||
} else {
|
||||
info.files[relWithinRoot].categoryOverride = category;
|
||||
}
|
||||
writeInfoForRoot(rootFolder, info);
|
||||
return res.json({ ok: true });
|
||||
} catch (err) {
|
||||
console.error("❌ Kategori güncelleme hatası:", err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// --- 🗑️ Çöp listesi API (.trash flag sistemi) ---
|
||||
app.get("/api/trash", requireAuth, (req, res) => {
|
||||
try {
|
||||
@@ -10338,18 +10414,25 @@ function collectMusicEntries() {
|
||||
if (!fileKeys.length) continue;
|
||||
|
||||
let targetPath = info.primaryVideoPath;
|
||||
if (targetPath && files[targetPath]?.type !== "music") {
|
||||
const primaryMeta = targetPath ? files[targetPath] : null;
|
||||
const primaryCategory =
|
||||
primaryMeta?.categoryOverride || primaryMeta?.type || null;
|
||||
if (targetPath && primaryCategory !== "music") {
|
||||
targetPath = null;
|
||||
}
|
||||
if (!targetPath) {
|
||||
targetPath =
|
||||
fileKeys.find((key) => files[key]?.type === "music") || fileKeys[0];
|
||||
fileKeys.find(
|
||||
(key) =>
|
||||
(files[key]?.categoryOverride || files[key]?.type) === "music"
|
||||
) || fileKeys[0];
|
||||
}
|
||||
if (!targetPath) continue;
|
||||
const fileMeta = files[targetPath];
|
||||
// Hedef dosya çöpteyse atla
|
||||
if (isPathTrashed(folder, targetPath, false)) continue;
|
||||
const mediaType = fileMeta?.type || info.type || null;
|
||||
const mediaType =
|
||||
fileMeta?.categoryOverride || fileMeta?.type || info.type || null;
|
||||
if (mediaType !== "music") continue;
|
||||
const absMusic = resolveStoragePath(`${folder}/${targetPath}`);
|
||||
if (!absMusic) continue;
|
||||
|
||||
Reference in New Issue
Block a user