This commit is contained in:
kodjomoustapha 2024-09-04 11:41:19 +01:00
parent 606c59d70b
commit e2c7398903
5 changed files with 65 additions and 41 deletions

View file

@ -37,16 +37,14 @@ Future<List<PageUrl>> downloadChapter(
bool isOk = false;
final manga = chapter.manga.value!;
final path1 = await storageProvider.getDirectory();
final forbiddenCharacters =
RegExp(r'[\\/:*?"<>|\0]|(^CON$|^PRN$|^AUX$|^NUL$|^COM[1-9]$|^LPT[1-9]$)');
String scanlator = chapter.scanlator!.isNotEmpty
? "${chapter.scanlator!.replaceAll(forbiddenCharacters, '_')}_"
? "${chapter.scanlator!.replaceForbiddenCharacters('_')}_"
: "";
final chapterName = chapter.name!.replaceAll(forbiddenCharacters, ' ');
final chapterName = chapter.name!.replaceForbiddenCharacters(' ');
final isManga = chapter.manga.value!.isManga!;
final finalPath =
"downloads/${isManga ? "Manga" : "Anime"}/${manga.source} (${manga.lang!.toUpperCase()})/${manga.name!.replaceAll(forbiddenCharacters, '_')}${isManga ? "/$scanlator${chapter.name!.replaceAll(forbiddenCharacters, '_')}" : ""}";
"downloads/${isManga ? "Manga" : "Anime"}/${manga.source} (${manga.lang!.toUpperCase()})/${manga.name!.replaceForbiddenCharacters('_')}${isManga ? "/$scanlator${chapter.name!.replaceForbiddenCharacters('_')}" : ""}";
path = Directory("${path1!.path}$finalPath/");
Map<String, String> videoHeader = {};
@ -120,7 +118,7 @@ Future<List<PageUrl>> downloadChapter(
final path4 = Directory(
"${path5.path}${manga.source} (${manga.lang!.toUpperCase()})/");
final path3 = Directory(
"${path4.path}${manga.name!.replaceAll(forbiddenCharacters, '_')}/");
"${path4.path}${manga.name!.replaceForbiddenCharacters('_')}/");
if (!(await path1.exists())) {
await path1.create();

View file

@ -12,14 +12,12 @@ import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/models/track.dart';
import 'package:mangayomi/models/track_preference.dart';
import 'package:mangayomi/utils/extensions/string_extensions.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:path/path.dart' as path;
class StorageProvider {
final _forbiddenCharacters =
RegExp(r'[\\/:*?"<>|\0]|(^CON$|^PRN$|^AUX$|^NUL$|^COM[1-9]$|^LPT[1-9]$)');
Future<bool> requestPermission() async {
Permission permission = Permission.manageExternalStorage;
if (Platform.isAndroid) {
@ -80,12 +78,12 @@ class StorageProvider {
) async {
final manga = chapter.manga.value!;
String scanlator = chapter.scanlator?.isNotEmpty ?? false
? "${chapter.scanlator!.replaceAll(_forbiddenCharacters, '_')}_"
? "${chapter.scanlator!.replaceForbiddenCharacters('_')}_"
: "";
final isManga = chapter.manga.value!.isManga!;
final dir = await getDirectory();
return Directory(
"${dir!.path}/downloads/${isManga ? "Manga" : "Anime"}/${manga.source} (${manga.lang!.toUpperCase()})/${manga.name!.replaceAll(_forbiddenCharacters, '_')}/$scanlator${chapter.name!.replaceAll(_forbiddenCharacters, '_')}/");
"${dir!.path}/downloads/${isManga ? "Manga" : "Anime"}/${manga.source} (${manga.lang!.toUpperCase()})/${manga.name!.replaceForbiddenCharacters('_')}/$scanlator${chapter.name!.replaceForbiddenCharacters('_')}/");
}
Future<Directory?> getMangaMainDirectory(Chapter chapter) async {
@ -93,7 +91,7 @@ class StorageProvider {
final isManga = chapter.manga.value!.isManga!;
final dir = await getDirectory();
return Directory(
"${dir!.path}/downloads/${isManga ? "Manga" : "Anime"}/${manga.source} (${manga.lang!.toUpperCase()})/${manga.name!.replaceAll(_forbiddenCharacters, '_')}/");
"${dir!.path}/downloads/${isManga ? "Manga" : "Anime"}/${manga.source} (${manga.lang!.toUpperCase()})/${manga.name!.replaceForbiddenCharacters('_')}/");
}
Future<Directory?> getDatabaseDirectory() async {

View file

@ -35,9 +35,12 @@ base class Request {
/// Set [post] to make the request using POST instead of GET.
/// In the constructor, [post] must be one of the following:
/// - a String: POST request with [post] as the body, encoded in utf8
/// - a List of bytes: POST request with [post] as the body
/// - a Map: will be jsonEncoded to a String and set as the POST body
/// - a List of bytes: will be converted to a String using String.fromCharCodes
/// and set as the POST body
/// - a List: map will be jsonEncoded to a String and set as the POST body
///
/// The field [post] will be a UInt8List representing the bytes, or the String
/// The field [post] will be a String
final String? post;
/// Maximum number of retries the downloader should attempt
@ -59,7 +62,10 @@ base class Request {
/// [post] if set, uses POST instead of GET. Post must be one of the
/// following:
/// - a String: POST request with [post] as the body, encoded in utf8
/// - a List of bytes: POST request with [post] as the body
/// - a Map: will be jsonEncoded to a String and set as the POST body
/// - a List of bytes: will be converted to a String using String.fromCharCodes
/// and set as the POST body
/// - a List: map will be jsonEncoded to a String and set as the POST body
///
/// [retries] if >0 will retry a failed download this many times
Request(
@ -74,7 +80,11 @@ base class Request {
headers = headers ?? {},
httpRequestMethod =
httpRequestMethod?.toUpperCase() ?? (post == null ? 'GET' : 'POST'),
post = post is Uint8List ? String.fromCharCodes(post) : post,
post = post is Uint8List
? String.fromCharCodes(post)
: post is Map || post is List
? jsonEncode(post)
: post,
retriesRemaining = retries,
creationTime = creationTime ?? DateTime.now() {
if (retries < 0 || retries > 10) {
@ -255,7 +265,10 @@ sealed class Task extends Request implements Comparable {
/// [post] if set, uses POST instead of GET. Post must be one of the
/// following:
/// - a String: POST request with [post] as the body, encoded in utf8
/// - a List of bytes: POST request with [post] as the body
/// - a Map: will be jsonEncoded to a String and set as the POST body
/// - a List of bytes: will be converted to a String using String.fromCharCodes
/// and set as the POST body
/// - a List: map will be jsonEncoded to a String and set as the POST body
/// [directory] optional directory name, precedes [filename]
/// [baseDirectory] one of the base directories, precedes [directory]
/// [group] if set allows different callbacks or processing for different
@ -346,6 +359,10 @@ sealed class Task extends Request implements Comparable {
}
/// Returns the path to the directory represented by [baseDirectory]
///
/// On Windows, if [baseDirectory] is .root, returns the empty string
/// because the drive letter is required to be included in the directory
/// path
static Future<String> baseDirectoryPath(BaseDirectory baseDirectory) async {
Directory? externalStorageDirectory;
Directory? externalCacheDirectory;
@ -377,7 +394,9 @@ sealed class Task extends Request implements Comparable {
(BaseDirectory.applicationLibrary, true) =>
Directory(p.join(externalStorageDirectory!.path, 'Library'))
};
return baseDir.absolute.path;
return (Platform.isWindows && baseDirectory == BaseDirectory.root)
? ''
: baseDir.absolute.path;
}
/// Extract the baseDirectory, directory and filename from
@ -398,19 +417,20 @@ sealed class Task extends Request implements Comparable {
// try to match the start of the absoluteDirectory to one of the
// directories represented by the BaseDirectory enum.
// Order matters, as some may be subdirs of others
final testSequence = Platform.isAndroid || Platform.isLinux
? [
BaseDirectory.temporary,
BaseDirectory.applicationLibrary,
BaseDirectory.applicationSupport,
BaseDirectory.applicationDocuments
]
: [
BaseDirectory.temporary,
BaseDirectory.applicationSupport,
BaseDirectory.applicationLibrary,
BaseDirectory.applicationDocuments
];
final testSequence =
Platform.isAndroid || Platform.isLinux || Platform.isWindows
? [
BaseDirectory.temporary,
BaseDirectory.applicationLibrary,
BaseDirectory.applicationSupport,
BaseDirectory.applicationDocuments
]
: [
BaseDirectory.temporary,
BaseDirectory.applicationSupport,
BaseDirectory.applicationLibrary,
BaseDirectory.applicationDocuments
];
for (final baseDirectoryEnum in testSequence) {
final baseDirPath = await baseDirectoryPath(baseDirectoryEnum);
final (match, directory) = _contains(baseDirPath, absoluteDirectoryPath);
@ -437,7 +457,9 @@ sealed class Task extends Request implements Comparable {
/// [dirPath] should not contain a filename - if it does, it is returned
/// as part of the subdir.
static (bool, String) _contains(String baseDirPath, String dirPath) {
final match = RegExp('^$baseDirPath/?(.*)').firstMatch(dirPath);
final escapedBaseDirPath =
'$baseDirPath${Platform.pathSeparator}?'.replaceAll(r'\', r'\\');
final match = RegExp('^$escapedBaseDirPath(.*)').firstMatch(dirPath);
return (match != null, match?.group(1) ?? '');
}
@ -561,12 +583,11 @@ final class DownloadTask extends Task {
/// [httpRequestMethod] the HTTP request method used (e.g. GET, POST)
/// [post] if set, uses POST instead of GET. Post must be one of the
/// following:
/// - true: POST request without a body
/// - a String: POST request with [post] as the body, encoded in utf8 and
/// content-type 'text/plain'
/// - a List of bytes: POST request with [post] as the body
/// - a Map: POST request with [post] as form fields, encoded in utf8 and
/// content-type 'application/x-www-form-urlencoded'
/// - a String: POST request with [post] as the body, encoded in utf8
/// - a Map: will be jsonEncoded to a String and set as the POST body
/// - a List of bytes: will be converted to a String using String.fromCharCodes
/// and set as the POST body
/// - a List: map will be jsonEncoded to a String and set as the POST body
///
/// [directory] optional directory name, precedes [filename]
/// [baseDirectory] one of the base directories, precedes [directory]
@ -667,6 +688,7 @@ final class DownloadTask extends Task {
///
/// The suggested filename is obtained by making a HEAD request to the url
/// represented by the [DownloadTask], including urlQueryParameters and headers
/// Constant used with `filename` field to indicate server suggestion requested
static const suggestedFilename = '?';

View file

@ -8,6 +8,7 @@ import 'package:mangayomi/models/video.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/services/torrent_server.dart';
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:mangayomi/utils/extensions/string_extensions.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'get_video_list.g.dart';
@ -20,10 +21,8 @@ Future<(List<Video>, bool, String?)> getVideoList(
final mangaDirectory = await storageProvider.getMangaMainDirectory(episode);
final isLocalArchive = episode.manga.value!.isLocalArchive! &&
episode.manga.value!.source != "torrent";
final forbiddenCharacters =
RegExp(r'[\\/:*?"<>|\0]|(^CON$|^PRN$|^AUX$|^NUL$|^COM[1-9]$|^LPT[1-9]$)');
final mp4animePath =
"${mangaDirectory!.path}${episode.name!.replaceAll(forbiddenCharacters, ' ')}.mp4";
"${mangaDirectory!.path}${episode.name!.replaceForbiddenCharacters(' ')}.mp4";
if (await File(mp4animePath).exists() || isLocalArchive) {
final path = isLocalArchive ? episode.archivePath : mp4animePath;

View file

@ -36,6 +36,13 @@ extension StringExtensions on String {
return substring(leftIndex, rightIndex);
}
String replaceForbiddenCharacters(String source) {
return replaceAll(
RegExp(
r'[\\/:*?"<>|\0]|(^CON$|^PRN$|^AUX$|^NUL$|^COM[1-9]$|^LPT[1-9]$)'),
source);
}
bool isMediaVideo() {
return [
"3gp",