[conv] ffmpeg full conversion
This commit is contained in:
parent
3eb4d7b905
commit
f97b20da49
|
|
@ -8,8 +8,14 @@ const colorDarkRed = Color.fromRGBO(153, 0, 0, 1);
|
|||
const colorMainBlue = Color(0xFF1f74ad);
|
||||
|
||||
enum Format {
|
||||
mp3(format: 'MP3'),
|
||||
mp3HD(format: 'MP3 HD'),
|
||||
mp3(
|
||||
format: 'MP3',
|
||||
ffmpegCmd: '-f mp3 -loglevel quiet -ab 192k -vn',
|
||||
extension: 'mp3'),
|
||||
mp3HD(
|
||||
format: 'MP3 HD',
|
||||
ffmpegCmd: '-f mp3 -loglevel quiet -ab 320k -vn',
|
||||
extension: 'mp3'),
|
||||
mp4(format: 'MP4'),
|
||||
mp4HD(
|
||||
ytCmd:
|
||||
|
|
@ -19,16 +25,36 @@ enum Format {
|
|||
ytCmd:
|
||||
"bestvideo[height=1440][ext=mp4]+bestaudio[ext=m4a]/bestvideo[height<=1080][ext=mp4][vcodec~='^(avc|h264)']+bestaudio[ext=m4a]/bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best",
|
||||
format: 'MP4 2K'),
|
||||
tGP(format: '3GP'),
|
||||
flv(format: 'FLV'),
|
||||
m4a;
|
||||
tGP(
|
||||
format: '3GP',
|
||||
ffmpegCmd:
|
||||
'-movflags frag_keyframe+empty_moov -r 20 -s 352x288 -vb 400k -acodec aac -strict experimental -ac 1 -ar 8000 -ab 24k -f 3gp',
|
||||
extension: '3gp'),
|
||||
flv(
|
||||
format: 'FLV',
|
||||
ffmpegCmd:
|
||||
'-vcodec libx264 -preset slower -b 512k -bt 512k -threads 0 -s 640x360 -aspect 16:9 -acodec libmp3lame -ar 44100 -ab 32 -progress pipe:1',
|
||||
extension: 'flv'),
|
||||
m4a(format: 'M4A'),
|
||||
;
|
||||
|
||||
final String ytCmd;
|
||||
final String format;
|
||||
final String ffmpegCmd;
|
||||
final String extension;
|
||||
|
||||
const Format({
|
||||
this.ytCmd =
|
||||
'"18/22/bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best"',
|
||||
this.format = 'MP4',
|
||||
this.ffmpegCmd = '',
|
||||
this.extension = '',
|
||||
});
|
||||
}
|
||||
|
||||
const convertedFormats = [
|
||||
Format.mp3,
|
||||
Format.mp3HD,
|
||||
Format.tGP,
|
||||
Format.flv,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import 'dart:convert';
|
|||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:notube/constants.dart';
|
||||
import 'package:notube/models/video.dart';
|
||||
import 'package:notube/services/download.dart';
|
||||
|
||||
|
|
@ -12,7 +14,7 @@ class DlFormCubit extends Cubit<DlFormState> {
|
|||
|
||||
late DLServices dlService;
|
||||
|
||||
void setFormat(String newFormat) {
|
||||
void setFormat(Format newFormat) {
|
||||
emit(state.copyWith(
|
||||
format: newFormat,
|
||||
));
|
||||
|
|
@ -39,17 +41,15 @@ class DlFormCubit extends Cubit<DlFormState> {
|
|||
var playlistTitle = dataInfos['title'];
|
||||
for (var videoTmp in dataInfos['entries']) {
|
||||
var video = Video.fromJson(videoTmp);
|
||||
video.copyWith(
|
||||
format: state.format,
|
||||
);
|
||||
videos.add(video);
|
||||
videos.add(video.copyWith(
|
||||
format: state.format,
|
||||
filename: '${video.id}_${state.format.format}'));
|
||||
}
|
||||
} else {
|
||||
var video = Video.fromJson(dataInfos);
|
||||
video.copyWith(
|
||||
format: state.format,
|
||||
);
|
||||
videos.add(video);
|
||||
videos.add(video.copyWith(
|
||||
format: state.format,
|
||||
filename: '${video.id}_${state.format.format}'));
|
||||
}
|
||||
emit(state.copyWith(
|
||||
isParsed: true,
|
||||
|
|
@ -60,7 +60,7 @@ class DlFormCubit extends Cubit<DlFormState> {
|
|||
});
|
||||
}
|
||||
|
||||
void clearVideos() {
|
||||
void clearForm() {
|
||||
emit(state.copyWith(
|
||||
videos: [],
|
||||
isParsed: false,
|
||||
|
|
|
|||
|
|
@ -3,21 +3,21 @@ part of 'dl_form_cubit.dart';
|
|||
class DlFormState extends Equatable {
|
||||
const DlFormState({
|
||||
this.url = '',
|
||||
this.format = 'MP3',
|
||||
this.format = Format.mp4,
|
||||
this.videos = const [],
|
||||
this.isParsed = false,
|
||||
this.extractor = '',
|
||||
});
|
||||
|
||||
final String url;
|
||||
final String format;
|
||||
final Format format;
|
||||
final bool isParsed;
|
||||
final List<Video> videos;
|
||||
final String extractor;
|
||||
|
||||
DlFormState copyWith({
|
||||
String? url,
|
||||
String? format,
|
||||
Format? format,
|
||||
List<Video>? videos,
|
||||
bool? isParsed,
|
||||
String? extractor,
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class DebugDlFormState extends StatelessWidget {
|
|||
return Column(
|
||||
children: <Widget>[
|
||||
Text('Url: $url'),
|
||||
Text('Format: $format'),
|
||||
Text('Format: ${format.format}'),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,12 +18,14 @@ class DropdownFormat extends StatelessWidget {
|
|||
|
||||
// dropdown below..
|
||||
child: DropdownButton<String>(
|
||||
value: dlFormState.format,
|
||||
value: dlFormState.format.format,
|
||||
elevation: 16,
|
||||
style: const TextStyle(color: colorMainWhite),
|
||||
dropdownColor: colorMainBlue,
|
||||
onChanged: (String? value) {
|
||||
context.read<DlFormCubit>().setFormat(value!);
|
||||
var format =
|
||||
Format.values.firstWhere((element) => element.format == value);
|
||||
context.read<DlFormCubit>().setFormat(format!);
|
||||
},
|
||||
underline: Container(),
|
||||
items: Format.values.map<DropdownMenuItem<String>>((Format format) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class _SubmitButtonState extends State<SubmitButton> {
|
|||
debugPrint('Adding video: $video');
|
||||
context.read<VideosCubit>().addVideo(video);
|
||||
}
|
||||
context.read<DlFormCubit>().clearVideos();
|
||||
context.read<DlFormCubit>().clearForm();
|
||||
context.read<VideosCubit>().startDownloader();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:notube/constants.dart';
|
||||
|
||||
@immutable
|
||||
class Video extends Equatable {
|
||||
|
|
@ -9,7 +10,7 @@ class Video extends Equatable {
|
|||
this.extractor = '',
|
||||
this.isParsed = false,
|
||||
this.status = 'pending',
|
||||
this.format = 'MP4 HD',
|
||||
this.format = Format.mp4,
|
||||
this.title = '',
|
||||
this.thumbnail = '',
|
||||
this.description = '',
|
||||
|
|
@ -24,7 +25,7 @@ class Video extends Equatable {
|
|||
final String extractor;
|
||||
final bool isParsed;
|
||||
final String status;
|
||||
final String format;
|
||||
final Format format;
|
||||
final String title;
|
||||
final String thumbnail;
|
||||
final String description;
|
||||
|
|
@ -54,7 +55,7 @@ class Video extends Equatable {
|
|||
String? extractor,
|
||||
bool? isParsed,
|
||||
String? status,
|
||||
String? format,
|
||||
Format? format,
|
||||
String? title,
|
||||
String? thumbnail,
|
||||
String? description,
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class Home extends StatelessWidget {
|
|||
),
|
||||
title: Text(currentVideo.title),
|
||||
subtitle: Text(
|
||||
'${currentVideo.status} - ${currentVideo.format}'),
|
||||
'${currentVideo.status} - ${currentVideo.format.format}'),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:process_run/process_run.dart';
|
||||
import 'package:notube/models/video.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
class ConverterService {
|
||||
late Directory tempDir;
|
||||
|
|
@ -39,18 +42,47 @@ class ConverterService {
|
|||
ffmpegPath = tempFile.path;
|
||||
}
|
||||
|
||||
Future<Stream> convertFile(String inputPath, String outputPath) async {
|
||||
debugPrint('Converting $inputPath to $outputPath');
|
||||
Future<File?> getTmpFile(String filename) async {
|
||||
final Directory directory = Directory('temp');
|
||||
final List<FileSystemEntity> files = directory.listSync();
|
||||
for (FileSystemEntity file in files) {
|
||||
final String dirFilename = p.basenameWithoutExtension(file.path);
|
||||
debugPrint('dirFilename $dirFilename');
|
||||
debugPrint('filename $filename');
|
||||
if (dirFilename == '${filename}_tmp') {
|
||||
return File(file.path);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var command = ' -q --flat-playlist -J';
|
||||
Future<Stream> convertFile(Video video) async {
|
||||
debugPrint(
|
||||
'____Converting ${video.title} to ${video.format.format} format_____');
|
||||
|
||||
File tmpFile = File('temp/${video.filename}_tmp.mp4');
|
||||
if (!tmpFile.existsSync()) {
|
||||
tmpFile = await getTmpFile(video.filename) ??
|
||||
File('temp/${video.filename}_tmp.mp4');
|
||||
}
|
||||
|
||||
File doneFile =
|
||||
File('temp/${video.filename}_done.${video.format.extension}');
|
||||
if (doneFile.existsSync()) {
|
||||
debugPrint('File already converted');
|
||||
return Stream.fromIterable(['progress=end']);
|
||||
}
|
||||
|
||||
var command =
|
||||
'-i "${tmpFile.path}" ${video.format.ffmpegCmd} "temp/${video.filename}_done.${video.format.extension}"';
|
||||
|
||||
var shellLinesController = ShellLinesController();
|
||||
var shell = Shell(stdout: shellLinesController.sink);
|
||||
var shell = Shell(
|
||||
stdout: shellLinesController.sink, stderr: shellLinesController.sink);
|
||||
|
||||
await shell.run('''
|
||||
$ffmpegPath $command
|
||||
''').then((result) {
|
||||
debugPrint('Analyse result: $result');
|
||||
});
|
||||
''');
|
||||
|
||||
return shellLinesController.stream;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ class DLServices {
|
|||
}
|
||||
|
||||
Future<Stream> downloadFile(Video video) async {
|
||||
debugPrint('Downloading $url in $video.format format');
|
||||
debugPrint(
|
||||
'Downloading $url in ${video.format.format} format (${video.filename})');
|
||||
|
||||
/*
|
||||
https://youtu.be/playlist?list=PLk1fi9OrZmvGBdh9BdWIhZImGVDUqls1X
|
||||
|
|
@ -62,12 +63,18 @@ class DLServices {
|
|||
|
||||
// get the format code from the Format enum
|
||||
var formatCmd =
|
||||
Format.values.firstWhere((e) => e.format == video.format).ytCmd;
|
||||
Format.values.firstWhere((e) => e.format == video.format.format).ytCmd;
|
||||
debugPrint('Format code: $formatCmd');
|
||||
|
||||
var cleanTitle = video.title.replaceAll(RegExp(r'[^\w\s]+'), '');
|
||||
File doneFile = File('temp/${video.filename}_tmp.mp4');
|
||||
if (doneFile.existsSync()) {
|
||||
debugPrint('File already downloaded');
|
||||
return Stream.fromIterable(['[EmbedThumbnail]']);
|
||||
}
|
||||
|
||||
var strType = convertedFormats.contains(video.format) ? 'tmp' : 'done';
|
||||
var command =
|
||||
'${video.url.trim()} --sub-langs "all,-live_chat" --embed-subs --embed-thumbnail --embed-metadata --progress -o "temp/${video.id}_${video.format}_tmp.%(ext)s" -f "$formatCmd"';
|
||||
'${video.url.trim()} --sub-langs "all,-live_chat" --embed-subs --embed-thumbnail --embed-metadata --progress -o "temp/${video.filename}_$strType.%(ext)s" -f "$formatCmd"';
|
||||
|
||||
var shellLinesController = ShellLinesController();
|
||||
var shell = Shell(stdout: shellLinesController.sink, verbose: false);
|
||||
|
|
@ -84,7 +91,7 @@ class DLServices {
|
|||
var command = '${url.trim()} -q --flat-playlist -J';
|
||||
|
||||
var shellLinesController = ShellLinesController();
|
||||
var shell = Shell(stdout: shellLinesController.sink);
|
||||
var shell = Shell(stdout: shellLinesController.sink, verbose: false);
|
||||
|
||||
await shell.run('''
|
||||
$ytDlpPath $command
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
import 'dart:math';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:notube/models/Video.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';
|
||||
|
||||
|
|
@ -28,6 +31,17 @@ class VideosCubit extends Cubit<VideosState> {
|
|||
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));
|
||||
|
|
@ -38,10 +52,20 @@ class VideosCubit extends Cubit<VideosState> {
|
|||
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 != 'downloaded').take(3);
|
||||
_videoList.where((video) => video.status == 'pending').take(3);
|
||||
|
||||
debugPrint('Videos to download: ${downloadingVid.length}');
|
||||
while (downloadingVid.isNotEmpty && runningTasks < maxConcurrentTasks) {
|
||||
|
|
@ -53,6 +77,37 @@ class VideosCubit extends Cubit<VideosState> {
|
|||
_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);
|
||||
|
|
@ -70,17 +125,59 @@ class VideosCubit extends Cubit<VideosState> {
|
|||
|
||||
Future convertVideo(Video video) async {
|
||||
converterService = await ConverterService.init();
|
||||
debugPrint('Converting ${video.title} to $video.format ');
|
||||
final shellStream =
|
||||
await converterService.convertFile(video.url, video.format);
|
||||
shellStream.listen((line) {
|
||||
debugPrint(line);
|
||||
});
|
||||
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.url}');
|
||||
'___Downloading ${video.title} in ${video.format} format ${video.filename}____');
|
||||
final shellStream = await dlService.downloadFile(video);
|
||||
var completer = Completer<void>();
|
||||
|
||||
|
|
@ -92,22 +189,19 @@ class VideosCubit extends Cubit<VideosState> {
|
|||
var percentString =
|
||||
line.split('[download]')[1].split(' of')[0].split('%')[0].trim();
|
||||
var percent = percentString == null ? 0 : double.parse(percentString);
|
||||
video.status = 'downloading $percent%';
|
||||
emit(VideosState(videoList: _videoList));
|
||||
changeStatus(video, 'downloading $percent%');
|
||||
}
|
||||
|
||||
if (line.contains('[EmbedThumbnail]')) {
|
||||
video.status = 'downloaded';
|
||||
video.filename =
|
||||
line.split('Adding thumbnail to "')[1].split('"')[0].trim() ?? '';
|
||||
emit(VideosState(videoList: _videoList));
|
||||
changeStatus(video, 'downloaded');
|
||||
|
||||
if (!completer.isCompleted) {
|
||||
debugPrint('Download completed');
|
||||
debugPrint('____Download completed___');
|
||||
runningTasks--;
|
||||
completer.complete();
|
||||
}
|
||||
}
|
||||
debugPrint(line);
|
||||
//debugPrint(line);
|
||||
},
|
||||
onError: (error) {
|
||||
if (!completer.isCompleted) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import FlutterMacOS
|
|||
import Foundation
|
||||
|
||||
import device_info_plus
|
||||
import downloadsfolder
|
||||
import flutter_localization
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
|
|
@ -16,6 +17,7 @@ import window_manager
|
|||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
DownloadsfolderPlugin.register(with: registry.registrar(forPlugin: "DownloadsfolderPlugin"))
|
||||
FlutterLocalizationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalizationPlugin"))
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
|
|
|
|||
90
pubspec.lock
90
pubspec.lock
|
|
@ -57,6 +57,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -65,6 +73,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
dartx:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dartx
|
||||
sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
device_info_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -81,6 +97,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
diacritic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: diacritic
|
||||
sha256: "96db5db6149cbe4aa3cfcbfd170aca9b7648639be7e48025f9d458517f807fe4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.5"
|
||||
downloadsfolder:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: downloadsfolder
|
||||
sha256: e9987e56b998e3788047f977d31a1b50b4af58c388aefc3d157b9ac5c5d786a2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
easy_localization:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -381,6 +413,54 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
permission_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.3.1"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: b29a799ca03be9f999aa6c39f7de5209482d638e6f857f6b93b0875c618b7e54
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.0.7"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.4.5"
|
||||
permission_handler_html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_html
|
||||
sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.1"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_windows
|
||||
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -454,7 +534,7 @@ packages:
|
|||
source: hosted
|
||||
version: "0.1.9"
|
||||
shared_preferences:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
|
||||
|
|
@ -570,6 +650,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.2"
|
||||
time:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: time
|
||||
sha256: ad8e018a6c9db36cb917a031853a1aae49467a93e0d464683e029537d848c221
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ dependencies:
|
|||
equatable: ^2.0.5
|
||||
json_annotation: ^4.9.0
|
||||
bloc: ^8.1.4
|
||||
shared_preferences: ^2.2.3
|
||||
downloadsfolder: ^1.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
|
|
@ -6,14 +6,20 @@
|
|||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <downloadsfolder/downloadsfolder_plugin_c_api.h>
|
||||
#include <flutter_localization/flutter_localization_plugin_c_api.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
DownloadsfolderPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("DownloadsfolderPluginCApi"));
|
||||
FlutterLocalizationPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterLocalizationPluginCApi"));
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
downloadsfolder
|
||||
flutter_localization
|
||||
permission_handler_windows
|
||||
screen_retriever
|
||||
url_launcher_windows
|
||||
window_manager
|
||||
|
|
|
|||
Loading…
Reference in New Issue