mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
Merge branch 'dev' into chore/tests
This commit is contained in:
commit
dbd83c4bea
@ -12,3 +12,5 @@ LASTFM_API_SECRET=
|
|||||||
|
|
||||||
# Release channel. Can be: nightly, stable
|
# Release channel. Can be: nightly, stable
|
||||||
RELEASE_CHANNEL=
|
RELEASE_CHANNEL=
|
||||||
|
|
||||||
|
HIDE_DONATIONS=
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
{
|
{
|
||||||
"flutterSdkVersion": "3.22.3",
|
"flutterSdkVersion": "3.24.3"
|
||||||
"flavors": {}
|
|
||||||
}
|
}
|
30
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
30
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -7,8 +7,12 @@ labels:
|
|||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Is there an existing issue for this?
|
label: Is there an existing issue for this? (Please read the description)
|
||||||
description: Make sure to check if this issue is a duplicate.
|
description: |
|
||||||
|
PLEASE! Make sure to check if this issue is a duplicate.
|
||||||
|
Don't waste our time, we are working hard to make Spotube better for you.
|
||||||
|
|
||||||
|
Try with multiple similar keywords, and check the closed issues too.
|
||||||
options:
|
options:
|
||||||
- label: I have searched the existing issues
|
- label: I have searched the existing issues
|
||||||
required: true
|
required: true
|
||||||
@ -16,23 +20,41 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Current Behavior
|
label: Current Behavior
|
||||||
description: Write what you are experiencing currently.
|
description: Write what you are experiencing currently.
|
||||||
|
placeholder: |
|
||||||
|
The app isn't working as expected. It crashes when I do this...
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Expected Behavior
|
label: Expected Behavior
|
||||||
description: Write what you expected to happen.
|
description: Write what you expected to happen.
|
||||||
|
placeholder: |
|
||||||
|
The app should do this when I do that...
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Steps to reproduce
|
label: Steps to reproduce
|
||||||
description: Steps to reproduce the issue. A not well written description might delay the resolve of it.
|
description: Steps to reproduce the issue. A not well written description might lead to the delay in fixing the issue.
|
||||||
placeholder: |
|
placeholder: |
|
||||||
1. I opened the app
|
1. I opened the app
|
||||||
2. I did this
|
2. I did this
|
||||||
3. And that
|
3. And that
|
||||||
4. Then this happened
|
4. Then this happened
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Logs
|
||||||
|
description: |
|
||||||
|
If you have any logs, paste them here. Make sure to remove any sensitive information.
|
||||||
|
You can find the logs in the app's Settings > Developers > Logs page.
|
||||||
|
value: |
|
||||||
|
<details>
|
||||||
|
<summary>Logs</summary>
|
||||||
|
|
||||||
|
```
|
||||||
|
<Replace this line by pasting your logs here>
|
||||||
|
```
|
||||||
|
</details>
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
@ -74,7 +96,7 @@ body:
|
|||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Self grab
|
label: Self grab
|
||||||
description: If you are a developer and want to work on this issue yourself, you can check this box and wait for maintainer response. We welcome contributions!
|
description: If you are a developer and want to work on this issue yourself, you can check this box and wait for maintainer response. Any contributions are welcome!
|
||||||
options:
|
options:
|
||||||
- label: I'm ready to work on this issue!
|
- label: I'm ready to work on this issue!
|
||||||
required: false
|
required: false
|
||||||
|
2
.github/workflows/spotube-publish-binary.yml
vendored
2
.github/workflows/spotube-publish-binary.yml
vendored
@ -4,7 +4,7 @@ on:
|
|||||||
inputs:
|
inputs:
|
||||||
version:
|
version:
|
||||||
description: Version to publish (x.x.x)
|
description: Version to publish (x.x.x)
|
||||||
default: 3.8.1
|
default: 3.8.3
|
||||||
required: true
|
required: true
|
||||||
dry_run:
|
dry_run:
|
||||||
description: Dry run
|
description: Dry run
|
||||||
|
2
.github/workflows/spotube-release-binary.yml
vendored
2
.github/workflows/spotube-release-binary.yml
vendored
@ -20,7 +20,7 @@ on:
|
|||||||
description: Dry run without uploading to release
|
description: Dry run without uploading to release
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FLUTTER_VERSION: 3.22.3
|
FLUTTER_VERSION: 3.24.3
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -73,8 +73,10 @@ dist
|
|||||||
appimage-build
|
appimage-build
|
||||||
|
|
||||||
android/key.properties
|
android/key.properties
|
||||||
.fvm/flutter_sdk
|
|
||||||
|
|
||||||
**/pb_data
|
**/pb_data
|
||||||
|
|
||||||
tm.json
|
tm.json
|
||||||
|
|
||||||
|
# FVM Version Cache
|
||||||
|
.fvm/
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -25,6 +25,7 @@
|
|||||||
"explorer.fileNesting.patterns": {
|
"explorer.fileNesting.patterns": {
|
||||||
"pubspec.yaml": "pubspec.lock,analysis_options.yaml,.packages,.flutter-plugins,.flutter-plugins-dependencies,flutter_launcher_icons*.yaml,flutter_native_splash*.yaml",
|
"pubspec.yaml": "pubspec.lock,analysis_options.yaml,.packages,.flutter-plugins,.flutter-plugins-dependencies,flutter_launcher_icons*.yaml,flutter_native_splash*.yaml",
|
||||||
"README.md": "LICENSE,CODE_OF_CONDUCT.md,CONTRIBUTING.md,SECURITY.md,CONTRIBUTION.md,CHANGELOG.md,PRIVACY_POLICY.md",
|
"README.md": "LICENSE,CODE_OF_CONDUCT.md,CONTRIBUTING.md,SECURITY.md,CONTRIBUTION.md,CHANGELOG.md,PRIVACY_POLICY.md",
|
||||||
"*.dart": "${capture}.g.dart,${capture}.freezed.dart",
|
"*.dart": "${capture}.g.dart,${capture}.freezed.dart"
|
||||||
}
|
},
|
||||||
|
"dart.flutterSdkPath": ".fvm/flutter_sdk"
|
||||||
}
|
}
|
21
CHANGELOG.md
21
CHANGELOG.md
@ -2,6 +2,27 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
## [3.8.3](https://github.com/krtirtho/spotube/compare/v3.8.2...v3.8.3) (2024-10-09)
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- update youtube_explode_dart to 2.2.3 to fix no playback (#1980)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- **macos**: enable same window webview support
|
||||||
|
|
||||||
|
## [3.8.2](https://github.com/krtirtho/spotube/compare/v3.8.1...v3.8.2) (2024-09-30)
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- endless song loading issue and no playback #1925
|
||||||
|
|
||||||
|
|
||||||
## [3.8.1](https://github.com/krtirtho/spotube/compare/v3.8.0...v3.8.1) (2024-09-15)
|
## [3.8.1](https://github.com/krtirtho/spotube/compare/v3.8.0...v3.8.1) (2024-09-15)
|
||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
org.gradle.jvmargs=-Xmx1536M
|
org.gradle.jvmargs=-Xmx4608m
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
BIN
assets/invidious.jpg
Normal file
BIN
assets/invidious.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
@ -10,6 +10,8 @@ targets:
|
|||||||
explicit_to_json: true
|
explicit_to_json: true
|
||||||
drift_dev:
|
drift_dev:
|
||||||
options:
|
options:
|
||||||
|
databases:
|
||||||
|
app_db: lib/models/database/database.dart
|
||||||
sql:
|
sql:
|
||||||
dialect: sqlite
|
dialect: sqlite
|
||||||
options:
|
options:
|
||||||
|
@ -25,7 +25,8 @@ class AndroidBuildCommand extends Command with BuildCommandCommonSteps {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await dotEnvFile.writeAsString(
|
await dotEnvFile.writeAsString(
|
||||||
"\nENABLE_UPDATE_CHECK=0",
|
"\nENABLE_UPDATE_CHECK=0"
|
||||||
|
"\nHIDE_DONATIONS=1",
|
||||||
mode: FileMode.append,
|
mode: FileMode.append,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ class AndroidBuildCommand extends Command with BuildCommandCommonSteps {
|
|||||||
|
|
||||||
await shell.run(
|
await shell.run(
|
||||||
"""
|
"""
|
||||||
|
dart run build_runner clean
|
||||||
dart run build_runner build --delete-conflicting-outputs
|
dart run build_runner build --delete-conflicting-outputs
|
||||||
flutter build appbundle --flavor ${CliEnv.channel.name}
|
flutter build appbundle --flavor ${CliEnv.channel.name}
|
||||||
""",
|
""",
|
||||||
|
1
drift_schemas/app_db/drift_schema_v1.json
Normal file
1
drift_schemas/app_db/drift_schema_v1.json
Normal file
File diff suppressed because one or more lines are too long
1
drift_schemas/app_db/drift_schema_v2.json
Normal file
1
drift_schemas/app_db/drift_schema_v2.json
Normal file
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- app_links (0.0.1):
|
- app_links (0.0.2):
|
||||||
- Flutter
|
- Flutter
|
||||||
- audio_service (0.0.1):
|
- audio_service (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
@ -54,10 +54,10 @@ PODS:
|
|||||||
- flutter_inappwebview_ios (0.0.1):
|
- flutter_inappwebview_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_inappwebview_ios/Core (= 0.0.1)
|
- flutter_inappwebview_ios/Core (= 0.0.1)
|
||||||
- OrderedSet (~> 5.0)
|
- OrderedSet (~> 6.0.3)
|
||||||
- flutter_inappwebview_ios/Core (0.0.1):
|
- flutter_inappwebview_ios/Core (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- OrderedSet (~> 5.0)
|
- OrderedSet (~> 6.0.3)
|
||||||
- flutter_native_splash (0.0.1):
|
- flutter_native_splash (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_secure_storage (6.0.0):
|
- flutter_secure_storage (6.0.0):
|
||||||
@ -74,7 +74,7 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- metadata_god (0.0.1):
|
- metadata_god (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- OrderedSet (5.0.0)
|
- OrderedSet (6.0.3)
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
- path_provider_foundation (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
@ -202,7 +202,7 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
app_links: e70ca16b4b0f88253b3b3660200d4a10b4ea9795
|
app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0
|
||||||
audio_service: f509d65da41b9521a61f1c404dd58651f265a567
|
audio_service: f509d65da41b9521a61f1c404dd58651f265a567
|
||||||
audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207
|
audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207
|
||||||
bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842
|
bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842
|
||||||
@ -214,16 +214,16 @@ SPEC CHECKSUMS:
|
|||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
flutter_broadcasts: 3ece15b27d8ccbe2132c3df303e7c3401feab882
|
flutter_broadcasts: 3ece15b27d8ccbe2132c3df303e7c3401feab882
|
||||||
flutter_discord_rpc: e1c342f29ceb9dd76cdc01db59a70c93bb4d9ec5
|
flutter_discord_rpc: e1c342f29ceb9dd76cdc01db59a70c93bb4d9ec5
|
||||||
flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0
|
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
|
||||||
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
||||||
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
||||||
flutter_sharing_intent: e35380d0e1501d7111dbb7e46d5ac6339da6da98
|
flutter_sharing_intent: e35380d0e1501d7111dbb7e46d5ac6339da6da98
|
||||||
image_picker_ios: b545a5f16c0fa88e3ecbbce3ed4de45567a8ec18
|
image_picker_ios: b545a5f16c0fa88e3ecbbce3ed4de45567a8ec18
|
||||||
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
|
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
|
||||||
media_kit_libs_ios_audio: 8f39d96a9c630685dfb844c289bd1d114c486fb3
|
media_kit_libs_ios_audio: 8f39d96a9c630685dfb844c289bd1d114c486fb3
|
||||||
media_kit_native_event_loop: 99111eded5acbdc9c2738021ea6550dd36ca8837
|
media_kit_native_event_loop: 99111eded5acbdc9c2738021ea6550dd36ca8837
|
||||||
metadata_god: 4bbd8523cdb5d42c5e59d2fabad01ff8f4bc53f9
|
metadata_god: 4bbd8523cdb5d42c5e59d2fabad01ff8f4bc53f9
|
||||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||||
|
@ -49,6 +49,7 @@ class Assets {
|
|||||||
AssetGenImage('assets/bengali-patterns-bg.jpg');
|
AssetGenImage('assets/bengali-patterns-bg.jpg');
|
||||||
static const AssetGenImage branding = AssetGenImage('assets/branding.png');
|
static const AssetGenImage branding = AssetGenImage('assets/branding.png');
|
||||||
static const AssetGenImage emptyBox = AssetGenImage('assets/empty_box.png');
|
static const AssetGenImage emptyBox = AssetGenImage('assets/empty_box.png');
|
||||||
|
static const AssetGenImage invidious = AssetGenImage('assets/invidious.jpg');
|
||||||
static const AssetGenImage jiosaavn = AssetGenImage('assets/jiosaavn.png');
|
static const AssetGenImage jiosaavn = AssetGenImage('assets/jiosaavn.png');
|
||||||
static const AssetGenImage likedTracks =
|
static const AssetGenImage likedTracks =
|
||||||
AssetGenImage('assets/liked-tracks.jpg');
|
AssetGenImage('assets/liked-tracks.jpg');
|
||||||
@ -59,6 +60,8 @@ class Assets {
|
|||||||
AssetGenImage('assets/spotube-hero-banner.png');
|
AssetGenImage('assets/spotube-hero-banner.png');
|
||||||
static const AssetGenImage spotubeLogoForeground =
|
static const AssetGenImage spotubeLogoForeground =
|
||||||
AssetGenImage('assets/spotube-logo-foreground.jpg');
|
AssetGenImage('assets/spotube-logo-foreground.jpg');
|
||||||
|
static const AssetGenImage spotubeLogoBmp =
|
||||||
|
AssetGenImage('assets/spotube-logo.bmp');
|
||||||
static const String spotubeLogoIco = 'assets/spotube-logo.ico';
|
static const String spotubeLogoIco = 'assets/spotube-logo.ico';
|
||||||
static const AssetGenImage spotubeLogoPng =
|
static const AssetGenImage spotubeLogoPng =
|
||||||
AssetGenImage('assets/spotube-logo.png');
|
AssetGenImage('assets/spotube-logo.png');
|
||||||
@ -93,11 +96,13 @@ class Assets {
|
|||||||
bengaliPatternsBg,
|
bengaliPatternsBg,
|
||||||
branding,
|
branding,
|
||||||
emptyBox,
|
emptyBox,
|
||||||
|
invidious,
|
||||||
jiosaavn,
|
jiosaavn,
|
||||||
likedTracks,
|
likedTracks,
|
||||||
placeholder,
|
placeholder,
|
||||||
spotubeHeroBanner,
|
spotubeHeroBanner,
|
||||||
spotubeLogoForeground,
|
spotubeLogoForeground,
|
||||||
|
spotubeLogoBmp,
|
||||||
spotubeLogoIco,
|
spotubeLogoIco,
|
||||||
spotubeLogoPng,
|
spotubeLogoPng,
|
||||||
spotubeLogoSvg,
|
spotubeLogoSvg,
|
||||||
|
@ -19,6 +19,11 @@ abstract class Env {
|
|||||||
@EnviedField(varName: 'LASTFM_API_SECRET')
|
@EnviedField(varName: 'LASTFM_API_SECRET')
|
||||||
static final String lastFmApiSecret = _Env.lastFmApiSecret;
|
static final String lastFmApiSecret = _Env.lastFmApiSecret;
|
||||||
|
|
||||||
|
@EnviedField(varName: 'HIDE_DONATIONS', defaultValue: "0")
|
||||||
|
static final int _hideDonations = _Env._hideDonations;
|
||||||
|
|
||||||
|
static bool get hideDonations => _hideDonations == 1;
|
||||||
|
|
||||||
static final spotifySecrets = rawSpotifySecrets.split(',').map((e) {
|
static final spotifySecrets = rawSpotifySecrets.split(',').map((e) {
|
||||||
final secrets = e.trim().split(":").map((e) => e.trim());
|
final secrets = e.trim().split(":").map((e) => e.trim());
|
||||||
return {
|
return {
|
||||||
@ -41,4 +46,4 @@ abstract class Env {
|
|||||||
kIsFlatpak || _enableUpdateChecker == "1";
|
kIsFlatpak || _enableUpdateChecker == "1";
|
||||||
|
|
||||||
static String discordAppId = "1176718791388975124";
|
static String discordAppId = "1176718791388975124";
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import 'package:spotube/services/logger/logger.dart';
|
|||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
final appLinks = AppLinks();
|
final appLinks = AppLinks();
|
||||||
final linkStream = appLinks.allStringLinkStream.asBroadcastStream();
|
final linkStream = appLinks.stringLinkStream.asBroadcastStream();
|
||||||
|
|
||||||
void useDeepLinking(WidgetRef ref) {
|
void useDeepLinking(WidgetRef ref) {
|
||||||
// single instance no worries
|
// single instance no worries
|
||||||
|
@ -190,6 +190,9 @@
|
|||||||
"piped_instance": "Piped Server Instance",
|
"piped_instance": "Piped Server Instance",
|
||||||
"piped_description": "The Piped server instance to use for track matching",
|
"piped_description": "The Piped server instance to use for track matching",
|
||||||
"piped_warning": "Some of them might not work well. So use at your own risk",
|
"piped_warning": "Some of them might not work well. So use at your own risk",
|
||||||
|
"invidious_instance": "Invidious Server Instance",
|
||||||
|
"invidious_description": "The Invidious server instance to use for track matching",
|
||||||
|
"invidious_warning": "Some of them might not work well. So use at your own risk",
|
||||||
"generate_playlist": "Generate Playlist",
|
"generate_playlist": "Generate Playlist",
|
||||||
"track_exists": "Track {track} already exists",
|
"track_exists": "Track {track} already exists",
|
||||||
"replace_downloaded_tracks": "Replace all downloaded tracks",
|
"replace_downloaded_tracks": "Replace all downloaded tracks",
|
||||||
@ -307,6 +310,7 @@
|
|||||||
"youtube_source_description": "Recommended and works best.",
|
"youtube_source_description": "Recommended and works best.",
|
||||||
"piped_source_description": "Feeling free? Same as YouTube but a lot free.",
|
"piped_source_description": "Feeling free? Same as YouTube but a lot free.",
|
||||||
"jiosaavn_source_description": "Best for South Asian region.",
|
"jiosaavn_source_description": "Best for South Asian region.",
|
||||||
|
"invidious_source_description": "Similar to Piped but with higher availability.",
|
||||||
"highest_quality": "Highest Quality: {quality}",
|
"highest_quality": "Highest Quality: {quality}",
|
||||||
"select_audio_source": "Select Audio Source",
|
"select_audio_source": "Select Audio Source",
|
||||||
"endless_playback_description": "Automatically append new songs\nto the end of the queue",
|
"endless_playback_description": "Automatically append new songs\nto the end of the queue",
|
||||||
|
@ -16,7 +16,7 @@ _$WebSocketLoadEventDataPlaylistImpl
|
|||||||
? null
|
? null
|
||||||
: PlaylistSimple.fromJson(
|
: PlaylistSimple.fromJson(
|
||||||
Map<String, dynamic>.from(json['collection'] as Map)),
|
Map<String, dynamic>.from(json['collection'] as Map)),
|
||||||
initialIndex: json['initialIndex'] as int?,
|
initialIndex: (json['initialIndex'] as num?)?.toInt(),
|
||||||
$type: json['runtimeType'] as String?,
|
$type: json['runtimeType'] as String?,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ _$WebSocketLoadEventDataAlbumImpl _$$WebSocketLoadEventDataAlbumImplFromJson(
|
|||||||
? null
|
? null
|
||||||
: AlbumSimple.fromJson(
|
: AlbumSimple.fromJson(
|
||||||
Map<String, dynamic>.from(json['collection'] as Map)),
|
Map<String, dynamic>.from(json['collection'] as Map)),
|
||||||
initialIndex: json['initialIndex'] as int?,
|
initialIndex: (json['initialIndex'] as num?)?.toInt(),
|
||||||
$type: json['runtimeType'] as String?,
|
$type: json['runtimeType'] as String?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import 'package:media_kit/media_kit.dart' hide Track;
|
|||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:spotify/spotify.dart' hide Playlist;
|
import 'package:spotify/spotify.dart' hide Playlist;
|
||||||
|
import 'package:spotube/models/database/database.steps.dart';
|
||||||
import 'package:spotube/models/lyrics.dart';
|
import 'package:spotube/models/lyrics.dart';
|
||||||
import 'package:spotube/services/kv_store/encrypted_kv_store.dart';
|
import 'package:spotube/services/kv_store/encrypted_kv_store.dart';
|
||||||
import 'package:spotube/services/kv_store/kv_store.dart';
|
import 'package:spotube/services/kv_store/kv_store.dart';
|
||||||
@ -57,7 +58,20 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());
|
AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 1;
|
int get schemaVersion => 2;
|
||||||
|
|
||||||
|
@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,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyDatabase _openConnection() {
|
LazyDatabase _openConnection() {
|
||||||
|
File diff suppressed because it is too large
Load Diff
652
lib/models/database/database.steps.dart
Normal file
652
lib/models/database/database.steps.dart
Normal file
@ -0,0 +1,652 @@
|
|||||||
|
import 'package:drift/internal/versioned_schema.dart' as i0;
|
||||||
|
import 'package:drift/drift.dart' as i1;
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
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
|
||||||
|
|
||||||
|
// GENERATED BY drift_dev, DO NOT MODIFY.
|
||||||
|
final class Schema2 extends i0.VersionedSchema {
|
||||||
|
Schema2({required super.database}) : super(version: 2);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> 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 Shape2 preferencesTable = Shape2(
|
||||||
|
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,
|
||||||
|
],
|
||||||
|
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 Shape0 extends i0.VersionedTable {
|
||||||
|
Shape0({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get cookie =>
|
||||||
|
columnsByName['cookie']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get accessToken =>
|
||||||
|
columnsByName['access_token']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<DateTime> get expiration =>
|
||||||
|
columnsByName['expiration']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_0(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('id', aliasedName, false,
|
||||||
|
hasAutoIncrement: true,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
defaultConstraints:
|
||||||
|
i1.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
||||||
|
i1.GeneratedColumn<String> _column_1(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('cookie', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<String> _column_2(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('access_token', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<DateTime> _column_3(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<DateTime>('expiration', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.dateTime);
|
||||||
|
|
||||||
|
class Shape1 extends i0.VersionedTable {
|
||||||
|
Shape1({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get name =>
|
||||||
|
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get elementType =>
|
||||||
|
columnsByName['element_type']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get elementId =>
|
||||||
|
columnsByName['element_id']! as i1.GeneratedColumn<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<String> _column_4(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('name', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<String> _column_5(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('element_type', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<String> _column_6(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('element_id', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
|
||||||
|
class Shape2 extends i0.VersionedTable {
|
||||||
|
Shape2({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get audioQuality =>
|
||||||
|
columnsByName['audio_quality']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<bool> get albumColorSync =>
|
||||||
|
columnsByName['album_color_sync']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<bool> get amoledDarkTheme =>
|
||||||
|
columnsByName['amoled_dark_theme']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<bool> get checkUpdate =>
|
||||||
|
columnsByName['check_update']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<bool> get normalizeAudio =>
|
||||||
|
columnsByName['normalize_audio']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<bool> get showSystemTrayIcon =>
|
||||||
|
columnsByName['show_system_tray_icon']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<bool> get systemTitleBar =>
|
||||||
|
columnsByName['system_title_bar']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<bool> get skipNonMusic =>
|
||||||
|
columnsByName['skip_non_music']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<String> get closeBehavior =>
|
||||||
|
columnsByName['close_behavior']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get accentColorScheme =>
|
||||||
|
columnsByName['accent_color_scheme']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get layoutMode =>
|
||||||
|
columnsByName['layout_mode']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get locale =>
|
||||||
|
columnsByName['locale']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get market =>
|
||||||
|
columnsByName['market']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get searchMode =>
|
||||||
|
columnsByName['search_mode']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get downloadLocation =>
|
||||||
|
columnsByName['download_location']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get localLibraryLocation =>
|
||||||
|
columnsByName['local_library_location']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get pipedInstance =>
|
||||||
|
columnsByName['piped_instance']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get invidiousInstance =>
|
||||||
|
columnsByName['invidious_instance']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get themeMode =>
|
||||||
|
columnsByName['theme_mode']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get audioSource =>
|
||||||
|
columnsByName['audio_source']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get streamMusicCodec =>
|
||||||
|
columnsByName['stream_music_codec']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get downloadMusicCodec =>
|
||||||
|
columnsByName['download_music_codec']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<bool> get discordPresence =>
|
||||||
|
columnsByName['discord_presence']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<bool> get endlessPlayback =>
|
||||||
|
columnsByName['endless_playback']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<bool> get enableConnect =>
|
||||||
|
columnsByName['enable_connect']! as i1.GeneratedColumn<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<String> _column_7(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('audio_quality', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: Constant(SourceQualities.high.name));
|
||||||
|
i1.GeneratedColumn<bool> _column_8(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('album_color_sync', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("album_color_sync" IN (0, 1))'),
|
||||||
|
defaultValue: const Constant(true));
|
||||||
|
i1.GeneratedColumn<bool> _column_9(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('amoled_dark_theme', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("amoled_dark_theme" IN (0, 1))'),
|
||||||
|
defaultValue: const Constant(false));
|
||||||
|
i1.GeneratedColumn<bool> _column_10(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('check_update', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("check_update" IN (0, 1))'),
|
||||||
|
defaultValue: const Constant(true));
|
||||||
|
i1.GeneratedColumn<bool> _column_11(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('normalize_audio', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("normalize_audio" IN (0, 1))'),
|
||||||
|
defaultValue: const Constant(false));
|
||||||
|
i1.GeneratedColumn<bool> _column_12(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('show_system_tray_icon', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("show_system_tray_icon" IN (0, 1))'),
|
||||||
|
defaultValue: const Constant(false));
|
||||||
|
i1.GeneratedColumn<bool> _column_13(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('system_title_bar', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("system_title_bar" IN (0, 1))'),
|
||||||
|
defaultValue: const Constant(false));
|
||||||
|
i1.GeneratedColumn<bool> _column_14(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('skip_non_music', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("skip_non_music" IN (0, 1))'),
|
||||||
|
defaultValue: const Constant(false));
|
||||||
|
i1.GeneratedColumn<String> _column_15(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('close_behavior', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: Constant(CloseBehavior.close.name));
|
||||||
|
i1.GeneratedColumn<String> _column_16(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('accent_color_scheme', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: const Constant("Blue:0xFF2196F3"));
|
||||||
|
i1.GeneratedColumn<String> _column_17(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('layout_mode', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: Constant(LayoutMode.adaptive.name));
|
||||||
|
i1.GeneratedColumn<String> _column_18(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('locale', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue:
|
||||||
|
const Constant('{"languageCode":"system","countryCode":"system"}'));
|
||||||
|
i1.GeneratedColumn<String> _column_19(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('market', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string, defaultValue: Constant(Market.US.name));
|
||||||
|
i1.GeneratedColumn<String> _column_20(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('search_mode', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: Constant(SearchMode.youtube.name));
|
||||||
|
i1.GeneratedColumn<String> _column_21(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('download_location', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string, defaultValue: const Constant(""));
|
||||||
|
i1.GeneratedColumn<String> _column_22(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('local_library_location', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string, defaultValue: const Constant(""));
|
||||||
|
i1.GeneratedColumn<String> _column_23(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('piped_instance', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: const Constant("https://pipedapi.kavin.rocks"));
|
||||||
|
i1.GeneratedColumn<String> _column_24(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('invidious_instance', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: const Constant("https://inv.nadeko.net"));
|
||||||
|
i1.GeneratedColumn<String> _column_25(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('theme_mode', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: Constant(ThemeMode.system.name));
|
||||||
|
i1.GeneratedColumn<String> _column_26(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('audio_source', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: Constant(AudioSource.youtube.name));
|
||||||
|
i1.GeneratedColumn<String> _column_27(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('stream_music_codec', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: Constant(SourceCodecs.weba.name));
|
||||||
|
i1.GeneratedColumn<String> _column_28(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('download_music_codec', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: Constant(SourceCodecs.m4a.name));
|
||||||
|
i1.GeneratedColumn<bool> _column_29(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('discord_presence', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("discord_presence" IN (0, 1))'),
|
||||||
|
defaultValue: const Constant(true));
|
||||||
|
i1.GeneratedColumn<bool> _column_30(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('endless_playback', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("endless_playback" IN (0, 1))'),
|
||||||
|
defaultValue: const Constant(true));
|
||||||
|
i1.GeneratedColumn<bool> _column_31(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('enable_connect', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("enable_connect" IN (0, 1))'),
|
||||||
|
defaultValue: const Constant(false));
|
||||||
|
|
||||||
|
class Shape3 extends i0.VersionedTable {
|
||||||
|
Shape3({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||||
|
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
i1.GeneratedColumn<String> get username =>
|
||||||
|
columnsByName['username']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get passwordHash =>
|
||||||
|
columnsByName['password_hash']! as i1.GeneratedColumn<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<DateTime> _column_32(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<DateTime>('created_at', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.dateTime, defaultValue: currentDateAndTime);
|
||||||
|
i1.GeneratedColumn<String> _column_33(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('username', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<String> _column_34(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('password_hash', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
|
||||||
|
class Shape4 extends i0.VersionedTable {
|
||||||
|
Shape4({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get start =>
|
||||||
|
columnsByName['start']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get end =>
|
||||||
|
columnsByName['end']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get trackId =>
|
||||||
|
columnsByName['track_id']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||||
|
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_35(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('start', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
i1.GeneratedColumn<int> _column_36(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('end', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
i1.GeneratedColumn<String> _column_37(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('track_id', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
|
||||||
|
class Shape5 extends i0.VersionedTable {
|
||||||
|
Shape5({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get trackId =>
|
||||||
|
columnsByName['track_id']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get sourceId =>
|
||||||
|
columnsByName['source_id']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get sourceType =>
|
||||||
|
columnsByName['source_type']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||||
|
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<String> _column_38(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('source_id', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<String> _column_39(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('source_type', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: Constant(SourceType.youtube.name));
|
||||||
|
|
||||||
|
class Shape6 extends i0.VersionedTable {
|
||||||
|
Shape6({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<bool> get playing =>
|
||||||
|
columnsByName['playing']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<String> get loopMode =>
|
||||||
|
columnsByName['loop_mode']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<bool> get shuffled =>
|
||||||
|
columnsByName['shuffled']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<String> get collections =>
|
||||||
|
columnsByName['collections']! as i1.GeneratedColumn<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<bool> _column_40(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('playing', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("playing" IN (0, 1))'));
|
||||||
|
i1.GeneratedColumn<String> _column_41(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('loop_mode', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<bool> _column_42(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('shuffled', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("shuffled" IN (0, 1))'));
|
||||||
|
i1.GeneratedColumn<String> _column_43(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('collections', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
|
||||||
|
class Shape7 extends i0.VersionedTable {
|
||||||
|
Shape7({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get audioPlayerStateId =>
|
||||||
|
columnsByName['audio_player_state_id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get index =>
|
||||||
|
columnsByName['index']! as i1.GeneratedColumn<int>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_44(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('audio_player_state_id', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'REFERENCES audio_player_state_table (id)'));
|
||||||
|
i1.GeneratedColumn<int> _column_45(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('index', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
|
||||||
|
class Shape8 extends i0.VersionedTable {
|
||||||
|
Shape8({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get playlistId =>
|
||||||
|
columnsByName['playlist_id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get uri =>
|
||||||
|
columnsByName['uri']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get extras =>
|
||||||
|
columnsByName['extras']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get httpHeaders =>
|
||||||
|
columnsByName['http_headers']! as i1.GeneratedColumn<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_46(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('playlist_id', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'REFERENCES playlist_table (id)'));
|
||||||
|
i1.GeneratedColumn<String> _column_47(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('uri', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<String> _column_48(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('extras', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<String> _column_49(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('http_headers', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
|
||||||
|
class Shape9 extends i0.VersionedTable {
|
||||||
|
Shape9({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||||
|
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
i1.GeneratedColumn<String> get type =>
|
||||||
|
columnsByName['type']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get itemId =>
|
||||||
|
columnsByName['item_id']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get data =>
|
||||||
|
columnsByName['data']! as i1.GeneratedColumn<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<String> _column_50(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('type', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<String> _column_51(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('item_id', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<String> _column_52(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('data', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
|
||||||
|
class Shape10 extends i0.VersionedTable {
|
||||||
|
Shape10({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get trackId =>
|
||||||
|
columnsByName['track_id']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get data =>
|
||||||
|
columnsByName['data']! as i1.GeneratedColumn<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i0.MigrationStepWithVersion migrationSteps({
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||||
|
}) {
|
||||||
|
return (currentVersion, database) async {
|
||||||
|
switch (currentVersion) {
|
||||||
|
case 1:
|
||||||
|
final schema = Schema2(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from1To2(migrator, schema);
|
||||||
|
return 2;
|
||||||
|
default:
|
||||||
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.OnUpgrade stepByStep({
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||||
|
}) =>
|
||||||
|
i0.VersionedSchema.stepByStepHelper(
|
||||||
|
step: migrationSteps(
|
||||||
|
from1To2: from1To2,
|
||||||
|
));
|
@ -14,7 +14,8 @@ enum CloseBehavior {
|
|||||||
enum AudioSource {
|
enum AudioSource {
|
||||||
youtube,
|
youtube,
|
||||||
piped,
|
piped,
|
||||||
jiosaavn;
|
jiosaavn,
|
||||||
|
invidious;
|
||||||
|
|
||||||
String get label => name[0].toUpperCase() + name.substring(1);
|
String get label => name[0].toUpperCase() + name.substring(1);
|
||||||
}
|
}
|
||||||
@ -77,6 +78,8 @@ class PreferencesTable extends Table {
|
|||||||
text().withDefault(const Constant("")).map(const StringListConverter())();
|
text().withDefault(const Constant("")).map(const StringListConverter())();
|
||||||
TextColumn get pipedInstance =>
|
TextColumn get pipedInstance =>
|
||||||
text().withDefault(const Constant("https://pipedapi.kavin.rocks"))();
|
text().withDefault(const Constant("https://pipedapi.kavin.rocks"))();
|
||||||
|
TextColumn get invidiousInstance =>
|
||||||
|
text().withDefault(const Constant("https://inv.nadeko.net"))();
|
||||||
TextColumn get themeMode =>
|
TextColumn get themeMode =>
|
||||||
textEnum<ThemeMode>().withDefault(Constant(ThemeMode.system.name))();
|
textEnum<ThemeMode>().withDefault(Constant(ThemeMode.system.name))();
|
||||||
TextColumn get audioSource =>
|
TextColumn get audioSource =>
|
||||||
@ -113,6 +116,7 @@ class PreferencesTable extends Table {
|
|||||||
downloadLocation: "",
|
downloadLocation: "",
|
||||||
localLibraryLocation: const [],
|
localLibraryLocation: const [],
|
||||||
pipedInstance: "https://pipedapi.kavin.rocks",
|
pipedInstance: "https://pipedapi.kavin.rocks",
|
||||||
|
invidiousInstance: "https://inv.nadeko.net",
|
||||||
themeMode: ThemeMode.system,
|
themeMode: ThemeMode.system,
|
||||||
audioSource: AudioSource.youtube,
|
audioSource: AudioSource.youtube,
|
||||||
streamMusicCodec: SourceCodecs.weba,
|
streamMusicCodec: SourceCodecs.weba,
|
||||||
|
@ -73,41 +73,41 @@ class UserAlbums extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SliverGap(10),
|
const SliverGap(10),
|
||||||
Skeletonizer.sliver(
|
SliverLayoutBuilder(builder: (context, constrains) {
|
||||||
enabled: albumsQuery.isLoading,
|
return SliverGrid.builder(
|
||||||
child: SliverLayoutBuilder(builder: (context, constrains) {
|
itemCount: albums.isEmpty ? 6 : albums.length + 1,
|
||||||
return SliverGrid.builder(
|
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
itemCount: albums.isEmpty ? 6 : albums.length + 1,
|
maxCrossAxisExtent: 200,
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
mainAxisExtent: constrains.smAndDown ? 225 : 250,
|
||||||
maxCrossAxisExtent: 200,
|
crossAxisSpacing: 8,
|
||||||
mainAxisExtent: constrains.smAndDown ? 225 : 250,
|
mainAxisSpacing: 8,
|
||||||
crossAxisSpacing: 8,
|
),
|
||||||
mainAxisSpacing: 8,
|
itemBuilder: (context, index) {
|
||||||
),
|
if (albums.isNotEmpty && index == albums.length) {
|
||||||
itemBuilder: (context, index) {
|
if (albumsQuery.asData?.value.hasMore != true) {
|
||||||
if (albums.isNotEmpty && index == albums.length) {
|
return const SizedBox.shrink();
|
||||||
if (albumsQuery.asData?.value.hasMore != true) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Waypoint(
|
|
||||||
controller: controller,
|
|
||||||
isGrid: true,
|
|
||||||
onTouchEdge: albumsQueryNotifier.fetchMore,
|
|
||||||
child: Skeletonizer(
|
|
||||||
enabled: true,
|
|
||||||
child: AlbumCard(FakeData.albumSimple),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return AlbumCard(
|
return Waypoint(
|
||||||
albums.elementAtOrNull(index) ?? FakeData.albumSimple,
|
controller: controller,
|
||||||
|
isGrid: true,
|
||||||
|
onTouchEdge: albumsQueryNotifier.fetchMore,
|
||||||
|
child: Skeletonizer(
|
||||||
|
enabled: true,
|
||||||
|
child: AlbumCard(FakeData.albumSimple),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
|
||||||
}),
|
return Skeletonizer(
|
||||||
),
|
enabled: albumsQuery.isLoading,
|
||||||
|
child: AlbumCard(
|
||||||
|
albums.elementAtOrNull(index) ?? FakeData.albumSimple,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -74,45 +74,45 @@ class UserArtists extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SliverGap(10),
|
const SliverGap(10),
|
||||||
Skeletonizer.sliver(
|
SliverLayoutBuilder(builder: (context, constrains) {
|
||||||
enabled: artistQuery.isLoading,
|
return SliverGrid.builder(
|
||||||
child: SliverLayoutBuilder(builder: (context, constrains) {
|
itemCount: filteredArtists.isEmpty
|
||||||
return SliverGrid.builder(
|
? 6
|
||||||
itemCount: filteredArtists.isEmpty
|
: filteredArtists.length + 1,
|
||||||
? 6
|
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
: filteredArtists.length + 1,
|
maxCrossAxisExtent: 200,
|
||||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
mainAxisExtent: constrains.smAndDown ? 225 : 250,
|
||||||
maxCrossAxisExtent: 200,
|
crossAxisSpacing: 8,
|
||||||
mainAxisExtent: constrains.smAndDown ? 225 : 250,
|
mainAxisSpacing: 8,
|
||||||
crossAxisSpacing: 8,
|
),
|
||||||
mainAxisSpacing: 8,
|
itemBuilder: (context, index) {
|
||||||
),
|
if (filteredArtists.isNotEmpty &&
|
||||||
itemBuilder: (context, index) {
|
index == filteredArtists.length) {
|
||||||
if (filteredArtists.isNotEmpty &&
|
if (artistQuery.asData?.value.hasMore != true) {
|
||||||
index == filteredArtists.length) {
|
return const SizedBox.shrink();
|
||||||
if (artistQuery.asData?.value.hasMore != true) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Waypoint(
|
|
||||||
controller: controller,
|
|
||||||
isGrid: true,
|
|
||||||
onTouchEdge: artistQueryNotifier.fetchMore,
|
|
||||||
child: Skeletonizer(
|
|
||||||
enabled: true,
|
|
||||||
child: ArtistCard(FakeData.artist),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ArtistCard(
|
return Waypoint(
|
||||||
|
controller: controller,
|
||||||
|
isGrid: true,
|
||||||
|
onTouchEdge: artistQueryNotifier.fetchMore,
|
||||||
|
child: Skeletonizer(
|
||||||
|
enabled: true,
|
||||||
|
child: ArtistCard(FakeData.artist),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Skeletonizer(
|
||||||
|
enabled: artistQuery.isLoading,
|
||||||
|
child: ArtistCard(
|
||||||
filteredArtists.elementAtOrNull(index) ??
|
filteredArtists.elementAtOrNull(index) ??
|
||||||
FakeData.artist,
|
FakeData.artist,
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
}),
|
);
|
||||||
),
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -23,6 +23,7 @@ import 'package:spotube/provider/user_preferences/user_preferences_provider.dart
|
|||||||
import 'package:spotube/services/sourced_track/models/source_info.dart';
|
import 'package:spotube/services/sourced_track/models/source_info.dart';
|
||||||
import 'package:spotube/services/sourced_track/models/video_info.dart';
|
import 'package:spotube/services/sourced_track/models/video_info.dart';
|
||||||
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
||||||
|
import 'package:spotube/services/sourced_track/sources/invidious.dart';
|
||||||
import 'package:spotube/services/sourced_track/sources/jiosaavn.dart';
|
import 'package:spotube/services/sourced_track/sources/jiosaavn.dart';
|
||||||
import 'package:spotube/services/sourced_track/sources/piped.dart';
|
import 'package:spotube/services/sourced_track/sources/piped.dart';
|
||||||
import 'package:spotube/services/sourced_track/sources/youtube.dart';
|
import 'package:spotube/services/sourced_track/sources/youtube.dart';
|
||||||
@ -42,6 +43,17 @@ final sourceInfoToIconMap = {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PipedSourceInfo: const Icon(SpotubeIcons.piped),
|
PipedSourceInfo: const Icon(SpotubeIcons.piped),
|
||||||
|
InvidiousSourceInfo: Container(
|
||||||
|
height: 18,
|
||||||
|
width: 18,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(90),
|
||||||
|
image: DecorationImage(
|
||||||
|
image: Assets.invidious.provider(),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
class SiblingTracksSheet extends HookConsumerWidget {
|
class SiblingTracksSheet extends HookConsumerWidget {
|
||||||
|
@ -17,6 +17,10 @@ final audioSourceToIconMap = {
|
|||||||
size: 30,
|
size: 30,
|
||||||
),
|
),
|
||||||
AudioSource.piped: const Icon(SpotubeIcons.piped, size: 30),
|
AudioSource.piped: const Icon(SpotubeIcons.piped, size: 30),
|
||||||
|
AudioSource.invidious: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(48),
|
||||||
|
child: Assets.invidious.image(width: 48, height: 48),
|
||||||
|
),
|
||||||
AudioSource.jiosaavn: Assets.jiosaavn.image(width: 48, height: 48),
|
AudioSource.jiosaavn: Assets.jiosaavn.image(width: 48, height: 48),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -45,6 +49,7 @@ class GettingStartedPagePlaybackSection extends HookConsumerWidget {
|
|||||||
AudioSource.jiosaavn:
|
AudioSource.jiosaavn:
|
||||||
"${context.l10n.jiosaavn_source_description}\n"
|
"${context.l10n.jiosaavn_source_description}\n"
|
||||||
"${context.l10n.highest_quality("320kbps mp")}",
|
"${context.l10n.highest_quality("320kbps mp")}",
|
||||||
|
AudioSource.invidious: context.l10n.invidious_source_description,
|
||||||
},
|
},
|
||||||
[]);
|
[]);
|
||||||
|
|
||||||
@ -104,7 +109,9 @@ class GettingStartedPagePlaybackSection extends HookConsumerWidget {
|
|||||||
title: Align(
|
title: Align(
|
||||||
alignment: switch (preferences.audioSource) {
|
alignment: switch (preferences.audioSource) {
|
||||||
AudioSource.youtube => Alignment.centerLeft,
|
AudioSource.youtube => Alignment.centerLeft,
|
||||||
AudioSource.piped => Alignment.center,
|
AudioSource.piped ||
|
||||||
|
AudioSource.invidious =>
|
||||||
|
Alignment.center,
|
||||||
AudioSource.jiosaavn => Alignment.centerRight,
|
AudioSource.jiosaavn => Alignment.centerRight,
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:spotube/collections/env.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/modules/getting_started/blur_card.dart';
|
import 'package:spotube/modules/getting_started/blur_card.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
@ -62,21 +63,23 @@ class GettingStartedScreenSupportSection extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Gap(16),
|
if (!Env.hideDonations) ...[
|
||||||
FilledButton.icon(
|
const Gap(16),
|
||||||
icon: const Icon(SpotubeIcons.openCollective),
|
FilledButton.icon(
|
||||||
label: Text(context.l10n.donate_on_open_collective),
|
icon: const Icon(SpotubeIcons.openCollective),
|
||||||
style: FilledButton.styleFrom(
|
label: Text(context.l10n.donate_on_open_collective),
|
||||||
backgroundColor: const Color(0xff4cb7f6),
|
style: FilledButton.styleFrom(
|
||||||
foregroundColor: Colors.white,
|
backgroundColor: const Color(0xff4cb7f6),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
await launchUrlString(
|
||||||
|
"https://opencollective.com/spotube",
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
]
|
||||||
await launchUrlString(
|
|
||||||
"https://opencollective.com/spotube",
|
|
||||||
mode: LaunchMode.externalApplication,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -19,7 +19,7 @@ Future<void> Function() useLoginCallback(WidgetRef ref) {
|
|||||||
final authNotifier = ref.read(authenticationProvider.notifier);
|
final authNotifier = ref.read(authenticationProvider.notifier);
|
||||||
|
|
||||||
return useCallback(() async {
|
return useCallback(() async {
|
||||||
if (kIsMobile) {
|
if (kIsMobile || kIsMacOS) {
|
||||||
context.pushNamed(WebViewLogin.name);
|
context.pushNamed(WebViewLogin.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:spotube/components/titlebar/titlebar.dart';
|
||||||
|
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
@ -23,44 +24,47 @@ class WebViewLogin extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
appBar: const PageWindowTitleBar(
|
||||||
child: InAppWebView(
|
leading: BackButton(color: Colors.white),
|
||||||
initialSettings: InAppWebViewSettings(
|
backgroundColor: Colors.transparent,
|
||||||
userAgent:
|
),
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 safari/537.36",
|
extendBodyBehindAppBar: true,
|
||||||
),
|
body: InAppWebView(
|
||||||
initialUrlRequest: URLRequest(
|
initialSettings: InAppWebViewSettings(
|
||||||
url: WebUri("https://accounts.spotify.com/"),
|
userAgent:
|
||||||
),
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 safari/537.36",
|
||||||
onPermissionRequest: (controller, permissionRequest) async {
|
|
||||||
return PermissionResponse(
|
|
||||||
resources: permissionRequest.resources,
|
|
||||||
action: PermissionResponseAction.GRANT,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onLoadStop: (controller, action) async {
|
|
||||||
if (action == null) return;
|
|
||||||
String url = action.toString();
|
|
||||||
if (url.endsWith("/")) {
|
|
||||||
url = url.substring(0, url.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
final exp = RegExp(r"https:\/\/accounts.spotify.com\/.+\/status");
|
|
||||||
|
|
||||||
if (exp.hasMatch(url)) {
|
|
||||||
final cookies =
|
|
||||||
await CookieManager.instance().getCookies(url: action);
|
|
||||||
final cookieHeader =
|
|
||||||
"sp_dc=${cookies.firstWhere((element) => element.name == "sp_dc").value}";
|
|
||||||
|
|
||||||
await authenticationNotifier.login(cookieHeader);
|
|
||||||
if (context.mounted) {
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
GoRouter.of(context).go("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
initialUrlRequest: URLRequest(
|
||||||
|
url: WebUri("https://accounts.spotify.com/"),
|
||||||
|
),
|
||||||
|
onPermissionRequest: (controller, permissionRequest) async {
|
||||||
|
return PermissionResponse(
|
||||||
|
resources: permissionRequest.resources,
|
||||||
|
action: PermissionResponseAction.GRANT,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onLoadStop: (controller, action) async {
|
||||||
|
if (action == null) return;
|
||||||
|
String url = action.toString();
|
||||||
|
if (url.endsWith("/")) {
|
||||||
|
url = url.substring(0, url.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
final exp = RegExp(r"https:\/\/accounts.spotify.com\/.+\/status");
|
||||||
|
|
||||||
|
if (exp.hasMatch(url)) {
|
||||||
|
final cookies =
|
||||||
|
await CookieManager.instance().getCookies(url: action);
|
||||||
|
final cookieHeader =
|
||||||
|
"sp_dc=${cookies.firstWhere((element) => element.name == "sp_dc").value}";
|
||||||
|
|
||||||
|
await authenticationNotifier.login(cookieHeader);
|
||||||
|
if (context.mounted) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
GoRouter.of(context).go("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import 'package:spotube/components/titlebar/titlebar.dart';
|
|||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/hooks/controllers/use_package_info.dart';
|
import 'package:spotube/hooks/controllers/use_package_info.dart';
|
||||||
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
@ -139,63 +138,6 @@ class AboutSpotube extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Wrap(
|
|
||||||
runSpacing: 20,
|
|
||||||
spacing: 20,
|
|
||||||
alignment: WrapAlignment.center,
|
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
|
||||||
runAlignment: WrapAlignment.center,
|
|
||||||
children: [
|
|
||||||
MouseRegion(
|
|
||||||
cursor: SystemMouseCursors.click,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
launchUrl(
|
|
||||||
Uri.parse("https://www.buymeacoffee.com/krtirtho"),
|
|
||||||
mode: LaunchMode.externalApplication,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: SvgPicture.network(
|
|
||||||
"https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=krtirtho&button_colour=FF5F5F&font_colour=ffffff&font_family=Inter&outline_colour=000000&coffee_colour=FFDD00",
|
|
||||||
height: 45,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
MouseRegion(
|
|
||||||
cursor: SystemMouseCursors.click,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
launchUrl(
|
|
||||||
Uri.parse(
|
|
||||||
"https://opencollective.com/spotube",
|
|
||||||
),
|
|
||||||
mode: LaunchMode.externalApplication,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Image.network(
|
|
||||||
"https://opencollective.com/spotube/donate/button.png?color=blue",
|
|
||||||
height: 45,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
MouseRegion(
|
|
||||||
cursor: SystemMouseCursors.click,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
launchUrl(
|
|
||||||
Uri.parse("https://patreon.com/krtirtho"),
|
|
||||||
mode: LaunchMode.externalApplication,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Image.network(
|
|
||||||
"https://user-images.githubusercontent.com/61944859/180249027-678b01b8-c336-451e-b147-6d84a5b9d0e7.png",
|
|
||||||
height: 45,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Text(
|
Text(
|
||||||
context.l10n.made_with,
|
context.l10n.made_with,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
@ -21,48 +21,50 @@ class SettingsAboutSection extends HookConsumerWidget {
|
|||||||
return SectionCardWithHeading(
|
return SectionCardWithHeading(
|
||||||
heading: context.l10n.about,
|
heading: context.l10n.about,
|
||||||
children: [
|
children: [
|
||||||
AdaptiveListTile(
|
if (!Env.hideDonations)
|
||||||
leading: const Icon(
|
AdaptiveListTile(
|
||||||
SpotubeIcons.heart,
|
leading: const Icon(
|
||||||
color: Colors.pink,
|
SpotubeIcons.heart,
|
||||||
),
|
color: Colors.pink,
|
||||||
title: SizedBox(
|
),
|
||||||
height: 50,
|
title: SizedBox(
|
||||||
width: 200,
|
height: 50,
|
||||||
child: Align(
|
width: 200,
|
||||||
alignment: Alignment.centerLeft,
|
child: Align(
|
||||||
child: AutoSizeText(
|
alignment: Alignment.centerLeft,
|
||||||
context.l10n.u_love_spotube,
|
child: AutoSizeText(
|
||||||
maxLines: 1,
|
context.l10n.u_love_spotube,
|
||||||
style: const TextStyle(
|
maxLines: 1,
|
||||||
color: Colors.pink,
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
color: Colors.pink,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
trailing: (context, update) => FilledButton(
|
||||||
trailing: (context, update) => FilledButton(
|
style: ButtonStyle(
|
||||||
style: ButtonStyle(
|
backgroundColor: WidgetStatePropertyAll(Colors.red[100]),
|
||||||
backgroundColor: WidgetStatePropertyAll(Colors.red[100]),
|
foregroundColor:
|
||||||
foregroundColor: const WidgetStatePropertyAll(Colors.pinkAccent),
|
const WidgetStatePropertyAll(Colors.pinkAccent),
|
||||||
padding: const WidgetStatePropertyAll(EdgeInsets.all(15)),
|
padding: const WidgetStatePropertyAll(EdgeInsets.all(15)),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
launchUrlString(
|
launchUrlString(
|
||||||
"https://opencollective.com/spotube",
|
"https://opencollective.com/spotube",
|
||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Icon(SpotubeIcons.heart),
|
const Icon(SpotubeIcons.heart),
|
||||||
const SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
Text(context.l10n.please_sponsor),
|
Text(context.l10n.please_sponsor),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
if (Env.enableUpdateChecker)
|
if (Env.enableUpdateChecker)
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
secondary: const Icon(SpotubeIcons.update),
|
secondary: const Icon(SpotubeIcons.update),
|
||||||
|
@ -10,7 +10,8 @@ import 'package:spotube/models/database/database.dart';
|
|||||||
import 'package:spotube/modules/settings/section_card_with_heading.dart';
|
import 'package:spotube/modules/settings/section_card_with_heading.dart';
|
||||||
import 'package:spotube/components/adaptive/adaptive_select_tile.dart';
|
import 'package:spotube/components/adaptive/adaptive_select_tile.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/provider/piped_instances_provider.dart';
|
import 'package:spotube/provider/audio_player/sources/invidious_instances_provider.dart';
|
||||||
|
import 'package:spotube/provider/audio_player/sources/piped_instances_provider.dart';
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
|
|
||||||
import 'package:spotube/services/sourced_track/enums.dart';
|
import 'package:spotube/services/sourced_track/enums.dart';
|
||||||
@ -135,6 +136,73 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
child: preferences.audioSource != AudioSource.invidious
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: Consumer(builder: (context, ref, child) {
|
||||||
|
final instanceList = ref.watch(invidiousInstancesProvider);
|
||||||
|
|
||||||
|
return instanceList.when(
|
||||||
|
data: (data) {
|
||||||
|
return AdaptiveSelectTile<String>(
|
||||||
|
secondary: const Icon(SpotubeIcons.piped),
|
||||||
|
title: Text(context.l10n.invidious_instance),
|
||||||
|
subtitle: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: context.l10n.invidious_description,
|
||||||
|
style: theme.textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
const TextSpan(text: "\n"),
|
||||||
|
TextSpan(
|
||||||
|
text: context.l10n.invidious_warning,
|
||||||
|
style: theme.textTheme.labelMedium,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
value: preferences.invidiousInstance,
|
||||||
|
showValueWhenUnfolded: false,
|
||||||
|
options: data
|
||||||
|
.sortedBy((e) => e.name)
|
||||||
|
.map(
|
||||||
|
(e) => DropdownMenuItem(
|
||||||
|
value: e.details.uri,
|
||||||
|
child: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: "${e.name.trim()}\n",
|
||||||
|
style: theme.textTheme.labelLarge,
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: countryCodeToEmoji(
|
||||||
|
e.details.region,
|
||||||
|
),
|
||||||
|
style: GoogleFonts.notoColorEmoji(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
preferencesNotifier.setInvidiousInstance(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading: () => const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
error: (error, stackTrace) => Text(error.toString()),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
AnimatedSwitcher(
|
AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
child: preferences.audioSource != AudioSource.piped
|
child: preferences.audioSource != AudioSource.piped
|
||||||
@ -159,7 +227,8 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
|||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
child: preferences.searchMode == SearchMode.youtube &&
|
child: preferences.searchMode == SearchMode.youtube &&
|
||||||
(preferences.audioSource == AudioSource.piped ||
|
(preferences.audioSource == AudioSource.piped ||
|
||||||
preferences.audioSource == AudioSource.youtube)
|
preferences.audioSource == AudioSource.youtube ||
|
||||||
|
preferences.audioSource == AudioSource.invidious)
|
||||||
? SwitchListTile(
|
? SwitchListTile(
|
||||||
secondary: const Icon(SpotubeIcons.skip),
|
secondary: const Icon(SpotubeIcons.skip),
|
||||||
title: Text(context.l10n.skip_non_music),
|
title: Text(context.l10n.skip_non_music),
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:spotube/services/sourced_track/sources/invidious.dart';
|
||||||
|
|
||||||
|
final invidiousInstancesProvider = FutureProvider((ref) async {
|
||||||
|
final invidious = ref.watch(invidiousProvider);
|
||||||
|
|
||||||
|
final instances = await invidious.instances();
|
||||||
|
|
||||||
|
return instances
|
||||||
|
.where((instance) => instance.details.type == "https")
|
||||||
|
.toList();
|
||||||
|
});
|
@ -20,6 +20,17 @@ class ServerPlaybackRoutes {
|
|||||||
|
|
||||||
/// @get('/stream/<trackId>')
|
/// @get('/stream/<trackId>')
|
||||||
Future<Response> getStreamTrackId(Request request, String trackId) async {
|
Future<Response> getStreamTrackId(Request request, String trackId) async {
|
||||||
|
final options = Options(
|
||||||
|
headers: {
|
||||||
|
...request.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",
|
||||||
|
},
|
||||||
|
responseType: ResponseType.stream,
|
||||||
|
validateStatus: (status) => status! < 400,
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
final track =
|
final track =
|
||||||
playlist.tracks.firstWhere((element) => element.id == trackId);
|
playlist.tracks.firstWhere((element) => element.id == trackId);
|
||||||
@ -30,22 +41,33 @@ class ServerPlaybackRoutes {
|
|||||||
: await ref.read(sourcedTrackProvider(SpotubeMedia(track)).future);
|
: await ref.read(sourcedTrackProvider(SpotubeMedia(track)).future);
|
||||||
|
|
||||||
ref.read(activeSourcedTrackProvider.notifier).update(sourcedTrack);
|
ref.read(activeSourcedTrackProvider.notifier).update(sourcedTrack);
|
||||||
|
final res = await dio
|
||||||
final res = await dio.get(
|
.get(
|
||||||
sourcedTrack!.url,
|
sourcedTrack!.url,
|
||||||
options: Options(
|
options: options.copyWith(
|
||||||
headers: {
|
headers: {
|
||||||
...request.headers,
|
...options.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",
|
|
||||||
"host": Uri.parse(sourcedTrack.url).host,
|
"host": Uri.parse(sourcedTrack.url).host,
|
||||||
"Cache-Control": "max-age=0",
|
|
||||||
"Connection": "keep-alive",
|
|
||||||
},
|
},
|
||||||
responseType: ResponseType.stream,
|
|
||||||
validateStatus: (status) => status! < 500,
|
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
|
.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 =
|
final audioStream =
|
||||||
(res.data?.stream as Stream<Uint8List>?)?.asBroadcastStream();
|
(res.data?.stream as Stream<Uint8List>?)?.asBroadcastStream();
|
||||||
|
@ -5,24 +5,44 @@ import 'package:spotube/provider/audio_player/audio_player.dart';
|
|||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
||||||
|
|
||||||
final sourcedTrackProvider =
|
class SourcedTrackNotifier
|
||||||
FutureProvider.family<SourcedTrack?, SpotubeMedia?>((ref, media) async {
|
extends FamilyAsyncNotifier<SourcedTrack?, SpotubeMedia?> {
|
||||||
final track = media?.track;
|
@override
|
||||||
if (track == null || track is LocalTrack) {
|
build(media) async {
|
||||||
return null;
|
final track = media?.track;
|
||||||
|
if (track == null || track is LocalTrack) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.listen(
|
||||||
|
audioPlayerProvider.select((value) => value.tracks),
|
||||||
|
(old, next) {
|
||||||
|
if (next.isEmpty || next.none((element) => element.id == track.id)) {
|
||||||
|
ref.invalidateSelf();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final sourcedTrack =
|
||||||
|
await SourcedTrack.fetchFromTrack(track: track, ref: ref);
|
||||||
|
|
||||||
|
return sourcedTrack;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref.listen(
|
Future<SourcedTrack?> switchToAlternativeSources() async {
|
||||||
audioPlayerProvider.select((value) => value.tracks),
|
if (arg == null) {
|
||||||
(old, next) {
|
return null;
|
||||||
if (next.isEmpty || next.none((element) => element.id == track.id)) {
|
}
|
||||||
ref.invalidateSelf();
|
return await update((prev) async {
|
||||||
}
|
return await SourcedTrack.fetchFromTrackAltSource(
|
||||||
},
|
track: arg!.track,
|
||||||
);
|
ref: ref,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final sourcedTrack =
|
final sourcedTrackProvider = AsyncNotifierProviderFamily<SourcedTrackNotifier,
|
||||||
await SourcedTrack.fetchFromTrack(track: track, ref: ref);
|
SourcedTrack?, SpotubeMedia?>(
|
||||||
|
() => SourcedTrackNotifier(),
|
||||||
return sourcedTrack;
|
);
|
||||||
});
|
|
||||||
|
@ -157,8 +157,12 @@ class UserPreferencesNotifier extends Notifier<PreferencesTableData> {
|
|||||||
await setData(PreferencesTableCompanion(pipedInstance: Value(instance)));
|
await setData(PreferencesTableCompanion(pipedInstance: Value(instance)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setSearchMode(SearchMode mode) async {
|
void setInvidiousInstance(String instance) {
|
||||||
await setData(PreferencesTableCompanion(searchMode: Value(mode)));
|
setData(PreferencesTableCompanion(invidiousInstance: Value(instance)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSearchMode(SearchMode mode) {
|
||||||
|
setData(PreferencesTableCompanion(searchMode: Value(mode)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setSkipNonMusic(bool skip) async {
|
Future<void> setSkipNonMusic(bool skip) async {
|
||||||
|
@ -12,7 +12,7 @@ SourceInfo _$SourceInfoFromJson(Map json) => SourceInfo(
|
|||||||
artist: json['artist'] as String,
|
artist: json['artist'] as String,
|
||||||
thumbnail: json['thumbnail'] as String,
|
thumbnail: json['thumbnail'] as String,
|
||||||
pageUrl: json['pageUrl'] as String,
|
pageUrl: json['pageUrl'] as String,
|
||||||
duration: Duration(microseconds: json['duration'] as int),
|
duration: Duration(microseconds: (json['duration'] as num).toInt()),
|
||||||
artistUrl: json['artistUrl'] as String,
|
artistUrl: json['artistUrl'] as String,
|
||||||
album: json['album'] as String?,
|
album: json['album'] as String?,
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:invidious/invidious.dart';
|
||||||
import 'package:piped_client/piped_client.dart';
|
import 'package:piped_client/piped_client.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
|
|
||||||
@ -112,4 +113,24 @@ class YoutubeVideoInfo {
|
|||||||
channelId: stream.uploaderUrl,
|
channelId: stream.uploaderUrl,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory YoutubeVideoInfo.fromSearchResponse(
|
||||||
|
InvidiousSearchResponseVideo searchResponse,
|
||||||
|
SearchMode searchMode,
|
||||||
|
) {
|
||||||
|
return YoutubeVideoInfo(
|
||||||
|
searchMode: searchMode,
|
||||||
|
title: searchResponse.title,
|
||||||
|
duration: Duration(seconds: searchResponse.lengthSeconds),
|
||||||
|
thumbnailUrl: searchResponse.videoThumbnails.first.url,
|
||||||
|
id: searchResponse.videoId,
|
||||||
|
likes: 0,
|
||||||
|
dislikes: 0,
|
||||||
|
views: searchResponse.viewCount,
|
||||||
|
channelName: searchResponse.author,
|
||||||
|
channelId: searchResponse.authorId,
|
||||||
|
publishedAt:
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(searchResponse.published * 1000),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import 'package:spotube/services/sourced_track/enums.dart';
|
|||||||
import 'package:spotube/services/sourced_track/exceptions.dart';
|
import 'package:spotube/services/sourced_track/exceptions.dart';
|
||||||
import 'package:spotube/services/sourced_track/models/source_info.dart';
|
import 'package:spotube/services/sourced_track/models/source_info.dart';
|
||||||
import 'package:spotube/services/sourced_track/models/source_map.dart';
|
import 'package:spotube/services/sourced_track/models/source_map.dart';
|
||||||
|
import 'package:spotube/services/sourced_track/sources/invidious.dart';
|
||||||
import 'package:spotube/services/sourced_track/sources/jiosaavn.dart';
|
import 'package:spotube/services/sourced_track/sources/jiosaavn.dart';
|
||||||
import 'package:spotube/services/sourced_track/sources/piped.dart';
|
import 'package:spotube/services/sourced_track/sources/piped.dart';
|
||||||
import 'package:spotube/services/sourced_track/sources/youtube.dart';
|
import 'package:spotube/services/sourced_track/sources/youtube.dart';
|
||||||
@ -85,6 +86,13 @@ abstract class SourcedTrack extends Track {
|
|||||||
sourceInfo: sourceInfo,
|
sourceInfo: sourceInfo,
|
||||||
track: track,
|
track: track,
|
||||||
),
|
),
|
||||||
|
AudioSource.invidious => InvidiousSourcedTrack(
|
||||||
|
ref: ref,
|
||||||
|
source: source,
|
||||||
|
siblings: siblings,
|
||||||
|
sourceInfo: sourceInfo,
|
||||||
|
track: track,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +112,49 @@ abstract class SourcedTrack extends Track {
|
|||||||
return "$title - ${artists.join(", ")}";
|
return "$title - ${artists.join(", ")}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fetchFromTrackAltSource({
|
||||||
|
required Track track,
|
||||||
|
required Ref ref,
|
||||||
|
}) async {
|
||||||
|
final preferences = ref.read(userPreferencesProvider);
|
||||||
|
try {
|
||||||
|
return switch (preferences.audioSource) {
|
||||||
|
AudioSource.piped ||
|
||||||
|
AudioSource.invidious ||
|
||||||
|
AudioSource.jiosaavn =>
|
||||||
|
await YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||||
|
AudioSource.youtube =>
|
||||||
|
await JioSaavnSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||||
|
};
|
||||||
|
} on TrackNotFoundError catch (_) {
|
||||||
|
return switch (preferences.audioSource) {
|
||||||
|
AudioSource.piped ||
|
||||||
|
AudioSource.youtube ||
|
||||||
|
AudioSource.invidious =>
|
||||||
|
await JioSaavnSourcedTrack.fetchFromTrack(
|
||||||
|
track: track,
|
||||||
|
ref: ref,
|
||||||
|
weakMatch: true,
|
||||||
|
),
|
||||||
|
AudioSource.jiosaavn =>
|
||||||
|
await YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||||
|
};
|
||||||
|
} on HttpClientClosedException catch (_) {
|
||||||
|
return await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref);
|
||||||
|
} on VideoUnplayableException catch (_) {
|
||||||
|
return await InvidiousSourcedTrack.fetchFromTrack(track: track, ref: ref);
|
||||||
|
} catch (e) {
|
||||||
|
if (e is DioException || e is ClientException || e is SocketException) {
|
||||||
|
return await JioSaavnSourcedTrack.fetchFromTrack(
|
||||||
|
track: track,
|
||||||
|
ref: ref,
|
||||||
|
weakMatch: preferences.audioSource == AudioSource.jiosaavn,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Future<SourcedTrack> fetchFromTrack({
|
static Future<SourcedTrack> fetchFromTrack({
|
||||||
required Track track,
|
required Track track,
|
||||||
required Ref ref,
|
required Ref ref,
|
||||||
@ -117,11 +168,14 @@ abstract class SourcedTrack extends Track {
|
|||||||
await YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
await YoutubeSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||||
AudioSource.jiosaavn =>
|
AudioSource.jiosaavn =>
|
||||||
await JioSaavnSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
await JioSaavnSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||||
|
AudioSource.invidious =>
|
||||||
|
await InvidiousSourcedTrack.fetchFromTrack(track: track, ref: ref),
|
||||||
};
|
};
|
||||||
} on TrackNotFoundError catch (_) {
|
} on TrackNotFoundError catch (_) {
|
||||||
return switch (preferences.audioSource) {
|
return switch (preferences.audioSource) {
|
||||||
AudioSource.piped ||
|
AudioSource.piped ||
|
||||||
AudioSource.youtube =>
|
AudioSource.youtube ||
|
||||||
|
AudioSource.invidious =>
|
||||||
await JioSaavnSourcedTrack.fetchFromTrack(
|
await JioSaavnSourcedTrack.fetchFromTrack(
|
||||||
track: track,
|
track: track,
|
||||||
ref: ref,
|
ref: ref,
|
||||||
@ -136,11 +190,19 @@ abstract class SourcedTrack extends Track {
|
|||||||
return await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref);
|
return await PipedSourcedTrack.fetchFromTrack(track: track, ref: ref);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e is DioException || e is ClientException || e is SocketException) {
|
if (e is DioException || e is ClientException || e is SocketException) {
|
||||||
return await JioSaavnSourcedTrack.fetchFromTrack(
|
return switch (preferences.audioSource) {
|
||||||
track: track,
|
AudioSource.piped ||
|
||||||
ref: ref,
|
AudioSource.invidious =>
|
||||||
weakMatch: preferences.audioSource == AudioSource.jiosaavn,
|
await YoutubeSourcedTrack.fetchFromTrack(
|
||||||
);
|
track: track,
|
||||||
|
ref: ref,
|
||||||
|
),
|
||||||
|
_ => await JioSaavnSourcedTrack.fetchFromTrack(
|
||||||
|
track: track,
|
||||||
|
ref: ref,
|
||||||
|
weakMatch: preferences.audioSource == AudioSource.jiosaavn,
|
||||||
|
)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@ -159,6 +221,8 @@ abstract class SourcedTrack extends Track {
|
|||||||
YoutubeSourcedTrack.fetchSiblings(track: track, ref: ref),
|
YoutubeSourcedTrack.fetchSiblings(track: track, ref: ref),
|
||||||
AudioSource.jiosaavn =>
|
AudioSource.jiosaavn =>
|
||||||
JioSaavnSourcedTrack.fetchSiblings(track: track, ref: ref),
|
JioSaavnSourcedTrack.fetchSiblings(track: track, ref: ref),
|
||||||
|
AudioSource.invidious =>
|
||||||
|
InvidiousSourcedTrack.fetchSiblings(track: track, ref: ref),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
266
lib/services/sourced_track/sources/invidious.dart
Normal file
266
lib/services/sourced_track/sources/invidious.dart
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:spotube/models/database/database.dart';
|
||||||
|
import 'package:spotube/provider/database/database.dart';
|
||||||
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
|
import 'package:spotube/services/sourced_track/enums.dart';
|
||||||
|
import 'package:spotube/services/sourced_track/exceptions.dart';
|
||||||
|
import 'package:spotube/services/sourced_track/models/source_info.dart';
|
||||||
|
import 'package:spotube/services/sourced_track/models/source_map.dart';
|
||||||
|
import 'package:spotube/services/sourced_track/models/video_info.dart';
|
||||||
|
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
||||||
|
import 'package:invidious/invidious.dart';
|
||||||
|
import 'package:spotube/services/sourced_track/sources/youtube.dart';
|
||||||
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
|
|
||||||
|
final invidiousProvider = Provider<InvidiousClient>(
|
||||||
|
(ref) {
|
||||||
|
final invidiousInstance = ref.watch(
|
||||||
|
userPreferencesProvider.select((s) => s.invidiousInstance),
|
||||||
|
);
|
||||||
|
return InvidiousClient(server: invidiousInstance);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
class InvidiousSourceInfo extends SourceInfo {
|
||||||
|
InvidiousSourceInfo({
|
||||||
|
required super.id,
|
||||||
|
required super.title,
|
||||||
|
required super.artist,
|
||||||
|
required super.thumbnail,
|
||||||
|
required super.pageUrl,
|
||||||
|
required super.duration,
|
||||||
|
required super.artistUrl,
|
||||||
|
required super.album,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvidiousSourcedTrack extends SourcedTrack {
|
||||||
|
InvidiousSourcedTrack({
|
||||||
|
required super.ref,
|
||||||
|
required super.source,
|
||||||
|
required super.siblings,
|
||||||
|
required super.sourceInfo,
|
||||||
|
required super.track,
|
||||||
|
});
|
||||||
|
|
||||||
|
static Future<SourcedTrack> fetchFromTrack({
|
||||||
|
required Track track,
|
||||||
|
required Ref ref,
|
||||||
|
}) async {
|
||||||
|
final database = ref.read(databaseProvider);
|
||||||
|
final cachedSource = await (database.select(database.sourceMatchTable)
|
||||||
|
..where((s) => s.trackId.equals(track.id!))
|
||||||
|
..limit(1)
|
||||||
|
..orderBy([
|
||||||
|
(s) =>
|
||||||
|
OrderingTerm(expression: s.createdAt, mode: OrderingMode.desc),
|
||||||
|
]))
|
||||||
|
.getSingleOrNull();
|
||||||
|
final invidiousClient = ref.read(invidiousProvider);
|
||||||
|
|
||||||
|
if (cachedSource == null) {
|
||||||
|
final siblings = await fetchSiblings(ref: ref, track: track);
|
||||||
|
if (siblings.isEmpty) {
|
||||||
|
throw TrackNotFoundError(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
await database.into(database.sourceMatchTable).insert(
|
||||||
|
SourceMatchTableCompanion.insert(
|
||||||
|
trackId: track.id!,
|
||||||
|
sourceId: siblings.first.info.id,
|
||||||
|
sourceType: const Value(SourceType.youtube),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return InvidiousSourcedTrack(
|
||||||
|
ref: ref,
|
||||||
|
siblings: siblings.map((s) => s.info).skip(1).toList(),
|
||||||
|
source: siblings.first.source as SourceMap,
|
||||||
|
sourceInfo: siblings.first.info,
|
||||||
|
track: track,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final manifest =
|
||||||
|
await invidiousClient.videos.get(cachedSource.sourceId, local: true);
|
||||||
|
|
||||||
|
return InvidiousSourcedTrack(
|
||||||
|
ref: ref,
|
||||||
|
siblings: [],
|
||||||
|
source: toSourceMap(manifest),
|
||||||
|
sourceInfo: InvidiousSourceInfo(
|
||||||
|
id: manifest.videoId,
|
||||||
|
artist: manifest.author,
|
||||||
|
artistUrl: manifest.authorUrl,
|
||||||
|
pageUrl: "https://www.youtube.com/watch?v=${manifest.videoId}",
|
||||||
|
thumbnail: manifest.videoThumbnails.first.url,
|
||||||
|
title: manifest.title,
|
||||||
|
duration: Duration(seconds: manifest.lengthSeconds),
|
||||||
|
album: null,
|
||||||
|
),
|
||||||
|
track: track,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SourceMap toSourceMap(InvidiousVideoResponse manifest) {
|
||||||
|
final m4a = manifest.adaptiveFormats
|
||||||
|
.where((audio) => audio.type.contains("audio/mp4"))
|
||||||
|
.sorted((a, b) => int.parse(a.bitrate).compareTo(int.parse(b.bitrate)));
|
||||||
|
|
||||||
|
final weba = manifest.adaptiveFormats
|
||||||
|
.where((audio) => audio.type.contains("audio/webm"))
|
||||||
|
.sorted((a, b) => int.parse(a.bitrate).compareTo(int.parse(b.bitrate)));
|
||||||
|
|
||||||
|
return SourceMap(
|
||||||
|
m4a: SourceQualityMap(
|
||||||
|
high: m4a.first.url.toString(),
|
||||||
|
medium: (m4a.elementAtOrNull(m4a.length ~/ 2) ?? m4a[1]).url.toString(),
|
||||||
|
low: m4a.last.url.toString(),
|
||||||
|
),
|
||||||
|
weba: SourceQualityMap(
|
||||||
|
high: weba.first.url.toString(),
|
||||||
|
medium:
|
||||||
|
(weba.elementAtOrNull(weba.length ~/ 2) ?? weba[1]).url.toString(),
|
||||||
|
low: weba.last.url.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<SiblingType> toSiblingType(
|
||||||
|
int index,
|
||||||
|
YoutubeVideoInfo item,
|
||||||
|
InvidiousClient invidiousClient,
|
||||||
|
) async {
|
||||||
|
SourceMap? sourceMap;
|
||||||
|
if (index == 0) {
|
||||||
|
final manifest = await invidiousClient.videos.get(item.id, local: true);
|
||||||
|
sourceMap = toSourceMap(manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
final SiblingType sibling = (
|
||||||
|
info: InvidiousSourceInfo(
|
||||||
|
id: item.id,
|
||||||
|
artist: item.channelName,
|
||||||
|
artistUrl: "https://www.youtube.com/${item.channelId}",
|
||||||
|
pageUrl: "https://www.youtube.com/watch?v=${item.id}",
|
||||||
|
thumbnail: item.thumbnailUrl,
|
||||||
|
title: item.title,
|
||||||
|
duration: item.duration,
|
||||||
|
album: null,
|
||||||
|
),
|
||||||
|
source: sourceMap,
|
||||||
|
);
|
||||||
|
|
||||||
|
return sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<List<SiblingType>> fetchSiblings({
|
||||||
|
required Track track,
|
||||||
|
required Ref ref,
|
||||||
|
}) async {
|
||||||
|
final invidiousClient = ref.read(invidiousProvider);
|
||||||
|
final preference = ref.read(userPreferencesProvider);
|
||||||
|
|
||||||
|
final query = SourcedTrack.getSearchTerm(track);
|
||||||
|
|
||||||
|
final searchResults = await invidiousClient.search.list(
|
||||||
|
query,
|
||||||
|
type: InvidiousSearchType.video,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ServiceUtils.onlyContainsEnglish(query)) {
|
||||||
|
return await Future.wait(
|
||||||
|
searchResults
|
||||||
|
.whereType<InvidiousSearchResponseVideo>()
|
||||||
|
.map(
|
||||||
|
(result) => YoutubeVideoInfo.fromSearchResponse(
|
||||||
|
result,
|
||||||
|
preference.searchMode,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.mapIndexed((i, r) => toSiblingType(i, r, invidiousClient)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final rankedSiblings = YoutubeSourcedTrack.rankResults(
|
||||||
|
searchResults
|
||||||
|
.whereType<InvidiousSearchResponseVideo>()
|
||||||
|
.map(
|
||||||
|
(result) => YoutubeVideoInfo.fromSearchResponse(
|
||||||
|
result,
|
||||||
|
preference.searchMode,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
track,
|
||||||
|
);
|
||||||
|
|
||||||
|
return await Future.wait(
|
||||||
|
rankedSiblings.mapIndexed((i, r) => toSiblingType(i, r, invidiousClient)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SourcedTrack> copyWithSibling() async {
|
||||||
|
if (siblings.isNotEmpty) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
final fetchedSiblings = await fetchSiblings(ref: ref, track: this);
|
||||||
|
|
||||||
|
return InvidiousSourcedTrack(
|
||||||
|
ref: ref,
|
||||||
|
siblings: fetchedSiblings
|
||||||
|
.where((s) => s.info.id != sourceInfo.id)
|
||||||
|
.map((s) => s.info)
|
||||||
|
.toList(),
|
||||||
|
source: source,
|
||||||
|
sourceInfo: sourceInfo,
|
||||||
|
track: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SourcedTrack?> swapWithSibling(SourceInfo sibling) async {
|
||||||
|
if (sibling.id == sourceInfo.id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a sibling source that was fetched from the search results
|
||||||
|
final isStepSibling = siblings.none((s) => s.id == sibling.id);
|
||||||
|
|
||||||
|
final newSourceInfo = isStepSibling
|
||||||
|
? sibling
|
||||||
|
: siblings.firstWhere((s) => s.id == sibling.id);
|
||||||
|
final newSiblings = siblings.where((s) => s.id != sibling.id).toList()
|
||||||
|
..insert(0, sourceInfo);
|
||||||
|
|
||||||
|
final pipedClient = ref.read(invidiousProvider);
|
||||||
|
|
||||||
|
final manifest =
|
||||||
|
await pipedClient.videos.get(newSourceInfo.id, local: true);
|
||||||
|
|
||||||
|
final database = ref.read(databaseProvider);
|
||||||
|
await database.into(database.sourceMatchTable).insert(
|
||||||
|
SourceMatchTableCompanion.insert(
|
||||||
|
trackId: id!,
|
||||||
|
sourceId: newSourceInfo.id,
|
||||||
|
sourceType: const Value(SourceType.youtube),
|
||||||
|
// Because we're sorting by createdAt in the query
|
||||||
|
// we have to update it to indicate priority
|
||||||
|
createdAt: Value(DateTime.now()),
|
||||||
|
),
|
||||||
|
mode: InsertMode.replace,
|
||||||
|
);
|
||||||
|
|
||||||
|
return InvidiousSourcedTrack(
|
||||||
|
ref: ref,
|
||||||
|
siblings: newSiblings,
|
||||||
|
source: toSourceMap(manifest),
|
||||||
|
sourceInfo: newSourceInfo,
|
||||||
|
track: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- flutter_inappwebview_macos (0.0.1):
|
- flutter_inappwebview_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- OrderedSet (~> 5.0)
|
- OrderedSet (~> 6.0.3)
|
||||||
- flutter_secure_storage_macos (6.1.1):
|
- flutter_secure_storage_macos (6.1.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- FlutterMacOS (1.0.0)
|
- FlutterMacOS (1.0.0)
|
||||||
@ -30,7 +30,7 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- metadata_god (0.0.1):
|
- metadata_god (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- OrderedSet (5.0.0)
|
- OrderedSet (6.0.3)
|
||||||
- package_info_plus (0.0.1):
|
- package_info_plus (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- path_provider_foundation (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
@ -161,14 +161,14 @@ SPEC CHECKSUMS:
|
|||||||
device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720
|
device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720
|
||||||
file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9
|
file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9
|
||||||
flutter_discord_rpc: 67a7c10ea24d9d3bf35d01af643f48fbcfa7c24f
|
flutter_discord_rpc: 67a7c10ea24d9d3bf35d01af643f48fbcfa7c24f
|
||||||
flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d
|
flutter_inappwebview_macos: bdf207b8f4ebd58e86ae06cd96b147de99a67c9b
|
||||||
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
|
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
|
||||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff
|
local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff
|
||||||
media_kit_libs_macos_audio: 3871782a4f3f84c77f04d7666c87800a781c24da
|
media_kit_libs_macos_audio: 3871782a4f3f84c77f04d7666c87800a781c24da
|
||||||
media_kit_native_event_loop: 7321675377cb9ae8596a29bddf3a3d2b5e8792c5
|
media_kit_native_event_loop: 7321675377cb9ae8596a29bddf3a3d2b5e8792c5
|
||||||
metadata_god: 829f61208b44ac1173e7cd32ab740d8776be5435
|
metadata_god: 829f61208b44ac1173e7cd32ab740d8776be5435
|
||||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||||
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
|
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
|
||||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||||
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
|
|
||||||
@NSApplicationMain
|
@main
|
||||||
class AppDelegate: FlutterAppDelegate {
|
class AppDelegate: FlutterAppDelegate {
|
||||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||||
return false
|
return false
|
||||||
|
249
pubspec.lock
249
pubspec.lock
@ -37,10 +37,34 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: app_links
|
name: app_links
|
||||||
sha256: "42dc15aecf2618ace4ffb74a2e58a50e45cd1b9f2c17c8f0cafe4c297f08c815"
|
sha256: ad1a6d598e7e39b46a34f746f9a8b011ee147e4c275d407fa457e7a62f84dd99
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.1"
|
version: "6.3.2"
|
||||||
|
app_links_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_linux
|
||||||
|
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.3"
|
||||||
|
app_links_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_platform_interface
|
||||||
|
sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
|
app_links_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_web
|
||||||
|
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -205,10 +229,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_daemon
|
name: build_daemon
|
||||||
sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
|
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.1"
|
version: "4.0.2"
|
||||||
build_resolvers:
|
build_resolvers:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -221,10 +245,10 @@ packages:
|
|||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
|
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.9"
|
version: "2.4.13"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -502,10 +526,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dio
|
name: dio
|
||||||
sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5"
|
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.3+1"
|
version: "5.7.0"
|
||||||
|
dio_web_adapter:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dio_web_adapter
|
||||||
|
sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
disable_battery_optimization:
|
disable_battery_optimization:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -527,18 +559,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: drift
|
name: drift
|
||||||
sha256: "6acedc562ffeed308049f78fb1906abad3d65714580b6745441ee6d50ec564cd"
|
sha256: df027d168a2985a2e9da900adeba2ab0136f0d84436592cf3cd5135f82c8579c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.18.0"
|
version: "2.21.0"
|
||||||
drift_dev:
|
drift_dev:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: drift_dev
|
name: drift_dev
|
||||||
sha256: d9b020736ea85fff1568699ce18b89fabb3f0f042e8a7a05e84a3ec20d39acde
|
sha256: "27bab15e7869b69259663590381180117873b9b273a1ea9ebb21bb73133d1233"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.18.0"
|
version: "2.21.0"
|
||||||
duration:
|
duration:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -591,10 +623,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.3"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -715,12 +747,11 @@ packages:
|
|||||||
flutter_discord_rpc:
|
flutter_discord_rpc:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "packages/flutter_discord_rpc"
|
name: flutter_discord_rpc
|
||||||
ref: cargokit
|
sha256: "9363a803863d56fd89c0a21639c70b126245fcbafaeb0a3d091e7ac06951d03f"
|
||||||
resolved-ref: "331636d8e378e3ac9ad30a4b0d3eed17d5a85fe9"
|
url: "https://pub.dev"
|
||||||
url: "https://github.com/KRTirtho/frb_plugins.git"
|
source: hosted
|
||||||
source: git
|
version: "1.0.0"
|
||||||
version: "0.1.0+1"
|
|
||||||
flutter_displaymode:
|
flutter_displaymode:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -770,18 +801,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_inappwebview
|
name: flutter_inappwebview
|
||||||
sha256: "3e9a443a18ecef966fb930c3a76ca5ab6a7aafc0c7b5e14a4a850cf107b09959"
|
sha256: "274edbb07196944e316722d9f6f641c77d0e71261200869887e10f59614c0458"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
version: "6.1.3"
|
||||||
flutter_inappwebview_android:
|
flutter_inappwebview_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_inappwebview_android
|
name: flutter_inappwebview_android
|
||||||
sha256: d247f6ed417f1f8c364612fa05a2ecba7f775c8d0c044c1d3b9ee33a6515c421
|
sha256: f48203a11c5eb0c23dd5a3cb3638ae678056b6ceae22819373e36c6cb4f1d46a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.13"
|
version: "1.1.1"
|
||||||
flutter_inappwebview_internal_annotations:
|
flutter_inappwebview_internal_annotations:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -794,34 +825,42 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_inappwebview_ios
|
name: flutter_inappwebview_ios
|
||||||
sha256: f363577208b97b10b319cd0c428555cd8493e88b468019a8c5635a0e4312bd0f
|
sha256: f6f88d464b38f2fc1c5f2ae74024498115eb1470715bd8b40f902dd4ac99ccc8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.13"
|
version: "1.1.1"
|
||||||
flutter_inappwebview_macos:
|
flutter_inappwebview_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_inappwebview_macos
|
name: flutter_inappwebview_macos
|
||||||
sha256: b55b9e506c549ce88e26580351d2c71d54f4825901666bd6cfa4be9415bb2636
|
sha256: "68e0c3785d8d789710cda7d7efe6effa337c91bf300dd28af7efc2d358fa1a98"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.11"
|
version: "1.1.1"
|
||||||
flutter_inappwebview_platform_interface:
|
flutter_inappwebview_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_inappwebview_platform_interface
|
name: flutter_inappwebview_platform_interface
|
||||||
sha256: "545fd4c25a07d2775f7d5af05a979b2cac4fbf79393b0a7f5d33ba39ba4f6187"
|
sha256: "97b4ab116d949ede20c90c7e3d15d24afaf1b706cc0af96b060770293cd6c49d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.10"
|
version: "1.2.0"
|
||||||
flutter_inappwebview_web:
|
flutter_inappwebview_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_inappwebview_web
|
name: flutter_inappwebview_web
|
||||||
sha256: d8c680abfb6fec71609a700199635d38a744df0febd5544c5a020bd73de8ee07
|
sha256: f7f97b6faa39416e4e86da1184edd4de6c27b271d036f0838ea3ff9a250a1de2
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.8"
|
version: "1.1.1"
|
||||||
|
flutter_inappwebview_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_inappwebview_windows
|
||||||
|
sha256: "86702d2109384311f8ea634855e90ee143b9bfabddd3858696d905a2c28808aa"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.0"
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -871,10 +910,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_rust_bridge
|
name: flutter_rust_bridge
|
||||||
sha256: fac14d2dd67eeba29a20e5d99fac0d4d9fcd552cdf6bf4f8945f7679c6b07b1d
|
sha256: "0ad5079de35d317650fec59b26cb4d0c116ebc2ce703a29f9367513b8a91c287"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.5.0"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -969,10 +1008,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: freezed_annotation
|
name: freezed_annotation
|
||||||
sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d
|
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.4"
|
||||||
frontend_server_client:
|
frontend_server_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1223,6 +1262,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.19.0"
|
version: "0.19.0"
|
||||||
|
invidious:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: invidious
|
||||||
|
sha256: "7cb879c0b4b99aa06ec720af84f6988ff0080bb0434d041f6fb0c4add680ee36"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.0"
|
||||||
io:
|
io:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -1251,34 +1298,34 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.1"
|
version: "4.9.0"
|
||||||
json_serializable:
|
json_serializable:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: json_serializable
|
name: json_serializable
|
||||||
sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969
|
sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.7.1"
|
version: "6.8.0"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.4"
|
version: "10.0.5"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.5"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1295,6 +1342,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
|
lists:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lists
|
||||||
|
sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
local_notifier:
|
local_notifier:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1339,10 +1394,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.0"
|
version: "0.11.1"
|
||||||
media_kit:
|
media_kit:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1419,19 +1474,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.12.0"
|
version: "1.15.0"
|
||||||
metadata_god:
|
metadata_god:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "packages/metadata_god"
|
name: metadata_god
|
||||||
ref: cargokit
|
sha256: "025d8149059f62f44108ab9d74ebd77aa8f0af98b238f3f25121a4711ee3e5d0"
|
||||||
resolved-ref: "331636d8e378e3ac9ad30a4b0d3eed17d5a85fe9"
|
url: "https://pub.dev"
|
||||||
url: "https://github.com/KRTirtho/frb_plugins.git"
|
source: hosted
|
||||||
source: git
|
version: "1.0.0"
|
||||||
version: "0.5.3"
|
|
||||||
mime:
|
mime:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1644,10 +1698,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.4"
|
version: "3.1.5"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1901,10 +1955,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "2.0.0"
|
||||||
shortid:
|
shortid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1953,12 +2007,11 @@ packages:
|
|||||||
smtc_windows:
|
smtc_windows:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "packages/smtc_windows"
|
name: smtc_windows
|
||||||
ref: cargokit
|
sha256: "80f7c10867da485ffdf87f842bf27e6763589933c18c11af5dc1cd1e158c3154"
|
||||||
resolved-ref: "331636d8e378e3ac9ad30a4b0d3eed17d5a85fe9"
|
url: "https://pub.dev"
|
||||||
url: "https://github.com/KRTirtho/frb_plugins.git"
|
source: hosted
|
||||||
source: git
|
version: "1.0.0"
|
||||||
version: "0.1.3"
|
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2051,10 +2104,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sqlparser
|
name: sqlparser
|
||||||
sha256: ade9a67fd70d0369329ed3373208de7ebd8662470e8c396fc8d0d60f9acdfc9f
|
sha256: c5f63dff8677407ddcddfa4744c176ea6dc44286c47ba9e69e76d8071398034d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.36.0"
|
version: "0.39.1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2144,29 +2197,29 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.1"
|
||||||
test:
|
test:
|
||||||
dependency: "direct dev"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: test
|
name: test
|
||||||
sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073"
|
sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.25.2"
|
version: "1.25.7"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.0"
|
version: "0.7.2"
|
||||||
test_core:
|
test_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_core
|
name: test_core
|
||||||
sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4"
|
sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.6.4"
|
||||||
time:
|
time:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2223,6 +2276,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.2"
|
||||||
|
unicode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: unicode
|
||||||
|
sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.1"
|
||||||
universal_io:
|
universal_io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2323,10 +2384,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
|
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.4.0"
|
version: "4.5.1"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2363,10 +2424,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.1"
|
version: "14.2.5"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2376,21 +2437,29 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "1.1.0"
|
||||||
|
web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket
|
||||||
|
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.6"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
|
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.5"
|
version: "3.0.1"
|
||||||
webdriver:
|
webdriver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2419,18 +2488,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a"
|
sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.0"
|
version: "5.5.4"
|
||||||
win32_registry:
|
win32_registry:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: win32_registry
|
name: win32_registry
|
||||||
sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb"
|
sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "1.1.5"
|
||||||
window_manager:
|
window_manager:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -2467,10 +2536,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: youtube_explode_dart
|
name: youtube_explode_dart
|
||||||
sha256: "26c9671d638f3396a1bfb2666f586988ee7b0ba3469e478b22a4c1a168bcf6ee"
|
sha256: "523a01ef948607d3e8fdcdcdcef1ce805c7d26480f609e3b209d1c73520a6c3c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.3.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.4.0 <4.0.0"
|
dart: ">=3.5.3 <4.0.0"
|
||||||
flutter: ">=3.19.2"
|
flutter: ">=3.24.0"
|
||||||
|
44
pubspec.yaml
44
pubspec.yaml
@ -3,7 +3,7 @@ description: Open source Spotify client that doesn't require Premium nor uses El
|
|||||||
|
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
|
|
||||||
version: 3.8.1+34
|
version: 3.8.3+37
|
||||||
|
|
||||||
homepage: https://spotube.krtirtho.dev
|
homepage: https://spotube.krtirtho.dev
|
||||||
repository: https://github.com/KRTirtho/spotube
|
repository: https://github.com/KRTirtho/spotube
|
||||||
@ -21,7 +21,7 @@ dependencies:
|
|||||||
auto_size_text: ^3.0.0
|
auto_size_text: ^3.0.0
|
||||||
buttons_tabbar: ^1.3.8
|
buttons_tabbar: ^1.3.8
|
||||||
cached_network_image: ^3.3.1
|
cached_network_image: ^3.3.1
|
||||||
collection: ^1.15.0
|
collection: ^1.18.0
|
||||||
curved_navigation_bar: ^1.0.3
|
curved_navigation_bar: ^1.0.3
|
||||||
desktop_webview_window:
|
desktop_webview_window:
|
||||||
git:
|
git:
|
||||||
@ -42,7 +42,7 @@ dependencies:
|
|||||||
flutter_displaymode: ^0.6.0
|
flutter_displaymode: ^0.6.0
|
||||||
flutter_feather_icons: ^2.0.0+1
|
flutter_feather_icons: ^2.0.0+1
|
||||||
flutter_hooks: ^0.20.5
|
flutter_hooks: ^0.20.5
|
||||||
flutter_inappwebview: ^6.0.0
|
flutter_inappwebview: ^6.1.3
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_native_splash: ^2.4.0
|
flutter_native_splash: ^2.4.0
|
||||||
@ -58,15 +58,12 @@ dependencies:
|
|||||||
html: ^0.15.1
|
html: ^0.15.1
|
||||||
image_picker: ^1.1.0
|
image_picker: ^1.1.0
|
||||||
intl: any
|
intl: any
|
||||||
|
invidious: ^0.1.0
|
||||||
json_annotation: ^4.8.1
|
json_annotation: ^4.8.1
|
||||||
logger: ^2.0.2
|
logger: ^2.0.2
|
||||||
media_kit: ^1.1.10+1
|
media_kit: ^1.1.10+1
|
||||||
media_kit_libs_audio: ^1.0.4
|
media_kit_libs_audio: ^1.0.4
|
||||||
metadata_god:
|
metadata_god: ^1.0.0
|
||||||
git:
|
|
||||||
url: https://github.com/KRTirtho/frb_plugins.git
|
|
||||||
path: packages/metadata_god
|
|
||||||
ref: cargokit
|
|
||||||
mime: ^1.0.2
|
mime: ^1.0.2
|
||||||
package_info_plus: ^6.0.0
|
package_info_plus: ^6.0.0
|
||||||
palette_generator: ^0.3.3
|
palette_generator: ^0.3.3
|
||||||
@ -82,11 +79,7 @@ dependencies:
|
|||||||
scroll_to_index: ^3.0.1
|
scroll_to_index: ^3.0.1
|
||||||
sidebarx: ^0.17.1
|
sidebarx: ^0.17.1
|
||||||
shared_preferences: ^2.2.3
|
shared_preferences: ^2.2.3
|
||||||
smtc_windows:
|
smtc_windows: ^1.0.0
|
||||||
git:
|
|
||||||
url: https://github.com/KRTirtho/frb_plugins.git
|
|
||||||
path: packages/smtc_windows
|
|
||||||
ref: cargokit
|
|
||||||
stroke_text: ^0.0.2
|
stroke_text: ^0.0.2
|
||||||
system_theme: ^2.1.0
|
system_theme: ^2.1.0
|
||||||
titlebar_buttons: ^1.0.0
|
titlebar_buttons: ^1.0.0
|
||||||
@ -95,7 +88,7 @@ dependencies:
|
|||||||
version: ^3.0.2
|
version: ^3.0.2
|
||||||
visibility_detector: ^0.4.0+2
|
visibility_detector: ^0.4.0+2
|
||||||
window_manager: ^0.3.9
|
window_manager: ^0.3.9
|
||||||
youtube_explode_dart: ^2.2.1
|
youtube_explode_dart: ^2.3.1
|
||||||
simple_icons: ^10.1.3
|
simple_icons: ^10.1.3
|
||||||
jiosaavn: ^0.1.0
|
jiosaavn: ^0.1.0
|
||||||
draggable_scrollbar:
|
draggable_scrollbar:
|
||||||
@ -105,16 +98,12 @@ dependencies:
|
|||||||
very_good_infinite_list: ^0.7.1
|
very_good_infinite_list: ^0.7.1
|
||||||
gap: ^3.0.1
|
gap: ^3.0.1
|
||||||
sliver_tools: ^0.2.12
|
sliver_tools: ^0.2.12
|
||||||
flutter_discord_rpc:
|
flutter_discord_rpc: ^1.0.0
|
||||||
git:
|
|
||||||
url: https://github.com/KRTirtho/frb_plugins.git
|
|
||||||
path: packages/flutter_discord_rpc
|
|
||||||
ref: cargokit
|
|
||||||
html_unescape: ^2.0.0
|
html_unescape: ^2.0.0
|
||||||
wikipedia_api: ^0.1.0
|
wikipedia_api: ^0.1.0
|
||||||
skeletonizer: ^1.1.1
|
skeletonizer: ^1.1.1
|
||||||
app_links: ^4.0.1
|
app_links: ^6.3.2
|
||||||
win32_registry: ^1.1.3
|
win32_registry: ^1.1.5
|
||||||
flutter_sharing_intent: ^1.1.0
|
flutter_sharing_intent: ^1.1.0
|
||||||
flutter_broadcasts: ^0.4.0
|
flutter_broadcasts: ^0.4.0
|
||||||
freezed_annotation: ^2.4.1
|
freezed_annotation: ^2.4.1
|
||||||
@ -122,22 +111,23 @@ dependencies:
|
|||||||
bonsoir: ^5.1.10
|
bonsoir: ^5.1.10
|
||||||
shelf: ^1.4.1
|
shelf: ^1.4.1
|
||||||
shelf_router: ^1.1.4
|
shelf_router: ^1.1.4
|
||||||
shelf_web_socket: ^1.0.4
|
shelf_web_socket: ^2.0.0
|
||||||
web_socket_channel: ^2.4.5
|
web_socket_channel: ^3.0.1
|
||||||
lrc: ^1.0.2
|
lrc: ^1.0.2
|
||||||
timezone: ^0.9.2
|
timezone: ^0.9.2
|
||||||
local_notifier: ^0.1.6
|
local_notifier: ^0.1.6
|
||||||
tray_manager: ^0.2.2
|
tray_manager: ^0.2.2
|
||||||
http: ^1.2.1
|
http: ^1.2.1
|
||||||
riverpod: ^2.5.1
|
riverpod: ^2.5.1
|
||||||
drift: ^2.18.0
|
drift: ^2.21.0
|
||||||
sqlite3_flutter_libs: ^0.5.23
|
sqlite3_flutter_libs: ^0.5.23
|
||||||
sqlite3: ^2.4.3
|
sqlite3: ^2.4.3
|
||||||
encrypt: ^5.0.3
|
encrypt: ^5.0.3
|
||||||
go_router: ^14.2.7
|
go_router: ^14.2.7
|
||||||
|
test: ^1.25.7
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.9
|
build_runner: ^2.4.13
|
||||||
crypto: ^3.0.3
|
crypto: ^3.0.3
|
||||||
envied_generator: ^0.5.4+1
|
envied_generator: ^0.5.4+1
|
||||||
flutter_gen_runner: ^5.4.0
|
flutter_gen_runner: ^5.4.0
|
||||||
@ -157,12 +147,12 @@ dev_dependencies:
|
|||||||
pub_api_client: ^2.7.0
|
pub_api_client: ^2.7.0
|
||||||
xml: ^6.5.0
|
xml: ^6.5.0
|
||||||
io: ^1.0.4
|
io: ^1.0.4
|
||||||
drift_dev: ^2.18.0
|
|
||||||
test: ^1.25.2
|
test: ^1.25.2
|
||||||
mocktail: ^1.0.4
|
mocktail: ^1.0.4
|
||||||
|
drift_dev: ^2.21.0
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
uuid: ^4.4.0
|
web: ^1.1.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
generate: true
|
generate: true
|
||||||
|
23
test/drift/app_db/generated/schema.dart
Normal file
23
test/drift/app_db/generated/schema.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// 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_v2.dart' as v2;
|
||||||
|
import 'schema_v1.dart' as v1;
|
||||||
|
|
||||||
|
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
|
@override
|
||||||
|
GeneratedDatabase databaseForVersion(QueryExecutor db, int version) {
|
||||||
|
switch (version) {
|
||||||
|
case 2:
|
||||||
|
return v2.DatabaseAtV2(db);
|
||||||
|
case 1:
|
||||||
|
return v1.DatabaseAtV1(db);
|
||||||
|
default:
|
||||||
|
throw MissingSchemaException(version, versions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const versions = const [1, 2];
|
||||||
|
}
|
3333
test/drift/app_db/generated/schema_v1.dart
Normal file
3333
test/drift/app_db/generated/schema_v1.dart
Normal file
File diff suppressed because it is too large
Load Diff
3366
test/drift/app_db/generated/schema_v2.dart
Normal file
3366
test/drift/app_db/generated/schema_v2.dart
Normal file
File diff suppressed because it is too large
Load Diff
128
test/drift/app_db/migration_test.dart
Normal file
128
test/drift/app_db/migration_test.dart
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// ignore_for_file: unused_local_variable, unused_import
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:drift_dev/api/migrations.dart';
|
||||||
|
import 'package:spotube/models/database/database.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'generated/schema.dart';
|
||||||
|
|
||||||
|
import 'generated/schema_v1.dart' as v1;
|
||||||
|
import 'generated/schema_v2.dart' as v2;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
||||||
|
late SchemaVerifier verifier;
|
||||||
|
|
||||||
|
setUpAll(() {
|
||||||
|
verifier = SchemaVerifier(GeneratedHelper());
|
||||||
|
});
|
||||||
|
|
||||||
|
group('simple database migrations', () {
|
||||||
|
// These simple tests verify all possible schema updates with a simple (no
|
||||||
|
// data) migration. This is a quick way to ensure that written database
|
||||||
|
// migrations properly alter the schema.
|
||||||
|
final versions = GeneratedHelper.versions;
|
||||||
|
for (final (i, fromVersion) in versions.indexed) {
|
||||||
|
group('from $fromVersion', () {
|
||||||
|
for (final toVersion in versions.skip(i + 1)) {
|
||||||
|
test('to $toVersion', () async {
|
||||||
|
final schema = await verifier.schemaAt(fromVersion);
|
||||||
|
final db = Database(schema.newConnection());
|
||||||
|
await verifier.migrateAndValidate(db, toVersion);
|
||||||
|
await db.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Simple tests ensure the schema is transformed correctly, but some
|
||||||
|
// migrations benefit from a test verifying that data is transformed correctly
|
||||||
|
// too. This is particularly true for migrations that change existing columns
|
||||||
|
// (e.g. altering their type or constraints). Migrations that only add tables
|
||||||
|
// or columns typically don't need these advanced tests.
|
||||||
|
// TODO: Check whether you have migrations that could benefit from these tests
|
||||||
|
// and adapt this example to your database if necessary:
|
||||||
|
test("migration from v1 to v2 does not corrupt data", () async {
|
||||||
|
// Add data to insert into the old database, and the expected rows after the
|
||||||
|
// migration.
|
||||||
|
final oldAuthenticationTableData = <v1.AuthenticationTableData>[];
|
||||||
|
final expectedNewAuthenticationTableData = <v2.AuthenticationTableData>[];
|
||||||
|
|
||||||
|
final oldBlacklistTableData = <v1.BlacklistTableData>[];
|
||||||
|
final expectedNewBlacklistTableData = <v2.BlacklistTableData>[];
|
||||||
|
|
||||||
|
final oldPreferencesTableData = <v1.PreferencesTableData>[];
|
||||||
|
final expectedNewPreferencesTableData = <v2.PreferencesTableData>[];
|
||||||
|
|
||||||
|
final oldScrobblerTableData = <v1.ScrobblerTableData>[];
|
||||||
|
final expectedNewScrobblerTableData = <v2.ScrobblerTableData>[];
|
||||||
|
|
||||||
|
final oldSkipSegmentTableData = <v1.SkipSegmentTableData>[];
|
||||||
|
final expectedNewSkipSegmentTableData = <v2.SkipSegmentTableData>[];
|
||||||
|
|
||||||
|
final oldSourceMatchTableData = <v1.SourceMatchTableData>[];
|
||||||
|
final expectedNewSourceMatchTableData = <v2.SourceMatchTableData>[];
|
||||||
|
|
||||||
|
final oldAudioPlayerStateTableData = <v1.AudioPlayerStateTableData>[];
|
||||||
|
final expectedNewAudioPlayerStateTableData =
|
||||||
|
<v2.AudioPlayerStateTableData>[];
|
||||||
|
|
||||||
|
final oldPlaylistTableData = <v1.PlaylistTableData>[];
|
||||||
|
final expectedNewPlaylistTableData = <v2.PlaylistTableData>[];
|
||||||
|
|
||||||
|
final oldPlaylistMediaTableData = <v1.PlaylistMediaTableData>[];
|
||||||
|
final expectedNewPlaylistMediaTableData = <v2.PlaylistMediaTableData>[];
|
||||||
|
|
||||||
|
final oldHistoryTableData = <v1.HistoryTableData>[];
|
||||||
|
final expectedNewHistoryTableData = <v2.HistoryTableData>[];
|
||||||
|
|
||||||
|
final oldLyricsTableData = <v1.LyricsTableData>[];
|
||||||
|
final expectedNewLyricsTableData = <v2.LyricsTableData>[];
|
||||||
|
|
||||||
|
await verifier.testWithDataIntegrity(
|
||||||
|
oldVersion: 1,
|
||||||
|
newVersion: 2,
|
||||||
|
createOld: v1.DatabaseAtV1.new,
|
||||||
|
createNew: v2.DatabaseAtV2.new,
|
||||||
|
openTestedDatabase: (x) => AppDatabase(),
|
||||||
|
createItems: (batch, oldDb) {
|
||||||
|
batch.insertAll(oldDb.authenticationTable, oldAuthenticationTableData);
|
||||||
|
batch.insertAll(oldDb.blacklistTable, oldBlacklistTableData);
|
||||||
|
batch.insertAll(oldDb.preferencesTable, oldPreferencesTableData);
|
||||||
|
batch.insertAll(oldDb.scrobblerTable, oldScrobblerTableData);
|
||||||
|
batch.insertAll(oldDb.skipSegmentTable, oldSkipSegmentTableData);
|
||||||
|
batch.insertAll(oldDb.sourceMatchTable, oldSourceMatchTableData);
|
||||||
|
batch.insertAll(
|
||||||
|
oldDb.audioPlayerStateTable, oldAudioPlayerStateTableData);
|
||||||
|
batch.insertAll(oldDb.playlistTable, oldPlaylistTableData);
|
||||||
|
batch.insertAll(oldDb.playlistMediaTable, oldPlaylistMediaTableData);
|
||||||
|
batch.insertAll(oldDb.historyTable, oldHistoryTableData);
|
||||||
|
batch.insertAll(oldDb.lyricsTable, oldLyricsTableData);
|
||||||
|
},
|
||||||
|
validateItems: (newDb) async {
|
||||||
|
expect(expectedNewAuthenticationTableData,
|
||||||
|
await newDb.select(newDb.authenticationTable).get());
|
||||||
|
expect(expectedNewBlacklistTableData,
|
||||||
|
await newDb.select(newDb.blacklistTable).get());
|
||||||
|
expect(expectedNewPreferencesTableData,
|
||||||
|
await newDb.select(newDb.preferencesTable).get());
|
||||||
|
expect(expectedNewScrobblerTableData,
|
||||||
|
await newDb.select(newDb.scrobblerTable).get());
|
||||||
|
expect(expectedNewSkipSegmentTableData,
|
||||||
|
await newDb.select(newDb.skipSegmentTable).get());
|
||||||
|
expect(expectedNewSourceMatchTableData,
|
||||||
|
await newDb.select(newDb.sourceMatchTable).get());
|
||||||
|
expect(expectedNewAudioPlayerStateTableData,
|
||||||
|
await newDb.select(newDb.audioPlayerStateTable).get());
|
||||||
|
expect(expectedNewPlaylistTableData,
|
||||||
|
await newDb.select(newDb.playlistTable).get());
|
||||||
|
expect(expectedNewPlaylistMediaTableData,
|
||||||
|
await newDb.select(newDb.playlistMediaTable).get());
|
||||||
|
expect(expectedNewHistoryTableData,
|
||||||
|
await newDb.select(newDb.historyTable).get());
|
||||||
|
expect(expectedNewLyricsTableData,
|
||||||
|
await newDb.select(newDb.lyricsTable).get());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -1 +1,183 @@
|
|||||||
{}
|
{
|
||||||
|
"ar": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"bn": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"ca": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"cs": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"de": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"es": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"eu": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"fa": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"fi": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"fr": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"hi": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"id": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"it": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"ja": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"ka": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"ko": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"ne": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"nl": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"pl": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"pt": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"ru": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"th": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"tr": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"uk": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"vi": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
],
|
||||||
|
|
||||||
|
"zh": [
|
||||||
|
"invidious_instance",
|
||||||
|
"invidious_description",
|
||||||
|
"invidious_warning",
|
||||||
|
"invidious_source_description"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
@ -8,21 +8,21 @@ If you're seeing this, you've probably already done this step. Congrats!
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# create a new project in the current directory
|
# create a new project in the current directory
|
||||||
npm create svelte@latest
|
pnpm create svelte@latest
|
||||||
|
|
||||||
# create a new project in my-app
|
# create a new project in my-app
|
||||||
npm create svelte@latest my-app
|
pnpm create svelte@latest my-app
|
||||||
```
|
```
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
Once you've created a project and installed dependencies with `pnpm install` (or `pnpm install` or `yarn`), start a development server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev
|
pnpm run dev
|
||||||
|
|
||||||
# or start the server and open the app in a new browser tab
|
# or start the server and open the app in a new browser tab
|
||||||
npm run dev -- --open
|
pnpm run dev -- --open
|
||||||
```
|
```
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
@ -30,9 +30,9 @@ npm run dev -- --open
|
|||||||
To create a production version of your app:
|
To create a production version of your app:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run build
|
pnpm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
You can preview the production build with `npm run preview`.
|
You can preview the production build with `pnpm run preview`.
|
||||||
|
|
||||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
||||||
|
6391
website/package-lock.json
generated
6391
website/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,38 +14,38 @@
|
|||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.28.1",
|
"@playwright/test": "^1.41.2",
|
||||||
"@skeletonlabs/skeleton": "2.8.0",
|
"@skeletonlabs/skeleton": "2.8.0",
|
||||||
"@skeletonlabs/tw-plugin": "0.3.1",
|
"@skeletonlabs/tw-plugin": "0.3.1",
|
||||||
"@sveltejs/adapter-cloudflare": "^4.1.0",
|
"@sveltejs/adapter-cloudflare": "^4.1.0",
|
||||||
"@sveltejs/kit": "^2.0.0",
|
"@sveltejs/kit": "^2.5.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@sveltejs/vite-plugin-svelte": "^3.0.2",
|
||||||
"@tailwindcss/typography": "0.5.10",
|
"@tailwindcss/typography": "0.5.10",
|
||||||
"@types/eslint": "8.56.0",
|
"@types/eslint": "8.56.0",
|
||||||
"@types/node": "^20.11.16",
|
"@types/node": "^20.11.16",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
"autoprefixer": "10.4.17",
|
"autoprefixer": "10.4.17",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-svelte": "^2.35.1",
|
"eslint-plugin-svelte": "^2.35.1",
|
||||||
"mdsvex": "^0.11.0",
|
"mdsvex": "^0.11.0",
|
||||||
"postcss": "8.4.35",
|
"postcss": "8.4.35",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-svelte": "^3.1.2",
|
"prettier-plugin-svelte": "^3.1.2",
|
||||||
"svelte": "^4.2.7",
|
"svelte": "^4.2.10",
|
||||||
"svelte-check": "^3.6.0",
|
"svelte-check": "^3.6.3",
|
||||||
"tailwindcss": "3.4.1",
|
"tailwindcss": "3.4.1",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.3.3",
|
||||||
"vite": "^5.0.3",
|
"vite": "^5.1.0",
|
||||||
"vite-plugin-tailwind-purgecss": "0.2.0"
|
"vite-plugin-tailwind-purgecss": "0.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/dom": "1.6.1",
|
"@floating-ui/dom": "1.6.1",
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.5.1",
|
"@fortawesome/free-brands-svg-icons": "^6.5.1",
|
||||||
"@octokit/openapi-types": "^19.1.0",
|
"@octokit/openapi-types": "^22.2.0",
|
||||||
"@octokit/rest": "^20.0.2",
|
"@octokit/rest": "^21.0.2",
|
||||||
"date-fns": "^3.3.1",
|
"date-fns": "^3.3.1",
|
||||||
"highlight.js": "11.9.0",
|
"highlight.js": "11.9.0",
|
||||||
"lucide-svelte": "^0.323.0",
|
"lucide-svelte": "^0.323.0",
|
||||||
|
4153
website/pnpm-lock.yaml
Normal file
4153
website/pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@ -102,7 +102,7 @@
|
|||||||
</h5>
|
</h5>
|
||||||
<div class="flex flex-wrap gap-4">
|
<div class="flex flex-wrap gap-4">
|
||||||
{#each assets as asset}
|
{#each assets as asset}
|
||||||
<a href={release.assets_url}>
|
<a href={asset.browser_download_url}>
|
||||||
<button class="btn variant-glass-primary rounded p-0 flex flex-col gap-2">
|
<button class="btn variant-glass-primary rounded p-0 flex flex-col gap-2">
|
||||||
<span class="bg-primary-500 rounded-t p-3 w-full">
|
<span class="bg-primary-500 rounded-t p-3 w-full">
|
||||||
<Fa class="inline" icon={getIcon(asset.browser_download_url)} />
|
<Fa class="inline" icon={getIcon(asset.browser_download_url)} />
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import type { PageLoad } from './$types';
|
import type { PageLoad } from "./$types";
|
||||||
import { Octokit } from '@octokit/rest';
|
import { Octokit } from "@octokit/rest";
|
||||||
|
|
||||||
const github = new Octokit();
|
const github = new Octokit();
|
||||||
export const load: PageLoad = async () => {
|
export const load: PageLoad = async () => {
|
||||||
const { data: releases } = await github.repos.listReleases({
|
const { data: releases } = await github.repos.listReleases({
|
||||||
owner: 'KRTirtho',
|
owner: "KRTirtho",
|
||||||
repo: 'spotube'
|
repo: "spotube",
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
releases
|
releases,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <bonsoir_windows/bonsoir_windows_plugin_c_api.h>
|
#include <bonsoir_windows/bonsoir_windows_plugin_c_api.h>
|
||||||
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
|
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
#include <local_notifier/local_notifier_plugin.h>
|
#include <local_notifier/local_notifier_plugin.h>
|
||||||
#include <media_kit_libs_windows_audio/media_kit_libs_windows_audio_plugin_c_api.h>
|
#include <media_kit_libs_windows_audio/media_kit_libs_windows_audio_plugin_c_api.h>
|
||||||
@ -30,6 +31,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin"));
|
registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin"));
|
||||||
FileSelectorWindowsRegisterWithRegistrar(
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
|
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
LocalNotifierPluginRegisterWithRegistrar(
|
LocalNotifierPluginRegisterWithRegistrar(
|
||||||
|
@ -7,6 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
bonsoir_windows
|
bonsoir_windows
|
||||||
desktop_webview_window
|
desktop_webview_window
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
|
flutter_inappwebview_windows
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
local_notifier
|
local_notifier
|
||||||
media_kit_libs_windows_audio
|
media_kit_libs_windows_audio
|
||||||
|
Loading…
Reference in New Issue
Block a user