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

#691 customizable stream format #757

Merged
merged 3 commits into from
Sep 28, 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
2 changes: 2 additions & 0 deletions lib/collections/spotube_icons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,6 @@ abstract class SpotubeIcons {
static const edit = FeatherIcons.edit;
static const web = FeatherIcons.globe;
static const amoled = FeatherIcons.sunset;
static const file = FeatherIcons.file;
static const stream = Icons.stream_rounded;
}
1 change: 0 additions & 1 deletion lib/components/player/player_actions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/duration.dart';
import 'package:spotube/models/local_track.dart';
import 'package:spotube/models/logger.dart';
import 'package:spotube/models/spotube_track.dart';
import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
Expand Down
4 changes: 3 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -268,5 +268,7 @@
"normalize_audio": "Normalize audio",
"change_cover": "Change cover",
"add_cover": "Add cover",
"restore_defaults": "Restore defaults"
"restore_defaults": "Restore defaults",
"download_music_codec": "Download music codec",
"streaming_music_codec": "Streaming music codec"
}
31 changes: 24 additions & 7 deletions lib/models/spotube_track.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,23 @@ class TrackNotFoundException implements Exception {
class SpotubeTrack extends Track {
final YoutubeVideoInfo ytTrack;
final String ytUri;
final MusicCodec codec;

final List<YoutubeVideoInfo> siblings;

SpotubeTrack(
this.ytTrack,
this.ytUri,
this.siblings,
this.codec,
) : super();

SpotubeTrack.fromTrack({
required Track track,
required this.ytTrack,
required this.ytUri,
required this.siblings,
required this.codec,
}) : super() {
album = track.album;
artists = track.artists;
Expand Down Expand Up @@ -149,6 +152,7 @@ class SpotubeTrack extends Track {
static Future<SpotubeTrack> fetchFromTrack(
Track track,
YoutubeEndpoints client,
MusicCodec codec,
) async {
final matchedCachedTrack = await MatchedTrack.box.get(track.id!);
var siblings = <YoutubeVideoInfo>[];
Expand All @@ -157,16 +161,17 @@ class SpotubeTrack extends Track {
if (matchedCachedTrack != null &&
matchedCachedTrack.searchMode == client.preferences.searchMode) {
(ytVideo, ytStreamUrl) = await client.video(
matchedCachedTrack.youtubeId,
matchedCachedTrack.searchMode,
);
matchedCachedTrack.youtubeId, matchedCachedTrack.searchMode, codec);
} else {
siblings = await fetchSiblings(track, client);
if (siblings.isEmpty) {
throw TrackNotFoundException(track);
}
(ytVideo, ytStreamUrl) =
await client.video(siblings.first.id, siblings.first.searchMode);
(ytVideo, ytStreamUrl) = await client.video(
siblings.first.id,
siblings.first.searchMode,
codec,
);

await MatchedTrack.box.put(
track.id!,
Expand All @@ -183,6 +188,7 @@ class SpotubeTrack extends Track {
ytTrack: ytVideo,
ytUri: ytStreamUrl,
siblings: siblings,
codec: codec,
);
}

Expand All @@ -193,8 +199,12 @@ class SpotubeTrack extends Track {
// sibling tracks that were manually searched and swapped
final isStepSibling = siblings.none((element) => element.id == video.id);

final (ytVideo, ytStreamUrl) =
await client.video(video.id, siblings.first.searchMode);
final (ytVideo, ytStreamUrl) = await client.video(
video.id,
siblings.first.searchMode,
// siblings are always swapped when streaming
client.preferences.streamMusicCodec,
);

if (!isStepSibling) {
await MatchedTrack.box.put(
Expand All @@ -215,6 +225,7 @@ class SpotubeTrack extends Track {
video,
...siblings.where((element) => element.id != video.id),
],
codec: client.preferences.streamMusicCodec,
);
}

Expand All @@ -226,6 +237,10 @@ class SpotubeTrack extends Track {
siblings: List.castFrom<dynamic, Map<String, dynamic>>(map["siblings"])
.map((sibling) => YoutubeVideoInfo.fromJson(sibling))
.toList(),
codec: MusicCodec.values.firstWhere(
(element) => element.name == map["codec"],
orElse: () => MusicCodec.m4a,
),
);
}

Expand All @@ -242,6 +257,7 @@ class SpotubeTrack extends Track {
ytTrack: ytTrack,
ytUri: ytUri,
siblings: siblings,
codec: codec,
);
}

Expand All @@ -268,6 +284,7 @@ class SpotubeTrack extends Track {
"ytTrack": ytTrack.toJson(),
"ytUri": ytUri,
"siblings": siblings.map((sibling) => sibling.toJson()).toList(),
"codec": codec.name,
};
}
}
38 changes: 38 additions & 0 deletions lib/pages/settings/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,44 @@ class SettingsPage extends HookConsumerWidget {
value: preferences.normalizeAudio,
onChanged: preferences.setNormalizeAudio,
),
AdaptiveSelectTile<MusicCodec>(
secondary: const Icon(SpotubeIcons.stream),
title: Text(context.l10n.streaming_music_codec),
value: preferences.streamMusicCodec,
showValueWhenUnfolded: false,
options: MusicCodec.values
.map((e) => DropdownMenuItem(
value: e,
child: Text(
e.label,
style: theme.textTheme.labelMedium,
),
))
.toList(),
onChanged: (value) {
if (value == null) return;
preferences.setStreamMusicCodec(value);
},
),
AdaptiveSelectTile<MusicCodec>(
secondary: const Icon(SpotubeIcons.file),
title: Text(context.l10n.download_music_codec),
value: preferences.downloadMusicCodec,
showValueWhenUnfolded: false,
options: MusicCodec.values
.map((e) => DropdownMenuItem(
value: e,
child: Text(
e.label,
style: theme.textTheme.labelMedium,
),
))
.toList(),
onChanged: (value) {
if (value == null) return;
preferences.setDownloadMusicCodec(value);
},
),
],
),
SectionCardWithHeading(
Expand Down
14 changes: 10 additions & 4 deletions lib/provider/download_manager_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ class DownloadManagerProvider extends ChangeNotifier {
await oldFile.exists()) {
await oldFile.rename(savePath);
}
if (status != DownloadStatus.completed) return;
if (status != DownloadStatus.completed ||
//? WebA audiotagging is not supported yet
//? Although in future by converting weba to opus & then tagging it
//? is possible using vorbis comments
downloadCodec == MusicCodec.weba) return;

final file = File(request.path);

Expand Down Expand Up @@ -89,6 +93,8 @@ class DownloadManagerProvider extends ChangeNotifier {
YoutubeEndpoints get yt => ref.read(youtubeProvider);
String get downloadDirectory =>
ref.read(userPreferencesProvider.select((s) => s.downloadLocation));
MusicCodec get downloadCodec =>
ref.read(userPreferencesProvider.select((s) => s.downloadMusicCodec));

int get $downloadCount => dl
.getAllDownloads()
Expand Down Expand Up @@ -130,7 +136,7 @@ class DownloadManagerProvider extends ChangeNotifier {

String getTrackFileUrl(Track track) {
final name =
"${track.name} - ${TypeConversionUtils.artists_X_String(track.artists ?? <Artist>[])}.m4a";
"${track.name} - ${TypeConversionUtils.artists_X_String(track.artists ?? <Artist>[])}.${downloadCodec.name}";
return join(downloadDirectory, PrimitiveUtils.toSafeFileName(name));
}

Expand Down Expand Up @@ -166,15 +172,15 @@ class DownloadManagerProvider extends ChangeNotifier {
await oldFile.rename("$savePath.old");
}

if (track is SpotubeTrack) {
if (track is SpotubeTrack && track.codec == downloadCodec) {
final downloadTask = await dl.addDownload(track.ytUri, savePath);
if (downloadTask != null) {
$history.add(track);
}
} else {
$backHistory.add(track);
final spotubeTrack =
await SpotubeTrack.fetchFromTrack(track, yt).then((d) {
await SpotubeTrack.fetchFromTrack(track, yt, downloadCodec).then((d) {
$backHistory.remove(track);
return d;
});
Expand Down
1 change: 1 addition & 0 deletions lib/provider/proxy_playlist/next_fetcher_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mixin NextFetcher on StateNotifier<ProxyPlaylist> {
final future = SpotubeTrack.fetchFromTrack(
track,
youtube,
preferences.streamMusicCodec,
);
if (i == 0) {
return await future;
Expand Down
18 changes: 13 additions & 5 deletions lib/provider/proxy_playlist/proxy_playlist_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,12 @@ class ProxyPlaylistNotifier extends PersistedStateNotifier<ProxyPlaylist>
),
);
} catch (e) {
currentSegments.value = (
source: audioPlayer.currentSource!,
segments: [],
);
if (audioPlayer.currentSource != null) {
currentSegments.value = (
source: audioPlayer.currentSource!,
segments: [],
);
}
} finally {
isFetchingSegments.value = false;
}
Expand Down Expand Up @@ -223,7 +225,11 @@ class ProxyPlaylistNotifier extends PersistedStateNotifier<ProxyPlaylist>

final nthFetchedTrack = switch (track.runtimeType) {
SpotubeTrack => track as SpotubeTrack,
_ => await SpotubeTrack.fetchFromTrack(track, youtube),
_ => await SpotubeTrack.fetchFromTrack(
track,
youtube,
preferences.streamMusicCodec,
),
};

await audioPlayer.replaceSource(
Expand Down Expand Up @@ -309,10 +315,12 @@ class ProxyPlaylistNotifier extends PersistedStateNotifier<ProxyPlaylist>
final addableTrack = await SpotubeTrack.fetchFromTrack(
tracks.elementAtOrNull(initialIndex) ?? tracks.first,
youtube,
preferences.streamMusicCodec,
).catchError((e, stackTrace) {
return SpotubeTrack.fetchFromTrack(
tracks.elementAtOrNull(initialIndex + 1) ?? tracks.first,
youtube,
preferences.streamMusicCodec,
);
});

Expand Down
Loading