112 Commits
Author SHA1 Message Date
hectorqin dca46d909e perf: harden sync and authentication and defer route loading 2026-11-08 05:17:42 +00:00
hectorqin 990cd20ee1 fix(test): initialize settings in media browser review fixture 2026-11-07 12:13:52 +00:00
hectorqin eeb508f880 feat(media): add global and per-user controls with reading-only mode 2026-11-07 12:09:26 +00:00
hectorqin 7e66582f84 perf(pdf): render with PDF.js and authenticated range loading 2026-11-07 11:21:27 +00:00
hectorqinandClaude Opus 5.5 84e04d84b3 perf(server): cheaper rescans, stream-hash large books
Scan IO
- The walk decides whether a path is absorbed (page of a directory book,
  stray image on a shelf) before stat'ing it, replacing the separate
  recursive listing of every claimed directory.
- Ancestor checks are set lookups along the path instead of prefix scans
  over every shelf/claimed directory (quadratic on large libraries).
- Directory handlers may provide fingerprint(); an unchanged comic folder
  is settled from its listing instead of re-parsing and re-reading its
  cover every scan.

Memory
- File handlers may provide parseFile(ctx, {size, contentHash}). The
  scanner then hashes the file as a stream and settles identical bytes
  under a touched mtime before parsing. PDF, CBZ and EPUB implement it;
  EPUB metadata/cover/spine are read entry by entry through ZipArchive
  (falling back to the buffered path for archives it cannot open).
  parse(buf) is unchanged for acquisitions.

Synthetic library (1k shelves, 100 comics / 20k pages, one 300MB PDF):
unchanged rescan ~3.6s -> ~0.9s; first-scan peak RSS +352MB -> +78MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 09:18:23 +00:00
hectorqinandClaude Opus 5.5 5c807c46d9 fix(server): nested comic chapters, directory handler dispatch, AI batch cap
- comic-directory: `!isBookCollection()` negated a Promise, so chapter
  folders inside a volume (第01卷/第001话/*.jpg) were never collected.
  Await it, count only real book files as a collection (ComicInfo.xml
  and other sidecars no longer veto a chapter), and keep the root's
  loose-page pass non-recursive so volume pages are not counted twice.
- scanner: remember the handler that claimed a directory instead of
  re-resolving it with Array.find over an async predicate; drop the
  unused directory signature; list the tree once per scan and memoise
  "holds a book file" across discovery and the shelf pass.
- ai: one splitPathBatches for jobs and direct scans; folders larger
  than batchSize are split instead of overflowing a prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 09:07:46 +00:00
hectorqinandClaude Opus 5.5 df93900236 feat: 首页 continue cards share one shape; recently added / random media rows
- Books, video and audiobooks in 「继续」 are the same card (cover, work title,
  episode/chapter, progress), most recent first, up to two books. An episode
  resolves to its series for title and poster instead of reading "第 1 集".
- New rows 看点什么 / 听本书 / 听张专辑, each switching between 最近添加 and
  随便看看 (with 换一批); hidden when the channel is empty or unavailable.
- Server: browse accepts sort=random and sort=added (newest available file
  mtime across the work and its episodes/tracks; no schema change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 14:24:43 +00:00
hectorqinandClaude Opus 5.5 c0b8acd469 fix(server): 时长统计 reads 500 in production — resolve the shelf on first use
main.ts registers the routes with a placeholder context and attaches the
services afterwards, so the stats route captured ctx.shelf while it was still
undefined and every summary failed with "Cannot read properties of undefined
(reading 'getMany')". Writes never touched the shelf and were unaffected. The
service is now built on first request; a new test builds the app the way
main.ts does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 12:50:51 +00:00
hectorqinandClaude Opus 5.5 005b9be99e feat: AI 扫描 follows the AI switches, like AI 总结 already did
/api/v1/ai/capabilities now also answers `scan`: 启用 AI and 启用 AI 扫描 on and
Base URL, key and model filled in. 扫描与刮削 shows the AI 扫描 tab only then; while
the answer is unknown there is no tab, a ?tab=ai link falls back to 媒体库扫描,
and a server without the endpoint keeps the tab as before. The answer is
re-asked each time the page opens, so a switch changed in 系统设置 takes effect
on the next visit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 11:55:19 +00:00
hectorqinandClaude Opus 5.5 3b1f8b46dc feat: 时长统计 — reading, watching and listening time by day, month, year and work
Reading time is counted by the reader: only while the page is visible and the
reader has turned, scrolled or tapped within three minutes, or while 朗读 plays.
It is kept per book per 15-minute UTC slot and per device install, saved
locally until sent, and stored as MAX(stored, reported) in the new
reading_time table, so a replayed report never counts twice.

Media time is derived on the server from the progress saves the web player and
Android Media3 already send: the smaller of the wall-clock gap and the position
advance between two saves of one session, gaps over 30s ignored. It is stored
in media.db (media_watch_time), so neither client changes.

我的 → 时长统计 shows a stacked bar per day (one month), month (one year) or
year, with a legend that filters by kind, a table view and the works ranked for
the range or the bar picked. Administrators also get every account's totals.
Chart colours are new mode-level tokens (--color-chart-1..4), validated for
colour-vision separation on both surfaces. The React UI review now covers the
page and names the overflowing elements when a screen overflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 10:55:52 +00:00
hectorqinandClaude Opus 5.5 842331684a feat(server): keep a per-book reading-progress history
reading_progress keeps one row per book and last-writer-wins, which answers
"where am I" and loses "where was I": a stale device, a mistapped 目录 entry
or a clock running ahead overwrote the place for good (P0: 进度历史).

Every valid progress write now also lands in reading_progress_history,
including a write that lost the merge. A device reading on through one
chapter updates its latest entry (same chapter, within 5% and 30 minutes)
instead of adding rows; another chapter, a jump, another device or an
out-of-order write is a new entry. The newest 50 per book are kept.
GET /api/v1/sync/progress/:bookId/history lists them, newest first, for the
signed-in account only.

The table is new (CREATE TABLE IF NOT EXISTS), so the schema change is
additive and older builds sharing the database ignore it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 03:51:42 +00:00
hectorqinandClaude Opus 5.5 c1056743ad feat: the reader shows AI 总结 only when the server can answer it
The top bar always drew AI 总结, although AI is off by default, so most
readers pressed it only to be told AI_DISABLED (ui.md §4/§5: a control the
reader cannot use is not drawn). GET /api/v1/ai/capabilities tells any signed-in
account whether chapter summaries are enabled and configured, without exposing
the configuration; the reader asks once per book and leaves the button out
when the answer is no. Against an older server (404) the button stays.

The busy line while a summary is generated is a global notice like every other
one, instead of a hand-drawn .floating-notice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 04:36:01 +00:00
hectorqinandClaude Opus 5.5 415a9f5dd3 chore: drop CNB config; Android package is com.htmake.readerpro
- Remove .cnb.yml and CNB references in the Dockerfile and Compose file; the
  Compose default image is hectorqin/reader-pro:latest.
- Rename the Android applicationId/namespace and Kotlin package from
  cool.cnb.reader to com.htmake.readerpro (shortcuts, ProGuard, shell check).
  Installs under the old package are a different app and are not migrated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 02:03:45 +00:00
hectorqinandClaude Opus 5.5 32595c87a5 feat: file management is admin-only; members upload books behind a setting
- browse move/rename/mkdir/delete/metadata now require an administrator
  (403 ADMIN_REQUIRED for members).
- Members may upload, but only add books: unsupported formats are skipped,
  zip/cbz are stored as comic archives rather than unpacked, and overwrite is
  refused. The new business setting library.memberUpload (default on) turns
  member uploads off (403 UPLOAD_DISABLED); GET /library/upload reports
  `allowed`.
- The library page shows 上传书籍 to members when uploads are allowed.
- Shelf tests read user_books for the owner only, now that a second user exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 02:03:45 +00:00
Kiro Agent f15365d03c feat(shelf): 在读 / 想读 / 已读 and 分组 (prototypes 2 and 19)
Server: user_books gains reading_status. The shelf list computes each
book's readingState — an explicit 在读/已读 mark wins, otherwise progress
decides (>=99% 已读, started 在读), and 想读 gives way once reading starts —
filters by ?status=, and PUT /api/v1/books/:id/reading-status sets or
clears the mark.

Client: chips 全部/在读/想读/已读 and 分组 (a series or a tag from the
shelf's facets) above the toolbar, all kept in the URL; a state label on
each cover; the ⋯ menu marks 想读/在读/已读, clears the mark, or takes the
book off the shelf.

Docs: API, UI and media user guide for this and the other V3.1 gaps.
2026-10-20 04:47:06 +00:00
Kiro Agent 68e085482c feat(media): library management states and 扫描与元数据 (prototype 20)
Server:
- Scan jobs record the number of files listed (total), so a running scan
  can report a real percentage.
- New 扫描设置 business settings, shown as the page's 扫描与元数据 card:
  自动扫描新文件 (hourly rescan of every library, default on),
  刮削封面与简介 (after a completed scan, scrape items that have no online
  metadata and were never attempted, with the first configured provider;
  default off), 读取内嵌字幕与歌词 (default on; off hides embedded subtitle
  streams and lyrics tags), 扫描隐藏文件夹 (default off: names starting
  with '.' are skipped, e.g. ._film.mp4 resource forks).
- MediaAutomation runs the scheduled scans and post-scan scraping as the
  first active administrator; MediaScanner gains onFinished/isScanning.

Client:
- Each library card shows its latest scan: 上次扫描 今天 09:12 beside
  重新扫描; 正在扫描 · 已处理 62% with a progress bar and 取消; or the
  failure (路径不可访问…) with 重试 and 查看详情 (the tasks page).
  Starting or cancelling a scan now stays on the page; scan-jobs is polled.
- 扫描与元数据 card beside the list saves each switch with the settings
  revision, and hides itself on servers without these settings.
2026-10-20 04:41:03 +00:00
hectorqin 8584d7b4e3 fix(web): align React UI with legacy reader UX
Unify media secondary page chrome, loading states, playback controls, favorites filtering, and source/library screens with the legacy reader experience.
2026-10-05 13:23:27 +08:00
hectorqin cd003020f3 fix(web): align media continuation states 2026-10-04 11:40:26 +08:00
hectorqin 007cc3c754 fix(web): finish media UX alignment
Align the React media detail, player, history, queue, shelf, and resource flows with the legacy UX. Refresh the migration validation record and add focused route, resource, and shelf parity coverage.
2026-10-04 11:02:23 +08:00
hectorqin c0af84a00f test(web): verify React media flows against legacy UX 2026-10-03 20:19:52 +08:00
hectorqin 4f001ac2d4 fix(web): align React media screens with legacy UX 2026-10-03 18:24:12 +08:00
hectorqin a4586faadd feat(extensions): support file and batch actions 2026-10-02 23:30:08 +08:00
hectorqin dbf4b59d75 feat(media): streamline resource and task management 2026-10-01 16:58:49 +08:00
hectorqin 358d6005d1 feat(media): add batch match policies and richer recent playback 2026-10-01 13:30:28 +08:00
hectorqin d1997b03db fix(media): improve scraping playback and media presentation 2026-10-01 13:22:03 +08:00
hectorqin b9263ce701 fix(web): repair media themes and source filters 2026-10-01 12:28:09 +08:00
hectorqin 79eeb5ded6 test(android): cover asset origin login bootstrap 2026-10-01 08:09:29 +08:00
hectorqin c473d29b14 fix(cors): return asset origin for Android WebView 2026-10-01 08:07:40 +08:00
hectorqin f0e6bbf370 fix(android): configure server URL and allow asset origin 2026-10-01 08:06:57 +08:00
hectorqin 6e4f4fbdea feat(media): improve scraping and OpenList playback 2026-09-30 23:48:23 +08:00
hectorqin 0166164c04 feat(media): add directory filename rules and replace folder routes 2026-09-29 15:35:07 +08:00
hectorqin 26265e4aac refactor: split scan history into tabs 2026-09-29 14:12:48 +08:00
hectorqin 5fe453ce39 feat: make AI media scans trackable jobs 2026-09-29 13:34:27 +08:00
hectorqin df4ed11f56 fix(ai): add structured troubleshooting logs 2026-09-29 12:14:14 +08:00
hectorqin d039d1ca20 fix(ai): allow long media scan requests 2026-09-29 12:09:33 +08:00
hectorqin 61e70ec418 feat(ai): add configurable scanning and chapter summaries 2026-09-29 11:57:45 +08:00
hectorqin 713d1eb5aa fix(media): recognize disc filenames under directory rules 2026-09-29 10:50:53 +08:00
hectorqin 2d13f11811 feat(media): support named season and edition groups 2026-09-29 09:59:56 +08:00
hectorqin 9bf885eabc fix(media): infer episode numbers for directory series 2026-09-29 09:48:38 +08:00
hectorqin dc2c96ae9a fix(media): apply inherited rules and clean release numbering 2026-09-29 09:38:33 +08:00
hectorqin 5f3a75c973 fix(media): keep folder navigation and honor directory season rules 2026-09-29 07:28:41 +08:00
hectorqin 0f06d6e9f3 fix(media): restore recognition scrolling and add bulk selection 2026-09-28 22:33:32 +08:00
hectorqin 385e68de78 feat(media): add directory recognition rules and reviewed reidentification 2026-09-28 17:53:30 +08:00
hectorqin f583b32e46 feat(media): clean missing resources by directory 2026-09-28 17:24:04 +08:00
hectorqin 5db43d2da0 fix(media): recover blocked direct playback through CORS and proxy 2026-09-28 14:43:26 +08:00
hectorqin 501ca71f6b fix(media): prefer OpenList direct playback and open player before buffering 2026-09-28 13:39:47 +08:00
hectorqin ff976a4cf6 feat(settings): manage business configuration from admin pages 2026-09-28 12:38:54 +08:00
hectorqin 996f0c555e fix(media): accelerate remote scans and expose scan diagnostics 2026-09-28 10:58:02 +08:00
hectorqin 73f9eb6e20 feat(media): add isolated media libraries and playback
Support local and OpenList libraries, metadata, independent media navigation, themes, and audio/video playback. Preserve approved v2 prototypes and current documentation; archive superseded review artifacts and keep generated validation output outside version control.
2026-09-27 18:22:51 +08:00
hectorqin 06b3d09e22 feat(admin): manage users and invite-based registration 2026-09-24 17:19:48 +08:00