[video] archive videos

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

3
.gitignore vendored
View File

@ -46,4 +46,5 @@ app.*.map.json
/android/app/profile
/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) {
if (state.isParsed) {
context.read<VideosCubit>().setGlobalStatus('loaded');
for (Video video in state.videos) {
debugPrint('Adding video: $video');
context.read<VideosCubit>().addVideo(video);
@ -45,6 +46,7 @@ class _SubmitButtonState extends State<SubmitButton> {
},
child: GestureDetector(
onTap: () {
context.read<VideosCubit>().setGlobalStatus('loading');
context.read<DlFormCubit>().parseUrl();
},
child: Container(

View File

@ -47,10 +47,41 @@ class Home extends StatelessWidget {
child: BlocConsumer<VideosCubit, VideosState>(
listener: (context, state) {},
builder: (context, state) {
return ListView.builder(
itemCount: state.videoList.length,
var returnWidget = <Widget>[];
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) {
var currentVideo = state.videoList[index];
var currentVideo =
state.archiveList[index];
return ListTile(
leading: SizedBox(
width: 72,
@ -64,6 +95,9 @@ class Home extends StatelessWidget {
'${currentVideo.status} - ${currentVideo.format.format}'),
);
},
));
return Stack(
children: returnWidget,
);
})),
],

View File

@ -7,5 +7,6 @@
"download_folder": "Download folder",
"en_US": "English",
"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",
"en_US": "Anglais",
"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 ConverterService converterService;
final List<Video> _videoList = [];
List<Video> get videoList => _videoList;
Iterable<Video> downloadingVid = [];
int runningTasks = 0;
int maxConcurrentTasks = 3;
void addVideo(Video video) {
_videoList.add(video);
emit(VideosState(videoList: _videoList));
var nVideoList = state.videoList.toList();
nVideoList.add(video);
emit(VideosState(videoList: nVideoList));
}
void changeStatus(Video video, String status) {
int index = _videoList.indexWhere((v) => v.id == video.id);
Future<void> changeStatus(Video video, String status) async {
var completer = Completer<void>();
var tmpVideoList = state.videoList.toList();
int index = tmpVideoList.indexWhere((v) => v.id == video.id);
if (index != -1) {
_videoList[index] = video.copyWith(status: status);
emit(VideosState(videoList: _videoList));
tmpVideoList[index] = video.copyWith(status: status);
emit(VideosState(videoList: tmpVideoList));
completer.complete();
} else {
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() {
_videoList.clear();
emit(VideosState(videoList: _videoList));
emit(VideosState(videoList: []));
}
void removeVideo(Video video) {
_videoList.remove(video);
emit(VideosState(videoList: _videoList));
var nVideoList = state.videoList.where((v) => v.id != video.id).toList();
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 {
var videosToConvert = _videoList.where((video) =>
var videosToConvert = state.videoList.where((video) =>
video.status == 'downloaded' &&
convertedFormats.contains(video.format));
for (Video video in videosToConvert) {
@ -64,20 +77,37 @@ class VideosCubit extends Cubit<VideosState> {
Future<void> startDownloader() async {
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) {
debugPrint('Videos to download: ${downloadingVid.length}');
runningTasks++;
debugPrint('Concurrent workers: $runningTasks');
await downloadVideo(downloadingVid.first);
downloadingVid =
_videoList.where((video) => video.status != 'downloaded');
state.videoList.where((video) => video.status == 'pending').take(3);
}
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();
Directory defaultDownloadDirectory = await getDownloadDirectory();
prefs.get('downloadFolder') ??
@ -86,7 +116,8 @@ class VideosCubit extends Cubit<VideosState> {
final downloadDirectory = Directory(
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]+'), '');
final tmpFile =
File('temp/${video.filename}_done.${video.format.extension}');
@ -97,7 +128,7 @@ class VideosCubit extends Cubit<VideosState> {
.catchError((e) => debugPrint('Error creating directory: $e'));
debugPrint('Playlist title: $playlistTitle');
if (playlistTitle.isNotEmpty && playlistTitle != '') {
cleanTitle = playlistTitle + '/' + cleanTitle;
cleanTitle = '$playlistTitle/$cleanTitle';
}
final newFile = File(
@ -113,33 +144,10 @@ class VideosCubit extends Cubit<VideosState> {
await Future.delayed(Duration(seconds: 1));
}
}
changeStatus(video, 'done');
await changeStatus(video, 'done');
archiveVideo(video.copyWith(status: 'done'));
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 {

View File

@ -1,9 +1,14 @@
part of 'videos_cubit.dart';
final class VideosState {
final String status;
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
version: "2.1.1"
logger:
dependency: transitive
dependency: "direct main"
description:
name: logger
sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4

View File

@ -33,6 +33,7 @@ dependencies:
filesystem_picker: ^4.1.0
file_picker: ^8.0.6
rename: ^3.0.2
logger: ^2.3.0
dev_dependencies:
flutter_test: