diff --git a/.env.example b/.env.example index 6a88cb992..888cbe6b8 100644 --- a/.env.example +++ b/.env.example @@ -1,16 +1,16 @@ # The format: # SPOTIFY_SECRETS=clintId1:clientSecret1,clientId2:clientSecret2 -SPOTIFY_SECRETS= +SPOTIFY_SECRETS=$SPOTIFY_SECRETS # 0 or 1 # 0 = disable # 1 = enable -ENABLE_UPDATE_CHECK= +ENABLE_UPDATE_CHECK=$ENABLE_UPDATE_CHECK -LASTFM_API_KEY= -LASTFM_API_SECRET= +LASTFM_API_KEY=$LASTFM_API_KEY +LASTFM_API_SECRET=$LASTFM_API_SECRET # Release channel. Can be: nightly, stable -RELEASE_CHANNEL= +RELEASE_CHANNEL=$RELEASE_CHANNEL -HIDE_DONATIONS= +HIDE_DONATIONS=$HIDE_DONATIONS diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml index db158029a..23e5cc74a 100644 --- a/.github/workflows/pr-lint.yml +++ b/.github/workflows/pr-lint.yml @@ -4,7 +4,7 @@ on: pull_request: env: - FLUTTER_VERSION: 3.22.2 + FLUTTER_VERSION: 3.24.5 jobs: lint: @@ -17,18 +17,23 @@ jobs: with: flutter-version: ${{ env.FLUTTER_VERSION }} + - name: Dummy Envs + run: | + envsubst < .env.example > .env + env: + SPOTIFY_SECRETS: xxx:xxx + ENABLE_UPDATE_CHECK: true + LASTFM_API_KEY: xxx + LASTFM_API_SECRET: xxx + RELEASE_CHANNEL: nightly + HIDE_DONATIONS: 0 + + - name: Configure repo run: | flutter pub get - echo '${{ secrets.DOTENV_NIGHTLY }}' > .env dart run build_runner build --delete-conflicting-outputs - name: Lint Dart files run: | - dart analyze --no-fatal-warnings - - - name: Lint translations & config files - run: | - npm install -g @prantlf/jsonlint - jsonlint -q -D --enforce-double-quotes ./lib/l10n/*.arb - jsonlint -q -D --enforce-double-quotes -T .vscode/*.json \ No newline at end of file + dart analyze --no-fatal-warnings \ No newline at end of file diff --git a/Makefile b/Makefile index 1add46b0e..25ac3a6dd 100644 --- a/Makefile +++ b/Makefile @@ -42,4 +42,7 @@ apk: mv build/app/outputs/apk/release/app-release.apk build/Spotube-android-all-arch.apk gensums: - sh -c scripts/gensums.sh \ No newline at end of file + sh -c scripts/gensums.sh + +migrate: + dart run drift_dev make-migrations \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index d5b904ccc..1eda286e5 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -39,3 +39,4 @@ analyzer: - "**.g.dart" - "**.gr.dart" - "**/generated_plugin_registrant.dart" + - test/**/*.dart diff --git a/drift_schemas/app_db/drift_schema_v3.json b/drift_schemas/app_db/drift_schema_v3.json new file mode 100644 index 000000000..93e0ef1b0 --- /dev/null +++ b/drift_schemas/app_db/drift_schema_v3.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"authentication_table","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"cookie","getter_name":"cookie","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"EncryptedTextConverter()","dart_type_name":"DecryptedText"}},{"name":"access_token","getter_name":"accessToken","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"EncryptedTextConverter()","dart_type_name":"DecryptedText"}},{"name":"expiration","getter_name":"expiration","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":1,"references":[],"type":"table","data":{"name":"blacklist_table","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"element_type","getter_name":"elementType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(BlacklistedType.values)","dart_type_name":"BlacklistedType"}},{"name":"element_id","getter_name":"elementId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":2,"references":[],"type":"table","data":{"name":"preferences_table","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"audio_quality","getter_name":"audioQuality","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"Constant(SourceQualities.high.name)","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(SourceQualities.values)","dart_type_name":"SourceQualities"}},{"name":"album_color_sync","getter_name":"albumColorSync","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"album_color_sync\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"album_color_sync\" IN (0, 1))"},"default_dart":"const Constant(true)","default_client_dart":null,"dsl_features":[]},{"name":"amoled_dark_theme","getter_name":"amoledDarkTheme","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"amoled_dark_theme\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"amoled_dark_theme\" IN (0, 1))"},"default_dart":"const Constant(false)","default_client_dart":null,"dsl_features":[]},{"name":"check_update","getter_name":"checkUpdate","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"check_update\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"check_update\" IN (0, 1))"},"default_dart":"const Constant(true)","default_client_dart":null,"dsl_features":[]},{"name":"normalize_audio","getter_name":"normalizeAudio","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"normalize_audio\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"normalize_audio\" IN (0, 1))"},"default_dart":"const Constant(false)","default_client_dart":null,"dsl_features":[]},{"name":"show_system_tray_icon","getter_name":"showSystemTrayIcon","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"show_system_tray_icon\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"show_system_tray_icon\" IN (0, 1))"},"default_dart":"const Constant(false)","default_client_dart":null,"dsl_features":[]},{"name":"system_title_bar","getter_name":"systemTitleBar","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"system_title_bar\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"system_title_bar\" IN (0, 1))"},"default_dart":"const Constant(false)","default_client_dart":null,"dsl_features":[]},{"name":"skip_non_music","getter_name":"skipNonMusic","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"skip_non_music\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"skip_non_music\" IN (0, 1))"},"default_dart":"const Constant(false)","default_client_dart":null,"dsl_features":[]},{"name":"close_behavior","getter_name":"closeBehavior","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"Constant(CloseBehavior.close.name)","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(CloseBehavior.values)","dart_type_name":"CloseBehavior"}},{"name":"accent_color_scheme","getter_name":"accentColorScheme","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const Constant(\"Blue:0xFF2196F3\")","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SpotubeColorConverter()","dart_type_name":"SpotubeColor"}},{"name":"layout_mode","getter_name":"layoutMode","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"Constant(LayoutMode.adaptive.name)","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(LayoutMode.values)","dart_type_name":"LayoutMode"}},{"name":"locale","getter_name":"locale","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const Constant('{\"languageCode\":\"system\",\"countryCode\":\"system\"}')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const LocaleConverter()","dart_type_name":"Locale"}},{"name":"market","getter_name":"market","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"Constant(Market.US.name)","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(Market.values)","dart_type_name":"Market"}},{"name":"search_mode","getter_name":"searchMode","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"Constant(SearchMode.youtube.name)","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(SearchMode.values)","dart_type_name":"SearchMode"}},{"name":"download_location","getter_name":"downloadLocation","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const Constant(\"\")","default_client_dart":null,"dsl_features":[]},{"name":"local_library_location","getter_name":"localLibraryLocation","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const Constant(\"\")","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const StringListConverter()","dart_type_name":"List"}},{"name":"piped_instance","getter_name":"pipedInstance","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const Constant(\"https://pipedapi.kavin.rocks\")","default_client_dart":null,"dsl_features":[]},{"name":"invidious_instance","getter_name":"invidiousInstance","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const Constant(\"https://inv.nadeko.net\")","default_client_dart":null,"dsl_features":[]},{"name":"theme_mode","getter_name":"themeMode","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"Constant(ThemeMode.system.name)","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(ThemeMode.values)","dart_type_name":"ThemeMode"}},{"name":"audio_source","getter_name":"audioSource","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"Constant(AudioSource.youtube.name)","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(AudioSource.values)","dart_type_name":"AudioSource"}},{"name":"stream_music_codec","getter_name":"streamMusicCodec","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"Constant(SourceCodecs.weba.name)","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(SourceCodecs.values)","dart_type_name":"SourceCodecs"}},{"name":"download_music_codec","getter_name":"downloadMusicCodec","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"Constant(SourceCodecs.m4a.name)","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(SourceCodecs.values)","dart_type_name":"SourceCodecs"}},{"name":"discord_presence","getter_name":"discordPresence","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"discord_presence\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"discord_presence\" IN (0, 1))"},"default_dart":"const Constant(true)","default_client_dart":null,"dsl_features":[]},{"name":"endless_playback","getter_name":"endlessPlayback","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"endless_playback\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"endless_playback\" IN (0, 1))"},"default_dart":"const Constant(true)","default_client_dart":null,"dsl_features":[]},{"name":"enable_connect","getter_name":"enableConnect","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"enable_connect\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"enable_connect\" IN (0, 1))"},"default_dart":"const Constant(false)","default_client_dart":null,"dsl_features":[]},{"name":"cache_music","getter_name":"cacheMusic","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"cache_music\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"cache_music\" IN (0, 1))"},"default_dart":"const Constant(true)","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":3,"references":[],"type":"table","data":{"name":"scrobbler_table","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"currentDateAndTime","default_client_dart":null,"dsl_features":[]},{"name":"username","getter_name":"username","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"password_hash","getter_name":"passwordHash","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"EncryptedTextConverter()","dart_type_name":"DecryptedText"}}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":4,"references":[],"type":"table","data":{"name":"skip_segment_table","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"start","getter_name":"start","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"end","getter_name":"end","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"track_id","getter_name":"trackId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"currentDateAndTime","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":5,"references":[],"type":"table","data":{"name":"source_match_table","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"track_id","getter_name":"trackId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_id","getter_name":"sourceId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"Constant(SourceType.youtube.name)","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(SourceType.values)","dart_type_name":"SourceType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"currentDateAndTime","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":6,"references":[],"type":"table","data":{"name":"audio_player_state_table","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"playing","getter_name":"playing","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"playing\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"playing\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"loop_mode","getter_name":"loopMode","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(PlaylistMode.values)","dart_type_name":"PlaylistMode"}},{"name":"shuffled","getter_name":"shuffled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"shuffled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"shuffled\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"collections","getter_name":"collections","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const StringListConverter()","dart_type_name":"List"}}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":7,"references":[6],"type":"table","data":{"name":"playlist_table","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"audio_player_state_id","getter_name":"audioPlayerStateId","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES audio_player_state_table (id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES audio_player_state_table (id)"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"index","getter_name":"index","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":8,"references":[7],"type":"table","data":{"name":"playlist_media_table","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"playlist_id","getter_name":"playlistId","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES playlist_table (id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES playlist_table (id)"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"uri","getter_name":"uri","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"extras","getter_name":"extras","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const MapTypeConverter()","dart_type_name":"Map"}},{"name":"http_headers","getter_name":"httpHeaders","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const MapTypeConverter()","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":9,"references":[],"type":"table","data":{"name":"history_table","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"currentDateAndTime","default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumNameConverter(HistoryEntryType.values)","dart_type_name":"HistoryEntryType"}},{"name":"item_id","getter_name":"itemId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const MapTypeConverter()","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":10,"references":[],"type":"table","data":{"name":"lyrics_table","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"track_id","getter_name":"trackId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"SubtitleTypeConverter()","dart_type_name":"SubtitleSimple"}}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":11,"references":[1],"type":"index","data":{"on":1,"name":"unique_blacklist","sql":null,"unique":true,"columns":["element_type","element_id"]}},{"id":12,"references":[5],"type":"index","data":{"on":5,"name":"uniq_track_match","sql":null,"unique":true,"columns":["track_id","source_id","source_type"]}}]} \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2d570cbc2..104ff7676 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -58,7 +58,7 @@ PODS: - flutter_inappwebview_ios/Core (0.0.1): - Flutter - OrderedSet (~> 6.0.3) - - flutter_native_splash (0.0.1): + - flutter_native_splash (2.4.3): - Flutter - flutter_secure_storage (6.0.0): - Flutter @@ -74,6 +74,8 @@ PODS: - Flutter - metadata_god (0.0.1): - Flutter + - open_file_ios (0.0.1): + - Flutter - OrderedSet (6.0.3) - package_info_plus (0.4.5): - Flutter @@ -88,21 +90,24 @@ PODS: - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - - sqflite (0.0.3): + - sqflite_darwin (0.0.4): - Flutter - FlutterMacOS - - "sqlite3 (3.46.0+1)": - - "sqlite3/common (= 3.46.0+1)" - - "sqlite3/common (3.46.0+1)" - - "sqlite3/fts5 (3.46.0+1)": + - sqlite3 (3.47.1): + - sqlite3/common (= 3.47.1) + - sqlite3/common (3.47.1) + - sqlite3/dbstatvtab (3.47.1): + - sqlite3/common + - sqlite3/fts5 (3.47.1): - sqlite3/common - - "sqlite3/perf-threadsafe (3.46.0+1)": + - sqlite3/perf-threadsafe (3.47.1): - sqlite3/common - - "sqlite3/rtree (3.46.0+1)": + - sqlite3/rtree (3.47.1): - sqlite3/common - sqlite3_flutter_libs (0.0.1): - Flutter - - sqlite3 (~> 3.46.0) + - sqlite3 (~> 3.47.0) + - sqlite3/dbstatvtab - sqlite3/fts5 - sqlite3/perf-threadsafe - sqlite3/rtree @@ -130,11 +135,12 @@ DEPENDENCIES: - media_kit_libs_ios_audio (from `.symlinks/plugins/media_kit_libs_ios_audio/ios`) - media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`) - metadata_god (from `.symlinks/plugins/metadata_god/ios`) + - open_file_ios (from `.symlinks/plugins/open_file_ios/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - - sqflite (from `.symlinks/plugins/sqflite/darwin`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) @@ -186,6 +192,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/media_kit_native_event_loop/ios" metadata_god: :path: ".symlinks/plugins/metadata_god/ios" + open_file_ios: + :path: ".symlinks/plugins/open_file_ios/ios" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: @@ -194,8 +202,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/permission_handler_apple/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" - sqflite: - :path: ".symlinks/plugins/sqflite/darwin" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" sqlite3_flutter_libs: :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" url_launcher_ios: @@ -206,35 +214,36 @@ SPEC CHECKSUMS: audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207 bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842 - device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d + device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342 DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 - file_selector_ios: 78baf21d03f1e37a7df97bb2494f9cd86de8fa5d + file_selector_ios: f0670c1064a8c8450e38145d8043160105d0b97c Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_broadcasts: 3ece15b27d8ccbe2132c3df303e7c3401feab882 flutter_discord_rpc: e1c342f29ceb9dd76cdc01db59a70c93bb4d9ec5 flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4 - flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778 - flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be + flutter_native_splash: e8a1e01082d97a8099d973f919f57904c925008a + flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 flutter_sharing_intent: e35380d0e1501d7111dbb7e46d5ac6339da6da98 - image_picker_ios: b545a5f16c0fa88e3ecbbce3ed4de45567a8ec18 + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 media_kit_libs_ios_audio: 8f39d96a9c630685dfb844c289bd1d114c486fb3 media_kit_native_event_loop: 99111eded5acbdc9c2738021ea6550dd36ca8837 metadata_god: 4bbd8523cdb5d42c5e59d2fabad01ff8f4bc53f9 + open_file_ios: 461db5853723763573e140de3193656f91990d9e OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 SDWebImage: a81bbb3ba4ea5f810f4069c68727cb118467a04a - shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 - sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec - sqlite3: 292c3e1bfe89f64e51ea7fc7dab9182a017c8630 - sqlite3_flutter_libs: 0d611efdf6d1c9297d5ab03dab21b75aeebdae31 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + sqlite3: 1e522f0938463e44b7faf50393b40bdc1e1e456d + sqlite3_flutter_libs: b55ef23cfafea5318ae5081e0bf3fbbce8417c94 SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f - url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586 + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe PODFILE CHECKSUM: 0659b64ac6e9e96b61d8550decffa8bff51a957e -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 70693e4a8..b63630348 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/lib/collections/assets.gen.dart b/lib/collections/assets.gen.dart index f10242e8b..6825fbd58 100644 --- a/lib/collections/assets.gen.dart +++ b/lib/collections/assets.gen.dart @@ -43,6 +43,7 @@ class $AssetsTutorialGen { class Assets { Assets._(); + static const String license = 'LICENSE'; static const AssetGenImage albumPlaceholder = AssetGenImage('assets/album-placeholder.png'); static const AssetGenImage bengaliPatternsBg = @@ -92,6 +93,7 @@ class Assets { /// List of all assets static List get values => [ + license, albumPlaceholder, bengaliPatternsBg, branding, @@ -122,10 +124,17 @@ class Assets { } class AssetGenImage { - const AssetGenImage(this._assetName); + const AssetGenImage( + this._assetName, { + this.size, + this.flavors = const {}, + }); final String _assetName; + final Size? size; + final Set flavors; + Image image({ Key? key, AssetBundle? bundle, @@ -144,7 +153,7 @@ class AssetGenImage { ImageRepeat repeat = ImageRepeat.noRepeat, Rect? centerSlice, bool matchTextDirection = false, - bool gaplessPlayback = false, + bool gaplessPlayback = true, bool isAntiAlias = false, String? package, FilterQuality filterQuality = FilterQuality.low, diff --git a/lib/collections/routes.dart b/lib/collections/routes.dart index 3bf1d883d..a0380e29f 100644 --- a/lib/collections/routes.dart +++ b/lib/collections/routes.dart @@ -128,9 +128,12 @@ final routerProvider = Provider((ref) { pageBuilder: (context, state) { assert(state.extra is String); return SpotubePage( - child: LocalLibraryPage(state.extra as String, - isDownloads: - state.uri.queryParameters["downloads"] != null), + child: LocalLibraryPage( + state.extra as String, + isDownloads: + state.uri.queryParameters["downloads"] != null, + isCache: state.uri.queryParameters["cache"] != null, + ), ); }, ), diff --git a/lib/collections/spotube_icons.dart b/lib/collections/spotube_icons.dart index a45e581ed..5c4df85f6 100644 --- a/lib/collections/spotube_icons.dart +++ b/lib/collections/spotube_icons.dart @@ -124,4 +124,7 @@ abstract class SpotubeIcons { static const chart = FeatherIcons.barChart2; static const folderAdd = FeatherIcons.folderPlus; static const folderRemove = FeatherIcons.folderMinus; + static const cache = FeatherIcons.hardDrive; + static const export = Icons.file_open_outlined; + static const delete = FeatherIcons.trash2; } diff --git a/lib/extensions/track.dart b/lib/extensions/track.dart index 02c0c4927..215a5ab2b 100644 --- a/lib/extensions/track.dart +++ b/lib/extensions/track.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'dart:typed_data'; import 'package:metadata_god/metadata_god.dart'; import 'package:path/path.dart'; @@ -37,6 +38,33 @@ extension TrackExtensions on Track { return this; } + + Metadata toMetadata({ + required int fileLength, + Uint8List? imageBytes, + }) { + return Metadata( + title: name, + artist: artists?.map((a) => a.name).join(", "), + album: album?.name, + albumArtist: artists?.map((a) => a.name).join(", "), + year: album?.releaseDate != null + ? int.tryParse(album!.releaseDate!.split("-").first) ?? 1969 + : 1969, + trackNumber: trackNumber, + discNumber: discNumber, + durationMs: durationMs?.toDouble() ?? 0.0, + fileSize: BigInt.from(fileLength), + trackTotal: album?.tracks?.length ?? 0, + picture: imageBytes != null + ? Picture( + data: imageBytes, + // Spotify images are always JPEGs + mimeType: 'image/jpeg', + ) + : null, + ); + } } extension TrackSimpleExtensions on TrackSimple { diff --git a/lib/hooks/utils/use_custom_status_bar_color.dart b/lib/hooks/utils/use_custom_status_bar_color.dart index 7c5c7b278..8afc6a59c 100644 --- a/lib/hooks/utils/use_custom_status_bar_color.dart +++ b/lib/hooks/utils/use_custom_status_bar_color.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -void useCustomStatusBarColor( +VoidCallback useCustomStatusBarColor( Color color, bool isCurrentRoute, { bool noSetBGColor = false, @@ -10,14 +10,19 @@ void useCustomStatusBarColor( }) { final context = useContext(); final backgroundColor = Theme.of(context).scaffoldBackgroundColor; - resetStatusbar() => SystemChrome.setSystemUIOverlayStyle( - SystemUiOverlayStyle( - statusBarColor: backgroundColor, // status bar color - statusBarIconBrightness: backgroundColor.computeLuminance() > 0.179 - ? Brightness.dark - : Brightness.light, - ), - ); + // ignore: invalid_use_of_visible_for_testing_member + final previousState = SystemChrome.latestStyle; + + void resetStatusbar() => previousState != null + ? SystemChrome.setSystemUIOverlayStyle(previousState) + : SystemChrome.setSystemUIOverlayStyle( + SystemUiOverlayStyle( + statusBarColor: backgroundColor, // status bar color + statusBarIconBrightness: backgroundColor.computeLuminance() > 0.179 + ? Brightness.dark + : Brightness.light, + ), + ); // ignore: invalid_use_of_visible_for_testing_member final statusBarColor = SystemChrome.latestStyle?.statusBarColor; @@ -54,4 +59,6 @@ void useCustomStatusBarColor( useEffect(() { return resetStatusbar; }, []); + + return resetStatusbar; } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 27a64c871..f949480e7 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -391,5 +391,15 @@ "total_money": "Total {money}", "webview_not_found": "Webview not found", "webview_not_found_description": "No webview runtime is installed in your device.\nIf it's installed make sure it's in the Environment PATH\n\nAfter installing, restart the app", - "unsupported_platform": "Unsupported platform" + "unsupported_platform": "Unsupported platform", + "cache_music": "Cache music", + "open": "Open", + "cache_folder": "Cache folder", + "export": "Export", + "clear_cache": "Clear cache", + "clear_cache_confirmation": "Do you want to clear the cache?", + "export_cache_files": "Export Cached Files", + "found_n_files": "Found {count} files", + "export_cache_confirmation": "Do you want to export these files to", + "exported_n_out_of_m_files": "Exported {filesExported} out of {files} files" } \ No newline at end of file diff --git a/lib/models/connect/connect.freezed.dart b/lib/models/connect/connect.freezed.dart index 088cfbd1a..9103dd2b8 100644 --- a/lib/models/connect/connect.freezed.dart +++ b/lib/models/connect/connect.freezed.dart @@ -99,8 +99,13 @@ mixin _$WebSocketLoadEventData { required TResult orElse(), }) => throw _privateConstructorUsedError; + + /// Serializes this WebSocketLoadEventData to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of WebSocketLoadEventData + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $WebSocketLoadEventDataCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -127,6 +132,8 @@ class _$WebSocketLoadEventDataCopyWithImpl<$Res, // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of WebSocketLoadEventData + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -171,6 +178,8 @@ class __$$WebSocketLoadEventDataPlaylistImplCopyWithImpl<$Res> $Res Function(_$WebSocketLoadEventDataPlaylistImpl) _then) : super(_value, _then); + /// Create a copy of WebSocketLoadEventData + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -247,12 +256,14 @@ class _$WebSocketLoadEventDataPlaylistImpl other.initialIndex == initialIndex)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, const DeepCollectionEquality().hash(_tracks), collection, initialIndex); - @JsonKey(ignore: true) + /// Create a copy of WebSocketLoadEventData + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$WebSocketLoadEventDataPlaylistImplCopyWith< @@ -372,8 +383,11 @@ abstract class WebSocketLoadEventDataPlaylist extends WebSocketLoadEventData { PlaylistSimple? get collection; @override int? get initialIndex; + + /// Create a copy of WebSocketLoadEventData + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$WebSocketLoadEventDataPlaylistImplCopyWith< _$WebSocketLoadEventDataPlaylistImpl> get copyWith => throw _privateConstructorUsedError; @@ -404,6 +418,8 @@ class __$$WebSocketLoadEventDataAlbumImplCopyWithImpl<$Res> $Res Function(_$WebSocketLoadEventDataAlbumImpl) _then) : super(_value, _then); + /// Create a copy of WebSocketLoadEventData + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -479,12 +495,14 @@ class _$WebSocketLoadEventDataAlbumImpl extends WebSocketLoadEventDataAlbum { other.initialIndex == initialIndex)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, const DeepCollectionEquality().hash(_tracks), collection, initialIndex); - @JsonKey(ignore: true) + /// Create a copy of WebSocketLoadEventData + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$WebSocketLoadEventDataAlbumImplCopyWith<_$WebSocketLoadEventDataAlbumImpl> @@ -603,8 +621,11 @@ abstract class WebSocketLoadEventDataAlbum extends WebSocketLoadEventData { AlbumSimple? get collection; @override int? get initialIndex; + + /// Create a copy of WebSocketLoadEventData + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$WebSocketLoadEventDataAlbumImplCopyWith<_$WebSocketLoadEventDataAlbumImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/lib/models/database/database.dart b/lib/models/database/database.dart index 8bb32bd50..0f30df198 100644 --- a/lib/models/database/database.dart +++ b/lib/models/database/database.dart @@ -58,18 +58,26 @@ class AppDatabase extends _$AppDatabase { AppDatabase() : super(_openConnection()); @override - int get schemaVersion => 2; + int get schemaVersion => 3; @override MigrationStrategy get migration { return MigrationStrategy( - onUpgrade: stepByStep(from1To2: (m, schema) async { - // Add invidiousInstance column to preferences table - await m.addColumn( - schema.preferencesTable, - schema.preferencesTable.invidiousInstance, - ); - }), + onUpgrade: stepByStep( + from1To2: (m, schema) async { + // Add invidiousInstance column to preferences table + await m.addColumn( + schema.preferencesTable, + schema.preferencesTable.invidiousInstance, + ); + }, + from2To3: (m, schema) async { + await m.addColumn( + schema.preferencesTable, + schema.preferencesTable.cacheMusic, + ); + }, + ), ); } } diff --git a/lib/models/database/database.g.dart b/lib/models/database/database.g.dart index def70bd29..951b2ed52 100644 --- a/lib/models/database/database.g.dart +++ b/lib/models/database/database.g.dart @@ -812,6 +812,16 @@ class $PreferencesTableTable extends PreferencesTable defaultConstraints: GeneratedColumn.constraintIsAlways( 'CHECK ("enable_connect" IN (0, 1))'), defaultValue: const Constant(false)); + static const VerificationMeta _cacheMusicMeta = + const VerificationMeta('cacheMusic'); + @override + late final GeneratedColumn cacheMusic = GeneratedColumn( + 'cache_music', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("cache_music" IN (0, 1))'), + defaultValue: const Constant(true)); @override List get $columns => [ id, @@ -839,7 +849,8 @@ class $PreferencesTableTable extends PreferencesTable downloadMusicCodec, discordPresence, endlessPlayback, - enableConnect + enableConnect, + cacheMusic ]; @override String get aliasedName => _alias ?? actualTableName; @@ -946,6 +957,12 @@ class $PreferencesTableTable extends PreferencesTable enableConnect.isAcceptableOrUnknown( data['enable_connect']!, _enableConnectMeta)); } + if (data.containsKey('cache_music')) { + context.handle( + _cacheMusicMeta, + cacheMusic.isAcceptableOrUnknown( + data['cache_music']!, _cacheMusicMeta)); + } return context; } @@ -1020,6 +1037,8 @@ class $PreferencesTableTable extends PreferencesTable .read(DriftSqlType.bool, data['${effectivePrefix}endless_playback'])!, enableConnect: attachedDatabase.typeMapping .read(DriftSqlType.bool, data['${effectivePrefix}enable_connect'])!, + cacheMusic: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}cache_music'])!, ); } @@ -1086,6 +1105,7 @@ class PreferencesTableData extends DataClass final bool discordPresence; final bool endlessPlayback; final bool enableConnect; + final bool cacheMusic; const PreferencesTableData( {required this.id, required this.audioQuality, @@ -1112,7 +1132,8 @@ class PreferencesTableData extends DataClass required this.downloadMusicCodec, required this.discordPresence, required this.endlessPlayback, - required this.enableConnect}); + required this.enableConnect, + required this.cacheMusic}); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1182,6 +1203,7 @@ class PreferencesTableData extends DataClass map['discord_presence'] = Variable(discordPresence); map['endless_playback'] = Variable(endlessPlayback); map['enable_connect'] = Variable(enableConnect); + map['cache_music'] = Variable(cacheMusic); return map; } @@ -1213,6 +1235,7 @@ class PreferencesTableData extends DataClass discordPresence: Value(discordPresence), endlessPlayback: Value(endlessPlayback), enableConnect: Value(enableConnect), + cacheMusic: Value(cacheMusic), ); } @@ -1257,6 +1280,7 @@ class PreferencesTableData extends DataClass discordPresence: serializer.fromJson(json['discordPresence']), endlessPlayback: serializer.fromJson(json['endlessPlayback']), enableConnect: serializer.fromJson(json['enableConnect']), + cacheMusic: serializer.fromJson(json['cacheMusic']), ); } @override @@ -1301,6 +1325,7 @@ class PreferencesTableData extends DataClass 'discordPresence': serializer.toJson(discordPresence), 'endlessPlayback': serializer.toJson(endlessPlayback), 'enableConnect': serializer.toJson(enableConnect), + 'cacheMusic': serializer.toJson(cacheMusic), }; } @@ -1330,7 +1355,8 @@ class PreferencesTableData extends DataClass SourceCodecs? downloadMusicCodec, bool? discordPresence, bool? endlessPlayback, - bool? enableConnect}) => + bool? enableConnect, + bool? cacheMusic}) => PreferencesTableData( id: id ?? this.id, audioQuality: audioQuality ?? this.audioQuality, @@ -1358,6 +1384,7 @@ class PreferencesTableData extends DataClass discordPresence: discordPresence ?? this.discordPresence, endlessPlayback: endlessPlayback ?? this.endlessPlayback, enableConnect: enableConnect ?? this.enableConnect, + cacheMusic: cacheMusic ?? this.cacheMusic, ); PreferencesTableData copyWithCompanion(PreferencesTableCompanion data) { return PreferencesTableData( @@ -1427,6 +1454,8 @@ class PreferencesTableData extends DataClass enableConnect: data.enableConnect.present ? data.enableConnect.value : this.enableConnect, + cacheMusic: + data.cacheMusic.present ? data.cacheMusic.value : this.cacheMusic, ); } @@ -1458,7 +1487,8 @@ class PreferencesTableData extends DataClass ..write('downloadMusicCodec: $downloadMusicCodec, ') ..write('discordPresence: $discordPresence, ') ..write('endlessPlayback: $endlessPlayback, ') - ..write('enableConnect: $enableConnect') + ..write('enableConnect: $enableConnect, ') + ..write('cacheMusic: $cacheMusic') ..write(')')) .toString(); } @@ -1490,7 +1520,8 @@ class PreferencesTableData extends DataClass downloadMusicCodec, discordPresence, endlessPlayback, - enableConnect + enableConnect, + cacheMusic ]); @override bool operator ==(Object other) => @@ -1521,7 +1552,8 @@ class PreferencesTableData extends DataClass other.downloadMusicCodec == this.downloadMusicCodec && other.discordPresence == this.discordPresence && other.endlessPlayback == this.endlessPlayback && - other.enableConnect == this.enableConnect); + other.enableConnect == this.enableConnect && + other.cacheMusic == this.cacheMusic); } class PreferencesTableCompanion extends UpdateCompanion { @@ -1551,6 +1583,7 @@ class PreferencesTableCompanion extends UpdateCompanion { final Value discordPresence; final Value endlessPlayback; final Value enableConnect; + final Value cacheMusic; const PreferencesTableCompanion({ this.id = const Value.absent(), this.audioQuality = const Value.absent(), @@ -1578,6 +1611,7 @@ class PreferencesTableCompanion extends UpdateCompanion { this.discordPresence = const Value.absent(), this.endlessPlayback = const Value.absent(), this.enableConnect = const Value.absent(), + this.cacheMusic = const Value.absent(), }); PreferencesTableCompanion.insert({ this.id = const Value.absent(), @@ -1606,6 +1640,7 @@ class PreferencesTableCompanion extends UpdateCompanion { this.discordPresence = const Value.absent(), this.endlessPlayback = const Value.absent(), this.enableConnect = const Value.absent(), + this.cacheMusic = const Value.absent(), }); static Insertable custom({ Expression? id, @@ -1634,6 +1669,7 @@ class PreferencesTableCompanion extends UpdateCompanion { Expression? discordPresence, Expression? endlessPlayback, Expression? enableConnect, + Expression? cacheMusic, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -1665,6 +1701,7 @@ class PreferencesTableCompanion extends UpdateCompanion { if (discordPresence != null) 'discord_presence': discordPresence, if (endlessPlayback != null) 'endless_playback': endlessPlayback, if (enableConnect != null) 'enable_connect': enableConnect, + if (cacheMusic != null) 'cache_music': cacheMusic, }); } @@ -1694,7 +1731,8 @@ class PreferencesTableCompanion extends UpdateCompanion { Value? downloadMusicCodec, Value? discordPresence, Value? endlessPlayback, - Value? enableConnect}) { + Value? enableConnect, + Value? cacheMusic}) { return PreferencesTableCompanion( id: id ?? this.id, audioQuality: audioQuality ?? this.audioQuality, @@ -1722,6 +1760,7 @@ class PreferencesTableCompanion extends UpdateCompanion { discordPresence: discordPresence ?? this.discordPresence, endlessPlayback: endlessPlayback ?? this.endlessPlayback, enableConnect: enableConnect ?? this.enableConnect, + cacheMusic: cacheMusic ?? this.cacheMusic, ); } @@ -1825,6 +1864,9 @@ class PreferencesTableCompanion extends UpdateCompanion { if (enableConnect.present) { map['enable_connect'] = Variable(enableConnect.value); } + if (cacheMusic.present) { + map['cache_music'] = Variable(cacheMusic.value); + } return map; } @@ -1856,7 +1898,8 @@ class PreferencesTableCompanion extends UpdateCompanion { ..write('downloadMusicCodec: $downloadMusicCodec, ') ..write('discordPresence: $discordPresence, ') ..write('endlessPlayback: $endlessPlayback, ') - ..write('enableConnect: $enableConnect') + ..write('enableConnect: $enableConnect, ') + ..write('cacheMusic: $cacheMusic') ..write(')')) .toString(); } @@ -4527,6 +4570,7 @@ typedef $$PreferencesTableTableCreateCompanionBuilder Value discordPresence, Value endlessPlayback, Value enableConnect, + Value cacheMusic, }); typedef $$PreferencesTableTableUpdateCompanionBuilder = PreferencesTableCompanion Function({ @@ -4556,6 +4600,7 @@ typedef $$PreferencesTableTableUpdateCompanionBuilder Value discordPresence, Value endlessPlayback, Value enableConnect, + Value cacheMusic, }); class $$PreferencesTableTableFilterComposer @@ -4677,6 +4722,9 @@ class $$PreferencesTableTableFilterComposer ColumnFilters get enableConnect => $composableBuilder( column: $table.enableConnect, builder: (column) => ColumnFilters(column)); + + ColumnFilters get cacheMusic => $composableBuilder( + column: $table.cacheMusic, builder: (column) => ColumnFilters(column)); } class $$PreferencesTableTableOrderingComposer @@ -4783,6 +4831,9 @@ class $$PreferencesTableTableOrderingComposer ColumnOrderings get enableConnect => $composableBuilder( column: $table.enableConnect, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get cacheMusic => $composableBuilder( + column: $table.cacheMusic, builder: (column) => ColumnOrderings(column)); } class $$PreferencesTableTableAnnotationComposer @@ -4880,6 +4931,9 @@ class $$PreferencesTableTableAnnotationComposer GeneratedColumn get enableConnect => $composableBuilder( column: $table.enableConnect, builder: (column) => column); + + GeneratedColumn get cacheMusic => $composableBuilder( + column: $table.cacheMusic, builder: (column) => column); } class $$PreferencesTableTableTableManager extends RootTableManager< @@ -4936,6 +4990,7 @@ class $$PreferencesTableTableTableManager extends RootTableManager< Value discordPresence = const Value.absent(), Value endlessPlayback = const Value.absent(), Value enableConnect = const Value.absent(), + Value cacheMusic = const Value.absent(), }) => PreferencesTableCompanion( id: id, @@ -4964,6 +5019,7 @@ class $$PreferencesTableTableTableManager extends RootTableManager< discordPresence: discordPresence, endlessPlayback: endlessPlayback, enableConnect: enableConnect, + cacheMusic: cacheMusic, ), createCompanionCallback: ({ Value id = const Value.absent(), @@ -4992,6 +5048,7 @@ class $$PreferencesTableTableTableManager extends RootTableManager< Value discordPresence = const Value.absent(), Value endlessPlayback = const Value.absent(), Value enableConnect = const Value.absent(), + Value cacheMusic = const Value.absent(), }) => PreferencesTableCompanion.insert( id: id, @@ -5020,6 +5077,7 @@ class $$PreferencesTableTableTableManager extends RootTableManager< discordPresence: discordPresence, endlessPlayback: endlessPlayback, enableConnect: enableConnect, + cacheMusic: cacheMusic, ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), BaseReferences(db, table, e))) @@ -5829,8 +5887,7 @@ final class $$PlaylistTableTableReferences extends BaseReferences<_$AppDatabase, db.audioPlayerStateTable.createAlias($_aliasNameGenerator( db.playlistTable.audioPlayerStateId, db.audioPlayerStateTable.id)); - $$AudioPlayerStateTableTableProcessedTableManager? get audioPlayerStateId { - if ($_item.audioPlayerStateId == null) return null; + $$AudioPlayerStateTableTableProcessedTableManager get audioPlayerStateId { final manager = $$AudioPlayerStateTableTableTableManager( $_db, $_db.audioPlayerStateTable) .filter((f) => f.id($_item.audioPlayerStateId!)); @@ -6156,8 +6213,7 @@ final class $$PlaylistMediaTableTableReferences extends BaseReferences< db.playlistTable.createAlias($_aliasNameGenerator( db.playlistMediaTable.playlistId, db.playlistTable.id)); - $$PlaylistTableTableProcessedTableManager? get playlistId { - if ($_item.playlistId == null) return null; + $$PlaylistTableTableProcessedTableManager get playlistId { final manager = $$PlaylistTableTableTableManager($_db, $_db.playlistTable) .filter((f) => f.id($_item.playlistId!)); final item = $_typedResult.readTableOrNull(_playlistIdTable($_db)); diff --git a/lib/models/database/database.steps.dart b/lib/models/database/database.steps.dart index 4814fa5a4..40546bdb6 100644 --- a/lib/models/database/database.steps.dart +++ b/lib/models/database/database.steps.dart @@ -1,10 +1,11 @@ +// dart format width=80 import 'package:drift/internal/versioned_schema.dart' as i0; import 'package:drift/drift.dart' as i1; -import 'package:drift/drift.dart'; +import 'package:drift/drift.dart'; // ignore_for_file: type=lint,unused_import import 'package:flutter/material.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/services/sourced_track/enums.dart'; -import 'package:spotube/utils/migrations/adapters.dart'; // ignore_for_file: type=lint,unused_import +import 'package:spotube/utils/migrations/adapters.dart'; // GENERATED BY drift_dev, DO NOT MODIFY. final class Schema2 extends i0.VersionedSchema { @@ -627,8 +628,288 @@ class Shape10 extends i0.VersionedTable { columnsByName['data']! as i1.GeneratedColumn; } +final class Schema3 extends i0.VersionedSchema { + Schema3({required super.database}) : super(version: 3); + @override + late final List entities = [ + authenticationTable, + blacklistTable, + preferencesTable, + scrobblerTable, + skipSegmentTable, + sourceMatchTable, + audioPlayerStateTable, + playlistTable, + playlistMediaTable, + historyTable, + lyricsTable, + uniqueBlacklist, + uniqTrackMatch, + ]; + late final Shape0 authenticationTable = Shape0( + source: i0.VersionedTable( + entityName: 'authentication_table', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape1 blacklistTable = Shape1( + source: i0.VersionedTable( + entityName: 'blacklist_table', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_4, + _column_5, + _column_6, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape11 preferencesTable = Shape11( + source: i0.VersionedTable( + entityName: 'preferences_table', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_7, + _column_8, + _column_9, + _column_10, + _column_11, + _column_12, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_22, + _column_23, + _column_24, + _column_25, + _column_26, + _column_27, + _column_28, + _column_29, + _column_30, + _column_31, + _column_53, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape3 scrobblerTable = Shape3( + source: i0.VersionedTable( + entityName: 'scrobbler_table', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_32, + _column_33, + _column_34, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape4 skipSegmentTable = Shape4( + source: i0.VersionedTable( + entityName: 'skip_segment_table', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_35, + _column_36, + _column_37, + _column_32, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape5 sourceMatchTable = Shape5( + source: i0.VersionedTable( + entityName: 'source_match_table', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_37, + _column_38, + _column_39, + _column_32, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape6 audioPlayerStateTable = Shape6( + source: i0.VersionedTable( + entityName: 'audio_player_state_table', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_40, + _column_41, + _column_42, + _column_43, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape7 playlistTable = Shape7( + source: i0.VersionedTable( + entityName: 'playlist_table', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_44, + _column_45, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape8 playlistMediaTable = Shape8( + source: i0.VersionedTable( + entityName: 'playlist_media_table', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_46, + _column_47, + _column_48, + _column_49, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape9 historyTable = Shape9( + source: i0.VersionedTable( + entityName: 'history_table', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_32, + _column_50, + _column_51, + _column_52, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape10 lyricsTable = Shape10( + source: i0.VersionedTable( + entityName: 'lyrics_table', + withoutRowId: false, + isStrict: false, + tableConstraints: [], + columns: [ + _column_0, + _column_37, + _column_52, + ], + attachedDatabase: database, + ), + alias: null); + final i1.Index uniqueBlacklist = i1.Index('unique_blacklist', + 'CREATE UNIQUE INDEX unique_blacklist ON blacklist_table (element_type, element_id)'); + final i1.Index uniqTrackMatch = i1.Index('uniq_track_match', + 'CREATE UNIQUE INDEX uniq_track_match ON source_match_table (track_id, source_id, source_type)'); +} + +class Shape11 extends i0.VersionedTable { + Shape11({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get audioQuality => + columnsByName['audio_quality']! as i1.GeneratedColumn; + i1.GeneratedColumn get albumColorSync => + columnsByName['album_color_sync']! as i1.GeneratedColumn; + i1.GeneratedColumn get amoledDarkTheme => + columnsByName['amoled_dark_theme']! as i1.GeneratedColumn; + i1.GeneratedColumn get checkUpdate => + columnsByName['check_update']! as i1.GeneratedColumn; + i1.GeneratedColumn get normalizeAudio => + columnsByName['normalize_audio']! as i1.GeneratedColumn; + i1.GeneratedColumn get showSystemTrayIcon => + columnsByName['show_system_tray_icon']! as i1.GeneratedColumn; + i1.GeneratedColumn get systemTitleBar => + columnsByName['system_title_bar']! as i1.GeneratedColumn; + i1.GeneratedColumn get skipNonMusic => + columnsByName['skip_non_music']! as i1.GeneratedColumn; + i1.GeneratedColumn get closeBehavior => + columnsByName['close_behavior']! as i1.GeneratedColumn; + i1.GeneratedColumn get accentColorScheme => + columnsByName['accent_color_scheme']! as i1.GeneratedColumn; + i1.GeneratedColumn get layoutMode => + columnsByName['layout_mode']! as i1.GeneratedColumn; + i1.GeneratedColumn get locale => + columnsByName['locale']! as i1.GeneratedColumn; + i1.GeneratedColumn get market => + columnsByName['market']! as i1.GeneratedColumn; + i1.GeneratedColumn get searchMode => + columnsByName['search_mode']! as i1.GeneratedColumn; + i1.GeneratedColumn get downloadLocation => + columnsByName['download_location']! as i1.GeneratedColumn; + i1.GeneratedColumn get localLibraryLocation => + columnsByName['local_library_location']! as i1.GeneratedColumn; + i1.GeneratedColumn get pipedInstance => + columnsByName['piped_instance']! as i1.GeneratedColumn; + i1.GeneratedColumn get invidiousInstance => + columnsByName['invidious_instance']! as i1.GeneratedColumn; + i1.GeneratedColumn get themeMode => + columnsByName['theme_mode']! as i1.GeneratedColumn; + i1.GeneratedColumn get audioSource => + columnsByName['audio_source']! as i1.GeneratedColumn; + i1.GeneratedColumn get streamMusicCodec => + columnsByName['stream_music_codec']! as i1.GeneratedColumn; + i1.GeneratedColumn get downloadMusicCodec => + columnsByName['download_music_codec']! as i1.GeneratedColumn; + i1.GeneratedColumn get discordPresence => + columnsByName['discord_presence']! as i1.GeneratedColumn; + i1.GeneratedColumn get endlessPlayback => + columnsByName['endless_playback']! as i1.GeneratedColumn; + i1.GeneratedColumn get enableConnect => + columnsByName['enable_connect']! as i1.GeneratedColumn; + i1.GeneratedColumn get cacheMusic => + columnsByName['cache_music']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_53(String aliasedName) => + i1.GeneratedColumn('cache_music', aliasedName, false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("cache_music" IN (0, 1))'), + defaultValue: const Constant(true)); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, + required Future Function(i1.Migrator m, Schema3 schema) from2To3, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -637,6 +918,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from1To2(migrator, schema); return 2; + case 2: + final schema = Schema3(database: database); + final migrator = i1.Migrator(database, schema); + await from2To3(migrator, schema); + return 3; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -645,8 +931,10 @@ i0.MigrationStepWithVersion migrationSteps({ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, + required Future Function(i1.Migrator m, Schema3 schema) from2To3, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, + from2To3: from2To3, )); diff --git a/lib/models/database/tables/preferences.dart b/lib/models/database/tables/preferences.dart index 96132de84..c3904c845 100644 --- a/lib/models/database/tables/preferences.dart +++ b/lib/models/database/tables/preferences.dart @@ -94,6 +94,7 @@ class PreferencesTable extends Table { boolean().withDefault(const Constant(true))(); BoolColumn get enableConnect => boolean().withDefault(const Constant(false))(); + BoolColumn get cacheMusic => boolean().withDefault(const Constant(true))(); // Default values as PreferencesTableData static PreferencesTableData defaults() { @@ -119,11 +120,12 @@ class PreferencesTable extends Table { invidiousInstance: "https://inv.nadeko.net", themeMode: ThemeMode.system, audioSource: AudioSource.youtube, - streamMusicCodec: SourceCodecs.weba, + streamMusicCodec: SourceCodecs.m4a, downloadMusicCodec: SourceCodecs.m4a, discordPresence: true, endlessPlayback: true, enableConnect: false, + cacheMusic: true, ); } } diff --git a/lib/models/parser/range_headers.dart b/lib/models/parser/range_headers.dart new file mode 100644 index 000000000..08025cbf9 --- /dev/null +++ b/lib/models/parser/range_headers.dart @@ -0,0 +1,71 @@ +class ContentRangeHeader { + final int start; + final int end; + final int total; + + ContentRangeHeader(this.start, this.end, this.total); + + factory ContentRangeHeader.parse(String value) { + if (value.isEmpty) { + throw FormatException('Invalid Content-Range header: $value'); + } + + final parts = value.split(' '); + if (parts.length != 2) { + throw FormatException('Invalid Content-Range header: $value'); + } + + final rangeParts = parts[1].split('/'); + if (rangeParts.length != 2) { + throw FormatException('Invalid Content-Range header: $value'); + } + + final range = rangeParts[0].split('-'); + if (range.length != 2) { + throw FormatException('Invalid Content-Range header: $value'); + } + + return ContentRangeHeader( + int.parse(range[0]), + int.parse(range[1]), + int.parse(rangeParts[1]), + ); + } + + @override + String toString() { + return 'bytes $start-$end/$total'; + } +} + +class RangeHeader { + final int start; + final int? end; + + RangeHeader(this.start, this.end); + + factory RangeHeader.parse(String value) { + if (value.isEmpty) { + return RangeHeader(0, null); + } + + final parts = value.split('='); + if (parts.length != 2) { + throw FormatException('Invalid Range header: $value'); + } + + final ranges = parts[1].split('-'); + + return RangeHeader( + int.parse(ranges[0]), + ranges.elementAtOrNull(1) != null && ranges[1].isNotEmpty + ? int.parse(ranges[1]) + : null, + ); + } + + @override + String toString() { + return 'bytes=$start-${end ?? ""}'; + } +} diff --git a/lib/models/spotify/home_feed.freezed.dart b/lib/models/spotify/home_feed.freezed.dart index c2bb2aba1..5076da294 100644 --- a/lib/models/spotify/home_feed.freezed.dart +++ b/lib/models/spotify/home_feed.freezed.dart @@ -29,8 +29,12 @@ mixin _$SpotifySectionPlaylist { String get owner => throw _privateConstructorUsedError; String get uri => throw _privateConstructorUsedError; + /// Serializes this SpotifySectionPlaylist to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of SpotifySectionPlaylist + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $SpotifySectionPlaylistCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -61,6 +65,8 @@ class _$SpotifySectionPlaylistCopyWithImpl<$Res, // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of SpotifySectionPlaylist + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -128,6 +134,8 @@ class __$$SpotifySectionPlaylistImplCopyWithImpl<$Res> $Res Function(_$SpotifySectionPlaylistImpl) _then) : super(_value, _then); + /// Create a copy of SpotifySectionPlaylist + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -221,12 +229,14 @@ class _$SpotifySectionPlaylistImpl extends _SpotifySectionPlaylist { (identical(other.uri, uri) || other.uri == uri)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, description, format, const DeepCollectionEquality().hash(_images), name, owner, uri); - @JsonKey(ignore: true) + /// Create a copy of SpotifySectionPlaylist + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$SpotifySectionPlaylistImplCopyWith<_$SpotifySectionPlaylistImpl> @@ -266,8 +276,11 @@ abstract class _SpotifySectionPlaylist extends SpotifySectionPlaylist { String get owner; @override String get uri; + + /// Create a copy of SpotifySectionPlaylist + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$SpotifySectionPlaylistImplCopyWith<_$SpotifySectionPlaylistImpl> get copyWith => throw _privateConstructorUsedError; } @@ -283,8 +296,12 @@ mixin _$SpotifySectionArtist { List get images => throw _privateConstructorUsedError; + /// Serializes this SpotifySectionArtist to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of SpotifySectionArtist + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $SpotifySectionArtistCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -309,6 +326,8 @@ class _$SpotifySectionArtistCopyWithImpl<$Res, // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of SpotifySectionArtist + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -352,6 +371,8 @@ class __$$SpotifySectionArtistImplCopyWithImpl<$Res> $Res Function(_$SpotifySectionArtistImpl) _then) : super(_value, _then); + /// Create a copy of SpotifySectionArtist + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -416,12 +437,14 @@ class _$SpotifySectionArtistImpl extends _SpotifySectionArtist { const DeepCollectionEquality().equals(other._images, _images)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, name, uri, const DeepCollectionEquality().hash(_images)); - @JsonKey(ignore: true) + /// Create a copy of SpotifySectionArtist + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$SpotifySectionArtistImplCopyWith<_$SpotifySectionArtistImpl> @@ -454,8 +477,11 @@ abstract class _SpotifySectionArtist extends SpotifySectionArtist { String get uri; @override List get images; + + /// Create a copy of SpotifySectionArtist + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$SpotifySectionArtistImplCopyWith<_$SpotifySectionArtistImpl> get copyWith => throw _privateConstructorUsedError; } @@ -473,8 +499,12 @@ mixin _$SpotifySectionAlbum { String get name => throw _privateConstructorUsedError; String get uri => throw _privateConstructorUsedError; + /// Serializes this SpotifySectionAlbum to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of SpotifySectionAlbum + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $SpotifySectionAlbumCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -502,6 +532,8 @@ class _$SpotifySectionAlbumCopyWithImpl<$Res, $Val extends SpotifySectionAlbum> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of SpotifySectionAlbum + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -554,6 +586,8 @@ class __$$SpotifySectionAlbumImplCopyWithImpl<$Res> $Res Function(_$SpotifySectionAlbumImpl) _then) : super(_value, _then); + /// Create a copy of SpotifySectionAlbum + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -635,7 +669,7 @@ class _$SpotifySectionAlbumImpl extends _SpotifySectionAlbum { (identical(other.uri, uri) || other.uri == uri)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, @@ -644,7 +678,9 @@ class _$SpotifySectionAlbumImpl extends _SpotifySectionAlbum { name, uri); - @JsonKey(ignore: true) + /// Create a copy of SpotifySectionAlbum + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$SpotifySectionAlbumImplCopyWith<_$SpotifySectionAlbumImpl> get copyWith => @@ -678,8 +714,11 @@ abstract class _SpotifySectionAlbum extends SpotifySectionAlbum { String get name; @override String get uri; + + /// Create a copy of SpotifySectionAlbum + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$SpotifySectionAlbumImplCopyWith<_$SpotifySectionAlbumImpl> get copyWith => throw _privateConstructorUsedError; } @@ -694,8 +733,12 @@ mixin _$SpotifySectionAlbumArtist { String get name => throw _privateConstructorUsedError; String get uri => throw _privateConstructorUsedError; + /// Serializes this SpotifySectionAlbumArtist to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of SpotifySectionAlbumArtist + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $SpotifySectionAlbumArtistCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -720,6 +763,8 @@ class _$SpotifySectionAlbumArtistCopyWithImpl<$Res, // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of SpotifySectionAlbumArtist + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -761,6 +806,8 @@ class __$$SpotifySectionAlbumArtistImplCopyWithImpl<$Res> $Res Function(_$SpotifySectionAlbumArtistImpl) _then) : super(_value, _then); + /// Create a copy of SpotifySectionAlbumArtist + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -808,11 +855,13 @@ class _$SpotifySectionAlbumArtistImpl extends _SpotifySectionAlbumArtist { (identical(other.uri, uri) || other.uri == uri)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, name, uri); - @JsonKey(ignore: true) + /// Create a copy of SpotifySectionAlbumArtist + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$SpotifySectionAlbumArtistImplCopyWith<_$SpotifySectionAlbumArtistImpl> @@ -840,8 +889,11 @@ abstract class _SpotifySectionAlbumArtist extends SpotifySectionAlbumArtist { String get name; @override String get uri; + + /// Create a copy of SpotifySectionAlbumArtist + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$SpotifySectionAlbumArtistImplCopyWith<_$SpotifySectionAlbumArtistImpl> get copyWith => throw _privateConstructorUsedError; } @@ -857,8 +909,12 @@ mixin _$SpotifySectionItemImage { String get url => throw _privateConstructorUsedError; num? get width => throw _privateConstructorUsedError; + /// Serializes this SpotifySectionItemImage to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of SpotifySectionItemImage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $SpotifySectionItemImageCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -883,6 +939,8 @@ class _$SpotifySectionItemImageCopyWithImpl<$Res, // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of SpotifySectionItemImage + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -929,6 +987,8 @@ class __$$SpotifySectionItemImageImplCopyWithImpl<$Res> $Res Function(_$SpotifySectionItemImageImpl) _then) : super(_value, _then); + /// Create a copy of SpotifySectionItemImage + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -985,11 +1045,13 @@ class _$SpotifySectionItemImageImpl extends _SpotifySectionItemImage { (identical(other.width, width) || other.width == width)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, height, url, width); - @JsonKey(ignore: true) + /// Create a copy of SpotifySectionItemImage + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$SpotifySectionItemImageImplCopyWith<_$SpotifySectionItemImageImpl> @@ -1020,8 +1082,11 @@ abstract class _SpotifySectionItemImage extends SpotifySectionItemImage { String get url; @override num? get width; + + /// Create a copy of SpotifySectionItemImage + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$SpotifySectionItemImageImplCopyWith<_$SpotifySectionItemImageImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1038,8 +1103,12 @@ mixin _$SpotifyHomeFeedSectionItem { SpotifySectionArtist? get artist => throw _privateConstructorUsedError; SpotifySectionAlbum? get album => throw _privateConstructorUsedError; + /// Serializes this SpotifyHomeFeedSectionItem to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of SpotifyHomeFeedSectionItem + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $SpotifyHomeFeedSectionItemCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1073,6 +1142,8 @@ class _$SpotifyHomeFeedSectionItemCopyWithImpl<$Res, // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of SpotifyHomeFeedSectionItem + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1101,6 +1172,8 @@ class _$SpotifyHomeFeedSectionItemCopyWithImpl<$Res, ) as $Val); } + /// Create a copy of SpotifyHomeFeedSectionItem + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $SpotifySectionPlaylistCopyWith<$Res>? get playlist { @@ -1113,6 +1186,8 @@ class _$SpotifyHomeFeedSectionItemCopyWithImpl<$Res, }); } + /// Create a copy of SpotifyHomeFeedSectionItem + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $SpotifySectionArtistCopyWith<$Res>? get artist { @@ -1125,6 +1200,8 @@ class _$SpotifyHomeFeedSectionItemCopyWithImpl<$Res, }); } + /// Create a copy of SpotifyHomeFeedSectionItem + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $SpotifySectionAlbumCopyWith<$Res>? get album { @@ -1171,6 +1248,8 @@ class __$$SpotifyHomeFeedSectionItemImplCopyWithImpl<$Res> $Res Function(_$SpotifyHomeFeedSectionItemImpl) _then) : super(_value, _then); + /// Create a copy of SpotifyHomeFeedSectionItem + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1237,12 +1316,14 @@ class _$SpotifyHomeFeedSectionItemImpl implements _SpotifyHomeFeedSectionItem { (identical(other.album, album) || other.album == album)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, typename, playlist, artist, album); - @JsonKey(ignore: true) + /// Create a copy of SpotifyHomeFeedSectionItem + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$SpotifyHomeFeedSectionItemImplCopyWith<_$SpotifyHomeFeedSectionItemImpl> @@ -1276,8 +1357,11 @@ abstract class _SpotifyHomeFeedSectionItem SpotifySectionArtist? get artist; @override SpotifySectionAlbum? get album; + + /// Create a copy of SpotifyHomeFeedSectionItem + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$SpotifyHomeFeedSectionItemImplCopyWith<_$SpotifyHomeFeedSectionItemImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1295,8 +1379,12 @@ mixin _$SpotifyHomeFeedSection { List get items => throw _privateConstructorUsedError; + /// Serializes this SpotifyHomeFeedSection to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of SpotifyHomeFeedSection + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $SpotifyHomeFeedSectionCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1325,6 +1413,8 @@ class _$SpotifyHomeFeedSectionCopyWithImpl<$Res, // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of SpotifyHomeFeedSection + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1380,6 +1470,8 @@ class __$$SpotifyHomeFeedSectionImplCopyWithImpl<$Res> $Res Function(_$SpotifyHomeFeedSectionImpl) _then) : super(_value, _then); + /// Create a copy of SpotifyHomeFeedSection + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1453,12 +1545,14 @@ class _$SpotifyHomeFeedSectionImpl implements _SpotifyHomeFeedSection { const DeepCollectionEquality().equals(other._items, _items)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, typename, title, uri, const DeepCollectionEquality().hash(_items)); - @JsonKey(ignore: true) + /// Create a copy of SpotifyHomeFeedSection + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$SpotifyHomeFeedSectionImplCopyWith<_$SpotifyHomeFeedSectionImpl> @@ -1492,8 +1586,11 @@ abstract class _SpotifyHomeFeedSection implements SpotifyHomeFeedSection { String get uri; @override List get items; + + /// Create a copy of SpotifyHomeFeedSection + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$SpotifyHomeFeedSectionImplCopyWith<_$SpotifyHomeFeedSectionImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1508,8 +1605,12 @@ mixin _$SpotifyHomeFeed { List get sections => throw _privateConstructorUsedError; + /// Serializes this SpotifyHomeFeed to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of SpotifyHomeFeed + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $SpotifyHomeFeedCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1533,6 +1634,8 @@ class _$SpotifyHomeFeedCopyWithImpl<$Res, $Val extends SpotifyHomeFeed> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of SpotifyHomeFeed + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1571,6 +1674,8 @@ class __$$SpotifyHomeFeedImplCopyWithImpl<$Res> _$SpotifyHomeFeedImpl _value, $Res Function(_$SpotifyHomeFeedImpl) _then) : super(_value, _then); + /// Create a copy of SpotifyHomeFeed + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1626,12 +1731,14 @@ class _$SpotifyHomeFeedImpl implements _SpotifyHomeFeed { const DeepCollectionEquality().equals(other._sections, _sections)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, greeting, const DeepCollectionEquality().hash(_sections)); - @JsonKey(ignore: true) + /// Create a copy of SpotifyHomeFeed + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$SpotifyHomeFeedImplCopyWith<_$SpotifyHomeFeedImpl> get copyWith => @@ -1659,8 +1766,11 @@ abstract class _SpotifyHomeFeed implements SpotifyHomeFeed { String get greeting; @override List get sections; + + /// Create a copy of SpotifyHomeFeed + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$SpotifyHomeFeedImplCopyWith<_$SpotifyHomeFeedImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/lib/models/spotify/recommendation_seeds.freezed.dart b/lib/models/spotify/recommendation_seeds.freezed.dart index adf4aab85..c55a4134b 100644 --- a/lib/models/spotify/recommendation_seeds.freezed.dart +++ b/lib/models/spotify/recommendation_seeds.freezed.dart @@ -24,7 +24,9 @@ mixin _$GeneratePlaylistProviderInput { RecommendationSeeds? get min => throw _privateConstructorUsedError; RecommendationSeeds? get target => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of GeneratePlaylistProviderInput + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $GeneratePlaylistProviderInputCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -62,6 +64,8 @@ class _$GeneratePlaylistProviderInputCopyWithImpl<$Res, // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of GeneratePlaylistProviderInput + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -105,6 +109,8 @@ class _$GeneratePlaylistProviderInputCopyWithImpl<$Res, ) as $Val); } + /// Create a copy of GeneratePlaylistProviderInput + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $RecommendationSeedsCopyWith<$Res>? get max { @@ -117,6 +123,8 @@ class _$GeneratePlaylistProviderInputCopyWithImpl<$Res, }); } + /// Create a copy of GeneratePlaylistProviderInput + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $RecommendationSeedsCopyWith<$Res>? get min { @@ -129,6 +137,8 @@ class _$GeneratePlaylistProviderInputCopyWithImpl<$Res, }); } + /// Create a copy of GeneratePlaylistProviderInput + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $RecommendationSeedsCopyWith<$Res>? get target { @@ -178,6 +188,8 @@ class __$$GeneratePlaylistProviderInputImplCopyWithImpl<$Res> $Res Function(_$GeneratePlaylistProviderInputImpl) _then) : super(_value, _then); + /// Create a copy of GeneratePlaylistProviderInput + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -283,7 +295,9 @@ class _$GeneratePlaylistProviderInputImpl min, target); - @JsonKey(ignore: true) + /// Create a copy of GeneratePlaylistProviderInput + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$GeneratePlaylistProviderInputImplCopyWith< @@ -317,8 +331,11 @@ abstract class _GeneratePlaylistProviderInput RecommendationSeeds? get min; @override RecommendationSeeds? get target; + + /// Create a copy of GeneratePlaylistProviderInput + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$GeneratePlaylistProviderInputImplCopyWith< _$GeneratePlaylistProviderInputImpl> get copyWith => throw _privateConstructorUsedError; @@ -347,8 +364,12 @@ mixin _$RecommendationSeeds { num? get timeSignature => throw _privateConstructorUsedError; num? get valence => throw _privateConstructorUsedError; + /// Serializes this RecommendationSeeds to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of RecommendationSeeds + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $RecommendationSeedsCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -386,6 +407,8 @@ class _$RecommendationSeedsCopyWithImpl<$Res, $Val extends RecommendationSeeds> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of RecommendationSeeds + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -498,6 +521,8 @@ class __$$RecommendationSeedsImplCopyWithImpl<$Res> $Res Function(_$RecommendationSeedsImpl) _then) : super(_value, _then); + /// Create a copy of RecommendationSeeds + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -665,7 +690,7 @@ class _$RecommendationSeedsImpl implements _RecommendationSeeds { (identical(other.valence, valence) || other.valence == valence)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, @@ -684,7 +709,9 @@ class _$RecommendationSeedsImpl implements _RecommendationSeeds { timeSignature, valence); - @JsonKey(ignore: true) + /// Create a copy of RecommendationSeeds + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$RecommendationSeedsImplCopyWith<_$RecommendationSeedsImpl> get copyWith => @@ -749,8 +776,11 @@ abstract class _RecommendationSeeds implements RecommendationSeeds { num? get timeSignature; @override num? get valence; + + /// Create a copy of RecommendationSeeds + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$RecommendationSeedsImplCopyWith<_$RecommendationSeedsImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/lib/modules/library/local_folder/cache_export_dialog.dart b/lib/modules/library/local_folder/cache_export_dialog.dart new file mode 100644 index 000000000..1d1421be5 --- /dev/null +++ b/lib/modules/library/local_folder/cache_export_dialog.dart @@ -0,0 +1,139 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:gap/gap.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:path/path.dart'; +import 'package:spotube/extensions/context.dart'; +import 'package:spotube/services/logger/logger.dart'; +import 'package:spotube/services/sourced_track/enums.dart'; + +final codecs = SourceCodecs.values.map((s) => s.name); + +class LocalFolderCacheExportDialog extends HookConsumerWidget { + final Directory exportDir; + final Directory cacheDir; + const LocalFolderCacheExportDialog({ + super.key, + required this.exportDir, + required this.cacheDir, + }); + + @override + Widget build(BuildContext context, ref) { + final ThemeData(:textTheme, :colorScheme) = Theme.of(context); + + final files = useState>([]); + final filesExported = useState(0); + + useEffect(() { + final stream = cacheDir.list().where( + (event) => + event is File && + codecs.contains(extension(event.path).replaceAll(".", "")), + ); + + stream.listen( + (event) { + files.value = [...files.value, event as File]; + }, + onError: (e, stack) { + AppLogger.reportError(e, stack); + }, + ); + return null; + }, []); + + useEffect(() { + if (filesExported.value == files.value.length && + filesExported.value > 0) { + Navigator.of(context).pop(); + } + return null; + }, [filesExported.value, files.value]); + + final isExportInProgress = + filesExported.value > 0 && filesExported.value != files.value.length; + + return AlertDialog( + title: Text(context.l10n.export_cache_files), + content: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: filesExported.value == 0 + ? Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.l10n.found_n_files(files.value.length.toString()), + ), + const Gap(10), + Text.rich( + TextSpan( + children: [ + TextSpan( + text: context.l10n.export_cache_confirmation, + ), + TextSpan( + text: "\n${exportDir.path}?", + style: textTheme.labelMedium!.copyWith( + color: colorScheme.secondary, + ), + ), + ], + ), + ), + ], + ) + : Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.l10n.exported_n_out_of_m_files( + files.value.length.toString(), + filesExported.value.toString(), + ), + ), + const Gap(10), + LinearProgressIndicator( + value: filesExported.value / files.value.length, + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: isExportInProgress + ? null + : () { + Navigator.of(context).pop(); + }, + child: Text(context.l10n.cancel), + ), + TextButton( + onPressed: isExportInProgress + ? null + : () async { + for (final file in files.value) { + try { + final destinationFile = File( + join(exportDir.path, basename(file.path)), + ); + + if (await destinationFile.exists()) { + await destinationFile.delete(); + } + await file.copy(destinationFile.path); + filesExported.value++; + } catch (e, stack) { + AppLogger.reportError(e, stack); + continue; + } + } + }, + child: Text(context.l10n.export), + ), + ], + ); + } +} diff --git a/lib/modules/library/local_folder/local_folder_item.dart b/lib/modules/library/local_folder/local_folder_item.dart index 02e47a534..a965a42d2 100644 --- a/lib/modules/library/local_folder/local_folder_item.dart +++ b/lib/modules/library/local_folder/local_folder_item.dart @@ -1,6 +1,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -10,6 +11,7 @@ import 'package:spotube/components/image/universal_image.dart'; import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/image.dart'; +import 'package:spotube/extensions/string.dart'; import 'package:spotube/hooks/utils/use_brightness_value.dart'; import 'package:spotube/pages/library/local_folder.dart'; import 'package:spotube/provider/local_tracks/local_tracks_provider.dart'; @@ -28,8 +30,10 @@ class LocalFolderItem extends HookConsumerWidget { final downloadFolder = ref.watch(userPreferencesProvider.select((s) => s.downloadLocation)); + final cacheFolder = useFuture(UserPreferencesNotifier.getMusicCacheDir()); final isDownloadFolder = folder == downloadFolder; + final isCacheFolder = folder == cacheFolder.data; final Uri(:pathSegments) = Uri.parse( folder @@ -62,6 +66,7 @@ class LocalFolderItem extends HookConsumerWidget { LocalLibraryPage.name, queryParameters: { if (isDownloadFolder) "downloads": "true", + if (isCacheFolder) "cache": "true", }, extra: folder, ); @@ -123,7 +128,9 @@ class LocalFolderItem extends HookConsumerWidget { child: Text( isDownloadFolder ? context.l10n.downloads - : basename(folder), + : isCacheFolder + ? context.l10n.cache_folder.capitalize() + : basename(folder), style: const TextStyle(fontWeight: FontWeight.bold), textAlign: TextAlign.center, ), diff --git a/lib/modules/library/user_local_tracks.dart b/lib/modules/library/user_local_tracks.dart index 926b4e80c..23fb3be0e 100644 --- a/lib/modules/library/user_local_tracks.dart +++ b/lib/modules/library/user_local_tracks.dart @@ -30,6 +30,7 @@ class UserLocalTracks extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final cacheDir = useFuture(UserPreferencesNotifier.getMusicCacheDir()); final preferencesNotifier = ref.watch(userPreferencesProvider.notifier); final preferences = ref.watch(userPreferencesProvider); @@ -83,12 +84,16 @@ class UserLocalTracks extends HookConsumerWidget { crossAxisSpacing: 10, mainAxisSpacing: 10, ), - itemCount: preferences.localLibraryLocation.length + 1, + itemCount: preferences.localLibraryLocation.length + + 1 + + (cacheDir.hasData ? 1 : 0), itemBuilder: (context, index) { return LocalFolderItem( folder: index == 0 ? preferences.downloadLocation - : preferences.localLibraryLocation[index - 1], + : index == 1 && cacheDir.hasData + ? cacheDir.data! + : preferences.localLibraryLocation[index - 1], ); }, ), diff --git a/lib/pages/home/genres/genre_playlists.dart b/lib/pages/home/genres/genre_playlists.dart index 58436bcf1..04658965e 100644 --- a/lib/pages/home/genres/genre_playlists.dart +++ b/lib/pages/home/genres/genre_playlists.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; +import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:spotify/spotify.dart' hide Offset; import 'package:spotube/collections/fake.dart'; +import 'package:spotube/hooks/utils/use_custom_status_bar_color.dart'; import 'package:spotube/modules/playlist/playlist_card.dart'; import 'package:spotube/components/image/universal_image.dart'; import 'package:spotube/components/titlebar/titlebar.dart'; @@ -27,6 +29,14 @@ class GenrePlaylistsPage extends HookConsumerWidget { final playlistsNotifier = ref.read(categoryPlaylistsProvider(category.id!).notifier); final scrollController = useScrollController(); + final routeName = GoRouterState.of(context).name; + + useCustomStatusBarColor( + Colors.black, + routeName == GenrePlaylistsPage.name, + noSetBGColor: true, + automaticSystemUiAdjustment: false, + ); return Scaffold( appBar: kIsDesktop diff --git a/lib/pages/library/local_folder.dart b/lib/pages/library/local_folder.dart index ad1d5d827..c2848b243 100644 --- a/lib/pages/library/local_folder.dart +++ b/lib/pages/library/local_folder.dart @@ -1,4 +1,8 @@ +import 'dart:io'; +import 'dart:math'; + import 'package:collection/collection.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart'; @@ -6,6 +10,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:spotube/collections/fake.dart'; import 'package:spotube/collections/spotube_icons.dart'; +import 'package:spotube/extensions/string.dart'; +import 'package:spotube/modules/library/local_folder/cache_export_dialog.dart'; import 'package:spotube/modules/library/user_local_tracks.dart'; import 'package:spotube/components/expandable_search/expandable_search.dart'; import 'package:spotube/components/fallbacks/not_found.dart'; @@ -18,6 +24,7 @@ import 'package:spotube/extensions/context.dart'; import 'package:spotube/models/local_track.dart'; import 'package:spotube/provider/local_tracks/local_tracks_provider.dart'; import 'package:spotube/provider/audio_player/audio_player.dart'; +import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/utils/service_utils.dart'; class LocalLibraryPage extends HookConsumerWidget { @@ -25,7 +32,13 @@ class LocalLibraryPage extends HookConsumerWidget { final String location; final bool isDownloads; - const LocalLibraryPage(this.location, {super.key, this.isDownloads = false}); + final bool isCache; + const LocalLibraryPage( + this.location, { + super.key, + this.isDownloads = false, + this.isCache = false, + }); Future playLocalTracks( WidgetRef ref, @@ -52,6 +65,8 @@ class LocalLibraryPage extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final ThemeData(:textTheme) = Theme.of(context); + final sortBy = useState(SortBy.none); final playlist = ref.watch(audioPlayerProvider); final trackSnapshot = ref.watch(localTracksProvider); @@ -65,14 +80,133 @@ class LocalLibraryPage extends HookConsumerWidget { final controller = useScrollController(); + final directorySize = useMemoized(() async { + final dir = Directory(location); + final files = await dir.list(recursive: true).toList(); + + final filesLength = + await Future.wait(files.whereType().map((e) => e.length())); + + return (filesLength.sum.toInt() / pow(10, 9)).toStringAsFixed(2); + }, [location]); + return SafeArea( bottom: false, child: Scaffold( appBar: PageWindowTitleBar( leading: const BackButton(), centerTitle: true, - title: Text(isDownloads ? context.l10n.downloads : location), + title: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + isDownloads + ? context.l10n.downloads + : isCache + ? context.l10n.cache_folder.capitalize() + : location, + style: textTheme.titleLarge, + ), + FutureBuilder( + future: directorySize, + builder: (context, snapshot) { + return Text( + "${(snapshot.data ?? 0)} GB", + style: textTheme.labelSmall, + ); + }, + ) + ], + ), backgroundColor: Colors.transparent, + actions: [ + if (isCache) ...[ + IconButton( + iconSize: 16, + icon: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(SpotubeIcons.delete), + Text( + context.l10n.clear_cache, + style: textTheme.labelSmall, + ) + ], + ), + onPressed: () async { + final accepted = await showDialog( + context: context, + builder: (context) => AlertDialog.adaptive( + title: Text(context.l10n.clear_cache_confirmation), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text(context.l10n.decline), + ), + TextButton( + onPressed: () async { + Navigator.of(context).pop(true); + }, + child: Text(context.l10n.accept), + ), + ], + ), + ); + + if (accepted ?? false) return; + + final cacheDir = Directory( + await UserPreferencesNotifier.getMusicCacheDir(), + ); + + if (cacheDir.existsSync()) { + await cacheDir.delete(recursive: true); + } + }, + ), + IconButton( + iconSize: 16, + icon: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(SpotubeIcons.export), + Text( + context.l10n.export, + style: textTheme.labelSmall, + ) + ], + ), + onPressed: () async { + final exportPath = + await FilePicker.platform.getDirectoryPath(); + + if (exportPath == null) return; + final exportDirectory = Directory(exportPath); + + if (!exportDirectory.existsSync()) { + await exportDirectory.create(recursive: true); + } + + final cacheDir = Directory( + await UserPreferencesNotifier.getMusicCacheDir()); + + if (!context.mounted) return; + await showDialog( + context: context, + builder: (context) { + return LocalFolderCacheExportDialog( + cacheDir: cacheDir, + exportDir: exportDirectory, + ); + }, + ); + }, + ), + ] + ], ), body: Column( children: [ diff --git a/lib/pages/lyrics/lyrics.dart b/lib/pages/lyrics/lyrics.dart index 423212f39..0f4f94733 100644 --- a/lib/pages/lyrics/lyrics.dart +++ b/lib/pages/lyrics/lyrics.dart @@ -38,10 +38,11 @@ class LyricsPage extends HookConsumerWidget { ); final palette = usePaletteColor(albumArt, ref); final mediaQuery = MediaQuery.of(context); + final route = ModalRoute.of(context); - useCustomStatusBarColor( + final resetStatusBar = useCustomStatusBarColor( palette.color, - true, + route?.isCurrent ?? false, noSetBGColor: true, ); @@ -81,53 +82,57 @@ class LyricsPage extends HookConsumerWidget { ); if (isModal) { - return DefaultTabController( - length: 2, - child: SafeArea( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), - child: Container( - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface.withOpacity(.4), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(10), - ), - ), - child: Column( - children: [ - const SizedBox(height: 5), - Container( - height: 7, - width: 150, - decoration: BoxDecoration( - color: palette.titleTextColor, - borderRadius: BorderRadius.circular(10), - ), + return PopScope( + canPop: true, + onPopInvokedWithResult: (_, __) => resetStatusBar(), + child: DefaultTabController( + length: 2, + child: SafeArea( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), + child: Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface.withOpacity(.4), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10), ), - AppBar( - leadingWidth: double.infinity, - leading: tabbar, - backgroundColor: Colors.transparent, - automaticallyImplyLeading: false, - actions: [ - IconButton( - icon: const Icon(SpotubeIcons.minimize), - onPressed: () => Navigator.of(context).pop(), + ), + child: Column( + children: [ + const SizedBox(height: 5), + Container( + height: 7, + width: 150, + decoration: BoxDecoration( + color: palette.titleTextColor, + borderRadius: BorderRadius.circular(10), ), - const SizedBox(width: 5), - ], - ), - Expanded( - child: TabBarView( - children: [ - SyncedLyrics(palette: palette, isModal: isModal), - PlainLyrics(palette: palette, isModal: isModal), + ), + AppBar( + leadingWidth: double.infinity, + leading: tabbar, + backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + actions: [ + IconButton( + icon: const Icon(SpotubeIcons.minimize), + onPressed: () => Navigator.of(context).pop(), + ), + const SizedBox(width: 5), ], ), - ), - ], + Expanded( + child: TabBarView( + children: [ + SyncedLyrics(palette: palette, isModal: isModal), + PlainLyrics(palette: palette, isModal: isModal), + ], + ), + ), + ], + ), ), ), ), diff --git a/lib/pages/settings/sections/playback.dart b/lib/pages/settings/sections/playback.dart index 1101f424c..f88687895 100644 --- a/lib/pages/settings/sections/playback.dart +++ b/lib/pages/settings/sections/playback.dart @@ -1,4 +1,5 @@ import 'package:collection/collection.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; @@ -15,6 +16,7 @@ import 'package:spotube/provider/audio_player/sources/piped_instances_provider.d import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/services/sourced_track/enums.dart'; +import 'package:spotube/utils/platform.dart'; class SettingsPlaybackSection extends HookConsumerWidget { const SettingsPlaybackSection({super.key}); @@ -239,6 +241,30 @@ class SettingsPlaybackSection extends HookConsumerWidget { ) : const SizedBox.shrink(), ), + SwitchListTile( + title: Text(context.l10n.cache_music), + subtitle: kIsMobile + ? null + : Text.rich( + TextSpan( + children: [ + TextSpan(text: "${context.l10n.open} "), + TextSpan( + text: context.l10n.cache_folder.toLowerCase(), + recognizer: TapGestureRecognizer() + ..onTap = preferencesNotifier.openCacheFolder, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.primary, + decoration: TextDecoration.underline, + ), + ) + ], + ), + ), + secondary: const Icon(SpotubeIcons.cache), + value: preferences.cacheMusic, + onChanged: preferencesNotifier.setCacheMusic, + ), ListTile( leading: const Icon(SpotubeIcons.playlistRemove), title: Text(context.l10n.blacklist), diff --git a/lib/provider/audio_player/audio_player_streams.dart b/lib/provider/audio_player/audio_player_streams.dart index 08550844c..e52b6109b 100644 --- a/lib/provider/audio_player/audio_player_streams.dart +++ b/lib/provider/audio_player/audio_player_streams.dart @@ -74,6 +74,7 @@ class AudioPlayerStreamListeners { StreamSubscription subscribeToPlaylist() { return audioPlayer.playlistStream.listen((mpvPlaylist) { try { + if (audioPlayerState.activeTrack == null) return; notificationService.addTrack(audioPlayerState.activeTrack!); discord.updatePresence(audioPlayerState.activeTrack!); updatePalette(); diff --git a/lib/provider/download_manager_provider.dart b/lib/provider/download_manager_provider.dart index 8c9ffadfe..5e9eda209 100644 --- a/lib/provider/download_manager_provider.dart +++ b/lib/provider/download_manager_provider.dart @@ -1,10 +1,10 @@ import 'dart:async'; import 'dart:io'; +import 'package:spotube/extensions/track.dart'; import 'package:spotube/services/logger/logger.dart'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:metadata_god/metadata_god.dart'; import 'package:path/path.dart'; @@ -16,6 +16,7 @@ import 'package:spotube/services/download_manager/download_manager.dart'; import 'package:spotube/services/sourced_track/enums.dart'; import 'package:spotube/services/sourced_track/sourced_track.dart'; import 'package:spotube/utils/primitive_utils.dart'; +import 'package:spotube/utils/service_utils.dart'; class DownloadManagerProvider extends ChangeNotifier { DownloadManagerProvider({required this.ref}) @@ -53,33 +54,16 @@ class DownloadManagerProvider extends ChangeNotifier { await oldFile.delete(); } - final imageBytes = await downloadImage( + final imageBytes = await ServiceUtils.downloadImage( (track.album?.images).asUrlString( placeholder: ImagePlaceholder.albumArt, index: 1, ), ); - final metadata = Metadata( - title: track.name, - artist: track.artists?.map((a) => a.name).join(", "), - album: track.album?.name, - albumArtist: track.artists?.map((a) => a.name).join(", "), - year: track.album?.releaseDate != null - ? int.tryParse(track.album!.releaseDate!.split("-").first) ?? 1969 - : 1969, - trackNumber: track.trackNumber, - discNumber: track.discNumber, - durationMs: track.durationMs?.toDouble() ?? 0.0, - fileSize: BigInt.from(await file.length()), - trackTotal: track.album?.tracks?.length ?? 0, - picture: imageBytes != null - ? Picture( - data: imageBytes, - // Spotify images are always JPEGs - mimeType: 'image/jpeg', - ) - : null, + final metadata = track.toMetadata( + fileLength: await file.length(), + imageBytes: imageBytes, ); await MetadataGod.writeMetadata( @@ -116,29 +100,6 @@ class DownloadManagerProvider extends ChangeNotifier { final Set $backHistory; final DownloadManager dl; - /// Spotify Images are always JPEGs - Future downloadImage( - String imageUrl, - ) async { - try { - final fileStream = DefaultCacheManager().getImageFile(imageUrl); - - final bytes = List.empty(growable: true); - - await for (final data in fileStream) { - if (data is FileInfo) { - bytes.addAll(data.file.readAsBytesSync()); - break; - } - } - - return Uint8List.fromList(bytes); - } catch (e, stackTrace) { - AppLogger.reportError(e, stackTrace); - return null; - } - } - String getTrackFileUrl(Track track) { final name = "${track.name} - ${track.artists?.asString() ?? ""}.${downloadCodec.name}"; diff --git a/lib/provider/local_tracks/local_tracks_provider.dart b/lib/provider/local_tracks/local_tracks_provider.dart index 513fd9b9f..3245ff2d6 100644 --- a/lib/provider/local_tracks/local_tracks_provider.dart +++ b/lib/provider/local_tracks/local_tracks_provider.dart @@ -44,14 +44,23 @@ final localTracksProvider = userPreferencesProvider.select((s) => s.downloadLocation), ); final downloadDir = Directory(downloadLocation); + final cacheDir = + Directory(await UserPreferencesNotifier.getMusicCacheDir()); if (!await downloadDir.exists()) { await downloadDir.create(recursive: true); } + if (!await cacheDir.exists()) { + await cacheDir.create(recursive: true); + } final localLibraryLocations = ref.watch( userPreferencesProvider.select((s) => s.localLibraryLocation), ); - for (final location in [downloadLocation, ...localLibraryLocations]) { + for (final location in [ + downloadLocation, + cacheDir.path, + ...localLibraryLocations + ]) { if (location.isEmpty) continue; final entities = []; if (await Directory(location).exists()) { diff --git a/lib/provider/server/routes/playback.dart b/lib/provider/server/routes/playback.dart index 93f5dac2d..34317aa1b 100644 --- a/lib/provider/server/routes/playback.dart +++ b/lib/provider/server/routes/playback.dart @@ -1,14 +1,27 @@ +import 'dart:io'; + import 'package:dio/dio.dart' hide Response; +import 'package:dio/dio.dart' as dio_lib; import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:metadata_god/metadata_god.dart'; +import 'package:path/path.dart'; import 'package:shelf/shelf.dart'; +import 'package:spotube/extensions/artist_simple.dart'; +import 'package:spotube/extensions/image.dart'; +import 'package:spotube/extensions/track.dart'; +import 'package:spotube/models/parser/range_headers.dart'; import 'package:spotube/provider/audio_player/audio_player.dart'; import 'package:spotube/provider/audio_player/state.dart'; + import 'package:spotube/provider/server/active_sourced_track.dart'; import 'package:spotube/provider/server/sourced_track.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/logger/logger.dart'; +import 'package:spotube/services/sourced_track/enums.dart'; +import 'package:spotube/services/sourced_track/sourced_track.dart'; +import 'package:spotube/utils/service_utils.dart'; class ServerPlaybackRoutes { final Ref ref; @@ -18,19 +31,139 @@ class ServerPlaybackRoutes { ServerPlaybackRoutes(this.ref) : dio = Dio(); - /// @get('/stream/') - Future getStreamTrackId(Request request, String trackId) async { - final options = Options( + Future<({dio_lib.Response response, Uint8List? bytes})> + streamTrack( + SourcedTrack track, + Map headers, + ) async { + final trackCacheFile = File( + join( + await UserPreferencesNotifier.getMusicCacheDir(), + '${track.name} - ${track.artists?.asString()} (${track.sourceInfo.id}).${track.codec.name}', + ), + ); + final trackPartialCacheFile = File("${trackCacheFile.path}.part"); + + var options = Options( headers: { - ...request.headers, + ...headers, "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", "Cache-Control": "max-age=0", "Connection": "keep-alive", + "host": Uri.parse(track.url).host, }, - responseType: ResponseType.stream, + responseType: ResponseType.bytes, validateStatus: (status) => status! < 400, ); + + final headersRes = await Future.value( + dio.head( + track.url, + options: options, + ), + ).catchError((_) async => null); + + final contentLength = headersRes?.headers.value("content-length"); + + if (await trackCacheFile.exists() && userPreferences.cacheMusic) { + final bytes = await trackCacheFile.readAsBytes(); + final cachedFileLength = bytes.length; + + return ( + response: dio_lib.Response( + statusCode: 200, + headers: Headers.fromMap({ + "content-type": ["audio/${track.codec.name}"], + "content-length": ["$cachedFileLength"], + "accept-ranges": ["bytes"], + "content-range": ["bytes 0-$cachedFileLength/$cachedFileLength"], + }), + requestOptions: RequestOptions(path: track.url), + ), + bytes: bytes, + ); + } + + /// Forcing partial content range as mpv sometimes greedily wants + /// everything at one go. Slows down overall streaming. + final range = RangeHeader.parse(headers["range"] ?? ""); + final contentPartialLength = int.tryParse(contentLength ?? ""); + if ((range.end == null) && + contentPartialLength != null && + range.start == 0) { + options = options.copyWith( + headers: { + ...?options.headers, + "range": "$range${(contentPartialLength * 0.3).ceil()}", + }, + ); + } + + final res = + await dio.get(track.url, options: options).catchError( + (e, stack) async { + final sourcedTrack = await ref + .read(sourcedTrackProvider(SpotubeMedia(track)).notifier) + .switchToAlternativeSources(); + + ref.read(activeSourcedTrackProvider.notifier).update(sourcedTrack); + + return await dio.get(sourcedTrack!.url, options: options); + }, + ); + + final bytes = res.data; + + if (bytes == null || !userPreferences.cacheMusic) { + return (response: res, bytes: bytes); + } + + final contentRange = + ContentRangeHeader.parse(res.headers.value("content-range") ?? ""); + + if (!await trackPartialCacheFile.exists()) { + await trackPartialCacheFile.create(recursive: true); + } + + // Write the stream to the file based on the range + final partialCacheFile = + await trackPartialCacheFile.open(mode: FileMode.writeOnlyAppend); + int fileLength = 0; + try { + await partialCacheFile.setPosition(contentRange.start); + await partialCacheFile.writeFrom(bytes); + fileLength = await partialCacheFile.length(); + } finally { + await partialCacheFile.close(); + } + + if (fileLength == contentRange.total) { + await trackPartialCacheFile.rename(trackCacheFile.path); + } + + if (contentRange.total == fileLength && track.codec != SourceCodecs.weba) { + final imageBytes = await ServiceUtils.downloadImage( + (track.album?.images).asUrlString( + placeholder: ImagePlaceholder.albumArt, + index: 1, + ), + ); + + await MetadataGod.writeMetadata( + file: trackCacheFile.path, + metadata: track.toMetadata( + fileLength: fileLength, + imageBytes: imageBytes, + ), + ); + } + + return (bytes: bytes, response: res); + } + + /// @get('/stream/') + Future getStreamTrackId(Request request, String trackId) async { try { final track = playlist.tracks.firstWhere((element) => element.id == trackId); @@ -41,48 +174,13 @@ class ServerPlaybackRoutes { : await ref.read(sourcedTrackProvider(SpotubeMedia(track)).future); ref.read(activeSourcedTrackProvider.notifier).update(sourcedTrack); - final res = await dio - .get( - sourcedTrack!.url, - options: options.copyWith( - headers: { - ...options.headers!, - "host": Uri.parse(sourcedTrack.url).host, - }, - ), - ) - .catchError((e, stack) async { - final sourcedTrack = await ref - .read(sourcedTrackProvider(SpotubeMedia(track)).notifier) - .switchToAlternativeSources(); - ref.read(activeSourcedTrackProvider.notifier).update(sourcedTrack); - - return await dio.get( - sourcedTrack!.url, - options: options.copyWith( - headers: { - ...options.headers!, - "host": Uri.parse(sourcedTrack.url).host, - }, - ), - ); - }); - - final audioStream = - (res.data?.stream as Stream?)?.asBroadcastStream(); - - audioStream!.listen( - (event) {}, - cancelOnError: true, - ); + final (bytes: audioBytes, response: res) = + await streamTrack(sourcedTrack!, request.headers); return Response( res.statusCode!, - body: audioStream, - context: { - "shelf.io.buffer_output": false, - }, + body: audioBytes, headers: res.headers.map, ); } catch (e, stack) { diff --git a/lib/provider/user_preferences/user_preferences_provider.dart b/lib/provider/user_preferences/user_preferences_provider.dart index 5fe3cb117..053f0994e 100644 --- a/lib/provider/user_preferences/user_preferences_provider.dart +++ b/lib/provider/user_preferences/user_preferences_provider.dart @@ -2,7 +2,7 @@ import 'package:drift/drift.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:path/path.dart'; -import 'package:path_provider/path_provider.dart'; +import 'package:path_provider/path_provider.dart' as paths; import 'package:spotify/spotify.dart'; import 'package:spotube/models/database/database.dart'; import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart'; @@ -14,6 +14,7 @@ import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/sourced_track/enums.dart'; import 'package:spotube/utils/platform.dart'; import 'package:window_manager/window_manager.dart'; +import 'package:open_file/open_file.dart'; typedef UserPreferences = PreferencesTableData; @@ -71,10 +72,10 @@ class UserPreferencesNotifier extends Notifier { if (kIsAndroid) return "/storage/emulated/0/Download/Spotube"; if (kIsMacOS) { - return join((await getLibraryDirectory()).path, "Caches"); + return join((await paths.getLibraryDirectory()).path, "Caches"); } - return getDownloadsDirectory().then((dir) { + return paths.getDownloadsDirectory().then((dir) { return join(dir!.path, "Spotube"); }); } @@ -95,6 +96,30 @@ class UserPreferencesNotifier extends Notifier { await query.replace(PreferencesTableCompanion.insert()); } + static Future getMusicCacheDir() async { + if (kIsAndroid) { + final dir = + await paths.getExternalCacheDirectories().then((dirs) => dirs!.first); + if (!await dir.exists()) { + await dir.create(recursive: true); + } + return join(dir.path, 'Cached Tracks'); + } + + final dir = await paths.getApplicationCacheDirectory(); + return join(dir.path, 'cached_tracks'); + } + + Future openCacheFolder() async { + try { + final filePath = await getMusicCacheDir(); + + await OpenFile.open(filePath); + } catch (e, stack) { + AppLogger.reportError(e, stack); + } + } + void setStreamMusicCodec(SourceCodecs codec) { setData(PreferencesTableCompanion(streamMusicCodec: Value(codec))); } @@ -211,6 +236,10 @@ class UserPreferencesNotifier extends Notifier { void setEnableConnect(bool enable) { setData(PreferencesTableCompanion(enableConnect: Value(enable))); } + + void setCacheMusic(bool cache) { + setData(PreferencesTableCompanion(cacheMusic: Value(cache))); + } } final userPreferencesProvider = diff --git a/lib/services/song_link/song_link.freezed.dart b/lib/services/song_link/song_link.freezed.dart index 0a1af8a9b..c704cde33 100644 --- a/lib/services/song_link/song_link.freezed.dart +++ b/lib/services/song_link/song_link.freezed.dart @@ -30,8 +30,12 @@ mixin _$SongLink { String? get nativeAppUriMobile => throw _privateConstructorUsedError; String? get nativeAppUriDesktop => throw _privateConstructorUsedError; + /// Serializes this SongLink to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of SongLink + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $SongLinkCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -63,6 +67,8 @@ class _$SongLinkCopyWithImpl<$Res, $Val extends SongLink> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of SongLink + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -145,6 +151,8 @@ class __$$SongLinkImplCopyWithImpl<$Res> _$SongLinkImpl _value, $Res Function(_$SongLinkImpl) _then) : super(_value, _then); + /// Create a copy of SongLink + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -261,12 +269,14 @@ class _$SongLinkImpl implements _SongLink { other.nativeAppUriDesktop == nativeAppUriDesktop)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, displayName, linkId, platform, show, uniqueId, country, url, nativeAppUriMobile, nativeAppUriDesktop); - @JsonKey(ignore: true) + /// Create a copy of SongLink + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$SongLinkImplCopyWith<_$SongLinkImpl> get copyWith => @@ -313,8 +323,11 @@ abstract class _SongLink implements SongLink { String? get nativeAppUriMobile; @override String? get nativeAppUriDesktop; + + /// Create a copy of SongLink + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$SongLinkImplCopyWith<_$SongLinkImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/lib/utils/migrations/adapters.freezed.dart b/lib/utils/migrations/adapters.freezed.dart index 339ec0e5b..40dfd6628 100644 --- a/lib/utils/migrations/adapters.freezed.dart +++ b/lib/utils/migrations/adapters.freezed.dart @@ -53,8 +53,12 @@ mixin _$UserPreferences { bool get endlessPlayback => throw _privateConstructorUsedError; bool get enableConnect => throw _privateConstructorUsedError; + /// Serializes this UserPreferences to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of UserPreferences + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $UserPreferencesCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -110,6 +114,8 @@ class _$UserPreferencesCopyWithImpl<$Res, $Val extends UserPreferences> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of UserPreferences + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -290,6 +296,8 @@ class __$$UserPreferencesImplCopyWithImpl<$Res> _$UserPreferencesImpl _value, $Res Function(_$UserPreferencesImpl) _then) : super(_value, _then); + /// Create a copy of UserPreferences + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -605,7 +613,7 @@ class _$UserPreferencesImpl implements _UserPreferences { other.enableConnect == enableConnect)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hashAll([ runtimeType, @@ -635,7 +643,9 @@ class _$UserPreferencesImpl implements _UserPreferences { enableConnect ]); - @JsonKey(ignore: true) + /// Create a copy of UserPreferences + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$UserPreferencesImplCopyWith<_$UserPreferencesImpl> get copyWith => @@ -744,8 +754,11 @@ abstract class _UserPreferences implements UserPreferences { bool get endlessPlayback; @override bool get enableConnect; + + /// Create a copy of UserPreferences + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$UserPreferencesImplCopyWith<_$UserPreferencesImpl> get copyWith => throw _privateConstructorUsedError; } @@ -812,8 +825,13 @@ mixin _$PlaybackHistoryItem { required TResult orElse(), }) => throw _privateConstructorUsedError; + + /// Serializes this PlaybackHistoryItem to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of PlaybackHistoryItem + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $PlaybackHistoryItemCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -837,6 +855,8 @@ class _$PlaybackHistoryItemCopyWithImpl<$Res, $Val extends PlaybackHistoryItem> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of PlaybackHistoryItem + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -873,6 +893,8 @@ class __$$PlaybackHistoryPlaylistImplCopyWithImpl<$Res> $Res Function(_$PlaybackHistoryPlaylistImpl) _then) : super(_value, _then); + /// Create a copy of PlaybackHistoryItem + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -925,11 +947,13 @@ class _$PlaybackHistoryPlaylistImpl implements PlaybackHistoryPlaylist { other.playlist == playlist)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, date, playlist); - @JsonKey(ignore: true) + /// Create a copy of PlaybackHistoryItem + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$PlaybackHistoryPlaylistImplCopyWith<_$PlaybackHistoryPlaylistImpl> @@ -1023,8 +1047,11 @@ abstract class PlaybackHistoryPlaylist implements PlaybackHistoryItem { @override DateTime get date; PlaylistSimple get playlist; + + /// Create a copy of PlaybackHistoryItem + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$PlaybackHistoryPlaylistImplCopyWith<_$PlaybackHistoryPlaylistImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1048,6 +1075,8 @@ class __$$PlaybackHistoryAlbumImplCopyWithImpl<$Res> $Res Function(_$PlaybackHistoryAlbumImpl) _then) : super(_value, _then); + /// Create a copy of PlaybackHistoryItem + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1099,11 +1128,13 @@ class _$PlaybackHistoryAlbumImpl implements PlaybackHistoryAlbum { (identical(other.album, album) || other.album == album)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, date, album); - @JsonKey(ignore: true) + /// Create a copy of PlaybackHistoryItem + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$PlaybackHistoryAlbumImplCopyWith<_$PlaybackHistoryAlbumImpl> @@ -1198,8 +1229,11 @@ abstract class PlaybackHistoryAlbum implements PlaybackHistoryItem { @override DateTime get date; AlbumSimple get album; + + /// Create a copy of PlaybackHistoryItem + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$PlaybackHistoryAlbumImplCopyWith<_$PlaybackHistoryAlbumImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1223,6 +1257,8 @@ class __$$PlaybackHistoryTrackImplCopyWithImpl<$Res> $Res Function(_$PlaybackHistoryTrackImpl) _then) : super(_value, _then); + /// Create a copy of PlaybackHistoryItem + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1274,11 +1310,13 @@ class _$PlaybackHistoryTrackImpl implements PlaybackHistoryTrack { (identical(other.track, track) || other.track == track)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, date, track); - @JsonKey(ignore: true) + /// Create a copy of PlaybackHistoryItem + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$PlaybackHistoryTrackImplCopyWith<_$PlaybackHistoryTrackImpl> @@ -1373,8 +1411,11 @@ abstract class PlaybackHistoryTrack implements PlaybackHistoryItem { @override DateTime get date; Track get track; + + /// Create a copy of PlaybackHistoryItem + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$PlaybackHistoryTrackImplCopyWith<_$PlaybackHistoryTrackImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/lib/utils/service_utils.dart b/lib/utils/service_utils.dart index c00f07ab9..bdc3877ad 100644 --- a/lib/utils/service_utils.dart +++ b/lib/utils/service_utils.dart @@ -1,4 +1,7 @@ +import 'dart:typed_data'; + import 'package:dio/dio.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:go_router/go_router.dart'; import 'package:html/dom.dart' hide Text; import 'package:spotify/spotify.dart'; @@ -8,6 +11,7 @@ import 'package:spotube/modules/root/update_dialog.dart'; import 'package:spotube/models/lyrics.dart'; import 'package:spotube/provider/database/database.dart'; import 'package:spotube/services/dio/dio.dart'; +import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/sourced_track/sourced_track.dart'; import 'package:spotube/utils/primitive_utils.dart'; @@ -449,4 +453,27 @@ abstract class ServiceUtils { ); } } + + /// Spotify Images are always JPEGs + static Future downloadImage( + String imageUrl, + ) async { + try { + final fileStream = DefaultCacheManager().getImageFile(imageUrl); + + final bytes = List.empty(growable: true); + + await for (final data in fileStream) { + if (data is FileInfo) { + bytes.addAll(data.file.readAsBytesSync()); + break; + } + } + + return Uint8List.fromList(bytes); + } catch (e, stackTrace) { + AppLogger.reportError(e, stackTrace); + return null; + } + } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index feeba39f2..06c33e052 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); + g_autoptr(FlPluginRegistrar) open_file_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "OpenFileLinuxPlugin"); + open_file_linux_plugin_register_with_registrar(open_file_linux_registrar); 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); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 3f37d5e64..e98b18ebc 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST gtk local_notifier media_kit_libs_linux + open_file_linux screen_retriever_linux sqlite3_flutter_libs system_theme diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 69e363a6c..608a854e0 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -16,6 +16,7 @@ import flutter_inappwebview_macos import flutter_secure_storage_macos import local_notifier import media_kit_libs_macos_audio +import open_file_mac import package_info_plus import path_provider_foundation import screen_retriever_macos @@ -39,6 +40,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin")) MediaKitLibsMacosAudioPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosAudioPlugin")) + OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 7afbf5532..fee425158 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -30,6 +30,8 @@ PODS: - FlutterMacOS - metadata_god (0.0.1): - FlutterMacOS + - open_file_mac (0.0.1): + - FlutterMacOS - OrderedSet (6.0.3) - package_info_plus (0.0.1): - FlutterMacOS @@ -87,6 +89,7 @@ DEPENDENCIES: - media_kit_libs_macos_audio (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_audio/macos`) - media_kit_native_event_loop (from `Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos`) - metadata_god (from `Flutter/ephemeral/.symlinks/plugins/metadata_god/macos`) + - open_file_mac (from `Flutter/ephemeral/.symlinks/plugins/open_file_mac/macos`) - 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`) @@ -134,6 +137,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos metadata_god: :path: Flutter/ephemeral/.symlinks/plugins/metadata_god/macos + open_file_mac: + :path: Flutter/ephemeral/.symlinks/plugins/open_file_mac/macos package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos path_provider_foundation: @@ -171,6 +176,7 @@ SPEC CHECKSUMS: media_kit_libs_macos_audio: 3871782a4f3f84c77f04d7666c87800a781c24da media_kit_native_event_loop: 7321675377cb9ae8596a29bddf3a3d2b5e8792c5 metadata_god: 829f61208b44ac1173e7cd32ab740d8776be5435 + open_file_mac: 0e554648e2a87ce59e9438e3e5ca3e552e90d89a OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 @@ -186,4 +192,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 0d3963a09fc94f580682bd88480486da345dc3f0 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/pubspec.lock b/pubspec.lock index 4b0912dd5..fd9c39612 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1555,6 +1555,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + open_file: + dependency: "direct main" + description: + name: open_file + sha256: d17e2bddf5b278cb2ae18393d0496aa4f162142ba97d1a9e0c30d476adf99c0e + url: "https://pub.dev" + source: hosted + version: "3.5.10" + open_file_android: + dependency: transitive + description: + name: open_file_android + sha256: "58141fcaece2f453a9684509a7275f231ac0e3d6ceb9a5e6de310a7dff9084aa" + url: "https://pub.dev" + source: hosted + version: "1.0.6" + open_file_ios: + dependency: transitive + description: + name: open_file_ios + sha256: "02996f01e5f6863832068e97f8f3a5ef9b613516db6897f373b43b79849e4d07" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + open_file_linux: + dependency: transitive + description: + name: open_file_linux + sha256: d189f799eecbb139c97f8bc7d303f9e720954fa4e0fa1b0b7294767e5f2d7550 + url: "https://pub.dev" + source: hosted + version: "0.0.5" + open_file_mac: + dependency: transitive + description: + name: open_file_mac + sha256: "1440b1e37ceb0642208cfeb2c659c6cda27b25187a90635c9d1acb7d0584d324" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + open_file_platform_interface: + dependency: transitive + description: + name: open_file_platform_interface + sha256: "101b424ca359632699a7e1213e83d025722ab668b9fd1412338221bf9b0e5757" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + open_file_web: + dependency: transitive + description: + name: open_file_web + sha256: e3dbc9584856283dcb30aef5720558b90f88036360bd078e494ab80a80130c4f + url: "https://pub.dev" + source: hosted + version: "0.0.4" + open_file_windows: + dependency: transitive + description: + name: open_file_windows + sha256: d26c31ddf935a94a1a3aa43a23f4fff8a5ff4eea395fe7a8cb819cf55431c875 + url: "https://pub.dev" + source: hosted + version: "0.0.3" package_config: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 310d51214..4551f956c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -84,6 +84,7 @@ dependencies: media_kit_libs_audio: ^1.0.4 metadata_god: ^1.0.0 mime: ^1.0.2 + open_file: ^3.5.10 package_info_plus: ^6.0.0 palette_generator: ^0.3.3 path: ^1.9.0 diff --git a/test/drift/app_db/generated/schema.dart b/test/drift/app_db/generated/schema.dart index 1b2142be9..9e67e542a 100644 --- a/test/drift/app_db/generated/schema.dart +++ b/test/drift/app_db/generated/schema.dart @@ -1,8 +1,9 @@ +// dart format width=80 // GENERATED CODE, DO NOT EDIT BY HAND. // ignore_for_file: type=lint -//@dart=2.12 import 'package:drift/drift.dart'; import 'package:drift/internal/migrations.dart'; +import 'schema_v3.dart' as v3; import 'schema_v2.dart' as v2; import 'schema_v1.dart' as v1; @@ -10,6 +11,8 @@ class GeneratedHelper implements SchemaInstantiationHelper { @override GeneratedDatabase databaseForVersion(QueryExecutor db, int version) { switch (version) { + case 3: + return v3.DatabaseAtV3(db); case 2: return v2.DatabaseAtV2(db); case 1: @@ -19,5 +22,5 @@ class GeneratedHelper implements SchemaInstantiationHelper { } } - static const versions = const [1, 2]; + static const versions = const [1, 2, 3]; } diff --git a/test/drift/app_db/generated/schema_v1.dart b/test/drift/app_db/generated/schema_v1.dart index 9535d6854..ca8485616 100644 --- a/test/drift/app_db/generated/schema_v1.dart +++ b/test/drift/app_db/generated/schema_v1.dart @@ -1,11 +1,7 @@ +// dart format width=80 // GENERATED CODE, DO NOT EDIT BY HAND. // ignore_for_file: type=lint -//@dart=2.12 import 'package:drift/drift.dart'; -import 'package:flutter/material.dart' hide Table; -import 'package:spotify/spotify.dart'; -import 'package:spotube/services/sourced_track/enums.dart'; -import 'package:spotube/utils/migrations/adapters.dart'; class AuthenticationTable extends Table with TableInfo { diff --git a/test/drift/app_db/generated/schema_v2.dart b/test/drift/app_db/generated/schema_v2.dart index caf34ab96..c9642f868 100644 --- a/test/drift/app_db/generated/schema_v2.dart +++ b/test/drift/app_db/generated/schema_v2.dart @@ -1,11 +1,7 @@ +// dart format width=80 // GENERATED CODE, DO NOT EDIT BY HAND. // ignore_for_file: type=lint -//@dart=2.12 import 'package:drift/drift.dart'; -import 'package:flutter/material.dart' hide Table; -import 'package:spotify/spotify.dart'; -import 'package:spotube/services/sourced_track/enums.dart'; -import 'package:spotube/utils/migrations/adapters.dart'; class AuthenticationTable extends Table with TableInfo { diff --git a/test/drift/app_db/generated/schema_v3.dart b/test/drift/app_db/generated/schema_v3.dart new file mode 100644 index 000000000..f6416823d --- /dev/null +++ b/test/drift/app_db/generated/schema_v3.dart @@ -0,0 +1,3396 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class AuthenticationTable extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AuthenticationTable(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn cookie = GeneratedColumn( + 'cookie', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn accessToken = GeneratedColumn( + 'access_token', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn expiration = GeneratedColumn( + 'expiration', aliasedName, false, + type: DriftSqlType.dateTime, requiredDuringInsert: true); + @override + List get $columns => [id, cookie, accessToken, expiration]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'authentication_table'; + @override + Set get $primaryKey => {id}; + @override + AuthenticationTableData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AuthenticationTableData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + cookie: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}cookie'])!, + accessToken: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}access_token'])!, + expiration: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}expiration'])!, + ); + } + + @override + AuthenticationTable createAlias(String alias) { + return AuthenticationTable(attachedDatabase, alias); + } +} + +class AuthenticationTableData extends DataClass + implements Insertable { + final int id; + final String cookie; + final String accessToken; + final DateTime expiration; + const AuthenticationTableData( + {required this.id, + required this.cookie, + required this.accessToken, + required this.expiration}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['cookie'] = Variable(cookie); + map['access_token'] = Variable(accessToken); + map['expiration'] = Variable(expiration); + return map; + } + + AuthenticationTableCompanion toCompanion(bool nullToAbsent) { + return AuthenticationTableCompanion( + id: Value(id), + cookie: Value(cookie), + accessToken: Value(accessToken), + expiration: Value(expiration), + ); + } + + factory AuthenticationTableData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AuthenticationTableData( + id: serializer.fromJson(json['id']), + cookie: serializer.fromJson(json['cookie']), + accessToken: serializer.fromJson(json['accessToken']), + expiration: serializer.fromJson(json['expiration']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'cookie': serializer.toJson(cookie), + 'accessToken': serializer.toJson(accessToken), + 'expiration': serializer.toJson(expiration), + }; + } + + AuthenticationTableData copyWith( + {int? id, + String? cookie, + String? accessToken, + DateTime? expiration}) => + AuthenticationTableData( + id: id ?? this.id, + cookie: cookie ?? this.cookie, + accessToken: accessToken ?? this.accessToken, + expiration: expiration ?? this.expiration, + ); + AuthenticationTableData copyWithCompanion(AuthenticationTableCompanion data) { + return AuthenticationTableData( + id: data.id.present ? data.id.value : this.id, + cookie: data.cookie.present ? data.cookie.value : this.cookie, + accessToken: + data.accessToken.present ? data.accessToken.value : this.accessToken, + expiration: + data.expiration.present ? data.expiration.value : this.expiration, + ); + } + + @override + String toString() { + return (StringBuffer('AuthenticationTableData(') + ..write('id: $id, ') + ..write('cookie: $cookie, ') + ..write('accessToken: $accessToken, ') + ..write('expiration: $expiration') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, cookie, accessToken, expiration); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AuthenticationTableData && + other.id == this.id && + other.cookie == this.cookie && + other.accessToken == this.accessToken && + other.expiration == this.expiration); +} + +class AuthenticationTableCompanion + extends UpdateCompanion { + final Value id; + final Value cookie; + final Value accessToken; + final Value expiration; + const AuthenticationTableCompanion({ + this.id = const Value.absent(), + this.cookie = const Value.absent(), + this.accessToken = const Value.absent(), + this.expiration = const Value.absent(), + }); + AuthenticationTableCompanion.insert({ + this.id = const Value.absent(), + required String cookie, + required String accessToken, + required DateTime expiration, + }) : cookie = Value(cookie), + accessToken = Value(accessToken), + expiration = Value(expiration); + static Insertable custom({ + Expression? id, + Expression? cookie, + Expression? accessToken, + Expression? expiration, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (cookie != null) 'cookie': cookie, + if (accessToken != null) 'access_token': accessToken, + if (expiration != null) 'expiration': expiration, + }); + } + + AuthenticationTableCompanion copyWith( + {Value? id, + Value? cookie, + Value? accessToken, + Value? expiration}) { + return AuthenticationTableCompanion( + id: id ?? this.id, + cookie: cookie ?? this.cookie, + accessToken: accessToken ?? this.accessToken, + expiration: expiration ?? this.expiration, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (cookie.present) { + map['cookie'] = Variable(cookie.value); + } + if (accessToken.present) { + map['access_token'] = Variable(accessToken.value); + } + if (expiration.present) { + map['expiration'] = Variable(expiration.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AuthenticationTableCompanion(') + ..write('id: $id, ') + ..write('cookie: $cookie, ') + ..write('accessToken: $accessToken, ') + ..write('expiration: $expiration') + ..write(')')) + .toString(); + } +} + +class BlacklistTable extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + BlacklistTable(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn elementType = GeneratedColumn( + 'element_type', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn elementId = GeneratedColumn( + 'element_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => [id, name, elementType, elementId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'blacklist_table'; + @override + Set get $primaryKey => {id}; + @override + BlacklistTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return BlacklistTableData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + elementType: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}element_type'])!, + elementId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}element_id'])!, + ); + } + + @override + BlacklistTable createAlias(String alias) { + return BlacklistTable(attachedDatabase, alias); + } +} + +class BlacklistTableData extends DataClass + implements Insertable { + final int id; + final String name; + final String elementType; + final String elementId; + const BlacklistTableData( + {required this.id, + required this.name, + required this.elementType, + required this.elementId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['element_type'] = Variable(elementType); + map['element_id'] = Variable(elementId); + return map; + } + + BlacklistTableCompanion toCompanion(bool nullToAbsent) { + return BlacklistTableCompanion( + id: Value(id), + name: Value(name), + elementType: Value(elementType), + elementId: Value(elementId), + ); + } + + factory BlacklistTableData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return BlacklistTableData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + elementType: serializer.fromJson(json['elementType']), + elementId: serializer.fromJson(json['elementId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'elementType': serializer.toJson(elementType), + 'elementId': serializer.toJson(elementId), + }; + } + + BlacklistTableData copyWith( + {int? id, String? name, String? elementType, String? elementId}) => + BlacklistTableData( + id: id ?? this.id, + name: name ?? this.name, + elementType: elementType ?? this.elementType, + elementId: elementId ?? this.elementId, + ); + BlacklistTableData copyWithCompanion(BlacklistTableCompanion data) { + return BlacklistTableData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + elementType: + data.elementType.present ? data.elementType.value : this.elementType, + elementId: data.elementId.present ? data.elementId.value : this.elementId, + ); + } + + @override + String toString() { + return (StringBuffer('BlacklistTableData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('elementType: $elementType, ') + ..write('elementId: $elementId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, elementType, elementId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is BlacklistTableData && + other.id == this.id && + other.name == this.name && + other.elementType == this.elementType && + other.elementId == this.elementId); +} + +class BlacklistTableCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value elementType; + final Value elementId; + const BlacklistTableCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.elementType = const Value.absent(), + this.elementId = const Value.absent(), + }); + BlacklistTableCompanion.insert({ + this.id = const Value.absent(), + required String name, + required String elementType, + required String elementId, + }) : name = Value(name), + elementType = Value(elementType), + elementId = Value(elementId); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? elementType, + Expression? elementId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (elementType != null) 'element_type': elementType, + if (elementId != null) 'element_id': elementId, + }); + } + + BlacklistTableCompanion copyWith( + {Value? id, + Value? name, + Value? elementType, + Value? elementId}) { + return BlacklistTableCompanion( + id: id ?? this.id, + name: name ?? this.name, + elementType: elementType ?? this.elementType, + elementId: elementId ?? this.elementId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (elementType.present) { + map['element_type'] = Variable(elementType.value); + } + if (elementId.present) { + map['element_id'] = Variable(elementId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('BlacklistTableCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('elementType: $elementType, ') + ..write('elementId: $elementId') + ..write(')')) + .toString(); + } +} + +class PreferencesTable extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PreferencesTable(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn audioQuality = GeneratedColumn( + 'audio_quality', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: Constant(SourceQualities.high.name)); + late final GeneratedColumn albumColorSync = GeneratedColumn( + 'album_color_sync', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("album_color_sync" IN (0, 1))'), + defaultValue: const Constant(true)); + late final GeneratedColumn amoledDarkTheme = GeneratedColumn( + 'amoled_dark_theme', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("amoled_dark_theme" IN (0, 1))'), + defaultValue: const Constant(false)); + late final GeneratedColumn checkUpdate = GeneratedColumn( + 'check_update', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("check_update" IN (0, 1))'), + defaultValue: const Constant(true)); + late final GeneratedColumn normalizeAudio = GeneratedColumn( + 'normalize_audio', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("normalize_audio" IN (0, 1))'), + defaultValue: const Constant(false)); + late final GeneratedColumn showSystemTrayIcon = GeneratedColumn( + 'show_system_tray_icon', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("show_system_tray_icon" IN (0, 1))'), + defaultValue: const Constant(false)); + late final GeneratedColumn systemTitleBar = GeneratedColumn( + 'system_title_bar', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("system_title_bar" IN (0, 1))'), + defaultValue: const Constant(false)); + late final GeneratedColumn skipNonMusic = GeneratedColumn( + 'skip_non_music', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("skip_non_music" IN (0, 1))'), + defaultValue: const Constant(false)); + late final GeneratedColumn closeBehavior = GeneratedColumn( + 'close_behavior', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: Constant(CloseBehavior.close.name)); + late final GeneratedColumn accentColorScheme = + GeneratedColumn('accent_color_scheme', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const Constant("Blue:0xFF2196F3")); + late final GeneratedColumn layoutMode = GeneratedColumn( + 'layout_mode', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: Constant(LayoutMode.adaptive.name)); + late final GeneratedColumn locale = GeneratedColumn( + 'locale', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: + const Constant('{"languageCode":"system","countryCode":"system"}')); + late final GeneratedColumn market = GeneratedColumn( + 'market', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: Constant(Market.US.name)); + late final GeneratedColumn searchMode = GeneratedColumn( + 'search_mode', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: Constant(SearchMode.youtube.name)); + late final GeneratedColumn downloadLocation = GeneratedColumn( + 'download_location', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const Constant("")); + late final GeneratedColumn localLibraryLocation = + GeneratedColumn('local_library_location', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const Constant("")); + late final GeneratedColumn pipedInstance = GeneratedColumn( + 'piped_instance', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const Constant("https://pipedapi.kavin.rocks")); + late final GeneratedColumn invidiousInstance = + GeneratedColumn('invidious_instance', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const Constant("https://inv.nadeko.net")); + late final GeneratedColumn themeMode = GeneratedColumn( + 'theme_mode', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: Constant(ThemeMode.system.name)); + late final GeneratedColumn audioSource = GeneratedColumn( + 'audio_source', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: Constant(AudioSource.youtube.name)); + late final GeneratedColumn streamMusicCodec = GeneratedColumn( + 'stream_music_codec', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: Constant(SourceCodecs.weba.name)); + late final GeneratedColumn downloadMusicCodec = + GeneratedColumn('download_music_codec', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: Constant(SourceCodecs.m4a.name)); + late final GeneratedColumn discordPresence = GeneratedColumn( + 'discord_presence', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("discord_presence" IN (0, 1))'), + defaultValue: const Constant(true)); + late final GeneratedColumn endlessPlayback = GeneratedColumn( + 'endless_playback', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("endless_playback" IN (0, 1))'), + defaultValue: const Constant(true)); + late final GeneratedColumn enableConnect = GeneratedColumn( + 'enable_connect', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("enable_connect" IN (0, 1))'), + defaultValue: const Constant(false)); + late final GeneratedColumn cacheMusic = GeneratedColumn( + 'cache_music', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("cache_music" IN (0, 1))'), + defaultValue: const Constant(true)); + @override + List get $columns => [ + id, + audioQuality, + albumColorSync, + amoledDarkTheme, + checkUpdate, + normalizeAudio, + showSystemTrayIcon, + systemTitleBar, + skipNonMusic, + closeBehavior, + accentColorScheme, + layoutMode, + locale, + market, + searchMode, + downloadLocation, + localLibraryLocation, + pipedInstance, + invidiousInstance, + themeMode, + audioSource, + streamMusicCodec, + downloadMusicCodec, + discordPresence, + endlessPlayback, + enableConnect, + cacheMusic + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'preferences_table'; + @override + Set get $primaryKey => {id}; + @override + PreferencesTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PreferencesTableData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + audioQuality: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}audio_quality'])!, + albumColorSync: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}album_color_sync'])!, + amoledDarkTheme: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}amoled_dark_theme'])!, + checkUpdate: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}check_update'])!, + normalizeAudio: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}normalize_audio'])!, + showSystemTrayIcon: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}show_system_tray_icon'])!, + systemTitleBar: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}system_title_bar'])!, + skipNonMusic: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}skip_non_music'])!, + closeBehavior: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}close_behavior'])!, + accentColorScheme: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}accent_color_scheme'])!, + layoutMode: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}layout_mode'])!, + locale: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}locale'])!, + market: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}market'])!, + searchMode: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}search_mode'])!, + downloadLocation: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}download_location'])!, + localLibraryLocation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}local_library_location'])!, + pipedInstance: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}piped_instance'])!, + invidiousInstance: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}invidious_instance'])!, + themeMode: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}theme_mode'])!, + audioSource: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}audio_source'])!, + streamMusicCodec: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}stream_music_codec'])!, + downloadMusicCodec: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}download_music_codec'])!, + discordPresence: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}discord_presence'])!, + endlessPlayback: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}endless_playback'])!, + enableConnect: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}enable_connect'])!, + cacheMusic: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}cache_music'])!, + ); + } + + @override + PreferencesTable createAlias(String alias) { + return PreferencesTable(attachedDatabase, alias); + } +} + +class PreferencesTableData extends DataClass + implements Insertable { + final int id; + final String audioQuality; + final bool albumColorSync; + final bool amoledDarkTheme; + final bool checkUpdate; + final bool normalizeAudio; + final bool showSystemTrayIcon; + final bool systemTitleBar; + final bool skipNonMusic; + final String closeBehavior; + final String accentColorScheme; + final String layoutMode; + final String locale; + final String market; + final String searchMode; + final String downloadLocation; + final String localLibraryLocation; + final String pipedInstance; + final String invidiousInstance; + final String themeMode; + final String audioSource; + final String streamMusicCodec; + final String downloadMusicCodec; + final bool discordPresence; + final bool endlessPlayback; + final bool enableConnect; + final bool cacheMusic; + const PreferencesTableData( + {required this.id, + required this.audioQuality, + required this.albumColorSync, + required this.amoledDarkTheme, + required this.checkUpdate, + required this.normalizeAudio, + required this.showSystemTrayIcon, + required this.systemTitleBar, + required this.skipNonMusic, + required this.closeBehavior, + required this.accentColorScheme, + required this.layoutMode, + required this.locale, + required this.market, + required this.searchMode, + required this.downloadLocation, + required this.localLibraryLocation, + required this.pipedInstance, + required this.invidiousInstance, + required this.themeMode, + required this.audioSource, + required this.streamMusicCodec, + required this.downloadMusicCodec, + required this.discordPresence, + required this.endlessPlayback, + required this.enableConnect, + required this.cacheMusic}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['audio_quality'] = Variable(audioQuality); + map['album_color_sync'] = Variable(albumColorSync); + map['amoled_dark_theme'] = Variable(amoledDarkTheme); + map['check_update'] = Variable(checkUpdate); + map['normalize_audio'] = Variable(normalizeAudio); + map['show_system_tray_icon'] = Variable(showSystemTrayIcon); + map['system_title_bar'] = Variable(systemTitleBar); + map['skip_non_music'] = Variable(skipNonMusic); + map['close_behavior'] = Variable(closeBehavior); + map['accent_color_scheme'] = Variable(accentColorScheme); + map['layout_mode'] = Variable(layoutMode); + map['locale'] = Variable(locale); + map['market'] = Variable(market); + map['search_mode'] = Variable(searchMode); + map['download_location'] = Variable(downloadLocation); + map['local_library_location'] = Variable(localLibraryLocation); + map['piped_instance'] = Variable(pipedInstance); + map['invidious_instance'] = Variable(invidiousInstance); + map['theme_mode'] = Variable(themeMode); + map['audio_source'] = Variable(audioSource); + map['stream_music_codec'] = Variable(streamMusicCodec); + map['download_music_codec'] = Variable(downloadMusicCodec); + map['discord_presence'] = Variable(discordPresence); + map['endless_playback'] = Variable(endlessPlayback); + map['enable_connect'] = Variable(enableConnect); + map['cache_music'] = Variable(cacheMusic); + return map; + } + + PreferencesTableCompanion toCompanion(bool nullToAbsent) { + return PreferencesTableCompanion( + id: Value(id), + audioQuality: Value(audioQuality), + albumColorSync: Value(albumColorSync), + amoledDarkTheme: Value(amoledDarkTheme), + checkUpdate: Value(checkUpdate), + normalizeAudio: Value(normalizeAudio), + showSystemTrayIcon: Value(showSystemTrayIcon), + systemTitleBar: Value(systemTitleBar), + skipNonMusic: Value(skipNonMusic), + closeBehavior: Value(closeBehavior), + accentColorScheme: Value(accentColorScheme), + layoutMode: Value(layoutMode), + locale: Value(locale), + market: Value(market), + searchMode: Value(searchMode), + downloadLocation: Value(downloadLocation), + localLibraryLocation: Value(localLibraryLocation), + pipedInstance: Value(pipedInstance), + invidiousInstance: Value(invidiousInstance), + themeMode: Value(themeMode), + audioSource: Value(audioSource), + streamMusicCodec: Value(streamMusicCodec), + downloadMusicCodec: Value(downloadMusicCodec), + discordPresence: Value(discordPresence), + endlessPlayback: Value(endlessPlayback), + enableConnect: Value(enableConnect), + cacheMusic: Value(cacheMusic), + ); + } + + factory PreferencesTableData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PreferencesTableData( + id: serializer.fromJson(json['id']), + audioQuality: serializer.fromJson(json['audioQuality']), + albumColorSync: serializer.fromJson(json['albumColorSync']), + amoledDarkTheme: serializer.fromJson(json['amoledDarkTheme']), + checkUpdate: serializer.fromJson(json['checkUpdate']), + normalizeAudio: serializer.fromJson(json['normalizeAudio']), + showSystemTrayIcon: serializer.fromJson(json['showSystemTrayIcon']), + systemTitleBar: serializer.fromJson(json['systemTitleBar']), + skipNonMusic: serializer.fromJson(json['skipNonMusic']), + closeBehavior: serializer.fromJson(json['closeBehavior']), + accentColorScheme: serializer.fromJson(json['accentColorScheme']), + layoutMode: serializer.fromJson(json['layoutMode']), + locale: serializer.fromJson(json['locale']), + market: serializer.fromJson(json['market']), + searchMode: serializer.fromJson(json['searchMode']), + downloadLocation: serializer.fromJson(json['downloadLocation']), + localLibraryLocation: + serializer.fromJson(json['localLibraryLocation']), + pipedInstance: serializer.fromJson(json['pipedInstance']), + invidiousInstance: serializer.fromJson(json['invidiousInstance']), + themeMode: serializer.fromJson(json['themeMode']), + audioSource: serializer.fromJson(json['audioSource']), + streamMusicCodec: serializer.fromJson(json['streamMusicCodec']), + downloadMusicCodec: + serializer.fromJson(json['downloadMusicCodec']), + discordPresence: serializer.fromJson(json['discordPresence']), + endlessPlayback: serializer.fromJson(json['endlessPlayback']), + enableConnect: serializer.fromJson(json['enableConnect']), + cacheMusic: serializer.fromJson(json['cacheMusic']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'audioQuality': serializer.toJson(audioQuality), + 'albumColorSync': serializer.toJson(albumColorSync), + 'amoledDarkTheme': serializer.toJson(amoledDarkTheme), + 'checkUpdate': serializer.toJson(checkUpdate), + 'normalizeAudio': serializer.toJson(normalizeAudio), + 'showSystemTrayIcon': serializer.toJson(showSystemTrayIcon), + 'systemTitleBar': serializer.toJson(systemTitleBar), + 'skipNonMusic': serializer.toJson(skipNonMusic), + 'closeBehavior': serializer.toJson(closeBehavior), + 'accentColorScheme': serializer.toJson(accentColorScheme), + 'layoutMode': serializer.toJson(layoutMode), + 'locale': serializer.toJson(locale), + 'market': serializer.toJson(market), + 'searchMode': serializer.toJson(searchMode), + 'downloadLocation': serializer.toJson(downloadLocation), + 'localLibraryLocation': serializer.toJson(localLibraryLocation), + 'pipedInstance': serializer.toJson(pipedInstance), + 'invidiousInstance': serializer.toJson(invidiousInstance), + 'themeMode': serializer.toJson(themeMode), + 'audioSource': serializer.toJson(audioSource), + 'streamMusicCodec': serializer.toJson(streamMusicCodec), + 'downloadMusicCodec': serializer.toJson(downloadMusicCodec), + 'discordPresence': serializer.toJson(discordPresence), + 'endlessPlayback': serializer.toJson(endlessPlayback), + 'enableConnect': serializer.toJson(enableConnect), + 'cacheMusic': serializer.toJson(cacheMusic), + }; + } + + PreferencesTableData copyWith( + {int? id, + String? audioQuality, + bool? albumColorSync, + bool? amoledDarkTheme, + bool? checkUpdate, + bool? normalizeAudio, + bool? showSystemTrayIcon, + bool? systemTitleBar, + bool? skipNonMusic, + String? closeBehavior, + String? accentColorScheme, + String? layoutMode, + String? locale, + String? market, + String? searchMode, + String? downloadLocation, + String? localLibraryLocation, + String? pipedInstance, + String? invidiousInstance, + String? themeMode, + String? audioSource, + String? streamMusicCodec, + String? downloadMusicCodec, + bool? discordPresence, + bool? endlessPlayback, + bool? enableConnect, + bool? cacheMusic}) => + PreferencesTableData( + id: id ?? this.id, + audioQuality: audioQuality ?? this.audioQuality, + albumColorSync: albumColorSync ?? this.albumColorSync, + amoledDarkTheme: amoledDarkTheme ?? this.amoledDarkTheme, + checkUpdate: checkUpdate ?? this.checkUpdate, + normalizeAudio: normalizeAudio ?? this.normalizeAudio, + showSystemTrayIcon: showSystemTrayIcon ?? this.showSystemTrayIcon, + systemTitleBar: systemTitleBar ?? this.systemTitleBar, + skipNonMusic: skipNonMusic ?? this.skipNonMusic, + closeBehavior: closeBehavior ?? this.closeBehavior, + accentColorScheme: accentColorScheme ?? this.accentColorScheme, + layoutMode: layoutMode ?? this.layoutMode, + locale: locale ?? this.locale, + market: market ?? this.market, + searchMode: searchMode ?? this.searchMode, + downloadLocation: downloadLocation ?? this.downloadLocation, + localLibraryLocation: localLibraryLocation ?? this.localLibraryLocation, + pipedInstance: pipedInstance ?? this.pipedInstance, + invidiousInstance: invidiousInstance ?? this.invidiousInstance, + themeMode: themeMode ?? this.themeMode, + audioSource: audioSource ?? this.audioSource, + streamMusicCodec: streamMusicCodec ?? this.streamMusicCodec, + downloadMusicCodec: downloadMusicCodec ?? this.downloadMusicCodec, + discordPresence: discordPresence ?? this.discordPresence, + endlessPlayback: endlessPlayback ?? this.endlessPlayback, + enableConnect: enableConnect ?? this.enableConnect, + cacheMusic: cacheMusic ?? this.cacheMusic, + ); + PreferencesTableData copyWithCompanion(PreferencesTableCompanion data) { + return PreferencesTableData( + id: data.id.present ? data.id.value : this.id, + audioQuality: data.audioQuality.present + ? data.audioQuality.value + : this.audioQuality, + albumColorSync: data.albumColorSync.present + ? data.albumColorSync.value + : this.albumColorSync, + amoledDarkTheme: data.amoledDarkTheme.present + ? data.amoledDarkTheme.value + : this.amoledDarkTheme, + checkUpdate: + data.checkUpdate.present ? data.checkUpdate.value : this.checkUpdate, + normalizeAudio: data.normalizeAudio.present + ? data.normalizeAudio.value + : this.normalizeAudio, + showSystemTrayIcon: data.showSystemTrayIcon.present + ? data.showSystemTrayIcon.value + : this.showSystemTrayIcon, + systemTitleBar: data.systemTitleBar.present + ? data.systemTitleBar.value + : this.systemTitleBar, + skipNonMusic: data.skipNonMusic.present + ? data.skipNonMusic.value + : this.skipNonMusic, + closeBehavior: data.closeBehavior.present + ? data.closeBehavior.value + : this.closeBehavior, + accentColorScheme: data.accentColorScheme.present + ? data.accentColorScheme.value + : this.accentColorScheme, + layoutMode: + data.layoutMode.present ? data.layoutMode.value : this.layoutMode, + locale: data.locale.present ? data.locale.value : this.locale, + market: data.market.present ? data.market.value : this.market, + searchMode: + data.searchMode.present ? data.searchMode.value : this.searchMode, + downloadLocation: data.downloadLocation.present + ? data.downloadLocation.value + : this.downloadLocation, + localLibraryLocation: data.localLibraryLocation.present + ? data.localLibraryLocation.value + : this.localLibraryLocation, + pipedInstance: data.pipedInstance.present + ? data.pipedInstance.value + : this.pipedInstance, + invidiousInstance: data.invidiousInstance.present + ? data.invidiousInstance.value + : this.invidiousInstance, + themeMode: data.themeMode.present ? data.themeMode.value : this.themeMode, + audioSource: + data.audioSource.present ? data.audioSource.value : this.audioSource, + streamMusicCodec: data.streamMusicCodec.present + ? data.streamMusicCodec.value + : this.streamMusicCodec, + downloadMusicCodec: data.downloadMusicCodec.present + ? data.downloadMusicCodec.value + : this.downloadMusicCodec, + discordPresence: data.discordPresence.present + ? data.discordPresence.value + : this.discordPresence, + endlessPlayback: data.endlessPlayback.present + ? data.endlessPlayback.value + : this.endlessPlayback, + enableConnect: data.enableConnect.present + ? data.enableConnect.value + : this.enableConnect, + cacheMusic: + data.cacheMusic.present ? data.cacheMusic.value : this.cacheMusic, + ); + } + + @override + String toString() { + return (StringBuffer('PreferencesTableData(') + ..write('id: $id, ') + ..write('audioQuality: $audioQuality, ') + ..write('albumColorSync: $albumColorSync, ') + ..write('amoledDarkTheme: $amoledDarkTheme, ') + ..write('checkUpdate: $checkUpdate, ') + ..write('normalizeAudio: $normalizeAudio, ') + ..write('showSystemTrayIcon: $showSystemTrayIcon, ') + ..write('systemTitleBar: $systemTitleBar, ') + ..write('skipNonMusic: $skipNonMusic, ') + ..write('closeBehavior: $closeBehavior, ') + ..write('accentColorScheme: $accentColorScheme, ') + ..write('layoutMode: $layoutMode, ') + ..write('locale: $locale, ') + ..write('market: $market, ') + ..write('searchMode: $searchMode, ') + ..write('downloadLocation: $downloadLocation, ') + ..write('localLibraryLocation: $localLibraryLocation, ') + ..write('pipedInstance: $pipedInstance, ') + ..write('invidiousInstance: $invidiousInstance, ') + ..write('themeMode: $themeMode, ') + ..write('audioSource: $audioSource, ') + ..write('streamMusicCodec: $streamMusicCodec, ') + ..write('downloadMusicCodec: $downloadMusicCodec, ') + ..write('discordPresence: $discordPresence, ') + ..write('endlessPlayback: $endlessPlayback, ') + ..write('enableConnect: $enableConnect, ') + ..write('cacheMusic: $cacheMusic') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + id, + audioQuality, + albumColorSync, + amoledDarkTheme, + checkUpdate, + normalizeAudio, + showSystemTrayIcon, + systemTitleBar, + skipNonMusic, + closeBehavior, + accentColorScheme, + layoutMode, + locale, + market, + searchMode, + downloadLocation, + localLibraryLocation, + pipedInstance, + invidiousInstance, + themeMode, + audioSource, + streamMusicCodec, + downloadMusicCodec, + discordPresence, + endlessPlayback, + enableConnect, + cacheMusic + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PreferencesTableData && + other.id == this.id && + other.audioQuality == this.audioQuality && + other.albumColorSync == this.albumColorSync && + other.amoledDarkTheme == this.amoledDarkTheme && + other.checkUpdate == this.checkUpdate && + other.normalizeAudio == this.normalizeAudio && + other.showSystemTrayIcon == this.showSystemTrayIcon && + other.systemTitleBar == this.systemTitleBar && + other.skipNonMusic == this.skipNonMusic && + other.closeBehavior == this.closeBehavior && + other.accentColorScheme == this.accentColorScheme && + other.layoutMode == this.layoutMode && + other.locale == this.locale && + other.market == this.market && + other.searchMode == this.searchMode && + other.downloadLocation == this.downloadLocation && + other.localLibraryLocation == this.localLibraryLocation && + other.pipedInstance == this.pipedInstance && + other.invidiousInstance == this.invidiousInstance && + other.themeMode == this.themeMode && + other.audioSource == this.audioSource && + other.streamMusicCodec == this.streamMusicCodec && + other.downloadMusicCodec == this.downloadMusicCodec && + other.discordPresence == this.discordPresence && + other.endlessPlayback == this.endlessPlayback && + other.enableConnect == this.enableConnect && + other.cacheMusic == this.cacheMusic); +} + +class PreferencesTableCompanion extends UpdateCompanion { + final Value id; + final Value audioQuality; + final Value albumColorSync; + final Value amoledDarkTheme; + final Value checkUpdate; + final Value normalizeAudio; + final Value showSystemTrayIcon; + final Value systemTitleBar; + final Value skipNonMusic; + final Value closeBehavior; + final Value accentColorScheme; + final Value layoutMode; + final Value locale; + final Value market; + final Value searchMode; + final Value downloadLocation; + final Value localLibraryLocation; + final Value pipedInstance; + final Value invidiousInstance; + final Value themeMode; + final Value audioSource; + final Value streamMusicCodec; + final Value downloadMusicCodec; + final Value discordPresence; + final Value endlessPlayback; + final Value enableConnect; + final Value cacheMusic; + const PreferencesTableCompanion({ + this.id = const Value.absent(), + this.audioQuality = const Value.absent(), + this.albumColorSync = const Value.absent(), + this.amoledDarkTheme = const Value.absent(), + this.checkUpdate = const Value.absent(), + this.normalizeAudio = const Value.absent(), + this.showSystemTrayIcon = const Value.absent(), + this.systemTitleBar = const Value.absent(), + this.skipNonMusic = const Value.absent(), + this.closeBehavior = const Value.absent(), + this.accentColorScheme = const Value.absent(), + this.layoutMode = const Value.absent(), + this.locale = const Value.absent(), + this.market = const Value.absent(), + this.searchMode = const Value.absent(), + this.downloadLocation = const Value.absent(), + this.localLibraryLocation = const Value.absent(), + this.pipedInstance = const Value.absent(), + this.invidiousInstance = const Value.absent(), + this.themeMode = const Value.absent(), + this.audioSource = const Value.absent(), + this.streamMusicCodec = const Value.absent(), + this.downloadMusicCodec = const Value.absent(), + this.discordPresence = const Value.absent(), + this.endlessPlayback = const Value.absent(), + this.enableConnect = const Value.absent(), + this.cacheMusic = const Value.absent(), + }); + PreferencesTableCompanion.insert({ + this.id = const Value.absent(), + this.audioQuality = const Value.absent(), + this.albumColorSync = const Value.absent(), + this.amoledDarkTheme = const Value.absent(), + this.checkUpdate = const Value.absent(), + this.normalizeAudio = const Value.absent(), + this.showSystemTrayIcon = const Value.absent(), + this.systemTitleBar = const Value.absent(), + this.skipNonMusic = const Value.absent(), + this.closeBehavior = const Value.absent(), + this.accentColorScheme = const Value.absent(), + this.layoutMode = const Value.absent(), + this.locale = const Value.absent(), + this.market = const Value.absent(), + this.searchMode = const Value.absent(), + this.downloadLocation = const Value.absent(), + this.localLibraryLocation = const Value.absent(), + this.pipedInstance = const Value.absent(), + this.invidiousInstance = const Value.absent(), + this.themeMode = const Value.absent(), + this.audioSource = const Value.absent(), + this.streamMusicCodec = const Value.absent(), + this.downloadMusicCodec = const Value.absent(), + this.discordPresence = const Value.absent(), + this.endlessPlayback = const Value.absent(), + this.enableConnect = const Value.absent(), + this.cacheMusic = const Value.absent(), + }); + static Insertable custom({ + Expression? id, + Expression? audioQuality, + Expression? albumColorSync, + Expression? amoledDarkTheme, + Expression? checkUpdate, + Expression? normalizeAudio, + Expression? showSystemTrayIcon, + Expression? systemTitleBar, + Expression? skipNonMusic, + Expression? closeBehavior, + Expression? accentColorScheme, + Expression? layoutMode, + Expression? locale, + Expression? market, + Expression? searchMode, + Expression? downloadLocation, + Expression? localLibraryLocation, + Expression? pipedInstance, + Expression? invidiousInstance, + Expression? themeMode, + Expression? audioSource, + Expression? streamMusicCodec, + Expression? downloadMusicCodec, + Expression? discordPresence, + Expression? endlessPlayback, + Expression? enableConnect, + Expression? cacheMusic, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (audioQuality != null) 'audio_quality': audioQuality, + if (albumColorSync != null) 'album_color_sync': albumColorSync, + if (amoledDarkTheme != null) 'amoled_dark_theme': amoledDarkTheme, + if (checkUpdate != null) 'check_update': checkUpdate, + if (normalizeAudio != null) 'normalize_audio': normalizeAudio, + if (showSystemTrayIcon != null) + 'show_system_tray_icon': showSystemTrayIcon, + if (systemTitleBar != null) 'system_title_bar': systemTitleBar, + if (skipNonMusic != null) 'skip_non_music': skipNonMusic, + if (closeBehavior != null) 'close_behavior': closeBehavior, + if (accentColorScheme != null) 'accent_color_scheme': accentColorScheme, + if (layoutMode != null) 'layout_mode': layoutMode, + if (locale != null) 'locale': locale, + if (market != null) 'market': market, + if (searchMode != null) 'search_mode': searchMode, + if (downloadLocation != null) 'download_location': downloadLocation, + if (localLibraryLocation != null) + 'local_library_location': localLibraryLocation, + if (pipedInstance != null) 'piped_instance': pipedInstance, + if (invidiousInstance != null) 'invidious_instance': invidiousInstance, + if (themeMode != null) 'theme_mode': themeMode, + if (audioSource != null) 'audio_source': audioSource, + if (streamMusicCodec != null) 'stream_music_codec': streamMusicCodec, + if (downloadMusicCodec != null) + 'download_music_codec': downloadMusicCodec, + if (discordPresence != null) 'discord_presence': discordPresence, + if (endlessPlayback != null) 'endless_playback': endlessPlayback, + if (enableConnect != null) 'enable_connect': enableConnect, + if (cacheMusic != null) 'cache_music': cacheMusic, + }); + } + + PreferencesTableCompanion copyWith( + {Value? id, + Value? audioQuality, + Value? albumColorSync, + Value? amoledDarkTheme, + Value? checkUpdate, + Value? normalizeAudio, + Value? showSystemTrayIcon, + Value? systemTitleBar, + Value? skipNonMusic, + Value? closeBehavior, + Value? accentColorScheme, + Value? layoutMode, + Value? locale, + Value? market, + Value? searchMode, + Value? downloadLocation, + Value? localLibraryLocation, + Value? pipedInstance, + Value? invidiousInstance, + Value? themeMode, + Value? audioSource, + Value? streamMusicCodec, + Value? downloadMusicCodec, + Value? discordPresence, + Value? endlessPlayback, + Value? enableConnect, + Value? cacheMusic}) { + return PreferencesTableCompanion( + id: id ?? this.id, + audioQuality: audioQuality ?? this.audioQuality, + albumColorSync: albumColorSync ?? this.albumColorSync, + amoledDarkTheme: amoledDarkTheme ?? this.amoledDarkTheme, + checkUpdate: checkUpdate ?? this.checkUpdate, + normalizeAudio: normalizeAudio ?? this.normalizeAudio, + showSystemTrayIcon: showSystemTrayIcon ?? this.showSystemTrayIcon, + systemTitleBar: systemTitleBar ?? this.systemTitleBar, + skipNonMusic: skipNonMusic ?? this.skipNonMusic, + closeBehavior: closeBehavior ?? this.closeBehavior, + accentColorScheme: accentColorScheme ?? this.accentColorScheme, + layoutMode: layoutMode ?? this.layoutMode, + locale: locale ?? this.locale, + market: market ?? this.market, + searchMode: searchMode ?? this.searchMode, + downloadLocation: downloadLocation ?? this.downloadLocation, + localLibraryLocation: localLibraryLocation ?? this.localLibraryLocation, + pipedInstance: pipedInstance ?? this.pipedInstance, + invidiousInstance: invidiousInstance ?? this.invidiousInstance, + themeMode: themeMode ?? this.themeMode, + audioSource: audioSource ?? this.audioSource, + streamMusicCodec: streamMusicCodec ?? this.streamMusicCodec, + downloadMusicCodec: downloadMusicCodec ?? this.downloadMusicCodec, + discordPresence: discordPresence ?? this.discordPresence, + endlessPlayback: endlessPlayback ?? this.endlessPlayback, + enableConnect: enableConnect ?? this.enableConnect, + cacheMusic: cacheMusic ?? this.cacheMusic, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (audioQuality.present) { + map['audio_quality'] = Variable(audioQuality.value); + } + if (albumColorSync.present) { + map['album_color_sync'] = Variable(albumColorSync.value); + } + if (amoledDarkTheme.present) { + map['amoled_dark_theme'] = Variable(amoledDarkTheme.value); + } + if (checkUpdate.present) { + map['check_update'] = Variable(checkUpdate.value); + } + if (normalizeAudio.present) { + map['normalize_audio'] = Variable(normalizeAudio.value); + } + if (showSystemTrayIcon.present) { + map['show_system_tray_icon'] = Variable(showSystemTrayIcon.value); + } + if (systemTitleBar.present) { + map['system_title_bar'] = Variable(systemTitleBar.value); + } + if (skipNonMusic.present) { + map['skip_non_music'] = Variable(skipNonMusic.value); + } + if (closeBehavior.present) { + map['close_behavior'] = Variable(closeBehavior.value); + } + if (accentColorScheme.present) { + map['accent_color_scheme'] = Variable(accentColorScheme.value); + } + if (layoutMode.present) { + map['layout_mode'] = Variable(layoutMode.value); + } + if (locale.present) { + map['locale'] = Variable(locale.value); + } + if (market.present) { + map['market'] = Variable(market.value); + } + if (searchMode.present) { + map['search_mode'] = Variable(searchMode.value); + } + if (downloadLocation.present) { + map['download_location'] = Variable(downloadLocation.value); + } + if (localLibraryLocation.present) { + map['local_library_location'] = + Variable(localLibraryLocation.value); + } + if (pipedInstance.present) { + map['piped_instance'] = Variable(pipedInstance.value); + } + if (invidiousInstance.present) { + map['invidious_instance'] = Variable(invidiousInstance.value); + } + if (themeMode.present) { + map['theme_mode'] = Variable(themeMode.value); + } + if (audioSource.present) { + map['audio_source'] = Variable(audioSource.value); + } + if (streamMusicCodec.present) { + map['stream_music_codec'] = Variable(streamMusicCodec.value); + } + if (downloadMusicCodec.present) { + map['download_music_codec'] = Variable(downloadMusicCodec.value); + } + if (discordPresence.present) { + map['discord_presence'] = Variable(discordPresence.value); + } + if (endlessPlayback.present) { + map['endless_playback'] = Variable(endlessPlayback.value); + } + if (enableConnect.present) { + map['enable_connect'] = Variable(enableConnect.value); + } + if (cacheMusic.present) { + map['cache_music'] = Variable(cacheMusic.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PreferencesTableCompanion(') + ..write('id: $id, ') + ..write('audioQuality: $audioQuality, ') + ..write('albumColorSync: $albumColorSync, ') + ..write('amoledDarkTheme: $amoledDarkTheme, ') + ..write('checkUpdate: $checkUpdate, ') + ..write('normalizeAudio: $normalizeAudio, ') + ..write('showSystemTrayIcon: $showSystemTrayIcon, ') + ..write('systemTitleBar: $systemTitleBar, ') + ..write('skipNonMusic: $skipNonMusic, ') + ..write('closeBehavior: $closeBehavior, ') + ..write('accentColorScheme: $accentColorScheme, ') + ..write('layoutMode: $layoutMode, ') + ..write('locale: $locale, ') + ..write('market: $market, ') + ..write('searchMode: $searchMode, ') + ..write('downloadLocation: $downloadLocation, ') + ..write('localLibraryLocation: $localLibraryLocation, ') + ..write('pipedInstance: $pipedInstance, ') + ..write('invidiousInstance: $invidiousInstance, ') + ..write('themeMode: $themeMode, ') + ..write('audioSource: $audioSource, ') + ..write('streamMusicCodec: $streamMusicCodec, ') + ..write('downloadMusicCodec: $downloadMusicCodec, ') + ..write('discordPresence: $discordPresence, ') + ..write('endlessPlayback: $endlessPlayback, ') + ..write('enableConnect: $enableConnect, ') + ..write('cacheMusic: $cacheMusic') + ..write(')')) + .toString(); + } +} + +class ScrobblerTable extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + ScrobblerTable(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: currentDateAndTime); + late final GeneratedColumn username = GeneratedColumn( + 'username', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn passwordHash = GeneratedColumn( + 'password_hash', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => [id, createdAt, username, passwordHash]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'scrobbler_table'; + @override + Set get $primaryKey => {id}; + @override + ScrobblerTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return ScrobblerTableData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + username: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}username'])!, + passwordHash: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}password_hash'])!, + ); + } + + @override + ScrobblerTable createAlias(String alias) { + return ScrobblerTable(attachedDatabase, alias); + } +} + +class ScrobblerTableData extends DataClass + implements Insertable { + final int id; + final DateTime createdAt; + final String username; + final String passwordHash; + const ScrobblerTableData( + {required this.id, + required this.createdAt, + required this.username, + required this.passwordHash}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['username'] = Variable(username); + map['password_hash'] = Variable(passwordHash); + return map; + } + + ScrobblerTableCompanion toCompanion(bool nullToAbsent) { + return ScrobblerTableCompanion( + id: Value(id), + createdAt: Value(createdAt), + username: Value(username), + passwordHash: Value(passwordHash), + ); + } + + factory ScrobblerTableData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return ScrobblerTableData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + username: serializer.fromJson(json['username']), + passwordHash: serializer.fromJson(json['passwordHash']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'username': serializer.toJson(username), + 'passwordHash': serializer.toJson(passwordHash), + }; + } + + ScrobblerTableData copyWith( + {int? id, + DateTime? createdAt, + String? username, + String? passwordHash}) => + ScrobblerTableData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + username: username ?? this.username, + passwordHash: passwordHash ?? this.passwordHash, + ); + ScrobblerTableData copyWithCompanion(ScrobblerTableCompanion data) { + return ScrobblerTableData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + username: data.username.present ? data.username.value : this.username, + passwordHash: data.passwordHash.present + ? data.passwordHash.value + : this.passwordHash, + ); + } + + @override + String toString() { + return (StringBuffer('ScrobblerTableData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('username: $username, ') + ..write('passwordHash: $passwordHash') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, username, passwordHash); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ScrobblerTableData && + other.id == this.id && + other.createdAt == this.createdAt && + other.username == this.username && + other.passwordHash == this.passwordHash); +} + +class ScrobblerTableCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value username; + final Value passwordHash; + const ScrobblerTableCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.username = const Value.absent(), + this.passwordHash = const Value.absent(), + }); + ScrobblerTableCompanion.insert({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + required String username, + required String passwordHash, + }) : username = Value(username), + passwordHash = Value(passwordHash); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? username, + Expression? passwordHash, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (username != null) 'username': username, + if (passwordHash != null) 'password_hash': passwordHash, + }); + } + + ScrobblerTableCompanion copyWith( + {Value? id, + Value? createdAt, + Value? username, + Value? passwordHash}) { + return ScrobblerTableCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + username: username ?? this.username, + passwordHash: passwordHash ?? this.passwordHash, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (username.present) { + map['username'] = Variable(username.value); + } + if (passwordHash.present) { + map['password_hash'] = Variable(passwordHash.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ScrobblerTableCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('username: $username, ') + ..write('passwordHash: $passwordHash') + ..write(')')) + .toString(); + } +} + +class SkipSegmentTable extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + SkipSegmentTable(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn start = GeneratedColumn( + 'start', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn end = GeneratedColumn( + 'end', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn trackId = GeneratedColumn( + 'track_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: currentDateAndTime); + @override + List get $columns => [id, start, end, trackId, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'skip_segment_table'; + @override + Set get $primaryKey => {id}; + @override + SkipSegmentTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SkipSegmentTableData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + start: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}start'])!, + end: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}end'])!, + trackId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}track_id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + ); + } + + @override + SkipSegmentTable createAlias(String alias) { + return SkipSegmentTable(attachedDatabase, alias); + } +} + +class SkipSegmentTableData extends DataClass + implements Insertable { + final int id; + final int start; + final int end; + final String trackId; + final DateTime createdAt; + const SkipSegmentTableData( + {required this.id, + required this.start, + required this.end, + required this.trackId, + required this.createdAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['start'] = Variable(start); + map['end'] = Variable(end); + map['track_id'] = Variable(trackId); + map['created_at'] = Variable(createdAt); + return map; + } + + SkipSegmentTableCompanion toCompanion(bool nullToAbsent) { + return SkipSegmentTableCompanion( + id: Value(id), + start: Value(start), + end: Value(end), + trackId: Value(trackId), + createdAt: Value(createdAt), + ); + } + + factory SkipSegmentTableData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SkipSegmentTableData( + id: serializer.fromJson(json['id']), + start: serializer.fromJson(json['start']), + end: serializer.fromJson(json['end']), + trackId: serializer.fromJson(json['trackId']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'start': serializer.toJson(start), + 'end': serializer.toJson(end), + 'trackId': serializer.toJson(trackId), + 'createdAt': serializer.toJson(createdAt), + }; + } + + SkipSegmentTableData copyWith( + {int? id, + int? start, + int? end, + String? trackId, + DateTime? createdAt}) => + SkipSegmentTableData( + id: id ?? this.id, + start: start ?? this.start, + end: end ?? this.end, + trackId: trackId ?? this.trackId, + createdAt: createdAt ?? this.createdAt, + ); + SkipSegmentTableData copyWithCompanion(SkipSegmentTableCompanion data) { + return SkipSegmentTableData( + id: data.id.present ? data.id.value : this.id, + start: data.start.present ? data.start.value : this.start, + end: data.end.present ? data.end.value : this.end, + trackId: data.trackId.present ? data.trackId.value : this.trackId, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('SkipSegmentTableData(') + ..write('id: $id, ') + ..write('start: $start, ') + ..write('end: $end, ') + ..write('trackId: $trackId, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, start, end, trackId, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SkipSegmentTableData && + other.id == this.id && + other.start == this.start && + other.end == this.end && + other.trackId == this.trackId && + other.createdAt == this.createdAt); +} + +class SkipSegmentTableCompanion extends UpdateCompanion { + final Value id; + final Value start; + final Value end; + final Value trackId; + final Value createdAt; + const SkipSegmentTableCompanion({ + this.id = const Value.absent(), + this.start = const Value.absent(), + this.end = const Value.absent(), + this.trackId = const Value.absent(), + this.createdAt = const Value.absent(), + }); + SkipSegmentTableCompanion.insert({ + this.id = const Value.absent(), + required int start, + required int end, + required String trackId, + this.createdAt = const Value.absent(), + }) : start = Value(start), + end = Value(end), + trackId = Value(trackId); + static Insertable custom({ + Expression? id, + Expression? start, + Expression? end, + Expression? trackId, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (start != null) 'start': start, + if (end != null) 'end': end, + if (trackId != null) 'track_id': trackId, + if (createdAt != null) 'created_at': createdAt, + }); + } + + SkipSegmentTableCompanion copyWith( + {Value? id, + Value? start, + Value? end, + Value? trackId, + Value? createdAt}) { + return SkipSegmentTableCompanion( + id: id ?? this.id, + start: start ?? this.start, + end: end ?? this.end, + trackId: trackId ?? this.trackId, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (start.present) { + map['start'] = Variable(start.value); + } + if (end.present) { + map['end'] = Variable(end.value); + } + if (trackId.present) { + map['track_id'] = Variable(trackId.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SkipSegmentTableCompanion(') + ..write('id: $id, ') + ..write('start: $start, ') + ..write('end: $end, ') + ..write('trackId: $trackId, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class SourceMatchTable extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + SourceMatchTable(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn trackId = GeneratedColumn( + 'track_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn sourceId = GeneratedColumn( + 'source_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: Constant(SourceType.youtube.name)); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: currentDateAndTime); + @override + List get $columns => + [id, trackId, sourceId, sourceType, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'source_match_table'; + @override + Set get $primaryKey => {id}; + @override + SourceMatchTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SourceMatchTableData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + trackId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}track_id'])!, + sourceId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}source_id'])!, + sourceType: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}source_type'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + ); + } + + @override + SourceMatchTable createAlias(String alias) { + return SourceMatchTable(attachedDatabase, alias); + } +} + +class SourceMatchTableData extends DataClass + implements Insertable { + final int id; + final String trackId; + final String sourceId; + final String sourceType; + final DateTime createdAt; + const SourceMatchTableData( + {required this.id, + required this.trackId, + required this.sourceId, + required this.sourceType, + required this.createdAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['track_id'] = Variable(trackId); + map['source_id'] = Variable(sourceId); + map['source_type'] = Variable(sourceType); + map['created_at'] = Variable(createdAt); + return map; + } + + SourceMatchTableCompanion toCompanion(bool nullToAbsent) { + return SourceMatchTableCompanion( + id: Value(id), + trackId: Value(trackId), + sourceId: Value(sourceId), + sourceType: Value(sourceType), + createdAt: Value(createdAt), + ); + } + + factory SourceMatchTableData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SourceMatchTableData( + id: serializer.fromJson(json['id']), + trackId: serializer.fromJson(json['trackId']), + sourceId: serializer.fromJson(json['sourceId']), + sourceType: serializer.fromJson(json['sourceType']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'trackId': serializer.toJson(trackId), + 'sourceId': serializer.toJson(sourceId), + 'sourceType': serializer.toJson(sourceType), + 'createdAt': serializer.toJson(createdAt), + }; + } + + SourceMatchTableData copyWith( + {int? id, + String? trackId, + String? sourceId, + String? sourceType, + DateTime? createdAt}) => + SourceMatchTableData( + id: id ?? this.id, + trackId: trackId ?? this.trackId, + sourceId: sourceId ?? this.sourceId, + sourceType: sourceType ?? this.sourceType, + createdAt: createdAt ?? this.createdAt, + ); + SourceMatchTableData copyWithCompanion(SourceMatchTableCompanion data) { + return SourceMatchTableData( + id: data.id.present ? data.id.value : this.id, + trackId: data.trackId.present ? data.trackId.value : this.trackId, + sourceId: data.sourceId.present ? data.sourceId.value : this.sourceId, + sourceType: + data.sourceType.present ? data.sourceType.value : this.sourceType, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('SourceMatchTableData(') + ..write('id: $id, ') + ..write('trackId: $trackId, ') + ..write('sourceId: $sourceId, ') + ..write('sourceType: $sourceType, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, trackId, sourceId, sourceType, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SourceMatchTableData && + other.id == this.id && + other.trackId == this.trackId && + other.sourceId == this.sourceId && + other.sourceType == this.sourceType && + other.createdAt == this.createdAt); +} + +class SourceMatchTableCompanion extends UpdateCompanion { + final Value id; + final Value trackId; + final Value sourceId; + final Value sourceType; + final Value createdAt; + const SourceMatchTableCompanion({ + this.id = const Value.absent(), + this.trackId = const Value.absent(), + this.sourceId = const Value.absent(), + this.sourceType = const Value.absent(), + this.createdAt = const Value.absent(), + }); + SourceMatchTableCompanion.insert({ + this.id = const Value.absent(), + required String trackId, + required String sourceId, + this.sourceType = const Value.absent(), + this.createdAt = const Value.absent(), + }) : trackId = Value(trackId), + sourceId = Value(sourceId); + static Insertable custom({ + Expression? id, + Expression? trackId, + Expression? sourceId, + Expression? sourceType, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (trackId != null) 'track_id': trackId, + if (sourceId != null) 'source_id': sourceId, + if (sourceType != null) 'source_type': sourceType, + if (createdAt != null) 'created_at': createdAt, + }); + } + + SourceMatchTableCompanion copyWith( + {Value? id, + Value? trackId, + Value? sourceId, + Value? sourceType, + Value? createdAt}) { + return SourceMatchTableCompanion( + id: id ?? this.id, + trackId: trackId ?? this.trackId, + sourceId: sourceId ?? this.sourceId, + sourceType: sourceType ?? this.sourceType, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (trackId.present) { + map['track_id'] = Variable(trackId.value); + } + if (sourceId.present) { + map['source_id'] = Variable(sourceId.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SourceMatchTableCompanion(') + ..write('id: $id, ') + ..write('trackId: $trackId, ') + ..write('sourceId: $sourceId, ') + ..write('sourceType: $sourceType, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class AudioPlayerStateTable extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AudioPlayerStateTable(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn playing = GeneratedColumn( + 'playing', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("playing" IN (0, 1))')); + late final GeneratedColumn loopMode = GeneratedColumn( + 'loop_mode', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn shuffled = GeneratedColumn( + 'shuffled', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("shuffled" IN (0, 1))')); + late final GeneratedColumn collections = GeneratedColumn( + 'collections', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => + [id, playing, loopMode, shuffled, collections]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'audio_player_state_table'; + @override + Set get $primaryKey => {id}; + @override + AudioPlayerStateTableData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AudioPlayerStateTableData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + playing: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}playing'])!, + loopMode: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}loop_mode'])!, + shuffled: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}shuffled'])!, + collections: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}collections'])!, + ); + } + + @override + AudioPlayerStateTable createAlias(String alias) { + return AudioPlayerStateTable(attachedDatabase, alias); + } +} + +class AudioPlayerStateTableData extends DataClass + implements Insertable { + final int id; + final bool playing; + final String loopMode; + final bool shuffled; + final String collections; + const AudioPlayerStateTableData( + {required this.id, + required this.playing, + required this.loopMode, + required this.shuffled, + required this.collections}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['playing'] = Variable(playing); + map['loop_mode'] = Variable(loopMode); + map['shuffled'] = Variable(shuffled); + map['collections'] = Variable(collections); + return map; + } + + AudioPlayerStateTableCompanion toCompanion(bool nullToAbsent) { + return AudioPlayerStateTableCompanion( + id: Value(id), + playing: Value(playing), + loopMode: Value(loopMode), + shuffled: Value(shuffled), + collections: Value(collections), + ); + } + + factory AudioPlayerStateTableData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AudioPlayerStateTableData( + id: serializer.fromJson(json['id']), + playing: serializer.fromJson(json['playing']), + loopMode: serializer.fromJson(json['loopMode']), + shuffled: serializer.fromJson(json['shuffled']), + collections: serializer.fromJson(json['collections']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'playing': serializer.toJson(playing), + 'loopMode': serializer.toJson(loopMode), + 'shuffled': serializer.toJson(shuffled), + 'collections': serializer.toJson(collections), + }; + } + + AudioPlayerStateTableData copyWith( + {int? id, + bool? playing, + String? loopMode, + bool? shuffled, + String? collections}) => + AudioPlayerStateTableData( + id: id ?? this.id, + playing: playing ?? this.playing, + loopMode: loopMode ?? this.loopMode, + shuffled: shuffled ?? this.shuffled, + collections: collections ?? this.collections, + ); + AudioPlayerStateTableData copyWithCompanion( + AudioPlayerStateTableCompanion data) { + return AudioPlayerStateTableData( + id: data.id.present ? data.id.value : this.id, + playing: data.playing.present ? data.playing.value : this.playing, + loopMode: data.loopMode.present ? data.loopMode.value : this.loopMode, + shuffled: data.shuffled.present ? data.shuffled.value : this.shuffled, + collections: + data.collections.present ? data.collections.value : this.collections, + ); + } + + @override + String toString() { + return (StringBuffer('AudioPlayerStateTableData(') + ..write('id: $id, ') + ..write('playing: $playing, ') + ..write('loopMode: $loopMode, ') + ..write('shuffled: $shuffled, ') + ..write('collections: $collections') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, playing, loopMode, shuffled, collections); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AudioPlayerStateTableData && + other.id == this.id && + other.playing == this.playing && + other.loopMode == this.loopMode && + other.shuffled == this.shuffled && + other.collections == this.collections); +} + +class AudioPlayerStateTableCompanion + extends UpdateCompanion { + final Value id; + final Value playing; + final Value loopMode; + final Value shuffled; + final Value collections; + const AudioPlayerStateTableCompanion({ + this.id = const Value.absent(), + this.playing = const Value.absent(), + this.loopMode = const Value.absent(), + this.shuffled = const Value.absent(), + this.collections = const Value.absent(), + }); + AudioPlayerStateTableCompanion.insert({ + this.id = const Value.absent(), + required bool playing, + required String loopMode, + required bool shuffled, + required String collections, + }) : playing = Value(playing), + loopMode = Value(loopMode), + shuffled = Value(shuffled), + collections = Value(collections); + static Insertable custom({ + Expression? id, + Expression? playing, + Expression? loopMode, + Expression? shuffled, + Expression? collections, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (playing != null) 'playing': playing, + if (loopMode != null) 'loop_mode': loopMode, + if (shuffled != null) 'shuffled': shuffled, + if (collections != null) 'collections': collections, + }); + } + + AudioPlayerStateTableCompanion copyWith( + {Value? id, + Value? playing, + Value? loopMode, + Value? shuffled, + Value? collections}) { + return AudioPlayerStateTableCompanion( + id: id ?? this.id, + playing: playing ?? this.playing, + loopMode: loopMode ?? this.loopMode, + shuffled: shuffled ?? this.shuffled, + collections: collections ?? this.collections, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (playing.present) { + map['playing'] = Variable(playing.value); + } + if (loopMode.present) { + map['loop_mode'] = Variable(loopMode.value); + } + if (shuffled.present) { + map['shuffled'] = Variable(shuffled.value); + } + if (collections.present) { + map['collections'] = Variable(collections.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AudioPlayerStateTableCompanion(') + ..write('id: $id, ') + ..write('playing: $playing, ') + ..write('loopMode: $loopMode, ') + ..write('shuffled: $shuffled, ') + ..write('collections: $collections') + ..write(')')) + .toString(); + } +} + +class PlaylistTable extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PlaylistTable(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn audioPlayerStateId = GeneratedColumn( + 'audio_player_state_id', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES audio_player_state_table (id)')); + late final GeneratedColumn index = GeneratedColumn( + 'index', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + @override + List get $columns => [id, audioPlayerStateId, index]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'playlist_table'; + @override + Set get $primaryKey => {id}; + @override + PlaylistTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PlaylistTableData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + audioPlayerStateId: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}audio_player_state_id'])!, + index: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}index'])!, + ); + } + + @override + PlaylistTable createAlias(String alias) { + return PlaylistTable(attachedDatabase, alias); + } +} + +class PlaylistTableData extends DataClass + implements Insertable { + final int id; + final int audioPlayerStateId; + final int index; + const PlaylistTableData( + {required this.id, + required this.audioPlayerStateId, + required this.index}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['audio_player_state_id'] = Variable(audioPlayerStateId); + map['index'] = Variable(index); + return map; + } + + PlaylistTableCompanion toCompanion(bool nullToAbsent) { + return PlaylistTableCompanion( + id: Value(id), + audioPlayerStateId: Value(audioPlayerStateId), + index: Value(index), + ); + } + + factory PlaylistTableData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PlaylistTableData( + id: serializer.fromJson(json['id']), + audioPlayerStateId: serializer.fromJson(json['audioPlayerStateId']), + index: serializer.fromJson(json['index']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'audioPlayerStateId': serializer.toJson(audioPlayerStateId), + 'index': serializer.toJson(index), + }; + } + + PlaylistTableData copyWith({int? id, int? audioPlayerStateId, int? index}) => + PlaylistTableData( + id: id ?? this.id, + audioPlayerStateId: audioPlayerStateId ?? this.audioPlayerStateId, + index: index ?? this.index, + ); + PlaylistTableData copyWithCompanion(PlaylistTableCompanion data) { + return PlaylistTableData( + id: data.id.present ? data.id.value : this.id, + audioPlayerStateId: data.audioPlayerStateId.present + ? data.audioPlayerStateId.value + : this.audioPlayerStateId, + index: data.index.present ? data.index.value : this.index, + ); + } + + @override + String toString() { + return (StringBuffer('PlaylistTableData(') + ..write('id: $id, ') + ..write('audioPlayerStateId: $audioPlayerStateId, ') + ..write('index: $index') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, audioPlayerStateId, index); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PlaylistTableData && + other.id == this.id && + other.audioPlayerStateId == this.audioPlayerStateId && + other.index == this.index); +} + +class PlaylistTableCompanion extends UpdateCompanion { + final Value id; + final Value audioPlayerStateId; + final Value index; + const PlaylistTableCompanion({ + this.id = const Value.absent(), + this.audioPlayerStateId = const Value.absent(), + this.index = const Value.absent(), + }); + PlaylistTableCompanion.insert({ + this.id = const Value.absent(), + required int audioPlayerStateId, + required int index, + }) : audioPlayerStateId = Value(audioPlayerStateId), + index = Value(index); + static Insertable custom({ + Expression? id, + Expression? audioPlayerStateId, + Expression? index, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (audioPlayerStateId != null) + 'audio_player_state_id': audioPlayerStateId, + if (index != null) 'index': index, + }); + } + + PlaylistTableCompanion copyWith( + {Value? id, Value? audioPlayerStateId, Value? index}) { + return PlaylistTableCompanion( + id: id ?? this.id, + audioPlayerStateId: audioPlayerStateId ?? this.audioPlayerStateId, + index: index ?? this.index, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (audioPlayerStateId.present) { + map['audio_player_state_id'] = Variable(audioPlayerStateId.value); + } + if (index.present) { + map['index'] = Variable(index.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PlaylistTableCompanion(') + ..write('id: $id, ') + ..write('audioPlayerStateId: $audioPlayerStateId, ') + ..write('index: $index') + ..write(')')) + .toString(); + } +} + +class PlaylistMediaTable extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PlaylistMediaTable(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn playlistId = GeneratedColumn( + 'playlist_id', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('REFERENCES playlist_table (id)')); + late final GeneratedColumn uri = GeneratedColumn( + 'uri', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn extras = GeneratedColumn( + 'extras', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn httpHeaders = GeneratedColumn( + 'http_headers', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + @override + List get $columns => + [id, playlistId, uri, extras, httpHeaders]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'playlist_media_table'; + @override + Set get $primaryKey => {id}; + @override + PlaylistMediaTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PlaylistMediaTableData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + playlistId: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}playlist_id'])!, + uri: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}uri'])!, + extras: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}extras']), + httpHeaders: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}http_headers']), + ); + } + + @override + PlaylistMediaTable createAlias(String alias) { + return PlaylistMediaTable(attachedDatabase, alias); + } +} + +class PlaylistMediaTableData extends DataClass + implements Insertable { + final int id; + final int playlistId; + final String uri; + final String? extras; + final String? httpHeaders; + const PlaylistMediaTableData( + {required this.id, + required this.playlistId, + required this.uri, + this.extras, + this.httpHeaders}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['playlist_id'] = Variable(playlistId); + map['uri'] = Variable(uri); + if (!nullToAbsent || extras != null) { + map['extras'] = Variable(extras); + } + if (!nullToAbsent || httpHeaders != null) { + map['http_headers'] = Variable(httpHeaders); + } + return map; + } + + PlaylistMediaTableCompanion toCompanion(bool nullToAbsent) { + return PlaylistMediaTableCompanion( + id: Value(id), + playlistId: Value(playlistId), + uri: Value(uri), + extras: + extras == null && nullToAbsent ? const Value.absent() : Value(extras), + httpHeaders: httpHeaders == null && nullToAbsent + ? const Value.absent() + : Value(httpHeaders), + ); + } + + factory PlaylistMediaTableData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PlaylistMediaTableData( + id: serializer.fromJson(json['id']), + playlistId: serializer.fromJson(json['playlistId']), + uri: serializer.fromJson(json['uri']), + extras: serializer.fromJson(json['extras']), + httpHeaders: serializer.fromJson(json['httpHeaders']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'playlistId': serializer.toJson(playlistId), + 'uri': serializer.toJson(uri), + 'extras': serializer.toJson(extras), + 'httpHeaders': serializer.toJson(httpHeaders), + }; + } + + PlaylistMediaTableData copyWith( + {int? id, + int? playlistId, + String? uri, + Value extras = const Value.absent(), + Value httpHeaders = const Value.absent()}) => + PlaylistMediaTableData( + id: id ?? this.id, + playlistId: playlistId ?? this.playlistId, + uri: uri ?? this.uri, + extras: extras.present ? extras.value : this.extras, + httpHeaders: httpHeaders.present ? httpHeaders.value : this.httpHeaders, + ); + PlaylistMediaTableData copyWithCompanion(PlaylistMediaTableCompanion data) { + return PlaylistMediaTableData( + id: data.id.present ? data.id.value : this.id, + playlistId: + data.playlistId.present ? data.playlistId.value : this.playlistId, + uri: data.uri.present ? data.uri.value : this.uri, + extras: data.extras.present ? data.extras.value : this.extras, + httpHeaders: + data.httpHeaders.present ? data.httpHeaders.value : this.httpHeaders, + ); + } + + @override + String toString() { + return (StringBuffer('PlaylistMediaTableData(') + ..write('id: $id, ') + ..write('playlistId: $playlistId, ') + ..write('uri: $uri, ') + ..write('extras: $extras, ') + ..write('httpHeaders: $httpHeaders') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, playlistId, uri, extras, httpHeaders); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PlaylistMediaTableData && + other.id == this.id && + other.playlistId == this.playlistId && + other.uri == this.uri && + other.extras == this.extras && + other.httpHeaders == this.httpHeaders); +} + +class PlaylistMediaTableCompanion + extends UpdateCompanion { + final Value id; + final Value playlistId; + final Value uri; + final Value extras; + final Value httpHeaders; + const PlaylistMediaTableCompanion({ + this.id = const Value.absent(), + this.playlistId = const Value.absent(), + this.uri = const Value.absent(), + this.extras = const Value.absent(), + this.httpHeaders = const Value.absent(), + }); + PlaylistMediaTableCompanion.insert({ + this.id = const Value.absent(), + required int playlistId, + required String uri, + this.extras = const Value.absent(), + this.httpHeaders = const Value.absent(), + }) : playlistId = Value(playlistId), + uri = Value(uri); + static Insertable custom({ + Expression? id, + Expression? playlistId, + Expression? uri, + Expression? extras, + Expression? httpHeaders, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (playlistId != null) 'playlist_id': playlistId, + if (uri != null) 'uri': uri, + if (extras != null) 'extras': extras, + if (httpHeaders != null) 'http_headers': httpHeaders, + }); + } + + PlaylistMediaTableCompanion copyWith( + {Value? id, + Value? playlistId, + Value? uri, + Value? extras, + Value? httpHeaders}) { + return PlaylistMediaTableCompanion( + id: id ?? this.id, + playlistId: playlistId ?? this.playlistId, + uri: uri ?? this.uri, + extras: extras ?? this.extras, + httpHeaders: httpHeaders ?? this.httpHeaders, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (playlistId.present) { + map['playlist_id'] = Variable(playlistId.value); + } + if (uri.present) { + map['uri'] = Variable(uri.value); + } + if (extras.present) { + map['extras'] = Variable(extras.value); + } + if (httpHeaders.present) { + map['http_headers'] = Variable(httpHeaders.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PlaylistMediaTableCompanion(') + ..write('id: $id, ') + ..write('playlistId: $playlistId, ') + ..write('uri: $uri, ') + ..write('extras: $extras, ') + ..write('httpHeaders: $httpHeaders') + ..write(')')) + .toString(); + } +} + +class HistoryTable extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + HistoryTable(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: currentDateAndTime); + late final GeneratedColumn type = GeneratedColumn( + 'type', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn itemId = GeneratedColumn( + 'item_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn data = GeneratedColumn( + 'data', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => [id, createdAt, type, itemId, data]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'history_table'; + @override + Set get $primaryKey => {id}; + @override + HistoryTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return HistoryTableData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + type: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}type'])!, + itemId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}item_id'])!, + data: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}data'])!, + ); + } + + @override + HistoryTable createAlias(String alias) { + return HistoryTable(attachedDatabase, alias); + } +} + +class HistoryTableData extends DataClass + implements Insertable { + final int id; + final DateTime createdAt; + final String type; + final String itemId; + final String data; + const HistoryTableData( + {required this.id, + required this.createdAt, + required this.type, + required this.itemId, + required this.data}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['type'] = Variable(type); + map['item_id'] = Variable(itemId); + map['data'] = Variable(data); + return map; + } + + HistoryTableCompanion toCompanion(bool nullToAbsent) { + return HistoryTableCompanion( + id: Value(id), + createdAt: Value(createdAt), + type: Value(type), + itemId: Value(itemId), + data: Value(data), + ); + } + + factory HistoryTableData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return HistoryTableData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + type: serializer.fromJson(json['type']), + itemId: serializer.fromJson(json['itemId']), + data: serializer.fromJson(json['data']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'type': serializer.toJson(type), + 'itemId': serializer.toJson(itemId), + 'data': serializer.toJson(data), + }; + } + + HistoryTableData copyWith( + {int? id, + DateTime? createdAt, + String? type, + String? itemId, + String? data}) => + HistoryTableData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + type: type ?? this.type, + itemId: itemId ?? this.itemId, + data: data ?? this.data, + ); + HistoryTableData copyWithCompanion(HistoryTableCompanion data) { + return HistoryTableData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + type: data.type.present ? data.type.value : this.type, + itemId: data.itemId.present ? data.itemId.value : this.itemId, + data: data.data.present ? data.data.value : this.data, + ); + } + + @override + String toString() { + return (StringBuffer('HistoryTableData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('type: $type, ') + ..write('itemId: $itemId, ') + ..write('data: $data') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, type, itemId, data); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is HistoryTableData && + other.id == this.id && + other.createdAt == this.createdAt && + other.type == this.type && + other.itemId == this.itemId && + other.data == this.data); +} + +class HistoryTableCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value type; + final Value itemId; + final Value data; + const HistoryTableCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.type = const Value.absent(), + this.itemId = const Value.absent(), + this.data = const Value.absent(), + }); + HistoryTableCompanion.insert({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + required String type, + required String itemId, + required String data, + }) : type = Value(type), + itemId = Value(itemId), + data = Value(data); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? type, + Expression? itemId, + Expression? data, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (type != null) 'type': type, + if (itemId != null) 'item_id': itemId, + if (data != null) 'data': data, + }); + } + + HistoryTableCompanion copyWith( + {Value? id, + Value? createdAt, + Value? type, + Value? itemId, + Value? data}) { + return HistoryTableCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + type: type ?? this.type, + itemId: itemId ?? this.itemId, + data: data ?? this.data, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (itemId.present) { + map['item_id'] = Variable(itemId.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('HistoryTableCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('type: $type, ') + ..write('itemId: $itemId, ') + ..write('data: $data') + ..write(')')) + .toString(); + } +} + +class LyricsTable extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LyricsTable(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + late final GeneratedColumn trackId = GeneratedColumn( + 'track_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn data = GeneratedColumn( + 'data', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => [id, trackId, data]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'lyrics_table'; + @override + Set get $primaryKey => {id}; + @override + LyricsTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LyricsTableData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + trackId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}track_id'])!, + data: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}data'])!, + ); + } + + @override + LyricsTable createAlias(String alias) { + return LyricsTable(attachedDatabase, alias); + } +} + +class LyricsTableData extends DataClass implements Insertable { + final int id; + final String trackId; + final String data; + const LyricsTableData( + {required this.id, required this.trackId, required this.data}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['track_id'] = Variable(trackId); + map['data'] = Variable(data); + return map; + } + + LyricsTableCompanion toCompanion(bool nullToAbsent) { + return LyricsTableCompanion( + id: Value(id), + trackId: Value(trackId), + data: Value(data), + ); + } + + factory LyricsTableData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LyricsTableData( + id: serializer.fromJson(json['id']), + trackId: serializer.fromJson(json['trackId']), + data: serializer.fromJson(json['data']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'trackId': serializer.toJson(trackId), + 'data': serializer.toJson(data), + }; + } + + LyricsTableData copyWith({int? id, String? trackId, String? data}) => + LyricsTableData( + id: id ?? this.id, + trackId: trackId ?? this.trackId, + data: data ?? this.data, + ); + LyricsTableData copyWithCompanion(LyricsTableCompanion data) { + return LyricsTableData( + id: data.id.present ? data.id.value : this.id, + trackId: data.trackId.present ? data.trackId.value : this.trackId, + data: data.data.present ? data.data.value : this.data, + ); + } + + @override + String toString() { + return (StringBuffer('LyricsTableData(') + ..write('id: $id, ') + ..write('trackId: $trackId, ') + ..write('data: $data') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, trackId, data); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LyricsTableData && + other.id == this.id && + other.trackId == this.trackId && + other.data == this.data); +} + +class LyricsTableCompanion extends UpdateCompanion { + final Value id; + final Value trackId; + final Value data; + const LyricsTableCompanion({ + this.id = const Value.absent(), + this.trackId = const Value.absent(), + this.data = const Value.absent(), + }); + LyricsTableCompanion.insert({ + this.id = const Value.absent(), + required String trackId, + required String data, + }) : trackId = Value(trackId), + data = Value(data); + static Insertable custom({ + Expression? id, + Expression? trackId, + Expression? data, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (trackId != null) 'track_id': trackId, + if (data != null) 'data': data, + }); + } + + LyricsTableCompanion copyWith( + {Value? id, Value? trackId, Value? data}) { + return LyricsTableCompanion( + id: id ?? this.id, + trackId: trackId ?? this.trackId, + data: data ?? this.data, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (trackId.present) { + map['track_id'] = Variable(trackId.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LyricsTableCompanion(') + ..write('id: $id, ') + ..write('trackId: $trackId, ') + ..write('data: $data') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV3 extends GeneratedDatabase { + DatabaseAtV3(QueryExecutor e) : super(e); + late final AuthenticationTable authenticationTable = + AuthenticationTable(this); + late final BlacklistTable blacklistTable = BlacklistTable(this); + late final PreferencesTable preferencesTable = PreferencesTable(this); + late final ScrobblerTable scrobblerTable = ScrobblerTable(this); + late final SkipSegmentTable skipSegmentTable = SkipSegmentTable(this); + late final SourceMatchTable sourceMatchTable = SourceMatchTable(this); + late final AudioPlayerStateTable audioPlayerStateTable = + AudioPlayerStateTable(this); + late final PlaylistTable playlistTable = PlaylistTable(this); + late final PlaylistMediaTable playlistMediaTable = PlaylistMediaTable(this); + late final HistoryTable historyTable = HistoryTable(this); + late final LyricsTable lyricsTable = LyricsTable(this); + late final Index uniqueBlacklist = Index('unique_blacklist', + 'CREATE UNIQUE INDEX unique_blacklist ON blacklist_table (element_type, element_id)'); + late final Index uniqTrackMatch = Index('uniq_track_match', + 'CREATE UNIQUE INDEX uniq_track_match ON source_match_table (track_id, source_id, source_type)'); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + authenticationTable, + blacklistTable, + preferencesTable, + scrobblerTable, + skipSegmentTable, + sourceMatchTable, + audioPlayerStateTable, + playlistTable, + playlistMediaTable, + historyTable, + lyricsTable, + uniqueBlacklist, + uniqTrackMatch + ]; + @override + int get schemaVersion => 3; +} diff --git a/untranslated_messages.json b/untranslated_messages.json index 1bfd16af2..9cbff978d 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -3,181 +3,441 @@ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "bn": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "ca": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "cs": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "de": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "es": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "eu": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "fa": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "fi": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "fr": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "hi": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "id": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "it": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "ja": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "ka": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "ko": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "ne": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "nl": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "pl": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "pt": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "ru": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "th": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "tr": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "uk": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "vi": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ], "zh": [ "invidious_instance", "invidious_description", "invidious_warning", - "invidious_source_description" + "invidious_source_description", + "cache_music", + "open", + "cache_folder", + "export", + "clear_cache", + "clear_cache_confirmation", + "export_cache_files", + "found_n_files", + "export_cache_confirmation", + "exported_n_out_of_m_files" ] }