mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
fix: alternative searched sources doesn't play #1059
This commit is contained in:
parent
59e0e6bb65
commit
a8e9b824f3
BIN
assets/jiosaavn.png
Normal file
BIN
assets/jiosaavn.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -34,6 +34,7 @@ class Assets {
|
|||||||
AssetGenImage('assets/bengali-patterns-bg.jpg');
|
AssetGenImage('assets/bengali-patterns-bg.jpg');
|
||||||
static const AssetGenImage branding = AssetGenImage('assets/branding.png');
|
static const AssetGenImage branding = AssetGenImage('assets/branding.png');
|
||||||
static const AssetGenImage emptyBox = AssetGenImage('assets/empty_box.png');
|
static const AssetGenImage emptyBox = AssetGenImage('assets/empty_box.png');
|
||||||
|
static const AssetGenImage jiosaavn = AssetGenImage('assets/jiosaavn.png');
|
||||||
static const AssetGenImage likedTracks =
|
static const AssetGenImage likedTracks =
|
||||||
AssetGenImage('assets/liked-tracks.jpg');
|
AssetGenImage('assets/liked-tracks.jpg');
|
||||||
static const AssetGenImage placeholder =
|
static const AssetGenImage placeholder =
|
||||||
@ -76,6 +77,7 @@ class Assets {
|
|||||||
bengaliPatternsBg,
|
bengaliPatternsBg,
|
||||||
branding,
|
branding,
|
||||||
emptyBox,
|
emptyBox,
|
||||||
|
jiosaavn,
|
||||||
likedTracks,
|
likedTracks,
|
||||||
placeholder,
|
placeholder,
|
||||||
spotubeHeroBanner,
|
spotubeHeroBanner,
|
||||||
|
@ -109,4 +109,5 @@ abstract class SpotubeIcons {
|
|||||||
static const normalize = FeatherIcons.barChart2;
|
static const normalize = FeatherIcons.barChart2;
|
||||||
static const wikipedia = SimpleIcons.wikipedia;
|
static const wikipedia = SimpleIcons.wikipedia;
|
||||||
static const discord = SimpleIcons.discord;
|
static const discord = SimpleIcons.discord;
|
||||||
|
static const youtube = SimpleIcons.youtube;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotify/spotify.dart' hide Offset;
|
import 'package:spotify/spotify.dart' hide Offset;
|
||||||
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
|
|
||||||
import 'package:spotube/components/shared/image/universal_image.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/source_info.dart';
|
||||||
import 'package:spotube/services/sourced_track/models/video_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/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/services/sourced_track/sources/youtube.dart';
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
import 'package:spotube/utils/type_conversion_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 {
|
class SiblingTracksSheet extends HookConsumerWidget {
|
||||||
final bool floating;
|
final bool floating;
|
||||||
const SiblingTracksSheet({
|
const SiblingTracksSheet({
|
||||||
@ -64,17 +83,34 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
return <SourceInfo>[];
|
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(
|
final searchResults = await Future.wait([
|
||||||
results.map(YoutubeVideoInfo.fromVideo).mapIndexed((i, video) async {
|
...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);
|
final siblingType = await YoutubeSourcedTrack.toSiblingType(i, video);
|
||||||
return siblingType.info;
|
return siblingType.info;
|
||||||
}),
|
}),
|
||||||
|
]);
|
||||||
|
final activeSourceInfo =
|
||||||
|
(playlist.activeTrack! as SourcedTrack).sourceInfo;
|
||||||
|
return searchResults
|
||||||
|
..removeWhere((element) => element.id == activeSourceInfo.id)
|
||||||
|
..insert(
|
||||||
|
0,
|
||||||
|
activeSourceInfo,
|
||||||
);
|
);
|
||||||
}, [
|
}, [
|
||||||
searchTerm,
|
searchTerm,
|
||||||
searchMode.value,
|
searchMode.value,
|
||||||
|
playlist.activeTrack,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
final siblings = useMemoized(
|
final siblings = useMemoized(
|
||||||
@ -104,6 +140,7 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
|
|
||||||
final itemBuilder = useCallback(
|
final itemBuilder = useCallback(
|
||||||
(SourceInfo sourceInfo) {
|
(SourceInfo sourceInfo) {
|
||||||
|
final icon = sourceInfoToIconMap[sourceInfo.runtimeType];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(sourceInfo.title),
|
title: Text(sourceInfo.title),
|
||||||
leading: Padding(
|
leading: Padding(
|
||||||
@ -118,7 +155,12 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(5),
|
||||||
),
|
),
|
||||||
trailing: Text(sourceInfo.duration.toHumanReadableString()),
|
trailing: Text(sourceInfo.duration.toHumanReadableString()),
|
||||||
subtitle: Text(sourceInfo.artist),
|
subtitle: Row(
|
||||||
|
children: [
|
||||||
|
if (icon != null) icon,
|
||||||
|
Text(" • ${sourceInfo.artist}"),
|
||||||
|
],
|
||||||
|
),
|
||||||
enabled: playlist.isFetching != true,
|
enabled: playlist.isFetching != true,
|
||||||
selected: playlist.isFetching != true &&
|
selected: playlist.isFetching != true &&
|
||||||
sourceInfo.id ==
|
sourceInfo.id ==
|
||||||
@ -137,7 +179,7 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
[playlist.isFetching, playlist.activeTrack, siblings],
|
[playlist.isFetching, playlist.activeTrack, siblings],
|
||||||
);
|
);
|
||||||
|
|
||||||
var mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
|
@ -15,4 +15,4 @@ enum SourceQualities {
|
|||||||
low,
|
low,
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef SiblingType = ({SourceInfo info, SourceMap? source});
|
typedef SiblingType<T extends SourceInfo> = ({T info, SourceMap? source});
|
||||||
|
@ -12,6 +12,19 @@ import 'package:spotube/extensions/string.dart';
|
|||||||
|
|
||||||
final jiosaavnClient = JioSaavnClient();
|
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 {
|
class JioSaavnSourcedTrack extends SourcedTrack {
|
||||||
JioSaavnSourcedTrack({
|
JioSaavnSourcedTrack({
|
||||||
required super.ref,
|
required super.ref,
|
||||||
@ -70,7 +83,7 @@ class JioSaavnSourcedTrack extends SourcedTrack {
|
|||||||
|
|
||||||
static SiblingType toSiblingType(SongResponse result) {
|
static SiblingType toSiblingType(SongResponse result) {
|
||||||
final SiblingType sibling = (
|
final SiblingType sibling = (
|
||||||
info: SourceInfo(
|
info: JioSaavnSourceInfo(
|
||||||
artist: [
|
artist: [
|
||||||
result.primaryArtists,
|
result.primaryArtists,
|
||||||
if (result.featuredArtists.isNotEmpty) ", ",
|
if (result.featuredArtists.isNotEmpty) ", ",
|
||||||
@ -155,12 +168,16 @@ class JioSaavnSourcedTrack extends SourcedTrack {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<JioSaavnSourcedTrack?> swapWithSibling(SourceInfo sibling) async {
|
Future<JioSaavnSourcedTrack?> swapWithSibling(SourceInfo sibling) async {
|
||||||
if (sibling.id == sourceInfo.id ||
|
if (sibling.id == sourceInfo.id) {
|
||||||
siblings.none((s) => s.id == sibling.id)) {
|
|
||||||
return null;
|
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()
|
final newSiblings = siblings.where((s) => s.id != sibling.id).toList()
|
||||||
..insert(0, sourceInfo);
|
..insert(0, sourceInfo);
|
||||||
|
|
||||||
|
@ -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 {
|
class PipedSourcedTrack extends SourcedTrack {
|
||||||
PipedSourcedTrack({
|
PipedSourcedTrack({
|
||||||
required super.ref,
|
required super.ref,
|
||||||
@ -71,7 +84,7 @@ class PipedSourcedTrack extends SourcedTrack {
|
|||||||
ref: ref,
|
ref: ref,
|
||||||
siblings: [],
|
siblings: [],
|
||||||
source: toSourceMap(manifest),
|
source: toSourceMap(manifest),
|
||||||
sourceInfo: SourceInfo(
|
sourceInfo: PipedSourceInfo(
|
||||||
id: manifest.id,
|
id: manifest.id,
|
||||||
artist: manifest.uploader,
|
artist: manifest.uploader,
|
||||||
artistUrl: manifest.uploaderUrl,
|
artistUrl: manifest.uploaderUrl,
|
||||||
@ -122,7 +135,7 @@ class PipedSourcedTrack extends SourcedTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final SiblingType sibling = (
|
final SiblingType sibling = (
|
||||||
info: SourceInfo(
|
info: PipedSourceInfo(
|
||||||
id: item.id,
|
id: item.id,
|
||||||
artist: item.channelName,
|
artist: item.channelName,
|
||||||
artistUrl: "https://www.youtube.com/${item.channelId}",
|
artistUrl: "https://www.youtube.com/${item.channelId}",
|
||||||
@ -233,12 +246,16 @@ class PipedSourcedTrack extends SourcedTrack {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SourcedTrack?> swapWithSibling(SourceInfo sibling) async {
|
Future<SourcedTrack?> swapWithSibling(SourceInfo sibling) async {
|
||||||
if (sibling.id == sourceInfo.id ||
|
if (sibling.id == sourceInfo.id) {
|
||||||
siblings.none((s) => s.id == sibling.id)) {
|
|
||||||
return null;
|
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()
|
final newSiblings = siblings.where((s) => s.id != sibling.id).toList()
|
||||||
..insert(0, sourceInfo);
|
..insert(0, sourceInfo);
|
||||||
|
|
||||||
|
@ -17,6 +17,19 @@ final officialMusicRegex = RegExp(
|
|||||||
caseSensitive: false,
|
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 {
|
class YoutubeSourcedTrack extends SourcedTrack {
|
||||||
YoutubeSourcedTrack({
|
YoutubeSourcedTrack({
|
||||||
required super.source,
|
required super.source,
|
||||||
@ -64,7 +77,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
ref: ref,
|
ref: ref,
|
||||||
siblings: [],
|
siblings: [],
|
||||||
source: toSourceMap(manifest),
|
source: toSourceMap(manifest),
|
||||||
sourceInfo: SourceInfo(
|
sourceInfo: YoutubeSourceInfo(
|
||||||
id: item.id.value,
|
id: item.id.value,
|
||||||
artist: item.author,
|
artist: item.author,
|
||||||
artistUrl: "https://www.youtube.com/channel/${item.channelId}",
|
artistUrl: "https://www.youtube.com/channel/${item.channelId}",
|
||||||
@ -117,7 +130,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final SiblingType sibling = (
|
final SiblingType sibling = (
|
||||||
info: SourceInfo(
|
info: YoutubeSourceInfo(
|
||||||
id: item.id,
|
id: item.id,
|
||||||
artist: item.channelName,
|
artist: item.channelName,
|
||||||
artistUrl: "https://www.youtube.com/channel/${item.channelId}",
|
artistUrl: "https://www.youtube.com/channel/${item.channelId}",
|
||||||
@ -217,12 +230,16 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<YoutubeSourcedTrack?> swapWithSibling(SourceInfo sibling) async {
|
Future<YoutubeSourcedTrack?> swapWithSibling(SourceInfo sibling) async {
|
||||||
if (sibling.id == sourceInfo.id ||
|
if (sibling.id == sourceInfo.id) {
|
||||||
siblings.none((s) => s.id == sibling.id)) {
|
|
||||||
return null;
|
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()
|
final newSiblings = siblings.where((s) => s.id != sibling.id).toList()
|
||||||
..insert(0, sourceInfo);
|
..insert(0, sourceInfo);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user