mnamer 2.7.2.dev7__py3-none-any.whl → 2.7.2.dev8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mnamer/__version__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __version__ = "2.7.2.dev7"
4
+ __version__ = "2.7.2.dev8"
mnamer/setting_store.py CHANGED
@@ -366,20 +366,16 @@ class SettingStore:
366
366
  if f.metadata
367
367
  ]
368
368
 
369
- @staticmethod
370
- def _resolve_path(path: str | Path) -> Path:
371
- return Path(path).resolve()
372
-
373
369
  @override
374
370
  def __setattr__(self, key: str, value: Any):
375
371
  converter_map: dict[str, Callable[[Any], Any]] = {
376
372
  "episode_api": ProviderType,
377
- "episode_directory": self._resolve_path,
373
+ "episode_directory": Path,
378
374
  "language": Language.parse,
379
375
  "mask": normalize_containers,
380
376
  "media": MediaType,
381
377
  "movie_api": ProviderType,
382
- "movie_directory": self._resolve_path,
378
+ "movie_directory": Path,
383
379
  "targets": lambda targets: [Path(target) for target in targets],
384
380
  }
385
381
  converter: Callable[[Any], Any] | None = converter_map.get(key)
mnamer/target.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import datetime as dt
4
- from os import path
5
4
  from pathlib import Path
6
5
  from shutil import move
7
6
  from typing import Any, ClassVar, Self, override
@@ -94,21 +93,62 @@ class Target:
94
93
  preferences.
95
94
  """
96
95
  if self.directory:
97
- dir_head_ = format(self.metadata, str(self.directory))
98
- dir_head_ = str_sanitize(dir_head_)
99
- dir_head = Path(dir_head_)
96
+ dir_head = self._format_directory(self.directory)
100
97
  else:
101
98
  dir_head = self.source.parent
99
+
102
100
  file_path = format(self.metadata, self._settings.formatting_for(self.metadata))
103
- dir_tail, filename = path.split(Path(file_path))
104
- filename = filename_replace(filename, self._settings.replace_after)
101
+ dir_tail, filename = self._split_formatted_path(file_path)
102
+ directory = Path(dir_head, self._process_directory(dir_tail))
103
+ filename = self._process_filename(filename)
104
+ return Path(directory, filename).resolve()
105
+
106
+ def _format_directory(self, directory: Path) -> Path:
107
+ """Format and post-process a configured directory template.
108
+
109
+ Each part of the original (un-resolved) directory is formatted
110
+ independently so we can tell template substitutions apart from literal
111
+ user-typed parts. For relative paths every part is transformed; for
112
+ absolute paths only template parts are, keeping literal filesystem
113
+ prefixes like ``/Volumes/Media`` intact.
114
+ """
115
+ is_absolute = directory.is_absolute()
116
+ processed_parts: list[str] = []
117
+ for original_part in directory.parts:
118
+ formatted_part = format(self.metadata, original_part)
119
+ if not is_absolute or "{" in original_part:
120
+ formatted_part = self._process_path_text(formatted_part)
121
+ processed_parts.append(formatted_part)
122
+ return Path(*processed_parts) if processed_parts else Path()
123
+
124
+ @staticmethod
125
+ def _split_formatted_path(file_path: str) -> tuple[Path, str]:
126
+ """Split a formatted file template into optional directories and filename."""
127
+ formatted_path = Path(file_path)
128
+ dir_tail = formatted_path.parent
129
+ if str(dir_tail) == ".":
130
+ dir_tail = Path()
131
+ return dir_tail, formatted_path.name
132
+
133
+ def _process_directory(self, directory: Path) -> Path:
134
+ """Apply filename post-processing rules to each generated directory path."""
135
+ parts = tuple(self._process_path_text(part) for part in directory.parts)
136
+ return Path(*parts) if parts else Path()
137
+
138
+ def _process_filename(self, filename: str) -> str:
139
+ """Apply configured post-processing rules to a generated filename."""
140
+ return self._process_path_text(filename)
141
+
142
+ def _process_path_text(self, value: str) -> str:
143
+ """Apply replacement, scene, lower, and sanitize transforms in one place."""
144
+ if value in (".", ".."):
145
+ return value
146
+ value = filename_replace(value, self._settings.replace_after)
105
147
  if self._settings.scene:
106
- filename = str_scenify(filename)
148
+ value = str_scenify(value)
107
149
  if self._settings.lower:
108
- filename = filename.lower()
109
- filename = str_sanitize(filename)
110
- directory = Path(dir_head, dir_tail)
111
- return Path(directory, filename)
150
+ value = value.lower()
151
+ return str_sanitize(value)
112
152
 
113
153
  def _parse(self, file_path: Path):
114
154
  path_data: dict[str, Any] = {"language": self._settings.language}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mnamer
3
- Version: 2.7.2.dev7
3
+ Version: 2.7.2.dev8
4
4
  Summary: A command-line utility for organizing media files.
5
5
  Author-email: Jessy Williams <jessy@jessywilliams.com>
6
6
  Maintainer-email: Jessy Williams <jessy@jessywilliams.com>
@@ -1,6 +1,6 @@
1
1
  mnamer/__init__.py,sha256=ZjNNbifQHIhjmUekOwc_VZQQdR289XCqqSIdjDSF5pw,344
2
2
  mnamer/__main__.py,sha256=8AEEyLkBtfYQKOvrbg-XlLxEu9_0G-1-Rd657EBlnI8,913
3
- mnamer/__version__.py,sha256=Cad7mu-MWngZCZCohrD0M-PgJinSGiU3OccvuTHE4uU,110
3
+ mnamer/__version__.py,sha256=RBCcQ_PgBZmTWJZcxA5Qyyto_6zqxd8dAOveMBFcIck,110
4
4
  mnamer/argument.py,sha256=PicJzTRGmamHeSKCWwhNMuf5WBSso8AbuPoZ3yOwcI4,3293
5
5
  mnamer/const.py,sha256=__YSxzRc8qDJqp4mlJncjiaRnKoi3wMLtGT1AVAm2mc,2035
6
6
  mnamer/endpoints.py,sha256=QAC1Vb-T_rDYOr-Fu92THffK9Dc48nJ3Jn12nC5yA_0,22778
@@ -11,14 +11,14 @@ mnamer/metadata.py,sha256=Z3BhX-oKpwoStMkH62Zh9WCRcFTl7S3TJTPpsT-IQaw,5839
11
11
  mnamer/providers.py,sha256=XYIvMLAKIbAl19RmB19pkSabnkEpqZk5liz2Q4pYasI,19042
12
12
  mnamer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  mnamer/setting_spec.py,sha256=37V7iT0_6XJdeqD6rItqQmYG-fQYQSglPX01KRls0KQ,1279
14
- mnamer/setting_store.py,sha256=n8E1tCCaRnwW1qTtemfUOq97CzsaSXwcdK42w7nTEII,16109
15
- mnamer/target.py,sha256=P29APLtH3772gblIBO5XPlDGHzoyYNv9DSwTuDskW-A,9176
14
+ mnamer/setting_store.py,sha256=BcnUuNn823RHpOLpWvr7gVlrHZJWfxDaNfDDNqYjcqI,15977
15
+ mnamer/target.py,sha256=RzvtzDabkRjw2Ygzi94FwwKrjAW22RmcYJRkrPpT7c4,11091
16
16
  mnamer/tty.py,sha256=v66fiv_rq_NPayUt9QfsEx5mxPJWAF28RSKiHe6op_Q,4146
17
17
  mnamer/types.py,sha256=aYMfEcBmPhrkA9PNrtr8JrDuBXeUNN8dbmd4T5rvRjg,638
18
18
  mnamer/utils.py,sha256=dB_DOKlXlHwF0KGk9hF5dB9eGbyzXLajOSBc_YQevqY,15639
19
- mnamer-2.7.2.dev7.dist-info/licenses/LICENSE.txt,sha256=WxN2vPlY96aW3C_CRs4q0xOK4CxO5RTVTzf2W25hGmM,1071
20
- mnamer-2.7.2.dev7.dist-info/METADATA,sha256=s0jsi2a3VVuRzVqJtPXFGXqVKMyS9acldcjkorEJxaI,8038
21
- mnamer-2.7.2.dev7.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
22
- mnamer-2.7.2.dev7.dist-info/entry_points.txt,sha256=STyNAl6d-ueccO1C_WdDfjq5GlYwi788H4AENG040fI,48
23
- mnamer-2.7.2.dev7.dist-info/top_level.txt,sha256=RER9MaloDml8ZAsXHlm6oKwvv2KgH_YWJLlOfKC1zAY,7
24
- mnamer-2.7.2.dev7.dist-info/RECORD,,
19
+ mnamer-2.7.2.dev8.dist-info/licenses/LICENSE.txt,sha256=WxN2vPlY96aW3C_CRs4q0xOK4CxO5RTVTzf2W25hGmM,1071
20
+ mnamer-2.7.2.dev8.dist-info/METADATA,sha256=4IUiAElfqQIHcR2UbU1Gh5gb21AbyDGw2M5Zlwo_1aY,8038
21
+ mnamer-2.7.2.dev8.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
22
+ mnamer-2.7.2.dev8.dist-info/entry_points.txt,sha256=STyNAl6d-ueccO1C_WdDfjq5GlYwi788H4AENG040fI,48
23
+ mnamer-2.7.2.dev8.dist-info/top_level.txt,sha256=RER9MaloDml8ZAsXHlm6oKwvv2KgH_YWJLlOfKC1zAY,7
24
+ mnamer-2.7.2.dev8.dist-info/RECORD,,