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
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ border-radius: 128px;
- Renamed comics no longer contain colons or other characters Windows and
SMB shares can't store.
- A Metron API key is no longer overridden by a `~/.netrc` login.
- Dynamic covers, custom covers and finishing a book on its last page are
always on; those settings and "Read RTL Comics LTR" are gone.

## v2.5.4

Expand Down
2 changes: 0 additions & 2 deletions codex/choices/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,8 +776,6 @@ def clean_table_columns(value) -> tuple[dict[str, list[str]], list[str]]:

BROWSER_DEFAULTS = MappingProxyType(
{
"custom_covers": True,
"dynamic_covers": True,
"filters": _DEFAULT_FILTERS,
"order_by": "sort_name",
"order_reverse": False,
Expand Down
2 changes: 0 additions & 2 deletions codex/choices/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@
)
READER_DEFAULTS = MappingProxyType(
{
"finish_on_last_page": True,
"fit_to": "W",
"reading_direction": "ltr",
"read_rtl_in_reverse": False,
"two_pages": False,
"page_transition": True,
"cache_book": False,
Expand Down
4 changes: 0 additions & 4 deletions codex/librarian/telemeter/per_user_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,13 @@
"order_by",
"view_mode",
"table_cover_size",
"dynamic_covers",
"custom_covers",
)
# Ordered, unlike SettingsReader.DIRECT_KEYS which is a frozenset. A test pins
# the two to the same set so this cannot drift from the model.
READER_FIELDS: Final[tuple[str, ...]] = (
"fit_to",
"two_pages",
"reading_direction",
"read_rtl_in_reverse",
"finish_on_last_page",
"page_transition",
"cache_book",
)
Expand Down
4 changes: 1 addition & 3 deletions codex/librarian/telemeter/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,11 @@
(
"top_collection",
"order_by",
"dynamic_covers",
"view_mode",
"table_cover_size",
"custom_covers",
),
),
(SettingsReader, ("finish_on_last_page", "fit_to", "reading_direction")),
(SettingsReader, ("fit_to", "reading_direction")),
)
# Sections built by a single collector function, in payload order.
_SIMPLE_SECTIONS: Final = (
Expand Down
39 changes: 39 additions & 0 deletions codex/migrations/0057_remove_pinned_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Generated by Django 6.1.2 on 2026-10-19 04:16."""

from django.db import migrations


class Migration(migrations.Migration):
"""
Remove four settings that stopped being user options.

v2.5.x pinned dynamic covers and custom covers on, finishing a book on its
last page on, and reading RTL comics in reverse off, and dropped their
checkboxes, but kept the columns so the toggles could come back with a
revert. They are gone for good now. All four shipped in 0038, so they
come out here rather than in a fold. Reversible: the browser columns have
defaults and the reader columns are nullable.
"""

dependencies = [
("codex", "0056_match_mode_ask"),
]

operations = [
migrations.RemoveField(
model_name="settingsbrowser",
name="custom_covers",
),
migrations.RemoveField(
model_name="settingsbrowser",
name="dynamic_covers",
),
migrations.RemoveField(
model_name="settingsreader",
name="finish_on_last_page",
),
migrations.RemoveField(
model_name="settingsreader",
name="read_rtl_in_reverse",
),
]
27 changes: 0 additions & 27 deletions codex/models/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,18 +362,6 @@ class SettingsBrowser(SettingsBase):
search = CharField(max_length=4095, default="", blank=True)

# Display preferences
#
# PENDING SCHEMA REMOVAL
#
# The next two are no longer user options: their checkboxes are gone
# from the browser settings drawer and the behavior is pinned on in
# ``views/browser/annotate/cover.py``. The columns stay so nobody's
# stored preference is destroyed while we find out whether anyone misses
# the toggles -- restoring them is a revert, not a data migration. Drop
# these two, their serializer fields and their ``BROWSER_DEFAULTS``
# entries together.
custom_covers = BooleanField(default=True)
dynamic_covers = BooleanField(default=True)
twenty_four_hour_time = BooleanField(default=False)
always_show_filename = BooleanField(default=False)

Expand Down Expand Up @@ -408,8 +396,6 @@ class SettingsBrowser(SettingsBase):
"order_extra_keys",
"collection_order_memory",
"search",
"custom_covers",
"dynamic_covers",
"twenty_four_hour_time",
"always_show_filename",
"view_mode",
Expand Down Expand Up @@ -488,17 +474,6 @@ class SettingsReader(SettingsBase):
default="",
max_length=max_choices_len(ReadingDirectionChoices),
)
# PENDING SCHEMA REMOVAL
#
# The next two are no longer user options: their checkboxes are gone from
# the reader settings drawer and the behavior is pinned in
# ``stores/reader.js`` -- finish-on-last-page on, read-RTL-in-reverse off
# (its default, so a right-to-left book is read right to left). The
# columns stay so nobody's stored preference is destroyed; restoring the
# toggles is a revert, not a data migration. Drop these two, their
# serializer fields and their ``READER_DEFAULTS`` entries together.
read_rtl_in_reverse = BooleanField(default=None, null=True)
finish_on_last_page = BooleanField(default=None, null=True)
page_transition = BooleanField(default=None, null=True)
cache_book = BooleanField(default=None, null=True)

Expand All @@ -508,8 +483,6 @@ class SettingsReader(SettingsBase):
"fit_to",
"two_pages",
"reading_direction",
"read_rtl_in_reverse",
"finish_on_last_page",
"page_transition",
"cache_book",
}
Expand Down
11 changes: 0 additions & 11 deletions codex/serializers/admin/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,10 @@ class StatsSessionsSerializer(Serializer):

top_collection = CountDictField(required=False, read_only=True)
order_by = CountDictField(required=False, read_only=True)
dynamic_covers = CountDictField(required=False, read_only=True)
finish_on_last_page = CountDictField(required=False, read_only=True)
fit_to = CountDictField(required=False, read_only=True)
reading_direction = CountDictField(required=False, read_only=True)
view_mode = CountDictField(required=False, read_only=True)
table_cover_size = CountDictField(required=False, read_only=True)
custom_covers = CountDictField(required=False, read_only=True)
multi_sort_count = IntegerField(required=False, read_only=True)


Expand Down Expand Up @@ -268,26 +265,18 @@ class StatsPerUserSerializer(Serializer):
browser_order_by_users = CountDictField(required=False)
browser_view_mode_users = CountDictField(required=False)
browser_table_cover_size_users = CountDictField(required=False)
browser_dynamic_covers_users = CountDictField(required=False)
browser_custom_covers_users = CountDictField(required=False)
browser_chosen_top_collection_users = CountDictField(required=False)
browser_chosen_order_by_users = CountDictField(required=False)
browser_chosen_view_mode_users = CountDictField(required=False)
browser_chosen_table_cover_size_users = CountDictField(required=False)
browser_chosen_dynamic_covers_users = CountDictField(required=False)
browser_chosen_custom_covers_users = CountDictField(required=False)
reader_global_fit_to_users = CountDictField(required=False)
reader_global_two_pages_users = CountDictField(required=False)
reader_global_reading_direction_users = CountDictField(required=False)
reader_global_read_rtl_in_reverse_users = CountDictField(required=False)
reader_global_finish_on_last_page_users = CountDictField(required=False)
reader_global_page_transition_users = CountDictField(required=False)
reader_global_cache_book_users = CountDictField(required=False)
reader_chosen_fit_to_users = CountDictField(required=False)
reader_chosen_two_pages_users = CountDictField(required=False)
reader_chosen_reading_direction_users = CountDictField(required=False)
reader_chosen_read_rtl_in_reverse_users = CountDictField(required=False)
reader_chosen_finish_on_last_page_users = CountDictField(required=False)
reader_chosen_page_transition_users = CountDictField(required=False)
reader_chosen_cache_book_users = CountDictField(required=False)

Expand Down
2 changes: 0 additions & 2 deletions codex/serializers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,6 @@ class SessionReaderDefaultsSerializer(Serializer):
two_pages = BooleanField(read_only=True)
page_transition = BooleanField(read_only=True)
cache_book = BooleanField(read_only=True)
finish_on_last_page = BooleanField(read_only=True)
read_rtl_in_reverse = BooleanField(read_only=True)


class SessionDefaultsSerializer(Serializer):
Expand Down
22 changes: 2 additions & 20 deletions codex/serializers/browser/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
BrowserRouteCollectionField,
)
from codex.serializers.mixins import JSONFieldSerializer
from codex.serializers.route import SimpleRouteSerializer
from codex.serializers.settings import SettingsInputSerializer

# Sorts worth remembering per top collection. ``search_score`` only means
Expand Down Expand Up @@ -113,33 +112,16 @@ class BrowserFilterChoicesInputSerializer(JSONFieldSerializer):
search = CharField(allow_blank=True, required=False)


class BrowserCoverInputSerializerBase(BrowserFilterChoicesInputSerializer):
"""Base Serializer for Cover and Settings."""
class BrowserSettingsSerializerBase(BrowserFilterChoicesInputSerializer):
"""Base Serializer for Browser & OPDS Settings."""

JSON_FIELDS = frozenset(BrowserFilterChoicesInputSerializer.JSON_FIELDS | {"show"})

custom_covers = BooleanField(required=False)
dynamic_covers = BooleanField(required=False)
order_by = ChoiceField(
choices=tuple(BROWSER_ORDER_BY_CHOICES.keys()), required=False
)
order_reverse = BooleanField(required=False)
show = BrowserSettingsShowCollectionFlagsSerializer(required=False)


class BrowserCoverInputSerializer(BrowserCoverInputSerializerBase):
"""Browser Settings for the cover response."""

JSON_FIELDS = frozenset(
BrowserCoverInputSerializerBase.JSON_FIELDS | {"parent_route"}
)

parent_route = SimpleRouteSerializer(required=False)


class BrowserSettingsSerializerBase(BrowserCoverInputSerializerBase):
"""Base Serializer for Browser & OPDS Settings."""

top_collection = BrowseCollectionField(required=False)

@override
Expand Down
2 changes: 0 additions & 2 deletions codex/serializers/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ class ReaderSettingsSerializer(Serializer):
reading_direction = ReadingDirectionField(
allow_blank=True, allow_null=True, required=False
)
read_rtl_in_reverse = BooleanField(allow_null=True, required=False)
finish_on_last_page = BooleanField(allow_null=True, required=False)
mtime = TimestampField(read_only=True)
page_transition = BooleanField(allow_null=True, required=False)
cache_book = BooleanField(allow_null=True, required=False)
Expand Down
19 changes: 2 additions & 17 deletions codex/settings/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,13 +241,6 @@ def _in_choices_or_factory(row: SettingsDefaults, key: str) -> Any:
"bookmark",
"table_columns",
)
_READER_DEFAULTS_KEYS: tuple[str, ...] = (
"fit_to",
"reading_direction",
"two_pages",
"page_transition",
"cache_book",
)


def get_browser_defaults() -> dict[str, Any]:
Expand All @@ -270,17 +263,9 @@ def get_browser_defaults() -> dict[str, Any]:


def get_reader_defaults() -> dict[str, Any]:
"""
Resolve the site default global reader settings.

Keeps the two pinned ``READER_DEFAULTS`` keys seeded alongside the
admin-configurable ones.
"""
"""Resolve the site default global reader settings."""
row = _get_settings_defaults_or_factory()
return {
**READER_DEFAULTS,
**{key: _in_choices_or_factory(row, key) for key in _READER_DEFAULTS_KEYS},
}
return {key: _in_choices_or_factory(row, key) for key in READER_DEFAULTS}


def get_throttle_settings() -> ThrottleSettings | None:
Expand Down
2 changes: 0 additions & 2 deletions codex/user_data/restore.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,8 +775,6 @@ def _rename_table_columns(raw) -> dict:
)
_BROWSER_BOOL_COLUMNS: Final[tuple[str, ...]] = (
"order_reverse",
"custom_covers",
"dynamic_covers",
"twenty_four_hour_time",
"always_show_filename",
)
Expand Down
2 changes: 0 additions & 2 deletions codex/user_data/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ CREATE TABLE IF NOT EXISTS settings_browser (
order_extra_keys TEXT NOT NULL DEFAULT '[]',
collection_order_memory TEXT NOT NULL DEFAULT '{}',
search TEXT NOT NULL DEFAULT '',
custom_covers INTEGER NOT NULL DEFAULT 1,
dynamic_covers INTEGER NOT NULL DEFAULT 1,
twenty_four_hour_time INTEGER NOT NULL DEFAULT 0,
always_show_filename INTEGER NOT NULL DEFAULT 0,
view_mode TEXT NOT NULL DEFAULT '',
Expand Down
2 changes: 0 additions & 2 deletions codex/user_data/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,6 @@ def serialize_settings_browser(
browser.collection_order_memory, separators=(",", ":")
),
"search": browser.search,
"custom_covers": int(browser.custom_covers),
"dynamic_covers": int(browser.dynamic_covers),
"twenty_four_hour_time": int(browser.twenty_four_hour_time),
"always_show_filename": int(browser.always_show_filename),
"view_mode": browser.view_mode,
Expand Down
Loading