No description
  • Python 57.6%
  • JavaScript 16.5%
  • HTML 15.2%
  • CSS 10%
  • Dockerfile 0.5%
  • Other 0.2%
Find a file
2026-10-25 13:04:18 +01:00
app Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00
migrate Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00
tests Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00
.gitignore Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00
compose.yml Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00
Containerfile Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00
COPYRIGHT Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00
dev.sh Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00
LICENSE Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00
LICENSE-books.alexwlchan.net Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00
README.md Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00
requirements.txt Stand vom 2026-10-25 2026-10-25 13:04:18 +01:00

readlist

Lese-Tracker: außen eine schlichte öffentliche Seite mit farbigen Buchkarten, innen eine Pflegeoberfläche hinter dem Login. Löst den früheren Jekyll-Aufbau ab (Markdown von Hand, Cover selbst herunterladen, Farbe extern ermitteln, _site per scp hochschieben).

Was sich geändert hat

früher jetzt
Markdown-Datei anlegen und benennen Formular in der Oberfläche
Cover suchen, benennen, ablegen Drag&Drop oder Bild-Adresse einfügen
Tint-Hex extern ermitteln und eintippen fünf Vorschläge aus dem Cover + Farbrad, mit Live-Vorschau
Jekyll-Build + 29 MB scp je Änderung Änderung ist sofort live, kein Deploy
Ruby + Python + Bash (macOS-Skripte) ein Python-Dienst

Die öffentlichen Adressen sind dieselben geblieben (/{jahr}/{titel}/), die alten /reviews/12-Adressen leiten weiter.

Bücher und Teilwerke

Ein Eintrag muss kein ganzes Buch sein. Eine Kurzgeschichte oder ein Aufsatz aus einem Sammelband ist ein Eintrag wie jeder andere — eigene Adresse, eigenes Lesedatum, eigene Besprechung — und zeigt über Aus welchem Band? auf den Band, aus dem er stammt. In der Jahresliste stehen Bücher und Teile gemeinsam und chronologisch; getrennt wird nur gezählt:

2026: I read 42 books, 7 stories and 3 essays

Den Band selbst musst du dafür nicht gelesen haben. Ein Eintrag mit dem Stand nur als Quelle erfasst steht in keiner Liste und in keiner Zählung, hat aber eine Seite — dort sammeln sich die Teile, die du daraus gelesen hast. Ein solcher Band braucht auch kein Lesedatum; seine Adresse hängt dann am Erscheinungsjahr.

Feld wofür
Art ganzes Buch, Kurzgeschichte oder Aufsatz
Aus welchem Band? der Band, in dem das Teil steht
Fundstelle 187-205 wird zu „S. 187–205"; Kap. 3 bleibt, wie es dasteht
Stelle im Band nur zum Sortieren der Teile auf der Bandseite
Verlag, Sprache Angaben des Bandes, die in die Zitation der Teile einfließen

Zitation

Ein Teilwerk wird bibliografisch zitiert — auf seiner Seite (der Bandtitel führt dorthin), auf der Karte in der Liste und in der Vorschau der Pflegemaske:

In: „Theorien des digitalen Kapitalismus: Arbeit und Ökonomie, Politik und
Subjekt", Hg.: Tanja Carstensen, Simon Schaupp, Sebastian Sevignani,
S. 187–205, Suhrkamp, 2023

Die Sprache richtet sich nach dem Werk, nicht nach der Seite. Ein englischer Aufsatz bekommt ed. by … und pp. 187–205, ein deutscher Hg.: … und S. 187–205; die Anführungszeichen folgen mit. Gewählt wird das am Eintrag — und wer einen Band auswählt, übernimmt dessen Sprache gleich mit, sodass die Einstellung einmal am Sammelband genügt.

Alles Fehlende fällt still weg. Für eine Kurzgeschichte aus einem Erzählband ohne Herausgeber, als EPUB ohne Seitenzahlen, bleibt:

In: „Erzählungen", Kap. 3, Diogenes, 2001

Hell und dunkel

Öffentliche Seiten und Pflegeoberfläche folgen prefers-color-scheme, ohne Umschalter. Die Farbe eines Buchs kommt aus seinem Cover und ist oft dunkel; als Linkfarbe auf dunklem Grund wird sie deshalb aufgehellt (render.on_dark), und die Karten bekommen beide Tönungen als CSS-Variablen mit — umgeschaltet wird in der Stildatei, nicht im Template.

Öffentlich und privat

Alles, was in der Oberfläche als privat ausgezeichnet ist (Format, Notizen), erscheint auf keiner öffentlichen Seite. Das ist nicht nur eine Frage der Templates: db.PUBLIC_COLUMNS bestimmt, welche Spalten für die öffentlichen Seiten überhaupt aus der Datenbank geholt werden — die privaten liegen dort gar nicht erst im Objekt. tests/test_privat.py hält das fest.

Loslegen

python -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python -m app.cli benutzer thee     # Konto anlegen
./dev.sh                                      # http://127.0.0.1:42311

Alten Bestand übernehmen

.venv/bin/python -m migrate.import_jekyll /pfad/zum/alten/books-repo

Liest die Markdown-Dateien samt Covern ein, behält Adressen und Lesedaten, übernimmt die _redirects und meldet am Ende, was auffällig war (Bücher ohne Cover, Cover ohne Buch, unbekannte Formatangaben). Lässt sich gefahrlos wiederholen — vorhandene Bücher werden aktualisiert, nicht verdoppelt.

Betrieb

echo "READLIST_SESSION_SECRET=$(openssl rand -hex 32)" > .env
podman-compose up -d --build

Der Dienst lauscht auf 127.0.0.1:8411; davor gehört ein Apache-vhost mit ProxyPass /. Datenbank, Cover und Sicherungen liegen im Volume readlist-data, also außerhalb des Repos: Ein Update ist git pull und ein Neustart, die Bilder wandern nie durch die Leitung.

Sicherung ziehen (täglich per cron oder systemd-timer sinnvoll):

podman exec readlist python -m app.cli sicherung

Umgebungsvariablen

Variable Vorgabe Bedeutung
READLIST_DATA ./data wo Datenbank und Cover liegen
READLIST_SESSION_SECRET zufällig je Start in der Produktion fest setzen
READLIST_SESSION_HTTPS false Sitzungscookie nur über HTTPS
READLIST_TITLE thee’s book tracker Name im Seitenkopf

Schema nachziehen

app/schema.sql legt nur an, was fehlt (CREATE TABLE IF NOT EXISTS) — sobald sich eine bestehende Tabelle ändert, reicht das nicht. Dafür steht in app/migrations.py je Schemastand ein Schritt; der erreichte Stand liegt in meta.schema_version, und der Dienst zieht beim Start selbst nach.

Wer eine Spalte ergänzt, erhöht CURRENT_VERSION und schreibt den Schritt zusammen — läuft der Dienst mit erhöhter Nummer, aber ohne Schritt, bricht er mit einer Meldung ab, statt die Version stillschweigend als erledigt zu vermerken und den Umbau für immer zu verschlucken.

Stand was er tut
2 Teilwerke (kind, parent_id, part_position) und der Stand reference. Baut books neu, weil SQLite eine CHECK-Bedingung nicht ändern kann
3 Zitation: publisher, language, locator — nur angehängte Spalten

Grenzen, bewusst gesetzt

  • Keine Metadatensuche im Netz. Titel, Verfasser und Jahr tippst du selbst. Die einzige Stelle, an der der Server nach außen spricht, ist das Holen einer Bild-Adresse, die du selbst eingefügt hast (covers.fetch_url) — mit Prüfung gegen Adressen im inneren Netz.
  • Keine Fremdbibliotheken im Browser. Farbrad, Filter und Vorschau sind eigenes JavaScript; ohne JavaScript bleiben alle Seiten benutzbar, nur die Filterleiste versteckt sich.

Tests

.venv/bin/python -m pytest tests -q

Herkunft

Angefangen hat readlist als Fork von alexwlchan/books.alexwlchan.net (Jekyll, MIT-Lizenz, © 2022 Alex Chan). Der Dienst hier ist neu geschrieben; aus dem Original stammen noch das Buch-Symbol (app/static/book.svg, book_white.svg) und die Papiertextur app/static/lightpaperfibers.jpg (von Subtle Patterns). Der Lizenztext des Originals liegt in LICENSE-books.alexwlchan.net.