Merge branch 'feat/ffmpeg-conv' into 'main'
Feat/ffmpeg conv See merge request jscampucci/libu-notube!1
This commit is contained in:
commit
86142c0028
|
|
@ -10,6 +10,9 @@
|
||||||
.svn/
|
.svn/
|
||||||
migrate_working_dir/
|
migrate_working_dir/
|
||||||
|
|
||||||
|
#VsCode
|
||||||
|
.vscode/
|
||||||
|
|
||||||
# IntelliJ related
|
# IntelliJ related
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
|
|
@ -42,3 +45,5 @@ app.*.map.json
|
||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
|
samples/
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "notube",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "dart"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "notube (profile mode)",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "dart",
|
|
||||||
"flutterMode": "profile"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "notube (release mode)",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "dart",
|
|
||||||
"flutterMode": "release"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -8,8 +8,14 @@ const colorDarkRed = Color.fromRGBO(153, 0, 0, 1);
|
||||||
const colorMainBlue = Color(0xFF1f74ad);
|
const colorMainBlue = Color(0xFF1f74ad);
|
||||||
|
|
||||||
enum Format {
|
enum Format {
|
||||||
mp3(format: 'MP3'),
|
mp3(
|
||||||
mp3HD(format: 'MP3 HD'),
|
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'),
|
mp4(format: 'MP4'),
|
||||||
mp4HD(
|
mp4HD(
|
||||||
ytCmd:
|
ytCmd:
|
||||||
|
|
@ -19,16 +25,36 @@ enum Format {
|
||||||
ytCmd:
|
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",
|
"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'),
|
format: 'MP4 2K'),
|
||||||
tGP(format: '3GP'),
|
tGP(
|
||||||
flv(format: 'FLV'),
|
format: '3GP',
|
||||||
m4a;
|
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 ytCmd;
|
||||||
final String format;
|
final String format;
|
||||||
|
final String ffmpegCmd;
|
||||||
|
final String extension;
|
||||||
|
|
||||||
const Format({
|
const Format({
|
||||||
this.ytCmd =
|
this.ytCmd =
|
||||||
'"18/22/bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best"',
|
'"18/22/bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best"',
|
||||||
this.format = 'MP4',
|
this.format = 'MP4',
|
||||||
|
this.ffmpegCmd = '',
|
||||||
|
this.extension = '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const convertedFormats = [
|
||||||
|
Format.mp3,
|
||||||
|
Format.mp3HD,
|
||||||
|
Format.tGP,
|
||||||
|
Format.flv,
|
||||||
|
];
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:notube/models/Video.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:notube/constants.dart';
|
||||||
|
import 'package:notube/models/video.dart';
|
||||||
import 'package:notube/services/download.dart';
|
import 'package:notube/services/download.dart';
|
||||||
|
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
part 'dl_form_state.dart';
|
part 'dl_form_state.dart';
|
||||||
|
|
||||||
class DlFormCubit extends Cubit<DlFormState> {
|
class DlFormCubit extends Cubit<DlFormState> {
|
||||||
|
|
@ -14,7 +14,7 @@ class DlFormCubit extends Cubit<DlFormState> {
|
||||||
|
|
||||||
late DLServices dlService;
|
late DLServices dlService;
|
||||||
|
|
||||||
void setFormat(String newFormat) {
|
void setFormat(Format newFormat) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
format: newFormat,
|
format: newFormat,
|
||||||
));
|
));
|
||||||
|
|
@ -29,7 +29,7 @@ class DlFormCubit extends Cubit<DlFormState> {
|
||||||
|
|
||||||
void parseUrl() async {
|
void parseUrl() async {
|
||||||
dlService = await DLServices.init();
|
dlService = await DLServices.init();
|
||||||
var shellStream = await dlService.analyseUrl(state.url!);
|
var shellStream = await dlService.analyseUrl(state.url);
|
||||||
|
|
||||||
shellStream.listen((line) {
|
shellStream.listen((line) {
|
||||||
if (line[0] == '{') {
|
if (line[0] == '{') {
|
||||||
|
|
@ -41,13 +41,15 @@ class DlFormCubit extends Cubit<DlFormState> {
|
||||||
var playlistTitle = dataInfos['title'];
|
var playlistTitle = dataInfos['title'];
|
||||||
for (var videoTmp in dataInfos['entries']) {
|
for (var videoTmp in dataInfos['entries']) {
|
||||||
var video = Video.fromJson(videoTmp);
|
var video = Video.fromJson(videoTmp);
|
||||||
video.format = state.format;
|
videos.add(video.copyWith(
|
||||||
videos.add(video);
|
format: state.format,
|
||||||
|
filename: '${video.id}_${state.format.format}'));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var video = Video.fromJson(dataInfos);
|
var video = Video.fromJson(dataInfos);
|
||||||
video.format = state.format;
|
videos.add(video.copyWith(
|
||||||
videos.add(video);
|
format: state.format,
|
||||||
|
filename: '${video.id}_${state.format.format}'));
|
||||||
}
|
}
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isParsed: true,
|
isParsed: true,
|
||||||
|
|
@ -58,7 +60,7 @@ class DlFormCubit extends Cubit<DlFormState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearVideos() {
|
void clearForm() {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
videos: [],
|
videos: [],
|
||||||
isParsed: false,
|
isParsed: false,
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,21 @@ part of 'dl_form_cubit.dart';
|
||||||
class DlFormState extends Equatable {
|
class DlFormState extends Equatable {
|
||||||
const DlFormState({
|
const DlFormState({
|
||||||
this.url = '',
|
this.url = '',
|
||||||
this.format = 'MP3',
|
this.format = Format.mp4,
|
||||||
this.videos = const [],
|
this.videos = const [],
|
||||||
this.isParsed = false,
|
this.isParsed = false,
|
||||||
this.extractor = '',
|
this.extractor = '',
|
||||||
});
|
});
|
||||||
|
|
||||||
final String url;
|
final String url;
|
||||||
final String format;
|
final Format format;
|
||||||
final bool isParsed;
|
final bool isParsed;
|
||||||
final List<Video> videos;
|
final List<Video> videos;
|
||||||
final String extractor;
|
final String extractor;
|
||||||
|
|
||||||
DlFormState copyWith({
|
DlFormState copyWith({
|
||||||
String? url,
|
String? url,
|
||||||
String? format,
|
Format? format,
|
||||||
List<Video>? videos,
|
List<Video>? videos,
|
||||||
bool? isParsed,
|
bool? isParsed,
|
||||||
String? extractor,
|
String? extractor,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:notube/dlForm/cubit/dl_form_cubit.dart';
|
import 'package:notube/dlForm/cubit/dl_form_cubit.dart';
|
||||||
import 'package:notube/dlForm/formComponents/formatDropdown.dart';
|
import 'package:notube/dlForm/formComponents/format_dropdown.dart';
|
||||||
import 'package:notube/dlForm/formComponents/submitButton.dart';
|
import 'package:notube/dlForm/formComponents/submit_button.dart';
|
||||||
import 'package:notube/dlForm/formComponents/urlTextField.dart';
|
import 'package:notube/dlForm/formComponents/url_text_field.dart';
|
||||||
|
|
||||||
class DlForm extends StatelessWidget {
|
class DlForm extends StatelessWidget {
|
||||||
const DlForm({super.key});
|
const DlForm({super.key});
|
||||||
|
|
@ -69,8 +69,8 @@ class DebugDlFormState extends StatelessWidget {
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text('Url: ${url}'),
|
Text('Url: $url'),
|
||||||
Text('Format: ${format}'),
|
Text('Format: ${format.format}'),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -3,17 +3,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:notube/constants.dart';
|
import 'package:notube/constants.dart';
|
||||||
import 'package:notube/dlForm/cubit/dl_form_cubit.dart';
|
import 'package:notube/dlForm/cubit/dl_form_cubit.dart';
|
||||||
|
|
||||||
const List<String> formatList = <String>[
|
|
||||||
"MP3",
|
|
||||||
"MP3 HD",
|
|
||||||
"MP4",
|
|
||||||
"MP4 HD",
|
|
||||||
"MP4 2K",
|
|
||||||
"3GP",
|
|
||||||
"FLV",
|
|
||||||
"M4A",
|
|
||||||
];
|
|
||||||
|
|
||||||
class DropdownFormat extends StatelessWidget {
|
class DropdownFormat extends StatelessWidget {
|
||||||
const DropdownFormat({super.key});
|
const DropdownFormat({super.key});
|
||||||
|
|
||||||
|
|
@ -29,18 +18,20 @@ class DropdownFormat extends StatelessWidget {
|
||||||
|
|
||||||
// dropdown below..
|
// dropdown below..
|
||||||
child: DropdownButton<String>(
|
child: DropdownButton<String>(
|
||||||
value: dlFormState.format,
|
value: dlFormState.format.format,
|
||||||
elevation: 16,
|
elevation: 16,
|
||||||
style: const TextStyle(color: colorMainWhite),
|
style: const TextStyle(color: colorMainWhite),
|
||||||
dropdownColor: colorMainBlue,
|
dropdownColor: colorMainBlue,
|
||||||
onChanged: (String? value) {
|
onChanged: (String? value) {
|
||||||
context.read<DlFormCubit>().setFormat(value!);
|
var format =
|
||||||
|
Format.values.firstWhere((element) => element.format == value);
|
||||||
|
context.read<DlFormCubit>().setFormat(format!);
|
||||||
},
|
},
|
||||||
underline: Container(),
|
underline: Container(),
|
||||||
items: formatList.map<DropdownMenuItem<String>>((String value) {
|
items: Format.values.map<DropdownMenuItem<String>>((Format format) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: value,
|
value: format.format,
|
||||||
child: Text(value),
|
child: Text(format.format),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
));
|
));
|
||||||
|
|
@ -2,9 +2,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:notube/constants.dart';
|
import 'package:notube/constants.dart';
|
||||||
import 'package:notube/models/Video.dart';
|
import 'package:notube/models/video.dart';
|
||||||
import 'package:notube/videoList/cubit/videos_cubit.dart';
|
import 'package:notube/videoList/cubit/videos_cubit.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:notube/dlForm/cubit/dl_form_cubit.dart';
|
import 'package:notube/dlForm/cubit/dl_form_cubit.dart';
|
||||||
|
|
||||||
class SubmitButton extends StatefulWidget {
|
class SubmitButton extends StatefulWidget {
|
||||||
|
|
@ -23,7 +22,7 @@ class _SubmitButtonState extends State<SubmitButton> {
|
||||||
debugPrint('Adding video: $video');
|
debugPrint('Adding video: $video');
|
||||||
context.read<VideosCubit>().addVideo(video);
|
context.read<VideosCubit>().addVideo(video);
|
||||||
}
|
}
|
||||||
context.read<DlFormCubit>().clearVideos();
|
context.read<DlFormCubit>().clearForm();
|
||||||
context.read<VideosCubit>().startDownloader();
|
context.read<VideosCubit>().startDownloader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,6 @@ import 'package:window_manager/window_manager.dart';
|
||||||
import 'package:upgrader/upgrader.dart';
|
import 'package:upgrader/upgrader.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:notube/videoList/cubit/videos_cubit.dart';
|
import 'package:notube/videoList/cubit/videos_cubit.dart';
|
||||||
import 'package:notube/models/Video.dart';
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:notube/constants.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
class Video extends Equatable {
|
class Video extends Equatable {
|
||||||
Video({
|
Video({
|
||||||
this.id = '',
|
this.id = '',
|
||||||
|
|
@ -7,7 +10,7 @@ class Video extends Equatable {
|
||||||
this.extractor = '',
|
this.extractor = '',
|
||||||
this.isParsed = false,
|
this.isParsed = false,
|
||||||
this.status = 'pending',
|
this.status = 'pending',
|
||||||
this.format = 'MP4 HD',
|
this.format = Format.mp4,
|
||||||
this.title = '',
|
this.title = '',
|
||||||
this.thumbnail = '',
|
this.thumbnail = '',
|
||||||
this.description = '',
|
this.description = '',
|
||||||
|
|
@ -17,19 +20,19 @@ class Video extends Equatable {
|
||||||
this.filename = '',
|
this.filename = '',
|
||||||
});
|
});
|
||||||
|
|
||||||
String id;
|
final String id;
|
||||||
String url;
|
final String url;
|
||||||
String extractor;
|
final String extractor;
|
||||||
bool isParsed;
|
final bool isParsed;
|
||||||
String status;
|
final String status;
|
||||||
String format;
|
final Format format;
|
||||||
String title;
|
final String title;
|
||||||
String thumbnail;
|
final String thumbnail;
|
||||||
String description;
|
final String description;
|
||||||
String duration;
|
final String duration;
|
||||||
String uploader;
|
final String uploader;
|
||||||
String uploadDate;
|
final String uploadDate;
|
||||||
String filename;
|
final String filename;
|
||||||
|
|
||||||
factory Video.fromJson(Map<String, dynamic> json) {
|
factory Video.fromJson(Map<String, dynamic> json) {
|
||||||
return Video(
|
return Video(
|
||||||
|
|
@ -45,6 +48,39 @@ class Video extends Equatable {
|
||||||
uploadDate: json['upload_date'] ?? '');
|
uploadDate: json['upload_date'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//set Status
|
||||||
|
Video copyWith({
|
||||||
|
String? id,
|
||||||
|
String? url,
|
||||||
|
String? extractor,
|
||||||
|
bool? isParsed,
|
||||||
|
String? status,
|
||||||
|
Format? format,
|
||||||
|
String? title,
|
||||||
|
String? thumbnail,
|
||||||
|
String? description,
|
||||||
|
String? duration,
|
||||||
|
String? uploader,
|
||||||
|
String? uploadDate,
|
||||||
|
String? filename,
|
||||||
|
}) {
|
||||||
|
return Video(
|
||||||
|
id: id ?? this.id,
|
||||||
|
url: url ?? this.url,
|
||||||
|
extractor: extractor ?? this.extractor,
|
||||||
|
isParsed: isParsed ?? this.isParsed,
|
||||||
|
status: status ?? this.status,
|
||||||
|
format: format ?? this.format,
|
||||||
|
title: title ?? this.title,
|
||||||
|
thumbnail: thumbnail ?? this.thumbnail,
|
||||||
|
description: description ?? this.description,
|
||||||
|
duration: duration ?? this.duration,
|
||||||
|
uploader: uploader ?? this.uploader,
|
||||||
|
uploadDate: uploadDate ?? this.uploadDate,
|
||||||
|
filename: filename ?? this.filename,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [
|
List<Object> get props => [
|
||||||
id,
|
id,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:notube/constants.dart';
|
import 'package:notube/dlForm/dl_form.dart';
|
||||||
import 'package:notube/dlForm/dlForm.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:notube/videoList/cubit/videos_cubit.dart';
|
import 'package:notube/videoList/cubit/videos_cubit.dart';
|
||||||
import 'package:notube/models/Video.dart';
|
|
||||||
|
|
||||||
class Home extends StatelessWidget {
|
class Home extends StatelessWidget {
|
||||||
Home({super.key});
|
Home({super.key});
|
||||||
|
|
@ -44,15 +42,17 @@ class Home extends StatelessWidget {
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
var currentVideo = state.videoList[index];
|
var currentVideo = state.videoList[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: SizedBox(
|
leading: SizedBox(
|
||||||
width: 72,
|
width: 72,
|
||||||
height: 48,
|
height: 48,
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
currentVideo.thumbnail,
|
currentVideo.thumbnail,
|
||||||
fit: BoxFit.cover),
|
fit: BoxFit.cover),
|
||||||
),
|
),
|
||||||
title: Text(currentVideo.title),
|
title: Text(currentVideo.title),
|
||||||
subtitle: Text(currentVideo.status));
|
subtitle: Text(
|
||||||
|
'${currentVideo.status} - ${currentVideo.format.format}'),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
})),
|
})),
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:process_run/process_run.dart';
|
import 'package:process_run/process_run.dart';
|
||||||
|
import 'package:notube/models/video.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
class ConverterService {
|
class ConverterService {
|
||||||
late Directory tempDir;
|
late Directory tempDir;
|
||||||
|
|
@ -39,18 +42,47 @@ class ConverterService {
|
||||||
ffmpegPath = tempFile.path;
|
ffmpegPath = tempFile.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Stream> convertFile(String inputPath, String outputPath) async {
|
Future<File?> getTmpFile(String filename) async {
|
||||||
debugPrint('Converting $inputPath to $outputPath');
|
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 shellLinesController = ShellLinesController();
|
||||||
var shell = Shell(stdout: shellLinesController.sink);
|
var shell = Shell(
|
||||||
|
stdout: shellLinesController.sink, stderr: shellLinesController.sink);
|
||||||
|
|
||||||
await shell.run('''
|
await shell.run('''
|
||||||
$ffmpegPath $command
|
$ffmpegPath $command
|
||||||
''').then((result) {
|
''');
|
||||||
debugPrint('Analyse result: $result');
|
|
||||||
});
|
|
||||||
|
|
||||||
return shellLinesController.stream;
|
return shellLinesController.stream;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,12 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:notube/constants.dart';
|
import 'package:notube/constants.dart';
|
||||||
import 'package:notube/models/Video.dart';
|
import 'package:notube/models/video.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:process_run/shell.dart';
|
import 'package:process_run/shell.dart';
|
||||||
|
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
class DLServices {
|
class DLServices {
|
||||||
late Directory tempDir;
|
late Directory tempDir;
|
||||||
late String assetName;
|
late String assetName;
|
||||||
|
|
@ -57,7 +53,8 @@ class DLServices {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Stream> downloadFile(Video video) async {
|
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
|
https://youtu.be/playlist?list=PLk1fi9OrZmvGBdh9BdWIhZImGVDUqls1X
|
||||||
|
|
@ -66,12 +63,18 @@ class DLServices {
|
||||||
|
|
||||||
// get the format code from the Format enum
|
// get the format code from the Format enum
|
||||||
var formatCmd =
|
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');
|
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 =
|
var command =
|
||||||
'${video.url.trim()} --sub-langs "all,-live_chat" --embed-subs --embed-thumbnail --embed-metadata --progress -o "temp/%(playlist)s/${cleanTitle}_${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 shellLinesController = ShellLinesController();
|
||||||
var shell = Shell(stdout: shellLinesController.sink, verbose: false);
|
var shell = Shell(stdout: shellLinesController.sink, verbose: false);
|
||||||
|
|
@ -88,7 +91,7 @@ class DLServices {
|
||||||
var command = '${url.trim()} -q --flat-playlist -J';
|
var command = '${url.trim()} -q --flat-playlist -J';
|
||||||
|
|
||||||
var shellLinesController = ShellLinesController();
|
var shellLinesController = ShellLinesController();
|
||||||
var shell = Shell(stdout: shellLinesController.sink);
|
var shell = Shell(stdout: shellLinesController.sink, verbose: false);
|
||||||
|
|
||||||
await shell.run('''
|
await shell.run('''
|
||||||
$ytDlpPath $command
|
$ytDlpPath $command
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
import 'dart:math';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/converter.dart';
|
||||||
import 'package:notube/services/download.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';
|
part 'videos_state.dart';
|
||||||
|
|
||||||
|
|
@ -28,6 +31,17 @@ class VideosCubit extends Cubit<VideosState> {
|
||||||
emit(VideosState(videoList: _videoList));
|
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() {
|
void clearVideos() {
|
||||||
_videoList.clear();
|
_videoList.clear();
|
||||||
emit(VideosState(videoList: _videoList));
|
emit(VideosState(videoList: _videoList));
|
||||||
|
|
@ -38,10 +52,20 @@ class VideosCubit extends Cubit<VideosState> {
|
||||||
emit(VideosState(videoList: _videoList));
|
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 {
|
Future<void> startDownloader() async {
|
||||||
dlService = await DLServices.init();
|
dlService = await DLServices.init();
|
||||||
downloadingVid =
|
downloadingVid =
|
||||||
_videoList.where((video) => video.status != 'downloaded').take(3);
|
_videoList.where((video) => video.status == 'pending').take(3);
|
||||||
|
|
||||||
debugPrint('Videos to download: ${downloadingVid.length}');
|
debugPrint('Videos to download: ${downloadingVid.length}');
|
||||||
while (downloadingVid.isNotEmpty && runningTasks < maxConcurrentTasks) {
|
while (downloadingVid.isNotEmpty && runningTasks < maxConcurrentTasks) {
|
||||||
|
|
@ -53,6 +77,37 @@ class VideosCubit extends Cubit<VideosState> {
|
||||||
_videoList.where((video) => video.status != 'downloaded');
|
_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) {
|
for (Video video in videosLeft) {
|
||||||
final shellStream = await dlService.downloadFile(video.url, video.format);
|
final shellStream = await dlService.downloadFile(video.url, video.format);
|
||||||
|
|
@ -70,17 +125,59 @@ class VideosCubit extends Cubit<VideosState> {
|
||||||
|
|
||||||
Future convertVideo(Video video) async {
|
Future convertVideo(Video video) async {
|
||||||
converterService = await ConverterService.init();
|
converterService = await ConverterService.init();
|
||||||
debugPrint('Converting ${video.title} to $video.format ');
|
debugPrint('Converting ${video.title} to ${video.format.format}');
|
||||||
final shellStream =
|
changeStatus(video, 'converting');
|
||||||
await converterService.convertFile(video.url, video.format);
|
final shellStream = await converterService.convertFile(video);
|
||||||
shellStream.listen((line) {
|
var duration = '0.0';
|
||||||
debugPrint(line);
|
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 {
|
Future downloadVideo(Video video) async {
|
||||||
debugPrint(
|
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);
|
final shellStream = await dlService.downloadFile(video);
|
||||||
var completer = Completer<void>();
|
var completer = Completer<void>();
|
||||||
|
|
||||||
|
|
@ -92,22 +189,19 @@ class VideosCubit extends Cubit<VideosState> {
|
||||||
var percentString =
|
var percentString =
|
||||||
line.split('[download]')[1].split(' of')[0].split('%')[0].trim();
|
line.split('[download]')[1].split(' of')[0].split('%')[0].trim();
|
||||||
var percent = percentString == null ? 0 : double.parse(percentString);
|
var percent = percentString == null ? 0 : double.parse(percentString);
|
||||||
video.status = 'downloading $percent%';
|
changeStatus(video, 'downloading $percent%');
|
||||||
emit(VideosState(videoList: _videoList));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.contains('[EmbedThumbnail]')) {
|
if (line.contains('[EmbedThumbnail]')) {
|
||||||
video.status = 'downloaded';
|
changeStatus(video, 'downloaded');
|
||||||
video.filename =
|
|
||||||
line.split('Adding thumbnail to "')[1].split('"')[0].trim() ?? '';
|
|
||||||
emit(VideosState(videoList: _videoList));
|
|
||||||
if (!completer.isCompleted) {
|
if (!completer.isCompleted) {
|
||||||
debugPrint('Download completed');
|
debugPrint('____Download completed___');
|
||||||
runningTasks--;
|
runningTasks--;
|
||||||
completer.complete();
|
completer.complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debugPrint(line);
|
//debugPrint(line);
|
||||||
},
|
},
|
||||||
onError: (error) {
|
onError: (error) {
|
||||||
if (!completer.isCompleted) {
|
if (!completer.isCompleted) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
|
import downloadsfolder
|
||||||
import flutter_localization
|
import flutter_localization
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
|
|
@ -16,6 +17,7 @@ import window_manager
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
|
DownloadsfolderPlugin.register(with: registry.registrar(forPlugin: "DownloadsfolderPlugin"))
|
||||||
FlutterLocalizationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalizationPlugin"))
|
FlutterLocalizationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalizationPlugin"))
|
||||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
|
|
||||||
92
pubspec.lock
92
pubspec.lock
|
|
@ -18,7 +18,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.0"
|
version: "2.11.0"
|
||||||
bloc:
|
bloc:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: bloc
|
name: bloc
|
||||||
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
|
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
|
||||||
|
|
@ -57,6 +57,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.3"
|
||||||
csslib:
|
csslib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -65,6 +73,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
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:
|
device_info_plus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -81,6 +97,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.0"
|
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:
|
easy_localization:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -381,6 +413,54 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
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:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -454,7 +534,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.9"
|
version: "0.1.9"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
|
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
|
||||||
|
|
@ -570,6 +650,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.7.2"
|
||||||
|
time:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: time
|
||||||
|
sha256: ad8e018a6c9db36cb917a031853a1aae49467a93e0d464683e029537d848c221
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ dependencies:
|
||||||
flutter_bloc: ^8.1.6
|
flutter_bloc: ^8.1.6
|
||||||
equatable: ^2.0.5
|
equatable: ^2.0.5
|
||||||
json_annotation: ^4.9.0
|
json_annotation: ^4.9.0
|
||||||
|
bloc: ^8.1.4
|
||||||
|
shared_preferences: ^2.2.3
|
||||||
|
downloadsfolder: ^1.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,20 @@
|
||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <downloadsfolder/downloadsfolder_plugin_c_api.h>
|
||||||
#include <flutter_localization/flutter_localization_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 <screen_retriever/screen_retriever_plugin.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
DownloadsfolderPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("DownloadsfolderPluginCApi"));
|
||||||
FlutterLocalizationPluginCApiRegisterWithRegistrar(
|
FlutterLocalizationPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterLocalizationPluginCApi"));
|
registry->GetRegistrarForPlugin("FlutterLocalizationPluginCApi"));
|
||||||
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
downloadsfolder
|
||||||
flutter_localization
|
flutter_localization
|
||||||
|
permission_handler_windows
|
||||||
screen_retriever
|
screen_retriever
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
window_manager
|
window_manager
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue