Coverage for /opt/hostedtoolcache/Python/3.10.17/x64/lib/python3.10/site-packages/vfx_seqtools/actions/seqmv.py: 87%
54 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-05-30 00:30 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-05-30 00:30 +0000
1import logging
2import shutil
3from typing import Annotated, Optional
5import fileseq
6import typer
7from rich.progress import (
8 BarColumn,
9 MofNCompleteColumn,
10 Progress,
11 TaskProgressColumn,
12 TextColumn,
13 TimeRemainingColumn,
14)
16from vfx_seqtools import common_options
17from vfx_seqtools.decorators import attach_hook
18from vfx_seqtools.parser import replace_hash_and_at_with_framenumber
21def do_action(tupl: tuple) -> None:
22 src, dst, frame, is_dryrun, be_verbose, be_interactive, be_strict, logger = tupl
23 substituted_src = replace_hash_and_at_with_framenumber(src, frame)
24 substituted_dst = replace_hash_and_at_with_framenumber(dst, frame)
25 if is_dryrun:
26 logger.info(f"dry-run: MOVE {substituted_src} {substituted_dst}")
27 else:
28 if be_verbose:
29 logger.info(f"MOVE: {substituted_src} {substituted_dst}")
30 try:
31 if be_interactive:
32 confirm = input(f"move {substituted_src} -> {substituted_dst}? (y/n): ")
33 if confirm.lower() != "y":
34 return
35 shutil.move(substituted_src, substituted_dst)
36 except Exception as e:
37 if be_strict:
38 raise
39 logger.warning(
40 f"Error moving file: {substituted_src} -> {substituted_dst}, skipping it. Use --strict to stop on errors. {e}"
41 )
44@attach_hook(common_options.dry_run_option, hook_output_kwarg="is_dryrun")
45@attach_hook(common_options.frame_range_options, hook_output_kwarg="frame_range")
46@attach_hook(common_options.frame_seq_options, hook_output_kwarg="frame_seq")
47@attach_hook(common_options.logging_options, hook_output_kwarg="logger")
48@attach_hook(common_options.interactive_option, hook_output_kwarg="be_interactive")
49@attach_hook(common_options.verbose_option, hook_output_kwarg="be_verbose")
50@attach_hook(common_options.strict_option, hook_output_kwarg="be_strict")
51@attach_hook(common_options.version_option, hook_output_kwarg="show_version")
52def seqmv(
53 src: Annotated[str, typer.Argument(help="The source name pattern for the frames.")],
54 dst: Annotated[
55 str, typer.Argument(help="The destination name pattern for the frames.")
56 ],
57 logger: logging.Logger,
58 be_verbose: Optional[bool] = False,
59 be_strict: Optional[bool] = False,
60 be_interactive: Optional[bool] = False,
61 frame_range: tuple[int, int, int] = (0, 0, 0),
62 frame_seq: str = "",
63 is_dryrun: Optional[bool] = False,
64 show_version: Optional[bool] = False,
65) -> None:
66 """
67 Move(rename) files from <SRC> to <DST>, using the provided framerange.
68 """
69 if frame_seq:
70 frames = fileseq.FrameSet(frame_seq)
71 elif frame_range[0] != 0 or frame_range[1] != 0:
72 frames = fileseq.FrameSet(f"{frame_range[0]}-{frame_range[1]}x{frame_range[2]}")
73 else:
74 frames = []
76 if is_dryrun:
77 for frame in frames:
78 do_action(
79 (
80 src,
81 dst,
82 frame,
83 is_dryrun,
84 be_verbose,
85 be_interactive,
86 be_strict,
87 logger,
88 )
89 )
90 else:
91 # skip progress bar if interactive
92 if be_interactive:
93 for frame in frames:
94 do_action(
95 (
96 src,
97 dst,
98 frame,
99 is_dryrun,
100 be_verbose,
101 be_interactive,
102 be_strict,
103 logger,
104 )
105 )
106 else:
107 from rich.logging import RichHandler
109 logging.basicConfig(
110 level=logging.INFO,
111 format="%(message)s",
112 datefmt="[%X]",
113 handlers=[RichHandler(rich_tracebacks=True)],
114 )
115 logger = logging.getLogger("rich")
116 progress = Progress(
117 TextColumn("[progress.description]{task.description}"),
118 MofNCompleteColumn(),
119 BarColumn(),
120 TaskProgressColumn(),
121 TimeRemainingColumn(),
122 )
123 with progress:
124 for frame in progress.track(frames, description="Working..."):
125 do_action(
126 (
127 src,
128 dst,
129 frame,
130 is_dryrun,
131 be_verbose,
132 be_interactive,
133 be_strict,
134 logger,
135 )
136 )