Skip to content

Commit

Permalink
feat: bring pre download on desktop, disable pre download for long vi…
Browse files Browse the repository at this point in the history
…deos

fix: audio service calling self ref of playlist queue provider
  • Loading branch information
KRTirtho committed Feb 3, 2023
1 parent 3ccb525 commit 1d82bb0
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 72 deletions.
12 changes: 8 additions & 4 deletions lib/components/player/sibling_tracks_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,19 @@ class SiblingTracksSheet extends HookConsumerWidget {
subtitle: PlatformText(video.author),
enabled: playlist?.isLoading != true,
selected: playlist?.isLoading != true &&
video.id ==
(playlist?.activeTrack as SpotubeTrack).ytTrack.id,
video.id.value ==
(playlist?.activeTrack as SpotubeTrack)
.ytTrack
.id
.value,
selectedTileColor: Theme.of(context).popupMenuTheme.color,
onTap: () async {
if (playlist?.isLoading == false &&
video.id !=
video.id.value !=
(playlist?.activeTrack as SpotubeTrack)
.ytTrack
.id) {
.id
.value) {
await playlistNotifier.swapSibling(video);
}
},
Expand Down
10 changes: 3 additions & 7 deletions lib/components/root/bottom_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,11 @@ class BottomPlayer extends HookConsumerWidget {

useEffect(() {
if (volume.value != volumeState) {
volumeNotifier.setVolume(volume.value);
volume.value = volumeState;
}
return null;
}, [volumeState]);

return Listener(
onPointerSignal: (event) async {
if (event is PointerScrollEvent) {
Expand All @@ -147,12 +148,7 @@ class BottomPlayer extends HookConsumerWidget {
onChanged: (v) {
volume.value = v;
},
onChangeEnd: (value) async {
// You don't really need to know why but this
// way it works only
await volumeNotifier.setVolume(value);
await volumeNotifier.setVolume(value);
},
onChangeEnd: volumeNotifier.setVolume,
),
);
}),
Expand Down
4 changes: 4 additions & 0 deletions lib/extensions/video.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ extension GetSkipSegments on Video {
},
));

if (res.body == "Not Found") {
return List.castFrom<dynamic, Map<String, int>>([]);
}

final data = jsonDecode(res.body);
final segments = data.map((obj) {
return Map.castFrom<String, dynamic, String, int>({
Expand Down
6 changes: 4 additions & 2 deletions lib/models/spotube_track.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ class SpotubeTrack extends Track {
);
}

if (preferences.androidBytesPlay) {
if (preferences.predownload &&
ytVideo.duration! < const Duration(minutes: 15)) {
await DefaultCacheManager().getFileFromCache(track.id!).then(
(file) async {
if (file != null) return file.file;
Expand Down Expand Up @@ -232,7 +233,8 @@ class SpotubeTrack extends Track {
);
}

if (preferences.androidBytesPlay) {
if (preferences.predownload &&
video.duration! < const Duration(minutes: 15)) {
await DefaultCacheManager().getFileFromCache(id!).then(
(file) async {
if (file != null) return file.file;
Expand Down
30 changes: 14 additions & 16 deletions lib/pages/settings/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import 'package:spotube/collections/spotify_markets.dart';
import 'package:spotube/models/spotube_track.dart';
import 'package:spotube/provider/auth_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/utils/platform.dart';
import 'package:url_launcher/url_launcher_string.dart';

class SettingsPage extends HookConsumerWidget {
Expand Down Expand Up @@ -308,22 +307,21 @@ class SettingsPage extends HookConsumerWidget {
},
),
),
if (kIsMobile)
PlatformListTile(
leading: const Icon(SpotubeIcons.download),
title: const PlatformText(
"Pre download and play",
),
subtitle: const PlatformText(
"Instead of streaming audio, download bytes and play instead (Recommended for higher bandwidth users)",
),
trailing: PlatformSwitch(
value: preferences.androidBytesPlay,
onChanged: (state) {
preferences.setAndroidBytesPlay(state);
},
),
PlatformListTile(
leading: const Icon(SpotubeIcons.download),
title: const PlatformText(
"Pre download and play",
),
subtitle: const PlatformText(
"Instead of streaming audio, download bytes and play instead (Recommended for higher bandwidth users)",
),
trailing: PlatformSwitch(
value: preferences.predownload,
onChanged: (state) {
preferences.setPredownload(state);
},
),
),
PlatformListTile(
leading: const Icon(SpotubeIcons.fastForward),
title: const PlatformText(
Expand Down
60 changes: 39 additions & 21 deletions lib/provider/playlist_queue_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:spotube/utils/persisted_state_notifier.dart';
import 'package:spotube/utils/platform.dart';
import 'package:spotube/utils/type_conversion_utils.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart' hide Playlist;
import 'package:collection/collection.dart';

final audioPlayer = AudioPlayer();
final youtube = YoutubeExplode();
Expand All @@ -24,20 +25,32 @@ class PlaylistQueue {

Track get activeTrack => tracks.elementAt(active);

factory PlaylistQueue.fromJson(Map<String, dynamic> json) {
static Future<PlaylistQueue> fromJson(
Map<String, dynamic> json, UserPreferences preferences) async {
final List? tracks = json['tracks'];
return PlaylistQueue(
Set.from(json['tracks'].map(
(e) {
final json = Map.castFrom<dynamic, dynamic, String, dynamic>(e);
if (e["ytTrack"] != null) {
return SpotubeTrack.fromJson(json);
} else if (e["path"] != null) {
return LocalTrack.fromJson(json);
} else {
return Track.fromJson(json);
}
},
)),
Set.from(
await Future.wait(
tracks?.mapIndexed(
(i, e) async {
final jsonTrack =
Map.castFrom<dynamic, dynamic, String, dynamic>(e);

if (e["path"] != null) {
return LocalTrack.fromJson(jsonTrack);
} else if (i == json["active"] && !json.containsKey("path")) {
return await SpotubeTrack.fromFetchTrack(
Track.fromJson(jsonTrack),
preferences,
);
} else {
return Track.fromJson(jsonTrack);
}
},
) ??
[],
),
),
active: json['active'],
);
}
Expand Down Expand Up @@ -97,7 +110,7 @@ class PlaylistQueueNotifier extends PersistedStateNotifier<PlaylistQueue?> {
void configure() async {
if (kIsMobile || kIsMacOS) {
mobileService = await AudioService.init(
builder: () => MobileAudioService(ref),
builder: () => MobileAudioService(this),
config: const AudioServiceConfig(
androidNotificationChannelId: 'com.krtirtho.Spotube',
androidNotificationChannelName: 'Spotube',
Expand All @@ -106,7 +119,7 @@ class PlaylistQueueNotifier extends PersistedStateNotifier<PlaylistQueue?> {
);
}
if (kIsLinux) {
linuxService = LinuxAudioService(ref);
linuxService = LinuxAudioService(ref, this);
}
addListener((state) {
linuxService?.player.updateProperties();
Expand Down Expand Up @@ -160,10 +173,13 @@ class PlaylistQueueNotifier extends PersistedStateNotifier<PlaylistQueue?> {
static bool get isPaused => audioPlayer.state == PlayerState.paused;
static bool get isStopped => audioPlayer.state == PlayerState.stopped;

static Stream<Duration> get duration => audioPlayer.onDurationChanged;
static Stream<Duration> get position => audioPlayer.onPositionChanged;
static Stream<Duration> get duration =>
audioPlayer.onDurationChanged.asBroadcastStream();
static Stream<Duration> get position =>
audioPlayer.onPositionChanged.asBroadcastStream();
static Stream<bool> get playing => audioPlayer.onPlayerStateChanged
.map((event) => event == PlayerState.playing);
.map((event) => event == PlayerState.playing)
.asBroadcastStream();

List<Video> get siblings => state?.isLoading == false
? (state!.activeTrack as SpotubeTrack).siblings
Expand Down Expand Up @@ -192,13 +208,15 @@ class PlaylistQueueNotifier extends PersistedStateNotifier<PlaylistQueue?> {
..removeAt(state!.active)
..shuffle()
},
active: 0,
);
}

void unshuffle() {
if (!isShuffled || !isLoaded) return;
state = state?.copyWith(
tracks: _tempTracks,
active: _tempTracks.toList().indexOf(state!.activeTrack),
);
_tempTracks = {};
}
Expand Down Expand Up @@ -265,7 +283,7 @@ class PlaylistQueueNotifier extends PersistedStateNotifier<PlaylistQueue?> {

final cached =
await DefaultCacheManager().getFileFromCache(state!.activeTrack.id!);
if (preferences.androidBytesPlay && cached != null) {
if (preferences.predownload && cached != null) {
await audioPlayer.play(
DeviceFileSource(cached.file.path),
mode: PlayerMode.mediaPlayer,
Expand Down Expand Up @@ -368,9 +386,9 @@ class PlaylistQueueNotifier extends PersistedStateNotifier<PlaylistQueue?> {
}

@override
PlaylistQueue? fromJson(Map<String, dynamic> json) {
Future<PlaylistQueue>? fromJson(Map<String, dynamic> json) {
if (json.isEmpty) return null;
return PlaylistQueue.fromJson(json);
return PlaylistQueue.fromJson(json, preferences);
}

@override
Expand Down
14 changes: 8 additions & 6 deletions lib/provider/user_preferences_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ class UserPreferences extends PersistedChangeNotifier {
LayoutMode layoutMode;
bool rotatingAlbumArt;

bool androidBytesPlay;
bool predownload;

UserPreferences({
required this.geniusAccessToken,
required this.recommendationMarket,
required this.themeMode,
required this.ytSearchFormat,
required this.layoutMode,
this.androidBytesPlay = true,
required this.predownload,
this.saveTrackLyrics = false,
this.accentColorScheme = Colors.green,
this.backgroundColorScheme = Colors.grey,
Expand All @@ -70,9 +70,10 @@ class UserPreferences extends PersistedChangeNotifier {
}
}

void setAndroidBytesPlay(bool value) {
androidBytesPlay = value;
void setPredownload(bool value) {
predownload = value;
notifyListeners();
updatePersistence();
}

void setThemeMode(ThemeMode mode) {
Expand Down Expand Up @@ -203,7 +204,7 @@ class UserPreferences extends PersistedChangeNotifier {
orElse: () => kIsDesktop ? LayoutMode.extended : LayoutMode.compact,
);
rotatingAlbumArt = map["rotatingAlbumArt"] ?? rotatingAlbumArt;
androidBytesPlay = map["androidBytesPlay"] ?? androidBytesPlay;
predownload = map["predownload"] ?? predownload;
}

@override
Expand All @@ -223,7 +224,7 @@ class UserPreferences extends PersistedChangeNotifier {
"downloadLocation": downloadLocation,
"layoutMode": layoutMode.name,
"rotatingAlbumArt": rotatingAlbumArt,
"androidBytesPlay": androidBytesPlay,
"predownload": predownload,
};
}
}
Expand All @@ -235,5 +236,6 @@ final userPreferencesProvider = ChangeNotifierProvider(
themeMode: ThemeMode.system,
ytSearchFormat: "\$MAIN_ARTIST - \$TITLE \$FEATURED_ARTISTS",
layoutMode: kIsMobile ? LayoutMode.compact : LayoutMode.adaptive,
predownload: kIsMobile,
),
);
13 changes: 6 additions & 7 deletions lib/services/linux_audio_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,11 @@ class _MprisMediaPlayer2 extends DBusObject {
}

class _MprisMediaPlayer2Player extends DBusObject {
Ref ref;
final Ref ref;
final PlaylistQueueNotifier playlistNotifier;

/// Creates a new object to expose on [path].
_MprisMediaPlayer2Player(this.ref)
_MprisMediaPlayer2Player(this.ref, this.playlistNotifier)
: super(DBusObjectPath("/org/mpris/MediaPlayer2")) {
(() async {
final nameStatus =
Expand All @@ -233,9 +234,7 @@ class _MprisMediaPlayer2Player extends DBusObject {
}());
}

PlaylistQueue? get playlist => ref.read(PlaylistQueueNotifier.provider);
PlaylistQueueNotifier get playlistNotifier =>
ref.read(PlaylistQueueNotifier.notifier);
PlaylistQueue? get playlist => playlistNotifier.state;
double get volume => ref.read(VolumeProvider.provider);
VolumeProvider get volumeNotifier =>
ref.read(VolumeProvider.provider.notifier);
Expand Down Expand Up @@ -727,9 +726,9 @@ class LinuxAudioService {
_MprisMediaPlayer2 mp2;
_MprisMediaPlayer2Player player;

LinuxAudioService(Ref ref)
LinuxAudioService(Ref ref, PlaylistQueueNotifier playlistNotifier)
: mp2 = _MprisMediaPlayer2(),
player = _MprisMediaPlayer2Player(ref);
player = _MprisMediaPlayer2Player(ref, playlistNotifier);

void dispose() {
mp2.dispose();
Expand Down
10 changes: 4 additions & 6 deletions lib/services/mobile_audio_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@ import 'dart:async';
import 'package:audio_service/audio_service.dart';
import 'package:audio_session/audio_session.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotube/provider/playlist_queue_provider.dart';

class MobileAudioService extends BaseAudioHandler {
final Ref ref;
AudioSession? session;
final PlaylistQueueNotifier playlistNotifier;

PlaylistQueue? get playlist => ref.watch(PlaylistQueueNotifier.provider);
PlaylistQueueNotifier get playlistNotifier =>
ref.watch(PlaylistQueueNotifier.notifier);

PlaylistQueue? get playlist => playlistNotifier.state;

MobileAudioService(this.ref) {
MobileAudioService(this.playlistNotifier) {
AudioSession.instance.then((s) {
session = s;
s.interruptionEventStream.listen((event) async {
Expand Down
Loading

0 comments on commit 1d82bb0

Please sign in to comment.