Commit Graph
134 Commits
Author SHA1 Message Date
edde746 3f49bcabf8 fix(prefs): replace the desktop preference store atomically
Upstream shared_preferences_windows and _linux write the whole preference
document with a bare `writeAsStringSync`. That opens with the default
`FileMode.write`, which truncates the live file before writing it, so every
single preference write has a window in which the only copy on disk is empty
or half-written. A crash, power loss, forced reboot or antivirus interception
inside that window leaves a document that fails to parse on every subsequent
launch — and the store holds the credential-vault key, so the loss is not
recoverable by rewriting it. This is the corruption class behind #1732; the
recovery path already landed is a band-aid over it.

Vendor both packages under packages/ — the convention saf_util and
wakelock_plus already follow — and stage, flush, then rename over the target.
The flush has to precede the rename or it could publish contents that were
never committed, the same corruption by another route. Staging uses one fixed
sibling name rather than a stamped one, because the file is a plaintext copy
of the vault key, tracker refresh tokens and Seerr cookies; it is created in
the target's own directory so rename stays on one volume and the mode matches
what the canonical file would have had, and a stale one is swept once the
canonical document has been read cleanly. Both deltas are marked in-source and
in provenance.json with the refresh contract.

Atomicity is proven, not asserted. A hard link to the store observes the old
document after a write, which only holds when the directory entry was replaced
— truncate-in-place would have rewritten the shared inode, and that test does
fail against unpatched upstream. Upstream's own suites still pass unchanged in
both packages and now run in CI, so the patch keeps the contract it inherited.
Windows `MoveFileExW` replacement semantics cannot be proven on a POSIX runner
or a memory file system, so they get their own test on the existing
windows-latest job, including replacement while a reader holds the file open —
antivirus and Search Indexer both do.
2026-08-01 06:59:20 +02:00
edde746 3ae7aa554b fix(prefs): recover a preference store whose bytes are not valid UTF-8
`File.readAsString` reports a UTF-8 decode failure as a FileSystemException,
not a FormatException, so three guards written for that case never ran. The
preflight's `on FormatException` branch was unreachable and its
`on FileSystemException` sibling waved the document through; the plugin then
threw the same FileSystemException, which failed the FormatException/TypeError
test that decides repairability; and quarantine's lossy-decode fallback sat
dead behind a rethrow. A store with one bad high byte — a UTF-16 BOM, a stray
0x80 — therefore reached the user as a failure screen with no Repair button
and no way forward at all.

Read bytes and decode explicitly instead, at both sites. Classification moves
into describeStoreDamage, so a failure that surfaces after the preflight
passed is judged by re-reading the file rather than by the error's type: a
denied or locked store is indistinguishable from a decode failure by type or
message, and offering a destructive repair for a permissions problem would
reset every setting and risk the vault key over something a chmod fixes.
isCorruptStoreError went with it, having no remaining callers.

A repair that quarantines the store and then cannot reopen it no longer
strands the process either. The repaired future was built straight from the
cache loader, bypassing the self-healing reset sharedCache installs, so a
failed reopen parked a rejected future in _cacheFuture and every later attempt
replayed that stale error — with the damaged file already moved aside, so a
restart would have booted cleanly.

CorruptPreferenceStoreException now carries reopenSafe and a derived,
content-free shape: byte length, whether it decoded, whether every byte is
zero. #1732 arrived as "FormatException at offset 0" and nothing else, which
cannot separate an all-zero file from a non-JSON first character from bytes
that are not UTF-8; these can, and never quote the document.

Cover the loop against the real desktop backend rather than a fake.
shared_preferences_linux is pure Dart, byte-identical to the Windows
implementation, and exposes fs/pathProvider, so pointing it at a temp
directory exercises the genuine read, parse, cache and write path on any host
— the join between preflight, classification and reopen where every one of
these defects lived, and which had no coverage at all.
2026-08-01 06:59:20 +02:00
edde746 0c6dab01b3 build(deps): upgrade Flutter package dependencies 2026-07-24 03:56:40 +02:00
edde746 b41fb4fe75 fix(ui): harden settings focus and semantics 2026-07-24 03:46:50 +02:00
edde746 658da37b48 build: make toolchain inputs reproducible 2026-07-24 03:40:06 +02:00
edde746 991aee6c90 fix(ci): resolve analyzer and settings test failures 2026-07-14 01:21:38 +02:00
edde746 a0013323f2 chore(ui): enforce icon consistency 2026-07-13 23:13:53 +02:00
edde746 780a1ea180 fix(ci): restore analyzer and unit checks 2026-07-12 19:55:24 +02:00
edde746 858952929b fix(deps): refresh and document native dependencies 2026-07-12 17:31:16 +02:00
edde746 94852e49cb fix(ios): stop reclaiming audio session while paused in PiP
close #1496

Bumps os_media_controls so the audio session is only activated on
playing-state pushes; paused/metadata pushes no longer re-interrupt
other apps' audio (which caused the 1-2 Hz play/pause loop).
2026-07-07 08:54:31 +02:00
edde746andClaude Fable 5 04c0714881 chore: bump os_media_controls for lock-screen artwork fixes
Pulls edde746/media_controls@71b96f8: iOS/macOS guard async artwork
downloads against track changes (slow art no longer lands on the next
track) and clear stale artwork when a track has none.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:33:21 +02:00
edde746 db18ee4b34 feat(music): android background playback via media_controls foreground service
Bumps os_media_controls to 4f4b28f3: MediaStyle foreground service with
a JUnit-tested promote/demote/stop policy, artwork URL download (also
fixes video lock-screen art), and task-removal teardown that can't leak
orphan notifications. The music service opts into background mode per
session and requests POST_NOTIFICATIONS before first playback.
2026-07-05 21:52:58 +02:00
edde746 97901cba87 chore(deps): bump background_downloader 2026-07-05 14:29:57 +02:00
edde746 7f81a0d4fd fix(windows): retry connectivity listener registration outside input-sync calls
INetworkListManagerEvents Advise fails with
RPC_E_CANTCALLOUT_ININPUTSYNCCALL when the platform thread is inside an
input-synchronous call at subscribe time, and an OnListen error can
only surface in Dart as an uncatchable fatal FlutterError. The fork now
retries the registration via a message-loop timer and reports only
persistent failures through the event sink, where stream onError
handlers observe them.
2026-07-05 14:16:05 +02:00
edde746 173117602a fix(android): vendor saf_util with a sound pendingResult lifecycle
saf_util 2.0.0 leaves an already-answered MethodChannel.Result behind
in pendingResult: unrelated activity request codes consume (and answer)
the pending picker, and a failed picker launch replies without clearing
the pending state. The next delivery for request 1001 then replies a
second time — IllegalStateException "Reply already submitted" in
MainActivity.onActivityResult, crashing the process.

Vendored at packages/saf_util (BSD-3) with take-and-clear reply
ownership, request codes scoped to the plugin's own pickers, and a
guarded reply on the teardown race.
2026-07-05 14:16:05 +02:00
edde746 27596bb919 fix(windows): enable WinHTTP IPv6 fast fallback
Dual-stack hostnames with unreachable IPv6 stalled past the endpoint
probe budget because WinHTTP tries addresses sequentially. Enable
WINHTTP_OPTION_IPV6_FAST_FALLBACK (Happy Eyeballs) via win_http 0.2.2
so IPv4 is raced after the IPv6 attempt, matching curl/Cronet/NSURLSession.

close #1128
2026-07-04 21:56:41 +02:00
edde746 f74314bbf0 fix(livetv): debounce rapid time-shift skips to stop overshoot 2026-06-06 10:48:51 +02:00
edde746 92bfae322e fix(tvos): handle play pause remote
close #1230
2026-06-03 14:19:23 +02:00
edde746 5e5702c961 fix: resume playback after iOS audio route changes
close #1185
2026-05-29 11:04:27 +02:00
edde746 a101ee76d6 fix(downloads): prevent duplicate native tasks
close #1141
2026-05-26 22:43:05 +02:00
edde746 cec8993b80 fix: update win_http 2026-05-25 23:19:59 +02:00
edde746 11b97e626e fix(gamepad): update reconnect dependency 2026-05-21 08:41:03 +02:00
edde746 423be62218 fix(tv): pause gamepad during native text input 2026-05-19 12:10:49 +02:00
edde746 e98d7febdd chore: upgrade Flutter to 3.44 2026-05-18 22:52:51 +02:00
edde746 d3e8dcef86 fix(windows): pin patched connectivity_plus 2026-05-18 19:05:08 +02:00
edde746 be0e9686b5 fix(search): rank media search results
close #1056
2026-05-16 21:40:22 +02:00
edde746 3e74937386 feat(trackers): sync anime watch state by episode 2026-05-16 20:54:39 +02:00
edde746 7b4a37e576 fix(downloads): clean orphaned temp files
close #1040
2026-05-14 09:05:06 +02:00
edde746 86c19fc1fc fix(downloads): pin downloader cleanup
close #1026
close #921
2026-05-12 19:10:35 +02:00
edde746 5139e39ed4 fix(sentry): upload symbols with dart plugin 2026-05-12 06:40:54 +02:00
edde746 f9615491b3 refactor(media): migrate media item to freezed 2026-05-11 07:01:46 +02:00
edde746 ffc20b26d4 fix(player): block tv background media resume
close #990
2026-05-09 14:37:00 +02:00
edde746 3a612df3d3 chore: remove in-app review 2026-05-09 11:47:37 +02:00
edde746 c4b9da0950 refactor(images): replace flutter cache manager 2026-05-03 20:09:23 +02:00
edde746 e9a706fa72 fix(tvos): enable wakelock plugin 2026-05-03 01:26:12 +02:00
edde746 31d2d9dc98 feat: jellyfin 2026-05-01 01:20:36 +02:00
edde746 f049c65623 test: provider/mixin/service tests + visibleForTesting reset hooks 2026-04-25 13:15:01 +02:00
edde746 edff1f29db test: scaffolding + first provider test (hidden_libraries) 2026-04-25 12:56:38 +02:00
edde746 1de91a598c refactor(tvos): cleanup 2026-04-24 20:21:30 +02:00
edde746 8380980abd fix: update background_downloader fork ref 2026-04-15 00:32:18 +02:00
edde746 6eb5c847af fix: download cleanup, retry circuit breaker, SAF resume 2026-04-14 21:54:00 +02:00
edde746 d093023ce7 feat: replace QR remote pairing with encrypted LAN discovery
Replace session ID + PIN auth with HKDF-derived home secret and
per-session AES-256-GCM encrypted channel. Auto-start remote server
on host devices, auto-discover hosts via authenticated UDP broadcast.
2026-04-14 12:33:40 +02:00
edde746 0b20300572 fix: noisy audio pause on android tv 2026-04-10 10:24:19 +02:00
edde746 e7ae108c49 chore: bump win_http to 0.2.0 2026-04-08 04:24:06 +02:00
edde746 87c308f6ca feat: use win_http on Windows 2026-04-08 01:54:40 +02:00
edde746 15b22f75a8 refactor: replace dio with package:http 2026-04-08 00:43:08 +02:00
edde746 52b8944d71 feat: use HTTP/2 for all network requests 2026-04-06 22:53:13 +02:00
edde746 cef8f5efe2 fix: use sentry-dart fork to fix Windows build 2026-04-06 22:31:37 +02:00
edde746 36d63860e2 chore: update pubspec.lock 2026-03-27 09:39:50 +01:00
edde746 b4b90eec63 fix: use forked material_symbols_icons for IconData compat 2026-03-26 08:32:16 +01:00