Commit graph

1999 commits

Author SHA1 Message Date
NBA2K1
34c97cf7ec Add graceful handling for database init failures
- Wrap `storage.initDB` in try/catch
- Log initialization errors with stack trace
- Show a minimal `_StartupErrorApp` when startup fails
- Skip post‑launch initialization if DB init did not succeed
- Introduce simple error UI with selectable error text
2026-05-11 19:45:27 +02:00
GitHub Action
d27443470f source update: v0.7.75 2026-05-11 09:19:55 +00:00
Moustapha Kodjo Amadou
905c6f0a89 v0.7.75 2026-05-11 09:55:18 +01:00
Moustapha Kodjo Amadou
6acb8d11b1
Merge pull request #725 from mrandhawa14/fix/macos-tcc-permission-denied
fix(macos): host libmdbx DB under Application Support to avoid TCC EACCES on launch
2026-05-11 09:17:51 +01:00
Moustapha Kodjo Amadou
f93d9277b9 refactor: simplify cover image resolution in LibraryGridViewWidget and LibraryListViewWidget 2026-05-11 09:04:37 +01:00
Moustapha Kodjo Amadou
2d9eebe94e
Merge pull request #728 from mrandhawa14/fix/cover-decode-resize
perf(library): decode covers at thumbnail resolution to cut image-cache RAM (refs #609)
2026-05-11 09:00:42 +01:00
Moustapha Kodjo Amadou
6449cfdd5a + 2026-05-11 08:59:18 +01:00
Mehakdeep Singh
e16f0ba9be Merge remote-tracking branch 'upstream/main' into fix/cover-decode-resize
# Conflicts:
#	lib/modules/library/widgets/library_gridview_widget.dart
#	lib/modules/library/widgets/library_listview_widget.dart
2026-05-11 00:56:00 -07:00
Moustapha Kodjo Amadou
a961834a88
Merge pull request #723 from NBA2K1/reduce-code-duplication
New file to reduce code duplication
2026-05-11 08:42:22 +01:00
Moustapha Kodjo Amadou
b507f17846
Merge pull request #719 from NBA2K1/main
Make Code More Readable
2026-05-11 08:38:24 +01:00
Moustapha Kodjo Amadou
e345c68d83
Merge pull request #727 from mrandhawa14/fix/image-cache-cap
perf(images): cap decoded image cache by platform (refs #609)
2026-05-11 08:36:33 +01:00
Moustapha Kodjo Amadou
8cc66e1f72
Merge pull request #726 from mrandhawa14/fix/statistics-memory
perf(statistics): aggregate inside Isar to fix #543 crash on large libraries
2026-05-11 08:34:05 +01:00
Moustapha Kodjo Amadou
cf86d63ac4
Merge pull request #724 from mrandhawa14/fix/anime-download-stall
fix(downloads): unstall anime downloads on URLs with query strings + 206 responses
2026-05-11 08:30:49 +01:00
Mehakdeep Singh
861ee65113 perf(library): decode covers at thumbnail resolution to cut image-cache RAM
Refs #609 (high RAM with stutters).

Manga / anime covers from sources are typically 720x1080 or larger
(~3 MB decoded RGBA per cover). The library grid, library list and
generic browse / search card widgets render those covers at roughly
150x220 logical pixels, but every cover decoded to its full source
resolution and that decoded bitmap landed in Flutter's `imageCache`.
With 30-50 covers in flight during a normal scroll, the default
100 MB cache filled and the engine started evicting + re-decoding
aggressively — exactly the symptom in #609 (stutters + high RAM).

Mangayomi already had `ExtendedResizeImage` available via the
`extended_image_library` package and used it in one place
(`cachedCompressedNetworkImage`, called only from the History
screen). This commit generalises that pattern.

Add a `coverProvider()` helper in `lib/utils/cached_network.dart`
that wraps `CustomExtendedNetworkImageProvider` in
`ExtendedResizeImage` with a 200 KB encoded budget — sharp at
typical thumbnail size on high-DPR screens, ~3.6x smaller decoded
than a full-resolution cover. Pass through the same `cache` /
`cacheMaxAge` knobs the underlying provider exposes so existing
disk-cache behaviour is preserved.

Swap the three high-traffic thumbnail call sites to use it:

* `lib/modules/library/widgets/library_gridview_widget.dart`
* `lib/modules/library/widgets/library_listview_widget.dart`
* `lib/modules/widgets/manga_image_card_widget.dart`
  (both `MangaImageCardWidget` and `MangaImageCardListTileWidget`,
  used by browse and search results)

Deliberately not changed:

* The manga / anime detail page hero cover — large display, full
  resolution is appropriate.
* Reader pages — already memory-managed by `ChapterPreloadManager`
  and need full resolution for actual reading.
* `cachedNetworkImage()` and other lower-traffic thumbnail surfaces
  (tracker results, calendar, recommendation grid). Easy to extend
  in a follow-up if anyone asks; kept narrow here so review is
  manageable.

Verified

* `flutter analyze` clean on every touched file
* `flutter build macos --release` succeeds
* Smoke-tested on macOS with the local-all-fixes build: library
  grid, library list and browse card all render identical-looking
  covers at typical thumbnail sizes; no visible quality regression
  at the displayed scale
2026-05-09 23:37:44 -07:00
Mehakdeep Singh
7b3916a221 perf(images): cap the decoded image cache by platform
Refs #609 (high RAM with stutters).

Flutter's `imageCache.maximumSizeBytes` defaults to 100 MB. With a large
manga library and full-resolution covers (a typical 720x1080 cover is
about 3 MB decoded), scrolling fills that cap quickly and the engine
starts evicting and re-decoding aggressively — felt as stutters and
elevated heap on memory-constrained devices.

Set the cap explicitly, sized for the platform: 64 MB on mobile, 256 MB
on desktop. Mobile gets a tight ceiling so the OS does not kill the app
on heap pressure; desktop keeps a generous budget so users with very
large libraries still scroll smoothly.

This is a one-line ceiling, not a fix for the underlying decode-at-
source-resolution behaviour — that is being tracked separately and
will further reduce per-image cache pressure.

The encoded-bytes LRU cache in `CustomExtendedNetworkImageProvider`
(`_memoryCache`, 50 MB) is independent of `PaintingBinding.imageCache`
and is unaffected by this change. So is the disk cache from
`extended_image_library`. Only the in-memory cache of decoded
`ui.Image` objects is bounded here.

Verified:
- `flutter analyze` clean
- `flutter build macos --release` succeeds
- Tested on macOS — Activity Monitor shows the desktop budget is
  comfortably above what the existing UI normally needs; mobile budget
  is consistent with what the system already imposes via OOM kills.
2026-05-09 23:30:59 -07:00
Mehakdeep Singh
8babb975ab perf(statistics): aggregate inside Isar instead of loading all favourite chapters into memory
Fixes #543. The statistics screen was crashing on Android with a
populated library (~75+ favourites). Repro: open the More -> Statistics
screen on an account with a real-sized library; app hangs and is
killed.

Cause is in `getStatistics()` in `lib/modules/more/statistics/
statistics_provider.dart`. The provider materialised every favourite
manga *and* every chapter of every favourite into a Dart list, then
walked the lists in memory:

    final items = await isar.mangas.filter()...findAll();
    final chapters = await isar.chapters.filter()
        .manga((q) => q.favoriteEqualTo(true)...).findAll();

For a library with 75 favourites at ~100 chapters each that is 7,500+
heavy Isar objects materialised at once just to compute six counts.
The "completed items" loop then re-fetched chapters per completed
manga via the link relation (`item.chapters.toList()`), and the
reading-time aggregation loaded every History row to sum a single
int field.

This commit rewrites the function to do all aggregation inside Isar:

* total / read / completed-items / downloads => `count()` queries on
  indexed filter chains. No object hydration.
* completed favourites => projects only the IDs (`idProperty()`),
  then runs two cheap `count()` queries per item to check
  "has-chapters && unread == 0". Logic is unchanged from the original
  `every(isRead) && isNotEmpty` check.
* reading time => projects the `readingTimeSeconds` field
  (`readingTimeSecondsProperty()`) and folds the resulting List<int?>.
  Avoids hydrating full History rows.

Memory: peak in-memory list shrinks from ~7,500 fully-hydrated chapters
plus all favourite mangas plus all history rows, to (at most) the IDs
of completed favourites (typically <50 ints) and the projected reading-
time list (one int per history row, decoded as a flat list).

DB round-trips: the completed-items branch now does 2 round-trips per
completed favourite instead of 1 link-traversal per completed favourite.
On indexed `mangaId` + `isRead` filters this is sub-millisecond per
query and well below the cost of materialising thousands of rows. The
overall function is faster end-to-end on any non-trivial library.

Behaviour preserved: same six fields, same definitions, same edge
cases (manga with no chapters is not counted as completed).

Verified
- `flutter analyze` clean on the touched file
- `flutter build macos --release` succeeds
- Manual smoke test on macOS with the local-all-fixes build (the bug
  reproduces only with a large mobile library; macOS desktop with a
  small test library returns the same numbers as before)
2026-05-09 23:12:49 -07:00
Mehakdeep Singh
b42bceb6f9 fix(macos): host libmdbx DB under Application Support to avoid TCC permission denial on launch
On macOS, the libmdbx / Isar database lives under
`getApplicationDocumentsDirectory()` -> `~/Documents/...`. With iCloud
Drive's "Desktop & Documents Folders" sync enabled (a common default),
macOS protects ~/Documents with TCC and denies unsigned / sideloaded /
dev / not-yet-permission-granted builds the file access libmdbx needs
to open its database. The result is a black screen on launch with the
following error in the Flutter / app log:

    [ERROR:flutter/runtime/dart_isolate.cc(1402)] Unhandled exception:
    IsarError: Cannot open Environment: MdbxError (13): Permission denied

POSIX errno 13 is EACCES, raised by the OS for the access denial — not
errno 15 (ENOTBLK / "Block device required"), and not iCloud "Optimise
Mac Storage" evicting files. Verified on macOS 26.3 / Apple Silicon
with iCloud Desktop & Documents sync active: a Terminal `mkdir`+`echo
> file` to the same path succeeds (Terminal inherits the user's TCC
grant), but the unsigned dev build fails on first DB open with the
error above.

Fix: on macOS only, host the database under `getApplicationSupport-
Directory()` -> `~/Library/Application Support/<bundle id>/...`. That
location is app-private, not TCC-gated, and Apple's recommended
location for app data files. iOS, Windows, Linux are unchanged — they
keep using Documents (iOS for Files-app visibility next to backups,
Windows / Linux because Documents is the conventional location and
neither has TCC).

Includes a one-shot best-effort migration: existing macOS users with a
DB at `~/Documents/Mangayomi/databases/` have it renamed to the new
path on first launch. Migration is skipped if the new location is
non-empty so we never overwrite user data, and any failure falls back
to a fresh DB rather than crashing on launch (the user can then move
the legacy directory manually if needed). Subsequent launches skip the
migration branch because the new path already exists.

Repro
- macOS with iCloud Drive's "Desktop & Documents Folders" sync enabled
- Unsigned / sideloaded / dev build of Mangayomi (or signed build that
  hasn't yet received the user's "Files and Folders > Documents" TCC
  grant)
- Launch -> black screen, IsarError MdbxError (13)

Verification
- Reproduced the exact error on this branch's parent commit
  (upstream/main 25c1d72c) on macOS 26.3, iCloud Desktop & Documents
  sync active, captured `MdbxError (13): Permission denied`
- After this patch the same build launches cleanly and opens the
  database at `~/Library/Application Support/<bundle>/Mangayomi/
  databases/mangayomiDb.isar`
- Existing 15 MB Isar database from a prior run preserved through the
  rebuild — no data loss

Notes
- This is a narrower follow-up to the earlier proposed Application-
  Support move that was correctly rejected for being cross-platform
  and missing migration. This change is gated by `Platform.isMacOS`
  and migrates existing macOS users.
- Hive (`Hive.initFlutter` in main.dart) still uses Documents on
  macOS. It is initialized after Isar via `_postLaunchInit` and is
  unawaited, so a Hive failure wouldn't reproduce the black screen.
  If Hive turns out to be affected by the same TCC denial, a
  follow-up PR can move it the same way.
2026-05-09 22:09:53 -07:00
Mehakdeep Singh
bbc7fa04f5 fix(downloads): unstall anime downloads on sources that return URLs with query strings or HTTP 206
Two related bugs left anime downloads stuck at 0% with no error visible to
the user. Manga downloads from clean HTTPS sources were unaffected.

1) lib/utils/extensions/string_extensions.dart

   isMediaVideo() did a plain endsWith on the full URL string, so URLs
   shaped like https://host/play/{id}/video.mp4?for={token} (used by
   AnimeGG and several other sources) failed the filter because of the
   trailing `?for=...` query string. With both m3u8Urls and nonM3u8Urls
   empty in downloadChapter, the surrounding
   Future.doWhile(() => isOk == true) poll never sets isOk and waits
   forever -- MDownloader is never constructed.

   Fix: match against Uri.tryParse(this)?.path instead of the full
   string, and use a leading "." in the suffix so e.g. "flashmp4" cannot
   accidentally match.

2) lib/services/download_manager/download_isolate_pool.dart

   Once the URL passes the filter, _downloadFile (anime branch) opens a
   streaming request. When the source extension sets Range: bytes=0-,
   the server correctly responds with HTTP 206 Partial Content. The
   previous "if (response.statusCode != 200)" check rejected that,
   retried 3x, and threw. The throw was masked by an outer catch(_) in
   downloadChapter, so the user only saw a forever-spinner.

   Fix: accept any 2xx (>= 200 && < 300). Same fix applied to
   _downloadSegment for HLS segment fetches.

Repro
- Source: AnimeGG (en) -- install via Mangayomi extensions
- Pick any episode (tested with Toriko Ep 147, Gintama Ep 39, Grand Blue
  Ep 12)
- Tap the download icon

Before: an empty
".../AnimeGG (EN)/<series>/<episode>.mp4"-named folder is created, the
download icon stays in the spinner state, no error toast.

After: the .mp4 is written to disk at the size declared in Content-Length
(65,026,283 bytes for Toriko Ep 147), plays in the system video player.

Tested on macOS 26.3 / Apple Silicon with AnimeGG (multiple episodes,
multiple series, including a 180 MB 720p) and a manga control
(Asura Scans, 9 pages) on the same build to confirm no regression on the
manga path.
2026-05-09 19:10:35 -07:00
NBA2K1
aeff8be3b3 refactor the widget‑wrapping logic to be cleaner
- Replace child mutation with two clearly named variables
  - This is a pure refactor:
    - base = the result of wrapping with BotToastInit
    - withBackHandler = optionally wrapped with _MouseBackButtonHandler
  - No logic changes - just clearer variable names and no reassignment.

Improves readability and maintainability.
2026-05-09 18:23:37 +02:00
NBA2K1
116ade1bb8 New file to reduce code duplication
+ listview now also shows unread chapter count like gridview did, instead of all chapter count.
2026-05-09 18:11:20 +02:00
Moustapha Kodjo Amadou
25c1d72c8b
Merge pull request #721 from NBA2K1/improve-updates
Improve updates
2026-05-09 16:07:59 +01:00
Moustapha Kodjo Amadou
e2d67e7157
Merge pull request #722 from NBA2K1/fix-exception
Fix null pointer exception
2026-05-09 16:06:47 +01:00
Moustapha Kodjo Amadou
db4a777609
Merge pull request #720 from NBA2K1/fix-downloadedChapterIdsProvider
Fix downloadedChapterIdsProvider
2026-05-09 16:04:06 +01:00
NBA2K1
caab37f662 Fix null pointer exception 2026-05-09 15:03:26 +02:00
NBA2K1
1b630954b1 Remove BuildContext from checkForUpdate Provider
Do not pass Context into any provider.

Handle logic and BotToasts in the onTap of the AboutScreen, not inside the provider.

Also make _checkUpdate() more efficient, by fetching the `/latest` API.
Before, it was fetching 10 releases and immediately discarding 9, only leaving the latest.
2026-05-09 12:59:32 +02:00
NBA2K1
27690975f4 Make _updateLibrary() fully async
also make _updateNumbers() readable.
2026-05-09 12:59:12 +02:00
NBA2K1
e0a8301595 Use downloadedChapterIdsProvider instead
Use the already-existing downloadedChapterIdsProvider and do a simple Set.contains() lookup instead of the synchronous Isar query.

Performance improvement
2026-05-09 12:52:26 +02:00
NBA2K1
134a7406d9 remove redundant helper 2026-05-09 00:36:16 +02:00
NBA2K1
0316afc2c6 Make downloadedChapterIdsProvider async 2026-05-09 00:33:36 +02:00
NBA2K1
8914357d89 Fix downloadedChapterIdsProvider
Before the "Downloaded" filter in the library AND the "Downloaded chapters" badge would not update, unless you restart the app.

Now the "Downloaded chapters" badge is increasing and the "Downloaded" filter is showing new downloaded manga chapters and hiding deleted manga chapters.
2026-05-08 23:52:52 +02:00
Moustapha Kodjo Amadou
56ab6151e6 Update 2026-05-08 15:05:03 +01:00
NBA2K1
b799a6d377 No need to reverse the list
before:
list, reverse, first
list, reverse, last

after:
list, last
list, first

Small performance improvement
2026-05-07 21:29:59 +02:00
NBA2K1
40fce92a87 Extract helper to keep build() readable
No functional change. Just a simple method extraction from inside `itemBuilder` to `_buildPagedItem()`.
2026-05-07 21:28:41 +02:00
NBA2K1
6880090f8d Reduce Code Duplication 2026-05-07 19:17:10 +02:00
NBA2K1
0672d1de67 Reduce Code Duplication 2026-05-07 19:14:50 +02:00
Moustapha Kodjo Amadou
73a7528abf
Merge pull request #717 from NBA2K1/main
Improve _handlePageNavigation()
2026-05-04 11:51:13 +01:00
NBA2K1
3fe74f8883 Clean up _setReaderMode()
No logic difference. This refactor just makes the helper more readable.
2026-04-30 19:47:46 +02:00
NBA2K1
d6bc53ab1f Add comments to ReaderModeExtension 2026-04-30 19:23:00 +02:00
NBA2K1
ffe8dee065 Improve _handlePageNavigation()
The helper now handles every continuous reading mode, not just webtoon.
2026-04-30 17:32:15 +02:00
NBA2K1
456589ccfb Use ReaderModeExtension 2026-04-30 17:30:43 +02:00
GitHub Action
d8df3a361e source update: v0.7.70 2026-04-30 13:19:10 +00:00
Moustapha Kodjo Amadou
f5ebd01dc7 v0.7.70 2026-04-30 13:58:16 +01:00
Moustapha Kodjo Amadou
fe42b43cf8 Update Dart SDK version to 3.11.5 in pubspec files 2026-04-30 13:57:16 +01:00
Moustapha Kodjo Amadou
2ecd78283a
Merge pull request #716 from 5y3b/QoF-webtoon
Refactor page navigation handling in reader view
2026-04-30 13:08:46 +01:00
abdelmonm alsnajleh
bc19e2d7cb
Merge branch 'main' into QoF-webtoon 2026-04-30 14:25:01 +03:00
Moustapha Kodjo Amadou
8c416563d4
Merge pull request #714 from NBA2K1/main
Rewrite chapter update & sorting; Preserve read state & improve ChapterRecognition & adapt EndOfMangaCard to reading mode & Clean Code
2026-04-30 11:19:11 +01:00
NBA2K1
3051c0ab97 Fix bug
Fix a rare bug in the chapter list.
2026-04-27 22:58:38 +02:00
Abdelmonm Alsnajleh
0894b94767 Refactor page navigation handling in reader view
* Extracted page navigation to its own function
* Allow for better reading in webtoon mode
2026-04-27 20:14:24 +03:00
NBA2K1
8d5aa05952 Fix potential focus flicker
`wrapWithKeyboardListener` creates a `FocusNode()` internally on every call.
If no `focusNode` is passed, a new one is allocated every rebuild, which can cause focus flicker.
Without this fix, keyboard focus can be intermittently lost after widget rebuilds,
which would silently swallow keyboard shortcuts.
2026-04-27 00:08:15 +02:00
NBA2K1
ad4207da82 Add Copilot suggested change
https://github.com/kodjodevf/mangayomi/pull/714#discussion_r3144115142
2026-04-26 23:57:23 +02:00