Files
felhom.eu/documentation/audits/media-build-2026-10-15/tools/where.py
T

48 lines
2.2 KiB
Python

#!/usr/bin/env python3
"""where.py — S4: where every media file sits, per drive. Run INSIDE the guest (run_in_guest.py where.py).
For each enrolled drive under /mnt/felhom-drives/*/userdata/media: every regular file (hidden temp folders included)
with its size, link count and inode, grouped by top folder (tv, movies, torrents, transmission, ...). Then the pairs
that are ONE file (same device + inode = a hardlink: no extra copy) and the library files that are NOT linked to any
download (link count 1 = a copy, or a download already removed). Stdlib only; reads, never writes.
"""
import collections, glob, os
ROOTS = sorted(glob.glob("/mnt/felhom-drives/*/userdata/media"))
by_ino = collections.defaultdict(list)
total = 0
for root in ROOTS:
drive = root.split("/")[3]
print(f"== drive {drive} ({root})")
for top in sorted(os.listdir(root)):
rows = []
for dp, dn, fn in os.walk(os.path.join(root, top)):
for f in fn:
p = os.path.join(dp, f)
try:
s = os.lstat(p)
except OSError as e:
rows.append(f" ?? {p}: {e}")
continue
if not os.path.isfile(p) or os.path.islink(p):
continue
total += 1
by_ino[(s.st_dev, s.st_ino)].append((drive, top, p, s.st_size, s.st_nlink))
rows.append(f" {s.st_size/1e6:10.1f} MB links={s.st_nlink} inode={s.st_ino:<10} {os.path.relpath(p, root)}")
if rows:
print(f" -- {top}/ ({len(rows)} file(s))")
print("\n".join(rows))
print(f"\n== {total} file(s) on {len(ROOTS)} drive(s)")
print("== ONE FILE, TWO NAMES (hardlink — no extra copy):")
n = 0
for k, v in by_ino.items():
if len(v) > 1:
n += 1
print(f" drive {v[0][0]} inode {k[1]} ({v[0][3]/1e6:.1f} MB): " + " <-> ".join(f"{x[1]}/…/{os.path.basename(x[2])}" for x in v))
print(f" {n} pair(s)")
print("== LIBRARY FILES WITH LINK COUNT 1 (a copy, or its download is gone):")
for k, v in by_ino.items():
for d, top, p, size, nl in v:
if top in ("tv", "movies") and nl == 1:
print(f" drive {d}: {os.path.relpath(p, '/mnt/felhom-drives')} ({size/1e6:.1f} MB)")