From 0591db4b2f81399116ac3f625039f066f937bf42 Mon Sep 17 00:00:00 2001 From: kodi Date: Fri, 13 Mar 2026 16:41:04 +0100 Subject: [PATCH] feat: upload - deel 03 --- .env | 3 +-- app/services/file_discovery_service.py | 11 +++++++++-- app/services/session_service.py | 16 +++++++++++++--- app/static/app.js | 15 +++++++++++++-- data/session_state.sqlite3 | Bin 303104 -> 380928 bytes 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/.env b/.env index 0e59fb9..18731d7 100644 --- a/.env +++ b/.env @@ -2,8 +2,7 @@ APP_HOST=0.0.0.0 APP_PORT=8080 APP_DATA_DIR=/app/data -#MEDIA_ROOT=/data/media -ALLOWED_MEDIA_ROOTS=/Volumes/8TB/Shared_Folders/Downloads, /Volumes/8TB/Shared_Folders/TV_Shows, /Volumes/8TB/Shared_Folders/Library/TV_Shows +ALLOWED_MEDIA_ROOTS=/Volumes/8TB/Shared_Folders/Downloads, /Volumes/8TB/Shared_Folders/TV_Shows, /Volumes/8TB_RAID1/Shared_Folders/Library/TV_Shows, /Volumes/8TB_RAID1/Shared_Folders/Library/TV_Shows/#Documentaires TVDB_API_KEY=2c951d0c-0b7e-405b-bdb2-e250491dc69d TVDB_PIN= TVDB_BASE_URL=https://api4.thetvdb.com/v4 diff --git a/app/services/file_discovery_service.py b/app/services/file_discovery_service.py index 7c7f43b..a0ed68e 100644 --- a/app/services/file_discovery_service.py +++ b/app/services/file_discovery_service.py @@ -1,4 +1,5 @@ import os +import re from pathlib import Path @@ -49,8 +50,6 @@ class FileDiscoveryService: iterator = target.iterdir() for entry in iterator: - if len(files) >= limit: - break if not entry.is_file(): continue ext = entry.suffix.lower() @@ -74,6 +73,10 @@ class FileDiscoveryService: } ) + files.sort(key=lambda item: self._natural_sort_key(item.get("relative_path", ""))) + if len(files) > limit: + files = files[:limit] + return { "root_id": root["id"], "root_path": str(root["path"]), @@ -83,6 +86,10 @@ class FileDiscoveryService: "items": files, } + def _natural_sort_key(self, value: str): + text = str(value or "") + return [int(part) if part.isdigit() else part.lower() for part in re.split(r"(\d+)", text)] + def list_folders( self, root_id: str, diff --git a/app/services/session_service.py b/app/services/session_service.py index 733eb23..92ee067 100644 --- a/app/services/session_service.py +++ b/app/services/session_service.py @@ -736,6 +736,8 @@ class SessionService: proposed_filename=proposed_filename, allowed_roots=allowed_roots, ) + if source_path_str and source_path == destination_path: + errors = [err for err in errors if err != "source and destination paths are equal"] status = "ready" if errors: @@ -777,17 +779,25 @@ class SessionService: for item in preflight_items: source_path = Path(item["source_path"]) destination_path = Path(item["destination_path"]) - os.replace(str(source_path), str(destination_path)) + rename_needed = source_path != destination_path + + if rename_needed: + os.replace(str(source_path), str(destination_path)) + target_path = destination_path + item_status = "renamed" + else: + target_path = source_path + item_status = "unchanged" file_date_status, file_date_detail = self._apply_file_date_after_rename( enabled=set_file_date_to_first_aired, aired_value=aired_by_index.get(int(item["index"])), - destination_path=destination_path, + destination_path=target_path, ) results.append( { **item, - "status": "renamed", + "status": item_status, "errors": [], "file_date_status": file_date_status, "file_date_detail": file_date_detail, diff --git a/app/static/app.js b/app/static/app.js index 1e73875..d09173b 100644 --- a/app/static/app.js +++ b/app/static/app.js @@ -213,6 +213,17 @@ return idx >= 0 ? normalized.slice(idx + 1) : normalized; } + function compareModalFilesByName(a, b) { + const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }); + const aName = (a && (a.name || basename(a.relative_path || a.path || ""))) || ""; + const bName = (b && (b.name || basename(b.relative_path || b.path || ""))) || ""; + const byName = collator.compare(aName, bName); + if (byName !== 0) return byName; + const aPath = (a && (a.relative_path || a.path || "")) || ""; + const bPath = (b && (b.relative_path || b.path || "")) || ""; + return collator.compare(aPath, bPath); + } + function fallbackText(value) { const text = (value || "").toString().trim(); return text || "-"; @@ -890,7 +901,7 @@ const visible = (state.modalFiles || []).filter((file) => { const text = `${file.relative_path || ""} ${file.name || ""}`.toLowerCase(); return !filter || text.includes(filter); - }); + }).sort(compareModalFilesByName); state.modalVisibleFiles = visible; el.modalFilesList.innerHTML = ""; @@ -966,7 +977,7 @@ const recursive = el.modalRecursiveInput.checked ? "true" : "false"; const data = await api( - `/api/files/discover?root_id=${encodeURIComponent(rootId)}&subpath=${encodeURIComponent(chosenSubpath)}&recursive=${recursive}&limit=200` + `/api/files/discover?root_id=${encodeURIComponent(rootId)}&subpath=${encodeURIComponent(chosenSubpath)}&recursive=${recursive}&limit=1000` ); state.modalSelectionAnchorPath = null; state.modalFiles = data.items || []; diff --git a/data/session_state.sqlite3 b/data/session_state.sqlite3 index f083fe3202ca26ff8f03634bfcfe08580e602f76..fb70ba07114d843d4cdbbeace5157caf5ddb35f7 100644 GIT binary patch delta 58344 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 AQ2+n{ delta 1473 zcmZ`&ZEO@(6rDHoc4zm^&fI>{Vkwl`#z?T+nf>VQ4q8e`u@W0dEFY2LR@$Yh7Ft-U zU{tyii6&@Uo;p9ERTF}NNJ`z-!Nf@Ufd(SgL|Q16P!%Dj(5^9%v{mp;3%CJ&FY_`v zbI!fz-utHIldP7$w6;0U436WLvqx<&ScEdVs}Dd5hEWD^9+-k#@CS^>Q~NH!qgqU0 zU}iHrNGUs15QKcRHM04|O_A`{=FL$p0}F*o zK)8NeWK(oC{LTRjb#JBf!U8@TX^7Npi`0fU);C0=+!gjQt9?njNZ89tJV8Zu1#!Ga zhDjZHmT0tAEyp&O&Bc;eV4Q;kh`vyk8Bh0*5xb?Zt)pToQyd{vNp8dsk927E;M#ZBG>}ZgC ziu;b{rA)=O?g5@hOBru55@K`K)6aY6(!>rD-_qMkM49E$gj_=$!8MH1%bU5s;-6hl zSiUuKhJ#)hmIvf6xl!tXeGr2>r~ofKqTEq_lXgk<(hA8>)gMckwuaCf=SlqBg+g1V z2$xs@PQQB(uERC>1uiktw?Ie63xw5jD-{z*7;w=QZ~c2KZ}|`)hl69V267m#fRO_z zf36y;)O@-c(tTccFc9*EJPTD-SJg0&7p*c{0L`XL4ir4*Q9Z?ls<-g}=*tjZ0TssO z#VF6X49J=GFwvX@L|XJTu7q=Kl#Y-QlJ6`6PC<@RF(6~xbM{L7x^R#`jjkh$^w2Wa z9>8af@Iidh`Fw1HoeQ8{PP%3LEOP@z`;k$!9}SwfKR}vDcO3nS9`5NhZ5`+iKTm>F zR;==*W}SzhtcF29oMOE=4953e$iEOexl}0OpahLChIrb+{&G(jLtx5#E$smz4QR8I&ux zAs_a_UKXQTo>w4Lpycj`bS-BZl2fjgjiy~WxLDLr4PaNhH# zO{eoRdtP_IqZWHn(471MjX~<&QU9C6oVbh<47dCx2^fDIjW^EV$-d3WFyH^ag(nvo z)Xdm7ya(Altg{{y_1O~eQG&-GVj<7gje5BW*1-1!(-ef|u3YIP=fWotIp>Nw|8#;Q zU0egRl(lk$y~Xx1KEbG;V)Nx&e3spK^Co`-vzTAgyp&^R>@R+&@a22~HxWT<i(~`{R83{k2(MV