Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ point is tag `upstream-baseline`.
## Unreleased

### Added
- `Config/target_path/in_series_no_part` (default `{author}/{series}/{series} - {title}`): the template for a
book in a series whose part is unknown. `in_series` rendered `{part}` empty and produced folders such as
`Jack Reacher # - Three More Jack Reacher Novellas` (upstream #27); two unnumbered entries of one series could
collide. Set it to your `in_series` value to keep the old names. Books already filed are not moved.
- `--pin RELEASE=ASIN` (repeatable; `Config/pins`): use that Audible product for the release and re-process it
now, ignoring its cached answers and processed marker. Shorthand for a hint `{"asin": ..., "refresh": true}`;
a malformed pin exits 2, an unused one is reported at the end of the run.
Expand Down
2 changes: 2 additions & 0 deletions CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ A copy of default_config.cfg can be found under the /templates folder. It is re
"target_path": {
"multi_author": "{first_author}",
"in_series": "{author}/{series}/{series} #{part} - {title}",
"in_series_no_part": "{author}/{series}/{series} - {title}",
"no_series": "{author}/{title}",
"disc_folder": "{title} {disc}"
},
Expand Down Expand Up @@ -92,6 +93,7 @@ A copy of default_config.cfg can be found under the /templates folder. It is re
| target_path | | | |
| | multi_author | How to handle the Author folder for multi-author books: first_author, authors, "", "Static folder name" | first_author |
| | in_series | Format of the generated tree for books in a series | {author}/{series}/{series} #{part} - {title} |
| | in_series_no_part | Format for books in a series whose part number is unknown (a novella collection, a companion book): `in_series` would render `{part}` empty and leave `Series # - Title`. Set it to your `in_series` value to keep that | {author}/{series}/{series} - {title} |
| | no_series | Format of the generated tree for books that are NOT in a series | {author}/{title} |
| | disc_folder | Format of the folder name for multi-disc books (files whose parent folder is `cd N`, `disc N`, `disk N` or `part N`) | {title} {disc} |
| tokens | | | |
Expand Down
10 changes: 9 additions & 1 deletion myx_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class Contributor:
name:str
#books:list[int]= field(default_factory=list)

#target_path template for a book in a series whose part is unknown (Config/target_path/in_series_no_part)
IN_SERIES_NO_PART = "{author}/{series}/{series} - {title}"


#Series Class
@dataclass
class Series:
Expand Down Expand Up @@ -307,6 +311,7 @@ def getConfigTargetPath(self, cfg, book):
multi_author = cfg.get("Config/target_path/multi_author")
in_series = cfg.get("Config/target_path/in_series")
no_series = cfg.get("Config/target_path/no_series")
in_series_no_part = cfg.get("Config/target_path/in_series_no_part") or IN_SERIES_NO_PART
disc_folder = cfg.get("Config/target_path/disc_folder")

if (book is not None):
Expand Down Expand Up @@ -367,7 +372,10 @@ def getConfigTargetPath(self, cfg, book):

sPath = ""
if len(book.series):
x = in_series.format (**tokens)
#a series entry without a part (a novella collection, a companion) would render "{part}" empty and
#leave "Series # - Title" behind (upstream #27): such books use in_series_no_part instead
template = in_series if len(tokens["part"]) else in_series_no_part
x = template.format (**tokens)
#use in_series format
for p in x.split ("/"):
sPath=os.path.join (sPath, p.strip())
Expand Down
1 change: 1 addition & 0 deletions templates/default_config.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"target_path": {
"multi_author": "{first_author}",
"in_series": "{author}/{series}/{series} #{part} - {title}",
"in_series_no_part": "{author}/{series}/{series} - {title}",
"no_series": "{author}/{title}",
"disc_folder": "{title} {disc}",
"calibre_ingest_path": "/path/to/calibre/upload"
Expand Down
27 changes: 27 additions & 0 deletions tests/test_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,30 @@ def test_grouped_discs_get_distinct_target_folders(self):
self.assertEqual(len(targets), 4)
self.assertIn("/lib/Author/Title/Title Disk 1", targets)



class InSeriesNoPartTest(unittest.TestCase):
def target(self, part, **over):
import myx_classes
book = myx_classes.Book(asin="B000000001", title="Three More Novellas")
book.authors = [myx_classes.Contributor("Lee Child")]
book.series = [myx_classes.Series("Jack Reacher", part)]
cfg = FakeConfig("/tmp", **{"Config/target_path/no_series": "{author}/{title}", **over})
bf = myx_classes.BookFile("x.m4b", "/dl/x.m4b", "/dl", "/lib")
return bf.getConfigTargetPath(cfg, book)

def test_series_without_a_part_uses_the_no_part_template(self):
# upstream #27: "Jack Reacher # - Three More Novellas"
self.assertEqual(self.target(""), "/lib/Lee Child/Jack Reacher/Jack Reacher - Three More Novellas")
self.assertEqual(self.target(" "), "/lib/Lee Child/Jack Reacher/Jack Reacher - Three More Novellas")
self.assertEqual(self.target("23.5"), "/lib/Lee Child/Jack Reacher/Jack Reacher #23.5 - Three More Novellas")
self.assertEqual(self.target("3"), "/lib/Lee Child/Jack Reacher/Jack Reacher #3 - Three More Novellas")

def test_template_is_configurable_and_the_old_layout_can_be_kept(self):
self.assertEqual(self.target("", **{"Config/target_path/in_series_no_part": "{author}/{series}/{title}"}),
"/lib/Lee Child/Jack Reacher/Three More Novellas")
old = "{author}/{series}/{series} #{part} - {title}"
self.assertEqual(self.target("", **{"Config/target_path/in_series_no_part": old}),
"/lib/Lee Child/Jack Reacher/Jack Reacher # - Three More Novellas")
self.assertEqual(self.target("", **{"Config/target_path/in_series_no_part": ""}), # blank = default
"/lib/Lee Child/Jack Reacher/Jack Reacher - Three More Novellas")
Loading