Files
wordarr/wordarr/db.py
T
SteppenstreunerandClaude Fable 5 508ae5f26e Add wordarr: request & import manager for ebooks, comics and audiobooks
FastAPI + SQLite backend with vanilla-JS web UI. Requests via Open
Library / Audible / AniList metadata search (or manual entry) with
per-request target library selection. Manual import flow: scan the
download dir, fuzzy-match files against missing requests, review,
then move+rename into the library per configurable naming templates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 12:34:07 +02:00

68 lines
2.3 KiB
Python

import datetime
from sqlalchemy import ForeignKey, String, create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, sessionmaker
from . import config
class Base(DeclarativeBase):
pass
class Library(Base):
__tablename__ = "libraries"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String, unique=True)
media_type: Mapped[str] = mapped_column(String) # ebook | audiobook | comic
root_path: Mapped[str] = mapped_column(String)
folder_template: Mapped[str] = mapped_column(String)
file_template: Mapped[str] = mapped_column(String)
requests: Mapped[list["BookRequest"]] = relationship(back_populates="library")
class BookRequest(Base):
__tablename__ = "requests"
id: Mapped[int] = mapped_column(primary_key=True)
media_type: Mapped[str] = mapped_column(String)
library_id: Mapped[int] = mapped_column(ForeignKey("libraries.id"))
title: Mapped[str] = mapped_column(String)
authors: Mapped[str] = mapped_column(String, default="") # comma-separated
external_id: Mapped[str] = mapped_column(String, default="") # ISBN / ASIN / AniList id
year: Mapped[int | None] = mapped_column(default=None)
series: Mapped[str] = mapped_column(String, default="")
volume: Mapped[int | None] = mapped_column(default=None)
cover_url: Mapped[str] = mapped_column(String, default="")
status: Mapped[str] = mapped_column(String, default="missing") # missing | imported
created_at: Mapped[datetime.datetime] = mapped_column(
default=lambda: datetime.datetime.now(datetime.UTC)
)
imported_path: Mapped[str] = mapped_column(String, default="")
library: Mapped[Library] = relationship(back_populates="requests")
_engine = None
SessionLocal = None
def init_db(db_path=None):
global _engine, SessionLocal
path = db_path or config.DB_PATH
path.parent.mkdir(parents=True, exist_ok=True)
_engine = create_engine(f"sqlite:///{path}", connect_args={"check_same_thread": False})
Base.metadata.create_all(_engine)
SessionLocal = sessionmaker(bind=_engine, expire_on_commit=False)
return _engine
def get_session():
session = SessionLocal()
try:
yield session
finally:
session.close()