fix: alternative searched sources doesn't play #1059

This commit is contained in:
Kingkor Roy Tirtho 2024-01-22 19:02:10 +06:00
parent 59e0e6bb65
commit a8e9b824f3
8 changed files with 117 additions and 21 deletions

BIN
assets/jiosaavn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -34,6 +34,7 @@ class Assets {
AssetGenImage('assets/bengali-patterns-bg.jpg');
static const AssetGenImage branding = AssetGenImage('assets/branding.png');
static const AssetGenImage emptyBox = AssetGenImage('assets/empty_box.png');
static const AssetGenImage jiosaavn = AssetGenImage('assets/jiosaavn.png');
static const AssetGenImage likedTracks =
AssetGenImage('assets/liked-tracks.jpg');
static const AssetGenImage placeholder =
@ -76,6 +77,7 @@ class Assets {
bengaliPatternsBg,
branding,
emptyBox,
jiosaavn,
likedTracks,
placeholder,
spotubeHeroBanner,

View File

@ -109,4 +109,5 @@ abstract class SpotubeIcons {
static const normalize = FeatherIcons.barChart2;
static const wikipedia = SimpleIcons.wikipedia;
static const discord = SimpleIcons.discord;
static const youtube = SimpleIcons.youtube;
}

View File

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart' hide Offset;
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
@ -19,10 +20,28 @@ import 'package:spotube/provider/user_preferences/user_preferences_state.dart';
import 'package:spotube/services/sourced_track/models/source_info.dart';
import 'package:spotube/services/sourced_track/models/video_info.dart';
import 'package:spotube/services/sourced_track/sourced_track.dart';
import 'package:spotube/services/sourced_track/sources/jiosaavn.dart';
import 'package:spotube/services/sourced_track/sources/piped.dart';
import 'package:spotube/services/sourced_track/sources/youtube.dart';
import 'package:spotube/utils/service_utils.dart';
import 'package:spotube/utils/type_conversion_utils.dart';
final sourceInfoToIconMap = {
YoutubeSourceInfo: const Icon(SpotubeIcons.youtube, color: Color(0xFFFF0000)),
JioSaavnSourceInfo: Container(
height: 30,
width: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(90),
image: DecorationImage(
image: Assets.jiosaavn.provider(),
fit: BoxFit.cover,
),
),
),
PipedSourceInfo: const Icon(SpotubeIcons.piped),
};
class SiblingTracksSheet extends HookConsumerWidget {
final bool floating;
const SiblingTracksSheet({
@ -64,17 +83,34 @@ class SiblingTracksSheet extends HookConsumerWidget {
return <SourceInfo>[];
}
final results = await youtubeClient.search.search(searchTerm.trim());
final resultsYt = await youtubeClient.search.search(searchTerm.trim());
final resultsJioSaavn =
await jiosaavnClient.search.songs(searchTerm.trim());
return await Future.wait(
results.map(YoutubeVideoInfo.fromVideo).mapIndexed((i, video) async {
final searchResults = await Future.wait([
...resultsJioSaavn.results.mapIndexed((i, song) async {
final siblingType = JioSaavnSourcedTrack.toSiblingType(song);
return siblingType.info;
}),
...resultsYt
.map(YoutubeVideoInfo.fromVideo)
.mapIndexed((i, video) async {
final siblingType = await YoutubeSourcedTrack.toSiblingType(i, video);
return siblingType.info;
}),
);
]);
final activeSourceInfo =
(playlist.activeTrack! as SourcedTrack).sourceInfo;
return searchResults
..removeWhere((element) => element.id == activeSourceInfo.id)
..insert(
0,
activeSourceInfo,
);
}, [
searchTerm,
searchMode.value,
playlist.activeTrack,
]);
final siblings = useMemoized(
@ -104,6 +140,7 @@ class SiblingTracksSheet extends HookConsumerWidget {
final itemBuilder = useCallback(
(SourceInfo sourceInfo) {
final icon = sourceInfoToIconMap[sourceInfo.runtimeType];
return ListTile(
title: Text(sourceInfo.title),
leading: Padding(
@ -118,7 +155,12 @@ class SiblingTracksSheet extends HookConsumerWidget {
borderRadius: BorderRadius.circular(5),
),
trailing: Text(sourceInfo.duration.toHumanReadableString()),
subtitle: Text(sourceInfo.artist),
subtitle: Row(
children: [
if (icon != null) icon,
Text("${sourceInfo.artist}"),
],
),
enabled: playlist.isFetching != true,
selected: playlist.isFetching != true &&
sourceInfo.id ==
@ -137,7 +179,7 @@ class SiblingTracksSheet extends HookConsumerWidget {
[playlist.isFetching, playlist.activeTrack, siblings],
);
var mediaQuery = MediaQuery.of(context);
final mediaQuery = MediaQuery.of(context);
return SafeArea(
child: ClipRRect(
borderRadius: borderRadius,

View File

@ -15,4 +15,4 @@ enum SourceQualities {
low,
}
typedef SiblingType = ({SourceInfo info, SourceMap? source});
typedef SiblingType<T extends SourceInfo> = ({T info, SourceMap? source});

View File

@ -12,6 +12,19 @@ import 'package:spotube/extensions/string.dart';
final jiosaavnClient = JioSaavnClient();
class JioSaavnSourceInfo extends SourceInfo {
JioSaavnSourceInfo({
required super.id,
required super.title,
required super.artist,
required super.thumbnail,
required super.pageUrl,
required super.duration,
required super.artistUrl,
required super.album,
});
}
class JioSaavnSourcedTrack extends SourcedTrack {
JioSaavnSourcedTrack({
required super.ref,
@ -70,7 +83,7 @@ class JioSaavnSourcedTrack extends SourcedTrack {
static SiblingType toSiblingType(SongResponse result) {
final SiblingType sibling = (
info: SourceInfo(
info: JioSaavnSourceInfo(
artist: [
result.primaryArtists,
if (result.featuredArtists.isNotEmpty) ", ",
@ -155,12 +168,16 @@ class JioSaavnSourcedTrack extends SourcedTrack {
@override
Future<JioSaavnSourcedTrack?> swapWithSibling(SourceInfo sibling) async {
if (sibling.id == sourceInfo.id ||
siblings.none((s) => s.id == sibling.id)) {
if (sibling.id == sourceInfo.id) {
return null;
}
final newSourceInfo = siblings.firstWhere((s) => s.id == sibling.id);
// a sibling source that was fetched from the search results
final isStepSibling = siblings.none((s) => s.id == sibling.id);
final newSourceInfo = isStepSibling
? sibling
: siblings.firstWhere((s) => s.id == sibling.id);
final newSiblings = siblings.where((s) => s.id != sibling.id).toList()
..insert(0, sourceInfo);

View File

@ -22,6 +22,19 @@ final pipedProvider = Provider<PipedClient>(
},
);
class PipedSourceInfo extends SourceInfo {
PipedSourceInfo({
required super.id,
required super.title,
required super.artist,
required super.thumbnail,
required super.pageUrl,
required super.duration,
required super.artistUrl,
required super.album,
});
}
class PipedSourcedTrack extends SourcedTrack {
PipedSourcedTrack({
required super.ref,
@ -71,7 +84,7 @@ class PipedSourcedTrack extends SourcedTrack {
ref: ref,
siblings: [],
source: toSourceMap(manifest),
sourceInfo: SourceInfo(
sourceInfo: PipedSourceInfo(
id: manifest.id,
artist: manifest.uploader,
artistUrl: manifest.uploaderUrl,
@ -122,7 +135,7 @@ class PipedSourcedTrack extends SourcedTrack {
}
final SiblingType sibling = (
info: SourceInfo(
info: PipedSourceInfo(
id: item.id,
artist: item.channelName,
artistUrl: "https://www.youtube.com/${item.channelId}",
@ -233,12 +246,16 @@ class PipedSourcedTrack extends SourcedTrack {
@override
Future<SourcedTrack?> swapWithSibling(SourceInfo sibling) async {
if (sibling.id == sourceInfo.id ||
siblings.none((s) => s.id == sibling.id)) {
if (sibling.id == sourceInfo.id) {
return null;
}
final newSourceInfo = siblings.firstWhere((s) => s.id == sibling.id);
// a sibling source that was fetched from the search results
final isStepSibling = siblings.none((s) => s.id == sibling.id);
final newSourceInfo = isStepSibling
? sibling
: siblings.firstWhere((s) => s.id == sibling.id);
final newSiblings = siblings.where((s) => s.id != sibling.id).toList()
..insert(0, sourceInfo);

View File

@ -17,6 +17,19 @@ final officialMusicRegex = RegExp(
caseSensitive: false,
);
class YoutubeSourceInfo extends SourceInfo {
YoutubeSourceInfo({
required super.id,
required super.title,
required super.artist,
required super.thumbnail,
required super.pageUrl,
required super.duration,
required super.artistUrl,
required super.album,
});
}
class YoutubeSourcedTrack extends SourcedTrack {
YoutubeSourcedTrack({
required super.source,
@ -64,7 +77,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
ref: ref,
siblings: [],
source: toSourceMap(manifest),
sourceInfo: SourceInfo(
sourceInfo: YoutubeSourceInfo(
id: item.id.value,
artist: item.author,
artistUrl: "https://www.youtube.com/channel/${item.channelId}",
@ -117,7 +130,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
}
final SiblingType sibling = (
info: SourceInfo(
info: YoutubeSourceInfo(
id: item.id,
artist: item.channelName,
artistUrl: "https://www.youtube.com/channel/${item.channelId}",
@ -217,12 +230,16 @@ class YoutubeSourcedTrack extends SourcedTrack {
@override
Future<YoutubeSourcedTrack?> swapWithSibling(SourceInfo sibling) async {
if (sibling.id == sourceInfo.id ||
siblings.none((s) => s.id == sibling.id)) {
if (sibling.id == sourceInfo.id) {
return null;
}
final newSourceInfo = siblings.firstWhere((s) => s.id == sibling.id);
// a sibling source that was fetched from the search results
final isStepSibling = siblings.none((s) => s.id == sibling.id);
final newSourceInfo = isStepSibling
? sibling
: siblings.firstWhere((s) => s.id == sibling.id);
final newSiblings = siblings.where((s) => s.id != sibling.id).toList()
..insert(0, sourceInfo);