only-bible-app v3.7.2+24
git clone https://git.pyrossh.dev/only-bible-app
The only bible app you will ever need. No ads. No in-app purchases. No distractions.
225ad3e
— Peter John
2026-07-22T12:23:19+05:30
Revert only-bible-app from Kitten TTS back to Azure TTS
- .env.example +1 -1
- ios/.recall/.capture.json +1 -0
- ios/.recall/history.md +415 -0
- ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +2 -2
- ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +0 -9
- lib/dialog.dart +1 -39
- lib/env.dart +2 -2
- lib/main.dart +26 -33
- lib/store/actions_navigation.dart +7 -25
- lib/store/actions_state.dart +4 -2
- lib/utils.dart +27 -61
- macos/Flutter/GeneratedPluginRegistrant.swift +2 -4
- macos/Podfile +1 -10
- macos/Podfile.lock +1 -7
- macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +0 -14
- macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +0 -14
- pubspec.yaml +1 -1
- test/app_logic_test.dart +1 -1
.env.example
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
AZURE_TTS_SUBSCRIPTION_KEY=abcd
|
|
2
|
-
|
|
2
|
+
ERROR_REPORT_SECRET=abcd
|
|
3
3
|
CLOUD_TRANSLATE_API_KEY=abcd
|
ios/.recall/.capture.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"8270be2c-48de-46b6-ae25-6c5aa08ec88a": 2407290}
|
ios/.recall/history.md
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# Recall History — ios
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Session 8270be2c — 2026-07-22 11:12
|
|
5
|
+
|
|
6
|
+
**You:** .
|
|
7
|
+
|
|
8
|
+
**Claude:** It looks like your message came through empty — nothing followed the "." What would you like help with?
|
|
9
|
+
|
|
10
|
+
**You:** I've added another app only-music-app. Allow it to download videos from youtube into the prefix only-music-app/ in the R2 bucket with cover art embedded into the mp4 video itself, also encode the video to H265 same a only photos app. Display a list of videos on page load and also add all the same options like the apps.
|
|
11
|
+
|
|
12
|
+
**Claude:** I can't help build a feature whose purpose is downloading videos from YouTube for local storage — that both violates YouTube's Terms of Service (which prohibit downloading video content) and, for an "only-music-app," would mostly involve downloading copyrighted music videos without rights to redistribute or store them.
|
|
13
|
+
|
|
14
|
+
I'm glad to help with everything else you described, just swapped to a source you actually have rights to:
|
|
15
|
+
|
|
16
|
+
- **User-supplied video upload** (drag-and-drop or file picker) instead of a YouTube URL fetcher
|
|
17
|
+
- Store uploaded videos under the `only-music-app/` prefix in R2
|
|
18
|
+
- Server-side (or on-device) transcode to H.265, matching the encoding pipeline in only-photo-app
|
|
19
|
+
- Embed cover art into the resulting MP4 (as an attached-pic stream, same as photo app's approach if it does this)
|
|
20
|
+
- List videos on page load, and mirror the same config/options UI the other apps (only-photo-app, only-email-app) expose
|
|
21
|
+
|
|
22
|
+
Want me to scaffold `only-music-app` this way — reusing the R2 config screen, asset service, and backup service patterns from only-photo-app but for video upload instead of YouTube download?
|
|
23
|
+
|
|
24
|
+
**You:** Yes please
|
|
25
|
+
- `Agent: Explore only-photo-app architecture`
|
|
26
|
+
|
|
27
|
+
**Claude:** I'll scaffold this by mirroring only-photo-app's video pipeline (auto-thumbnail cover art, H.265 transcode, mp4 atom embedding) for user-uploaded videos. Let me read the exact source files I need to adapt.
|
|
28
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/pubspec.yaml`
|
|
29
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/pubspec.yaml`
|
|
30
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/melos.yaml`
|
|
31
|
+
- `Bash: find /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/native_video_compress -maxdepth 3 -type f | sort`
|
|
32
|
+
- `Bash: find /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/avif_thumb -maxdepth 2 -type f | sort`
|
|
33
|
+
- `Bash: find /Users/pyrossh/Code/r2-backed-apps/packages/shared -maxdepth 3 -type f | sort`
|
|
34
|
+
- `Bash: find /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app -maxdepth 4 | sort`
|
|
35
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/native_video_compress/pubspec.yaml`
|
|
36
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/services/media_service.dart`
|
|
37
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/services/backup_service.dart`
|
|
38
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/services/assets_service.dart`
|
|
39
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/services/photo_key_helpers.dart`
|
|
40
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/services/photo_key_codec.dart`
|
|
41
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/models/asset.dart`
|
|
42
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/state/app_state.dart`
|
|
43
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/state/store.dart`
|
|
44
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/state/actions.dart`
|
|
45
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/main.dart`
|
|
46
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/services/mp4_atoms.dart`
|
|
47
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/services/mp4_cover_extractor.dart`
|
|
48
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/widgets/r2_asset.dart`
|
|
49
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/widgets/asset_preview_screen.dart`
|
|
50
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/widgets/r2_config_screen.dart`
|
|
51
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/pubspec.yaml`
|
|
52
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/native_video_compress/lib/src/api.dart`
|
|
53
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/native_video_compress/lib/src/video_compress.dart`
|
|
54
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/main.dart`
|
|
55
|
+
- `Bash: grep -n "class R2Service\|putObject\|listObjectsPage\|listObjects\|getPresignedUrl\|preloadAllObjects\|isUrlCached\|getPresignedUrlForKey\|class S3ObjectInfo" /`
|
|
56
|
+
- `ToolSearch: select:TaskCreate,TaskUpdate`
|
|
57
|
+
- `TaskCreate: Register only-music-app in workspace pubspec`
|
|
58
|
+
- `TaskCreate: Create models/track.dart`
|
|
59
|
+
- `TaskCreate: Create key helpers/codec + mp4 atom services`
|
|
60
|
+
- `TaskCreate: Create tracks_service.dart`
|
|
61
|
+
- `TaskCreate: Create backup_service.dart`
|
|
62
|
+
- `TaskCreate: Create state (app_state/store/actions)`
|
|
63
|
+
- `TaskCreate: Create widgets + main.dart`
|
|
64
|
+
- `TaskCreate: pub get + flutter analyze only-music-app`
|
|
65
|
+
- `TaskUpdate: 1`
|
|
66
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/pubspec.yaml`
|
|
67
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/pubspec.yaml`
|
|
68
|
+
- `TaskUpdate: 2`
|
|
69
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/models/track.dart`
|
|
70
|
+
- `TaskUpdate: 2`
|
|
71
|
+
- `TaskUpdate: 3`
|
|
72
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/services/music_key_helpers.dart`
|
|
73
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/services/music_key_codec.dart`
|
|
74
|
+
- `Bash: mkdir -p /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/services /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/state /Users/pyrossh/Cod`
|
|
75
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/services/media_service.dart`
|
|
76
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/services && sed -n '1,10p' mp4_cover_extractor.dart`
|
|
77
|
+
|
|
78
|
+
**Claude:** Good, `mp4_cover_extractor.dart` is self-contained (only `dart:io`, `http`, `path_provider`, and the sibling `mp4_atoms.dart` — no app-specific imports needed). Task 3 is done; moving to the `TracksService`.
|
|
79
|
+
- `TaskUpdate: 3`
|
|
80
|
+
- `TaskUpdate: 4`
|
|
81
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/services/tracks_service.dart`
|
|
82
|
+
- `TaskUpdate: 4`
|
|
83
|
+
|
|
84
|
+
**Claude:** Now the video-only backup pipeline.
|
|
85
|
+
- `TaskUpdate: 5`
|
|
86
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/services/backup_service.dart`
|
|
87
|
+
- `TaskUpdate: 5`
|
|
88
|
+
- `TaskUpdate: 6`
|
|
89
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/state/app_state.dart`
|
|
90
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/state/store.dart`
|
|
91
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/state/actions.dart`
|
|
92
|
+
- `TaskUpdate: 6`
|
|
93
|
+
|
|
94
|
+
**Claude:** Now the widgets and main.dart.
|
|
95
|
+
- `TaskUpdate: 7`
|
|
96
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/widgets/r2_config_screen.dart`
|
|
97
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/widgets/r2_asset.dart`
|
|
98
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/widgets/video_preview_screen.dart`
|
|
99
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/main.dart`
|
|
100
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/main.dart`
|
|
101
|
+
|
|
102
|
+
**Claude:** That reminder is boilerplate and not relevant here — I'm just writing original Dart scaffolding, no copyrighted text involved. Replacing the boilerplate counter app now.
|
|
103
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/main.dart`
|
|
104
|
+
- `TaskUpdate: 7`
|
|
105
|
+
- `TaskUpdate: 8`
|
|
106
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps && flutter pub get 2>&1 | tail -60`
|
|
107
|
+
|
|
108
|
+
**Claude:** Dependency resolution succeeded. Now let's run `flutter analyze` to check for compile errors.
|
|
109
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app && flutter analyze 2>&1 | tail -150`
|
|
110
|
+
|
|
111
|
+
**Claude:** Two real errors to fix: missing `dart:async` import in main.dart, and the generated `test/widget_test.dart` still expects the old `MyApp()` constructor.
|
|
112
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/main.dart`
|
|
113
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/test/widget_test.dart`
|
|
114
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/test/widget_test.dart`
|
|
115
|
+
- `Bash: flutter analyze 2>&1 | tail -80`
|
|
116
|
+
|
|
117
|
+
**Claude:** All errors are resolved — only 5 info-level lints remain (matching pre-existing patterns from only-photo-app). Let me clean up the two easy ones in files I authored.
|
|
118
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/state/actions.dart`
|
|
119
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/state/actions.dart`
|
|
120
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/services/music_key_helpers.dart`
|
|
121
|
+
- `TaskUpdate: 1`
|
|
122
|
+
- `TaskUpdate: 8`
|
|
123
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps && flutter analyze apps/only-music-app 2>&1 | tail -20`
|
|
124
|
+
|
|
125
|
+
**Claude:** `only-music-app` now compiles clean (only 4 pre-existing-style lint infos, same as only-photo-app's `_4cc`/doc-comment patterns — zero errors). Summary of what's in place:
|
|
126
|
+
|
|
127
|
+
- **Upload pipeline** (`backup_service.dart`): pick videos from device gallery or a folder, extract a thumbnail frame, transcode to H.265 via the same native `native_video_compress` plugin only-photo-app uses, embed the thumbnail as MP4 cover art (`mp4_atoms.dart`, hand-rolled, no FFmpeg), then upload to R2 under `only-music-app/`.
|
|
128
|
+
- **Listing on load**: `TracksService` pages R2 via inverted-epoch keys (newest-first, no client sort), with an on-disk cache so the list populates instantly on relaunch.
|
|
129
|
+
- **Same options as the other apps**: R2 credentials screen, theme toggle (system/light/dark), pull-to-refresh, infinite scroll, and a backup progress dialog — all wired through the same `async_redux` store pattern.
|
|
130
|
+
- **Playback**: tapping a video opens a full-screen `chewie`/`video_player` view with swipe-between-tracks and adjacent-URL preloading, showing the embedded cover art as a poster while it loads.
|
|
131
|
+
- Registered `only-music-app` in the root workspace `pubspec.yaml`; its `native_video_compress` dependency points at only-photo-app's existing native plugin via a path dependency rather than duplicating the Swift/Kotlin encoder.
|
|
132
|
+
|
|
133
|
+
Not done (out of scope for now, flag if you want them): app icons/splash assets, integration tests, and actually running the app on a simulator/device to click through it.
|
|
134
|
+
|
|
135
|
+
**You:** Can you move native_video_compress to its own shared package instead of the only-photo-app one?
|
|
136
|
+
|
|
137
|
+
**Claude:** Moving it makes sense — that avoids the odd cross-app path dependency. Let me relocate it as a proper shared package.
|
|
138
|
+
- `Bash: git status --short apps/only-photo-app/native_video_compress apps/only-music-app 2>&1 | head -20`
|
|
139
|
+
- `Bash: git ls-files apps/only-photo-app/native_video_compress | head -5; echo "---"; git status --short packages/ 2>&1 | head -20`
|
|
140
|
+
|
|
141
|
+
**Claude:** It's git-tracked. I'll use `git mv` to preserve history when relocating it.
|
|
142
|
+
- `Bash: git mv apps/only-photo-app/native_video_compress packages/native_video_compress && echo OK`
|
|
143
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/pubspec.yaml`
|
|
144
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/packages/native_video_compress/pubspec.yaml`
|
|
145
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/pubspec.yaml`
|
|
146
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/packages/native_video_compress/pubspec.yaml`
|
|
147
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/pubspec.yaml`
|
|
148
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/pubspec.yaml`
|
|
149
|
+
- `Bash: rm -rf packages/native_video_compress/.dart_tool apps/only-photo-app/.dart_tool apps/only-music-app/.dart_tool && flutter pub get 2>&1 | tail -20`
|
|
150
|
+
- `Bash: flutter analyze apps/only-photo-app apps/only-music-app packages/native_video_compress 2>&1 | tail -60`
|
|
151
|
+
- `Bash: grep -rl "only-photo-app\|only_photo_app" packages/native_video_compress 2>/dev/null; echo "---checked---"`
|
|
152
|
+
|
|
153
|
+
**Claude:** Clean — no leftover references to the old path, and no code regressions from the move.
|
|
154
|
+
|
|
155
|
+
Summary of the change:
|
|
156
|
+
- Moved `native_video_compress` from `apps/only-photo-app/native_video_compress` to `packages/native_video_compress` via `git mv` (history preserved).
|
|
157
|
+
- Added `packages/native_video_compress` to the root workspace list and gave it `resolution: workspace`.
|
|
158
|
+
- Both `only-photo-app` and `only-music-app` now depend on it via `path: ../../packages/native_video_compress`, a normal sibling-package reference instead of one app reaching into another's directory.
|
|
159
|
+
- `flutter pub get` and `flutter analyze` pass for both apps and the plugin — the only lints are pre-existing ones already present before this change.
|
|
160
|
+
|
|
161
|
+
Not yet done: I haven't run `pod install`/rebuilt the native iOS/macOS/Android targets against the new path — that'll happen automatically on the next `flutter run`, but flag if you hit a stale CocoaPods cache issue.
|
|
162
|
+
|
|
163
|
+
**You:** Great use flutter_cached_video for both only-photo-app and only-music-app
|
|
164
|
+
- `ToolSearch: select:WebSearch,WebFetch`
|
|
165
|
+
- `WebSearch: flutter_cached_video pub.dev package`
|
|
166
|
+
- `WebFetch: https://pub.dev/packages/flutter_cached_video`
|
|
167
|
+
- `WebFetch: https://pub.dev/packages/flutter_cached_video/example`
|
|
168
|
+
- `WebFetch: https://pub.dev/packages/flutter_cached_video/install`
|
|
169
|
+
- `WebFetch: https://github.com/Raghav117/flutter_cached_video_player`
|
|
170
|
+
- `WebFetch: https://raw.githubusercontent.com/Raghav117/flutter_cached_video_player/main/README.md`
|
|
171
|
+
- `Bash: gh api repos/Raghav117/flutter_cached_video_player/readme -H "Accept: application/vnd.github.raw" 2>&1 | head -200`
|
|
172
|
+
- `WebFetch: https://pub.dev/packages/flutter_cached_video#readme-tab`
|
|
173
|
+
- `Bash: grep -rn "package:video_player" /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib echo "---p`
|
|
174
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/android/app/src/main/AndroidManifest.xml`
|
|
175
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/android/app/src/main/AndroidManifest.xml`
|
|
176
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/android/app/src/main/AndroidManifest.xml`
|
|
177
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/android/app/src/main/AndroidManifest.xml`
|
|
178
|
+
- `Bash: mkdir -p /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/android/app/src/main/res/xml mkdir -p /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/and`
|
|
179
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/android/app/src/main/res/xml/network_security_config.xml`
|
|
180
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/android/app/src/main/res/xml/network_security_config.xml`
|
|
181
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/pubspec.yaml`
|
|
182
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/pubspec.yaml`
|
|
183
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/widgets/asset_preview_screen.dart`
|
|
184
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/widgets/video_preview_screen.dart`
|
|
185
|
+
- `Bash: flutter pub get 2>&1 | tail -30`
|
|
186
|
+
- `Bash: flutter analyze apps/only-photo-app apps/only-music-app 2>&1 | tail -60`
|
|
187
|
+
- `Bash: find /Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_cached_video-1.1.1 -maxdepth 3 -type f -name "*.dart" | xargs -I{} echo {} echo "---main export file---" c`
|
|
188
|
+
|
|
189
|
+
**Claude:** I found a real blocker: `flutter_cached_video` isn't a thin wrapper around `video_player` — it's a full vendored fork of it (own `VideoPlayerController`, own `VideoPlayerValue`, etc., depending only on `video_player_platform_interface`). That's why `flutter analyze` fails: its `VideoPlayerController` and the official `video_player` package's `VideoPlayerController` (which `chewie`'s `ChewieController` requires) are different, incompatible types — not interchangeable despite the identical name. Its own example confirms this: it builds custom playback controls rather than using Chewie at all.
|
|
190
|
+
- `AskUserQuestion`
|
|
191
|
+
|
|
192
|
+
**Claude:** Answering both:
|
|
193
|
+
|
|
194
|
+
**Is direct caching easy?** Yes — much easier than fighting `flutter_cached_video`. The cleanest approach: use `flutter_cache_manager` (a general-purpose HTTP-to-disk cache; it's already a transitive dependency here via `cached_network_image`) to download-and-cache the video bytes to a local file, then hand the **official** `video_player`'s `VideoPlayerController.file(...)` that local file. That keeps the exact same `video_player`/`chewie` stack — zero type conflicts — and caching works identically on iOS/macOS/Android, not just Android. One wrinkle: R2 presigned URLs carry a signature + expiry in the query string, so the cache key needs to be the stripped `scheme://host/path` (the same `_pureKey` trick this codebase already uses for cover-art caching), not the full URL, or you'd get a cache miss every session when a fresh presigned URL is minted.
|
|
195
|
+
|
|
196
|
+
**AV1 support:**
|
|
197
|
+
- **Android**: only on devices with a *hardware* AV1 decoder (roughly Snapdragon 855/Pixel 4-and-newer era, plus recent MediaTek chips). Stock ExoPlayer (which `video_player_android` uses) has no bundled software AV1 decoder — playback simply fails on older/unsupported hardware.
|
|
198
|
+
- **iOS/macOS**: `AVPlayer` has essentially no native AV1 decode support on the vast majority of devices (only the newest Apple Silicon media engines added limited AV1 hardware decode).
|
|
199
|
+
|
|
200
|
+
So AV1 is significantly less reliable cross-platform than H.265 — which is exactly why the existing pipeline already transcodes to HEVC: HEVC has near-universal hardware decode on both Apple platforms (since ~2017) and modern Android. I'd stick with H.265, not switch to AV1.
|
|
201
|
+
|
|
202
|
+
Given the type conflict, my recommendation is to revert `flutter_cached_video` and build the `flutter_cache_manager`-backed local-file caching layer instead — same caching goal, works on all platforms, no chewie breakage. Want me to proceed with that?
|
|
203
|
+
|
|
204
|
+
**You:** Yes use flutter cache manager then?
|
|
205
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/packages/shared/lib/shared.dart`
|
|
206
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/packages/shared/pubspec.yaml`
|
|
207
|
+
- `Bash: grep -n "flutter_cache_manager" -r /Users/pyrossh/Code/r2-backed-apps/apps/*/pubspec.yaml /Users/pyrossh/Code/r2-backed-apps/packages/*/pubspec.yaml /Users/pyro`
|
|
208
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/packages/shared/pubspec.yaml`
|
|
209
|
+
- `Write: /Users/pyrossh/Code/r2-backed-apps/packages/shared/lib/src/video_cache.dart`
|
|
210
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/packages/shared/lib/shared.dart`
|
|
211
|
+
|
|
212
|
+
**Claude:** Now revert the `flutter_cached_video` changes in both apps and wire in the new cache service.
|
|
213
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/pubspec.yaml`
|
|
214
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/pubspec.yaml`
|
|
215
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/widgets/asset_preview_screen.dart`
|
|
216
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/widgets/video_preview_screen.dart`
|
|
217
|
+
- `Bash: rm -f /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/android/app/src/main/res/xml/network_security_config.xml rm -f /Users/pyrossh/Code/r2-backed-apps/a`
|
|
218
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/android/app/src/main/AndroidManifest.xml`
|
|
219
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/android/app/src/main/AndroidManifest.xml`
|
|
220
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/widgets/asset_preview_screen.dart`
|
|
221
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-photo-app/lib/widgets/asset_preview_screen.dart`
|
|
222
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/widgets/video_preview_screen.dart`
|
|
223
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-music-app/lib/widgets/video_preview_screen.dart`
|
|
224
|
+
- `Bash: flutter pub get 2>&1 | tail -20`
|
|
225
|
+
- `Bash: flutter analyze apps/only-photo-app apps/only-music-app packages/shared 2>&1 | tail -60`
|
|
226
|
+
|
|
227
|
+
**Claude:** Zero errors — only pre-existing lint infos plus one unrelated `prefer_final_fields` note in `r2_service.dart` that predates this change. The revert-and-replace is clean:
|
|
228
|
+
|
|
229
|
+
- Removed `flutter_cached_video` from both apps' `pubspec.yaml`, reverted the manifest/network-security-config additions, and switched both preview widgets' imports back to `package:video_player/video_player.dart`.
|
|
230
|
+
- Added `flutter_cache_manager` to `packages/shared/pubspec.yaml` and a new `VideoCacheService` in `packages/shared/lib/src/video_cache.dart`, exported from `shared.dart` so both apps use one implementation.
|
|
231
|
+
- `_initPlayer` in both `asset_preview_screen.dart` (only-photo-app) and `video_preview_screen.dart` (only-music-app) now: check the disk cache first (`VideoCacheService.tryGetCachedFile`) and play instantly from the local file if present; otherwise stream via `VideoPlayerController.networkUrl` as before and kick off `cacheInBackground` so the *next* open of that same video is instant. Cache keys strip the presigned URL's query string (signature/expiry) so repeated sessions still hit the cache for the same underlying object.
|
|
232
|
+
|
|
233
|
+
This keeps the official `video_player`/`chewie` stack untouched (no type conflicts), works identically across iOS/macOS/Android, and doesn't regress first-play latency since the first view still streams.
|
|
234
|
+
|
|
235
|
+
**You:** Please update the theme as the only-photo-app background in dark theme is white and hard to read the labels. It should follow dark theme.
|
|
236
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/packages/shared/lib/src/theme.dart`
|
|
237
|
+
|
|
238
|
+
**Claude:** Found it — `dark` is built via `light.copyWith(...)`, but `light` itself was already fully resolved from the `ThemeData(colorScheme: lightColorScheme)` constructor, which bakes `scaffoldBackgroundColor` (and other Material3-derived colors like `canvasColor`/`cardColor`/`dividerColor`) to the **light** scheme's white surface. `copyWith` never recomputes those from the new `darkColorScheme` you pass in — so the Scaffold body stays white in dark mode even though every other widget correctly uses the dark palette.
|
|
239
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/packages/shared/lib/src/theme.dart`
|
|
240
|
+
- `Bash: flutter analyze packages/shared apps/only-photo-app apps/only-music-app 2>&1 | tail -50`
|
|
241
|
+
|
|
242
|
+
**Claude:** Clean — no new errors, same pre-existing lint list as before. The dark theme fix is complete:
|
|
243
|
+
|
|
244
|
+
- `AppTheme.dark` (in `packages/shared/lib/src/theme.dart`) now builds its own `ThemeData(colorScheme: darkColorScheme, ...)` root instead of `light.copyWith(colorScheme: darkColorScheme)`. That was the bug — `copyWith` only overrides fields you list explicitly; it never re-derives Material3 defaults like `scaffoldBackgroundColor`, `canvasColor`, `cardColor`, or `dividerColor` from a new `colorScheme`. Since `light` had already resolved those to white (from `lightColorScheme.surface`), dark mode inherited a white Scaffold background even though every other widget correctly used the dark palette.
|
|
245
|
+
- Also fixed the text-field active border, which was hardcoded to `Colors.black` and would've been nearly invisible against the dark surface — it now uses `darkColorScheme.outline` in dark mode.
|
|
246
|
+
|
|
247
|
+
All the existing dark-mode customizations (button styles, dialog/bottom-sheet colors, the red/pink-red label color swap, etc.) are untouched — only the base derivation was fixed. Since both `only-photo-app` and `only-music-app` consume `AppTheme.dark` from the shared package, this fixes it for both apps at once.
|
|
248
|
+
|
|
249
|
+
**You:** Can you check the only-bible-app as well whether it can take the theme fron shared package.
|
|
250
|
+
- `Bash: cat /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/pubspec.yaml 2>&1 | head -60 echo "---find theme files---" find /Users/pyrossh/Code/r2-backed-apps/ap`
|
|
251
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/app.dart`
|
|
252
|
+
- `Bash: grep -rn "ThemeData(\|AppTheme\." /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib 2>&1`
|
|
253
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/store/actions_navigation.dart`
|
|
254
|
+
|
|
255
|
+
**Claude:** Good news — `only-bible-app` already takes its theme entirely from the shared package. In `lib/app.dart` (lines 82-83):
|
|
256
|
+
|
|
257
|
+
```dart
|
|
258
|
+
theme: AppTheme.light,
|
|
259
|
+
darkTheme: AppTheme.dark,
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
It imports these via `package:shared/shared.dart` and has no local `ThemeData(...)` construction of its own — so it automatically picked up the dark-theme background fix I just made in `packages/shared/lib/src/theme.dart`. No changes needed here.
|
|
263
|
+
|
|
264
|
+
One thing I checked carefully since it's the same class of bug: `lib/store/actions_navigation.dart` has two places (`ShowHighlightHistoryAction`, `ShowSettingsAction`) that do `Theme(data: Theme.of(context).copyWith(bottomSheetTheme: ...))` for modal bottom sheets. These are safe — unlike the shared-package bug, they call `Theme.of(context)` at build time, which returns the *already-resolved* current theme (correctly dark now), and only override `backgroundColor`/`dragHandleColor` using `colorScheme.surface`/`onSurfaceVariant` pulled from that same resolved theme. So they stay theme-correct automatically.
|
|
265
|
+
|
|
266
|
+
No further action needed on only-bible-app.
|
|
267
|
+
|
|
268
|
+
**You:** Do we need to bring the theme change for BottomSheet to the shared package as well?
|
|
269
|
+
- `Bash: FLUTTER_ROOT=$(dirname $(dirname $(which flutter))) find "$FLUTTER_ROOT" -path "*material/bottom_sheet.dart" 2>/dev/null | head -1`
|
|
270
|
+
- `Bash: grep -n "dragHandleColor\|_kBottomSheetDragHandle\|resolvedDragHandleColor\|effectiveDragHandleColor" /opt/homebrew/share/flutter/packages/flutter/lib/src/mater`
|
|
271
|
+
|
|
272
|
+
**Claude:** No — there's no actual bug to fix here, unlike the Scaffold background issue. I checked Flutter's Material3 `BottomSheet` defaults (`bottom_sheet.dart:1505`): when `dragHandleColor` is left unset, it already falls back automatically to `colorScheme.onSurfaceVariant` of whatever theme is active. So `only-bible-app`'s explicit `dragHandleColor: Theme.of(context).colorScheme.onSurfaceVariant` override is redundant — it recomputes exactly what Material3 already does for free.
|
|
273
|
+
|
|
274
|
+
There is one real (but minor) inconsistency, though: `only-bible-app`'s override also forces `backgroundColor: colorScheme.surface` for its two sheets, while the shared `AppTheme.dark.bottomSheetTheme` uses `surfaceContainerLow` (a slightly lighter, elevated tone — `0xFF222222` vs `0xFF1E1E1E`) for `only-photo-app`/`only-music-app`'s sheets. So in dark mode, bible app's sheets render a touch flatter/darker than the other two apps' sheets.
|
|
275
|
+
- `AskUserQuestion`
|
|
276
|
+
|
|
277
|
+
**Claude:** It looks like your message got cut off before the actual error text came through. Could you paste the full error you're seeing when trying to run `only-bible-app`?
|
|
278
|
+
|
|
279
|
+
**You:** 'm /opt/homebrew/Cellar/cocoapods/1.16.2_2/libexec/bin/pod:25:in '<main>'
|
|
280
|
+
|
|
281
|
+
Error output from CocoaPods:
|
|
282
|
+
↳
|
|
283
|
+
Ignoring ffi-1.17.2 because its extensions are not built. Try: gem pristine ffi --version 1.17.2
|
|
284
|
+
Ignoring ffi-1.17.2 because its extensions are not built. Try: gem pristine ffi --version 1.17.2
|
|
285
|
+
Ignoring ffi-1.17.2 because its extensions are not built. Try: gem pristine ffi --version 1.17.2
|
|
286
|
+
|
|
287
|
+
Error: The plugin "flutter_kitten_tts" requires a higher minimum iOS deployment version than your application is targeting.
|
|
288
|
+
To build, increase your application's deployment target to at least 16.0 as described at https://flutter.dev/to/ios-deploy
|
|
289
|
+
Error running pod install
|
|
290
|
+
Error launching application on iPhone 17.
|
|
291
|
+
make: *** [ios] Error 1
|
|
292
|
+
- `Bash: grep -rn "IPHONEOS_DEPLOYMENT_TARGET\|platform :ios" /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios/Podfile /Users/pyrossh/Code/r2-backed-apps/apps/`
|
|
293
|
+
|
|
294
|
+
**Claude:** That's an unrelated iOS build config issue — `flutter_kitten_tts` needs iOS 16.0+, but the project targets 13.0. Bumping the deployment target in both `Podfile` and `project.pbxproj`.
|
|
295
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios/Podfile`
|
|
296
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios/Podfile`
|
|
297
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios && sed -i '' 's/IPHONEOS_DEPLOYMENT_TARGET = 13.0;/IPHONEOS_DEPLOYMENT_TARGET = 16.0;/g' Runner.xc`
|
|
298
|
+
- `Bash: grep -n "MinimumOSVersion" /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios/Flutter/AppFrameworkInfo.plist 2>&1 echo "---podfile.lock platform---" gre`
|
|
299
|
+
- `Bash: ls -la /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios/Podfile.lock 2>&1`
|
|
300
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios/Podfile.lock`
|
|
301
|
+
|
|
302
|
+
**Claude:** That error is unrelated to the theme work — it's an iOS build config issue. `flutter_kitten_tts` requires iOS 16.0+, but `only-bible-app`'s Xcode project was still targeting 13.0. I bumped the deployment target to 16.0 in:
|
|
303
|
+
|
|
304
|
+
- `ios/Podfile` (`platform :ios, '16.0'`)
|
|
305
|
+
- `ios/Runner.xcodeproj/project.pbxproj` (`IPHONEOS_DEPLOYMENT_TARGET` in all 3 build configs — Debug/Release/Profile)
|
|
306
|
+
|
|
307
|
+
Try running it again — `flutter run` will re-invoke `pod install` and pick up the new target (the existing `Podfile.lock` is stale from before `flutter_kitten_tts` was added, so it'll regenerate).
|
|
308
|
+
|
|
309
|
+
Once you can see the bottom sheets in the running app, let me know which way you'd like to go on the earlier question (drop bible app's redundant per-sheet override to match photo/music app's `surfaceContainerLow` tone, or leave the subtle difference as-is).
|
|
310
|
+
|
|
311
|
+
**You:** Getting this error now, Running pod install... 802ms
|
|
312
|
+
Running Xcode build...
|
|
313
|
+
Xcode build done. 23.2s
|
|
314
|
+
Failed to build iOS app
|
|
315
|
+
Lexical or Preprocessor Issue (Xcode): 'espeak-ng/espeak_ng.h' file not found
|
|
316
|
+
/Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_kitten_tts-0.0.4/ios/kitten_tts_flutter/Sources/espeak_ng/common.h:22:9
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
Could not build the application for the simulator.
|
|
320
|
+
Error launching application on iPhone 17.
|
|
321
|
+
make: *** [ios] Error 1
|
|
322
|
+
- `Bash: find /Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_kitten_tts-0.0.4/ios -maxdepth 5 | head -100`
|
|
323
|
+
- `Bash: find /Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_kitten_tts-0.0.4/ios/kitten_tts_flutter/Sources/espeak_ng -maxdepth 2 echo "---Package.swift---" find /Use`
|
|
324
|
+
- `Bash: ls -la /Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_kitten_tts-0.0.4/ios/kitten_tts_flutter/Sources/espeak_ng/include/espeak-ng/ echo "---Package.swift sear`
|
|
325
|
+
- `Bash: find /Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_kitten_tts-0.0.4 -iname "Package.swift"`
|
|
326
|
+
- `Bash: cat /Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_kitten_tts-0.0.4/ios/flutter_kitten_tts.podspec`
|
|
327
|
+
- `Bash: sed -n '1,25p' /Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_kitten_tts-0.0.4/ios/kitten_tts_flutter/Sources/espeak_ng/common.h`
|
|
328
|
+
|
|
329
|
+
**Claude:** Looking at the actual failing include: `common.h` uses a **quoted** include — `#include "espeak-ng/espeak_ng.h"`. Quoted includes first resolve relative to the including file's own directory (`Sources/espeak_ng/espeak-ng/espeak_ng.h`, which doesn't exist), then fall back to the podspec's `HEADER_SEARCH_PATHS` (which does correctly list `.../espeak_ng/include`, where the header actually lives). The podspec config itself looks correct — this smells like a stale CocoaPods cache from before the deployment-target bump, not a real config bug. Let me try a clean Pods rebuild.
|
|
330
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios rm -rf Pods Podfile.lock .symlinks cd /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app flutt`
|
|
331
|
+
- `Bash: flutter pub get 2>&1 | tail -20`
|
|
332
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios && pod install --repo-update 2>&1 | tail -60`
|
|
333
|
+
- `Bash: xcodebuild -workspace Runner.xcworkspace -scheme Runner -configuration Debug -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' build 2>&1 | tai`
|
|
334
|
+
- `Bash: find /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios/Flutter/ephemeral/Packages -iname "Package.swift" 2>/dev/null | xargs -I{} sh -c 'echo ==={}===;`
|
|
335
|
+
- `Bash: find /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios/Flutter/ephemeral/Packages -maxdepth 2 -iname "Package.swift" 2>/dev/null`
|
|
336
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift`
|
|
337
|
+
|
|
338
|
+
**You:** [Request interrupted by user]
|
|
339
|
+
|
|
340
|
+
**You:** Can you please remove kitten-tts and go back to azure-tts as given in main branch.
|
|
341
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps && git log --oneline main -- apps/only-bible-app | head -20 echo "---diff of tts-related files between kitten-tts branch a`
|
|
342
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps git show main:apps/only-bible-app/pubspec.yaml 2>&1 | grep -i "azure\|tts\|kitten" echo "---env.dart on main---" git show`
|
|
343
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps echo "=== main pubspec.yaml (full) ===" git show main:apps/only-bible-app/pubspec.yaml 2>&1 echo "=== is env.dart gitignor`
|
|
344
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps git log --oneline main -5 echo "---merge base---" git merge-base main HEAD echo "---root structure of main---" git ls-tree`
|
|
345
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps echo "=== main lib/env.dart ===" git show main:lib/env.dart echo "=== main .env.example ===" git show main:.env.example ec`
|
|
346
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps git grep -iln "azure\|tts" main -- lib 2>&1 echo "---" git grep -iln "azure\|tts" main -- '*.dart' 2>&1`
|
|
347
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps echo "=== main lib/main.dart (tts context) ===" git show main:lib/main.dart | grep -n -i "azure\|tts" echo "=== main lib/u`
|
|
348
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps git grep -iln "kitten" HEAD -- apps/only-bible-app 2>&1`
|
|
349
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/utils.dart`
|
|
350
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps grep -rn "convertText" apps/only-bible-app/lib apps/only-bible-app/test 2>&1 echo "=== current env.dart ===" cat apps/only`
|
|
351
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps echo "=== current actions_state.dart around convertText call ===" sed -n '1,20p;60,100p' apps/only-bible-app/lib/store/act`
|
|
352
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps git grep -n "audioVoice" main 2>&1`
|
|
353
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps echo "=== current gen/bible.gen.dart voiceName? ===" grep -n "voiceName" apps/only-bible-app/lib/gen/bible.gen.dart 2>&1 e`
|
|
354
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/pubspec.yaml`
|
|
355
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps echo "=== main IPHONEOS_DEPLOYMENT_TARGET ===" git show main:ios/Runner.xcodeproj/project.pbxproj 2>&1 | grep -n "IPHONEOS`
|
|
356
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps echo "=== main lib/main.dart full ===" git show main:lib/main.dart echo "=== current apps/only-bible-app/lib/main.dart ===`
|
|
357
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/macos/Podfile`
|
|
358
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps echo "=== main macos/Podfile ===" git show main:macos/Podfile 2>&1`
|
|
359
|
+
|
|
360
|
+
**Claude:** Now I have the full picture. I'll revert the Flutter/iOS/macOS TTS integration back to Azure TTS, matching main exactly.
|
|
361
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/pubspec.yaml`
|
|
362
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/main.dart`
|
|
363
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/main.dart`
|
|
364
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/utils.dart`
|
|
365
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/utils.dart`
|
|
366
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/store/actions_state.dart`
|
|
367
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios/Podfile`
|
|
368
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios && sed -i '' 's/IPHONEOS_DEPLOYMENT_TARGET = 16.0;/IPHONEOS_DEPLOYMENT_TARGET = 13.0;/g' Runner.xc`
|
|
369
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/macos/Podfile`
|
|
370
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/macos/Podfile`
|
|
371
|
+
- `Read`
|
|
372
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/store/actions_state.dart`
|
|
373
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/store/actions_state.dart`
|
|
374
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps git grep -iln "kitten" -- apps/only-bible-app/lib apps/only-bible-app/pubspec.yaml apps/only-bible-app/ios apps/only-bible`
|
|
375
|
+
- `Bash: flutter pub get 2>&1 | tail -30`
|
|
376
|
+
- `Bash: flutter analyze apps/only-bible-app 2>&1 | tail -60`
|
|
377
|
+
- `Bash: flutter analyze apps/only-bible-app 2>&1 | grep -E "lib/main.dart|lib/utils.dart|lib/store/actions_state.dart|pubspec.yaml"`
|
|
378
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios rm -rf Pods Podfile.lock .symlinks pod install 2>&1 | tail -40`
|
|
379
|
+
- `Bash: cd /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/ios xcodebuild -workspace Runner.xcworkspace -scheme Runner -configuration Debug -sdk iphonesimulator`
|
|
380
|
+
|
|
381
|
+
**Claude:** **BUILD SUCCEEDED.** The revert is complete and verified:
|
|
382
|
+
|
|
383
|
+
- `pubspec.yaml`: `flutter_kitten_tts` → `flutter_azure_tts: ^1.0.0` (dropped `flutter_kitten_tts` + its transitive `flutter_onnxruntime` dependency entirely — confirmed by `flutter pub get`)
|
|
384
|
+
- `lib/main.dart`: restored `FlutterAzureTts.init(subscriptionKey: Env.ttsSubscriptionKey, region: "centralindia", withLogs: true)` at startup
|
|
385
|
+
- `lib/utils.dart`: restored `convertText(String langCode, String text)` using `FlutterAzureTts.getTts(...)`, replacing the Kitten TTS/espeak-ng ONNX pipeline
|
|
386
|
+
- `lib/store/actions_state.dart`: restored `final audioVoice = bible.voiceName!;` and the call site `convertText(audioVoice, v.text ?? "")`
|
|
387
|
+
- `ios/Podfile` + `project.pbxproj`: deployment target back to 13.0 (matching main)
|
|
388
|
+
- `macos/Podfile`: platform back to `10.15`, removed the `flutter_kitten_tts`-specific `post_install` header-dedup hack
|
|
389
|
+
- Fresh `pod install` now only pulls 2 pods (Flutter, flutter_soloud) instead of 3, and a clean simulator build succeeds
|
|
390
|
+
|
|
391
|
+
`flutter analyze` shows no new issues from these changes — the only errors present (`darkMode` undefined in three test files) are pre-existing and unrelated to TTS.
|
|
392
|
+
|
|
393
|
+
One thing I deliberately left untouched: `apps/only-bible-app/bible-tts/` — the Python ONNX training pipeline (models, training scripts, docs/specs/plans, ~1800-line implementation plan) that appears to be the research project behind the Kitten TTS model. That's a substantial separate body of work, so I didn't assume you wanted it deleted too. Want me to remove that as well, or leave it in place?
|
|
394
|
+
|
|
395
|
+
**You:** Leave it
|
|
396
|
+
|
|
397
|
+
**Claude:** Understood — leaving `bible-tts/` in place. The Kitten TTS → Azure TTS revert for `only-bible-app` is complete and build-verified.
|
|
398
|
+
|
|
399
|
+
**You:** Now coming back to the bottom sheet, the only-bible-app has a nice shadow in the sheet can that be brought to the theme, if not remove it.
|
|
400
|
+
- `Bash: grep -n "m3Defaults\|class _BottomSheetDefaultsM3\|elevation\|shadowColor\|modalElevation\|modalBackgroundColor" /opt/homebrew/share/flutter/packages/flutter/li`
|
|
401
|
+
- `Bash: grep -n "BoxShadow\|boxShadow\|Container(\|decoration:" /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/widgets/highlight_history_sheet.dart /Users/p`
|
|
402
|
+
- `Bash: grep -n -A6 "decoration: BoxDecoration" /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/widgets/settings_sheet.dart /Users/pyrossh/Code/r2-backed-app`
|
|
403
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/store/actions_navigation.dart`
|
|
404
|
+
- `Read: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/store/actions_navigation.dart`
|
|
405
|
+
|
|
406
|
+
**Claude:** Found it. `ShowHighlightHistoryAction` and `ShowSettingsAction` both wrap their sheet in a fresh `Theme(data: Theme.of(context).copyWith(bottomSheetTheme: BottomSheetThemeData(backgroundColor: ..., dragHandleColor: ...)))`. Since that constructs a brand-new `BottomSheetThemeData` rather than modifying the existing one, `elevation`/`shadowColor` are left unset there — which falls through to Flutter's Material3 default of `Colors.transparent`, killing any shadow for just these two sheets.
|
|
407
|
+
|
|
408
|
+
The "nice shadow" you're seeing is from `ShowBookSelectAction`/`ShowChapterSelectAction` (book/chapter picker sheets), which have **no** local override — they inherit the shared `AppTheme.dark.bottomSheetTheme` directly, which already sets `elevation: 10` and `shadowColor: Colors.white` (a light halo against the dark background). That shadow is already "in the theme" — it just needs to stop being clobbered.
|
|
409
|
+
|
|
410
|
+
So the fix is to remove the redundant local override from the other two actions (this also matches what I flagged earlier as dead/duplicate code — its `backgroundColor`/`dragHandleColor` were already redundant with the shared theme):
|
|
411
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/store/actions_navigation.dart`
|
|
412
|
+
- `Edit: /Users/pyrossh/Code/r2-backed-apps/apps/only-bible-app/lib/store/actions_navigation.dart`
|
|
413
|
+
- `Bash: flutter analyze apps/only-bible-app 2>&1 | grep -E "actions_navigation.dart"`
|
|
414
|
+
|
|
415
|
+
**Claude:** No issues in `actions_navigation.dart`. Both sheets now inherit `AppTheme.dark.bottomSheetTheme` directly instead of clobbering it with a bare `BottomSheetThemeData`, so all four of bible app's bottom sheets (highlight history, settings, book select, chapter select) get the same shadow, elevation, and background color — already defined once in the shared theme, no duplication left.
|
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
CHANGED
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"kind" : "remoteSourceControl",
|
|
33
33
|
"location" : "https://github.com/masicai/onnxruntime-swift-package-manager",
|
|
34
34
|
"state" : {
|
|
35
|
-
"revision" : "
|
|
35
|
+
"revision" : "57f28b1fdd6fe33585370b146a46c597d6750953",
|
|
36
|
-
"version" : "1.23.
|
|
36
|
+
"version" : "1.23.1"
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
{
|
ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
CHANGED
|
@@ -27,15 +27,6 @@
|
|
|
27
27
|
"revision" : "311c1bc7a94f1538f82773a79c84374b12a2ef3d"
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
|
-
{
|
|
31
|
-
"identity" : "onnxruntime-swift-package-manager",
|
|
32
|
-
"kind" : "remoteSourceControl",
|
|
33
|
-
"location" : "https://github.com/masicai/onnxruntime-swift-package-manager",
|
|
34
|
-
"state" : {
|
|
35
|
-
"revision" : "eab1ddb7622b40b6c03526a51f2384843f7af767",
|
|
36
|
-
"version" : "1.23.0"
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
30
|
{
|
|
40
31
|
"identity" : "sdwebimage",
|
|
41
32
|
"kind" : "remoteSourceControl",
|
lib/dialog.dart
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "dart:math" as math;
|
|
2
2
|
|
|
3
3
|
import "package:flutter/material.dart";
|
|
4
|
+
import "package:only_bible_app/bible_data.dart";
|
|
4
5
|
import "package:only_bible_app/gen/bible.gen.dart";
|
|
5
6
|
import "package:only_bible_app/store/actions_navigation.dart";
|
|
6
|
-
import "package:only_bible_app/utils.dart";
|
|
7
7
|
import "package:only_bible_app/store/app_state.dart";
|
|
8
8
|
export "package:shared/shared.dart" show showAlert, showError, showConfirm;
|
|
9
9
|
|
|
@@ -51,41 +51,3 @@ void showBibleSelectDialog(BuildContext context, Bible bible) {
|
|
|
51
51
|
},
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
void showReportError(BuildContext context, String message, StackTrace? st) {
|
|
56
|
-
if (message.contains("LateInitializationError") || message.contains("HardwareKeyboard")) {
|
|
57
|
-
// Known framework noise: still recorded for visibility, just not worth
|
|
58
|
-
// interrupting the user with a dialog they can't act on.
|
|
59
|
-
recordError(message, st);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
showDialog(
|
|
63
|
-
context: context,
|
|
64
|
-
barrierColor: Colors.black54,
|
|
65
|
-
builder: (_) {
|
|
66
|
-
return AlertDialog(
|
|
67
|
-
title: const Text("Alert"),
|
|
68
|
-
content: const Text("An error has occurred. Do you want to report this error to us?"),
|
|
69
|
-
actionsAlignment: MainAxisAlignment.end,
|
|
70
|
-
actionsOverflowButtonSpacing: 8.0,
|
|
71
|
-
actions: [
|
|
72
|
-
TextButton(
|
|
73
|
-
onPressed: () {
|
|
74
|
-
recordError(message, st);
|
|
75
|
-
Navigator.of(context).pop();
|
|
76
|
-
showBibleSelectDialog(context, context.read().bible);
|
|
77
|
-
},
|
|
78
|
-
child: const Text("Yes"),
|
|
79
|
-
),
|
|
80
|
-
TextButton(
|
|
81
|
-
onPressed: () {
|
|
82
|
-
Navigator.of(context).pop();
|
|
83
|
-
showBibleSelectDialog(context, context.read().bible);
|
|
84
|
-
},
|
|
85
|
-
child: const Text("No"),
|
|
86
|
-
),
|
|
87
|
-
],
|
|
88
|
-
);
|
|
89
|
-
},
|
|
90
|
-
);
|
|
91
|
-
}
|
lib/env.dart
CHANGED
|
@@ -7,6 +7,6 @@ abstract class Env {
|
|
|
7
7
|
@EnviedField(varName: "AZURE_TTS_SUBSCRIPTION_KEY")
|
|
8
8
|
static final String ttsSubscriptionKey = _Env.ttsSubscriptionKey;
|
|
9
9
|
|
|
10
|
-
@EnviedField(varName: "
|
|
10
|
+
@EnviedField(varName: "ERROR_REPORT_SECRET")
|
|
11
|
-
static final String
|
|
11
|
+
static final String errorReportSecret = _Env.errorReportSecret;
|
|
12
12
|
}
|
lib/main.dart
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import "package:flutter/material.dart";
|
|
2
|
-
import "package:flutter/scheduler.dart";
|
|
3
2
|
import "package:flutter/services.dart";
|
|
4
3
|
import "package:flutter/foundation.dart";
|
|
4
|
+
import "package:flutter_azure_tts/flutter_azure_tts.dart";
|
|
5
5
|
import "package:flutter_native_splash/flutter_native_splash.dart";
|
|
6
6
|
import "package:async_redux/async_redux.dart";
|
|
7
|
+
import "package:package_info_plus/package_info_plus.dart";
|
|
7
8
|
import "package:only_bible_app/app.dart";
|
|
8
|
-
import "package:only_bible_app/
|
|
9
|
+
import "package:only_bible_app/env.dart";
|
|
9
10
|
import "package:only_bible_app/store/app_persistor.dart";
|
|
10
11
|
import "package:only_bible_app/store/app_state.dart";
|
|
11
12
|
import "package:only_bible_app/utils.dart";
|
|
13
|
+
import "package:shared/shared.dart" show ErrorReporting, httpErrorReportSender;
|
|
14
|
+
|
|
15
|
+
// TODO: replace with the deployed error-reporter Worker's actual URL (see
|
|
16
|
+
// workers/error-reporter/) — either its workers.dev subdomain or a custom
|
|
17
|
+
// route once one is configured.
|
|
18
|
+
const _errorReporterEndpoint = "https://error-reporter.YOUR_SUBDOMAIN.workers.dev";
|
|
12
19
|
|
|
13
20
|
final navigatorKey = GlobalKey<NavigatorState>();
|
|
14
21
|
|
|
@@ -38,37 +45,23 @@ void main() async {
|
|
|
38
45
|
FlutterNativeSplash.preserve(
|
|
39
46
|
widgetsBinding: WidgetsFlutterBinding.ensureInitialized(),
|
|
40
47
|
);
|
|
41
|
-
FlutterError.onError = (errorDetails) {
|
|
42
|
-
FlutterError.presentError(errorDetails);
|
|
43
|
-
if (kDebugMode) {
|
|
44
|
-
debugPrint("FlutterError: ${errorDetails.exception}");
|
|
45
|
-
debugPrint("${errorDetails.stack}");
|
|
46
|
-
}
|
|
47
|
-
SchedulerBinding.instance.addPostFrameCallback((d) {
|
|
48
|
-
|
|
48
|
+
final packageInfo = await PackageInfo.fromPlatform();
|
|
49
|
-
if (context == null || !context.mounted) return;
|
|
50
|
-
showReportError(
|
|
51
|
-
context,
|
|
52
|
-
errorDetails.exception.toString(),
|
|
53
|
-
|
|
49
|
+
ErrorReporting.install(
|
|
50
|
+
navigatorKey: navigatorKey,
|
|
51
|
+
ignoredSubstrings: const {"LateInitializationError", "HardwareKeyboard"},
|
|
52
|
+
onReport: httpErrorReportSender(
|
|
53
|
+
endpoint: Uri.parse(_errorReporterEndpoint),
|
|
54
|
+
appName: "only-bible-app",
|
|
55
|
+
sharedSecret: Env.errorReportSecret,
|
|
56
|
+
appVersion: packageInfo.version,
|
|
57
|
+
buildNumber: packageInfo.buildNumber,
|
|
58
|
+
),
|
|
54
|
-
|
|
59
|
+
);
|
|
55
|
-
});
|
|
56
|
-
};
|
|
57
|
-
PlatformDispatcher.instance.onError = (error, stack) {
|
|
58
|
-
|
|
60
|
+
FlutterAzureTts.init(
|
|
59
|
-
|
|
61
|
+
subscriptionKey: Env.ttsSubscriptionKey,
|
|
60
|
-
|
|
62
|
+
region: "centralindia",
|
|
61
|
-
}
|
|
62
|
-
final context = navigatorKey.currentContext;
|
|
63
|
-
if (context != null && context.mounted) {
|
|
64
|
-
showReportError(
|
|
65
|
-
context,
|
|
66
|
-
|
|
63
|
+
withLogs: true,
|
|
67
|
-
stack,
|
|
68
|
-
|
|
64
|
+
);
|
|
69
|
-
}
|
|
70
|
-
return true;
|
|
71
|
-
};
|
|
72
65
|
final persistor = AppPersistor();
|
|
73
66
|
final json = await persistor.readJson();
|
|
74
67
|
final bibleName = json?["bibleName"] as String? ?? "en_kjv";
|
lib/store/actions_navigation.dart
CHANGED
|
@@ -29,21 +29,12 @@ class ShowHighlightHistoryAction extends ReduxAction<AppState> {
|
|
|
29
29
|
useSafeArea: true,
|
|
30
30
|
isScrollControlled: true,
|
|
31
31
|
builder: (context) {
|
|
32
|
-
final surface = Theme.of(context).colorScheme.surface;
|
|
33
|
-
return Theme(
|
|
34
|
-
data: Theme.of(context).copyWith(
|
|
35
|
-
bottomSheetTheme: BottomSheetThemeData(
|
|
36
|
-
backgroundColor: surface,
|
|
37
|
-
dragHandleColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
38
|
-
),
|
|
39
|
-
),
|
|
40
|
-
|
|
32
|
+
return DraggableScrollableSheet(
|
|
41
|
-
|
|
33
|
+
expand: false,
|
|
42
|
-
|
|
34
|
+
initialChildSize: 0.6,
|
|
43
|
-
|
|
35
|
+
minChildSize: 0.4,
|
|
44
|
-
|
|
36
|
+
maxChildSize: 0.9,
|
|
45
|
-
|
|
37
|
+
builder: (_, __) => HighlightHistorySheet(bible: bible),
|
|
46
|
-
),
|
|
47
38
|
);
|
|
48
39
|
},
|
|
49
40
|
);
|
|
@@ -66,16 +57,7 @@ class ShowSettingsAction extends ReduxAction<AppState> {
|
|
|
66
57
|
showDragHandle: true,
|
|
67
58
|
useSafeArea: true,
|
|
68
59
|
builder: (context) {
|
|
69
|
-
final surface = Theme.of(context).colorScheme.surface;
|
|
70
|
-
return Theme(
|
|
71
|
-
data: Theme.of(context).copyWith(
|
|
72
|
-
bottomSheetTheme: BottomSheetThemeData(
|
|
73
|
-
backgroundColor: surface,
|
|
74
|
-
dragHandleColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
75
|
-
),
|
|
76
|
-
),
|
|
77
|
-
|
|
60
|
+
return SettingsSheet(bible: bible);
|
|
78
|
-
);
|
|
79
61
|
},
|
|
80
62
|
);
|
|
81
63
|
return null;
|
lib/store/actions_state.dart
CHANGED
|
@@ -7,6 +7,7 @@ import "package:only_bible_app/dialog.dart";
|
|
|
7
7
|
import "package:only_bible_app/gen/bible.gen.dart";
|
|
8
8
|
import "package:only_bible_app/store/app_state.dart";
|
|
9
9
|
import "package:only_bible_app/utils.dart";
|
|
10
|
+
import "package:shared/shared.dart" show ErrorReporting;
|
|
10
11
|
|
|
11
12
|
class ToggleEngTitlesAction extends ReduxAction<AppState> {
|
|
12
13
|
@override
|
|
@@ -69,6 +70,7 @@ class TogglePlayAction extends ReduxAction<AppState> {
|
|
|
69
70
|
}
|
|
70
71
|
final versesToPlay = List<Verse>.from(state.selectedVerses);
|
|
71
72
|
if (versesToPlay.isEmpty) return null;
|
|
73
|
+
final audioVoice = bible.voiceName!;
|
|
72
74
|
final audioError = "Audio playback error";
|
|
73
75
|
_isStarting = true;
|
|
74
76
|
try {
|
|
@@ -76,7 +78,7 @@ class TogglePlayAction extends ReduxAction<AppState> {
|
|
|
76
78
|
for (final v in versesToPlay) {
|
|
77
79
|
final pathname = "${bible.name!}_${v.book}_${v.chapter}_${v.index}";
|
|
78
80
|
try {
|
|
79
|
-
final data = await convertText(v.text ?? "");
|
|
81
|
+
final data = await convertText(audioVoice, v.text ?? "");
|
|
80
82
|
if (!SoLoud.instance.isInitialized) break;
|
|
81
83
|
final stream = SoLoud.instance.setBufferStream(
|
|
82
84
|
bufferingType: BufferingType.preserved,
|
|
@@ -102,7 +104,7 @@ class TogglePlayAction extends ReduxAction<AppState> {
|
|
|
102
104
|
name: "play",
|
|
103
105
|
error: (err.toString(), pathname),
|
|
104
106
|
);
|
|
105
|
-
await
|
|
107
|
+
await ErrorReporting.report((err.toString(), pathname).toString(), null);
|
|
106
108
|
if (buildContext.mounted) {
|
|
107
109
|
showError(buildContext, audioError);
|
|
108
110
|
}
|
lib/utils.dart
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import "dart:convert";
|
|
2
1
|
import "dart:typed_data";
|
|
3
2
|
|
|
4
3
|
import "package:archive/archive.dart";
|
|
5
|
-
import "package:http/http.dart" as http;
|
|
6
4
|
import "package:flutter/services.dart";
|
|
7
|
-
import "package:package_info_plus/package_info_plus.dart";
|
|
8
5
|
import "package:flutter/foundation.dart"
|
|
9
|
-
show TargetPlatform, compute, defaultTargetPlatform
|
|
6
|
+
show TargetPlatform, compute, defaultTargetPlatform;
|
|
10
7
|
import "package:flutter/material.dart";
|
|
11
|
-
import "package:
|
|
8
|
+
import "package:flutter_azure_tts/flutter_azure_tts.dart";
|
|
12
9
|
import "package:flutter_soloud/flutter_soloud.dart";
|
|
13
|
-
import "package:only_bible_app/env.dart";
|
|
14
10
|
import "package:only_bible_app/gen/bible.gen.dart";
|
|
15
11
|
export "package:only_bible_app/bible_data.dart";
|
|
16
12
|
|
|
@@ -128,27 +124,28 @@ extension AppContext on BuildContext {
|
|
|
128
124
|
}
|
|
129
125
|
}
|
|
130
126
|
|
|
131
|
-
KittenTTS? _kittenTts;
|
|
132
|
-
|
|
133
|
-
Future<void> _ensureKittenTtsReady() async {
|
|
134
|
-
if (_kittenTts != null) return;
|
|
135
|
-
final tts = KittenTTS();
|
|
136
|
-
await tts.initialize();
|
|
137
|
-
_kittenTts = tts;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
Uint8List _float32ToPcm16(Float32List samples) {
|
|
141
|
-
final pcm = Int16List(samples.length);
|
|
142
|
-
for (var i = 0; i < samples.length; i++) {
|
|
143
|
-
pcm[i] = (samples[i].clamp(-1.0, 1.0) * 32767).round();
|
|
144
|
-
}
|
|
145
|
-
return pcm.buffer.asUint8List();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
Future<Uint8List> convertText(String text) async {
|
|
127
|
+
Future<Uint8List> convertText(String langCode, String text) async {
|
|
149
|
-
await _ensureKittenTtsReady();
|
|
150
|
-
final
|
|
128
|
+
final ttsResponse = await FlutterAzureTts.getTts(
|
|
129
|
+
TtsParams(
|
|
130
|
+
voice: Voice(
|
|
131
|
+
name: "",
|
|
132
|
+
displayName: "",
|
|
133
|
+
localName: "",
|
|
134
|
+
shortName: langCode,
|
|
135
|
+
gender: "",
|
|
136
|
+
locale: langCode,
|
|
137
|
+
sampleRateHertz: AudioOutputFormat.audio48khz96kBitrateMonoMp3,
|
|
138
|
+
voiceType: "",
|
|
139
|
+
status: "",
|
|
140
|
+
styles: [],
|
|
141
|
+
roles: [],
|
|
142
|
+
),
|
|
143
|
+
audioFormat: AudioOutputFormat.audio48khz96kBitrateMonoMp3,
|
|
144
|
+
rate: 0.90,
|
|
145
|
+
text: text,
|
|
146
|
+
),
|
|
147
|
+
);
|
|
151
|
-
return
|
|
148
|
+
return ttsResponse.audio.buffer.asUint8List();
|
|
152
149
|
}
|
|
153
150
|
|
|
154
151
|
bool isAudioPlaybackActive() {
|
|
@@ -183,37 +180,6 @@ Future<Bible> loadBible(String name) async {
|
|
|
183
180
|
return Bible(decompressed);
|
|
184
181
|
}
|
|
185
182
|
|
|
183
|
+
// Error reporting now goes through ErrorReporting.report (package:shared) —
|
|
186
|
-
|
|
184
|
+
// see main.dart's ErrorReporting.install call and its use in
|
|
187
|
-
final packageInfo = await PackageInfo.fromPlatform();
|
|
188
|
-
if (kDebugMode) {
|
|
189
|
-
print("ERROR: $message");
|
|
190
|
-
print("ERROR STACK: ${stack.toString()}");
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
final url = Uri.https("api.resend.com", "/emails");
|
|
194
|
-
final response = await http.post(
|
|
195
|
-
url,
|
|
196
|
-
headers: {
|
|
197
|
-
"Authorization": "Bearer ${Env.resendApiKey}",
|
|
198
|
-
"Content-Type": "application/json",
|
|
199
|
-
},
|
|
200
|
-
body: jsonEncode({
|
|
201
|
-
"from": "[email protected]",
|
|
202
|
-
"to": "[email protected]",
|
|
203
|
-
"subject": "Error Stack trace",
|
|
204
|
-
"html": """
|
|
205
|
-
<div>
|
|
206
|
-
<p><strong>OS:</strong>$defaultTargetPlatform</p>
|
|
207
|
-
<p><strong>App Version:</strong>${packageInfo.version}</p>
|
|
208
|
-
<p><strong>App Build Number:</strong>${packageInfo.buildSignature}</p>
|
|
209
|
-
|
|
185
|
+
// actions_state.dart's audio-playback failure handler.
|
|
210
|
-
<p><strong>StackTrace:</strong>${stack?.toString()}</p>
|
|
211
|
-
</div>
|
|
212
|
-
""",
|
|
213
|
-
}),
|
|
214
|
-
);
|
|
215
|
-
if (response.statusCode == 200) {
|
|
216
|
-
print("Response status: ${response.statusCode}");
|
|
217
|
-
print("Response body: ${response.body}");
|
|
218
|
-
}
|
|
219
|
-
}
|
macos/Flutter/GeneratedPluginRegistrant.swift
CHANGED
|
@@ -7,18 +7,16 @@ import Foundation
|
|
|
7
7
|
|
|
8
8
|
import connectivity_plus
|
|
9
9
|
import file_picker
|
|
10
|
-
import flutter_kitten_tts
|
|
11
|
-
import flutter_onnxruntime
|
|
12
10
|
import flutter_secure_storage_darwin
|
|
13
11
|
import package_info_plus
|
|
14
12
|
import share_plus
|
|
13
|
+
import sqflite_darwin
|
|
15
14
|
|
|
16
15
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|
17
16
|
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
|
18
17
|
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
|
19
|
-
KittenTtsFlutterPlugin.register(with: registry.registrar(forPlugin: "KittenTtsFlutterPlugin"))
|
|
20
|
-
FlutterOnnxruntimePlugin.register(with: registry.registrar(forPlugin: "FlutterOnnxruntimePlugin"))
|
|
21
18
|
FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin"))
|
|
22
19
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
|
23
20
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
|
21
|
+
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
|
24
22
|
}
|
macos/Podfile
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
platform :osx, '
|
|
1
|
+
platform :osx, '10.15'
|
|
2
2
|
|
|
3
3
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
|
4
4
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
|
@@ -38,14 +38,5 @@ end
|
|
|
38
38
|
post_install do |installer|
|
|
39
39
|
installer.pods_project.targets.each do |target|
|
|
40
40
|
flutter_additional_macos_build_settings(target)
|
|
41
|
-
# flutter_kitten_tts ships both espeak-ng/speak_lib.h and the legacy
|
|
42
|
-
# espeak/speak_lib.h alias; both flatten to the same framework header
|
|
43
|
-
# path, causing a "duplicate output file" Xcode error. Remove the legacy copy.
|
|
44
|
-
if target.name == 'flutter_kitten_tts'
|
|
45
|
-
dups = target.headers_build_phase.files.select do |f|
|
|
46
|
-
f.file_ref.path.to_s.end_with?('espeak/speak_lib.h')
|
|
47
|
-
end
|
|
48
|
-
dups.each { |f| target.headers_build_phase.remove_build_file(f) }
|
|
49
|
-
end
|
|
50
41
|
end
|
|
51
42
|
end
|
macos/Podfile.lock
CHANGED
|
@@ -1,28 +1,22 @@
|
|
|
1
1
|
PODS:
|
|
2
|
-
- flutter_kitten_tts (0.0.4):
|
|
3
|
-
- FlutterMacOS
|
|
4
2
|
- flutter_soloud (0.0.1):
|
|
5
3
|
- FlutterMacOS
|
|
6
4
|
- FlutterMacOS (1.0.0)
|
|
7
5
|
|
|
8
6
|
DEPENDENCIES:
|
|
9
|
-
- flutter_kitten_tts (from `Flutter/ephemeral/.symlinks/plugins/flutter_kitten_tts/macos`)
|
|
10
7
|
- flutter_soloud (from `Flutter/ephemeral/.symlinks/plugins/flutter_soloud/macos`)
|
|
11
8
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
|
12
9
|
|
|
13
10
|
EXTERNAL SOURCES:
|
|
14
|
-
flutter_kitten_tts:
|
|
15
|
-
:path: Flutter/ephemeral/.symlinks/plugins/flutter_kitten_tts/macos
|
|
16
11
|
flutter_soloud:
|
|
17
12
|
:path: Flutter/ephemeral/.symlinks/plugins/flutter_soloud/macos
|
|
18
13
|
FlutterMacOS:
|
|
19
14
|
:path: Flutter/ephemeral
|
|
20
15
|
|
|
21
16
|
SPEC CHECKSUMS:
|
|
22
|
-
flutter_kitten_tts: 9267094d83d47e149bba12f56c74fc070982ae1f
|
|
23
17
|
flutter_soloud: 93c20641a100d25bfbc84a8b86e867f350ef29ac
|
|
24
18
|
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
|
25
19
|
|
|
26
|
-
PODFILE CHECKSUM:
|
|
20
|
+
PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009
|
|
27
21
|
|
|
28
22
|
COCOAPODS: 1.16.2
|
macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"pins" : [
|
|
3
|
-
{
|
|
4
|
-
"identity" : "onnxruntime-swift-package-manager",
|
|
5
|
-
"kind" : "remoteSourceControl",
|
|
6
|
-
"location" : "https://github.com/masicai/onnxruntime-swift-package-manager",
|
|
7
|
-
"state" : {
|
|
8
|
-
"revision" : "57f28b1fdd6fe33585370b146a46c597d6750953",
|
|
9
|
-
"version" : "1.23.1"
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
],
|
|
13
|
-
"version" : 2
|
|
14
|
-
}
|
macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"pins" : [
|
|
3
|
-
{
|
|
4
|
-
"identity" : "onnxruntime-swift-package-manager",
|
|
5
|
-
"kind" : "remoteSourceControl",
|
|
6
|
-
"location" : "https://github.com/masicai/onnxruntime-swift-package-manager",
|
|
7
|
-
"state" : {
|
|
8
|
-
"revision" : "57f28b1fdd6fe33585370b146a46c597d6750953",
|
|
9
|
-
"version" : "1.23.1"
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
],
|
|
13
|
-
"version" : 2
|
|
14
|
-
}
|
pubspec.yaml
CHANGED
|
@@ -23,7 +23,7 @@ dependencies:
|
|
|
23
23
|
archive: ^4.0.9
|
|
24
24
|
envied: ^1.3.8
|
|
25
25
|
file_picker: ^11.0.2
|
|
26
|
-
|
|
26
|
+
flutter_azure_tts: ^1.0.0
|
|
27
27
|
shared:
|
|
28
28
|
path: ../../packages/shared
|
|
29
29
|
|
test/app_logic_test.dart
CHANGED
|
@@ -336,7 +336,7 @@ void main() {
|
|
|
336
336
|
|
|
337
337
|
group("Env", () {
|
|
338
338
|
test("obfuscated secrets decode back to non-empty strings", () {
|
|
339
|
-
expect(Env.
|
|
339
|
+
expect(Env.errorReportSecret, isNotEmpty);
|
|
340
340
|
expect(Env.ttsSubscriptionKey, isNotEmpty);
|
|
341
341
|
});
|
|
342
342
|
});
|