[video] archive videos
This commit is contained in:
parent
215c334fa3
commit
8ca1864351
|
|
@ -46,4 +46,5 @@ app.*.map.json
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
samples/
|
samples/
|
||||||
|
apps_logs.txt
|
||||||
|
|
@ -20,6 +20,7 @@ class _SubmitButtonState extends State<SubmitButton> {
|
||||||
|
|
||||||
void addVideosListener(BuildContext context, DlFormState state) {
|
void addVideosListener(BuildContext context, DlFormState state) {
|
||||||
if (state.isParsed) {
|
if (state.isParsed) {
|
||||||
|
context.read<VideosCubit>().setGlobalStatus('loaded');
|
||||||
for (Video video in state.videos) {
|
for (Video video in state.videos) {
|
||||||
debugPrint('Adding video: $video');
|
debugPrint('Adding video: $video');
|
||||||
context.read<VideosCubit>().addVideo(video);
|
context.read<VideosCubit>().addVideo(video);
|
||||||
|
|
@ -45,6 +46,7 @@ class _SubmitButtonState extends State<SubmitButton> {
|
||||||
},
|
},
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
context.read<VideosCubit>().setGlobalStatus('loading');
|
||||||
context.read<DlFormCubit>().parseUrl();
|
context.read<DlFormCubit>().parseUrl();
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|
|
||||||
|
|
@ -47,10 +47,41 @@ class Home extends StatelessWidget {
|
||||||
child: BlocConsumer<VideosCubit, VideosState>(
|
child: BlocConsumer<VideosCubit, VideosState>(
|
||||||
listener: (context, state) {},
|
listener: (context, state) {},
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return ListView.builder(
|
var returnWidget = <Widget>[];
|
||||||
itemCount: state.videoList.length,
|
switch (state.status) {
|
||||||
|
case 'loading':
|
||||||
|
returnWidget.add(Center(
|
||||||
|
child: CircularProgressIndicator()));
|
||||||
|
case 'error':
|
||||||
|
returnWidget.add(Center(
|
||||||
|
child: Text('home_dl_error').tr(),
|
||||||
|
));
|
||||||
|
default:
|
||||||
|
returnWidget.add(ListView.builder(
|
||||||
|
itemCount: state.videoList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
var currentVideo =
|
||||||
|
state.videoList[index];
|
||||||
|
return ListTile(
|
||||||
|
leading: SizedBox(
|
||||||
|
width: 72,
|
||||||
|
height: 48,
|
||||||
|
child: Image.network(
|
||||||
|
currentVideo.thumbnail,
|
||||||
|
fit: BoxFit.cover),
|
||||||
|
),
|
||||||
|
title: Text(currentVideo.title),
|
||||||
|
subtitle: Text(
|
||||||
|
'${currentVideo.status} - ${currentVideo.format.format}'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
returnWidget.add(ListView.builder(
|
||||||
|
itemCount: state.archiveList.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
var currentVideo = state.videoList[index];
|
var currentVideo =
|
||||||
|
state.archiveList[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: SizedBox(
|
leading: SizedBox(
|
||||||
width: 72,
|
width: 72,
|
||||||
|
|
@ -64,6 +95,9 @@ class Home extends StatelessWidget {
|
||||||
'${currentVideo.status} - ${currentVideo.format.format}'),
|
'${currentVideo.status} - ${currentVideo.format.format}'),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
));
|
||||||
|
return Stack(
|
||||||
|
children: returnWidget,
|
||||||
);
|
);
|
||||||
})),
|
})),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,6 @@
|
||||||
"download_folder": "Download folder",
|
"download_folder": "Download folder",
|
||||||
"en_US": "English",
|
"en_US": "English",
|
||||||
"fr_FR": "French",
|
"fr_FR": "French",
|
||||||
"Ok": "OK"
|
"Ok": "OK",
|
||||||
|
"home_dl_error": "Error while downloading the video"
|
||||||
}
|
}
|
||||||
|
|
@ -7,5 +7,6 @@
|
||||||
"download_folder": "Dossier de téléchargement",
|
"download_folder": "Dossier de téléchargement",
|
||||||
"en_US": "Anglais",
|
"en_US": "Anglais",
|
||||||
"fr_FR": "Français",
|
"fr_FR": "Français",
|
||||||
"Ok": "OK"
|
"Ok": "OK",
|
||||||
|
"home_dl_error": "Erreur lors du téléchargement de la vidéo"
|
||||||
}
|
}
|
||||||
|
|
@ -19,41 +19,54 @@ class VideosCubit extends Cubit<VideosState> {
|
||||||
late DLServices dlService;
|
late DLServices dlService;
|
||||||
late ConverterService converterService;
|
late ConverterService converterService;
|
||||||
|
|
||||||
final List<Video> _videoList = [];
|
|
||||||
List<Video> get videoList => _videoList;
|
|
||||||
Iterable<Video> downloadingVid = [];
|
Iterable<Video> downloadingVid = [];
|
||||||
|
|
||||||
int runningTasks = 0;
|
int runningTasks = 0;
|
||||||
int maxConcurrentTasks = 3;
|
int maxConcurrentTasks = 3;
|
||||||
|
|
||||||
void addVideo(Video video) {
|
void addVideo(Video video) {
|
||||||
_videoList.add(video);
|
var nVideoList = state.videoList.toList();
|
||||||
emit(VideosState(videoList: _videoList));
|
nVideoList.add(video);
|
||||||
|
emit(VideosState(videoList: nVideoList));
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeStatus(Video video, String status) {
|
Future<void> changeStatus(Video video, String status) async {
|
||||||
int index = _videoList.indexWhere((v) => v.id == video.id);
|
var completer = Completer<void>();
|
||||||
|
var tmpVideoList = state.videoList.toList();
|
||||||
|
int index = tmpVideoList.indexWhere((v) => v.id == video.id);
|
||||||
|
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
_videoList[index] = video.copyWith(status: status);
|
tmpVideoList[index] = video.copyWith(status: status);
|
||||||
emit(VideosState(videoList: _videoList));
|
emit(VideosState(videoList: tmpVideoList));
|
||||||
|
completer.complete();
|
||||||
} else {
|
} else {
|
||||||
debugPrint('Video not found in the list');
|
debugPrint('Video not found in the list');
|
||||||
|
completer.completeError('Video not found in the list');
|
||||||
}
|
}
|
||||||
|
return completer.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setGlobalStatus(String status) {
|
||||||
|
emit(VideosState(status: status));
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearVideos() {
|
void clearVideos() {
|
||||||
_videoList.clear();
|
emit(VideosState(videoList: []));
|
||||||
emit(VideosState(videoList: _videoList));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeVideo(Video video) {
|
void removeVideo(Video video) {
|
||||||
_videoList.remove(video);
|
var nVideoList = state.videoList.where((v) => v.id != video.id).toList();
|
||||||
emit(VideosState(videoList: _videoList));
|
emit(VideosState(videoList: nVideoList));
|
||||||
|
}
|
||||||
|
|
||||||
|
void archiveVideo(Video video) {
|
||||||
|
debugPrint('Archiving video: ${video.title} ${video.format.format}');
|
||||||
|
var nVideoList = state.videoList.where((v) => v.id != video.id).toList();
|
||||||
|
emit(VideosState(videoList: nVideoList, archiveList: [video]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> startConverter() async {
|
Future<void> startConverter() async {
|
||||||
var videosToConvert = _videoList.where((video) =>
|
var videosToConvert = state.videoList.where((video) =>
|
||||||
video.status == 'downloaded' &&
|
video.status == 'downloaded' &&
|
||||||
convertedFormats.contains(video.format));
|
convertedFormats.contains(video.format));
|
||||||
for (Video video in videosToConvert) {
|
for (Video video in videosToConvert) {
|
||||||
|
|
@ -64,20 +77,37 @@ class VideosCubit extends Cubit<VideosState> {
|
||||||
|
|
||||||
Future<void> startDownloader() async {
|
Future<void> startDownloader() async {
|
||||||
dlService = await DLServices.init();
|
dlService = await DLServices.init();
|
||||||
downloadingVid =
|
|
||||||
_videoList.where((video) => video.status == 'pending').take(3);
|
|
||||||
|
|
||||||
debugPrint('Videos to download: ${downloadingVid.length}');
|
downloadingVid =
|
||||||
|
state.videoList.where((video) => video.status == 'pending').take(3);
|
||||||
|
|
||||||
while (downloadingVid.isNotEmpty && runningTasks < maxConcurrentTasks) {
|
while (downloadingVid.isNotEmpty && runningTasks < maxConcurrentTasks) {
|
||||||
|
debugPrint('Videos to download: ${downloadingVid.length}');
|
||||||
runningTasks++;
|
runningTasks++;
|
||||||
debugPrint('Concurrent workers: $runningTasks');
|
debugPrint('Concurrent workers: $runningTasks');
|
||||||
|
|
||||||
await downloadVideo(downloadingVid.first);
|
await downloadVideo(downloadingVid.first);
|
||||||
downloadingVid =
|
downloadingVid =
|
||||||
_videoList.where((video) => video.status != 'downloaded');
|
state.videoList.where((video) => video.status == 'pending').take(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
await startConverter();
|
await startConverter();
|
||||||
|
|
||||||
|
await moveVideos();
|
||||||
|
|
||||||
|
final Directory directory = Directory('temp');
|
||||||
|
final List<FileSystemEntity> files = directory.listSync();
|
||||||
|
for (FileSystemEntity file in files) {
|
||||||
|
final String dirFilename = p.basenameWithoutExtension(file.path);
|
||||||
|
if (dirFilename.contains('_tmp')) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint('All videos downloaded');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future moveVideos() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
Directory defaultDownloadDirectory = await getDownloadDirectory();
|
Directory defaultDownloadDirectory = await getDownloadDirectory();
|
||||||
prefs.get('downloadFolder') ??
|
prefs.get('downloadFolder') ??
|
||||||
|
|
@ -86,7 +116,8 @@ class VideosCubit extends Cubit<VideosState> {
|
||||||
final downloadDirectory = Directory(
|
final downloadDirectory = Directory(
|
||||||
prefs.getString('downloadFolder') ?? defaultDownloadDirectory.path);
|
prefs.getString('downloadFolder') ?? defaultDownloadDirectory.path);
|
||||||
|
|
||||||
for (Video video in _videoList) {
|
for (Video video in state.videoList.where((video) =>
|
||||||
|
video.status == 'downloaded' || video.status == 'converted')) {
|
||||||
var cleanTitle = video.title.replaceAll(RegExp(r'[^\w\s]+'), '');
|
var cleanTitle = video.title.replaceAll(RegExp(r'[^\w\s]+'), '');
|
||||||
final tmpFile =
|
final tmpFile =
|
||||||
File('temp/${video.filename}_done.${video.format.extension}');
|
File('temp/${video.filename}_done.${video.format.extension}');
|
||||||
|
|
@ -97,7 +128,7 @@ class VideosCubit extends Cubit<VideosState> {
|
||||||
.catchError((e) => debugPrint('Error creating directory: $e'));
|
.catchError((e) => debugPrint('Error creating directory: $e'));
|
||||||
debugPrint('Playlist title: $playlistTitle');
|
debugPrint('Playlist title: $playlistTitle');
|
||||||
if (playlistTitle.isNotEmpty && playlistTitle != '') {
|
if (playlistTitle.isNotEmpty && playlistTitle != '') {
|
||||||
cleanTitle = playlistTitle + '/' + cleanTitle;
|
cleanTitle = '$playlistTitle/$cleanTitle';
|
||||||
}
|
}
|
||||||
|
|
||||||
final newFile = File(
|
final newFile = File(
|
||||||
|
|
@ -113,33 +144,10 @@ class VideosCubit extends Cubit<VideosState> {
|
||||||
await Future.delayed(Duration(seconds: 1));
|
await Future.delayed(Duration(seconds: 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
changeStatus(video, 'done');
|
await changeStatus(video, 'done');
|
||||||
|
archiveVideo(video.copyWith(status: 'done'));
|
||||||
debugPrint('File moved to ${newFile.path}');
|
debugPrint('File moved to ${newFile.path}');
|
||||||
}
|
}
|
||||||
|
|
||||||
final Directory directory = Directory('temp');
|
|
||||||
final List<FileSystemEntity> files = directory.listSync();
|
|
||||||
for (FileSystemEntity file in files) {
|
|
||||||
final String dirFilename = p.basenameWithoutExtension(file.path);
|
|
||||||
if (dirFilename.contains('_tmp')) {
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debugPrint('All videos downloaded');
|
|
||||||
/*
|
|
||||||
for (Video video in videosLeft) {
|
|
||||||
final shellStream = await dlService.downloadFile(video.url, video.format);
|
|
||||||
shellStream.listen((line) {
|
|
||||||
if (line.contains('[download]')) {
|
|
||||||
debugPrint('___Download___');
|
|
||||||
debugPrint('%=${line.split('[download]')[1].split(' of')[0].trim()}');
|
|
||||||
debugPrint('______');
|
|
||||||
}
|
|
||||||
debugPrint(line);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future convertVideo(Video video) async {
|
Future convertVideo(Video video) async {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
part of 'videos_cubit.dart';
|
part of 'videos_cubit.dart';
|
||||||
|
|
||||||
final class VideosState {
|
final class VideosState {
|
||||||
|
final String status;
|
||||||
final List<Video> videoList;
|
final List<Video> videoList;
|
||||||
|
final List<Video> archiveList;
|
||||||
|
|
||||||
VideosState({this.videoList = const []});
|
VideosState(
|
||||||
|
{this.status = 'initial',
|
||||||
|
this.videoList = const [],
|
||||||
|
this.archiveList = const []});
|
||||||
|
|
||||||
List<Object> get props => [videoList];
|
List<Object> get props => [videoList, status, archiveList];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
logger:
|
logger:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: logger
|
name: logger
|
||||||
sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4
|
sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ dependencies:
|
||||||
filesystem_picker: ^4.1.0
|
filesystem_picker: ^4.1.0
|
||||||
file_picker: ^8.0.6
|
file_picker: ^8.0.6
|
||||||
rename: ^3.0.2
|
rename: ^3.0.2
|
||||||
|
logger: ^2.3.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue