adapt for web prod; add icon; fix a few bugs
|
@ -45,7 +45,5 @@ app.*.map.json
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
# API custom token
|
# Windows build setup
|
||||||
api-token.json
|
|
||||||
assets/
|
|
||||||
Output/
|
Output/
|
|
@ -3,7 +3,7 @@
|
||||||
<application
|
<application
|
||||||
android:label="fip_parser_ui"
|
android:label="fip_parser_ui"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/launcher_icon">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 11 KiB |
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"token": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
||||||
}
|
|
After Width: | Height: | Size: 9.1 KiB |
|
@ -478,4 +478,4 @@
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
}
|
}
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 564 B After Width: | Height: | Size: 467 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 712 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 8.7 KiB |
|
@ -1,4 +1,7 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
late String token;
|
late String token;
|
||||||
TextStyle globalTextStyle = TextStyle(color: Colors.grey[350]);
|
TextStyle globalTextStyle = TextStyle(color: Colors.grey[350]);
|
||||||
|
const proxyHeader =
|
||||||
|
kDebugMode || !kIsWeb ? 'https://' : 'http://127.0.0.1:8080/';
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
import 'dart:convert';
|
|
||||||
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:fip_parser_ui/screens/home.dart';
|
import 'package:fip_parser_ui/screens/home.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:kplayer/kplayer.dart';
|
import 'package:kplayer/kplayer.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
Player.boot();
|
Player.boot();
|
||||||
final tokenFile = await rootBundle.loadString('assets/api-token.json');
|
|
||||||
token = json.decode(tokenFile)['token'];
|
|
||||||
runApp(const FipyApp());
|
runApp(const FipyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import 'dart:io';
|
import 'package:universal_io/io.dart';
|
||||||
|
|
||||||
class Track {
|
class Track {
|
||||||
final int number;
|
final int number;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
// ignore_for_file: avoid_print
|
// ignore_for_file: avoid_print
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'package:fip_parser_ui/globals.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:universal_io/io.dart';
|
||||||
import 'package:fip_parser_ui/models/track.dart';
|
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:kplayer/kplayer.dart';
|
||||||
|
@ -25,11 +27,12 @@ class HomeProvider with ChangeNotifier {
|
||||||
bool stop = false;
|
bool stop = false;
|
||||||
while (pageNbr < userPageNbr && !stop) {
|
while (pageNbr < userPageNbr && !stop) {
|
||||||
final req = Uri.parse(
|
final req = Uri.parse(
|
||||||
'https://www.radiofrance.fr/api/v1.7/stations/fip/webradios/$radio/songs?pageCursor=$cursor');
|
'${proxyHeader}www.radiofrance.fr:443/api/v1.7/stations/fip/webradios/$radio/songs?pageCursor=$cursor');
|
||||||
|
|
||||||
final res = await get(req, headers: {
|
final res = await get(req, headers: {
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
"Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS"
|
"Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS",
|
||||||
|
'Content-Type': 'text/plain'
|
||||||
});
|
});
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
Map body = jsonDecode(res.body);
|
Map body = jsonDecode(res.body);
|
||||||
|
@ -76,22 +79,22 @@ class HomeProvider with ChangeNotifier {
|
||||||
currentTrack = track;
|
currentTrack = track;
|
||||||
|
|
||||||
final currentVolume = player?.volume ?? 1;
|
final currentVolume = player?.volume ?? 1;
|
||||||
player?.dispose();
|
if (player?.playing ?? false) player?.stop();
|
||||||
Future.delayed(const Duration(milliseconds: 5));
|
Future.delayed(const Duration(milliseconds: 5));
|
||||||
|
|
||||||
if (track.file == null) {
|
if (track.id == null) {
|
||||||
if (track.id == null) {
|
final secondMatch = track.artiste == '' ? track.album : track.artiste;
|
||||||
final secondMatch = track.artiste == '' ? track.album : track.artiste;
|
final resultUrl =
|
||||||
final resultUrl =
|
await yt.search.search(track.title + ' ' + secondMatch!);
|
||||||
await yt.search.search(track.title + ' ' + secondMatch!);
|
track.id = resultUrl.first.id.value;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
const invidiousUrl =
|
||||||
|
'yewtu.be'; //yewtu.be vid.puffyan.us invidious.snopyta.org invidious.fdn.fr
|
||||||
|
|
||||||
|
player = Player.network(
|
||||||
|
"https://$invidiousUrl/embed/${track.id}?raw=1&listen=1&quality=dash");
|
||||||
|
print(track.id);
|
||||||
|
|
||||||
player!.volume = currentVolume;
|
player!.volume = currentVolume;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -101,11 +104,12 @@ class HomeProvider with ChangeNotifier {
|
||||||
}
|
}
|
||||||
Future.delayed(const Duration(milliseconds: 500));
|
Future.delayed(const Duration(milliseconds: 500));
|
||||||
player!.callback = (PlayerEvent event) {
|
player!.callback = (PlayerEvent event) {
|
||||||
|
print(event.name);
|
||||||
if (event.name == 'position') {
|
if (event.name == 'position') {
|
||||||
currentTrack!.duration = player!.duration;
|
currentTrack!.duration = player!.duration;
|
||||||
playerProvider.reload();
|
playerProvider.reload();
|
||||||
}
|
}
|
||||||
if (event.name == 'status') {
|
if (event.name == 'status' && player!.position == player!.duration) {
|
||||||
var nextTrack = trackList
|
var nextTrack = trackList
|
||||||
.firstWhere((element) => element.number == track.number + 1);
|
.firstWhere((element) => element.number == track.number + 1);
|
||||||
playTrack(context, nextTrack);
|
playTrack(context, nextTrack);
|
||||||
|
@ -123,8 +127,6 @@ class HomeProvider with ChangeNotifier {
|
||||||
var streamManifest = StreamManifest(manifest.streams);
|
var streamManifest = StreamManifest(manifest.streams);
|
||||||
var streamInfo = streamManifest.audioOnly.withHighestBitrate();
|
var streamInfo = streamManifest.audioOnly.withHighestBitrate();
|
||||||
var stream = yt.videos.streamsClient.get(streamInfo);
|
var stream = yt.videos.streamsClient.get(streamInfo);
|
||||||
|
|
||||||
final filePath = await getDownloadsDirectory();
|
|
||||||
final fileName = '${track.title} - ${track.artiste}'
|
final fileName = '${track.title} - ${track.artiste}'
|
||||||
.replaceAll('\\', '')
|
.replaceAll('\\', '')
|
||||||
.replaceAll('/', '')
|
.replaceAll('/', '')
|
||||||
|
@ -135,20 +137,25 @@ class HomeProvider with ChangeNotifier {
|
||||||
.replaceAll('<', '')
|
.replaceAll('<', '')
|
||||||
.replaceAll('>', '')
|
.replaceAll('>', '')
|
||||||
.replaceAll('|', '');
|
.replaceAll('|', '');
|
||||||
var file = File('${filePath!.path}/$fileName.webm');
|
|
||||||
var fileStream = file.openWrite();
|
|
||||||
|
|
||||||
await stream.pipe(fileStream);
|
if (!kIsWeb) {
|
||||||
|
final filePath = Platform.isAndroid
|
||||||
|
? Directory('/storage/emulated/0/Download')
|
||||||
|
: await getDownloadsDirectory();
|
||||||
|
|
||||||
await fileStream.flush();
|
var file = File('${filePath!.path}/$fileName.webm');
|
||||||
await fileStream.close();
|
var fileStream = file.openWrite();
|
||||||
yt.close();
|
|
||||||
|
|
||||||
// Play it
|
await stream.pipe(fileStream);
|
||||||
track.file = file;
|
|
||||||
// playTrack(context, track);
|
|
||||||
|
|
||||||
print(file.path);
|
await fileStream.flush();
|
||||||
|
await fileStream.close();
|
||||||
|
yt.close();
|
||||||
|
|
||||||
|
track.file = file;
|
||||||
|
|
||||||
|
print(file.path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DropdownMenuItem<String>> get radioList {
|
List<DropdownMenuItem<String>> get radioList {
|
||||||
|
|
|
@ -409,7 +409,7 @@ TableRow _buildTableRow(Track track, BuildContext context) {
|
||||||
return Text(track.title,
|
return Text(track.title,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
color: hp.currentTrack == track
|
color: hp.currentTrack?.title == track.title && hp.currentTrack?.artiste == track.artiste
|
||||||
? Colors.green
|
? Colors.green
|
||||||
: Colors.grey[350],
|
: Colors.grey[350],
|
||||||
fontSize: 14));
|
fontSize: 14));
|
||||||
|
@ -454,8 +454,7 @@ TableRow _buildTableRow(Track track, BuildContext context) {
|
||||||
},
|
},
|
||||||
child: track.number == -1
|
child: track.number == -1
|
||||||
? Text('TÉLÉCHARGER', style: textStyle)
|
? Text('TÉLÉCHARGER', style: textStyle)
|
||||||
: Consumer<DownloadProvider>(
|
: Consumer<DownloadProvider>(builder: (context, _, __) {
|
||||||
builder: (context, playerProvider, _) {
|
|
||||||
return Row(children: [
|
return Row(children: [
|
||||||
const SizedBox(width: 37),
|
const SizedBox(width: 37),
|
||||||
isDownloading == track.number
|
isDownloading == track.number
|
||||||
|
|
|
@ -7,6 +7,9 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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}/linux plugins/${ffi_plugin})
|
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||||
|
endforeach(ffi_plugin)
|
||||||
|
|
42
pubspec.lock
|
@ -1,6 +1,20 @@
|
||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
|
archive:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: archive
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.3.0"
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -118,6 +132,13 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_launcher_icons_maker:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_launcher_icons_maker
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.10.2"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -163,6 +184,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "4.0.0"
|
||||||
|
image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.3"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -422,6 +450,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
universal_io:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: universal_io
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.4"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -513,6 +548,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.3.1"
|
version: "5.3.1"
|
||||||
|
yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: yaml
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
youtube_explode_dart:
|
youtube_explode_dart:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
18
pubspec.yaml
|
@ -11,7 +11,10 @@ dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
youtube_explode_dart: ^1.10.9+1
|
youtube_explode_dart: #^1.10.9+1
|
||||||
|
# git:
|
||||||
|
# url: https://git.p2p.legal/poka/youtube_explode.git
|
||||||
|
# ref: master
|
||||||
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
|
||||||
|
@ -21,19 +24,28 @@ dependencies:
|
||||||
ref: master
|
ref: master
|
||||||
provider: ^6.0.1
|
provider: ^6.0.1
|
||||||
path_provider: ^2.0.9
|
path_provider: ^2.0.9
|
||||||
|
universal_io: ^2.0.4
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^1.0.0
|
flutter_lints: ^1.0.0
|
||||||
|
flutter_launcher_icons_maker: ^0.10.2
|
||||||
|
|
||||||
|
flutter_icons:
|
||||||
|
android: "launcher_icon"
|
||||||
|
ios: true
|
||||||
|
macos: true
|
||||||
|
windows: true
|
||||||
|
web: true
|
||||||
|
image_path: "assets/logo.jpg"
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
assets:
|
assets:
|
||||||
- assets/api-token.json
|
- assets/logo.jpg
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||||
|
|
BIN
web/favicon.png
Before Width: | Height: | Size: 917 B After Width: | Height: | Size: 394 B |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 825 B |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 825 B |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 2.0 KiB |