mangayomi-mirror/lib/utils/extensions/others.dart
NBA2K1 0ed8ee2cd2 Move cacheDir creation to storage_provider
- Move the cacheDir creation to storage_provider from `others.dart`, `custom_extended_image_provider.dart` and `storage_usage.dart`.

- Use the correct directory, `getApplicationCacheDirectory()` instead of the `getTemporaryDirectory()` (which is being deleted by the OS regularly) for cache files.

- remove the `_cacheDownloadPath` from `storage_usage.dart` as the path is never being created in the first place, so using that path in `clearCache()` and `_getTotalDiskSpace()` is unnecessary.
2025-12-17 20:55:41 +01:00

159 lines
5.3 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:path/path.dart' as p;
extension FileFormatter on num {
String formattedFileSize({bool base1024 = true}) {
final base = base1024 ? 1024 : 1000;
if (this <= 0) return "0";
final units = ["B", "kB", "MB", "GB", "TB"];
int digitGroups = (log(this) / log(base)).round();
return "${NumberFormat("#,##0.#").format(this / pow(base, digitGroups))} ${units[digitGroups]}";
}
}
extension LetExtension<T> on T {
R let<R>(R Function(T) block) {
return block(this);
}
}
extension MedianExtension on List<int> {
int median() {
var middle = length ~/ 2;
if (length % 2 == 1) {
return this[middle];
} else {
return ((this[middle - 1] + this[middle]) / 2).round();
}
}
int arithmeticMean() {
return isNotEmpty ? (reduce((e1, e2) => e1 + e2) / length).round() : 0;
}
}
extension ImageProviderExtension on ImageProvider {
Future<Uint8List?> getBytes(
BuildContext context, {
ImageByteFormat format = ImageByteFormat.png,
}) async {
final imageStream = resolve(createLocalImageConfiguration(context));
final Completer<Uint8List?> completer = Completer<Uint8List?>();
final ImageStreamListener listener = ImageStreamListener((
imageInfo,
synchronousCall,
) async {
final bytes = await imageInfo.image.toByteData(format: format);
if (!completer.isCompleted) {
completer.complete(bytes?.buffer.asUint8List());
}
});
imageStream.addListener(listener);
final imageBytes = await completer.future;
imageStream.removeListener(listener);
return imageBytes;
}
}
extension UChapDataPreloadExtensions on UChapDataPreload {
Future<Uint8List?> get getImageBytes async {
Uint8List? imageBytes;
if (archiveImage != null) {
imageBytes = archiveImage;
} else if (isLocale == true && directory != null && index != null) {
imageBytes = File(
p.join(directory!.path, "${padIndex(index!)}.jpg"),
).readAsBytesSync();
} else {
File? cachedImage;
if (pageUrl != null) {
cachedImage = await _getCachedImageFile(pageUrl!.url);
if (cachedImage == null) {
await Future.delayed(const Duration(seconds: 3));
cachedImage = await _getCachedImageFile(pageUrl!.url);
}
}
imageBytes = cachedImage?.readAsBytesSync();
}
return imageBytes;
}
ImageProvider<Object> getImageProvider(
WidgetRef ref,
bool showCloudFlareError,
) {
final data = this;
if (data.isTransitionPage) {
return const AssetImage('assets/transparent.png')
as ImageProvider<Object>;
}
final isLocale = data.isLocale!;
final archiveImage = data.archiveImage;
final cropBorders = ref.watch(cropBordersStateProvider);
return cropBorders && data.cropImage != null
? ExtendedMemoryImageProvider(data.cropImage!)
: (isLocale
? archiveImage != null
? ExtendedMemoryImageProvider(archiveImage)
: ExtendedFileImageProvider(
File(
p.join(
data.directory!.path,
"${padIndex(data.index!)}.jpg",
),
),
)
: CustomExtendedNetworkImageProvider(
data.pageUrl!.url.trim().trimLeft().trimRight(),
cache: true,
cacheMaxAge: const Duration(days: 7),
showCloudFlareError: showCloudFlareError,
imageCacheFolderName: "cacheimagemanga",
headers: {
...data.pageUrl!.headers ?? {},
...ref.watch(
headersProvider(
source: data.chapter!.manga.value!.source!,
lang: data.chapter!.manga.value!.lang!,
sourceId: data.chapter!.manga.value!.sourceId,
),
),
},
))
as ImageProvider<Object>;
}
}
Future<File?> _getCachedImageFile(String url, {String? cacheKey}) async {
try {
final String key = cacheKey ?? keyToMd5(url);
final Directory cacheImagesDirectory = await StorageProvider()
.getCacheDirectory('cacheimagemanga');
if (cacheImagesDirectory.existsSync()) {
await for (final FileSystemEntity file in cacheImagesDirectory.list()) {
if (file.path.endsWith(key)) {
return File(file.path);
}
}
}
} catch (_) {
return null;
}
return null;
}