Open-source project
dmzz-yyhyy/LightNovelReader avatar
dmzz-yyhyy/LightNovelReader

LightNovelReader, a Compose reader whose plugins do the scraping

一款基于Compose的多数据源轻小说阅读器。支持epub导出,自定义背景样式,本地书架和更新提醒等功能。

2,162 stars75 forksKotlinApache-2.0

At a glance

What is it?
LightNovelReader is a Kotlin and Jetpack Compose light novel reader for Android 7 through 15, with EPUB export, offline caching, a named bookshelf and a plugin system that supplies the data sources. It is also mid-rewrite, which shows in the repository as three different branch names in the documentation, and the source-independence rule has a consequence for the bookshelf that the README states without explaining.
Who is it for?
Adopt LightNovelReader if you read light novels on Android and want a reader whose content sources you can change yourself rather than accept one vendor's catalogue, and if you are prepared to treat the default branch as a development line rather than a stable one. Do not start from the README's contribution instructions without checking which branch is current, because the guide points at a third name.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What LightNovelReader is, and the rewrite it sits inside

LightNovelReader is an open-source light novel reader for Android, written in Kotlin with Jetpack Compose, and the README describes it in terms of a lightweight size and a smooth reading experience, with EPUB export, offline reading and multi-source support as the additional useful features.

The important word in that description is not in the English translation of the summary but in the README itself, where the project is marked as the refactored version, with a superscript annotation, and a link to the branch holding the code from before the rewrite. That single parenthetical tells you most of what an adopter needs to know about the state of the project. This is not a mature application with a long history that has been incrementally improved. It is a second implementation, and the first one still exists in the repository under a different branch name.

Rewrites carry a specific risk, and it is worth being concrete about it here. A reader application is not just a rendering surface. It accumulates a user's library, their reading progress, their settings, and their expectations about how a chapter is laid out. A rewrite has to reproduce all of that behaviour without the accumulated knowledge of why each quirk exists. The feature list gives some sense of the surface area that has to be carried across: caching, offline-first reading, discovery with recommendation rankings and tag categories, keyword search, a bookshelf with named collections and update notifications, EPUB export, and the ability to read comics as well as text.

The support range is worth noting as an engineering statement rather than a compatibility claim. The README says the app supports Android 7.0 through 15, which is a range spanning roughly eight years of platform versions, on a UI framework that arrived long after Android 7 and was designed for a much newer baseline. Shipping a modern declarative UI on a decade-old operating system is a real constraint, and it is the kind of thing that shows up as work rather than as a bullet point.

The audience is not in doubt either. This is for people who read serialized light novels from sources that are not an official store, which is the entire reason a data-source plugin system exists rather than being a nice-to-have.

Three branch names, and which one you are actually reading

The repository and its own documentation disagree about which branch matters, and if you are going to build or contribute you need to know which name to trust.

The repository's default branch is `dev/1.3`. The released versions are 1.2.2, 1.2.2a and 1.2.3, so the default branch is the next minor version in development rather than the released line, which is an unusual but not unreasonable choice. It does mean that a clone of the default branch is a development snapshot.

The README's contribution guide says something different. Its seventh step is to open a pull request against the `refactoring` branch, and the same README links to a design document for the EPUB module at a path beginning with `refactoring/`, meaning that document is expected to live on that branch too. A third name appears elsewhere in the same file, where the link to the pre-rewrite code points at `master`.

So the repository names in play are `master` for the original implementation, `refactoring` for the target of the rewrite as far as the documentation is concerned, and `dev/1.3` for whatever is actually the default branch today. At most two of those can be the live line, and the documentation does not say which pair.

The consequence for a contributor is concrete. If you follow the README literally and open a pull request against `refactoring`, and that branch is no longer where development happens, your work sits in a branch nobody merges. The reverse is also true: a pull request opened against `dev/1.3` when the maintainers actually want `refactoring` is a conversation before a review. Neither is a disaster, but both are avoidable by checking which branch the recent release commits landed on before you start.

The commit guidance has one more instruction that is unusual enough to mention. Contributors are asked to keep commits atomic and descriptive, and to update the version in `app/build.gradle.kts` if their change affects it. That is a small thing with a disproportionate effect: it puts the release version under the control of every contributor rather than the maintainer, which means a well-meant change can produce a release number nobody intended.

The mechanics the guide asks for are ordinary fork-and-branch, and worth reproducing here because the sequence is the whole instruction:

bash
git clone https://github.com/your-username/LightNovelReader.git
git checkout -b feature/your-feature-name
git push origin feature/your-feature-name

The three-channel build picture is worth stating at the same time, because it explains what a contributor is producing. GitHub Releases carries the last published version, the Actions artifacts carry whatever the default branch currently builds, and F-Droid carries a reviewed build under its own package id. Three channels means three version streams, and a version number that a contributor is expected to edit is being read by all three.

api/, plugin/ and proxy/: how the data source system is put together

A novel reader that does not ship its own content has to get it from somewhere, and the module structure in the repository is the clearest evidence of how that is arranged.

The top level contains `api/`, `plugin/` and `proxy/` as separate Gradle modules, alongside `app/` for the Android application, `epub/` for the export module, and a `compiler/` module whose purpose the README does not describe. Three names in a row is not a coincidence: a proxy module is what would handle network requests, an api module is what a plugin author codes against, and a plugin module is what loads and runs the extensions. That is a deliberate separation between the thing that fetches, the contract that extends, and the runtime that runs the extension.

The documentation for that system is unusually well organised for a project of this size. There is a template repository specifically for getting a plugin started, a plugin development guide hosted on the project's own documentation site alongside the main docs, and a separate API documentation site generated from KDoc. Three different resources for three different stages, which is the right structure: a template for someone starting, a guide for someone building, and generated reference documentation for someone integrating.

The template repository's name contains a typo, reading Plguin rather than Plugin, which is the sort of thing that costs a first-time contributor a minute. It is also the kind of thing that cannot be renamed once people have linked to it, so it is likely to stay.

The user-facing consequence of all this is stated in the feature list as the ability to switch data sources, to read comics as well as novels, and that data is independent between sources. That last clause is the one to think about carefully, and the README does not follow it through. If data is independent between sources, then the same book found in two sources is two separate entries. Your reading progress, your cached chapters, your place in the bookshelf, and any note you attached belong to the source, not to the book. Switching sources to get past a dead mirror does not carry your position with you, and a user who rotates between three sources maintains three copies of their library.

That may well be the right design, because sources have genuinely incompatible identifier schemes and a shared identity layer is a large amount of work to get wrong. But it is a design decision with a user-visible cost, and it is documented as a feature rather than as a trade-off.

Caching, offline reading, and a bookshelf that has to be rebuilt on a source switch

The caching and bookshelf features are listed together in the README as two separate capabilities, and together they are the part of the application that determines whether daily use is pleasant.

Caching is described as supporting cached book content and offline-first reading. The phrase that matters is offline-first, which describes an ordering rather than a state. The content is treated as available locally and refreshed opportunistically, rather than fetched on demand and cached as a side effect. For a reader used on a commute, that is the difference between opening a chapter instantly and waiting on a connection, and it is the feature that makes the source switch tolerable at all.

The bookshelf is described as a complete system supporting the creation and naming of shelves, adding books to a collection, and receiving notifications when a book updates. Named shelves matter more than they sound, because a reader accumulating hundreds of series across several genres needs more than a single favourites list, and the ability to create your own shelves means the taxonomy is yours rather than the app's.

Update notifications are the feature with a hidden dependency. A notification that a chapter has appeared is only possible if something is checking the source, and in a multi-source design that something has to run against the source the book came from, on a schedule, against a network. The README does not describe the scheduling, the battery cost, or whether it respects Android's background execution limits, and on a modern Android version that last point is the one that decides whether the feature works at all rather than working badly.

Combine the two features and the source-independence rule produces its real consequence. The bookshelf is a set of entries that each belong to a source. The cache belongs to a source. So the library is not a library of books, it is a set of per-source views of books, and the only thing that survives a source switch is the part you can reconstruct by hand, which is the shelf name.

For a reader with one or two sources this never comes up. For someone deliberately rotating between mirrors of the same site, which is a common pattern when a source is rate-limited or a chapter is late, it is the central annoyance of the application, and it is the thing to test before recommending it to a heavy reader.

Jetpack Compose back to Android 7, and a benchmark module in the tree

Two details in the repository suggest this is built by people who measure rather than assume, and both are worth noting because they are unusual in an app of this type.

The first is the platform range. The README states support for Android 7.0 through 15. Jetpack Compose is a modern declarative UI toolkit with tooling, libraries and idioms that assume a recent platform, and running it on a 2016 operating system means either accepting the toolkit's own minimum with desugaring, or carrying compatibility shims for the parts of the framework that reach into platform APIs the old version does not have. Either way, the decision constrains which Compose features can be used, and it constrains the build.

The second is a `benchmark/` module at the top level. An Android application that ships a benchmark module in its repository is measuring something, and for a reader the obvious candidates are the text layout path, which is where a scrolling list of formatted text either performs or does not, and the parsing path, which runs over every chapter fetched from every source. A library that caches and parses volumes of text has a plausible reason to want a repeatable measurement, and the README's claim of a smooth reading experience is the kind of claim that should be backed by one.

Neither detail is described in the README, which is a limitation of the documentation rather than of the project. There is no performance section, no figures for startup time, memory use, or parse throughput, and no explanation of what the benchmark module measures. A reader moving from a different app will notice the difference or not, and nothing in the repository lets them predict it.

The rest of the top level is the standard equipment for a released Android application. There is a `fastlane/` directory for release automation, a `crowdin.yml` for translation management, Gradle wrapper files, a `gradle.properties`, root and app build scripts in Kotlin DSL, and a `.github/` directory. There is also an `.idea/` directory, which means JetBrains project files are committed to the repository, a common source of merge noise that most projects exclude and this one has not.

EPUB export, the separate module built for it, and two other distribution channels

EPUB export gets more attention in this repository than a single feature usually would, and the structure around it is the interesting part.

The README has a dedicated section explaining that the EPUB handling was extracted into its own processing module, called EpubLib, and links to a document describing it. That document lives at the top level as `epub.md`, with a Russian translation alongside it as `epub_ru.md`, and the module itself is the `epub/` directory. So the export path is a separately designed, separately documented component rather than a few hundred lines at the end of the main application.

That is a reasonable boundary. EPUB is a specification with more structure than most people expect, involving an archive with a manifest, an ordered spine, metadata and a content document hierarchy, and a reader that has to write files other reading systems will accept. Doing that properly inside a reader app that is also parsing remote HTML is a separation of concerns worth having, and writing a design document for it is the behaviour of a maintainer who intends other people to read the code.

The distribution story is three channels, and they serve different purposes. GitHub Releases carries the latest published version. The Actions artifacts carry the newest features and bug fixes, which means there is a continuous build channel that people are expected to use if they want the current state, and that is the channel the default branch corresponds to. F-Droid carries a curated build under the package id `indi.dmzz_yyhyy.lightnovelreader`, distributed with a Simplified Chinese badge.

The F-Droid listing is the one that matters for anyone evaluating trust, because it is the channel with a review process rather than a self-service upload. An application whose entire purpose is fetching content from sources it does not control is exactly the kind of thing distribution channels scrutinise, and being listed on F-Droid with a specific package identifier is a stronger signal about how the project handles that than anything the README says.

There is also a sponsorship programme through a Chinese creator platform, with a statement that funds go to continued development, implementing new features, server maintenance if there is any, and community building. The parenthetical about servers is worth noticing, since it implies a server exists or may exist, which for an app that fetches from third-party sources usually means a proxy or a cache, which connects back to the `proxy/` module.

Apache-2.0, Crowdin, four translated READMEs, and two copyright holders

The licence is the Apache License version 2.0, and the README reproduces the full licence text inside a fenced block, attributed to two copyright holders for 2024, NightFish and yukonisen, with a repository `LICENSE` file as well. Apache-2.0 is a permissive licence with an explicit patent grant, which is a good fit for an Android application that people will modify and redistribute.

The dual copyright line is worth a sentence. A rewrite of an existing project raises the question of who holds rights to the new code, and listing two holders from the same year suggests the rewrite was a collaboration rather than a solo continuation. It also means any change of maintainer has to be handled carefully, since Apache-2.0 has no mechanism for one copyright holder to act alone.

Translations are handled in two places, and the split is sensible. The application's own strings go through Crowdin, with a project page, a badge, and an explicit invitation for anyone whose language is missing to request it. The project documentation, by contrast, is translated in the repository itself: alongside the main README there are Traditional Chinese, English and Russian versions, and the contribution guide has the same three-way split with `CONTRIBUTING_TW.md` and `CONTRIBUTING_US.md`.

Putting the app strings on Crowdin rather than in the repository is the right call at this scale, because a translation platform handles contributor access, review and per-language downloads without any tooling in the build. Keeping the developer documentation in the repository is also right, because a pull request that changes behaviour and the paragraph describing it can then be reviewed together.

The community infrastructure is broad for a single-developer project: GitHub issues with a template chooser, a QQ group with an invite link, a Discord server, and a Telegram group. Four channels for a reader app is a sign of where its users are, and for anyone filing a bug the practical order is the issue tracker first and the chat channels second.

The last thing to note is the topic list on the repository, which contains three misspellings, including an Android development topic with the first vowel wrong and two variants of the word material. It is a small thing, and it is a reminder that this is a project maintained by people in their spare time who would rather fix the reading experience than curate their topic tags.

Editorial conclusion

Adopt LightNovelReader if you read light novels on Android and want a reader whose content sources you can change yourself rather than accept one vendor's catalogue, and if you are prepared to treat the default branch as a development line rather than a stable one. Do not start from the README's contribution instructions without checking which branch is current, because the guide points at a third name. Verify first by installing the F-Droid build under indi.dmzz_yyhyy.lightnovelreader, reading the plugin development guide before writing a source, and testing offline reading on a source you switch away from, since the README states data is kept independent between sources.

Frequently asked questions

What is LightNovelReader?

It is an open-source light novel reading application for Android written in Kotlin with Jetpack Compose, described as lightweight with a smooth reading experience. The features include caching with offline-first reading, discovery with recommendation rankings and tag categories, keyword search, a bookshelf with named collections and update notifications, EPUB export, and the ability to read comics as well as novels.

How do I install LightNovelReader?

Three channels. GitHub Releases carries the latest published version, the Actions artifacts carry the newest features and bug fixes if you want the current state, and F-Droid carries a curated build under the package id indi.dmzz_yyhyy.lightnovelreader. The README states the app supports Android 7.0 through 15.

How do plugins and custom data sources work in LightNovelReader?

The README points to a plugin template repository, a plugin development guide on the project's documentation site, and a separate API documentation site generated from KDoc. The repository itself has api, plugin and proxy modules, and the app can switch data sources at runtime, read comics, and keeps data independent between sources.

What does the bookshelf and caching do?

The bookshelf supports creating and naming shelves, adding books to a collection, and getting notifications when a book updates. Caching covers book content and supports offline-first reading. Because data is kept independent between sources, a book's progress, cache and shelf membership belong to the source it came from rather than to the book.

Which branch should I contribute to?

The repository's default branch is dev/1.3 while released versions are 1.2.x, and the README's contribution guide says to open a pull request against the refactoring branch, with the pre-rewrite code on master. The commit guide also asks contributors to update the version in app/build.gradle.kts if their change affects it.

What licence is LightNovelReader released under?

The Apache License version 2.0, with copyright lines for two 2024 holders, NightFish and yukonisen. The full text appears both in the README and in a LICENSE file at the repository root, and application translations are managed on Crowdin while the documentation has in-repository translations.

Official sources

  1. dmzz-yyhyy/LightNovelReader on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dmzz-yyhyy-lightnovelreader.svg)](https://hysenlabs.com/projects/dmzz-yyhyy-lightnovelreader)