mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
feat: configure pocketbase, generate dart types, update playback to use server instead of hive cache
This commit currently turns off sponsor block segment for compatibility reasons
This commit is contained in:
parent
84d94b05bc
commit
ad90c11ab0
4
.github/workflows/spotube-nightly.yml
vendored
4
.github/workflows/spotube-nightly.yml
vendored
@ -28,6 +28,7 @@ jobs:
|
|||||||
curl -sS https://webi.sh/yq | sh
|
curl -sS https://webi.sh/yq | sh
|
||||||
yq -i '.version |= sub("\+\d+", "-nightly-")' pubspec.yaml
|
yq -i '.version |= sub("\+\d+", "-nightly-")' pubspec.yaml
|
||||||
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
||||||
|
echo '${{ secrets.DOT_ENV }}' > .env
|
||||||
flutter config --enable-linux-desktop
|
flutter config --enable-linux-desktop
|
||||||
flutter pub get
|
flutter pub get
|
||||||
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
||||||
@ -63,6 +64,7 @@ jobs:
|
|||||||
curl -sS https://webi.sh/yq | sh
|
curl -sS https://webi.sh/yq | sh
|
||||||
yq -i '.version |= sub("\+\d+", "-nightly-")' pubspec.yaml
|
yq -i '.version |= sub("\+\d+", "-nightly-")' pubspec.yaml
|
||||||
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
||||||
|
echo '${{ secrets.DOT_ENV }}' > .env
|
||||||
flutter pub get
|
flutter pub get
|
||||||
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
||||||
echo '${{ secrets.KEYSTORE }}' | base64 --decode > android/app/upload-keystore.jks
|
echo '${{ secrets.KEYSTORE }}' | base64 --decode > android/app/upload-keystore.jks
|
||||||
@ -93,6 +95,7 @@ jobs:
|
|||||||
yq -i '.version |= sub("\+\d+", "-nightly-")' pubspec.yaml
|
yq -i '.version |= sub("\+\d+", "-nightly-")' pubspec.yaml
|
||||||
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
||||||
sed -i "s/%{{SPOTUBE_VERSION}}%/${{ env.GITHUB_RUN_NUMBER }}/" windows/runner/Runner.rc
|
sed -i "s/%{{SPOTUBE_VERSION}}%/${{ env.GITHUB_RUN_NUMBER }}/" windows/runner/Runner.rc
|
||||||
|
echo '${{ secrets.DOT_ENV }}' > .env
|
||||||
flutter config --enable-windows-desktop
|
flutter config --enable-windows-desktop
|
||||||
flutter pub get
|
flutter pub get
|
||||||
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
||||||
@ -120,6 +123,7 @@ jobs:
|
|||||||
- run: brew install yq
|
- run: brew install yq
|
||||||
- run: yq -i '.version |= sub("\+\d+", "-nightly-")' pubspec.yaml
|
- run: yq -i '.version |= sub("\+\d+", "-nightly-")' pubspec.yaml
|
||||||
- run: yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
- run: yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
||||||
|
- run: echo '${{ secrets.DOT_ENV }}' > .env
|
||||||
- run: flutter config --enable-macos-desktop
|
- run: flutter config --enable-macos-desktop
|
||||||
- run: flutter pub get
|
- run: flutter pub get
|
||||||
- run: dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
- run: dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
||||||
|
4
.github/workflows/spotube-release.yml
vendored
4
.github/workflows/spotube-release.yml
vendored
@ -31,6 +31,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
cache: true
|
cache: true
|
||||||
- run: |
|
- run: |
|
||||||
|
echo '${{ secrets.DOT_ENV }}' > .env
|
||||||
flutter config --enable-windows-desktop
|
flutter config --enable-windows-desktop
|
||||||
flutter pub get
|
flutter pub get
|
||||||
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
||||||
@ -72,6 +73,7 @@ jobs:
|
|||||||
- uses: subosito/flutter-action@v2.8.0
|
- uses: subosito/flutter-action@v2.8.0
|
||||||
with:
|
with:
|
||||||
cache: true
|
cache: true
|
||||||
|
- run: echo '${{ secrets.DOT_ENV }}' > .env
|
||||||
- run: flutter config --enable-macos-desktop
|
- run: flutter config --enable-macos-desktop
|
||||||
- run: flutter pub get
|
- run: flutter pub get
|
||||||
- run: dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
- run: dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
||||||
@ -112,6 +114,7 @@ jobs:
|
|||||||
# replacing & adding new release version with older version
|
# replacing & adding new release version with older version
|
||||||
- run: |
|
- run: |
|
||||||
sed -i 's|%{{APPDATA_RELEASE}}%|<release version="${{ steps.tag.outputs.tag }}" date="${{ steps.date.outputs.date }}" />|' linux/com.github.KRTirtho.Spotube.appdata.xml
|
sed -i 's|%{{APPDATA_RELEASE}}%|<release version="${{ steps.tag.outputs.tag }}" date="${{ steps.date.outputs.date }}" />|' linux/com.github.KRTirtho.Spotube.appdata.xml
|
||||||
|
echo '${{ secrets.DOT_ENV }}' > .env
|
||||||
|
|
||||||
- run: |
|
- run: |
|
||||||
flutter config --enable-linux-desktop
|
flutter config --enable-linux-desktop
|
||||||
@ -146,6 +149,7 @@ jobs:
|
|||||||
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools patchelf desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse
|
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools patchelf desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse
|
||||||
|
|
||||||
- run: |
|
- run: |
|
||||||
|
echo '${{ secrets.DOT_ENV }}' > .env
|
||||||
flutter pub get
|
flutter pub get
|
||||||
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
|
||||||
echo '${{ secrets.KEYSTORE }}' | base64 --decode > android/app/upload-keystore.jks
|
echo '${{ secrets.KEYSTORE }}' | base64 --decode > android/app/upload-keystore.jks
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -75,3 +75,5 @@ appimage-build
|
|||||||
|
|
||||||
android/key.properties
|
android/key.properties
|
||||||
.fvm/flutter_sdk
|
.fvm/flutter_sdk
|
||||||
|
|
||||||
|
**/pb_data
|
11
lib/collections/env.dart
Normal file
11
lib/collections/env.dart
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
|
||||||
|
abstract class Env {
|
||||||
|
static final String pocketbaseUrl = dotenv.get('POCKETBASE_URL');
|
||||||
|
static final String username = dotenv.get('USERNAME');
|
||||||
|
static final String password = dotenv.get('PASSWORD');
|
||||||
|
|
||||||
|
static configure() async {
|
||||||
|
await dotenv.load(fileName: ".env");
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:spotube/entities/cache_track.dart';
|
import 'package:spotube/entities/cache_track.dart';
|
||||||
|
import 'package:spotube/models/track.dart';
|
||||||
import 'package:spotube/utils/duration.dart';
|
import 'package:spotube/utils/duration.dart';
|
||||||
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
||||||
|
|
||||||
@ -30,6 +31,11 @@ extension VideoFromCacheTrackExtension on Video {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<Video> fromBackendTrack(
|
||||||
|
BackendTrack track, YoutubeExplode youtube) {
|
||||||
|
return youtube.videos.get(VideoId.fromString(track.youtubeId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ThumbnailSetJson on ThumbnailSet {
|
extension ThumbnailSetJson on ThumbnailSet {
|
||||||
|
@ -12,6 +12,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:platform_ui/platform_ui.dart';
|
import 'package:platform_ui/platform_ui.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:spotube/collections/cache_keys.dart';
|
import 'package:spotube/collections/cache_keys.dart';
|
||||||
|
import 'package:spotube/collections/env.dart';
|
||||||
import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart';
|
import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart';
|
||||||
import 'package:spotube/entities/cache_track.dart';
|
import 'package:spotube/entities/cache_track.dart';
|
||||||
import 'package:spotube/collections/routes.dart';
|
import 'package:spotube/collections/routes.dart';
|
||||||
@ -23,6 +24,7 @@ import 'package:spotube/provider/playback_provider.dart';
|
|||||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||||
import 'package:spotube/provider/youtube_provider.dart';
|
import 'package:spotube/provider/youtube_provider.dart';
|
||||||
import 'package:spotube/services/mobile_audio_service.dart';
|
import 'package:spotube/services/mobile_audio_service.dart';
|
||||||
|
import 'package:spotube/services/pocketbase.dart';
|
||||||
import 'package:spotube/themes/dark_theme.dart';
|
import 'package:spotube/themes/dark_theme.dart';
|
||||||
import 'package:spotube/themes/light_theme.dart';
|
import 'package:spotube/themes/light_theme.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
@ -36,6 +38,8 @@ void main() async {
|
|||||||
Hive.registerAdapter(CacheTrackAdapter());
|
Hive.registerAdapter(CacheTrackAdapter());
|
||||||
Hive.registerAdapter(CacheTrackEngagementAdapter());
|
Hive.registerAdapter(CacheTrackEngagementAdapter());
|
||||||
Hive.registerAdapter(CacheTrackSkipSegmentAdapter());
|
Hive.registerAdapter(CacheTrackSkipSegmentAdapter());
|
||||||
|
await Env.configure();
|
||||||
|
await initializePocketBase();
|
||||||
if (kIsDesktop) {
|
if (kIsDesktop) {
|
||||||
await windowManager.ensureInitialized();
|
await windowManager.ensureInitialized();
|
||||||
WindowOptions windowOptions = const WindowOptions(
|
WindowOptions windowOptions = const WindowOptions(
|
||||||
@ -72,7 +76,17 @@ void main() async {
|
|||||||
enableApplicationParameters: false,
|
enableApplicationParameters: false,
|
||||||
),
|
),
|
||||||
FileHandler(await getLogsPath(), printLogs: false),
|
FileHandler(await getLogsPath(), printLogs: false),
|
||||||
SnackbarHandler(const Duration(seconds: 5)),
|
SnackbarHandler(
|
||||||
|
const Duration(seconds: 5),
|
||||||
|
action: SnackBarAction(
|
||||||
|
label: "Dismiss",
|
||||||
|
onPressed: () {
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
Catcher.navigatorKey!.currentContext!,
|
||||||
|
).hideCurrentSnackBar();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
releaseConfig: CatcherOptions(SilentReportMode(), [
|
releaseConfig: CatcherOptions(SilentReportMode(), [
|
||||||
|
29
lib/models/track.dart
Normal file
29
lib/models/track.dart
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:pocketbase/pocketbase.dart';
|
||||||
|
part 'track.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class BackendTrack extends RecordModel {
|
||||||
|
@JsonKey(name: "spotify_id")
|
||||||
|
final String spotifyId;
|
||||||
|
@JsonKey(name: "youtube_id")
|
||||||
|
final String youtubeId;
|
||||||
|
final int votes;
|
||||||
|
|
||||||
|
BackendTrack({
|
||||||
|
required this.spotifyId,
|
||||||
|
required this.youtubeId,
|
||||||
|
required this.votes,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory BackendTrack.fromRecord(RecordModel record) =>
|
||||||
|
BackendTrack.fromJson(record.toJson());
|
||||||
|
|
||||||
|
factory BackendTrack.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$BackendTrackFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() => _$BackendTrackToJson(this);
|
||||||
|
|
||||||
|
static String collection = "tracks";
|
||||||
|
}
|
30
lib/models/track.g.dart
Normal file
30
lib/models/track.g.dart
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'track.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
BackendTrack _$BackendTrackFromJson(Map<String, dynamic> json) => BackendTrack(
|
||||||
|
spotifyId: json['spotify_id'] as String,
|
||||||
|
youtubeId: json['youtube_id'] as String,
|
||||||
|
votes: json['votes'] as int,
|
||||||
|
)
|
||||||
|
..id = json['id'] as String
|
||||||
|
..created = json['created'] as String
|
||||||
|
..updated = json['updated'] as String
|
||||||
|
..collectionId = json['collectionId'] as String
|
||||||
|
..collectionName = json['collectionName'] as String;
|
||||||
|
|
||||||
|
Map<String, dynamic> _$BackendTrackToJson(BackendTrack instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created': instance.created,
|
||||||
|
'updated': instance.updated,
|
||||||
|
'collectionId': instance.collectionId,
|
||||||
|
'collectionName': instance.collectionName,
|
||||||
|
'spotify_id': instance.spotifyId,
|
||||||
|
'youtube_id': instance.youtubeId,
|
||||||
|
'votes': instance.votes,
|
||||||
|
};
|
@ -6,19 +6,19 @@ import 'package:audioplayers/audioplayers.dart';
|
|||||||
import 'package:catcher/catcher.dart';
|
import 'package:catcher/catcher.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:hive/hive.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/entities/cache_track.dart';
|
|
||||||
import 'package:spotube/extensions/video.dart';
|
import 'package:spotube/extensions/video.dart';
|
||||||
import 'package:spotube/models/current_playlist.dart';
|
import 'package:spotube/models/current_playlist.dart';
|
||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:spotube/models/spotube_track.dart';
|
import 'package:spotube/models/spotube_track.dart';
|
||||||
|
import 'package:spotube/models/track.dart';
|
||||||
import 'package:spotube/provider/audio_player_provider.dart';
|
import 'package:spotube/provider/audio_player_provider.dart';
|
||||||
import 'package:spotube/provider/blacklist_provider.dart';
|
import 'package:spotube/provider/blacklist_provider.dart';
|
||||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||||
import 'package:spotube/provider/youtube_provider.dart';
|
import 'package:spotube/provider/youtube_provider.dart';
|
||||||
import 'package:spotube/services/linux_audio_service.dart';
|
import 'package:spotube/services/linux_audio_service.dart';
|
||||||
import 'package:spotube/services/mobile_audio_service.dart';
|
import 'package:spotube/services/mobile_audio_service.dart';
|
||||||
|
import 'package:spotube/services/pocketbase.dart';
|
||||||
import 'package:spotube/utils/persisted_change_notifier.dart';
|
import 'package:spotube/utils/persisted_change_notifier.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:spotube/utils/primitive_utils.dart';
|
import 'package:spotube/utils/primitive_utils.dart';
|
||||||
@ -63,7 +63,6 @@ class Playback extends PersistedChangeNotifier {
|
|||||||
ref.read(BlackListNotifier.provider.notifier);
|
ref.read(BlackListNotifier.provider.notifier);
|
||||||
|
|
||||||
// playlist & track list properties
|
// playlist & track list properties
|
||||||
late LazyBox<CacheTrack> cache;
|
|
||||||
CurrentPlaylist? playlist;
|
CurrentPlaylist? playlist;
|
||||||
SpotubeTrack? track;
|
SpotubeTrack? track;
|
||||||
List<Video> _siblingYtVideos = [];
|
List<Video> _siblingYtVideos = [];
|
||||||
@ -94,8 +93,6 @@ class Playback extends PersistedChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(() async {
|
(() async {
|
||||||
cache = await Hive.openLazyBox<CacheTrack>("track-cache");
|
|
||||||
|
|
||||||
if (kIsAndroid) {
|
if (kIsAndroid) {
|
||||||
await player.setVolume(1);
|
await player.setVolume(1);
|
||||||
volume = 1;
|
volume = 1;
|
||||||
@ -386,7 +383,15 @@ class Playback extends PersistedChangeNotifier {
|
|||||||
bool noSponsorBlock = false,
|
bool noSponsorBlock = false,
|
||||||
bool overwriteCache = false,
|
bool overwriteCache = false,
|
||||||
}) async {
|
}) async {
|
||||||
final cachedTrack = await cache.get(track.id);
|
final cachedTracks = await pb
|
||||||
|
.collection(BackendTrack.collection)
|
||||||
|
.getFullList(filter: "spotify_id = '${track.id}'", sort: "-votes");
|
||||||
|
final cachedTrack = cachedTracks.isNotEmpty
|
||||||
|
? BackendTrack.fromRecord(cachedTracks.first)
|
||||||
|
: null;
|
||||||
|
final altTrack = cachedTracks.firstWhereOrNull(
|
||||||
|
(record) => record.data["youtube_id"] == ytVideo.id.value,
|
||||||
|
);
|
||||||
StreamManifest trackManifest = await raceMultiple(
|
StreamManifest trackManifest = await raceMultiple(
|
||||||
() => youtube.videos.streams.getManifest(ytVideo.id),
|
() => youtube.videos.streams.getManifest(ytVideo.id),
|
||||||
);
|
);
|
||||||
@ -412,29 +417,42 @@ class Playback extends PersistedChangeNotifier {
|
|||||||
|
|
||||||
final ytUri = chosenStreamInfo.url.toString();
|
final ytUri = chosenStreamInfo.url.toString();
|
||||||
|
|
||||||
final skipSegments = cachedTrack?.skipSegments != null &&
|
// final skipSegments =
|
||||||
cachedTrack!.skipSegments!.isNotEmpty
|
// cachedTrack.skipSegments != null && cachedTrack.skipSegments!.isNotEmpty
|
||||||
? cachedTrack.skipSegments!
|
// ? cachedTrack.skipSegments!
|
||||||
.map(
|
// .map(
|
||||||
(segment) => segment.toJson(),
|
// (segment) => segment.toJson(),
|
||||||
)
|
// )
|
||||||
.toList()
|
// .toList()
|
||||||
: noSponsorBlock
|
// : noSponsorBlock
|
||||||
? List.castFrom<dynamic, Map<String, int>>([])
|
// ? List.castFrom<dynamic, Map<String, int>>([])
|
||||||
: await getSkipSegments(ytVideo.id.value);
|
// : await getSkipSegments(ytVideo.id.value);
|
||||||
|
|
||||||
// only save when the track isn't available in the cache with same
|
// only save when the track isn't available in the cache with same
|
||||||
// matchAlgorithm
|
// matchAlgorithm
|
||||||
if (overwriteCache ||
|
|
||||||
cachedTrack == null ||
|
if (cachedTrack == null && altTrack == null) {
|
||||||
cachedTrack.mode != preferences.trackMatchAlgorithm.name) {
|
await pb.collection(BackendTrack.collection).create(
|
||||||
await cache.put(
|
body: BackendTrack(
|
||||||
track.id!,
|
spotifyId: track.id!,
|
||||||
CacheTrack.fromVideo(
|
youtubeId: ytVideo.id.value,
|
||||||
ytVideo,
|
votes: 0,
|
||||||
preferences.trackMatchAlgorithm.name,
|
).toJson(),
|
||||||
skipSegments: skipSegments,
|
);
|
||||||
),
|
} else if (cachedTrack != null && altTrack != null && overwriteCache) {
|
||||||
|
await pb.collection(BackendTrack.collection).update(
|
||||||
|
altTrack.id,
|
||||||
|
body: {
|
||||||
|
"votes": altTrack.data["votes"] + 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (cachedTrack != null && altTrack == null && overwriteCache) {
|
||||||
|
await pb.collection(BackendTrack.collection).create(
|
||||||
|
body: BackendTrack(
|
||||||
|
spotifyId: track.id!,
|
||||||
|
youtubeId: ytVideo.id.value,
|
||||||
|
votes: 1,
|
||||||
|
).toJson(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,7 +464,7 @@ class Playback extends PersistedChangeNotifier {
|
|||||||
// ('audio/webm', 'video/webm' & 'image/webp') thus using 'audio/mpeg'
|
// ('audio/webm', 'video/webm' & 'image/webp') thus using 'audio/mpeg'
|
||||||
// codec/mimetype for those Platforms
|
// codec/mimetype for those Platforms
|
||||||
ytUri: ytUri,
|
ytUri: ytUri,
|
||||||
skipSegments: skipSegments,
|
skipSegments: /* skipSegments */ [],
|
||||||
),
|
),
|
||||||
chosenStreamInfo,
|
chosenStreamInfo,
|
||||||
);
|
);
|
||||||
@ -481,14 +499,17 @@ class Playback extends PersistedChangeNotifier {
|
|||||||
_logger.v("[Youtube Search Term] $queryString");
|
_logger.v("[Youtube Search Term] $queryString");
|
||||||
|
|
||||||
Video ytVideo;
|
Video ytVideo;
|
||||||
final cachedTrack = await cache.get(track.id);
|
final cachedTrack = await pb
|
||||||
if (cachedTrack != null &&
|
.collection(BackendTrack.collection)
|
||||||
cachedTrack.mode == matchAlgorithm.name &&
|
.getFullList(filter: "spotify_id = '${track.id}'", sort: "-votes")
|
||||||
!ignoreCache) {
|
.then((l) => l.isNotEmpty ? BackendTrack.fromRecord(l.first) : null);
|
||||||
|
|
||||||
|
if (cachedTrack != null && !ignoreCache) {
|
||||||
_logger.v(
|
_logger.v(
|
||||||
"[Playing track from cache] youtubeId: ${cachedTrack.id} mode: ${cachedTrack.mode}",
|
"[Playing track from cache] youtubeId: ${cachedTrack.youtubeId}",
|
||||||
);
|
);
|
||||||
ytVideo = VideoFromCacheTrackExtension.fromCacheTrack(cachedTrack);
|
ytVideo = await VideoFromCacheTrackExtension.fromBackendTrack(
|
||||||
|
cachedTrack, youtube);
|
||||||
} else {
|
} else {
|
||||||
VideoSearchList videos =
|
VideoSearchList videos =
|
||||||
await raceMultiple(() => youtube.search.search(queryString));
|
await raceMultiple(() => youtube.search.search(queryString));
|
||||||
|
14
lib/services/pocketbase.dart
Normal file
14
lib/services/pocketbase.dart
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import 'package:catcher/catcher.dart';
|
||||||
|
import 'package:pocketbase/pocketbase.dart';
|
||||||
|
import 'package:spotube/collections/env.dart';
|
||||||
|
|
||||||
|
final pb = PocketBase(Env.pocketbaseUrl);
|
||||||
|
bool isLoggedIn = false;
|
||||||
|
Future<void> initializePocketBase() async {
|
||||||
|
try {
|
||||||
|
await pb.collection("users").authWithPassword(Env.username, Env.password);
|
||||||
|
isLoggedIn = true;
|
||||||
|
} catch (e, stack) {
|
||||||
|
Catcher.reportCheckedError(e, stack);
|
||||||
|
}
|
||||||
|
}
|
23
pubspec.lock
23
pubspec.lock
@ -575,6 +575,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.2"
|
version: "0.0.2"
|
||||||
|
flutter_dotenv:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_dotenv
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.2"
|
||||||
flutter_feather_icons:
|
flutter_feather_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -822,12 +829,19 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.4"
|
version: "0.6.4"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.0"
|
version: "4.8.0"
|
||||||
|
json_serializable:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: json_serializable
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.6.0"
|
||||||
libadwaita:
|
libadwaita:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1103,6 +1117,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
|
pocketbase:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pocketbase
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.1+1"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -31,6 +31,7 @@ dependencies:
|
|||||||
fluentui_system_icons: ^1.1.189
|
fluentui_system_icons: ^1.1.189
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_dotenv: ^5.0.2
|
||||||
flutter_feather_icons: ^2.0.0+1
|
flutter_feather_icons: ^2.0.0+1
|
||||||
flutter_hooks: ^0.18.2+1
|
flutter_hooks: ^0.18.2+1
|
||||||
flutter_inappwebview: ^5.7.2+3
|
flutter_inappwebview: ^5.7.2+3
|
||||||
@ -44,6 +45,8 @@ dependencies:
|
|||||||
html: ^0.15.1
|
html: ^0.15.1
|
||||||
http: ^0.13.5
|
http: ^0.13.5
|
||||||
introduction_screen: ^3.0.2
|
introduction_screen: ^3.0.2
|
||||||
|
json_annotation: ^4.8.0
|
||||||
|
json_serializable: ^6.6.0
|
||||||
libadwaita: ^1.2.5
|
libadwaita: ^1.2.5
|
||||||
logger: ^1.1.0
|
logger: ^1.1.0
|
||||||
macos_ui: ^1.7.5
|
macos_ui: ^1.7.5
|
||||||
@ -59,6 +62,7 @@ dependencies:
|
|||||||
git:
|
git:
|
||||||
url: https://github.com/KRTirtho/platform_ui.git
|
url: https://github.com/KRTirtho/platform_ui.git
|
||||||
ref: 073cefb9c419fcb01cbdfd6ca2f9714eec23c83b
|
ref: 073cefb9c419fcb01cbdfd6ca2f9714eec23c83b
|
||||||
|
pocketbase: ^0.7.1+1
|
||||||
popover: ^0.2.6+3
|
popover: ^0.2.6+3
|
||||||
queue: ^3.1.0+1
|
queue: ^3.1.0+1
|
||||||
scroll_to_index: ^3.0.1
|
scroll_to_index: ^3.0.1
|
||||||
@ -96,6 +100,7 @@ flutter:
|
|||||||
assets:
|
assets:
|
||||||
- assets/
|
- assets/
|
||||||
- assets/tutorial/
|
- assets/tutorial/
|
||||||
|
- .env
|
||||||
|
|
||||||
flutter_icons:
|
flutter_icons:
|
||||||
android: true
|
android: true
|
||||||
|
1
server/.pocketbase
Normal file
1
server/.pocketbase
Normal file
@ -0,0 +1 @@
|
|||||||
|
version=0.12.1
|
63
server/pb_migrations/1675256468_created_tracks.js
Normal file
63
server/pb_migrations/1675256468_created_tracks.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
migrate((db) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"id": "pevn93oxbnovw0s",
|
||||||
|
"created": "2023-02-01 13:01:08.893Z",
|
||||||
|
"updated": "2023-02-01 13:01:08.893Z",
|
||||||
|
"name": "tracks",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "ycnix0ai",
|
||||||
|
"name": "spotify_id",
|
||||||
|
"type": "text",
|
||||||
|
"required": true,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": 20,
|
||||||
|
"max": 22,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "ih8fxzgh",
|
||||||
|
"name": "youtube_id",
|
||||||
|
"type": "text",
|
||||||
|
"required": true,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": 10,
|
||||||
|
"max": 11,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "vzvqgsjf",
|
||||||
|
"name": "votes",
|
||||||
|
"type": "number",
|
||||||
|
"required": true,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Dao(db).saveCollection(collection);
|
||||||
|
}, (db) => {
|
||||||
|
const dao = new Dao(db);
|
||||||
|
const collection = dao.findCollectionByNameOrId("pevn93oxbnovw0s");
|
||||||
|
|
||||||
|
return dao.deleteCollection(collection);
|
||||||
|
})
|
17
server/pb_migrations/1675256557_updated_tracks.js
Normal file
17
server/pb_migrations/1675256557_updated_tracks.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
migrate((db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("pevn93oxbnovw0s")
|
||||||
|
|
||||||
|
collection.listRule = ""
|
||||||
|
collection.viewRule = ""
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
}, (db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("pevn93oxbnovw0s")
|
||||||
|
|
||||||
|
collection.listRule = null
|
||||||
|
collection.viewRule = null
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
})
|
19
server/pb_migrations/1675256593_updated_users.js
Normal file
19
server/pb_migrations/1675256593_updated_users.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
migrate((db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
collection.createRule = null
|
||||||
|
collection.updateRule = null
|
||||||
|
collection.deleteRule = null
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
}, (db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
collection.createRule = ""
|
||||||
|
collection.updateRule = "id = @request.auth.id"
|
||||||
|
collection.deleteRule = "id = @request.auth.id"
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
})
|
17
server/pb_migrations/1675256678_updated_tracks.js
Normal file
17
server/pb_migrations/1675256678_updated_tracks.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
migrate((db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("pevn93oxbnovw0s")
|
||||||
|
|
||||||
|
collection.createRule = "@request.auth.id != ''"
|
||||||
|
collection.updateRule = "@request.auth.id != ''"
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
}, (db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("pevn93oxbnovw0s")
|
||||||
|
|
||||||
|
collection.createRule = null
|
||||||
|
collection.updateRule = null
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
})
|
17
server/pb_migrations/1675257121_updated_tracks.js
Normal file
17
server/pb_migrations/1675257121_updated_tracks.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
migrate((db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("pevn93oxbnovw0s")
|
||||||
|
|
||||||
|
collection.createRule = "@request.auth.id != '' && ((spotify_id ?= @collection.tracks.spotify_id && youtube_id ?= @collection.tracks.youtube_id) || (spotify_id ?!= @collection.tracks.spotify_id && youtube_id ?!= @collection.tracks.youtube_id))"
|
||||||
|
collection.updateRule = "@request.auth.id != '' && ((spotify_id ?= @collection.tracks.spotify_id && youtube_id ?= @collection.tracks.youtube_id) || (spotify_id ?!= @collection.tracks.spotify_id && youtube_id ?!= @collection.tracks.youtube_id))"
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
}, (db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("pevn93oxbnovw0s")
|
||||||
|
|
||||||
|
collection.createRule = null
|
||||||
|
collection.updateRule = null
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
})
|
39
server/pb_migrations/1675257148_updated_tracks.js
Normal file
39
server/pb_migrations/1675257148_updated_tracks.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
migrate((db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("pevn93oxbnovw0s")
|
||||||
|
|
||||||
|
// update
|
||||||
|
collection.schema.addField(new SchemaField({
|
||||||
|
"system": false,
|
||||||
|
"id": "vzvqgsjf",
|
||||||
|
"name": "votes",
|
||||||
|
"type": "number",
|
||||||
|
"required": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
}, (db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("pevn93oxbnovw0s")
|
||||||
|
|
||||||
|
// update
|
||||||
|
collection.schema.addField(new SchemaField({
|
||||||
|
"system": false,
|
||||||
|
"id": "vzvqgsjf",
|
||||||
|
"name": "votes",
|
||||||
|
"type": "number",
|
||||||
|
"required": true,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user