Change API to official Rest, no GraphQL anymore
This commit is contained in:
parent
b8d6d7f3a1
commit
cca3d63737
|
@ -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});
|
||||||
|
}
|
|
@ -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: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 {
|
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() {
|
void reload() {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
@ -10,4 +187,4 @@ class DownloadProvider with ChangeNotifier {
|
||||||
void reload() {
|
void reload() {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,14 @@
|
||||||
// ignore_for_file: prefer_const_literals_to_create_immutables, avoid_print
|
// ignore_for_file: prefer_const_literals_to_create_immutables, avoid_print
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:convert';
|
||||||
|
import 'package:fip_parser_ui/models/track.dart';
|
||||||
import 'package:fip_parser_ui/globals.dart';
|
|
||||||
import 'package:fip_parser_ui/providers/home.dart';
|
import 'package:fip_parser_ui/providers/home.dart';
|
||||||
import 'package:fip_parser_ui/providers/player.dart';
|
import 'package:fip_parser_ui/providers/player.dart';
|
||||||
import 'package:flutter/material.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:miniplayer/miniplayer.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:retry/retry.dart';
|
|
||||||
import 'package:youtube_explode_dart/youtube_explode_dart.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 {
|
class HomeScreen extends StatefulWidget {
|
||||||
const HomeScreen({Key? key, required this.title}) : super(key: key);
|
const HomeScreen({Key? key, required this.title}) : super(key: key);
|
||||||
final String title;
|
final String title;
|
||||||
|
@ -29,107 +17,147 @@ class HomeScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HomeScreenState extends State<HomeScreen> {
|
class _HomeScreenState extends State<HomeScreen> {
|
||||||
|
String radio = 'fip_groove';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
HomeProvider hp = Provider.of<HomeProvider>(context, listen: false);
|
||||||
final MiniplayerController controller = MiniplayerController();
|
final MiniplayerController controller = MiniplayerController();
|
||||||
const hours = 8;
|
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Scaffold(
|
Scaffold(
|
||||||
body: SingleChildScrollView(
|
body: RawScrollbar(
|
||||||
child: Column(
|
thumbColor: Colors.grey[600],
|
||||||
children: <Widget>[
|
radius: const Radius.circular(20),
|
||||||
Container(
|
thickness: 12,
|
||||||
color: Colors.grey[900],
|
thumbVisibility: true,
|
||||||
height: 50,
|
mainAxisMargin: 70,
|
||||||
child: Center(
|
child: SingleChildScrollView(
|
||||||
child: DropdownButton(
|
child: Column(
|
||||||
dropdownColor: Colors.grey[900],
|
children: <Widget>[
|
||||||
value: radio,
|
Container(
|
||||||
style: TextStyle(fontSize: 15, color: Colors.grey[300]),
|
color: Colors.grey[900],
|
||||||
underline: const SizedBox(),
|
height: 50,
|
||||||
iconSize: 22,
|
child: Row(
|
||||||
onChanged: (String? newRadio) {
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
setState(() {
|
children: [
|
||||||
radio = newRadio!;
|
DropdownButton(
|
||||||
// getTracks(radio, hours);
|
dropdownColor: Colors.grey[900],
|
||||||
});
|
value: radio,
|
||||||
},
|
style: TextStyle(
|
||||||
items: radioList),
|
fontSize: 15, color: Colors.grey[300]),
|
||||||
),
|
underline: const SizedBox(),
|
||||||
),
|
iconSize: 22,
|
||||||
FutureBuilder<List<Track>>(
|
onChanged: (String? newRadio) {
|
||||||
future: getTracks(radio, hours),
|
setState(() {
|
||||||
builder: (
|
radio = newRadio!;
|
||||||
BuildContext context,
|
});
|
||||||
AsyncSnapshot<List<Track>> snapshot,
|
},
|
||||||
) {
|
items: hp.radioList),
|
||||||
print(snapshot.connectionState);
|
const SizedBox(width: 50),
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
DropdownButton(
|
||||||
return Stack(children: [
|
dropdownColor: Colors.grey[900],
|
||||||
Container(
|
value: hp.userPageNbr.toString(),
|
||||||
height: 10000,
|
style: TextStyle(
|
||||||
width: 10000,
|
fontSize: 15, color: Colors.grey[300]),
|
||||||
color: const Color(0xFF121212)),
|
underline: const SizedBox(),
|
||||||
Center(
|
iconSize: 22,
|
||||||
child: Column(children: [
|
onChanged: (String? newPageNumber) {
|
||||||
const SizedBox(height: 20),
|
setState(() {
|
||||||
SizedBox(
|
hp.userPageNbr = int.parse(newPageNumber!);
|
||||||
height: 30,
|
});
|
||||||
width: 30,
|
},
|
||||||
child: CircularProgressIndicator(
|
items: hp.pageList),
|
||||||
color: Colors.amber[100],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
]);
|
FutureBuilder<List<Track>>(
|
||||||
} else if (snapshot.connectionState ==
|
future: hp.getTracks(radio),
|
||||||
ConnectionState.done) {
|
builder: (
|
||||||
if (snapshot.hasError) {
|
BuildContext context,
|
||||||
return const Text('Error');
|
AsyncSnapshot<List<Track>> snapshot,
|
||||||
} else if (snapshot.hasData) {
|
) {
|
||||||
return Table(
|
print(snapshot.connectionState);
|
||||||
columnWidths: {
|
if (snapshot.connectionState ==
|
||||||
0: const FlexColumnWidth(4),
|
ConnectionState.waiting) {
|
||||||
1: const FlexColumnWidth(2),
|
return Stack(children: [
|
||||||
2: const FlexColumnWidth(1),
|
Container(
|
||||||
},
|
height: 10000,
|
||||||
defaultVerticalAlignment:
|
width: 10000,
|
||||||
TableCellVerticalAlignment.middle,
|
color: const Color(0xFF121212)),
|
||||||
children: snapshot.data!
|
Center(
|
||||||
.map((item) => _buildTableRow(item, context))
|
child: Column(children: [
|
||||||
.toList()
|
const SizedBox(height: 20),
|
||||||
..insert(
|
SizedBox(
|
||||||
0,
|
height: 30,
|
||||||
_buildTableRow(
|
width: 30,
|
||||||
Track(
|
child: CircularProgressIndicator(
|
||||||
number: -1,
|
color: Colors.amber[100],
|
||||||
title: 'TITRE',
|
),
|
||||||
artiste: 'ARTISTE',
|
),
|
||||||
album: 'ALBUM',
|
]),
|
||||||
id: 'URL'),
|
|
||||||
context),
|
|
||||||
),
|
),
|
||||||
);
|
]);
|
||||||
} else {
|
} else if (snapshot.connectionState ==
|
||||||
return const Text('Empty data');
|
ConnectionState.done) {
|
||||||
}
|
if (snapshot.hasError) {
|
||||||
} else {
|
return Stack(children: [
|
||||||
return Text('State: ${snapshot.connectionState}');
|
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, _) {
|
Consumer<PlayerProvider>(builder: (context, playerProvider, _) {
|
||||||
TextEditingController trackTitle = TextEditingController();
|
TextEditingController trackTitle = TextEditingController();
|
||||||
TextEditingController trackArtiste = TextEditingController();
|
TextEditingController trackArtiste = TextEditingController();
|
||||||
trackTitle.text = currentTrack?.title ?? '';
|
trackTitle.text = hp.currentTrack?.title ?? '';
|
||||||
trackArtiste.text = currentTrack?.artiste ?? '';
|
trackArtiste.text = hp.currentTrack?.artiste ?? '';
|
||||||
return Miniplayer(
|
return Miniplayer(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
backgroundColor: Colors.grey[900]!,
|
backgroundColor: Colors.grey[900]!,
|
||||||
|
@ -139,12 +167,13 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
return Row(
|
return Row(
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
currentTrack?.id != null
|
hp.currentTrack?.imageUrl != '' &&
|
||||||
|
hp.currentTrack?.imageUrl != null
|
||||||
? Image.network(
|
? Image.network(
|
||||||
'https://img.youtube.com/vi/${currentTrack?.id}/1.jpg',
|
hp.currentTrack!.imageUrl!,
|
||||||
width: 93,
|
width: 70,
|
||||||
)
|
)
|
||||||
: const SizedBox(width: 100),
|
: const SizedBox(width: 70),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
@ -190,30 +219,33 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
Column(children: [
|
Column(children: [
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Row(children: [
|
Row(children: [
|
||||||
|
const SizedBox(width: 70),
|
||||||
Column(children: [
|
Column(children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
icon: Icon(Icons.skip_previous,
|
icon: Icon(Icons.skip_previous,
|
||||||
color: Colors.grey[500], size: 32),
|
color: Colors.grey[500], size: 32),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (currentTrack != null &&
|
if (hp.currentTrack != null &&
|
||||||
currentTrack!.number > 1) {
|
hp.currentTrack!.number > 1) {
|
||||||
currentTrack = trackList.firstWhere((element) =>
|
hp.currentTrack = hp.trackList.firstWhere(
|
||||||
element.number == currentTrack!.number - 1);
|
(element) =>
|
||||||
playTrack(context, currentTrack!);
|
element.number ==
|
||||||
|
hp.currentTrack!.number - 1);
|
||||||
|
hp.playTrack(context, hp.currentTrack!);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
const SizedBox(height: 7),
|
|
||||||
]),
|
]),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 1),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
player?.playing ?? false
|
hp.player?.playing ?? false
|
||||||
? player?.pause()
|
? hp.player?.pause()
|
||||||
: player?.play();
|
: hp.player?.play();
|
||||||
playerProvider.reload();
|
playerProvider.reload();
|
||||||
},
|
},
|
||||||
child: Icon(
|
child: Icon(
|
||||||
player?.playing ?? false
|
hp.player?.playing ?? false
|
||||||
? Icons.pause
|
? Icons.pause
|
||||||
: Icons.play_arrow,
|
: Icons.play_arrow,
|
||||||
color: Colors.grey[900],
|
color: Colors.grey[900],
|
||||||
|
@ -227,25 +259,29 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
),
|
),
|
||||||
Column(children: [
|
Column(children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
icon: Icon(Icons.skip_next,
|
icon: Icon(Icons.skip_next,
|
||||||
color: Colors.grey[500], size: 32),
|
color: Colors.grey[500], size: 32),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (currentTrack != null &&
|
if (hp.currentTrack != null &&
|
||||||
currentTrack!.number <
|
hp.currentTrack!.number <
|
||||||
trackList.last.number) {
|
hp.trackList.last.number) {
|
||||||
currentTrack = trackList.firstWhere((element) =>
|
hp.currentTrack = hp.trackList.firstWhere(
|
||||||
element.number == currentTrack!.number + 1);
|
(element) =>
|
||||||
playTrack(context, currentTrack!);
|
element.number ==
|
||||||
|
hp.currentTrack!.number + 1);
|
||||||
|
hp.playTrack(context, hp.currentTrack!);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
const SizedBox(height: 7),
|
// const SizedBox(height: 7),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Row(children: [
|
Row(children: [
|
||||||
|
const SizedBox(width: 70),
|
||||||
Text(
|
Text(
|
||||||
timeFormat(
|
timeFormat(
|
||||||
player?.position ?? const Duration(seconds: 0)),
|
hp.player?.position ?? const Duration(seconds: 0)),
|
||||||
style: TextStyle(color: Colors.grey[500], fontSize: 12),
|
style: TextStyle(color: Colors.grey[500], fontSize: 12),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 3),
|
const SizedBox(width: 3),
|
||||||
|
@ -261,13 +297,14 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
width: 300,
|
width: 300,
|
||||||
child: Slider(
|
child: Slider(
|
||||||
value: double.parse(
|
value: double.parse(
|
||||||
player?.position.inSeconds.toString() ?? '0'),
|
hp.player?.position.inSeconds.toString() ??
|
||||||
|
'0'),
|
||||||
max: double.parse(
|
max: double.parse(
|
||||||
player?.duration.inSeconds.toString() ??
|
hp.player?.duration.inSeconds.toString() ??
|
||||||
'2000'),
|
'2000'),
|
||||||
onChanged: (double value) {
|
onChanged: (double value) {
|
||||||
if (player?.position != null) {
|
if (hp.player?.position != null) {
|
||||||
player!.position =
|
hp.player!.position =
|
||||||
Duration(seconds: value.toInt());
|
Duration(seconds: value.toInt());
|
||||||
playerProvider.reload();
|
playerProvider.reload();
|
||||||
}
|
}
|
||||||
|
@ -280,7 +317,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
const SizedBox(width: 3),
|
const SizedBox(width: 3),
|
||||||
Text(
|
Text(
|
||||||
timeFormat(
|
timeFormat(
|
||||||
player?.duration ?? const Duration(seconds: 0)),
|
hp.player?.duration ?? const Duration(seconds: 0)),
|
||||||
style: TextStyle(color: Colors.grey[500], fontSize: 12),
|
style: TextStyle(color: Colors.grey[500], fontSize: 12),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
|
@ -297,11 +334,11 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 130,
|
width: 130,
|
||||||
child: Slider(
|
child: Slider(
|
||||||
value: player?.volume ?? 1,
|
value: hp.player?.volume ?? 1,
|
||||||
max: 1,
|
max: 1,
|
||||||
onChanged: (double value) {
|
onChanged: (double value) {
|
||||||
if (player?.volume != null) {
|
if (hp.player?.volume != null) {
|
||||||
player!.volume = value;
|
hp.player!.volume = value;
|
||||||
playerProvider.reload();
|
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) {
|
TableRow _buildTableRow(Track track, BuildContext context) {
|
||||||
DownloadProvider downloadProvider =
|
DownloadProvider downloadProvider =
|
||||||
Provider.of<DownloadProvider>(context, listen: false);
|
Provider.of<DownloadProvider>(context, listen: false);
|
||||||
|
HomeProvider hp = Provider.of<HomeProvider>(context, listen: false);
|
||||||
|
int isDownloading = -1;
|
||||||
|
|
||||||
final textStyle = TextStyle(
|
final textStyle = TextStyle(
|
||||||
fontWeight: track.number == -1 ? FontWeight.w200 : FontWeight.normal,
|
fontWeight: track.number == -1 ? FontWeight.w200 : FontWeight.normal,
|
||||||
|
@ -477,30 +391,38 @@ TableRow _buildTableRow(Track track, BuildContext context) {
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
if (track.number != -1) {
|
if (track.number != -1) {
|
||||||
playTrack(context, track);
|
hp.playTrack(context, track);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Row(children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
track.number != -1
|
||||||
children: [
|
? track.image == ''
|
||||||
Consumer<PlayerProvider>(
|
? const SizedBox(width: 40)
|
||||||
builder: (context, playerProvider, _) {
|
: Image.memory(base64Decode(track.image!), height: 40)
|
||||||
return Text(track.title,
|
: const SizedBox(),
|
||||||
style: TextStyle(
|
const SizedBox(width: 10),
|
||||||
fontWeight: FontWeight.normal,
|
Column(
|
||||||
color: currentTrack == track
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
? Colors.green
|
children: [
|
||||||
: Colors.grey[350],
|
Consumer<PlayerProvider>(
|
||||||
fontSize: 14));
|
builder: (context, playerProvider, _) {
|
||||||
}),
|
return Text(track.title,
|
||||||
const SizedBox(height: 5),
|
style: TextStyle(
|
||||||
if (track.number != -1)
|
fontWeight: FontWeight.normal,
|
||||||
Text(track.artiste,
|
color: hp.currentTrack == track
|
||||||
style: TextStyle(
|
? Colors.green
|
||||||
fontWeight: FontWeight.normal,
|
: Colors.grey[350],
|
||||||
color: Colors.grey[500],
|
fontSize: 14));
|
||||||
fontSize: 13))
|
}),
|
||||||
]),
|
const SizedBox(height: 2),
|
||||||
|
if (track.number != -1)
|
||||||
|
Text(track.artiste,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: Colors.grey[500],
|
||||||
|
fontSize: 13))
|
||||||
|
]),
|
||||||
|
]),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
TableCell(
|
TableCell(
|
||||||
|
@ -524,7 +446,7 @@ TableRow _buildTableRow(Track track, BuildContext context) {
|
||||||
track.id = resultUrl.first.id.value;
|
track.id = resultUrl.first.id.value;
|
||||||
}
|
}
|
||||||
if (track.id != null) {
|
if (track.id != null) {
|
||||||
downloadMusic(context, track);
|
hp.downloadMusic(context, track);
|
||||||
}
|
}
|
||||||
isDownloading = -1;
|
isDownloading = -1;
|
||||||
yt.close();
|
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 timeFormat(Duration d) {
|
||||||
String dd = d.toString().split('.').first;
|
String dd = d.toString().split('.').first;
|
||||||
String minutes = dd.toString().split(':')[1];
|
String minutes = dd.toString().split(':')[1];
|
||||||
|
|
102
pubspec.lock
102
pubspec.lock
|
@ -56,7 +56,7 @@ packages:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.16.0"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -98,7 +98,7 @@ packages:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.3.0"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -142,69 +142,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
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:
|
html:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -232,7 +169,7 @@ packages:
|
||||||
name: js
|
name: js
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.3"
|
version: "0.6.4"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -309,7 +246,7 @@ packages:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.3"
|
version: "0.1.4"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -333,20 +270,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
normalize:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: normalize
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.6.0+1"
|
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.8.1"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -431,13 +361,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.2"
|
version: "6.0.2"
|
||||||
retry:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: retry
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.0"
|
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -456,7 +379,7 @@ packages:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.1"
|
version: "1.8.2"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -491,7 +414,7 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.8"
|
version: "0.4.9"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -568,14 +491,7 @@ packages:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
web_socket_channel:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: web_socket_channel
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -605,5 +521,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.11.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.16.2 <3.0.0"
|
dart: ">=2.17.0-0 <3.0.0"
|
||||||
flutter: ">=2.10.0"
|
flutter: ">=2.10.0"
|
||||||
|
|
|
@ -11,9 +11,7 @@ dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
graphql: ^5.0.0
|
|
||||||
youtube_explode_dart: ^1.10.9+1
|
youtube_explode_dart: ^1.10.9+1
|
||||||
retry: ^3.1.0
|
|
||||||
url_launcher: ^6.1.0
|
url_launcher: ^6.1.0
|
||||||
http: ^0.13.4
|
http: ^0.13.4
|
||||||
kplayer: ^0.1.12
|
kplayer: ^0.1.12
|
||||||
|
|
|
@ -7,6 +7,9 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|
||||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
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 $<TARGET_FILE:${plugin}_plugin>)
|
||||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||||
endforeach(plugin)
|
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)
|
||||||
|
|
Loading…
Reference in New Issue