This chapter gives you a detailed technical documentation of the architecture of the Libiry ecosystem. It is built with:
- Kivy - Cross-platform UI framework
- Python 3.12 - Core application logic
- SQLite - Thumbnail caching (local only)
- PyMuPDF/ebookmeta/mobi - E-book metadata extraction
Overview
┌───────────────────────────────────────────────────────────────────┐
│ Libiry Ecosystem │
├───────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │
│ │ Libiry │ │ Libiry2Go │ │ BookSpineScanner │ │
│ │ │ │ Calibre2Libiry │ │ │ │
│ │ │ │ Align Book Data │ │ │ │
│ │ │ │ │ │ │ │
│ │ (Desktop) │ │ (Desktop) │ │ (Web) │ │
│ │ │ │ │ │ │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌──────────────┐ │ │
│ │ │ Kivy │ │ │ │ Kivy │ │ │ │ Tesseract.js │ │ │
│ │ │ UI │ │ │ │ UI │ │ │ │ or GCV │ │ │
│ │ └────┬────┘ │ │ └────┬────┘ │ │ └───────┬──────┘ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ ┌────┴────┐ │ │ │ │ │ ┌───────┴──────┐ │ │
│ │ │ Core │◄─┼──┼───────┘ │ │ │ Book API's │ │ │
│ │ │ Modules │ │ │ │ │ └──────────────┘ │ │
│ │ └─────────┘ │ │ │ │ │ │
│ └───────────────┘ └─────────────────┘ └─────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ File System │ │
│ │ ┌──────┐ ┌─────┐ ┌──────────┐ ┌───────┐ │ │
│ │ │ EPUB │ │ CBZ │ │ Markdown │ │ Other │ │ │
│ │ └──────┘ └─────┘ └──────────┘ └───────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌───────────────────────────────────────────────────────┐ │ │
│ │ │ Markdown sidecar files │ │ │
│ │ │ (for all file types except EPUB and CBZ) │ │ │
│ │ └───────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘
Metadata logic
In Libiry, all book data are either stored in a sidecar or in the e-book itself. Per book type, either sidecars are used or they aren’t.
Libiry overrules book data from the book itself with book data from the accompanying markdown file. It supports .md and .markdown sidecars, but does not write .markdown sidecars itself.
In Libiry, the relationship between the source file and the sidecar file is based on the file name. Sidecar files have the same base name as the source file, but with a different extension. Because most operating systems and file managers have no knowledge of these relationships, they might allow the user to rename or move one of the files, thereby breaking the relationship. You should be aware of this when using other tools like Obsidian.
Configuration files
Libiry uses a hierarchical configuration system to maintain its settings:
- customize/customize.txt - user overrides (highest priority)
- resources/customize.txt - default values
- hardcoded defaults - fallback values
The user overrides can be maintained using the Settings button in Libiry.
The customizing settings are stored in:
- Windows: %APPDATA%\Libiry\customize\
- Linux: ~/.local/share/Libiry/customize/
The customize folder contains: customize.txt, can be maintained from Libiry’s settings screen selected_types.txt, can be maintained from Libiry’s settings screen language_codes.txt, can only be maintained directly An icons folder, can only be maintained directly
To override an icon for a screen button, simply paste a new icon file with the same file name over the old one. When you made a mistake, you can safely remove your own icon from the customize folder. Libiry wil then use the icon from its resources folder.
Cache configuration
The thumbnail cache is stored in:
- Windows: C:\Users<username>.libiry\cache\
- Linux: ~/.libiry/cache/`
Libiry application structure
┌────────────────────────────────────────────────────────────┐
│ libiry.py │
│ ┌────────────────────────────────────────────────────┐ │
│ │ LibiryApp │ │
│ │ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ │
│ │ │ Settings │ │ Grid │ │ Status │ │ │
│ │ │ Panel │ │ View │ │ Bar │ │ │
│ │ └────────────┘ └────────────┘ └──────────────┘ │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────┴────────────────────────────┐ │
│ │ Core Modules │ │
│ │ ┌──────────────────┐ ┌──────────────────────────┐ │ │
│ │ │metadata_extractor│ │ cover_extractor │ │ │
│ │ └──────────────────┘ └──────────────────────────┘ │ │
│ │ ┌──────────────────┐ ┌──────────────────────────┐ │ │
│ │ │ cover_cache │ │ file_opener │ │ │
│ │ └──────────────────┘ └──────────────────────────┘ │ │
│ │ ┌──────────────────┐ │ │
│ │ │ library │ │ │
│ │ └──────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
Key Classes
LibiryApp (libiry.py)
Main Kivy application class:
- Initializes UI components
- Handles navigation and state
- Manages settings persistence
- Coordinates background operations
class LibiryApp(App):
def build(self):
# Create main layout
# Initialize grid view
# Set up event handlers
passGridWidget (app/grid_widget.py)
Kivy RecycleView-based grid for displaying books:
- Efficient rendering for large collections
- Dynamic tile sizing
- Selection handling
- Lazy loading of covers
class GridWidget(RecycleView):
def __init__(self):
# Configure RecycleView
# Set up selection behavior
passCore Modules
metadata_extractor.py
Central metadata handling:
def extract_metadata(path: Path) -> dict:
"""Extract metadata from any supported file type."""
ext = path.suffix.lower()
if ext == '.epub':
return _extract_epub(path)
elif ext in ('.mobi', '.azw', '.azw3'):
return _extract_mobi(path)
# ... etcFormat-specific extractors:
_extract_epub()- Uses e-book metadata_extract_mobi()- Uses mobi library_extract_pdf()- Uses PyMuPDF_extract_comic()- Uses comicbox_extract_markdown()- Custom parser
sidecar functions:
get_sidecar_path()- Generate sidecar path for any fileread_sidecar_tags()- Parse sidecar for tagswrite_sidecar_tags()- Create/update sidecar with tagsmodify_sidecar_tags()- Add/remove specific tags
cover_extractor.py
Cover image extraction:
def extract_cover(path: Path, output_dir: Path) -> Optional[Path]:
"""Extract cover from e-book, return path to image."""
passSupports:
- EPUB cover images
- MOBI cover records
- PDF first page rendering
- Comic first image
cover_cache.py
SQLite-backed thumbnail caching:
class CoverCache:
def __init__(self, cache_dir: Path = None):
# Initialize SQLite database
pass
def get_thumbnail(self, path: Path) -> Optional[bytes]:
# Return cached thumbnail or None
pass
def set_thumbnail(self, path: Path, data: bytes):
# Store thumbnail in cache
passData Flow
Book display flow
1. User navigates to folder
│
▼
2. Scan folder for supported files
│
▼
3. For each file:
├── Check cover cache
│ ├── Hit: Use cached thumbnail
│ └── Miss: Extract cover
│ ├── Save to cache
│ └── Generate thumbnail
├── Extract metadata
│ ├── Check for sidecar
│ └── Read from file
└── Create grid tile
│
▼
4. Display in RecycleView grid
Metadata editing flow
1. User selects books
│
▼
2. Edit button → User edits metadata for one or more books
│
▼
3. For each selected file:
├── EPUB: Write to e-book
├── MOBI/AZW: Write to sidecar
├── CBZ: Write to ComicInfo.xml
├── Markdown: Write to file
└── Other: Write to sidecar
│
▼
4. Refresh display
BookSpineScanner application structure
┌─────────────────────────────────────────────────────────────┐
│ BookSpineScanner (PWA) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ main.js │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Camera │ │ OCR │ │ Book │ │ │
│ │ │ Module │ │ Module │ │ Lookup │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ Result Manager │ │ │
│ │ │ ├── Confidence scoring │ │ │
│ │ │ ├── User corrections │ │ │
│ │ │ └── Export generation │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────┴──────────────────────────┐ │
│ │ External APIs │ │
│ │ ┌────────────┐ ┌────────────┐ ┌─────────┐ ┌───┐ │ │
│ │ │Open Library│ │Google Books│ │Europeana│ │...│ │ │
│ │ └────────────┘ └────────────┘ └─────────┘ └───┘ │ │
│ └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
OCR Pipeline
Photo Capture
│
▼
Spine Detection (OpenCV.js)
│
├── Detect rectangles
├── Filter by aspect ratio
└── Extract spine regions
│
▼
OCR Processing
│
├── Tesseract.js (offline)
│ └── Character recognition
│
└── Google Cloud Vision (online)
└── Document text detection
│
▼
Text Cleanup
│
├── Remove noise
├── Identify title/author patterns
└── Extract ISBN if present
│
▼
Book Lookup
│
├── Search Open Library
├── Search Google Books
└── Search Europeana
└── Search Library of Congress
│
▼
Confidence Scoring
│
├── High (>85%): Green
├── Medium (50-85%): Orange
└── Low (<50%): Red
Data formats
Markdown (sidecar) YAML format
---
cover: url
booktitle: Title
author: Author
isbn: ISBN
tags:
- tag1
- tag2
rating: 8
---Performance
Times vary based on:
- File format complexity
- Storage speed (SSD vs HDD)
- Cover extraction settings
Lazy loading
- The grid only renders visible tiles
- Covers are loaded on demand
- Metadata is extracted as needed
Caching strategy
- SQLite for thumbnails
- In-memory for current folder
- No caching of raw metadata
Background threading
- Folder scans run in background
- UI remains responsive
- Progress updates via 8 Glossary/Kivy Clock
Security
File access
- Read/write only to user-specified folders
- No network access needed, except for cover lookup
- No data collection or telemetry
Libiry BookSpineScanner
- All processing happens in the browser or app
- Photos are never uploaded
- Settings are stored in local storage only
- API calls are only used for metadata lookup