[video] archive videos

This commit is contained in:
jscampucci 2024-07-05 15:26:13 +02:00
parent b6ff203e4b
commit b3fd91590c
9 changed files with 106 additions and 53 deletions

1
.gitignore vendored
View File

@ -47,3 +47,4 @@ app.*.map.json
/android/app/release /android/app/release
samples/ samples/
apps_logs.txt

View File

@ -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(

View File

@ -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,
); );
})), })),
], ],

View File

@ -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"
} }

View File

@ -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"
} }

View File

@ -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 {

View File

@ -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];
} }

View File

@ -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

View File

@ -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: