Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Playback Manager Rewrite and Custom Server #399

Merged
merged 6 commits into from
Feb 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.DOTENV_NIGHTLY }}' > .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.DOTENV_NIGHTLY }}' > .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.DOTENV_NIGHTLY }}' > .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.DOTENV_NIGHTLY }}' > .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.DOTENV_RELEASE }}' > .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/[email protected]
with:
cache: true
- run: echo '${{ secrets.DOTENV_RELEASE }}' > .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.DOTENV_RELEASE }}' > .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.DOTENV_RELEASE }}' > .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
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,6 @@ flutter {
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:multidex:1.0.3'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'com.android.support:multidex:2.0.1'
}
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.7.21'
repositories {
google()
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
15 changes: 15 additions & 0 deletions lib/collections/env.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';

abstract class Env {
static final String pocketbaseUrl =
dotenv.get('POCKETBASE_URL', fallback: 'http://localhost:8090');
static final String username = dotenv.get('USERNAME', fallback: 'root');
static final String password = dotenv.get('PASSWORD', fallback: '12345678');

static configure() async {
if (kReleaseMode) {
await dotenv.load(fileName: ".env");
}
}
}
42 changes: 20 additions & 22 deletions lib/collections/intents.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/player/player_controls.dart';
import 'package:spotube/collections/routes.dart';
import 'package:spotube/models/logger.dart';
import 'package:spotube/provider/playback_provider.dart';
import 'package:spotube/provider/playlist_queue_provider.dart';
import 'package:spotube/utils/platform.dart';
import 'package:window_manager/window_manager.dart';

Expand All @@ -23,23 +22,22 @@ class PlayPauseAction extends Action<PlayPauseIntent> {
if (PlayerControls.focusNode.canRequestFocus) {
PlayerControls.focusNode.requestFocus();
}
final playback = intent.ref.read(playbackProvider);
if (playback.track == null) {
final playlist = intent.ref.read(PlaylistQueueNotifier.provider);
final playlistNotifier = intent.ref.read(PlaylistQueueNotifier.notifier);
if (playlist == null) {
return null;
} else if (playback.track != null &&
playback.currentDuration == Duration.zero &&
await playback.player.getCurrentPosition() == Duration.zero) {
if (playback.track!.ytUri.startsWith("http")) {
final track = Track.fromJson(playback.track!.toJson());
playback.track = null;
await playback.play(track);
} else {
final track = playback.track;
playback.track = null;
await playback.play(track!);
}
} else if (!PlaylistQueueNotifier.isPlaying) {
// if (playlist.activeTrack is SpotubeTrack &&
// (playlist.activeTrack as SpotubeTrack).ytUri.startsWith("http")) {
// final track =
// Track.fromJson((playlist.activeTrack as SpotubeTrack).toJson());

// await playlistNotifier.play(track);
// } else {
// }
await playlistNotifier.play();
} else {
await playback.togglePlayPause();
await playlistNotifier.pause();
}
return null;
}
Expand Down Expand Up @@ -102,9 +100,9 @@ class SeekIntent extends Intent {
class SeekAction extends Action<SeekIntent> {
@override
invoke(intent) async {
final playback = intent.ref.read(playbackProvider);
if ((playback.playlist == null && playback.track == null) ||
playback.status == PlaybackStatus.loading) {
final playlist = intent.ref.read(PlaylistQueueNotifier.provider);
final playlistNotifier = intent.ref.read(PlaylistQueueNotifier.notifier);
if (playlist == null || playlist.isLoading) {
DirectionalFocusAction().invoke(
DirectionalFocusIntent(
intent.forward ? TraversalDirection.right : TraversalDirection.left,
Expand All @@ -113,8 +111,8 @@ class SeekAction extends Action<SeekIntent> {
return null;
}
final position =
(await playback.player.getCurrentPosition() ?? Duration.zero).inSeconds;
await playback.seekPosition(
(await audioPlayer.getCurrentPosition() ?? Duration.zero).inSeconds;
await playlistNotifier.seek(
Duration(
seconds: intent.forward ? position + 5 : position - 5,
),
Expand Down
31 changes: 13 additions & 18 deletions lib/components/album/album_card.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/shared/playbutton_card.dart';
import 'package:spotube/hooks/use_breakpoint_value.dart';
import 'package:spotube/models/current_playlist.dart';
import 'package:spotube/provider/playback_provider.dart';
import 'package:spotube/provider/spotify_provider.dart';
import 'package:spotube/provider/playlist_queue_provider.dart';
import 'package:spotube/utils/service_utils.dart';
import 'package:spotube/utils/type_conversion_utils.dart';

Expand All @@ -20,9 +20,10 @@ class AlbumCard extends HookConsumerWidget {

@override
Widget build(BuildContext context, ref) {
Playback playback = ref.watch(playbackProvider);
bool isPlaylistPlaying =
playback.playlist != null && playback.playlist!.id == album.id;
final playlist = ref.watch(PlaylistQueueNotifier.provider);
final playing = useStream(PlaylistQueueNotifier.playing).data ?? false;
final playlistNotifier = ref.watch(PlaylistQueueNotifier.notifier);
bool isPlaylistPlaying = playlistNotifier.isPlayingPlaylist(album.tracks!);
final int marginH =
useBreakpointValue(sm: 10, md: 15, lg: 20, xl: 20, xxl: 20);
return PlaybuttonCard(
Expand All @@ -32,9 +33,8 @@ class AlbumCard extends HookConsumerWidget {
),
viewType: viewType,
margin: EdgeInsets.symmetric(horizontal: marginH.toDouble()),
isPlaying: isPlaylistPlaying && playback.isPlaying,
isLoading: playback.status == PlaybackStatus.loading &&
playback.playlist?.id == album.id,
isPlaying: isPlaylistPlaying && playing,
isLoading: isPlaylistPlaying && playlist?.isLoading == true,
title: album.name!,
description:
"Album • ${TypeConversionUtils.artists_X_String<ArtistSimple>(album.artists ?? [])}",
Expand All @@ -43,23 +43,18 @@ class AlbumCard extends HookConsumerWidget {
},
onPlaybuttonPressed: () async {
SpotifyApi spotify = ref.read(spotifyProvider);
if (isPlaylistPlaying && playback.isPlaying) {
return playback.pause();
} else if (isPlaylistPlaying && !playback.isPlaying) {
return playback.resume();
if (isPlaylistPlaying && playing) {
return playlistNotifier.pause();
} else if (isPlaylistPlaying && !playing) {
return playlistNotifier.resume();
}
List<Track> tracks = (await spotify.albums.getTracks(album.id!).all())
.map((track) =>
TypeConversionUtils.simpleTrack_X_Track(track, album))
.toList();
if (tracks.isEmpty) return;

await playback.playPlaylist(CurrentPlaylist(
tracks: tracks,
id: album.id!,
name: album.name!,
thumbnail: album.images!.first.url!,
));
await playlistNotifier.loadAndPlay(tracks);
},
);
}
Expand Down
69 changes: 34 additions & 35 deletions lib/components/library/user_local_tracks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ import 'package:spotube/components/shared/sort_tracks_dropdown.dart';
import 'package:spotube/components/shared/track_table/track_tile.dart';
import 'package:spotube/hooks/use_async_effect.dart';
import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/models/current_playlist.dart';
import 'package:spotube/models/logger.dart';
import 'package:spotube/provider/playback_provider.dart';
import 'package:spotube/models/local_track.dart';
import 'package:spotube/provider/playlist_queue_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/utils/platform.dart';
import 'package:spotube/utils/primitive_utils.dart';
Expand Down Expand Up @@ -58,7 +57,7 @@ enum SortBy {
dateAdded,
}

final localTracksProvider = FutureProvider<List<Track>>((ref) async {
final localTracksProvider = FutureProvider<List<LocalTrack>>((ref) async {
try {
if (kIsWeb) return [];
final downloadLocation = ref.watch(
Expand Down Expand Up @@ -97,9 +96,8 @@ final localTracksProvider = FutureProvider<List<Track>>((ref) async {

return {"metadata": metadata, "file": f, "art": imageFile.path};
} on FfiException catch (e) {
if (e.message == "NoTag: reader does not contain an id3 tag") {
getLogger(FutureProvider<List<Track>>)
.v("[Fetching metadata]", e.message);
if (e.message != "NoTag: reader does not contain an id3 tag") {
rethrow;
}
return {};
} catch (e, stack) {
Expand All @@ -114,10 +112,13 @@ final localTracksProvider = FutureProvider<List<Track>>((ref) async {

final tracks = filesWithMetadata
.map(
(fileWithMetadata) => TypeConversionUtils.localTrack_X_Track(
fileWithMetadata["file"],
metadata: fileWithMetadata["metadata"],
art: fileWithMetadata["art"],
(fileWithMetadata) => LocalTrack.fromTrack(
track: TypeConversionUtils.localTrack_X_Track(
fileWithMetadata["file"],
metadata: fileWithMetadata["metadata"],
art: fileWithMetadata["art"],
),
path: fileWithMetadata["file"].path,
),
)
.toList();
Expand All @@ -132,37 +133,34 @@ final localTracksProvider = FutureProvider<List<Track>>((ref) async {
class UserLocalTracks extends HookConsumerWidget {
const UserLocalTracks({Key? key}) : super(key: key);

void playLocalTracks(Playback playback, List<Track> tracks,
{Track? currentTrack}) async {
void playLocalTracks(
PlaylistQueueNotifier playback,
List<LocalTrack> tracks, {
LocalTrack? currentTrack,
}) async {
currentTrack ??= tracks.first;
final isPlaylistPlaying = playback.playlist?.id == "local";
final isPlaylistPlaying = playback.isPlayingPlaylist(tracks);
if (!isPlaylistPlaying) {
await playback.playPlaylist(
CurrentPlaylist(
tracks: tracks,
id: "local",
name: "Local Tracks",
thumbnail: TypeConversionUtils.image_X_UrlString(
null,
placeholder: ImagePlaceholder.collection,
),
isLocal: true,
),
tracks.indexWhere((s) => s.id == currentTrack?.id),
await playback.loadAndPlay(
tracks,
active: tracks.indexWhere((s) => s.id == currentTrack?.id),
);
} else if (isPlaylistPlaying &&
currentTrack.id != null &&
currentTrack.id != playback.track?.id) {
await playback.play(currentTrack);
currentTrack.id != playback.state?.activeTrack.id) {
await playback.playTrack(currentTrack);
}
}

@override
Widget build(BuildContext context, ref) {
final sortBy = useState<SortBy>(SortBy.none);
final playback = ref.watch(playbackProvider);
final isPlaylistPlaying = playback.playlist?.id == "local";
final playlist = ref.watch(PlaylistQueueNotifier.provider);
final playlistNotifier = ref.watch(PlaylistQueueNotifier.notifier);
final trackSnapshot = ref.watch(localTracksProvider);
final isPlaylistPlaying = playlistNotifier.isPlayingPlaylist(
trackSnapshot.value ?? [],
);
final isMounted = useIsMounted();
final breakpoint = useBreakpoints();

Expand Down Expand Up @@ -198,9 +196,10 @@ class UserLocalTracks extends HookConsumerWidget {
? () {
if (trackSnapshot.value?.isNotEmpty == true) {
if (!isPlaylistPlaying) {
playLocalTracks(playback, trackSnapshot.value!);
playLocalTracks(
playlistNotifier, trackSnapshot.value!);
} else {
playback.stop();
playlistNotifier.stop();
}
}
}
Expand Down Expand Up @@ -267,17 +266,17 @@ class UserLocalTracks extends HookConsumerWidget {
itemBuilder: (context, index) {
final track = filteredTracks[index];
return TrackTile(
playback,
playlist,
duration:
"${track.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.duration?.inSeconds.remainder(60) ?? 0)}",
track: MapEntry(index, track),
isActive: playback.track?.id == track.id,
isActive: playlist?.activeTrack.id == track.id,
isChecked: false,
showCheck: false,
isLocal: true,
onTrackPlayButtonPressed: (currentTrack) {
return playLocalTracks(
playback,
playlistNotifier,
sortedTracks,
currentTrack: track,
);
Expand Down
Loading