notube-export/lib/videoList/cubit/videos_cubit.dart

216 lines
6.7 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:notube/models/video.dart';
import 'package:notube/services/converter.dart';
import 'package:notube/services/download.dart';
import 'package:notube/constants.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:downloadsfolder/downloadsfolder.dart';
import 'package:path/path.dart' as p;
part 'videos_state.dart';
class VideosCubit extends Cubit<VideosState> {
VideosCubit() : super(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));
}
void changeStatus(Video video, String status) {
int index = _videoList.indexWhere((v) => v.id == video.id);
if (index != -1) {
_videoList[index] = video.copyWith(status: status);
emit(VideosState(videoList: _videoList));
} else {
debugPrint('Video not found in the list');
}
}
void clearVideos() {
_videoList.clear();
emit(VideosState(videoList: _videoList));
}
void removeVideo(Video video) {
_videoList.remove(video);
emit(VideosState(videoList: _videoList));
}
Future<void> startConverter() async {
var videosToConvert = _videoList.where((video) =>
video.status == 'downloaded' &&
convertedFormats.contains(video.format));
for (Video video in videosToConvert) {
await convertVideo(video);
}
debugPrint('All videos converted');
}
Future<void> startDownloader() async {
dlService = await DLServices.init();
downloadingVid =
_videoList.where((video) => video.status == 'pending').take(3);
debugPrint('Videos to download: ${downloadingVid.length}');
while (downloadingVid.isNotEmpty && runningTasks < maxConcurrentTasks) {
runningTasks++;
debugPrint('Concurrent workers: $runningTasks');
await downloadVideo(downloadingVid.first);
downloadingVid =
_videoList.where((video) => video.status != 'downloaded');
}
await startConverter();
final prefs = await SharedPreferences.getInstance();
Directory defaultDownloadDirectory = await getDownloadDirectory();
prefs.get('downloadFolder') ??
await prefs.setString('downloadFolder', defaultDownloadDirectory.path);
final downloadDirectory = Directory(
prefs.getString('downloadFolder') ?? defaultDownloadDirectory.path);
for (Video video in _videoList) {
var cleanTitle = video.title.replaceAll(RegExp(r'[^\w\s]+'), '');
final tmpFile =
File('temp/${video.filename}_done.${video.format.extension}');
final newFile = File(
'${downloadDirectory.path}/${cleanTitle}.${video.format.extension}');
await tmpFile.rename(newFile.path);
changeStatus(video, '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 {
converterService = await ConverterService.init();
debugPrint('Converting ${video.title} to ${video.format.format}');
changeStatus(video, 'converting');
final shellStream = await converterService.convertFile(video);
var duration = '0.0';
var completer = Completer<void>();
shellStream.listen(
(line) {
debugPrint(line);
//FFmpeg doesn't return any output for audio conversion
if (video.format.extension == "mp3") {
changeStatus(video, 'converted');
if (!completer.isCompleted) {
debugPrint('____Conversion audio completed___');
runningTasks--;
completer.complete();
}
} else {
if (line.contains('Duration: ')) {
var durationString =
line.split('Duration: ')[1].split(',')[0].trim();
duration = durationString;
debugPrint('Duration: $duration');
}
if (line.contains('out_time_ms=')) {
var timeString = line.split('out_time_ms=')[1];
var time = timeString;
debugPrint('Time: $time');
changeStatus(video, 'converting $time on $duration');
}
if (line.contains('progress=end')) {
changeStatus(video, 'converted');
if (!completer.isCompleted) {
debugPrint('____Conversion completed___');
runningTasks--;
completer.complete();
}
}
}
},
onError: (error) {
if (!completer.isCompleted) {
completer
.completeError(error); // Complete with error if an error occurs
}
},
);
return completer.future;
}
Future downloadVideo(Video video) async {
debugPrint(
'___Downloading ${video.title} in ${video.format} format ${video.filename}____');
final shellStream = await dlService.downloadFile(video);
var completer = Completer<void>();
shellStream.listen(
(line) {
if (line.contains('[download]') &&
line.contains('of') &&
line.contains('%')) {
var percentString =
line.split('[download]')[1].split(' of')[0].split('%')[0].trim();
var percent = percentString == null ? 0 : double.parse(percentString);
changeStatus(video, 'downloading $percent%');
}
if (line.contains('[EmbedThumbnail]')) {
changeStatus(video, 'downloaded');
if (!completer.isCompleted) {
debugPrint('____Download completed___');
runningTasks--;
completer.complete();
}
}
//debugPrint(line);
},
onError: (error) {
if (!completer.isCompleted) {
completer
.completeError(error); // Complete with error if an error occurs
}
},
);
return completer.future;
}
}