Imrove startup; Fix layouts; Add time slider;

This commit is contained in:
poka 2022-04-29 12:18:26 +02:00
parent 9fefb780d1
commit 6e39b5435a
3 changed files with 192 additions and 84 deletions

View File

@ -25,6 +25,7 @@ class FipyApp extends StatelessWidget {
providers: [ providers: [
ChangeNotifierProvider(create: (_) => PlayerProvider()), ChangeNotifierProvider(create: (_) => PlayerProvider()),
ChangeNotifierProvider(create: (_) => HomeProvider()), ChangeNotifierProvider(create: (_) => HomeProvider()),
ChangeNotifierProvider(create: (_) => DownloadProvider()),
], ],
child: MaterialApp( child: MaterialApp(
title: 'Fipy', title: 'Fipy',

View File

@ -5,3 +5,9 @@ class HomeProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
} }
class DownloadProvider with ChangeNotifier {
void reload() {
notifyListeners();
}
}

View File

@ -18,6 +18,7 @@ PlayerController? player;
Track? currentTrack; Track? currentTrack;
List<Track> trackList = []; List<Track> trackList = [];
String radio = 'groove'; String radio = 'groove';
int isDownloading = -1;
class HomeScreen extends StatefulWidget { class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key, required this.title}) : super(key: key); const HomeScreen({Key? key, required this.title}) : super(key: key);
@ -51,7 +52,7 @@ class _HomeScreenState extends State<HomeScreen> {
onChanged: (String? newRadio) { onChanged: (String? newRadio) {
setState(() { setState(() {
radio = newRadio!; radio = newRadio!;
getTracks(radio, hours); // getTracks(radio, hours);
}); });
}, },
items: radioList), items: radioList),
@ -89,6 +90,11 @@ class _HomeScreenState extends State<HomeScreen> {
return const Text('Error'); return const Text('Error');
} else if (snapshot.hasData) { } else if (snapshot.hasData) {
return Table( return Table(
columnWidths: {
0: const FlexColumnWidth(4),
1: const FlexColumnWidth(2),
2: const FlexColumnWidth(1),
},
defaultVerticalAlignment: defaultVerticalAlignment:
TableCellVerticalAlignment.middle, TableCellVerticalAlignment.middle,
children: snapshot.data! children: snapshot.data!
@ -180,47 +186,106 @@ class _HomeScreenState extends State<HomeScreen> {
), ),
]), ]),
), ),
IconButton( Column(children: [
icon: Icon(Icons.skip_previous, const Spacer(),
color: Colors.grey[500], size: 32), Row(children: [
onPressed: () { Column(children: [
if (currentTrack != null && currentTrack!.number > 1) { IconButton(
currentTrack = trackList.firstWhere((element) => icon: Icon(Icons.skip_previous,
element.number == currentTrack!.number - 1); color: Colors.grey[500], size: 32),
playTrack(context, currentTrack!); onPressed: () {
} if (currentTrack != null &&
}), currentTrack!.number > 1) {
ElevatedButton( currentTrack = trackList.firstWhere((element) =>
onPressed: () { element.number == currentTrack!.number - 1);
player?.playing ?? false playTrack(context, currentTrack!);
? player?.pause() }
: player?.play(); }),
playerProvider.reload(); const SizedBox(height: 7),
}, ]),
child: Icon( const SizedBox(width: 8),
player?.playing ?? false ElevatedButton(
? Icons.pause onPressed: () {
: Icons.play_arrow, player?.playing ?? false
color: Colors.grey[900], ? player?.pause()
size: 32), : player?.play();
style: ElevatedButton.styleFrom( playerProvider.reload();
shape: const CircleBorder(), },
padding: const EdgeInsets.all(12), child: Icon(
primary: Colors.grey[300], player?.playing ?? false
onPrimary: Colors.grey[900], ? Icons.pause
), : Icons.play_arrow,
), color: Colors.grey[900],
IconButton( size: 30),
icon: Icon(Icons.skip_next, style: ElevatedButton.styleFrom(
color: Colors.grey[500], size: 32), shape: const CircleBorder(),
onPressed: () { padding: const EdgeInsets.all(12),
if (currentTrack != null && primary: Colors.grey[300],
currentTrack!.number < trackList.last.number) { onPrimary: Colors.grey[900],
currentTrack = trackList.firstWhere((element) => ),
element.number == currentTrack!.number + 1); ),
playTrack(context, currentTrack!); 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(), const Spacer(),
SliderTheme( SliderTheme(
data: const SliderThemeData( data: const SliderThemeData(
@ -339,8 +404,8 @@ Future<List<Track>> getTracks(String radio, int hours) async {
int trackNbr = 0; int trackNbr = 0;
trackList.clear(); trackList.clear();
final yt = YoutubeExplode(); // final yt = YoutubeExplode();
List resultUrlPool = []; // List resultUrlPool = [];
for (Map track in data) { for (Map track in data) {
track = track['track']; track = track['track'];
@ -358,36 +423,39 @@ Future<List<Track>> getTracks(String radio, int hours) async {
Track(number: trackNbr, title: title, artiste: artiste, album: album); Track(number: trackNbr, title: title, artiste: artiste, album: album);
trackList.add(thisTrack); trackList.add(thisTrack);
final secondMatch = artiste == '' ? album : artiste; // final secondMatch = artiste == '' ? album : artiste;
final resultUrl = yt.search.search(title + ' ' + secondMatch); // final resultUrl = yt.search.search(title + ' ' + secondMatch);
resultUrlPool.add(resultUrl); // resultUrlPool.add(resultUrl);
resultUrl.then((value) { // resultUrl.then((value) {
try { // try {
trackList[trackNbr - 1].id = value.first.id.value; // trackList[trackNbr - 1].id = value.first.id.value;
} catch (e) { // } catch (e) {
print( // print(
'Error: ' + trackList[trackNbr - 1].title + ' -> ' + e.toString()); // '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 = // final secondMatch =
trackList[0].artiste == '' ? trackList[0].album : trackList[0].artiste; // trackList[0].artiste == '' ? trackList[0].album : trackList[0].artiste;
yt.search.search(trackList[0].title + ' ' + secondMatch!).then((resultUrl) { // yt.search.search(trackList[0].title + ' ' + secondMatch!).then((resultUrl) {
trackList[0].id = resultUrl.first.id.value; // trackList[0].id = resultUrl.first.id.value;
player = Player.network( // player = Player.network(
"https://invidious.fdn.fr/embed/${trackList[0].id}?raw=1&?listen=1"); // "https://invidious.fdn.fr/embed/${trackList[0].id}?raw=1&?listen=1");
currentTrack = trackList[0]; // currentTrack = trackList[0];
}); // });
return trackList; return trackList;
} }
TableRow _buildTableRow(Track track, BuildContext context) { TableRow _buildTableRow(Track track, BuildContext context) {
DownloadProvider downloadProvider =
Provider.of<DownloadProvider>(context, listen: false);
final textStyle = TextStyle( final textStyle = TextStyle(
fontWeight: track.number == -1 ? FontWeight.w200 : FontWeight.normal, fontWeight: track.number == -1 ? FontWeight.w200 : FontWeight.normal,
color: Colors.grey[500], color: Colors.grey[500],
@ -442,25 +510,45 @@ TableRow _buildTableRow(Track track, BuildContext context) {
child: Padding( child: Padding(
padding: rowPadding, padding: rowPadding,
child: InkWell( child: InkWell(
onTap: () async { onTap: () async {
if (track.id == null) { isDownloading = track.number;
final secondMatch = downloadProvider.reload();
track.artiste == '' ? track.album : track.artiste; if (track.id == null) {
final resultUrl = await yt.search final secondMatch =
.search(track.title + ' ' + secondMatch!); track.artiste == '' ? track.album : track.artiste;
track.id = resultUrl.first.id.value; final resultUrl = await yt.search
} .search(track.title + ' ' + secondMatch!);
if (track.id != null) { track.id = resultUrl.first.id.value;
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));
} }
} if (track.id != null) {
}, final response = await http.get(Uri.parse(
child: Text('TÉLÉCHARGER', style: textStyle), '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<DownloadProvider>(
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<HomeProvider>(context, listen: false); HomeProvider homeProvider = Provider.of<HomeProvider>(context, listen: false);
track = trackList[track.number - 1]; track = trackList[track.number - 1];
currentTrack = track;
if (track.id == null) { if (track.id == null) {
final secondMatch = track.artiste == '' ? track.album : track.artiste; final secondMatch = track.artiste == '' ? track.album : track.artiste;
final resultUrl = await yt.search.search(track.title + ' ' + secondMatch!); final resultUrl = await yt.search.search(track.title + ' ' + secondMatch!);
track.id = resultUrl.first.id.value; track.id = resultUrl.first.id.value;
} }
final currentVolume = player?.volume ?? 1;
player?.dispose(); player?.dispose();
Future.delayed(const Duration(milliseconds: 5)); Future.delayed(const Duration(milliseconds: 5));
player = Player.network( player = Player.network(
"https://invidious.fdn.fr/embed/${track.id}?raw=1&?listen=1"); "https://invidious.fdn.fr/embed/${track.id}?raw=1&?listen=1");
print(track.id); print(track.id);
player!.volume = currentVolume;
try { try {
player!.play(); player!.play();
} catch (e) { } catch (e) {
print('Play error: ' + e.toString()); print('Play error: ' + e.toString());
} }
Future.delayed(const Duration(milliseconds: 50)); Future.delayed(const Duration(milliseconds: 500));
player!.callback = (PlayerEvent event) { player!.callback = (PlayerEvent event) {
if (event.name == 'position') {
currentTrack!.duration = player!.duration;
playerProvider.reload();
}
if (event.name == 'status') { if (event.name == 'status') {
var nextTrack = var nextTrack =
trackList.firstWhere((element) => element.number == track.number + 1); trackList.firstWhere((element) => element.number == track.number + 1);
playTrack(context, nextTrack); playTrack(context, nextTrack);
} }
}; };
currentTrack = track;
playerProvider.reload(); playerProvider.reload();
homeProvider.reload(); homeProvider.reload();
} }
@ -528,3 +622,10 @@ List<DropdownMenuItem<String>> get radioList {
]; ];
return menuItems; 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';
}