From 584549f1ef9e38460068b7f0a22cfaad043ee191 Mon Sep 17 00:00:00 2001 From: Deep Gaurav Date: Sun, 16 Jun 2019 05:50:52 +0530 Subject: [PATCH] fixes + search enhancement + equalizer --- android/app/build.gradle | 4 +- .../src/main/java/deep/ryd/MainActivity.java | 67 ++- .../main/java/deep/ryd/URLProxyFactory.java | 2 + lib/library.dart | 4 +- lib/main.dart | 111 +++- lib/playerService.dart | 4 + lib/playlist.dart | 2 +- lib/playlistSearchScreen.dart | 22 + lib/searchScreen.dart | 528 +++++++++++++----- lib/ytplaylist.dart | 23 +- 10 files changed, 586 insertions(+), 181 deletions(-) create mode 100644 lib/playlistSearchScreen.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index 28a3472..e7927d3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -44,8 +44,8 @@ android { applicationId "deep.ryd.rydplayer" minSdkVersion 21 targetSdkVersion 28 - versionCode 53 - versionName "2.0.3" + versionCode 54 + versionName "2.0.4" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } signingConfigs { diff --git a/android/app/src/main/java/deep/ryd/MainActivity.java b/android/app/src/main/java/deep/ryd/MainActivity.java index 9a0aaf6..48e0501 100644 --- a/android/app/src/main/java/deep/ryd/MainActivity.java +++ b/android/app/src/main/java/deep/ryd/MainActivity.java @@ -13,6 +13,7 @@ import android.media.AudioManager; import android.media.MediaMetadata; import android.media.MediaPlayer; +import android.media.audiofx.AudioEffect; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.net.Uri; @@ -32,6 +33,7 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.services.youtube.YoutubeService; +import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.StreamExtractor; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.utils.Localization; @@ -41,7 +43,9 @@ import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Timer; import java.util.TimerTask; @@ -54,6 +58,9 @@ import okhttp3.RequestBody; import okhttp3.Response; +import static android.media.audiofx.AudioEffect.CONTENT_TYPE_MUSIC; +import static android.media.audiofx.AudioEffect.EXTRA_CONTENT_TYPE; + enum PlayerState{ Playing,Paused,Loading,Idle @@ -210,6 +217,8 @@ public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) { Log.d("musicpiped", "androidcached " + cached); } else if(methodCall.method.equals("readyOpenURL")){ result.success(openURL); + } else if(methodCall.method.equals("openSystemEqualizer")){ + openAudioFx(); } else { @@ -280,7 +289,7 @@ public void onCompletion(MediaPlayer mp) { @Override public boolean onError(MediaPlayer mp, int what, int extra) { //methodChannel.invokeMethod("error",""); - Log.i("musicpiped", "MediPlayer Error : " + what + " " + extra); + //Log.i("musicpiped", "MediPlayer Error : " + what + " " + extra); if (extra == MediaPlayer.MEDIA_ERROR_IO || extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) { Log.d("musicpiped", "Timeout"); next(); @@ -296,6 +305,34 @@ public boolean onError(MediaPlayer mp, int what, int extra) { handleURL(appLinkIntent); } + public void openAudioFx() { + try{ + + Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL); + i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName()); + i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, UMP.getAudioSessionId()); + + startActivityForResult(i,0); + } + catch (Exception e){ + e.printStackTrace(); + } + } + + public void applyAudioFx(){ + try{ + + Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL); + i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName()); + i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, UMP.getAudioSessionId()); + + startActivityForResult(i,0); + } + catch (Exception e){ + e.printStackTrace(); + } + } + void handleURL(Intent appLinkIntent){ String appLinkAction = appLinkIntent.getAction(); @@ -696,7 +733,7 @@ class URLVerifier extends AsyncTask{ this.videoId=videoId; } - String getNewpipeURL() throws ExtractionException, IOException { + List getNewpipeURL() throws ExtractionException, IOException { Log.d("musicpiped","Get newpipe URL"); @@ -707,14 +744,11 @@ String getNewpipeURL() throws ExtractionException, IOException { YoutubeService youtubeService = (YoutubeService) NewPipe.getService(NewPipe.getIdOfService("YouTube")); StreamExtractor streamExtractor = youtubeService.getStreamExtractor(url); streamExtractor.fetchPage(); - if(streamExtractor.getAudioStreams().size()>1) - return streamExtractor.getAudioStreams().get(0).url; - else if(streamExtractor.getVideoStreams().size()>1){ - return streamExtractor.getVideoStreams().get(0).url; - } - else { - return "ERROR"; + List urls = new ArrayList<>(); + for(AudioStream s: streamExtractor.getAudioStreams()){ + urls.add(s.url); } + return urls; } boolean verifyURL (String url){ @@ -748,15 +782,24 @@ protected String doInBackground(String... strings) { String url = strings[0]; Log.d("musicpiped","CheckResponseCode"); - while (verifyURL(url)){ + for(int tries=0;tries<3 && verifyURL(url); tries++){ try { - url = getNewpipeURL(); + List newURLS = getNewpipeURL(); + for(String newurl : newURLS){ + if(!verifyURL(newurl)){ + url = newurl; + break; + } + } } catch (ExtractionException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } + if(verifyURL(url)){ + url = "ERROR"; + } return url; } @@ -764,7 +807,7 @@ protected String doInBackground(String... strings) { protected void onPostExecute(String s) { super.onPostExecute(s); if(s=="ERROR"){ - Toast.makeText(activity,"Can not play that track, Retry late",Toast.LENGTH_LONG); + Toast.makeText(activity,"Can not play that track, Retry later",Toast.LENGTH_LONG).show(); activity.next(); }else{ try { diff --git a/android/app/src/main/java/deep/ryd/URLProxyFactory.java b/android/app/src/main/java/deep/ryd/URLProxyFactory.java index 36b78fa..c9ce6b6 100644 --- a/android/app/src/main/java/deep/ryd/URLProxyFactory.java +++ b/android/app/src/main/java/deep/ryd/URLProxyFactory.java @@ -23,6 +23,8 @@ public static HttpProxyCacheServer getProxy(Context context) { private static HttpProxyCacheServer newProxy(Context context) { return new HttpProxyCacheServer.Builder(context) .fileNameGenerator(new TrackFileNameGenerator()) + .maxCacheFilesCount(50) + .maxCacheSize(1024 * 1024 * 500) .build(); } } diff --git a/lib/library.dart b/lib/library.dart index 2a1ce9b..08f1a34 100644 --- a/lib/library.dart +++ b/lib/library.dart @@ -59,9 +59,9 @@ class Library extends StatelessWidget { child: Card( child: ListTile( title: Text(p['title']), - trailing: p.containsKey('playlistId') ? Icon(Icons.sync) : null, + trailing: p.containsKey('playlistId') || p.containsKey('mixId') ? Icon(Icons.sync) : null, onTap: () { - if (p.containsKey('playlistId')) { + if (p.containsKey('playlistId') || p.containsKey('mixId')) { Navigator.push( context, MaterialPageRoute( diff --git a/lib/main.dart b/lib/main.dart index 4b99067..472ea26 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -38,7 +38,7 @@ var quality = ValueNotifier("best"); var ignorePositionUpdate = ValueNotifier(false); -AudioPlayer player = AudioPlayer(); +AudioPlayer player; void main() async { var appDir = await getApplicationDocumentsDirectory(); @@ -194,6 +194,8 @@ class MyHomePageState extends State setState(() {}); }); + player = AudioPlayer(); + player.addEventListener('timeupdate', (e) { if (ignorePositionUpdate.value) { return; @@ -231,7 +233,7 @@ class MyHomePageState extends State positionNotifier.addListener(() { if (totalLength.value > 0 && positionNotifier.value > 0 && - totalLength.value-positionNotifier.value<=2) { + totalLength.value - positionNotifier.value <= 2) { next(); } }); @@ -247,24 +249,20 @@ class MyHomePageState extends State onEnd(); } }); - - player.readyOpenURL.addListener(()async{ - try{ - String id = player.readyOpenURL.value; + player.readyOpenURL.addListener(() async { + try { + String id = player.readyOpenURL.value; String url = InvidiosAPI + "videos/" + id; var response = await http.get(url); - var curr = json.decode(utf8.decode(response.bodyBytes)); - queue.value = [curr]; - currentIndex.value = 0; - playCurrent(); - }catch(e){ + var curr = json.decode(utf8.decode(response.bodyBytes)); + queue.value = [curr]; + currentIndex.value = 0; + playCurrent(); + } catch (e) { print(e); } - - } - ); - + }); } Future checkCache(Map s) async { @@ -523,15 +521,37 @@ class MyHomePageState extends State return IconButton( icon: Icon(Icons.search), onPressed: () async { - String searchQ = await showSearch( + Map searchR = await showSearch( context: context, delegate: _searchDelegate); + String searchQ = searchR["query"]; + String type = searchR["type"]; if (searchQ.isNotEmpty) { - var result = await Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => (SearchScreen(searchQ)))); + var result = + await Navigator.of(context).push(MaterialPageRoute( + builder: (context) => (SearchScreen( + searchQ, + type, + (q) { + queue.value = q; + currentIndex.value = 0; + playCurrent(); + }, + (track) { + if (queue.value != null && + queue.value.isNotEmpty) { + queue.value + .insert(currentIndex.value + 1, track); + } else { + queue.value = [track]; + currentIndex.value = 0; + playCurrent(); + } + }, + )))); if (result == null) { return; } + print(result); queue.value = result["queue"]; currentIndex.value = 0; playerState.value = PlayerState.Loading; @@ -544,8 +564,8 @@ class MyHomePageState extends State icon: Icon(Icons.more_vert), onPressed: () { showModalBottomSheet( - context: context, - builder: (context) => Container( + context: context, + builder: (context) => Container( color: Theme.of(context).backgroundColor, child: Column( mainAxisSize: MainAxisSize.min, @@ -636,9 +656,18 @@ class MyHomePageState extends State content: Text("Reload to take effect"), )); }, - ) + ), + ListTile( + title: Text("Open System Equalizer"), + onTap: () { + print("Open Equalizer"); + player.openFX(); + }, + ), ], - ))); + ), + ), + ); }, ) ], @@ -1067,11 +1096,14 @@ class MyHomePageState extends State } } -class YoutubeSuggestion extends SearchDelegate { +class YoutubeSuggestion extends SearchDelegate { static String corsanywhere = 'https://cors-anywhere.herokuapp.com/'; String suggestionURL = 'http://suggestqueries.google.com/complete/search?client=firefox&ds=yt&q='; + String searchType = "all"; + var types = ["all", "video", "playlist"]; + @override List buildActions(BuildContext context) { return [ @@ -1082,10 +1114,26 @@ class YoutubeSuggestion extends SearchDelegate { showSuggestions(context); }, ), + PopupMenuButton( + icon: Icon(Icons.filter_list), + itemBuilder: (context) { + var p = List>(); + for (var i in types) { + p.add(PopupMenuItem( + value: i, + child: Text(i), + )); + } + return p; + }, + onSelected: (type) { + searchType = type; + }, + ), IconButton( icon: Icon(Icons.search), onPressed: () { - close(context, query); + close(context, {"query": query, "type": searchType}); }, ), ]; @@ -1096,7 +1144,7 @@ class YoutubeSuggestion extends SearchDelegate { return IconButton( icon: Icon(Icons.arrow_back), onPressed: () { - close(context, ""); + close(context, {"query": "", "type": searchType}); }, ); } @@ -1110,11 +1158,16 @@ class YoutubeSuggestion extends SearchDelegate { Widget buildSuggestions(BuildContext context) { if (query.isNotEmpty) { String queryURL = suggestionURL + query; - Future results = http.get(queryURL); + Future results; + try { + results = http.get(queryURL); + } on Exception catch (e) { + results = Future.error(Error()); + } return FutureBuilder( future: results, builder: (context, ass) { - if (ass.connectionState == ConnectionState.done) { + if (ass.connectionState == ConnectionState.done && !ass.hasError) { http.Response response = ass.data; List l = json.decode(response.body); List suggestions = List(); @@ -1126,7 +1179,7 @@ class YoutubeSuggestion extends SearchDelegate { query: query, onSelected: (str) { query = str; - close(context, str); + close(context, {"query": str, "type": searchType}); }, onAdd: (str) { query = str; diff --git a/lib/playerService.dart b/lib/playerService.dart index 50a1b77..243b827 100644 --- a/lib/playerService.dart +++ b/lib/playerService.dart @@ -77,4 +77,8 @@ class AudioPlayer{ void updateMetadata(){ platform.invokeMethod("updateMetadata",metadata); } + + void openFX(){ + platform.invokeMethod('openSystemEqualizer'); + } } \ No newline at end of file diff --git a/lib/playlist.dart b/lib/playlist.dart index 990b7f7..8a4ce29 100644 --- a/lib/playlist.dart +++ b/lib/playlist.dart @@ -94,7 +94,7 @@ class PlaylistState extends State { db.transaction('playlists', 'readonly').objectStore('playlists'); var plstream = playlistobstore.openCursor(autoAdvance: true); plstream.listen((pl) { - if (!(pl.value as Map).containsKey('playlistId')) { + if (!(pl.value as Map).containsKey('playlistId') && !(pl.value as Map).containsKey('mixId')) { playlists.add(pl.value); } }).onDone(() {}); diff --git a/lib/playlistSearchScreen.dart b/lib/playlistSearchScreen.dart new file mode 100644 index 0000000..fdf628e --- /dev/null +++ b/lib/playlistSearchScreen.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class PlaylistSearch extends StatefulWidget{ + + String query; + + PlaylistSearch(this.query); + + @override + _PlaylistSearchState createState() => _PlaylistSearchState(); +} + +class _PlaylistSearchState extends State { + + + Future getResult; + + @override + Widget build(BuildContext context) { + return null; + } +} \ No newline at end of file diff --git a/lib/searchScreen.dart b/lib/searchScreen.dart index aa8008e..2cf44b0 100644 --- a/lib/searchScreen.dart +++ b/lib/searchScreen.dart @@ -1,177 +1,445 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:http/http.dart' as http; import 'package:cached_network_image/cached_network_image.dart'; import 'trackDetail.dart'; import 'dart:convert'; + +import 'artistPlaylist.dart'; import 'main.dart' as main; class SearchScreen extends StatefulWidget { final String _initialSearch; - SearchScreen(this._initialSearch); + final String type; + void Function(List) play; + void Function(Map) playnext; + SearchScreen(this._initialSearch, this.type, this.play, this.playnext); @override State createState() { - return SearchScreenState(_initialSearch); + return SearchScreenState(_initialSearch, type, play, playnext); } } class SearchScreenState extends State { String _searchquery; + String _type; + + void Function(List) _play; + void Function(Map) _playnext; + List results; bool isSearching = true; String title; List selected = new List(); - SearchScreenState(searcgQuery) { + Future resultFuture; + + SearchScreenState(searcgQuery, type, play, playnext) { _searchquery = searcgQuery; + _type = type; + _play = play; + _playnext = playnext; title = searcgQuery; - searchVid(_searchquery); + resultFuture = searchVid(_searchquery); + } + + void exit(out){ + Navigator.pop(context,out); } Widget body() { - if (isSearching) { - return Center(child: CircularProgressIndicator()); - } else { - return ListView.builder( - itemCount: results.length, - itemBuilder: (BuildContext context, int index) { - Color cardcolor; - if (selected.contains(index)) { - cardcolor = Theme.of(context).highlightColor; + return FutureBuilder( + future: resultFuture, + builder: (context, ass) { + if (ass.connectionState == ConnectionState.done) { + if (ass.hasError) { + return Column( + children: [ + Text("Fetch Error cant load"), + RaisedButton( + child: Text("RETRY"), + onPressed: () { + setState(() { + resultFuture = searchVid(_searchquery); + }); + }, + ), + Text(ass.error.toString(), + style: Theme.of(context) + .textTheme + .body1 + .copyWith(color: Theme.of(context).errorColor)) + ], + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + ); } else { - cardcolor = Theme.of(context).canvasColor; - } - return ListTile( - title: Card( - color: cardcolor, - elevation: 6, - margin: EdgeInsets.all(6), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - child: InkWell( - onTap: () async { - if (selected.isEmpty) { - List returnValue = new List(); - returnValue.add(results[index]); + return ListView.builder( + itemCount: results.length, + itemBuilder: (BuildContext contextList, int index) { + Color cardcolor; + if (selected.contains(index)) { + cardcolor = Theme.of(context).highlightColor; + } else { + cardcolor = Theme.of(context).canvasColor; + } + String type = results[index]["type"]; + print(type); + if (type == "video" || type == "playlist") { + return ListTile( + title: Card( + color: cardcolor, + elevation: 6, + margin: EdgeInsets.all(6), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(10))), + child: InkWell( + onTap: () async { + if (type == "video") { + if (selected.isEmpty) { + List returnValue = new List(); + returnValue.add(results[index]); - //List updatedreturnvalue = await resultSearchtoVideoHash(returnValue); - //Navigator.pop(context,{'queue':updatedreturnvalue,'addtoexisting':false}); - var toreturn; - var c = showBottomSheet( - context: context, - builder: (context) { - return TrackDetail(results[index], (info) { - toreturn = info; - }); - }); - await c.closed; - if (toreturn != null) { - Navigator.pop( - context, {'queue': toreturn, 'addtoexisting': false}); - } - } - setState(() { - if (selected.isEmpty) { - } else { - if (selected.contains(index)) { - selected.remove(index); - if (selected.isEmpty) { - title = _searchquery; - } - } else { - selected.add(index); - title = selected.length.toString(); - } - } - }); - }, - onLongPress: () { - setState(() { - if (selected.contains(index)) - selected.remove(index); - else - selected.add(index); + //List updatedreturnvalue = await resultSearchtoVideoHash(returnValue); + //Navigator.pop(context,{'queue':updatedreturnvalue,'addtoexisting':false}); + var toreturn; + var c = showBottomSheet( + context: context, + builder: (context) { + return TrackDetail(results[index], (info) { + toreturn = info; + }); + }); + await c.closed; + if (toreturn != null) { + Navigator.pop(context, { + 'queue': toreturn, + 'addtoexisting': false + }); + } + } + setState(() { + if (selected.isEmpty) { + } else { + if (selected.contains(index)) { + selected.remove(index); + if (selected.isEmpty) { + title = _searchquery; + } + } else { + selected.add(index); + title = selected.length.toString(); + } + } + }); + } else { + var result = showBottomSheet( + context: context, + builder: (context2) { + var playlist; + return CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: Text( + results[index]["title"], + maxLines: 1, + style: Theme.of(context) + .primaryTextTheme + .title, + ), + ), + SliverToBoxAdapter( + child: ButtonBar( + children: [ + RaisedButton.icon( + icon: Icon(Icons.play_arrow), + label: Text("Play"), + onPressed: () { + if (playlist != null) { + var l = List(); + for(var x in playlist["videos"]){ + l.add(x); + } + _play( + l + ); + Navigator.popUntil(context, (route){ + return route.isFirst; + }); + } + }, + ), + RaisedButton.icon( + icon: Icon(Icons + .settings_backup_restore), + label: Text("Import"), + onPressed: () async { + var url = main.MyHomePageState + .InvidiosAPI + + "playlists/" + + results[index] + ["playlistId"]; + var response = + await http.get(url); + Map jsresponse = await json + .decode(utf8.decode( + response.bodyBytes)); + if (jsresponse + .containsKey('title')) { + var db = main.musicDB; + var ob = db + .transaction('playlists', + 'readwrite') + .objectStore('playlists'); + await ob.add(jsresponse); + Navigator.pop(context2, true); - if (selected.isEmpty) { - title = _searchquery; - } else { - title = selected.length.toString(); - } - }); - }, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - AnimatedContainer( - duration: Duration(seconds: 3), - padding: EdgeInsets.all(10), - child: CachedNetworkImage( - imageUrl: getThumbnaillink(results, index, - "videoThumbnails", "medium", "quality")), + Scaffold.of(context) + .showSnackBar(SnackBar( + content: Text( + "Playlist Imported"), + )); + } + }, + ) + ], + ), + ), + FutureBuilder( + future: http.get( + main.MyHomePageState.InvidiosAPI + + "playlists/" + + results[index]["playlistId"]), + builder: (context3, ass) { + if (ass.connectionState == + ConnectionState.done) { + playlist = json.decode(utf8 + .decode(ass.data.bodyBytes)); + print(playlist); + return SliverList( + delegate: + SliverChildBuilderDelegate( + (context, i) { + return Card( + child: ListTile( + title: Text( + playlist["videos"][i] + ["title"]), + ), + ); + }, + childCount: + playlist["videos"] + .length), + ); + } else { + return SliverToBoxAdapter( + child: Center( + child: + CircularProgressIndicator(), + ), + ); + } + }, + ), + ], + ); + }); + + } + }, + onLongPress: () { + setState(() { + if (selected.contains(index)) + selected.remove(index); + else + selected.add(index); + + if (selected.isEmpty) { + title = _searchquery; + } else { + title = selected.length.toString(); + } + }); + }, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Stack( + children: [ + AnimatedContainer( + duration: Duration(seconds: 3), + padding: EdgeInsets.all(10), + child: CachedNetworkImage( + imageUrl: type == "video" + ? getThumbnaillink( + results, + index, + "videoThumbnails", + "medium", + "quality") + : type == "playlist" + ? getThumbnaillink( + results[index]["videos"], + 0, + "videoThumbnails", + "medium", + "quality") + : "", + ), + ), + if (type == "playlist") + Positioned( + top: 0, + bottom: 0, + right: 0, + width: 100, + child: Container( + color: Colors.black.withAlpha(225), + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Text( + results[index]["videoCount"] + .toString(), + style: Theme.of(context) + .primaryTextTheme + .title, + ), + Icon(Icons.playlist_play) + ], + )), + ) + ], + ), + Row( + children: [ + Flexible( + child: Container( + padding: EdgeInsets.all(8), + child: Text( + results[index]["title"], + style: Theme.of(context).textTheme.title, + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), + )), + ], + ), + Container( + padding: EdgeInsets.all(10), + child: Row( + children: [ + Expanded( + child: Text( + results[index]["author"], + style: + Theme.of(context).textTheme.subtitle, + ), + ), + if (type == "video") + Text( + formatDuration(Duration( + seconds: results[index] + ["lengthSeconds"])), + style: + Theme.of(context).textTheme.subtitle, + ) + ], + ), + ) + ], + ), + ), ), - Row( + ); + } else if (type == "channel") { + return InkWell( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => (ArtistPlaylist( + _play, + _playnext, + results[index]["author"], + usePlaylist: true, + playlistdetail: { + 'id': results[index]["authorId"] + }, + )))); + }, + child: Column( children: [ - Flexible( - child: Container( - padding: EdgeInsets.all(8), - child: Text( - results[index]["title"], - style: Theme.of(context).textTheme.title, - overflow: TextOverflow.ellipsis, - maxLines: 2, - ), - )), + Container( + height: 176, + width: 176, + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.black, + blurRadius: 2, + offset: Offset(2, 2)) + ], + shape: BoxShape.circle, + image: DecorationImage( + image: CachedNetworkImageProvider( + getThumbnaillink(results, index, + "authorThumbnails", 176, "width")), + fit: BoxFit.cover)), + ), + Text( + results[index]["author"], + style: Theme.of(context).primaryTextTheme.title, + ) ], ), - Container( - padding: EdgeInsets.all(10), - child: Row( - children: [ - Expanded( - child: Text( - results[index]["author"], - style: Theme.of(context).textTheme.subtitle, - ), - ), - Text( - formatDuration(Duration( - seconds: results[index]["lengthSeconds"])), - style: Theme.of(context).textTheme.subtitle, - ) - ], - ), - ) - ], - ), - ), - ), + ); + } + }, + ); + } + } else { + return Center( + child: CircularProgressIndicator(), ); - }, - ); - } + } + }, + ); } Future searchVid(searchquery) async { String invidiosApi = "https://invidio.us/"; - String apiurl = invidiosApi + "api/v1/search?q="; - final response = await http.get(apiurl + searchquery); + String apiurl = + invidiosApi + "api/v1/search?type=" + _type + "&q=" + searchquery; + print(apiurl); - if (response.statusCode == 200) { - // If server returns an OK response, parse the JSON - dynamic js = json.decode(utf8.decode(response.bodyBytes)); - setState(() { - results = js; - isSearching = false; - }); - return js; - } else { - print("Fetch ERROR"); - // If that response was not OK, throw an error. - throw Exception('Failed to load post'); + try { + final response = await http.get(apiurl); + + if (response.statusCode == 200) { + // If server returns an OK response, parse the JSON + dynamic js = json.decode(utf8.decode(response.bodyBytes)); + setState(() { + results = js; + isSearching = false; + }); + return js; + } else { + print("Fetch ERROR"); + // If that response was not OK, throw an error. + throw Exception('Failed to load post'); + } + } catch (e) { + return Future.error(Exception(e.toString())); } } diff --git a/lib/ytplaylist.dart b/lib/ytplaylist.dart index 9513943..6cf005f 100644 --- a/lib/ytplaylist.dart +++ b/lib/ytplaylist.dart @@ -1,5 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'dart:async'; + import 'playlist.dart'; +import 'main.dart'; class YTPlaylist extends Playlist{ YTPlaylist(play, playnext, String playlistname,{usePlaylist = false,playlistdetail}) : @@ -14,11 +19,19 @@ class _YTPlaylistState extends PlaylistState { @override void settracks() { - var vids = List(); - for(Map v in widget.playlistdetail['videos']){ - vids.add(v); - } - tracks = Future>.value(vids); + String playlistId = widget.playlistdetail.containsKey('playlistId')?widget.playlistdetail['playlistId']:widget.playlistdetail['mixId']; + var url = MyHomePageState.InvidiosAPI+"playlists/" + playlistId; + var c = Completer>(); + tracks = c.future; + http.get(url).then((response) { + Map playlistDetail = json.decode(utf8.decode(response.bodyBytes)); + var vids = List(); + for (Map v in playlistDetail['videos']) { + vids.add(v); + } + print(vids); + c.complete(vids); + }); } @override