diff --git a/lib/main.dart b/lib/main.dart index 6bdb9da..16bb640 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -25,6 +25,7 @@ class FipyApp extends StatelessWidget { providers: [ ChangeNotifierProvider(create: (_) => PlayerProvider()), ChangeNotifierProvider(create: (_) => HomeProvider()), + ChangeNotifierProvider(create: (_) => DownloadProvider()), ], child: MaterialApp( title: 'Fipy', diff --git a/lib/providers/home.dart b/lib/providers/home.dart index 70d1844..d2330b0 100644 --- a/lib/providers/home.dart +++ b/lib/providers/home.dart @@ -5,3 +5,9 @@ class HomeProvider with ChangeNotifier { notifyListeners(); } } + +class DownloadProvider with ChangeNotifier { + void reload() { + notifyListeners(); + } +} \ No newline at end of file diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 58b4e63..7cf11c1 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -18,6 +18,7 @@ PlayerController? player; Track? currentTrack; List trackList = []; String radio = 'groove'; +int isDownloading = -1; class HomeScreen extends StatefulWidget { const HomeScreen({Key? key, required this.title}) : super(key: key); @@ -51,7 +52,7 @@ class _HomeScreenState extends State { onChanged: (String? newRadio) { setState(() { radio = newRadio!; - getTracks(radio, hours); + // getTracks(radio, hours); }); }, items: radioList), @@ -89,6 +90,11 @@ class _HomeScreenState extends State { return const Text('Error'); } else if (snapshot.hasData) { return Table( + columnWidths: { + 0: const FlexColumnWidth(4), + 1: const FlexColumnWidth(2), + 2: const FlexColumnWidth(1), + }, defaultVerticalAlignment: TableCellVerticalAlignment.middle, children: snapshot.data! @@ -180,47 +186,106 @@ class _HomeScreenState extends State { ), ]), ), - IconButton( - icon: Icon(Icons.skip_previous, - color: Colors.grey[500], size: 32), - onPressed: () { - if (currentTrack != null && currentTrack!.number > 1) { - currentTrack = trackList.firstWhere((element) => - element.number == currentTrack!.number - 1); - playTrack(context, currentTrack!); - } - }), - ElevatedButton( - onPressed: () { - player?.playing ?? false - ? player?.pause() - : player?.play(); - playerProvider.reload(); - }, - child: Icon( - player?.playing ?? false - ? Icons.pause - : Icons.play_arrow, - color: Colors.grey[900], - size: 32), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - padding: const EdgeInsets.all(12), - primary: Colors.grey[300], - onPrimary: Colors.grey[900], - ), - ), - IconButton( - icon: Icon(Icons.skip_next, - color: Colors.grey[500], size: 32), - onPressed: () { - if (currentTrack != null && - currentTrack!.number < trackList.last.number) { - currentTrack = trackList.firstWhere((element) => - element.number == currentTrack!.number + 1); - playTrack(context, currentTrack!); - } - }), + Column(children: [ + const Spacer(), + Row(children: [ + Column(children: [ + IconButton( + icon: Icon(Icons.skip_previous, + color: Colors.grey[500], size: 32), + onPressed: () { + if (currentTrack != null && + currentTrack!.number > 1) { + currentTrack = trackList.firstWhere((element) => + element.number == currentTrack!.number - 1); + playTrack(context, currentTrack!); + } + }), + const SizedBox(height: 7), + ]), + const SizedBox(width: 8), + ElevatedButton( + onPressed: () { + player?.playing ?? false + ? player?.pause() + : player?.play(); + playerProvider.reload(); + }, + child: Icon( + player?.playing ?? false + ? Icons.pause + : Icons.play_arrow, + color: Colors.grey[900], + size: 30), + style: ElevatedButton.styleFrom( + shape: const CircleBorder(), + padding: const EdgeInsets.all(12), + primary: Colors.grey[300], + onPrimary: Colors.grey[900], + ), + ), + Column(children: [ + IconButton( + icon: Icon(Icons.skip_next, + color: Colors.grey[500], size: 32), + onPressed: () { + if (currentTrack != null && + currentTrack!.number < + trackList.last.number) { + currentTrack = trackList.firstWhere((element) => + element.number == currentTrack!.number + 1); + playTrack(context, currentTrack!); + } + }), + const SizedBox(height: 7), + ]), + ]), + const Spacer(), + Row(children: [ + Text( + timeFormat( + player?.position ?? const Duration(seconds: 0)), + style: TextStyle(color: Colors.grey[500], fontSize: 12), + ), + const SizedBox(width: 3), + SliderTheme( + data: const SliderThemeData( + thumbShape: + RoundSliderThumbShape(enabledThumbRadius: 2), + overlayShape: + RoundSliderThumbShape(enabledThumbRadius: 5), + trackHeight: 2, + ), + child: SizedBox( + width: 300, + child: Slider( + value: double.parse( + player?.position.inSeconds.toString() ?? '0'), + max: double.parse( + player?.duration.inSeconds.toString() ?? + '2000'), + onChanged: (double value) { + print(value); + if (player?.position != null) { + player!.position = + Duration(seconds: value.toInt()); + playerProvider.reload(); + } + }, + activeColor: Colors.grey[400], + inactiveColor: Colors.grey[700], + ), + ), + ), + const SizedBox(width: 3), + Text( + timeFormat( + player?.duration ?? const Duration(seconds: 0)), + style: TextStyle(color: Colors.grey[500], fontSize: 12), + ), + ]), + const Spacer(), + ]), const Spacer(), SliderTheme( data: const SliderThemeData( @@ -339,8 +404,8 @@ Future> getTracks(String radio, int hours) async { int trackNbr = 0; trackList.clear(); - final yt = YoutubeExplode(); - List resultUrlPool = []; + // final yt = YoutubeExplode(); + // List resultUrlPool = []; for (Map track in data) { track = track['track']; @@ -358,36 +423,39 @@ Future> getTracks(String radio, int hours) async { Track(number: trackNbr, title: title, artiste: artiste, album: album); trackList.add(thisTrack); - final secondMatch = artiste == '' ? album : artiste; - final resultUrl = yt.search.search(title + ' ' + secondMatch); + // final secondMatch = artiste == '' ? album : artiste; + // final resultUrl = yt.search.search(title + ' ' + secondMatch); - resultUrlPool.add(resultUrl); + // resultUrlPool.add(resultUrl); - resultUrl.then((value) { - try { - trackList[trackNbr - 1].id = value.first.id.value; - } catch (e) { - print( - 'Error: ' + trackList[trackNbr - 1].title + ' -> ' + e.toString()); - } - }); + // resultUrl.then((value) { + // try { + // trackList[trackNbr - 1].id = value.first.id.value; + // } catch (e) { + // print( + // 'Error: ' + trackList[trackNbr - 1].title + ' -> ' + e.toString()); + // } + // }); } - trackList.sort((a, b) => a.number.compareTo(b.number)); + // trackList.sort((a, b) => a.number.compareTo(b.number)); - final secondMatch = - trackList[0].artiste == '' ? trackList[0].album : trackList[0].artiste; - yt.search.search(trackList[0].title + ' ' + secondMatch!).then((resultUrl) { - trackList[0].id = resultUrl.first.id.value; + // final secondMatch = + // trackList[0].artiste == '' ? trackList[0].album : trackList[0].artiste; + // yt.search.search(trackList[0].title + ' ' + secondMatch!).then((resultUrl) { + // trackList[0].id = resultUrl.first.id.value; - player = Player.network( - "https://invidious.fdn.fr/embed/${trackList[0].id}?raw=1&?listen=1"); - currentTrack = trackList[0]; - }); + // player = Player.network( + // "https://invidious.fdn.fr/embed/${trackList[0].id}?raw=1&?listen=1"); + // currentTrack = trackList[0]; + // }); return trackList; } TableRow _buildTableRow(Track track, BuildContext context) { + DownloadProvider downloadProvider = + Provider.of(context, listen: false); + final textStyle = TextStyle( fontWeight: track.number == -1 ? FontWeight.w200 : FontWeight.normal, color: Colors.grey[500], @@ -442,25 +510,45 @@ TableRow _buildTableRow(Track track, BuildContext context) { child: Padding( padding: rowPadding, child: InkWell( - onTap: () async { - if (track.id == null) { - final secondMatch = - track.artiste == '' ? track.album : track.artiste; - final resultUrl = await yt.search - .search(track.title + ' ' + secondMatch!); - track.id = resultUrl.first.id.value; - } - if (track.id != null) { - final response = await http.get(Uri.parse( - 'https://ytdl.p2p.legal/ytdl.sh?${track.id}')); - final dlLink = response.body; - if (await canLaunchUrl(Uri.parse(dlLink))) { - launchUrl(Uri.parse(dlLink)); + onTap: () async { + isDownloading = track.number; + downloadProvider.reload(); + if (track.id == null) { + final secondMatch = + track.artiste == '' ? track.album : track.artiste; + final resultUrl = await yt.search + .search(track.title + ' ' + secondMatch!); + track.id = resultUrl.first.id.value; } - } - }, - child: Text('TÉLÉCHARGER', style: textStyle), - )), + if (track.id != null) { + final response = await http.get(Uri.parse( + 'https://ytdl.p2p.legal/ytdl.sh?${track.id}')); + final dlLink = response.body; + if (await canLaunchUrl(Uri.parse(dlLink))) { + launchUrl(Uri.parse(dlLink)); + } + } + isDownloading = -1; + downloadProvider.reload(); + }, + child: track.number == -1 + ? Text('TÉLÉCHARGER', style: textStyle) + : Consumer( + builder: (context, playerProvider, _) { + return Row(children: [ + const SizedBox(width: 37), + isDownloading == track.number + ? SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.grey[500], + ), + ) + : Icon(Icons.download, color: Colors.grey[500]), + ]); + }))), ), ]); } @@ -472,32 +560,38 @@ Future playTrack(BuildContext context, Track track) async { HomeProvider homeProvider = Provider.of(context, listen: false); track = trackList[track.number - 1]; + currentTrack = track; if (track.id == null) { final secondMatch = track.artiste == '' ? track.album : track.artiste; final resultUrl = await yt.search.search(track.title + ' ' + secondMatch!); track.id = resultUrl.first.id.value; } + final currentVolume = player?.volume ?? 1; player?.dispose(); Future.delayed(const Duration(milliseconds: 5)); player = Player.network( "https://invidious.fdn.fr/embed/${track.id}?raw=1&?listen=1"); print(track.id); + player!.volume = currentVolume; try { player!.play(); } catch (e) { print('Play error: ' + e.toString()); } - Future.delayed(const Duration(milliseconds: 50)); + Future.delayed(const Duration(milliseconds: 500)); player!.callback = (PlayerEvent event) { + if (event.name == 'position') { + currentTrack!.duration = player!.duration; + playerProvider.reload(); + } if (event.name == 'status') { var nextTrack = trackList.firstWhere((element) => element.number == track.number + 1); playTrack(context, nextTrack); } }; - currentTrack = track; playerProvider.reload(); homeProvider.reload(); } @@ -528,3 +622,10 @@ List> get radioList { ]; return menuItems; } + +String timeFormat(Duration d) { + String dd = d.toString().split('.').first; + String minutes = dd.toString().split(':')[1]; + String seconds = dd.toString().split(':')[2]; + return '$minutes:$seconds'; +}