Change API to official Rest, no GraphQL anymore

This commit is contained in:
poka 2022-04-30 01:48:17 +02:00
parent b8d6d7f3a1
commit cca3d63737
6 changed files with 421 additions and 488 deletions

24
lib/models/track.dart Normal file
View File

@ -0,0 +1,24 @@
import 'dart:io';
class Track {
final int number;
final String title;
final String artiste;
final String? album;
String? image;
String? imageUrl;
String? id;
Duration? duration;
File? file;
Track(
{required this.number,
required this.title,
required this.artiste,
this.album,
this.id,
this.duration,
this.file,
this.image,
this.imageUrl});
}

View File

@ -1,6 +1,183 @@
// ignore_for_file: avoid_print
import 'dart:convert';
import 'dart:io';
import 'package:fip_parser_ui/models/track.dart';
import 'package:flutter/material.dart';
import 'package:kplayer/kplayer.dart';
import 'package:path_provider/path_provider.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
import 'package:fip_parser_ui/providers/player.dart';
import 'package:provider/provider.dart';
import 'package:http/http.dart';
class HomeProvider with ChangeNotifier {
Track? currentTrack;
PlayerController? player;
List<Track> trackList = [];
int trackNbr = 0;
int userPageNbr = 3;
Future<List<Track>> getTracks(String radio, {String cursor = ''}) async {
int pageNbr = 0;
trackList.clear();
trackNbr = 0;
bool stop = false;
while (pageNbr < userPageNbr && !stop) {
final req = Uri.parse(
'https://www.radiofrance.fr/api/v1.7/stations/fip/webradios/$radio/songs?pageCursor=$cursor');
final res = await get(req, headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS"
});
if (res.statusCode == 200) {
Map body = jsonDecode(res.body);
if (body['next'] != null) {
cursor = body['next'];
} else {
print('Page N°$pageNbr');
stop = true;
}
for (Map track in body['songs']) {
trackNbr++;
final String title = track['secondLine'] ?? '';
final String artiste = track['firstLine'] ?? '';
final String album = track['release']['title'] ?? '';
final String image = track['visual']?['preview'] ?? '';
final String imageUrl = track['visual']?['src'] ?? '';
final thisTrack = Track(
number: trackNbr,
title: title,
artiste: artiste,
album: album,
image: image,
imageUrl: imageUrl);
trackList.add(thisTrack);
}
pageNbr++;
} else {
throw "Unable to retrieve tracks.";
}
}
return trackList;
}
Future playTrack(BuildContext context, Track track) async {
var yt = YoutubeExplode();
PlayerProvider playerProvider =
Provider.of<PlayerProvider>(context, listen: false);
HomeProvider homeProvider =
Provider.of<HomeProvider>(context, listen: false);
// track = trackList[track.number - 1];
currentTrack = track;
final currentVolume = player?.volume ?? 1;
player?.dispose();
Future.delayed(const Duration(milliseconds: 5));
if (track.file == null) {
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;
}
player = Player.network(
"https://invidious.fdn.fr/embed/${track.id}?raw=1&?listen=1");
print(track.id);
} else {
player = Player.asset(track.file!.path);
}
player!.volume = currentVolume;
try {
player!.play();
} catch (e) {
print('Play error: ' + e.toString());
}
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);
}
};
playerProvider.reload();
homeProvider.reload();
yt.close();
}
Future downloadMusic(BuildContext context, Track track) async {
var yt = YoutubeExplode();
var manifest = await yt.videos.streamsClient.getManifest(track.id);
var streamManifest = StreamManifest(manifest.streams);
var streamInfo = streamManifest.audioOnly.withHighestBitrate();
var stream = yt.videos.streamsClient.get(streamInfo);
final filePath = await getDownloadsDirectory();
final fileName = '${track.title} - ${track.artiste}'
.replaceAll('\\', '')
.replaceAll('/', '')
.replaceAll(':', '')
.replaceAll('*', '')
.replaceAll('?', '')
.replaceAll('"', '')
.replaceAll('<', '')
.replaceAll('>', '')
.replaceAll('|', '');
var file = File('${filePath!.path}/$fileName.webm');
var fileStream = file.openWrite();
await stream.pipe(fileStream);
await fileStream.flush();
await fileStream.close();
yt.close();
// Play it
track.file = file;
// playTrack(context, track);
print(file.path);
}
List<DropdownMenuItem<String>> get radioList {
List<DropdownMenuItem<String>> menuItems = [
const DropdownMenuItem(child: Text("FIP"), value: "fip"),
const DropdownMenuItem(child: Text("Electro"), value: "fip_electro"),
const DropdownMenuItem(child: Text("Groove"), value: "fip_groove"),
const DropdownMenuItem(child: Text("Rock"), value: "fip_rock"),
const DropdownMenuItem(child: Text("Jazz"), value: "fip_jazz"),
const DropdownMenuItem(child: Text("Pop"), value: "fip_pop"),
const DropdownMenuItem(child: Text("Reggae"), value: "fip_reggae"),
const DropdownMenuItem(child: Text("World"), value: "fip_world"),
const DropdownMenuItem(
child: Text("Nouveautés"), value: "fip_nouveautes"),
];
return menuItems;
}
List<DropdownMenuItem<String>> get pageList {
List<DropdownMenuItem<String>> menuItems = [
const DropdownMenuItem(child: Text("25"), value: "3"),
const DropdownMenuItem(child: Text("50"), value: "6"),
const DropdownMenuItem(child: Text("100"), value: "12"),
const DropdownMenuItem(child: Text("200"), value: "25"),
const DropdownMenuItem(child: Text("500"), value: "62"),
];
return menuItems;
}
void reload() {
notifyListeners();
}
@ -10,4 +187,4 @@ class DownloadProvider with ChangeNotifier {
void reload() {
notifyListeners();
}
}
}

View File

@ -1,26 +1,14 @@
// ignore_for_file: prefer_const_literals_to_create_immutables, avoid_print
import 'dart:io';
import 'package:fip_parser_ui/globals.dart';
import 'dart:convert';
import 'package:fip_parser_ui/models/track.dart';
import 'package:fip_parser_ui/providers/home.dart';
import 'package:fip_parser_ui/providers/player.dart';
import 'package:flutter/material.dart';
import 'package:fip_parser_ui/queries.dart';
import 'package:graphql/client.dart';
import 'package:kplayer/kplayer.dart';
import 'package:miniplayer/miniplayer.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import 'package:retry/retry.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
PlayerController? player;
Track? currentTrack;
List<Track> trackList = [];
String radio = 'groove';
int isDownloading = -1;
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key, required this.title}) : super(key: key);
final String title;
@ -29,107 +17,147 @@ class HomeScreen extends StatefulWidget {
}
class _HomeScreenState extends State<HomeScreen> {
String radio = 'fip_groove';
@override
Widget build(BuildContext context) {
HomeProvider hp = Provider.of<HomeProvider>(context, listen: false);
final MiniplayerController controller = MiniplayerController();
const hours = 8;
return Stack(
children: <Widget>[
Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
color: Colors.grey[900],
height: 50,
child: Center(
child: DropdownButton(
dropdownColor: Colors.grey[900],
value: radio,
style: TextStyle(fontSize: 15, color: Colors.grey[300]),
underline: const SizedBox(),
iconSize: 22,
onChanged: (String? newRadio) {
setState(() {
radio = newRadio!;
// getTracks(radio, hours);
});
},
items: radioList),
),
),
FutureBuilder<List<Track>>(
future: getTracks(radio, hours),
builder: (
BuildContext context,
AsyncSnapshot<List<Track>> snapshot,
) {
print(snapshot.connectionState);
if (snapshot.connectionState == ConnectionState.waiting) {
return Stack(children: [
Container(
height: 10000,
width: 10000,
color: const Color(0xFF121212)),
Center(
child: Column(children: [
const SizedBox(height: 20),
SizedBox(
height: 30,
width: 30,
child: CircularProgressIndicator(
color: Colors.amber[100],
),
),
body: RawScrollbar(
thumbColor: Colors.grey[600],
radius: const Radius.circular(20),
thickness: 12,
thumbVisibility: true,
mainAxisMargin: 70,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
color: Colors.grey[900],
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton(
dropdownColor: Colors.grey[900],
value: radio,
style: TextStyle(
fontSize: 15, color: Colors.grey[300]),
underline: const SizedBox(),
iconSize: 22,
onChanged: (String? newRadio) {
setState(() {
radio = newRadio!;
});
},
items: hp.radioList),
const SizedBox(width: 50),
DropdownButton(
dropdownColor: Colors.grey[900],
value: hp.userPageNbr.toString(),
style: TextStyle(
fontSize: 15, color: Colors.grey[300]),
underline: const SizedBox(),
iconSize: 22,
onChanged: (String? newPageNumber) {
setState(() {
hp.userPageNbr = int.parse(newPageNumber!);
});
},
items: hp.pageList),
]),
),
]);
} else if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.hasError) {
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!
.map((item) => _buildTableRow(item, context))
.toList()
..insert(
0,
_buildTableRow(
Track(
number: -1,
title: 'TITRE',
artiste: 'ARTISTE',
album: 'ALBUM',
id: 'URL'),
context),
),
FutureBuilder<List<Track>>(
future: hp.getTracks(radio),
builder: (
BuildContext context,
AsyncSnapshot<List<Track>> snapshot,
) {
print(snapshot.connectionState);
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Stack(children: [
Container(
height: 10000,
width: 10000,
color: const Color(0xFF121212)),
Center(
child: Column(children: [
const SizedBox(height: 20),
SizedBox(
height: 30,
width: 30,
child: CircularProgressIndicator(
color: Colors.amber[100],
),
),
]),
),
);
} else {
return const Text('Empty data');
}
} else {
return Text('State: ${snapshot.connectionState}');
}
},
]);
} else if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.hasError) {
return Stack(children: [
Container(
height: 10000,
width: 10000,
color: const Color(0xFF121212)),
Center(
child: Column(children: [
const SizedBox(height: 20),
Text(
'Error: ' + snapshot.error.toString(),
style: TextStyle(color: Colors.grey[500]),
),
]),
),
]);
} 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!
.map((item) => _buildTableRow(item, context))
.toList()
..insert(
0,
_buildTableRow(
Track(
number: -1,
title: 'TITRE',
artiste: 'ARTISTE',
album: 'ALBUM',
id: 'URL'),
context),
),
);
} else {
return const Text('Empty data');
}
} else {
return Text('State: ${snapshot.connectionState}');
}
},
),
const SizedBox(height: 70)
],
),
],
),
),
)),
),
Consumer<PlayerProvider>(builder: (context, playerProvider, _) {
TextEditingController trackTitle = TextEditingController();
TextEditingController trackArtiste = TextEditingController();
trackTitle.text = currentTrack?.title ?? '';
trackArtiste.text = currentTrack?.artiste ?? '';
trackTitle.text = hp.currentTrack?.title ?? '';
trackArtiste.text = hp.currentTrack?.artiste ?? '';
return Miniplayer(
controller: controller,
backgroundColor: Colors.grey[900]!,
@ -139,12 +167,13 @@ class _HomeScreenState extends State<HomeScreen> {
return Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
currentTrack?.id != null
hp.currentTrack?.imageUrl != '' &&
hp.currentTrack?.imageUrl != null
? Image.network(
'https://img.youtube.com/vi/${currentTrack?.id}/1.jpg',
width: 93,
hp.currentTrack!.imageUrl!,
width: 70,
)
: const SizedBox(width: 100),
: const SizedBox(width: 70),
Expanded(
child: Column(children: [
SizedBox(
@ -190,30 +219,33 @@ class _HomeScreenState extends State<HomeScreen> {
Column(children: [
const Spacer(),
Row(children: [
const SizedBox(width: 70),
Column(children: [
IconButton(
padding: const EdgeInsets.all(0),
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!);
if (hp.currentTrack != null &&
hp.currentTrack!.number > 1) {
hp.currentTrack = hp.trackList.firstWhere(
(element) =>
element.number ==
hp.currentTrack!.number - 1);
hp.playTrack(context, hp.currentTrack!);
}
}),
const SizedBox(height: 7),
]),
const SizedBox(width: 8),
const SizedBox(width: 1),
ElevatedButton(
onPressed: () {
player?.playing ?? false
? player?.pause()
: player?.play();
hp.player?.playing ?? false
? hp.player?.pause()
: hp.player?.play();
playerProvider.reload();
},
child: Icon(
player?.playing ?? false
hp.player?.playing ?? false
? Icons.pause
: Icons.play_arrow,
color: Colors.grey[900],
@ -227,25 +259,29 @@ class _HomeScreenState extends State<HomeScreen> {
),
Column(children: [
IconButton(
padding: const EdgeInsets.all(0),
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!);
if (hp.currentTrack != null &&
hp.currentTrack!.number <
hp.trackList.last.number) {
hp.currentTrack = hp.trackList.firstWhere(
(element) =>
element.number ==
hp.currentTrack!.number + 1);
hp.playTrack(context, hp.currentTrack!);
}
}),
const SizedBox(height: 7),
// const SizedBox(height: 7),
]),
]),
const Spacer(),
Row(children: [
const SizedBox(width: 70),
Text(
timeFormat(
player?.position ?? const Duration(seconds: 0)),
hp.player?.position ?? const Duration(seconds: 0)),
style: TextStyle(color: Colors.grey[500], fontSize: 12),
),
const SizedBox(width: 3),
@ -261,13 +297,14 @@ class _HomeScreenState extends State<HomeScreen> {
width: 300,
child: Slider(
value: double.parse(
player?.position.inSeconds.toString() ?? '0'),
hp.player?.position.inSeconds.toString() ??
'0'),
max: double.parse(
player?.duration.inSeconds.toString() ??
hp.player?.duration.inSeconds.toString() ??
'2000'),
onChanged: (double value) {
if (player?.position != null) {
player!.position =
if (hp.player?.position != null) {
hp.player!.position =
Duration(seconds: value.toInt());
playerProvider.reload();
}
@ -280,7 +317,7 @@ class _HomeScreenState extends State<HomeScreen> {
const SizedBox(width: 3),
Text(
timeFormat(
player?.duration ?? const Duration(seconds: 0)),
hp.player?.duration ?? const Duration(seconds: 0)),
style: TextStyle(color: Colors.grey[500], fontSize: 12),
),
]),
@ -297,11 +334,11 @@ class _HomeScreenState extends State<HomeScreen> {
child: SizedBox(
width: 130,
child: Slider(
value: player?.volume ?? 1,
value: hp.player?.volume ?? 1,
max: 1,
onChanged: (double value) {
if (player?.volume != null) {
player!.volume = value;
if (hp.player?.volume != null) {
hp.player!.volume = value;
playerProvider.reload();
}
},
@ -330,134 +367,11 @@ Widget trackLine(Track track) {
);
}
class Track {
final int number;
final String title;
final String artiste;
final String? album;
String? id;
Duration? duration;
File? file;
Track(
{required this.number,
required this.title,
required this.artiste,
this.album,
this.id,
this.duration,
this.file});
}
GraphQLClient initClient() {
final _httpLink = HttpLink(
'https://openapi.radiofrance.fr/v1/graphql?x-token=$token',
);
final GraphQLClient client = GraphQLClient(
cache: GraphQLCache(),
link: _httpLink,
);
return client;
}
Future<List<Track>> getTracks(String radio, int hours) async {
// TODO: Change API to one use on website: https://www.radiofrance.fr/api/v1.7/stations/fip/webradios/fip_groove/songs?pageCursor=
const int queryTimeout = 8;
final client = initClient();
final now = (DateTime.now().millisecondsSinceEpoch ~/ 1000);
final yesterday = now - (hours * 3600);
final String radioQuery;
if (radio == 'fip') {
radioQuery = 'FIP';
} else {
radioQuery = 'FIP_${radio.toUpperCase()}';
}
final QueryOptions options = QueryOptions(
document: gql(getSongs),
pollInterval: const Duration(milliseconds: 50),
variables: <String, dynamic>{
'start': yesterday,
'end': now,
'radio': radioQuery,
},
);
QueryResult result;
result = await retry(
() async => await client
.query(options)
.timeout(const Duration(seconds: queryTimeout)),
onRetry: (_) => print('Timeout, retry...'),
);
if (result.hasException) {
print(result.exception.toString());
}
final List data = result.data?['grid'] ?? [];
print(data.length.toString() + ' songs');
// #######
int trackNbr = 0;
trackList.clear();
// final yt = YoutubeExplode();
// List resultUrlPool = [];
for (Map track in data) {
track = track['track'];
trackNbr++;
final String title = track['title'];
final String artiste;
if (track['mainArtists'].isNotEmpty) {
artiste = track['mainArtists'].first;
} else {
artiste = '';
}
final String album = track['albumTitle'];
final thisTrack =
Track(number: trackNbr, title: title, artiste: artiste, album: album);
trackList.add(thisTrack);
// final secondMatch = artiste == '' ? album : artiste;
// final resultUrl = yt.search.search(title + ' ' + secondMatch);
// 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());
// }
// });
}
// 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;
// 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<DownloadProvider>(context, listen: false);
HomeProvider hp = Provider.of<HomeProvider>(context, listen: false);
int isDownloading = -1;
final textStyle = TextStyle(
fontWeight: track.number == -1 ? FontWeight.w200 : FontWeight.normal,
@ -477,30 +391,38 @@ TableRow _buildTableRow(Track track, BuildContext context) {
child: InkWell(
onTap: () async {
if (track.number != -1) {
playTrack(context, track);
hp.playTrack(context, track);
}
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Consumer<PlayerProvider>(
builder: (context, playerProvider, _) {
return Text(track.title,
style: TextStyle(
fontWeight: FontWeight.normal,
color: currentTrack == track
? Colors.green
: Colors.grey[350],
fontSize: 14));
}),
const SizedBox(height: 5),
if (track.number != -1)
Text(track.artiste,
style: TextStyle(
fontWeight: FontWeight.normal,
color: Colors.grey[500],
fontSize: 13))
]),
child: Row(children: [
track.number != -1
? track.image == ''
? const SizedBox(width: 40)
: Image.memory(base64Decode(track.image!), height: 40)
: const SizedBox(),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Consumer<PlayerProvider>(
builder: (context, playerProvider, _) {
return Text(track.title,
style: TextStyle(
fontWeight: FontWeight.normal,
color: hp.currentTrack == track
? Colors.green
: Colors.grey[350],
fontSize: 14));
}),
const SizedBox(height: 2),
if (track.number != -1)
Text(track.artiste,
style: TextStyle(
fontWeight: FontWeight.normal,
color: Colors.grey[500],
fontSize: 13))
]),
]),
)),
),
TableCell(
@ -524,7 +446,7 @@ TableRow _buildTableRow(Track track, BuildContext context) {
track.id = resultUrl.first.id.value;
}
if (track.id != null) {
downloadMusic(context, track);
hp.downloadMusic(context, track);
}
isDownloading = -1;
yt.close();
@ -552,118 +474,6 @@ TableRow _buildTableRow(Track track, BuildContext context) {
]);
}
Future playTrack(BuildContext context, Track track) async {
var yt = YoutubeExplode();
PlayerProvider playerProvider =
Provider.of<PlayerProvider>(context, listen: false);
HomeProvider homeProvider = Provider.of<HomeProvider>(context, listen: false);
// track = trackList[track.number - 1];
currentTrack = track;
final currentVolume = player?.volume ?? 1;
player?.dispose();
Future.delayed(const Duration(milliseconds: 5));
if (track.file == null) {
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;
}
player = Player.network(
"https://invidious.fdn.fr/embed/${track.id}?raw=1&?listen=1");
print(track.id);
} else {
player = Player.asset(track.file!.path);
}
player!.volume = currentVolume;
try {
player!.play();
} catch (e) {
print('Play error: ' + e.toString());
}
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);
}
};
playerProvider.reload();
homeProvider.reload();
yt.close();
}
Future downloadMusic(BuildContext context, Track track) async {
var yt = YoutubeExplode();
var manifest = await yt.videos.streamsClient.getManifest(track.id);
var streamManifest = StreamManifest(manifest.streams);
var streamInfo = streamManifest.audioOnly.withHighestBitrate();
var stream = yt.videos.streamsClient.get(streamInfo);
final filePath = await getDownloadsDirectory();
final fileName = '${track.title} - ${track.artiste}'
.replaceAll('\\', '')
.replaceAll('/', '')
.replaceAll(':', '')
.replaceAll('*', '')
.replaceAll('?', '')
.replaceAll('"', '')
.replaceAll('<', '')
.replaceAll('>', '')
.replaceAll('|', '');
var file = File('${filePath!.path}/$fileName.webm');
var fileStream = file.openWrite();
await stream.pipe(fileStream);
await fileStream.flush();
await fileStream.close();
yt.close();
// Play it
track.file = file;
// playTrack(context, track);
print(file.path);
}
// final radioList = [
// 'fip',
// 'electro',
// 'groove',
// 'rock',
// 'jazz',
// 'pop',
// 'reggae',
// 'world',
// 'nouveautes',
// ];
List<DropdownMenuItem<String>> get radioList {
List<DropdownMenuItem<String>> menuItems = [
const DropdownMenuItem(child: Text("FIP"), value: "fip"),
const DropdownMenuItem(child: Text("Electro"), value: "electro"),
const DropdownMenuItem(child: Text("Groove"), value: "groove"),
const DropdownMenuItem(child: Text("Rock"), value: "rock"),
const DropdownMenuItem(child: Text("Jazz"), value: "jazz"),
const DropdownMenuItem(child: Text("Pop"), value: "pop"),
const DropdownMenuItem(child: Text("Reggae"), value: "reggae"),
const DropdownMenuItem(child: Text("World"), value: "world"),
const DropdownMenuItem(child: Text("Nouveautes"), value: "nouveautes"),
];
return menuItems;
}
String timeFormat(Duration d) {
String dd = d.toString().split('.').first;
String minutes = dd.toString().split(':')[1];

View File

@ -56,7 +56,7 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
version: "1.16.0"
crypto:
dependency: transitive
description:
@ -98,7 +98,7 @@ packages:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.0"
ffi:
dependency: transitive
description:
@ -142,69 +142,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
gql:
dependency: transitive
description:
name: gql
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.1"
gql_dedupe_link:
dependency: transitive
description:
name: gql_dedupe_link
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
gql_error_link:
dependency: transitive
description:
name: gql_error_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.2"
gql_exec:
dependency: transitive
description:
name: gql_exec
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0"
gql_http_link:
dependency: transitive
description:
name: gql_http_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
gql_link:
dependency: transitive
description:
name: gql_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
gql_transform_link:
dependency: transitive
description:
name: gql_transform_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.2"
graphql:
dependency: "direct main"
description:
name: graphql
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.1"
hive:
dependency: transitive
description:
name: hive
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
html:
dependency: transitive
description:
@ -232,7 +169,7 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
version: "0.6.4"
json_annotation:
dependency: transitive
description:
@ -309,7 +246,7 @@ packages:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
version: "0.1.4"
meta:
dependency: transitive
description:
@ -333,20 +270,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
normalize:
dependency: transitive
description:
name: normalize
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.0+1"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
version: "1.8.1"
path_provider:
dependency: "direct main"
description:
@ -431,13 +361,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.2"
retry:
dependency: "direct main"
description:
name: retry
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
rxdart:
dependency: transitive
description:
@ -456,7 +379,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.2"
stack_trace:
dependency: transitive
description:
@ -491,7 +414,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.8"
version: "0.4.9"
typed_data:
dependency: transitive
description:
@ -568,14 +491,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
version: "2.1.2"
win32:
dependency: transitive
description:
@ -605,5 +521,5 @@ packages:
source: hosted
version: "1.11.0"
sdks:
dart: ">=2.16.2 <3.0.0"
dart: ">=2.17.0-0 <3.0.0"
flutter: ">=2.10.0"

View File

@ -11,9 +11,7 @@ dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
graphql: ^5.0.0
youtube_explode_dart: ^1.10.9+1
retry: ^3.1.0
url_launcher: ^6.1.0
http: ^0.13.4
kplayer: ^0.1.12

View File

@ -7,6 +7,9 @@ list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
@ -15,3 +18,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST})
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)