Treat a project as spent once its song has been exported
Opening an exported song starts a fresh session from detection instead of resuming: cuts, discards and metadata do not carry over, so a re-cut never inherits decisions that have already shipped. --resume overrides it on edit, export and project. This reverses what was agreed in planning and written into docs/spec.md and CONTEXT.md, which promised resume-across-sessions and re-export. Both are corrected. The cost is deliberate and worth stating: changing the width cap or adding the SVG renderer later now means re-cutting each song by hand rather than regenerating every bundle from its project file. Export records the flag in bundle.write, so no caller can forget it. Also removed --refit and the Auto-fit buttons, which were added without being asked for and whose only purpose - migrating projects made before the content rectangle was proposed - disappears once exported projects start fresh. Reset now restores detection's proposal rather than the whole page: clearing to full width would undo the thing the rectangle exists for, so one button covers it. open_project() replaces four copies of load-or-detect across the CLI and the editor.
This commit is contained in:
+23
-38
@@ -51,32 +51,18 @@ def _detect(args: argparse.Namespace) -> int:
|
||||
|
||||
|
||||
def _project(args: argparse.Namespace) -> int:
|
||||
from .project import Project, default_path
|
||||
from .project import default_path, open_project
|
||||
|
||||
source = open_source(args.pdf, SourceType(args.type) if args.type else None)
|
||||
path = default_path(source.path)
|
||||
|
||||
if path.exists() and not args.force:
|
||||
project = Project.load(path)
|
||||
print(f"{path.name}: loaded")
|
||||
project = open_project(source, resume=args.resume and not args.force)
|
||||
if project.path is None:
|
||||
print(f"{path.name}: fresh session from detection")
|
||||
else:
|
||||
print(f"{path.name}: resumed")
|
||||
if project.source_changed():
|
||||
print(" WARNING: the PDF has changed since these cuts were made")
|
||||
if args.refit:
|
||||
# Re-propose the content rectangle without disturbing cuts,
|
||||
# discards or metadata — for projects made before detection
|
||||
# proposed one, or whose rectangle was dragged wrong.
|
||||
for i in range(len(project.pages)):
|
||||
gray = page_raster(source, i)
|
||||
project.pages[i].content_rect = detect_page(gray).content
|
||||
print(f" content rectangle re-fitted on {len(project.pages)} pages")
|
||||
else:
|
||||
detections, heights = [], []
|
||||
for i in range(len(source)):
|
||||
gray = page_raster(source, i)
|
||||
detections.append(detect_page(gray))
|
||||
heights.append(gray.shape[0])
|
||||
project = Project.from_detection(source.path, detections, heights)
|
||||
print(f"{path.name}: created from detection")
|
||||
|
||||
kept = project.kept_slices()
|
||||
for i, page in enumerate(project.pages):
|
||||
@@ -92,23 +78,14 @@ def _project(args: argparse.Namespace) -> int:
|
||||
|
||||
def _export(args: argparse.Namespace) -> int:
|
||||
from . import bundle
|
||||
from .project import Project, default_path
|
||||
from .project import open_project
|
||||
|
||||
source = open_source(args.pdf, SourceType(args.type) if args.type else None)
|
||||
path = default_path(source.path)
|
||||
|
||||
if path.exists():
|
||||
project = Project.load(path)
|
||||
if project.source_changed():
|
||||
print("WARNING: the PDF has changed since these cuts were made")
|
||||
else:
|
||||
detections, heights = [], []
|
||||
for i in range(len(source)):
|
||||
gray = page_raster(source, i)
|
||||
detections.append(detect_page(gray))
|
||||
heights.append(gray.shape[0])
|
||||
project = Project.from_detection(source.path, detections, heights)
|
||||
print("no project file; exporting straight from detection")
|
||||
project = open_project(source, resume=args.resume)
|
||||
if project.path is None:
|
||||
print("no unspent project state; exporting straight from detection")
|
||||
elif project.source_changed():
|
||||
print("WARNING: the PDF has changed since these cuts were made")
|
||||
|
||||
out = Path(args.out) if args.out else source.path.with_suffix(".zip")
|
||||
bundle.write(project, source, out)
|
||||
@@ -122,7 +99,9 @@ def _export(args: argparse.Namespace) -> int:
|
||||
def _edit(args: argparse.Namespace) -> int:
|
||||
from .editor import launch
|
||||
|
||||
return launch(Path(args.pdf), SourceType(args.type) if args.type else None)
|
||||
return launch(
|
||||
Path(args.pdf), SourceType(args.type) if args.type else None, resume=args.resume
|
||||
)
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
@@ -154,9 +133,9 @@ def main(argv: list[str] | None = None) -> int:
|
||||
proj.add_argument("--save", action="store_true", help="write the project file")
|
||||
proj.add_argument("--force", action="store_true", help="re-detect, discarding existing state")
|
||||
proj.add_argument(
|
||||
"--refit",
|
||||
"--resume",
|
||||
action="store_true",
|
||||
help="re-propose the content rectangle, keeping cuts and metadata",
|
||||
help="reopen an already-exported project instead of starting fresh",
|
||||
)
|
||||
proj.add_argument("--type", choices=[t.value for t in SourceType])
|
||||
proj.set_defaults(func=_project)
|
||||
@@ -164,11 +143,17 @@ def main(argv: list[str] | None = None) -> int:
|
||||
exp = sub.add_parser("export", help="render the song and write a bundle")
|
||||
exp.add_argument("pdf")
|
||||
exp.add_argument("--out", help="output zip (default: alongside the PDF)")
|
||||
exp.add_argument("--resume", action="store_true", help="use already-exported project state")
|
||||
exp.add_argument("--type", choices=[t.value for t in SourceType])
|
||||
exp.set_defaults(func=_export)
|
||||
|
||||
ed = sub.add_parser("edit", help="open the editor")
|
||||
ed.add_argument("pdf")
|
||||
ed.add_argument(
|
||||
"--resume",
|
||||
action="store_true",
|
||||
help="reopen an already-exported project instead of starting fresh",
|
||||
)
|
||||
ed.add_argument("--type", choices=[t.value for t in SourceType])
|
||||
ed.set_defaults(func=_edit)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user