Skip to content

Commit

Permalink
feat: configure pocketbase, generate dart types, update playback to u…
Browse files Browse the repository at this point in the history
…se server instead of hive cache

This commit currently turns off sponsor block segment for compatibility reasons
  • Loading branch information
KRTirtho committed Feb 1, 2023
1 parent 84d94b0 commit ad90c11
Show file tree
Hide file tree
Showing 19 changed files with 369 additions and 35 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/spotube-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
curl -sS https://webi.sh/yq | sh
yq -i '.version |= sub("\+\d+", "-nightly-")' pubspec.yaml
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
echo '${{ secrets.DOT_ENV }}' > .env
flutter config --enable-linux-desktop
flutter pub get
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
Expand Down Expand Up @@ -63,6 +64,7 @@ jobs:
curl -sS https://webi.sh/yq | sh
yq -i '.version |= sub("\+\d+", "-nightly-")' pubspec.yaml
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
echo '${{ secrets.DOT_ENV }}' > .env
flutter pub get
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
echo '${{ secrets.KEYSTORE }}' | base64 --decode > android/app/upload-keystore.jks
Expand Down Expand Up @@ -93,6 +95,7 @@ jobs:
yq -i '.version |= sub("\+\d+", "-nightly-")' pubspec.yaml
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
sed -i "s/%{{SPOTUBE_VERSION}}%/${{ env.GITHUB_RUN_NUMBER }}/" windows/runner/Runner.rc
echo '${{ secrets.DOT_ENV }}' > .env
flutter config --enable-windows-desktop
flutter pub get
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
Expand Down Expand Up @@ -120,6 +123,7 @@ jobs:
- run: brew install yq
- run: yq -i '.version |= sub("\+\d+", "-nightly-")' 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 pub get
- run: dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/spotube-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
with:
cache: true
- run: |
echo '${{ secrets.DOT_ENV }}' > .env
flutter config --enable-windows-desktop
flutter pub get
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
Expand Down Expand Up @@ -72,6 +73,7 @@ jobs:
- uses: subosito/flutter-action@v2.8.0
with:
cache: true
- run: echo '${{ secrets.DOT_ENV }}' > .env
- run: flutter config --enable-macos-desktop
- run: flutter pub get
- run: dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
Expand Down Expand Up @@ -112,6 +114,7 @@ jobs:
# replacing & adding new release version with older version
- run: |
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: |
flutter config --enable-linux-desktop
Expand Down Expand Up @@ -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
- run: |
echo '${{ secrets.DOT_ENV }}' > .env
flutter pub get
dart bin/create-secrets.dart '${{ secrets.LYRICS_SECRET }}' '${{ secrets.SPOTIFY_SECRET }}'
echo '${{ secrets.KEYSTORE }}' | base64 --decode > android/app/upload-keystore.jks
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,5 @@ appimage-build

android/key.properties
.fvm/flutter_sdk

**/pb_data
11 changes: 11 additions & 0 deletions lib/collections/env.dart
Original file line number Diff line number Diff line change
@@ -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");
}
}
6 changes: 6 additions & 0 deletions lib/extensions/video.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:spotube/entities/cache_track.dart';
import 'package:spotube/models/track.dart';
import 'package:spotube/utils/duration.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';

Expand Down Expand Up @@ -30,6 +31,11 @@ extension VideoFromCacheTrackExtension on Video {
false,
);
}

static Future<Video> fromBackendTrack(
BackendTrack track, YoutubeExplode youtube) {
return youtube.videos.get(VideoId.fromString(track.youtubeId));
}
}

extension ThumbnailSetJson on ThumbnailSet {
Expand Down
16 changes: 15 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:shared_preferences/shared_preferences.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/entities/cache_track.dart';
import 'package:spotube/collections/routes.dart';
Expand All @@ -23,6 +24,7 @@ import 'package:spotube/provider/playback_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/provider/youtube_provider.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/light_theme.dart';
import 'package:spotube/utils/platform.dart';
Expand All @@ -36,6 +38,8 @@ void main() async {
Hive.registerAdapter(CacheTrackAdapter());
Hive.registerAdapter(CacheTrackEngagementAdapter());
Hive.registerAdapter(CacheTrackSkipSegmentAdapter());
await Env.configure();
await initializePocketBase();
if (kIsDesktop) {
await windowManager.ensureInitialized();
WindowOptions windowOptions = const WindowOptions(
Expand Down Expand Up @@ -72,7 +76,17 @@ void main() async {
enableApplicationParameters: 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(), [
Expand Down
29 changes: 29 additions & 0 deletions lib/models/track.dart
Original file line number Diff line number Diff line change
@@ -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 changes: 30 additions & 0 deletions lib/models/track.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 54 additions & 33 deletions lib/provider/playback_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ import 'package:audioplayers/audioplayers.dart';
import 'package:catcher/catcher.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive/hive.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/entities/cache_track.dart';
import 'package:spotube/extensions/video.dart';
import 'package:spotube/models/current_playlist.dart';
import 'package:spotube/models/logger.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/blacklist_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/provider/youtube_provider.dart';
import 'package:spotube/services/linux_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/platform.dart';
import 'package:spotube/utils/primitive_utils.dart';
Expand Down Expand Up @@ -63,7 +63,6 @@ class Playback extends PersistedChangeNotifier {
ref.read(BlackListNotifier.provider.notifier);

// playlist & track list properties
late LazyBox<CacheTrack> cache;
CurrentPlaylist? playlist;
SpotubeTrack? track;
List<Video> _siblingYtVideos = [];
Expand Down Expand Up @@ -94,8 +93,6 @@ class Playback extends PersistedChangeNotifier {
}

(() async {
cache = await Hive.openLazyBox<CacheTrack>("track-cache");

if (kIsAndroid) {
await player.setVolume(1);
volume = 1;
Expand Down Expand Up @@ -386,7 +383,15 @@ class Playback extends PersistedChangeNotifier {
bool noSponsorBlock = false,
bool overwriteCache = false,
}) 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(
() => youtube.videos.streams.getManifest(ytVideo.id),
);
Expand All @@ -412,30 +417,43 @@ class Playback extends PersistedChangeNotifier {

final ytUri = chosenStreamInfo.url.toString();

final skipSegments = cachedTrack?.skipSegments != null &&
cachedTrack!.skipSegments!.isNotEmpty
? cachedTrack.skipSegments!
.map(
(segment) => segment.toJson(),
)
.toList()
: noSponsorBlock
? List.castFrom<dynamic, Map<String, int>>([])
: await getSkipSegments(ytVideo.id.value);
// final skipSegments =
// cachedTrack.skipSegments != null && cachedTrack.skipSegments!.isNotEmpty
// ? cachedTrack.skipSegments!
// .map(
// (segment) => segment.toJson(),
// )
// .toList()
// : noSponsorBlock
// ? List.castFrom<dynamic, Map<String, int>>([])
// : await getSkipSegments(ytVideo.id.value);

// only save when the track isn't available in the cache with same
// matchAlgorithm
if (overwriteCache ||
cachedTrack == null ||
cachedTrack.mode != preferences.trackMatchAlgorithm.name) {
await cache.put(
track.id!,
CacheTrack.fromVideo(
ytVideo,
preferences.trackMatchAlgorithm.name,
skipSegments: skipSegments,
),

if (cachedTrack == null && altTrack == null) {
await pb.collection(BackendTrack.collection).create(
body: BackendTrack(
spotifyId: track.id!,
youtubeId: ytVideo.id.value,
votes: 0,
).toJson(),
);
} 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(),
);
}

return Tuple2(
Expand All @@ -446,7 +464,7 @@ class Playback extends PersistedChangeNotifier {
// ('audio/webm', 'video/webm' & 'image/webp') thus using 'audio/mpeg'
// codec/mimetype for those Platforms
ytUri: ytUri,
skipSegments: skipSegments,
skipSegments: /* skipSegments */ [],
),
chosenStreamInfo,
);
Expand Down Expand Up @@ -481,14 +499,17 @@ class Playback extends PersistedChangeNotifier {
_logger.v("[Youtube Search Term] $queryString");

Video ytVideo;
final cachedTrack = await cache.get(track.id);
if (cachedTrack != null &&
cachedTrack.mode == matchAlgorithm.name &&
!ignoreCache) {
final cachedTrack = await pb
.collection(BackendTrack.collection)
.getFullList(filter: "spotify_id = '${track.id}'", sort: "-votes")
.then((l) => l.isNotEmpty ? BackendTrack.fromRecord(l.first) : null);

if (cachedTrack != null && !ignoreCache) {
_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 {
VideoSearchList videos =
await raceMultiple(() => youtube.search.search(queryString));
Expand Down
14 changes: 14 additions & 0 deletions lib/services/pocketbase.dart
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit ad90c11

Please sign in to comment.