diff --git a/README.md b/README.md index 25e0a64..d101336 100644 --- a/README.md +++ b/README.md @@ -459,8 +459,9 @@ gives `The Saga - 02 - The Book - Ann Writer.epub`. In Calibre, `{title}` and `{series}` give the title sort in a file name, for example `Book, The`. In Bindery, they keep the article. This is the same as Calibre with the tweak -`save_template_title_series_sorting` set to `strictly_alphabetic`. Use `{title_sort}` for the title -with the article at the end. +`save_template_title_series_sorting` set to `strictly_alphabetic`. Use `{title_sort}` and +`{series_sort}` for the title and the series with the article at the end. `{series_sort}` is a +Bindery field. Calibre does not have it, and gives empty text for it. | Field | Value | | --- | --- | @@ -469,6 +470,7 @@ with the article at the end. | `{authors}`, `{author}` | The authors, with ` & ` between them. | | `{author_sort}` | The authors with the last name first: `Writer, Ann`. | | `{series}` | The series. | +| `{series_sort}` | The series with a leading article at the end: `Saga, The`. | | `{series_index}` | The series number: `2`, or `2.5`. | | `{tags}` | The tags in sorted order, with `, ` between them. | | `{languages}` | The language, as Bindery stores it. | diff --git a/bindery/filename_template.py b/bindery/filename_template.py index b5aa469..149b5d2 100644 --- a/bindery/filename_template.py +++ b/bindery/filename_template.py @@ -5,6 +5,8 @@ the order of titles and series as an argument. Dates take a strftime format, wit default. Bindery takes {field}, {field[0]}, {field:format|prefix|suffix} and {{ }}. It refuses template functions and the program modes. It never runs template text as code. +{series_sort} is a Bindery field. It is to {series} what {title_sort} is to {title}. Calibre has no such field. + The tags sort close to Calibre's ICU rules, but their order can differ for characters outside ASCII. """ @@ -34,6 +36,8 @@ FIELDS = { "author": "The authors", "author_sort": "The author sort string. To use only the first letter of the name, use {author_sort[0]}", "series": "The series", + "series_sort": 'The series with a leading "The", "A" or "An" at the end, such as "Saga, The". ' + "Calibre does not have this field", "series_index": "The series number. To get leading zeros, use {series_index:0>3s}", "tags": "The tags", "languages": "The language", @@ -207,7 +211,8 @@ def prepare( values["authors"] = values["author"] = " & ".join(a.replace("&", "&&") for a in authors) values["author_sort"] = " & ".join(author_sort(a) for a in authors) if series: - values["series"] = title_sort(series) if library else series.strip() + values["series_sort"] = title_sort(series) + values["series"] = values["series_sort"] if library else series.strip() if meta.get("series_index") is not None: values["series_index"] = series_index(meta["series_index"]) values["tags"] = ", ".join(sorted(meta.get("tags") or [], key=_tag_key)).removeprefix("/") diff --git a/bindery/static/js/settings.js b/bindery/static/js/settings.js index 2cf66cf..0e55d1b 100644 --- a/bindery/static/js/settings.js +++ b/bindery/static/js/settings.js @@ -208,7 +208,8 @@ export function settingsPage(main, _params, config) { h( 'li', {}, - '{title} and {series} keep a leading "The", "A" or "An". {title_sort} moves it to the end, so ' + + '{title} and {series} keep a leading "The", "A" or "An". {title_sort} and {series_sort} move it to the ' + + 'end, so ' + '"The Book" becomes "Book, The". Calibre puts the title sort in {title} as well, unless you set its ' + 'tweak save_template_title_series_sorting to strictly_alphabetic.', ), diff --git a/tests/test_api_flows.py b/tests/test_api_flows.py index 12deded..bae3fbd 100644 --- a/tests/test_api_flows.py +++ b/tests/test_api_flows.py @@ -164,6 +164,8 @@ def test_download_template_names_the_file(client, story): # The default moves the article to the end, as Calibre's default does. client.put("/api/settings", json={"download_template": ""}, headers=H) assert 'filename="Book, The - Ann Writer.epub"' in download_name(client, sid) + client.put("/api/settings", json={"download_template": "{series_sort} - {title} - {id}"}, headers=H) + assert f'filename="Saga, The - The Book - {sid}.epub"' in download_name(client, sid) # The ID is that of the CWA book, when the story has one. o.store.update_story(sid, calibre_id=77, pinned={"title": "Caf\u00e9", "series": ["", None]}) client.put("/api/settings", json={"download_template": "{series:|| - }{title} - {id}"}, headers=H) diff --git a/tests/test_filename_template.py b/tests/test_filename_template.py index 40f99bf..629b69c 100644 --- a/tests/test_filename_template.py +++ b/tests/test_filename_template.py @@ -55,6 +55,11 @@ def test_reference_cases_for_a_missing_series_and_for_alphabetic_order(): assert expand("{series}", "strictly_alphabetic") == "The Saga" +def test_series_sort_is_a_bindery_field(): + assert expand("{series_sort}") == expand("{series_sort}", "strictly_alphabetic") == "Saga, The" + assert ft.expand("x{series_sort}x", ft.prepare(NO_SERIES, 42)) == "xx" + + @pytest.mark.parametrize( "template,expected", [