Compare commits

..

3 Commits

Author SHA1 Message Date
poka 9b821402df regenerate windows folder 2023-04-15 22:16:54 +02:00
poka 11c90c6d7f upgrade flutter 3.7 2023-04-15 22:10:29 +02:00
poka ed996df71e change var to final 2022-08-07 14:21:33 +02:00
29 changed files with 607 additions and 812 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,122 +1,122 @@
{
"images": [
"images" : [
{
"filename": "Icon-App-20x20@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "20x20"
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-20x20@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "20x20"
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"filename": "Icon-App-29x29@1x.png",
"idiom": "iphone",
"scale": "1x",
"size": "29x29"
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"filename": "Icon-App-29x29@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "29x29"
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-29x29@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "29x29"
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"filename": "Icon-App-40x40@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "40x40"
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-40x40@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "40x40"
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"filename": "Icon-App-60x60@2x.png",
"idiom": "iphone",
"scale": "2x",
"size": "60x60"
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-60x60@3x.png",
"idiom": "iphone",
"scale": "3x",
"size": "60x60"
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"filename": "Icon-App-20x20@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "20x20"
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"filename": "Icon-App-20x20@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "20x20"
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-29x29@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "29x29"
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"filename": "Icon-App-29x29@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "29x29"
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-40x40@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "40x40"
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"filename": "Icon-App-40x40@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "40x40"
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-76x76@1x.png",
"idiom": "ipad",
"scale": "1x",
"size": "76x76"
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"filename": "Icon-App-76x76@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "76x76"
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-83.5x83.5@2x.png",
"idiom": "ipad",
"scale": "2x",
"size": "83.5x83.5"
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"filename": "Icon-App-1024x1024@1x.png",
"idiom": "ios-marketing",
"scale": "1x",
"size": "1024x1024"
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info": {
"author": "icons_launcher",
"version": 1
"info" : {
"version" : 1,
"author" : "xcode"
}
}
}

View File

@ -1,14 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
TextStyle globalTextStyle = TextStyle(color: Colors.grey[350]);
const proxyHeader =
kDebugMode || !kIsWeb ? 'https://' : 'http://127.0.0.1:8080/';
// Logger
final log = Logger();
// context
late BuildContext homeContext;
late BuildContext playerContext;

View File

@ -1,4 +1,3 @@
import 'package:fipy/providers/download.dart';
import 'package:fipy/providers/home.dart';
import 'package:fipy/providers/player.dart';
import 'package:fipy/screens/home.dart';
@ -24,8 +23,7 @@ class FipyApp extends StatelessWidget {
child: MaterialApp(
title: 'Fipy',
theme: ThemeData(
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.blue)
.copyWith(background: Colors.grey[900])),
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.blue).copyWith(background: Colors.grey[900])),
home: const HomeScreen(title: 'Fipy'),
),
);

View File

@ -1,61 +0,0 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:io';
import 'package:fipy/globals.dart';
import 'package:fipy/models/track.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
class DownloadProvider with ChangeNotifier {
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 fileName = '${track.title} - ${track.artiste}'
.replaceAll('\\', '')
.replaceAll('/', '')
.replaceAll(':', '')
.replaceAll('*', '')
.replaceAll('?', '')
.replaceAll('"', '')
.replaceAll('<', '')
.replaceAll('>', '')
.replaceAll('|', '');
if (!kIsWeb) {
final filePath = Platform.isAndroid
? Directory('/storage/emulated/0/Download')
: await getDownloadsDirectory();
var file = File('${filePath!.path}/$fileName.webm');
var fileStream = file.openWrite();
await stream.pipe(fileStream);
await fileStream.flush();
await fileStream.close();
yt.close();
// convertToMp3(file.path);
track.file = file;
ScaffoldMessenger.of(playerContext).showSnackBar(
SnackBar(
content:
SizedBox(height: 90, child: Text('Son téléchargé: ${file.path}')),
),
);
log.d(file.path);
}
}
void reload() {
notifyListeners();
}
}

View File

@ -1,7 +1,12 @@
// ignore_for_file: avoid_print
import 'dart:convert';
import 'package:fipy/globals.dart';
import 'package:flutter/foundation.dart';
import 'package:universal_io/io.dart';
import 'package:fipy/models/track.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
import 'package:fipy/providers/player.dart';
import 'package:provider/provider.dart';
@ -31,13 +36,12 @@ class HomeProvider with ChangeNotifier {
"Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS",
'Content-Type': 'text/plain'
});
if (res.statusCode == 200) {
Map body = jsonDecode(res.body);
if (body['next'] != null) {
cursor = body['next'];
} else {
log.d('Page N°$pageNbr');
print('Page N°$pageNbr');
stop = true;
}
@ -66,12 +70,12 @@ class HomeProvider with ChangeNotifier {
return trackList;
}
Future playTrack(Track track) async {
var yt = YoutubeExplode();
Future playTrack(BuildContext context, Track track) async {
final yt = YoutubeExplode();
PlayerProvider playerProvider =
Provider.of<PlayerProvider>(homeContext, listen: false);
Provider.of<PlayerProvider>(context, listen: false);
HomeProvider homeProvider =
Provider.of<HomeProvider>(homeContext, listen: false);
Provider.of<HomeProvider>(context, listen: false);
// track = trackList[track.number - 1];
currentTrack = track;
@ -94,11 +98,11 @@ class HomeProvider with ChangeNotifier {
try {
final source = UrlSource(
"https://${invidiousUrl[3]}/latest_version?id=${track.id}&itag=140&local=true&listen=1");
log.d(source.url);
"https://${invidiousUrl[1]}/latest_version?id=${track.id}&itag=140&local=true&listen=1");
print(source.url);
await player.play(source);
} catch (e) {
log.d('Play error: $e');
print('Play error: $e');
}
Future.delayed(const Duration(milliseconds: 50));
@ -113,10 +117,10 @@ class HomeProvider with ChangeNotifier {
});
player.onPlayerComplete.listen((event) {
var nextTrack =
final nextTrack =
trackList.firstWhere((element) => element.number == track.number + 1);
currentTrack = nextTrack;
playTrack(nextTrack);
playTrack(context, nextTrack);
});
playerProvider.reload();
@ -124,31 +128,44 @@ class HomeProvider with ChangeNotifier {
yt.close();
}
Future nextTrack() async {
if (currentTrack != null && currentTrack!.number < trackList.last.number) {
currentTrack = trackList
.firstWhere((element) => element.number == currentTrack!.number + 1);
await playTrack(currentTrack!);
Future downloadMusic(BuildContext context, Track track) async {
final yt = YoutubeExplode();
final manifest = await yt.videos.streamsClient.getManifest(track.id);
final streamManifest = StreamManifest(manifest.streams);
final streamInfo = streamManifest.audioOnly.withHighestBitrate();
final stream = yt.videos.streamsClient.get(streamInfo);
final fileName = '${track.title} - ${track.artiste}'
.replaceAll('\\', '')
.replaceAll('/', '')
.replaceAll(':', '')
.replaceAll('*', '')
.replaceAll('?', '')
.replaceAll('"', '')
.replaceAll('<', '')
.replaceAll('>', '')
.replaceAll('|', '');
if (!kIsWeb) {
final filePath = Platform.isAndroid
? Directory('/storage/emulated/0/Download')
: await getDownloadsDirectory();
final file = File('${filePath!.path}/$fileName.webm');
final fileStream = file.openWrite();
await stream.pipe(fileStream);
await fileStream.flush();
await fileStream.close();
yt.close();
// convertToMp3(file.path);
track.file = file;
print(file.path);
}
}
Future backTrack() async {
if (currentTrack != null && currentTrack!.number > 1) {
currentTrack = trackList
.firstWhere((element) => element.number == currentTrack!.number - 1);
await playTrack(currentTrack!);
}
}
void resumePlay() {
if (currentTrack == null) {
currentTrack = trackList.first;
playTrack(currentTrack!);
} else {
player.state.name == 'playing' ? player.pause() : player.resume();
}
}
List<DropdownMenuItem<String>> get radioList {
List<DropdownMenuItem<String>> menuItems = [
const DropdownMenuItem(value: "fip", child: Text("FIP")),
@ -160,7 +177,8 @@ class HomeProvider with ChangeNotifier {
const DropdownMenuItem(value: "fip_reggae", child: Text("Reggae")),
const DropdownMenuItem(value: "fip_world", child: Text("World")),
const DropdownMenuItem(
value: "fip_nouveautes", child: Text("Nouveautés")),
value: "fip_nouveautes",
child: Text("Nouveautés")),
];
return menuItems;
}
@ -180,3 +198,34 @@ class HomeProvider with ChangeNotifier {
notifyListeners();
}
}
class DownloadProvider with ChangeNotifier {
void reload() {
notifyListeners();
}
}
// Future convertToMp3(String audioFile) async {
// audioFile = 'E:\\Téléchargements/test.webm';
// final command = FfmpegCommand(
// inputs: [FfmpegInput.asset(audioFile)],
// args: [
// // CliArg(name: 'i', value: audioFile),
// const CliArg(name: 'ab', value: '320'),
// const CliArg(name: 'ar', value: '44100'),
// ],
// filterGraph: const FilterGraph(
// chains: [
// FilterChain(
// inputs: [],
// filters: [],
// outputs: [],
// ),
// ],
// ),
// outputFilepath: "E:\\Téléchargements/test.mp3",
// );
// // Execute command
// await Ffmpeg().run(command);
// }

View File

@ -1,15 +1,16 @@
// ignore_for_file: prefer_const_literals_to_create_immutables, avoid_print
import 'dart:convert';
import 'package:fipy/globals.dart';
import 'package:fipy/models/track.dart';
import 'package:fipy/providers/home.dart';
import 'package:fipy/providers/player.dart';
import 'package:fipy/widgets/download_track.dart';
import 'package:fipy/widgets/player/player.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:miniplayer/miniplayer.dart';
import 'package:provider/provider.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
// import 'dart:math';
// import 'package:flutter/rendering.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key, required this.title}) : super(key: key);
@ -23,169 +24,386 @@ class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
homeContext = context;
HomeProvider hp = Provider.of<HomeProvider>(context, listen: false);
final MiniplayerController controller = MiniplayerController();
return RawKeyboardListener(
autofocus: true,
focusNode: FocusNode(),
onKey: (RawKeyEvent event) {
// log.d(event.data.logicalKey.keyId);
if (event.runtimeType == RawKeyDownEvent) //Enter Key ID from keyboard
if (event.runtimeType == RawKeyDownEvent &&
event.data.logicalKey.keyId == 32) //Enter Key ID from keyboard
{
switch (event.data.logicalKey.keyId) {
case 32:
case 112:
case 4294969861:
hp.resumePlay();
break;
case 110:
case 94489280688:
hp.nextTrack();
break;
case 98:
case 94489280689:
hp.backTrack();
break;
}
hp.player.state.name == 'playing'
? hp.player.pause()
: hp.player.resume();
}
},
child: Stack(
children: <Widget>[
Scaffold(
backgroundColor: Colors.black,
body: 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),
]),
),
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],
),
),
body: RawScrollbar(
thumbColor: Colors.grey[600],
radius: const Radius.circular(20),
thickness: 12,
// thumbVisibility: true,
mainAxisMargin: 70,
child: SingleChildScrollView(
// controller: AdjustableScrollController(40),
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 Stack(children: [
Container(
height: 10000,
width: 10000,
color: const Color(0xFF121212)),
Center(
child: Column(children: [
const SizedBox(height: 20),
Text(
'Error: ${snapshot.error}',
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))
.toList()
..insert(
0,
_buildTableRow(Track(
number: -1,
title: 'TITRE',
artiste: 'ARTISTE',
album: 'ALBUM',
id: 'URL')),
),
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}',
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)
],
),
const SizedBox(height: 70)
],
),
),
)),
),
const Player()
Consumer<PlayerProvider>(builder: (context, playerProvider, _) {
TextEditingController trackTitle = TextEditingController();
TextEditingController trackArtiste = TextEditingController();
trackTitle.text = hp.currentTrack?.title ?? '';
trackArtiste.text = hp.currentTrack?.artiste ?? '';
return Miniplayer(
controller: controller,
backgroundColor: Colors.grey[900]!,
minHeight: 70,
maxHeight: 70,
builder: (height, percentage) {
return Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
hp.currentTrack?.imageUrl != '' &&
hp.currentTrack?.imageUrl != null
? Image.network(
hp.currentTrack!.imageUrl!,
width: 70,
)
: const SizedBox(width: 70),
Expanded(
child: Column(children: [
SizedBox(
height: 20,
child: TextField(
enabled: false,
maxLines: 1,
controller: trackTitle,
decoration: const InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: EdgeInsets.only(left: 15),
),
style: TextStyle(
fontSize: 14,
color: Colors.grey[300],
),
),
),
SizedBox(
height: 20,
child: TextField(
enabled: false,
maxLines: 1,
controller: trackArtiste,
decoration: const InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: EdgeInsets.only(left: 15),
),
style: TextStyle(
fontSize: 12,
color: Colors.grey[500],
),
),
),
]),
),
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 (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(width: 1),
ElevatedButton(
onPressed: () {
hp.player.state.name == 'playing'
? hp.player.pause()
: hp.player.resume();
// playerProvider.reload();
},
style: ElevatedButton.styleFrom(
foregroundColor: Colors.grey[900],
backgroundColor: Colors.grey[300],
shape: const CircleBorder(),
padding: const EdgeInsets.all(12),
),
child: Icon(
hp.player.state.name == 'playing'
? Icons.pause
: Icons.play_arrow,
color: Colors.grey[900],
size: 30),
),
Column(children: [
IconButton(
padding: const EdgeInsets.all(0),
icon: Icon(Icons.skip_next,
color: Colors.grey[500], size: 32),
onPressed: () {
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 Spacer(),
Row(children: [
const SizedBox(width: 70),
Text(
timeFormat(hp.currentTrack?.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(hp
.currentTrack?.position?.inSeconds
.toString() ??
'0'),
max: double.parse(hp
.currentTrack?.duration?.inSeconds
.toString() ??
'2000'),
onChanged: (double value) {
if (hp.currentTrack?.position != null) {
hp.player.seek(
Duration(seconds: value.toInt()),
);
playerProvider.reload();
}
},
activeColor: Colors.grey[400],
inactiveColor: Colors.grey[700],
),
),
),
const SizedBox(width: 3),
Text(
timeFormat(hp.currentTrack?.duration ??
const Duration(seconds: 0)),
style:
TextStyle(color: Colors.grey[500], fontSize: 12),
),
]),
const Spacer(),
]),
const Spacer(),
SliderTheme(
data: const SliderThemeData(
thumbShape:
RoundSliderThumbShape(enabledThumbRadius: 7),
overlayShape:
RoundSliderThumbShape(enabledThumbRadius: 8),
trackHeight: 2.5,
),
child: SizedBox(
width: 130,
child: Slider(
value: hp.currentVolume,
max: 1,
onChanged: (double value) async {
hp.currentVolume = value;
await hp.player.setVolume(value);
playerProvider.reload();
},
activeColor: Colors.grey[400],
inactiveColor: Colors.grey[700],
),
),
),
const SizedBox(width: 20)
],
);
},
);
})
],
),
);
}
}
TableRow _buildTableRow(Track track) {
final hp = Provider.of<HomeProvider>(homeContext, listen: false);
Widget trackLine(Track track) {
return SizedBox(
height: 30,
child: Row(
children: [Text('${track.title} - ${track.artiste}')],
),
);
}
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,
color: Colors.grey[500],
fontSize: track.number == -1 ? 15 : 14);
const rowPadding = EdgeInsets.all(10);
final yt = YoutubeExplode();
return TableRow(
decoration: const BoxDecoration(
@ -198,7 +416,16 @@ TableRow _buildTableRow(Track track) {
child: InkWell(
onTap: () async {
if (track.number != -1) {
hp.playTrack(track);
// FutureBuilder<void>(
// future: hp.playTrack(context, track),
// builder: (
// BuildContext context,
// AsyncSnapshot<void> snapshot,
// ) {
// return const SizedBox();
// },
// );
hp.playTrack(context, track);
}
},
child: Row(children: [
@ -243,9 +470,69 @@ TableRow _buildTableRow(Track track) {
),
TableCell(
child: Padding(
padding: rowPadding,
child: DownloadTrack(track: track),
),
padding: rowPadding,
child: InkWell(
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;
}
if (track.id != null) {
hp.downloadMusic(context, track);
}
isDownloading = -1;
yt.close();
downloadProvider.reload();
},
child: track.number == -1
? Text('TÉLÉCHARGER', style: textStyle)
: Consumer<DownloadProvider>(builder: (context, _, __) {
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]),
]);
}))),
),
]);
}
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';
}
// class AdjustableScrollController extends ScrollController {
// AdjustableScrollController([int extraScrollSpeed = 40]) {
// // super.initialScrollOffset = 50;
// super.addListener(() {
// ScrollDirection scrollDirection = super.position.userScrollDirection;
// if (scrollDirection != ScrollDirection.idle) {
// double scrollEnd = super.offset +
// (scrollDirection == ScrollDirection.reverse
// ? extraScrollSpeed
// : -extraScrollSpeed);
// scrollEnd = min(super.position.maxScrollExtent,
// max(super.position.minScrollExtent, scrollEnd));
// WidgetsBinding.instance.addPostFrameCallback((_) {
// if (super.hasClients) jumpTo(scrollEnd);
// });
// }
// });
// }
// }

View File

@ -1,61 +0,0 @@
import 'package:fipy/models/track.dart';
import 'package:fipy/providers/download.dart';
import 'package:fipy/providers/home.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
class DownloadTrack extends StatelessWidget {
const DownloadTrack({Key? key, this.track}) : super(key: key);
final Track? track;
@override
Widget build(BuildContext context) {
final hp = Provider.of<HomeProvider>(context, listen: true);
final dl = Provider.of<DownloadProvider>(context, listen: false);
int isDownloading = -2;
final yt = YoutubeExplode();
final track = this.track ??
hp.currentTrack ??
Track(number: -3, title: 'title', artiste: 'artiste');
return InkWell(
onTap: track.number == -3
? null
: () async {
isDownloading = track.number;
dl.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;
}
if (track.id != null) {
// ignore: use_build_context_synchronously
await dl.downloadMusic(context, track);
}
isDownloading = -2;
yt.close();
dl.reload();
},
child: Consumer<DownloadProvider>(builder: (context, _, __) {
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]),
]);
}));
}
}

View File

@ -1,50 +0,0 @@
import 'package:fipy/globals.dart';
import 'package:fipy/providers/home.dart';
import 'package:fipy/widgets/download_track.dart';
import 'package:fipy/widgets/player/player_controls.dart';
import 'package:fipy/widgets/player/player_title.dart';
import 'package:fipy/widgets/player/player_volume.dart';
import 'package:flutter/material.dart';
import 'package:miniplayer/miniplayer.dart';
import 'package:provider/provider.dart';
class Player extends StatelessWidget {
const Player({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
playerContext = context;
final controller = MiniplayerController();
final hp = Provider.of<HomeProvider>(context, listen: false);
return Miniplayer(
controller: controller,
backgroundColor: Colors.grey[900]!,
minHeight: 70,
maxHeight: 70,
builder: (height, percentage) {
return Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
hp.currentTrack?.imageUrl != '' && hp.currentTrack?.imageUrl != null
? Image.network(
hp.currentTrack!.imageUrl!,
width: 70,
)
: const SizedBox(width: 70),
const PlayerTitle(),
const SizedBox(width: 90),
const PlayerControls(),
const Spacer(),
const DownloadTrack(),
const SizedBox(width: 20),
const PlayerVolume(),
const SizedBox(width: 20)
],
);
},
);
}
}

View File

@ -1,126 +0,0 @@
import 'package:fipy/providers/home.dart';
import 'package:fipy/providers/player.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class PlayerControls extends StatelessWidget {
const PlayerControls({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final hp = Provider.of<HomeProvider>(context, listen: true);
final playerProvider = Provider.of<PlayerProvider>(context, listen: true);
return 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: () {
hp.backTrack();
}),
]),
const SizedBox(width: 1),
ElevatedButton(
onPressed: () {
hp.resumePlay();
},
style: ElevatedButton.styleFrom(
foregroundColor: Colors.grey[900],
backgroundColor: Colors.grey[300],
shape: const CircleBorder(),
padding: const EdgeInsets.all(12),
),
child: Icon(
hp.player.state.name == 'playing'
? Icons.pause
: Icons.play_arrow,
color: Colors.grey[900],
size: 30),
),
Column(children: [
IconButton(
padding: const EdgeInsets.all(0),
icon: Icon(Icons.skip_next, color: Colors.grey[500], size: 32),
onPressed: () {
hp.nextTrack();
}),
// const SizedBox(height: 7),
]),
]),
const Spacer(),
Row(children: [
const SizedBox(width: 70),
Text(
timeFormat(hp.currentTrack?.position ?? const Duration(seconds: 0)),
style: TextStyle(color: Colors.grey[500], fontSize: 12),
),
const SizedBox(width: 3),
(hp.currentTrack?.duration?.inSeconds ?? -1) <= 0
? Column(
children: [
const SizedBox(width: 300),
hp.currentTrack?.title == null
? Text(
'Choisissez un titre',
style: TextStyle(color: Colors.grey[500]),
)
: const SizedBox(
height: 15,
width: 15,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.orange,
),
),
],
)
: SliderTheme(
data: const SliderThemeData(
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 2),
overlayShape: RoundSliderThumbShape(enabledThumbRadius: 5),
trackHeight: 2,
),
child: SizedBox(
width: 300,
child: Slider(
value: double.parse(
hp.currentTrack?.position?.inSeconds.toString() ?? '0'),
max: double.parse(
hp.currentTrack?.duration?.inSeconds.toString() ??
'2000'),
onChanged: (double value) {
if (hp.currentTrack?.position != null) {
hp.player.seek(
Duration(seconds: value.toInt()),
);
playerProvider.reload();
}
},
activeColor: Colors.grey[400],
inactiveColor: Colors.grey[700],
),
),
),
const SizedBox(width: 3),
Text(
timeFormat(hp.currentTrack?.duration ?? const Duration(seconds: 0)),
style: TextStyle(color: Colors.grey[500], fontSize: 12),
),
]),
const Spacer(),
]);
}
}
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';
}

View File

@ -1,61 +0,0 @@
import 'package:fipy/providers/home.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class PlayerTitle extends StatelessWidget {
const PlayerTitle({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final hp = Provider.of<HomeProvider>(context, listen: true);
final trackTitle = TextEditingController();
final trackArtiste = TextEditingController();
trackTitle.text = hp.currentTrack?.title ?? '';
trackArtiste.text = hp.currentTrack?.artiste ?? '';
return Expanded(
child: Column(children: [
SizedBox(
height: 20,
child: TextField(
enabled: false,
maxLines: 1,
controller: trackTitle,
decoration: const InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: EdgeInsets.only(left: 15),
),
style: TextStyle(
fontSize: 14,
color: Colors.grey[300],
),
),
),
SizedBox(
height: 20,
child: TextField(
enabled: false,
maxLines: 1,
controller: trackArtiste,
decoration: const InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: EdgeInsets.only(left: 15),
),
style: TextStyle(
fontSize: 12,
color: Colors.grey[500],
),
),
),
]),
);
}
}

View File

@ -1,36 +0,0 @@
import 'package:fipy/providers/home.dart';
import 'package:fipy/providers/player.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class PlayerVolume extends StatelessWidget {
const PlayerVolume({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final hp = Provider.of<HomeProvider>(context, listen: false);
final playerProvider = Provider.of<PlayerProvider>(context, listen: true);
return SliderTheme(
data: const SliderThemeData(
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7),
overlayShape: RoundSliderThumbShape(enabledThumbRadius: 8),
trackHeight: 2.5,
),
child: SizedBox(
width: 130,
child: Slider(
value: hp.currentVolume,
max: 1,
onChanged: (double value) async {
hp.currentVolume = value;
await hp.player.setVolume(value);
playerProvider.reload();
},
activeColor: Colors.grey[400],
inactiveColor: Colors.grey[700],
),
),
);
}
}

View File

@ -1,14 +0,0 @@
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
import audioplayers_darwin
import path_provider_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
}

View File

@ -1,11 +0,0 @@
// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=C:\Users\poka\dev\flutter
FLUTTER_APPLICATION_PATH=C:\Users\poka\dev\fipy
COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=0.0.2
FLUTTER_BUILD_NUMBER=2
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=true
TREE_SHAKE_ICONS=false
PACKAGE_CONFIG=.dart_tool/package_config.json

View File

@ -1,12 +0,0 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=C:\Users\poka\dev\flutter"
export "FLUTTER_APPLICATION_PATH=C:\Users\poka\dev\fipy"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=0.0.2"
export "FLUTTER_BUILD_NUMBER=2"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.dart_tool/package_config.json"

View File

@ -1,68 +0,0 @@
{
"images": [
{
"filename": "app_icon_16.png",
"idiom": "mac",
"scale": "1x",
"size": "16x16"
},
{
"filename": "app_icon_32.png",
"idiom": "mac",
"scale": "2x",
"size": "16x16"
},
{
"filename": "app_icon_32.png",
"idiom": "mac",
"scale": "1x",
"size": "32x32"
},
{
"filename": "app_icon_64.png",
"idiom": "mac",
"scale": "2x",
"size": "32x32"
},
{
"filename": "app_icon_128.png",
"idiom": "mac",
"scale": "1x",
"size": "128x128"
},
{
"filename": "app_icon_256.png",
"idiom": "mac",
"scale": "2x",
"size": "128x128"
},
{
"filename": "app_icon_256.png",
"idiom": "mac",
"scale": "1x",
"size": "256x256"
},
{
"filename": "app_icon_512.png",
"idiom": "mac",
"scale": "2x",
"size": "256x256"
},
{
"filename": "app_icon_512.png",
"idiom": "mac",
"scale": "1x",
"size": "512x512"
},
{
"filename": "app_icon_1024.png",
"idiom": "mac",
"scale": "2x",
"size": "512x512"
}
],
"info": {
"author": "icons_launcher",
"version": 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -36,9 +36,13 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
[Files]
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\dart_vlc_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\flutter_windows.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\libvlc.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\libvlccore.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\audioplayers_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\data\*"; DestDir: "{app}\data"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\Users\poka\dev\fipy\build\windows\runner\Release\plugins\*"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]

View File

@ -264,14 +264,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
logger:
dependency: "direct main"
description:
name: logger
sha256: db2ff852ed77090ba9f62d3611e4208a3d11dfa35991a81ae724c113fcb3e3f7
url: "https://pub.dev"
source: hosted
version: "1.3.0"
matcher:
dependency: transitive
description:

View File

@ -2,7 +2,7 @@ name: fipy
description: Advanced FIP radio track explorer
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.0.3+2
version: 0.0.2+1
environment:
sdk: ">=2.16.2 <3.0.0"
@ -27,39 +27,20 @@ dependencies:
universal_io: ^2.0.4
# ffmpeg_cli: ^0.1.0
audioplayers: ^4.0.1
logger: ^1.3.0
# git:
# url: https://github.com/bluefireteam/audioplayers/tree/main/packages/audioplayers_linux
# ref: main
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.1
icons_launcher: ^2.0.6
icons_launcher: ^2.1.0
icons_launcher:
image_path: 'assets/logo_green_dark.jpg'
platforms:
android:
enable: true
ios:
enable: true
windows:
enable: true
linux:
enable: true
macos:
enable: true
# flutter_icons:
# android: true
# ios: true
# # macos: true
# windows: true
# web: true
# image_path: "assets/logo_green_dark.jpg"
flutter_icons:
android: true
ios: true
# macos: true
windows: true
web: true
image_path: "assets/logo_green_dark.jpg"
flutter:
uses-material-design: true

View File

@ -1,8 +0,0 @@
[Desktop Entry]
Name=Flutter Linux App
Comment=Flutter Linux launcher icon
Exec=app_icon
Icon=app_icon.png
Terminal=false
Type=Application
Categories=Entertainment;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 33 KiB