Compare commits
2
Commits
v0.2.0
...
d6233c2995
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6233c2995 | ||
|
|
3f50577de6 |
@@ -229,6 +229,13 @@ producing files that already exist byte for byte. The run moves them instead,
|
|||||||
and says so. Prune then finds nothing to remove because nothing was left
|
and says so. Prune then finds nothing to remove because nothing was left
|
||||||
behind.
|
behind.
|
||||||
|
|
||||||
|
Renames are counted apart from encodes in the pass summary, and `--dry-run`
|
||||||
|
reports `would rename` rather than `would encode` — the difference between the
|
||||||
|
two is a minute against an afternoon, so a preview that conflated them would be
|
||||||
|
worse than no preview. A dry run also does not list the pre-rename files as
|
||||||
|
orphans: nothing was moved, so they are still there, but they are what a real
|
||||||
|
run would move rather than what it would delete.
|
||||||
|
|
||||||
### Album art
|
### Album art
|
||||||
|
|
||||||
Rockbox looks for cover art **on the filesystem** — `cover.jpg`, `folder.jpg`
|
Rockbox looks for cover art **on the filesystem** — `cover.jpg`, `folder.jpg`
|
||||||
|
|||||||
+24
-6
@@ -357,7 +357,7 @@ def copy(source, mirror, dry_run):
|
|||||||
return Result("copied", mirror)
|
return Result("copied", mirror)
|
||||||
|
|
||||||
|
|
||||||
def adopt_existing(source, mirror, previous):
|
def adopt_existing(source, mirror, previous, dry_run=False):
|
||||||
"""Move an already-encoded file to its new name. Returns whether it moved.
|
"""Move an already-encoded file to its new name. Returns whether it moved.
|
||||||
|
|
||||||
Turning on FAT32-safe naming changes the path of every track whose name
|
Turning on FAT32-safe naming changes the path of every track whose name
|
||||||
@@ -367,6 +367,9 @@ def adopt_existing(source, mirror, previous):
|
|||||||
"""
|
"""
|
||||||
if previous == mirror or not previous.is_file() or not is_current(source, previous):
|
if previous == mirror or not previous.is_file() or not is_current(source, previous):
|
||||||
return False
|
return False
|
||||||
|
if dry_run:
|
||||||
|
logger.info("would rename %s -> %s", previous.name, mirror.name)
|
||||||
|
return True
|
||||||
mirror.parent.mkdir(parents=True, exist_ok=True)
|
mirror.parent.mkdir(parents=True, exist_ok=True)
|
||||||
os.replace(previous, mirror)
|
os.replace(previous, mirror)
|
||||||
logger.info("renamed %s -> %s", previous.name, mirror.name)
|
logger.info("renamed %s -> %s", previous.name, mirror.name)
|
||||||
@@ -375,8 +378,12 @@ def adopt_existing(source, mirror, previous):
|
|||||||
|
|
||||||
def process(source, mirror, quality_args, dry_run, previous=None):
|
def process(source, mirror, quality_args, dry_run, previous=None):
|
||||||
"""Bring one source file's mirror entry up to date."""
|
"""Bring one source file's mirror entry up to date."""
|
||||||
if previous is not None and not dry_run and not mirror.exists():
|
# Counted separately from an encode, and reported in a dry run, because the
|
||||||
adopt_existing(source, mirror, previous)
|
# difference between moving a file and re-encoding it is the difference
|
||||||
|
# between a minute and an afternoon.
|
||||||
|
if previous is not None and not mirror.exists():
|
||||||
|
if adopt_existing(source, mirror, previous, dry_run):
|
||||||
|
return Result("renamed", mirror)
|
||||||
if is_current(source, mirror):
|
if is_current(source, mirror):
|
||||||
# A mirror written before this bit was set has a correct mtime, so
|
# A mirror written before this bit was set has a correct mtime, so
|
||||||
# nothing else in the pass would ever revisit it. Top it up here
|
# nothing else in the pass would ever revisit it. Top it up here
|
||||||
@@ -483,7 +490,7 @@ def run_once(
|
|||||||
"""
|
"""
|
||||||
started = time.monotonic()
|
started = time.monotonic()
|
||||||
logger.info("pass starting with %d concurrent encoders", jobs)
|
logger.info("pass starting with %d concurrent encoders", jobs)
|
||||||
counts = {"encoded": 0, "copied": 0, "skipped": 0, "failed": 0}
|
counts = {"encoded": 0, "copied": 0, "renamed": 0, "skipped": 0, "failed": 0}
|
||||||
failures = []
|
failures = []
|
||||||
|
|
||||||
work = plan(scan_root, source_root, mirror_root, safe)
|
work = plan(scan_root, source_root, mirror_root, safe)
|
||||||
@@ -506,16 +513,27 @@ def run_once(
|
|||||||
if result.action == "failed":
|
if result.action == "failed":
|
||||||
failures.append(result)
|
failures.append(result)
|
||||||
|
|
||||||
removed = prune(mirror_root, set(work), dry_run) if do_prune else 0
|
expected = set(work)
|
||||||
|
if safe and dry_run:
|
||||||
|
# Nothing was actually renamed, so the pre-sanitisation files are still
|
||||||
|
# on disk. They are not orphans -- they are the files a real run would
|
||||||
|
# move -- and reporting them for deletion would misrepresent the pass
|
||||||
|
# twice over.
|
||||||
|
expected |= {
|
||||||
|
mirror_path_for(source, source_root, mirror_root) for source in work.values()
|
||||||
|
}
|
||||||
|
removed = prune(mirror_root, expected, dry_run) if do_prune else 0
|
||||||
|
|
||||||
for failure in failures:
|
for failure in failures:
|
||||||
logger.error("failed: %s: %s", failure.path, failure.error)
|
logger.error("failed: %s: %s", failure.path, failure.error)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"pass complete in %.1fs: %d encoded, %d copied, %d up to date, %d removed, %d failed",
|
"pass complete in %.1fs: %d encoded, %d copied, %d renamed, %d up to date,"
|
||||||
|
" %d removed, %d failed",
|
||||||
time.monotonic() - started,
|
time.monotonic() - started,
|
||||||
counts["encoded"],
|
counts["encoded"],
|
||||||
counts["copied"],
|
counts["copied"],
|
||||||
|
counts["renamed"],
|
||||||
counts["skipped"],
|
counts["skipped"],
|
||||||
removed,
|
removed,
|
||||||
counts["failed"],
|
counts["failed"],
|
||||||
|
|||||||
@@ -558,3 +558,58 @@ def test_a_cover_left_without_tracks_is_pruned(tmp_path, make_flac, make_cover):
|
|||||||
run(source, mirror)
|
run(source, mirror)
|
||||||
|
|
||||||
assert not (mirror / "Gone").exists()
|
assert not (mirror / "Gone").exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_dry_run_reports_a_rename_not_an_encode(tmp_path, make_flac, caplog):
|
||||||
|
"""The difference between moving a file and re-encoding it is the
|
||||||
|
difference between a minute and an afternoon, so a dry run must not
|
||||||
|
describe the first as the second."""
|
||||||
|
source = tmp_path / "src"
|
||||||
|
mirror = tmp_path / "dst"
|
||||||
|
make_flac(source / "Album" / "Where Are You?.flac")
|
||||||
|
run(source, mirror)
|
||||||
|
|
||||||
|
with caplog.at_level("INFO"):
|
||||||
|
run(source, mirror, "--fat32-safe", "--dry-run")
|
||||||
|
|
||||||
|
assert "would rename" in caplog.text
|
||||||
|
assert "would encode" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_dry_run_does_not_call_the_old_paths_orphans(tmp_path, make_flac, caplog):
|
||||||
|
"""Nothing was renamed, so they are still there -- but they are the files a
|
||||||
|
real run would move, not files it would delete."""
|
||||||
|
source = tmp_path / "src"
|
||||||
|
mirror = tmp_path / "dst"
|
||||||
|
make_flac(source / "Album" / "Where Are You?.flac")
|
||||||
|
run(source, mirror)
|
||||||
|
|
||||||
|
with caplog.at_level("INFO"):
|
||||||
|
run(source, mirror, "--fat32-safe", "--dry-run")
|
||||||
|
|
||||||
|
assert "would remove orphan" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_dry_run_moves_nothing(tmp_path, make_flac):
|
||||||
|
source = tmp_path / "src"
|
||||||
|
mirror = tmp_path / "dst"
|
||||||
|
make_flac(source / "Album" / "Where Are You?.flac")
|
||||||
|
run(source, mirror)
|
||||||
|
|
||||||
|
run(source, mirror, "--fat32-safe", "--dry-run")
|
||||||
|
|
||||||
|
assert (mirror / "Album" / "Where Are You?.mp3").is_file()
|
||||||
|
assert not (mirror / "Album" / "Where Are You_.mp3").exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_renames_are_counted_separately_from_encodes(tmp_path, make_flac, caplog):
|
||||||
|
source = tmp_path / "src"
|
||||||
|
mirror = tmp_path / "dst"
|
||||||
|
make_flac(source / "Album" / "Where Are You?.flac")
|
||||||
|
run(source, mirror)
|
||||||
|
|
||||||
|
with caplog.at_level("INFO"):
|
||||||
|
run(source, mirror, "--fat32-safe")
|
||||||
|
|
||||||
|
assert "1 renamed" in caplog.text
|
||||||
|
assert "0 encoded" in caplog.text
|
||||||
|
|||||||
Reference in New Issue
Block a user