feat: crash log reporting
This commit is contained in:
@@ -30,6 +30,13 @@ android {
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = flutter.versionCode
|
||||
versionName = flutter.versionName
|
||||
|
||||
if (System.getenv("AMAZON") != null) {
|
||||
versionCode = (flutter.versionCode ?: 0) + 3000
|
||||
ndk {
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
|
||||
@@ -22,17 +22,17 @@ platform :android do
|
||||
debug_info_dir = "./debug-info/#{version_name}+#{version_code}"
|
||||
|
||||
# Build the Flutter app
|
||||
sh("cd #{ENV['PWD']}/.. && flutter build appbundle --dart-define=ENABLE_IN_APP_REVIEW=true --obfuscate --split-debug-info=#{debug_info_dir}/aab")
|
||||
# sh("cd #{ENV['PWD']}/.. && flutter build appbundle --dart-define=ENABLE_IN_APP_REVIEW=true --obfuscate --split-debug-info=#{debug_info_dir}/aab")
|
||||
|
||||
upload_to_play_store(
|
||||
track: "production",
|
||||
release_status: "completed",
|
||||
version_code: version_code,
|
||||
aab: "../build/app/outputs/bundle/release/app-release.aab"
|
||||
)
|
||||
# upload_to_play_store(
|
||||
# track: "production",
|
||||
# release_status: "completed",
|
||||
# version_code: version_code,
|
||||
# aab: "../build/app/outputs/bundle/release/app-release.aab"
|
||||
# )
|
||||
|
||||
# Build universal APK for Amazon Appstore (doesn't accept AABs, doesn't auto-target by ABI)
|
||||
sh("cd #{ENV['PWD']}/.. && flutter build apk --release --target-platform android-arm,android-arm64 --obfuscate --split-debug-info=#{debug_info_dir}/apk")
|
||||
# Build universal APK for Amazon Appstore (doesn't accept AABs, excludes x86 via env var)
|
||||
sh("cd #{ENV['PWD']}/.. && AMAZON=1 flutter build apk --release --obfuscate --split-debug-info=#{debug_info_dir}/apk")
|
||||
|
||||
upload_to_amazon_appstore(
|
||||
apk_paths: [
|
||||
|
||||
@@ -66,6 +66,11 @@ PODS:
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- PromisesObjC (2.4.0)
|
||||
- Sentry/HybridSDK (8.46.0)
|
||||
- sentry_flutter (8.14.2):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- Sentry/HybridSDK (= 8.46.0)
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@@ -117,6 +122,7 @@ DEPENDENCIES:
|
||||
- os_media_controls (from `.symlinks/plugins/os_media_controls/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
|
||||
@@ -138,6 +144,7 @@ SPEC REPOS:
|
||||
- MLKitVision
|
||||
- nanopb
|
||||
- PromisesObjC
|
||||
- Sentry
|
||||
- sqlite3
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
@@ -161,6 +168,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
sentry_flutter:
|
||||
:path: ".symlinks/plugins/sentry_flutter/ios"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||
sqflite_darwin:
|
||||
@@ -198,6 +207,8 @@ SPEC CHECKSUMS:
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
|
||||
sentry_flutter: 27892878729f42701297c628eb90e7c6529f3684
|
||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
sqlite3: 8d708bc63e9f4ce48f0ad9d6269e478c5ced1d9b
|
||||
|
||||
@@ -151,6 +151,8 @@
|
||||
"keyboardShortcutsDescription": "Tastenkürzel anpassen",
|
||||
"videoPlayerNavigation": "Videoplayer-Navigation",
|
||||
"videoPlayerNavigationDescription": "Pfeiltasten zur Navigation der Videoplayer-Steuerung verwenden",
|
||||
"crashReporting": "Absturzberichte",
|
||||
"crashReportingDescription": "Absturzberichte senden, um die App zu verbessern",
|
||||
"debugLogging": "Debug-Protokollierung",
|
||||
"debugLoggingDescription": "Detaillierte Protokolle zur Fehleranalyse aktivieren",
|
||||
"viewLogs": "Protokolle anzeigen",
|
||||
|
||||
@@ -151,6 +151,8 @@
|
||||
"keyboardShortcutsDescription": "Customize keyboard shortcuts",
|
||||
"videoPlayerNavigation": "Video Player Navigation",
|
||||
"videoPlayerNavigationDescription": "Use arrow keys to navigate video player controls",
|
||||
"crashReporting": "Crash Reporting",
|
||||
"crashReportingDescription": "Send crash reports to help improve the app",
|
||||
"debugLogging": "Debug Logging",
|
||||
"debugLoggingDescription": "Enable detailed logging for troubleshooting",
|
||||
"viewLogs": "View Logs",
|
||||
|
||||
@@ -151,6 +151,8 @@
|
||||
"keyboardShortcutsDescription": "Personalizar los atajos de teclado",
|
||||
"videoPlayerNavigation": "Navegación del Reproductor de Video",
|
||||
"videoPlayerNavigationDescription": "Usar las teclas de flecha para navegar por los controles del reproductor",
|
||||
"crashReporting": "Informes de Errores",
|
||||
"crashReportingDescription": "Enviar informes de errores para mejorar la aplicación",
|
||||
"debugLogging": "Registro de Depuración",
|
||||
"debugLoggingDescription": "Habilitar registros detallados para resolución de problemas",
|
||||
"viewLogs": "Ver Logs",
|
||||
|
||||
@@ -151,6 +151,8 @@
|
||||
"keyboardShortcutsDescription": "Personnaliser les raccourcis clavier",
|
||||
"videoPlayerNavigation": "Navigation dans le lecteur vidéo",
|
||||
"videoPlayerNavigationDescription": "Utilisez les touches fléchées pour naviguer dans les commandes du lecteur vidéo.",
|
||||
"crashReporting": "Rapports de plantage",
|
||||
"crashReportingDescription": "Envoyer des rapports de plantage pour améliorer l'application",
|
||||
"debugLogging": "Journalisation de débogage",
|
||||
"debugLoggingDescription": "Activer la journalisation détaillée pour le dépannage",
|
||||
"viewLogs": "Voir les logs",
|
||||
|
||||
@@ -151,6 +151,8 @@
|
||||
"keyboardShortcutsDescription": "Personalizza le scorciatoie da tastiera",
|
||||
"videoPlayerNavigation": "Navigazione del lettore video",
|
||||
"videoPlayerNavigationDescription": "Usa i tasti freccia per navigare nei controlli del lettore video",
|
||||
"crashReporting": "Segnalazione errori",
|
||||
"crashReportingDescription": "Invia segnalazioni di errori per migliorare l'app",
|
||||
"debugLogging": "Log di debug",
|
||||
"debugLoggingDescription": "Abilita il logging dettagliato per la risoluzione dei problemi",
|
||||
"viewLogs": "Visualizza log",
|
||||
|
||||
@@ -151,6 +151,8 @@
|
||||
"keyboardShortcutsDescription": "사용자 정의 키보드 단축키",
|
||||
"videoPlayerNavigation": "비디오 플레이어 탐색",
|
||||
"videoPlayerNavigationDescription": "방향 키를 사용하여 비디오 플레이어 컨트롤 탐색",
|
||||
"crashReporting": "충돌 보고",
|
||||
"crashReportingDescription": "앱 개선을 위해 충돌 보고서 전송",
|
||||
"debugLogging": "디버그 로깅",
|
||||
"debugLoggingDescription": "문제 해결을 위해 상세 로깅 활성화",
|
||||
"viewLogs": "로그 보기",
|
||||
|
||||
@@ -151,6 +151,8 @@
|
||||
"keyboardShortcutsDescription": "Pas toetsenbord sneltoetsen aan",
|
||||
"videoPlayerNavigation": "Videospeler navigatie",
|
||||
"videoPlayerNavigationDescription": "Gebruik pijltjestoetsen om door de videospeler bediening te navigeren",
|
||||
"crashReporting": "Crashrapportage",
|
||||
"crashReportingDescription": "Crashrapporten verzenden om de app te verbeteren",
|
||||
"debugLogging": "Debug logging",
|
||||
"debugLoggingDescription": "Schakel gedetailleerde logging in voor probleemoplossing",
|
||||
"viewLogs": "Bekijk logs",
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 9
|
||||
/// Strings: 7173 (797 per locale)
|
||||
/// Strings: 7245 (805 per locale)
|
||||
///
|
||||
/// Built on 2026-02-24 at 21:50 UTC
|
||||
/// Built on 2026-03-02 at 06:26 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint, unused_import
|
||||
|
||||
@@ -270,6 +270,8 @@ class _TranslationsSettingsDe implements TranslationsSettingsEn {
|
||||
@override String get keyboardShortcutsDescription => 'Tastenkürzel anpassen';
|
||||
@override String get videoPlayerNavigation => 'Videoplayer-Navigation';
|
||||
@override String get videoPlayerNavigationDescription => 'Pfeiltasten zur Navigation der Videoplayer-Steuerung verwenden';
|
||||
@override String get crashReporting => 'Absturzberichte';
|
||||
@override String get crashReportingDescription => 'Absturzberichte senden, um die App zu verbessern';
|
||||
@override String get debugLogging => 'Debug-Protokollierung';
|
||||
@override String get debugLoggingDescription => 'Detaillierte Protokolle zur Fehleranalyse aktivieren';
|
||||
@override String get viewLogs => 'Protokolle anzeigen';
|
||||
@@ -1441,6 +1443,8 @@ extension on TranslationsDe {
|
||||
'settings.keyboardShortcutsDescription' => 'Tastenkürzel anpassen',
|
||||
'settings.videoPlayerNavigation' => 'Videoplayer-Navigation',
|
||||
'settings.videoPlayerNavigationDescription' => 'Pfeiltasten zur Navigation der Videoplayer-Steuerung verwenden',
|
||||
'settings.crashReporting' => 'Absturzberichte',
|
||||
'settings.crashReportingDescription' => 'Absturzberichte senden, um die App zu verbessern',
|
||||
'settings.debugLogging' => 'Debug-Protokollierung',
|
||||
'settings.debugLoggingDescription' => 'Detaillierte Protokolle zur Fehleranalyse aktivieren',
|
||||
'settings.viewLogs' => 'Protokolle anzeigen',
|
||||
@@ -1487,6 +1491,8 @@ extension on TranslationsDe {
|
||||
'settings.maxVolumePercent' => ({required Object percent}) => '${percent}%',
|
||||
'settings.discordRichPresence' => 'Discord Rich Presence',
|
||||
'settings.discordRichPresenceDescription' => 'Zeige auf Discord, was du gerade schaust',
|
||||
'settings.autoPip' => 'Automatisches Bild-in-Bild',
|
||||
'settings.autoPipDescription' => 'Automatisch Bild-in-Bild aktivieren, wenn die App während der Wiedergabe verlassen wird',
|
||||
'settings.matchContentFrameRate' => 'Inhalts-Bildrate anpassen',
|
||||
'settings.matchContentFrameRateDescription' => 'Bildwiederholfrequenz des Displays an den Videoinhalt anpassen, reduziert Ruckeln und spart Akku',
|
||||
'settings.tunneledPlayback' => 'Tunneled Playback',
|
||||
@@ -1808,6 +1814,8 @@ extension on TranslationsDe {
|
||||
'liveTv.nowPlaying' => 'Läuft gerade',
|
||||
'liveTv.noPrograms' => 'Keine Programmdaten verfügbar',
|
||||
'liveTv.channelNumber' => ({required Object number}) => 'Kanal ${number}',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.live' => 'LIVE',
|
||||
'liveTv.hd' => 'HD',
|
||||
'liveTv.premiere' => 'NEU',
|
||||
@@ -1822,8 +1830,6 @@ extension on TranslationsDe {
|
||||
'liveTv.evening' => 'Abend',
|
||||
'liveTv.lateNight' => 'Spätnacht',
|
||||
'liveTv.whatsOn' => 'Jetzt im TV',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.watchChannel' => 'Kanal ansehen',
|
||||
'downloads.title' => 'Downloads',
|
||||
'downloads.manage' => 'Verwalten',
|
||||
|
||||
@@ -472,7 +472,7 @@ class TranslationsSettingsEn {
|
||||
/// en: 'Hide Spoilers for Unwatched Episodes'
|
||||
String get hideSpoilers => 'Hide Spoilers for Unwatched Episodes';
|
||||
|
||||
/// en: 'Blur thumbnails and hide descriptions for episodes you haven\'t watched yet'
|
||||
/// en: 'Blur thumbnails and hide descriptions for episodes you haven't watched yet'
|
||||
String get hideSpoilersDescription => 'Blur thumbnails and hide descriptions for episodes you haven\'t watched yet';
|
||||
|
||||
/// en: 'Player Backend'
|
||||
@@ -556,6 +556,12 @@ class TranslationsSettingsEn {
|
||||
/// en: 'Use arrow keys to navigate video player controls'
|
||||
String get videoPlayerNavigationDescription => 'Use arrow keys to navigate video player controls';
|
||||
|
||||
/// en: 'Crash Reporting'
|
||||
String get crashReporting => 'Crash Reporting';
|
||||
|
||||
/// en: 'Send crash reports to help improve the app'
|
||||
String get crashReportingDescription => 'Send crash reports to help improve the app';
|
||||
|
||||
/// en: 'Debug Logging'
|
||||
String get debugLogging => 'Debug Logging';
|
||||
|
||||
@@ -2807,7 +2813,6 @@ class TranslationsCompanionRemotePairingEn {
|
||||
|
||||
/// en: 'Failed to connect: ${error}'
|
||||
String failedToConnect({required Object error}) => 'Failed to connect: ${error}';
|
||||
|
||||
}
|
||||
|
||||
// Path: companionRemote.remote
|
||||
@@ -3055,6 +3060,8 @@ extension on Translations {
|
||||
'settings.keyboardShortcutsDescription' => 'Customize keyboard shortcuts',
|
||||
'settings.videoPlayerNavigation' => 'Video Player Navigation',
|
||||
'settings.videoPlayerNavigationDescription' => 'Use arrow keys to navigate video player controls',
|
||||
'settings.crashReporting' => 'Crash Reporting',
|
||||
'settings.crashReportingDescription' => 'Send crash reports to help improve the app',
|
||||
'settings.debugLogging' => 'Debug Logging',
|
||||
'settings.debugLoggingDescription' => 'Enable detailed logging for troubleshooting',
|
||||
'settings.viewLogs' => 'View Logs',
|
||||
@@ -3424,6 +3431,8 @@ extension on Translations {
|
||||
'liveTv.nowPlaying' => 'Now Playing',
|
||||
'liveTv.noPrograms' => 'No program data available',
|
||||
'liveTv.channelNumber' => ({required Object number}) => 'Ch. ${number}',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.live' => 'LIVE',
|
||||
'liveTv.hd' => 'HD',
|
||||
'liveTv.premiere' => 'NEW',
|
||||
@@ -3438,8 +3447,6 @@ extension on Translations {
|
||||
'liveTv.evening' => 'Evening',
|
||||
'liveTv.lateNight' => 'Late Night',
|
||||
'liveTv.whatsOn' => 'What\'s On',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.watchChannel' => 'Watch Channel',
|
||||
'collections.title' => 'Collections',
|
||||
'collections.collection' => 'Collection',
|
||||
|
||||
@@ -270,6 +270,8 @@ class _TranslationsSettingsEs implements TranslationsSettingsEn {
|
||||
@override String get keyboardShortcutsDescription => 'Personalizar los atajos de teclado';
|
||||
@override String get videoPlayerNavigation => 'Navegación del Reproductor de Video';
|
||||
@override String get videoPlayerNavigationDescription => 'Usar las teclas de flecha para navegar por los controles del reproductor';
|
||||
@override String get crashReporting => 'Informes de Errores';
|
||||
@override String get crashReportingDescription => 'Enviar informes de errores para mejorar la aplicación';
|
||||
@override String get debugLogging => 'Registro de Depuración';
|
||||
@override String get debugLoggingDescription => 'Habilitar registros detallados para resolución de problemas';
|
||||
@override String get viewLogs => 'Ver Logs';
|
||||
@@ -1441,6 +1443,8 @@ extension on TranslationsEs {
|
||||
'settings.keyboardShortcutsDescription' => 'Personalizar los atajos de teclado',
|
||||
'settings.videoPlayerNavigation' => 'Navegación del Reproductor de Video',
|
||||
'settings.videoPlayerNavigationDescription' => 'Usar las teclas de flecha para navegar por los controles del reproductor',
|
||||
'settings.crashReporting' => 'Informes de Errores',
|
||||
'settings.crashReportingDescription' => 'Enviar informes de errores para mejorar la aplicación',
|
||||
'settings.debugLogging' => 'Registro de Depuración',
|
||||
'settings.debugLoggingDescription' => 'Habilitar registros detallados para resolución de problemas',
|
||||
'settings.viewLogs' => 'Ver Logs',
|
||||
@@ -1487,6 +1491,8 @@ extension on TranslationsEs {
|
||||
'settings.maxVolumePercent' => ({required Object percent}) => '${percent}%',
|
||||
'settings.discordRichPresence' => 'Presencia de Discord',
|
||||
'settings.discordRichPresenceDescription' => 'Mostrar lo que estás viendo en Discord',
|
||||
'settings.autoPip' => 'Imagen en imagen automática',
|
||||
'settings.autoPipDescription' => 'Activar automáticamente imagen en imagen al salir de la app durante la reproducción',
|
||||
'settings.matchContentFrameRate' => 'Ajustar frecuencia de actualización',
|
||||
'settings.matchContentFrameRateDescription' => 'Ajustar la frecuencia de actualización de la pantalla para que coincida con el video, reduciendo tirones y ahorrando batería',
|
||||
'settings.tunneledPlayback' => 'Tunneled Playback',
|
||||
@@ -1808,6 +1814,8 @@ extension on TranslationsEs {
|
||||
'liveTv.nowPlaying' => 'Reproduciendo ahora',
|
||||
'liveTv.noPrograms' => 'No hay datos de programación disponibles',
|
||||
'liveTv.channelNumber' => ({required Object number}) => 'Canal ${number}',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.live' => 'EN VIVO',
|
||||
'liveTv.hd' => 'HD',
|
||||
'liveTv.premiere' => 'NUEVO',
|
||||
@@ -1822,8 +1830,6 @@ extension on TranslationsEs {
|
||||
'liveTv.evening' => 'Noche',
|
||||
'liveTv.lateNight' => 'Trasnoche',
|
||||
'liveTv.whatsOn' => 'En emisión',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.watchChannel' => 'Ver canal',
|
||||
'collections.title' => 'Colecciones',
|
||||
'collections.collection' => 'Colección',
|
||||
|
||||
@@ -270,6 +270,8 @@ class _TranslationsSettingsFr implements TranslationsSettingsEn {
|
||||
@override String get keyboardShortcutsDescription => 'Personnaliser les raccourcis clavier';
|
||||
@override String get videoPlayerNavigation => 'Navigation dans le lecteur vidéo';
|
||||
@override String get videoPlayerNavigationDescription => 'Utilisez les touches fléchées pour naviguer dans les commandes du lecteur vidéo.';
|
||||
@override String get crashReporting => 'Rapports de plantage';
|
||||
@override String get crashReportingDescription => 'Envoyer des rapports de plantage pour améliorer l\'application';
|
||||
@override String get debugLogging => 'Journalisation de débogage';
|
||||
@override String get debugLoggingDescription => 'Activer la journalisation détaillée pour le dépannage';
|
||||
@override String get viewLogs => 'Voir les logs';
|
||||
@@ -1441,6 +1443,8 @@ extension on TranslationsFr {
|
||||
'settings.keyboardShortcutsDescription' => 'Personnaliser les raccourcis clavier',
|
||||
'settings.videoPlayerNavigation' => 'Navigation dans le lecteur vidéo',
|
||||
'settings.videoPlayerNavigationDescription' => 'Utilisez les touches fléchées pour naviguer dans les commandes du lecteur vidéo.',
|
||||
'settings.crashReporting' => 'Rapports de plantage',
|
||||
'settings.crashReportingDescription' => 'Envoyer des rapports de plantage pour améliorer l\'application',
|
||||
'settings.debugLogging' => 'Journalisation de débogage',
|
||||
'settings.debugLoggingDescription' => 'Activer la journalisation détaillée pour le dépannage',
|
||||
'settings.viewLogs' => 'Voir les logs',
|
||||
@@ -1487,6 +1491,8 @@ extension on TranslationsFr {
|
||||
'settings.maxVolumePercent' => ({required Object percent}) => '${percent}%',
|
||||
'settings.discordRichPresence' => 'Discord Rich Presence',
|
||||
'settings.discordRichPresenceDescription' => 'Montrez ce que vous regardez sur Discord',
|
||||
'settings.autoPip' => 'Image dans l\'image automatique',
|
||||
'settings.autoPipDescription' => 'Activer automatiquement l\'image dans l\'image en quittant l\'application pendant la lecture',
|
||||
'settings.matchContentFrameRate' => 'Fréquence d\'images du contenu correspondant',
|
||||
'settings.matchContentFrameRateDescription' => 'Ajustez la fréquence de rafraîchissement de l\'écran en fonction du contenu vidéo, ce qui réduit les saccades et économise la batterie',
|
||||
'settings.tunneledPlayback' => 'Tunneled Playback',
|
||||
@@ -1808,6 +1814,8 @@ extension on TranslationsFr {
|
||||
'liveTv.nowPlaying' => 'En cours de lecture',
|
||||
'liveTv.noPrograms' => 'Aucune donnée de programme disponible',
|
||||
'liveTv.channelNumber' => ({required Object number}) => 'Ch. ${number}',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.live' => 'EN DIRECT',
|
||||
'liveTv.hd' => 'HD',
|
||||
'liveTv.premiere' => 'NOUVEAU',
|
||||
@@ -1822,8 +1830,6 @@ extension on TranslationsFr {
|
||||
'liveTv.evening' => 'Soirée',
|
||||
'liveTv.lateNight' => 'Nuit tardive',
|
||||
'liveTv.whatsOn' => 'En ce moment',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.watchChannel' => 'Regarder la chaîne',
|
||||
'collections.title' => 'Collections',
|
||||
'collections.collection' => 'Collection',
|
||||
|
||||
@@ -270,6 +270,8 @@ class _TranslationsSettingsIt implements TranslationsSettingsEn {
|
||||
@override String get keyboardShortcutsDescription => 'Personalizza le scorciatoie da tastiera';
|
||||
@override String get videoPlayerNavigation => 'Navigazione del lettore video';
|
||||
@override String get videoPlayerNavigationDescription => 'Usa i tasti freccia per navigare nei controlli del lettore video';
|
||||
@override String get crashReporting => 'Segnalazione errori';
|
||||
@override String get crashReportingDescription => 'Invia segnalazioni di errori per migliorare l\'app';
|
||||
@override String get debugLogging => 'Log di debug';
|
||||
@override String get debugLoggingDescription => 'Abilita il logging dettagliato per la risoluzione dei problemi';
|
||||
@override String get viewLogs => 'Visualizza log';
|
||||
@@ -1441,6 +1443,8 @@ extension on TranslationsIt {
|
||||
'settings.keyboardShortcutsDescription' => 'Personalizza le scorciatoie da tastiera',
|
||||
'settings.videoPlayerNavigation' => 'Navigazione del lettore video',
|
||||
'settings.videoPlayerNavigationDescription' => 'Usa i tasti freccia per navigare nei controlli del lettore video',
|
||||
'settings.crashReporting' => 'Segnalazione errori',
|
||||
'settings.crashReportingDescription' => 'Invia segnalazioni di errori per migliorare l\'app',
|
||||
'settings.debugLogging' => 'Log di debug',
|
||||
'settings.debugLoggingDescription' => 'Abilita il logging dettagliato per la risoluzione dei problemi',
|
||||
'settings.viewLogs' => 'Visualizza log',
|
||||
@@ -1487,6 +1491,8 @@ extension on TranslationsIt {
|
||||
'settings.maxVolumePercent' => ({required Object percent}) => '${percent}%',
|
||||
'settings.discordRichPresence' => 'Discord Rich Presence',
|
||||
'settings.discordRichPresenceDescription' => 'Mostra su Discord cosa stai guardando',
|
||||
'settings.autoPip' => 'Picture-in-Picture automatico',
|
||||
'settings.autoPipDescription' => 'Attiva automaticamente il picture-in-picture quando si esce dall\'app durante la riproduzione',
|
||||
'settings.matchContentFrameRate' => 'Adatta frequenza fotogrammi',
|
||||
'settings.matchContentFrameRateDescription' => 'Regola la frequenza di aggiornamento del display in base al contenuto video, riducendo i tremolii e risparmiando batteria',
|
||||
'settings.tunneledPlayback' => 'Tunneled Playback',
|
||||
@@ -1808,6 +1814,8 @@ extension on TranslationsIt {
|
||||
'liveTv.nowPlaying' => 'In riproduzione',
|
||||
'liveTv.noPrograms' => 'Nessun dato di programma disponibile',
|
||||
'liveTv.channelNumber' => ({required Object number}) => 'Canale ${number}',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.live' => 'IN DIRETTA',
|
||||
'liveTv.hd' => 'HD',
|
||||
'liveTv.premiere' => 'NUOVO',
|
||||
@@ -1822,8 +1830,6 @@ extension on TranslationsIt {
|
||||
'liveTv.evening' => 'Sera',
|
||||
'liveTv.lateNight' => 'Notte tarda',
|
||||
'liveTv.whatsOn' => 'In onda ora',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.watchChannel' => 'Guarda canale',
|
||||
'downloads.title' => 'Download',
|
||||
'downloads.manage' => 'Gestisci',
|
||||
|
||||
@@ -270,6 +270,8 @@ class _TranslationsSettingsKo implements TranslationsSettingsEn {
|
||||
@override String get keyboardShortcutsDescription => '사용자 정의 키보드 단축키';
|
||||
@override String get videoPlayerNavigation => '비디오 플레이어 탐색';
|
||||
@override String get videoPlayerNavigationDescription => '방향 키를 사용하여 비디오 플레이어 컨트롤 탐색';
|
||||
@override String get crashReporting => '충돌 보고';
|
||||
@override String get crashReportingDescription => '앱 개선을 위해 충돌 보고서 전송';
|
||||
@override String get debugLogging => '디버그 로깅';
|
||||
@override String get debugLoggingDescription => '문제 해결을 위해 상세 로깅 활성화';
|
||||
@override String get viewLogs => '로그 보기';
|
||||
@@ -1441,6 +1443,8 @@ extension on TranslationsKo {
|
||||
'settings.keyboardShortcutsDescription' => '사용자 정의 키보드 단축키',
|
||||
'settings.videoPlayerNavigation' => '비디오 플레이어 탐색',
|
||||
'settings.videoPlayerNavigationDescription' => '방향 키를 사용하여 비디오 플레이어 컨트롤 탐색',
|
||||
'settings.crashReporting' => '충돌 보고',
|
||||
'settings.crashReportingDescription' => '앱 개선을 위해 충돌 보고서 전송',
|
||||
'settings.debugLogging' => '디버그 로깅',
|
||||
'settings.debugLoggingDescription' => '문제 해결을 위해 상세 로깅 활성화',
|
||||
'settings.viewLogs' => '로그 보기',
|
||||
@@ -1487,6 +1491,8 @@ extension on TranslationsKo {
|
||||
'settings.maxVolumePercent' => ({required Object percent}) => '${percent}%',
|
||||
'settings.discordRichPresence' => 'Discord Rich Presence',
|
||||
'settings.discordRichPresenceDescription' => 'Discord에서 시청 중인 콘텐츠 표시',
|
||||
'settings.autoPip' => '자동 PIP 모드',
|
||||
'settings.autoPipDescription' => '재생 중 앱을 나갈 때 자동으로 PIP 모드로 전환',
|
||||
'settings.matchContentFrameRate' => '콘텐츠 프레임 레이트 맞춤',
|
||||
'settings.matchContentFrameRateDescription' => '비디오 콘텐츠에 맞게 디스플레이 주사율을 조정하여 떨림을 줄이고 배터리를 절약합니다',
|
||||
'settings.tunneledPlayback' => 'Tunneled Playback',
|
||||
@@ -1808,6 +1814,8 @@ extension on TranslationsKo {
|
||||
'liveTv.nowPlaying' => '현재 재생 중',
|
||||
'liveTv.noPrograms' => '프로그램 데이터가 없습니다',
|
||||
'liveTv.channelNumber' => ({required Object number}) => '채널 ${number}',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.live' => '실시간',
|
||||
'liveTv.hd' => 'HD',
|
||||
'liveTv.premiere' => '신규',
|
||||
@@ -1822,8 +1830,6 @@ extension on TranslationsKo {
|
||||
'liveTv.evening' => '저녁',
|
||||
'liveTv.lateNight' => '심야 방송',
|
||||
'liveTv.whatsOn' => '지금 방송 중',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.watchChannel' => '채널 시청',
|
||||
'collections.title' => '컬렉션',
|
||||
'collections.collection' => '컬렉션',
|
||||
|
||||
@@ -270,6 +270,8 @@ class _TranslationsSettingsNl implements TranslationsSettingsEn {
|
||||
@override String get keyboardShortcutsDescription => 'Pas toetsenbord sneltoetsen aan';
|
||||
@override String get videoPlayerNavigation => 'Videospeler navigatie';
|
||||
@override String get videoPlayerNavigationDescription => 'Gebruik pijltjestoetsen om door de videospeler bediening te navigeren';
|
||||
@override String get crashReporting => 'Crashrapportage';
|
||||
@override String get crashReportingDescription => 'Crashrapporten verzenden om de app te verbeteren';
|
||||
@override String get debugLogging => 'Debug logging';
|
||||
@override String get debugLoggingDescription => 'Schakel gedetailleerde logging in voor probleemoplossing';
|
||||
@override String get viewLogs => 'Bekijk logs';
|
||||
@@ -1441,6 +1443,8 @@ extension on TranslationsNl {
|
||||
'settings.keyboardShortcutsDescription' => 'Pas toetsenbord sneltoetsen aan',
|
||||
'settings.videoPlayerNavigation' => 'Videospeler navigatie',
|
||||
'settings.videoPlayerNavigationDescription' => 'Gebruik pijltjestoetsen om door de videospeler bediening te navigeren',
|
||||
'settings.crashReporting' => 'Crashrapportage',
|
||||
'settings.crashReportingDescription' => 'Crashrapporten verzenden om de app te verbeteren',
|
||||
'settings.debugLogging' => 'Debug logging',
|
||||
'settings.debugLoggingDescription' => 'Schakel gedetailleerde logging in voor probleemoplossing',
|
||||
'settings.viewLogs' => 'Bekijk logs',
|
||||
@@ -1487,6 +1491,8 @@ extension on TranslationsNl {
|
||||
'settings.maxVolumePercent' => ({required Object percent}) => '${percent}%',
|
||||
'settings.discordRichPresence' => 'Discord Rich Presence',
|
||||
'settings.discordRichPresenceDescription' => 'Toon op Discord wat je aan het kijken bent',
|
||||
'settings.autoPip' => 'Automatische beeld-in-beeld',
|
||||
'settings.autoPipDescription' => 'Automatisch beeld-in-beeld activeren bij het verlaten van de app tijdens afspelen',
|
||||
'settings.matchContentFrameRate' => 'Inhoudsframesnelheid afstemmen',
|
||||
'settings.matchContentFrameRateDescription' => 'Pas de schermverversingssnelheid aan op de video-inhoud, vermindert haperingen en bespaart batterij',
|
||||
'settings.tunneledPlayback' => 'Tunneled Playback',
|
||||
@@ -1808,6 +1814,8 @@ extension on TranslationsNl {
|
||||
'liveTv.nowPlaying' => 'Nu aan het afspelen',
|
||||
'liveTv.noPrograms' => 'Geen programmagegevens beschikbaar',
|
||||
'liveTv.channelNumber' => ({required Object number}) => 'Kanaal ${number}',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.live' => 'LIVE',
|
||||
'liveTv.hd' => 'HD',
|
||||
'liveTv.premiere' => 'NIEUW',
|
||||
@@ -1822,8 +1830,6 @@ extension on TranslationsNl {
|
||||
'liveTv.evening' => 'Avond',
|
||||
'liveTv.lateNight' => 'Late avond',
|
||||
'liveTv.whatsOn' => 'Nu op TV',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.watchChannel' => 'Kanaal bekijken',
|
||||
'downloads.title' => 'Downloads',
|
||||
'downloads.manage' => 'Beheren',
|
||||
|
||||
@@ -270,6 +270,8 @@ class _TranslationsSettingsSv implements TranslationsSettingsEn {
|
||||
@override String get keyboardShortcutsDescription => 'Anpassa tangentbordsgenvägar';
|
||||
@override String get videoPlayerNavigation => 'Navigering i videospelaren';
|
||||
@override String get videoPlayerNavigationDescription => 'Använd piltangenter för att navigera videospelarens kontroller';
|
||||
@override String get crashReporting => 'Kraschrapportering';
|
||||
@override String get crashReportingDescription => 'Skicka kraschrapporter för att förbättra appen';
|
||||
@override String get debugLogging => 'Felsökningsloggning';
|
||||
@override String get debugLoggingDescription => 'Aktivera detaljerad loggning för felsökning';
|
||||
@override String get viewLogs => 'Visa loggar';
|
||||
@@ -1441,6 +1443,8 @@ extension on TranslationsSv {
|
||||
'settings.keyboardShortcutsDescription' => 'Anpassa tangentbordsgenvägar',
|
||||
'settings.videoPlayerNavigation' => 'Navigering i videospelaren',
|
||||
'settings.videoPlayerNavigationDescription' => 'Använd piltangenter för att navigera videospelarens kontroller',
|
||||
'settings.crashReporting' => 'Kraschrapportering',
|
||||
'settings.crashReportingDescription' => 'Skicka kraschrapporter för att förbättra appen',
|
||||
'settings.debugLogging' => 'Felsökningsloggning',
|
||||
'settings.debugLoggingDescription' => 'Aktivera detaljerad loggning för felsökning',
|
||||
'settings.viewLogs' => 'Visa loggar',
|
||||
@@ -1487,6 +1491,8 @@ extension on TranslationsSv {
|
||||
'settings.maxVolumePercent' => ({required Object percent}) => '${percent}%',
|
||||
'settings.discordRichPresence' => 'Discord Rich Presence',
|
||||
'settings.discordRichPresenceDescription' => 'Visa vad du tittar på i Discord',
|
||||
'settings.autoPip' => 'Automatisk bild-i-bild',
|
||||
'settings.autoPipDescription' => 'Aktivera bild-i-bild automatiskt när appen lämnas under uppspelning',
|
||||
'settings.matchContentFrameRate' => 'Matcha innehållets bildfrekvens',
|
||||
'settings.matchContentFrameRateDescription' => 'Justera skärmens uppdateringsfrekvens för att matcha videoinnehållet, minskar hackighet och sparar batteri',
|
||||
'settings.tunneledPlayback' => 'Tunneled Playback',
|
||||
@@ -1808,6 +1814,8 @@ extension on TranslationsSv {
|
||||
'liveTv.nowPlaying' => 'Spelas nu',
|
||||
'liveTv.noPrograms' => 'Ingen programdata tillgänglig',
|
||||
'liveTv.channelNumber' => ({required Object number}) => 'Kanal ${number}',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.live' => 'LIVE',
|
||||
'liveTv.hd' => 'HD',
|
||||
'liveTv.premiere' => 'NY',
|
||||
@@ -1822,8 +1830,6 @@ extension on TranslationsSv {
|
||||
'liveTv.evening' => 'Kväll',
|
||||
'liveTv.lateNight' => 'Sen kväll',
|
||||
'liveTv.whatsOn' => 'På TV nu',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.watchChannel' => 'Titta på kanal',
|
||||
'downloads.title' => 'Nedladdningar',
|
||||
'downloads.manage' => 'Hantera',
|
||||
|
||||
@@ -270,6 +270,8 @@ class _TranslationsSettingsZh implements TranslationsSettingsEn {
|
||||
@override String get keyboardShortcutsDescription => '自定义键盘快捷键';
|
||||
@override String get videoPlayerNavigation => '视频播放器导航';
|
||||
@override String get videoPlayerNavigationDescription => '使用方向键导航视频播放器控件';
|
||||
@override String get crashReporting => '崩溃报告';
|
||||
@override String get crashReportingDescription => '发送崩溃报告以帮助改进应用';
|
||||
@override String get debugLogging => '调试日志';
|
||||
@override String get debugLoggingDescription => '启用详细日志记录以便故障排除';
|
||||
@override String get viewLogs => '查看日志';
|
||||
@@ -1441,6 +1443,8 @@ extension on TranslationsZh {
|
||||
'settings.keyboardShortcutsDescription' => '自定义键盘快捷键',
|
||||
'settings.videoPlayerNavigation' => '视频播放器导航',
|
||||
'settings.videoPlayerNavigationDescription' => '使用方向键导航视频播放器控件',
|
||||
'settings.crashReporting' => '崩溃报告',
|
||||
'settings.crashReportingDescription' => '发送崩溃报告以帮助改进应用',
|
||||
'settings.debugLogging' => '调试日志',
|
||||
'settings.debugLoggingDescription' => '启用详细日志记录以便故障排除',
|
||||
'settings.viewLogs' => '查看日志',
|
||||
@@ -1487,6 +1491,8 @@ extension on TranslationsZh {
|
||||
'settings.maxVolumePercent' => ({required Object percent}) => '${percent}%',
|
||||
'settings.discordRichPresence' => 'Discord 动态状态',
|
||||
'settings.discordRichPresenceDescription' => '在 Discord 上显示您正在观看的内容',
|
||||
'settings.autoPip' => '自动画中画',
|
||||
'settings.autoPipDescription' => '在播放期间离开应用时自动进入画中画模式',
|
||||
'settings.matchContentFrameRate' => '匹配内容帧率',
|
||||
'settings.matchContentFrameRateDescription' => '调整显示刷新率以匹配视频内容,减少画面抖动并节省电量',
|
||||
'settings.tunneledPlayback' => 'Tunneled Playback',
|
||||
@@ -1808,6 +1814,8 @@ extension on TranslationsZh {
|
||||
'liveTv.nowPlaying' => '正在播放',
|
||||
'liveTv.noPrograms' => '没有可用的节目数据',
|
||||
'liveTv.channelNumber' => ({required Object number}) => '频道 ${number}',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.live' => '直播',
|
||||
'liveTv.hd' => '高清',
|
||||
'liveTv.premiere' => '新',
|
||||
@@ -1822,8 +1830,6 @@ extension on TranslationsZh {
|
||||
'liveTv.evening' => '晚上',
|
||||
'liveTv.lateNight' => '深夜',
|
||||
'liveTv.whatsOn' => '正在播出',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'liveTv.watchChannel' => '观看频道',
|
||||
'downloads.title' => '下载',
|
||||
'downloads.manage' => '管理',
|
||||
|
||||
@@ -151,6 +151,8 @@
|
||||
"keyboardShortcutsDescription": "Anpassa tangentbordsgenvägar",
|
||||
"videoPlayerNavigation": "Navigering i videospelaren",
|
||||
"videoPlayerNavigationDescription": "Använd piltangenter för att navigera videospelarens kontroller",
|
||||
"crashReporting": "Kraschrapportering",
|
||||
"crashReportingDescription": "Skicka kraschrapporter för att förbättra appen",
|
||||
"debugLogging": "Felsökningsloggning",
|
||||
"debugLoggingDescription": "Aktivera detaljerad loggning för felsökning",
|
||||
"viewLogs": "Visa loggar",
|
||||
|
||||
@@ -151,6 +151,8 @@
|
||||
"keyboardShortcutsDescription": "自定义键盘快捷键",
|
||||
"videoPlayerNavigation": "视频播放器导航",
|
||||
"videoPlayerNavigationDescription": "使用方向键导航视频播放器控件",
|
||||
"crashReporting": "崩溃报告",
|
||||
"crashReportingDescription": "发送崩溃报告以帮助改进应用",
|
||||
"debugLogging": "调试日志",
|
||||
"debugLoggingDescription": "启用详细日志记录以便故障排除",
|
||||
"viewLogs": "查看日志",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
@@ -5,6 +7,7 @@ import 'dart:io' show Platform;
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'screens/main_screen.dart';
|
||||
import 'screens/auth_screen.dart';
|
||||
import 'services/storage_service.dart';
|
||||
@@ -48,6 +51,7 @@ import 'focus/input_mode_tracker.dart';
|
||||
import 'focus/key_event_utils.dart';
|
||||
import 'package:intl/date_symbol_data_local.dart';
|
||||
import 'utils/navigation_transitions.dart';
|
||||
import 'utils/log_redaction_manager.dart';
|
||||
|
||||
// Workaround for Flutter bug #177992: iPadOS 26.1+ misinterprets fake touch events
|
||||
// at (0,0) as barrier taps, causing modals to dismiss immediately.
|
||||
@@ -70,6 +74,17 @@ void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
_installZeroOffsetPointerGuard(); // Workaround for iPadOS 26.1+ modal dismissal bug
|
||||
|
||||
await SentryFlutter.init(
|
||||
(options) {
|
||||
options.dsn = 'https://7aa61595518d4770a4d5ea412982dcd7@bugs.plezy.app/1';
|
||||
options.sendDefaultPii = true;
|
||||
options.tracesSampleRate = 0;
|
||||
options.anrEnabled = true;
|
||||
options.anrTimeoutInterval = const Duration(seconds: 5);
|
||||
options.attachStacktrace = true;
|
||||
options.beforeSend = _beforeSend;
|
||||
},
|
||||
appRunner: () async {
|
||||
// Initialize settings first to get saved locale
|
||||
final settings = await SettingsService.getInstance();
|
||||
final savedLocale = settings.getAppLocale();
|
||||
@@ -133,6 +148,41 @@ void main() async {
|
||||
_registerShaderLicenses();
|
||||
|
||||
runApp(const MainApp());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<SentryEvent?> _beforeSend(SentryEvent event, Hint hint) {
|
||||
// Drop event if user opted out of crash reporting
|
||||
final instance = SettingsService.instanceOrNull;
|
||||
if (instance != null && !instance.getCrashReporting()) return null;
|
||||
|
||||
// Scrub Plex tokens and server URLs from exception messages
|
||||
var exceptions = event.exceptions;
|
||||
if (exceptions != null) {
|
||||
exceptions = exceptions.map((e) {
|
||||
final value = e.value;
|
||||
if (value != null) {
|
||||
return e.copyWith(value: LogRedactionManager.redact(value));
|
||||
}
|
||||
return e;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// Scrub breadcrumb messages and data
|
||||
var breadcrumbs = event.breadcrumbs;
|
||||
if (breadcrumbs != null) {
|
||||
breadcrumbs = breadcrumbs.map((b) {
|
||||
final message = b.message;
|
||||
final data = b.data;
|
||||
return b.copyWith(
|
||||
message: message != null ? LogRedactionManager.redact(message) : null,
|
||||
data: data?.map((k, v) => MapEntry(k, v is String ? LogRedactionManager.redact(v) : v)),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return event.copyWith(exceptions: exceptions, breadcrumbs: breadcrumbs);
|
||||
}
|
||||
|
||||
void _registerShaderLicenses() {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:plezy/widgets/app_icon.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import '../../models/hotkey_model.dart';
|
||||
@@ -97,6 +99,7 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
|
||||
static const _kDownloadOnWifiOnly = 'download_on_wifi_only';
|
||||
static const _kVideoPlayerControls = 'video_player_controls';
|
||||
static const _kVideoPlayerNavigation = 'video_player_navigation';
|
||||
static const _kCrashReporting = 'crash_reporting';
|
||||
static const _kDebugLogging = 'debug_logging';
|
||||
static const _kViewLogs = 'view_logs';
|
||||
static const _kClearCache = 'clear_cache';
|
||||
@@ -107,6 +110,7 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
|
||||
late final bool _keyboardShortcutsSupported = KeyboardShortcutsService.isPlatformSupported();
|
||||
bool _isLoading = true;
|
||||
|
||||
bool _crashReporting = true;
|
||||
bool _enableDebugLogging = false;
|
||||
bool _enableHardwareDecoding = true;
|
||||
int _bufferSize = 0;
|
||||
@@ -183,6 +187,7 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_crashReporting = _settingsService.getCrashReporting();
|
||||
_enableDebugLogging = _settingsService.getEnableDebugLogging();
|
||||
_enableHardwareDecoding = _settingsService.getEnableHardwareDecoding();
|
||||
_bufferSize = _settingsService.getBufferSize();
|
||||
@@ -490,7 +495,7 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
|
||||
await _settingsService.setEnableHardwareDecoding(value);
|
||||
},
|
||||
),
|
||||
if (Platform.isAndroid)
|
||||
if (Platform.isAndroid || Platform.isIOS)
|
||||
SwitchListTile(
|
||||
focusNode: _focusTracker.get(_kAutoPip),
|
||||
secondary: const AppIcon(Symbols.picture_in_picture_alt_rounded, fill: 1),
|
||||
@@ -935,6 +940,19 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
SwitchListTile(
|
||||
focusNode: _focusTracker.get(_kCrashReporting),
|
||||
secondary: const AppIcon(Symbols.monitoring_rounded, fill: 1),
|
||||
title: Text(t.settings.crashReporting),
|
||||
subtitle: Text(t.settings.crashReportingDescription),
|
||||
value: _crashReporting,
|
||||
onChanged: (value) async {
|
||||
setState(() {
|
||||
_crashReporting = value;
|
||||
});
|
||||
await _settingsService.setCrashReporting(value);
|
||||
},
|
||||
),
|
||||
SwitchListTile(
|
||||
focusNode: _focusTracker.get(_kDebugLogging),
|
||||
secondary: const AppIcon(Symbols.bug_report_rounded, fill: 1),
|
||||
@@ -974,6 +992,17 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
|
||||
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
|
||||
onTap: () => _showResetSettingsDialog(),
|
||||
),
|
||||
if (kDebugMode)
|
||||
ListTile(
|
||||
leading: const AppIcon(Symbols.error_rounded, fill: 1),
|
||||
title: const Text('Test Sentry'),
|
||||
subtitle: const Text('Send a test error to Bugsink'),
|
||||
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
|
||||
onTap: () {
|
||||
Sentry.captureException(Exception('Sentry test from settings'));
|
||||
showSnackBar(context, 'Test error sent to Sentry');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:os_media_controls/os_media_controls.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
@@ -581,6 +582,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
_playbackRestartSubscription = player!.streams.playbackRestart.listen((_) async {
|
||||
if (!_hasFirstFrame.value) {
|
||||
_hasFirstFrame.value = true;
|
||||
Sentry.addBreadcrumb(Breadcrumb(message: 'First frame ready', category: 'player'));
|
||||
|
||||
// Apply frame rate matching on Android if enabled
|
||||
if (Platform.isAndroid && settingsService.getMatchContentFrameRate()) {
|
||||
@@ -648,6 +650,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
// Set MPV video-sync mode for smoother playback when display is synced
|
||||
await player!.setProperty('video-sync', 'display-tempo');
|
||||
|
||||
Sentry.addBreadcrumb(Breadcrumb(message: 'Frame rate matching: ${fps}fps', category: 'player'));
|
||||
appLogger.d('Frame rate matching: Set display to ${fps}fps (duration: ${durationMs}ms)');
|
||||
} catch (e) {
|
||||
appLogger.w('Failed to apply frame rate matching', error: e);
|
||||
@@ -661,6 +664,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
try {
|
||||
await player!.clearVideoFrameRate();
|
||||
await player!.setProperty('video-sync', 'audio');
|
||||
Sentry.addBreadcrumb(Breadcrumb(message: 'Frame rate matching cleared', category: 'player'));
|
||||
appLogger.d('Frame rate matching: Cleared, restored default display mode');
|
||||
} catch (e) {
|
||||
appLogger.d('Failed to clear frame rate matching', error: e);
|
||||
@@ -1764,6 +1768,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
}
|
||||
}
|
||||
|
||||
Sentry.addBreadcrumb(Breadcrumb(message: 'Player dispose', category: 'player'));
|
||||
player?.dispose();
|
||||
if (_activeRatingKey == widget.metadata.ratingKey) {
|
||||
_activeRatingKey = null;
|
||||
|
||||
@@ -19,6 +19,7 @@ enum EpisodePosterMode { seriesPoster, seasonPoster, episodeThumbnail }
|
||||
class SettingsService extends BaseSharedPreferencesService {
|
||||
static const String _keyThemeMode = 'theme_mode';
|
||||
static const String _keyEnableDebugLogging = 'enable_debug_logging';
|
||||
static const String _keyCrashReporting = 'crash_reporting';
|
||||
static const String _keyBufferSize = 'buffer_size';
|
||||
static const String _keyBufferSizeMigratedToAuto = 'buffer_size_migrated_to_auto';
|
||||
static const String _keyKeyboardShortcuts = 'keyboard_shortcuts';
|
||||
@@ -85,10 +86,16 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
|
||||
SettingsService._();
|
||||
|
||||
static Future<SettingsService> getInstance() {
|
||||
return BaseSharedPreferencesService.initializeInstance(() => SettingsService._());
|
||||
static SettingsService? _cachedInstance;
|
||||
|
||||
static Future<SettingsService> getInstance() async {
|
||||
_cachedInstance ??= await BaseSharedPreferencesService.initializeInstance(() => SettingsService._());
|
||||
return _cachedInstance!;
|
||||
}
|
||||
|
||||
/// Synchronous access to the singleton, or null if not yet initialized.
|
||||
static SettingsService? get instanceOrNull => _cachedInstance;
|
||||
|
||||
/// Generic helper to get an enum value from preferences
|
||||
T _getEnumValue<T extends Enum>(String key, List<T> values, T defaultValue) {
|
||||
final stored = prefs.getString(key);
|
||||
@@ -121,6 +128,15 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
return prefs.getBool(_keyEnableDebugLogging) ?? false;
|
||||
}
|
||||
|
||||
// Crash Reporting
|
||||
Future<void> setCrashReporting(bool enabled) async {
|
||||
await prefs.setBool(_keyCrashReporting, enabled);
|
||||
}
|
||||
|
||||
bool getCrashReporting() {
|
||||
return prefs.getBool(_keyCrashReporting) ?? true; // Default enabled
|
||||
}
|
||||
|
||||
// Buffer Size (in MB)
|
||||
Future<void> setBufferSize(int sizeInMB) async {
|
||||
await prefs.setInt(_keyBufferSize, sizeInMB);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <os_media_controls/os_media_controls_plugin.h>
|
||||
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
|
||||
#include <sentry_flutter/sentry_flutter_plugin.h>
|
||||
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
||||
#include <universal_gamepad/gamepad_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
@@ -20,6 +21,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
|
||||
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) sentry_flutter_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin");
|
||||
sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar);
|
||||
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
|
||||
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
os_media_controls
|
||||
screen_retriever_linux
|
||||
sentry_flutter
|
||||
sqlite3_flutter_libs
|
||||
universal_gamepad
|
||||
url_launcher_linux
|
||||
|
||||
@@ -15,6 +15,7 @@ import os_media_controls
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import screen_retriever_macos
|
||||
import sentry_flutter
|
||||
import shared_preferences_foundation
|
||||
import sqflite_darwin
|
||||
import sqlite3_flutter_libs
|
||||
@@ -34,6 +35,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
|
||||
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
|
||||
|
||||
@@ -22,6 +22,11 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- screen_retriever_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- Sentry/HybridSDK (8.46.0)
|
||||
- sentry_flutter (8.14.2):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- Sentry/HybridSDK (= 8.46.0)
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@@ -75,6 +80,7 @@ DEPENDENCIES:
|
||||
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`)
|
||||
- sentry_flutter (from `Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos`)
|
||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`)
|
||||
- sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/darwin`)
|
||||
@@ -85,6 +91,7 @@ DEPENDENCIES:
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- Sentry
|
||||
- Sparkle
|
||||
- sqlite3
|
||||
|
||||
@@ -111,6 +118,8 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||
screen_retriever_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos
|
||||
sentry_flutter:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos
|
||||
shared_preferences_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
|
||||
sqflite_darwin:
|
||||
@@ -138,6 +147,8 @@ SPEC CHECKSUMS:
|
||||
package_info_plus: f0052d280d17aa382b932f399edf32507174e870
|
||||
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
|
||||
screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f
|
||||
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
|
||||
sentry_flutter: 27892878729f42701297c628eb90e7c6529f3684
|
||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||
Sparkle: f4355f9ebbe9b7d932df4980d70f13922ac97b2a
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
|
||||
@@ -1008,6 +1008,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
sentry:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sentry
|
||||
sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.14.2"
|
||||
sentry_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sentry_flutter
|
||||
sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.14.2"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -56,6 +56,7 @@ dependencies:
|
||||
mobile_scanner: ^6.0.2
|
||||
android_intent_plus: ^5.0.2
|
||||
background_downloader: ^9.5.2
|
||||
sentry_flutter: ^8.12.0
|
||||
auto_updater:
|
||||
git:
|
||||
url: https://github.com/edde746/auto_updater
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
ice.plezy.app {
|
||||
reverse_proxy relay:8080
|
||||
}
|
||||
|
||||
bugs.plezy.app {
|
||||
reverse_proxy bugsink:8000 {
|
||||
header_up X-Real-IP {remote_host}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
FROM golang:1.22-alpine AS build
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /relay .
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /relay /relay
|
||||
VOLUME /data/logs
|
||||
EXPOSE 8080
|
||||
ENV GOMEMLIMIT=64MiB
|
||||
ENTRYPOINT ["/relay"]
|
||||
@@ -0,0 +1,36 @@
|
||||
services:
|
||||
relay:
|
||||
build: .
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- logs:/data/logs
|
||||
expose:
|
||||
- "8080"
|
||||
|
||||
caddy:
|
||||
image: caddy:2-alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
|
||||
bugsink:
|
||||
image: bugsink/bugsink
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
environment:
|
||||
SECRET_KEY: ${BUGSINK_SECRET_KEY}
|
||||
CREATE_SUPERUSER: ${BUGSINK_SUPERUSER}
|
||||
PORT: 8000
|
||||
BEHIND_HTTPS_PROXY: "True"
|
||||
expose:
|
||||
- "8000"
|
||||
|
||||
volumes:
|
||||
logs:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
+1
-9
@@ -2,12 +2,4 @@ module github.com/edde746/plezy-relay
|
||||
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
golang.org/x/crypto v0.31.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
)
|
||||
require github.com/gorilla/websocket v1.5.3
|
||||
|
||||
@@ -1,8 +1,2 @@
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
|
||||
+56
-37
@@ -9,12 +9,14 @@ import (
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -29,12 +31,15 @@ const (
|
||||
pingInterval = 30 * time.Second
|
||||
maxMessageSize = 64 * 1024
|
||||
maxLogSize = 1 * 1024 * 1024 // 1MB
|
||||
logMaxAge = 7 * 24 * time.Hour
|
||||
logMaxAge = 3 * 24 * time.Hour
|
||||
logIDLength = 5
|
||||
logRateInterval = 1 * time.Minute
|
||||
maxLogEntries = 500
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(r *http.Request) bool { return true },
|
||||
}
|
||||
|
||||
@@ -150,21 +155,36 @@ func (r *Room) sendTo(targetID string, msg serverMsg) bool {
|
||||
// --- Log store ---
|
||||
|
||||
type logEntry struct {
|
||||
Text string
|
||||
Size int
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
type logStore struct {
|
||||
entries map[string]logEntry
|
||||
rateLimit map[string]time.Time // IP -> last upload time
|
||||
dir string
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func newLogStore() *logStore {
|
||||
return &logStore{
|
||||
func newLogStore(dir string) *logStore {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
log.Fatalf("failed to create log dir %s: %v", dir, err)
|
||||
}
|
||||
ls := &logStore{
|
||||
entries: make(map[string]logEntry),
|
||||
rateLimit: make(map[string]time.Time),
|
||||
dir: dir,
|
||||
}
|
||||
// Clean orphaned files from prior runs
|
||||
files, _ := os.ReadDir(dir)
|
||||
for _, f := range files {
|
||||
os.Remove(filepath.Join(dir, f.Name()))
|
||||
}
|
||||
return ls
|
||||
}
|
||||
|
||||
func (ls *logStore) filePath(id string) string {
|
||||
return filepath.Join(ls.dir, id+".log")
|
||||
}
|
||||
|
||||
const logIDChars = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
@@ -184,6 +204,7 @@ func (ls *logStore) cleanup() {
|
||||
now := time.Now()
|
||||
for id, entry := range ls.entries {
|
||||
if now.After(entry.ExpiresAt) {
|
||||
os.Remove(ls.filePath(id))
|
||||
delete(ls.entries, id)
|
||||
}
|
||||
}
|
||||
@@ -202,8 +223,8 @@ type Server struct {
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func newServer() *Server {
|
||||
s := &Server{rooms: make(map[string]*Room), logs: newLogStore()}
|
||||
func newServer(logDir string) *Server {
|
||||
s := &Server{rooms: make(map[string]*Room), logs: newLogStore(logDir)}
|
||||
go s.cleanupLoop()
|
||||
return s
|
||||
}
|
||||
@@ -271,10 +292,24 @@ func (s *Server) handlePostLogs(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
s.logs.mu.Lock()
|
||||
if len(s.logs.entries) >= maxLogEntries {
|
||||
s.logs.mu.Unlock()
|
||||
http.Error(w, "Log store full", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
s.logs.mu.Unlock()
|
||||
|
||||
id := generateLogID()
|
||||
if err := os.WriteFile(s.logs.filePath(id), body, 0644); err != nil {
|
||||
log.Printf("logs: failed to write %s: %v", id, err)
|
||||
http.Error(w, "Failed to store log", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s.logs.mu.Lock()
|
||||
s.logs.entries[id] = logEntry{
|
||||
Text: string(body),
|
||||
Size: len(body),
|
||||
ExpiresAt: time.Now().Add(logMaxAge),
|
||||
}
|
||||
s.logs.mu.Unlock()
|
||||
@@ -306,8 +341,15 @@ func (s *Server) handleGetLogs(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(s.logs.filePath(id))
|
||||
if err != nil {
|
||||
http.Error(w, "Not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
w.Write([]byte(entry.Text))
|
||||
w.Header().Set("Content-Length", strconv.Itoa(entry.Size))
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func (s *Server) sendError(conn *websocket.Conn, code, message string) {
|
||||
@@ -485,11 +527,11 @@ func (s *Server) handleWS(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
dev := flag.Bool("dev", false, "Run in development mode (plain HTTP on :8080)")
|
||||
host := flag.String("host", "ice.plezy.app", "Hostname for TLS autocert")
|
||||
addr := flag.String("addr", ":8080", "Listen address")
|
||||
logDir := flag.String("log-dir", "/data/logs", "Directory for log file storage")
|
||||
flag.Parse()
|
||||
|
||||
srv := newServer()
|
||||
srv := newServer(*logDir)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/relay", srv.handleWS)
|
||||
@@ -500,29 +542,6 @@ func main() {
|
||||
mux.HandleFunc("/logs", srv.handlePostLogs)
|
||||
mux.HandleFunc("/logs/", srv.handleGetLogs)
|
||||
|
||||
if *dev {
|
||||
log.Println("Starting dev server on :8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", mux))
|
||||
} else {
|
||||
certManager := autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist(*host),
|
||||
Cache: autocert.DirCache("/var/lib/plezy-relay/certs"),
|
||||
}
|
||||
|
||||
server := &http.Server{
|
||||
Addr: ":443",
|
||||
Handler: mux,
|
||||
TLSConfig: certManager.TLSConfig(),
|
||||
}
|
||||
|
||||
// HTTP challenge server for Let's Encrypt
|
||||
go func() {
|
||||
log.Println("Starting HTTP challenge server on :80")
|
||||
log.Fatal(http.ListenAndServe(":80", certManager.HTTPHandler(nil)))
|
||||
}()
|
||||
|
||||
log.Printf("Starting relay server on :443 (host=%s)", *host)
|
||||
log.Fatal(server.ListenAndServeTLS("", ""))
|
||||
}
|
||||
log.Printf("Starting relay server on %s", *addr)
|
||||
log.Fatal(http.ListenAndServe(*addr, mux))
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||
#include <os_media_controls/os_media_controls_plugin_c_api.h>
|
||||
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
|
||||
#include <sentry_flutter/sentry_flutter_plugin.h>
|
||||
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
||||
#include <universal_gamepad/gamepad_plugin_c_api.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
@@ -24,6 +25,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
registry->GetRegistrarForPlugin("OsMediaControlsPluginCApi"));
|
||||
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
|
||||
SentryFlutterPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("SentryFlutterPlugin"));
|
||||
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
|
||||
GamepadPluginCApiRegisterWithRegistrar(
|
||||
|
||||
@@ -7,6 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
connectivity_plus
|
||||
os_media_controls
|
||||
screen_retriever_windows
|
||||
sentry_flutter
|
||||
sqlite3_flutter_libs
|
||||
universal_gamepad
|
||||
url_launcher_windows
|
||||
|
||||
Reference in New Issue
Block a user