mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-21 07:41:58 +00:00
Merge branch 'temp/mpv-anime4k' into feature/mpv-anime4k
This commit is contained in:
commit
38728ce0cb
49 changed files with 1690 additions and 382 deletions
|
|
@ -12,6 +12,11 @@ class MChapterBridge {
|
||||||
url: namedArgs.get<String?>('url'),
|
url: namedArgs.get<String?>('url'),
|
||||||
dateUpload: namedArgs.get<String?>('dateUpload'),
|
dateUpload: namedArgs.get<String?>('dateUpload'),
|
||||||
scanlator: namedArgs.get<String?>('scanlator'),
|
scanlator: namedArgs.get<String?>('scanlator'),
|
||||||
|
isFiller: namedArgs.get<bool?>('isFiller'),
|
||||||
|
thumbnailUrl: namedArgs.get<String?>('scanlator'),
|
||||||
|
description: namedArgs.get<String?>('scanlator'),
|
||||||
|
downloadSize: namedArgs.get<String?>('scanlator'),
|
||||||
|
duration: namedArgs.get<String?>('scanlator'),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -20,6 +25,11 @@ class MChapterBridge {
|
||||||
'url': (visitor, target) => (target as MChapter).url,
|
'url': (visitor, target) => (target as MChapter).url,
|
||||||
'dateUpload': (visitor, target) => (target as MChapter).dateUpload,
|
'dateUpload': (visitor, target) => (target as MChapter).dateUpload,
|
||||||
'scanlator': (visitor, target) => (target as MChapter).scanlator,
|
'scanlator': (visitor, target) => (target as MChapter).scanlator,
|
||||||
|
'isFiller': (visitor, target) => (target as MChapter).isFiller,
|
||||||
|
'thumbnailUrl': (visitor, target) => (target as MChapter).thumbnailUrl,
|
||||||
|
'description': (visitor, target) => (target as MChapter).description,
|
||||||
|
'downloadSize': (visitor, target) => (target as MChapter).downloadSize,
|
||||||
|
'duration': (visitor, target) => (target as MChapter).duration,
|
||||||
},
|
},
|
||||||
setters: {
|
setters: {
|
||||||
'name': (visitor, target, value) =>
|
'name': (visitor, target, value) =>
|
||||||
|
|
@ -30,6 +40,16 @@ class MChapterBridge {
|
||||||
(target as MChapter).dateUpload = value as String?,
|
(target as MChapter).dateUpload = value as String?,
|
||||||
'scanlator': (visitor, target, value) =>
|
'scanlator': (visitor, target, value) =>
|
||||||
(target as MChapter).scanlator = value as String?,
|
(target as MChapter).scanlator = value as String?,
|
||||||
|
'isFiller': (visitor, target, value) =>
|
||||||
|
(target as MChapter).isFiller = value as bool?,
|
||||||
|
'thumbnailUrl': (visitor, target, value) =>
|
||||||
|
(target as MChapter).thumbnailUrl = value as String?,
|
||||||
|
'description': (visitor, target, value) =>
|
||||||
|
(target as MChapter).description = value as String?,
|
||||||
|
'downloadSize': (visitor, target, value) =>
|
||||||
|
(target as MChapter).downloadSize = value as String?,
|
||||||
|
'duration': (visitor, target, value) =>
|
||||||
|
(target as MChapter).duration = value as String?,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
void registerBridgedClasses(D4rt interpreter) {
|
void registerBridgedClasses(D4rt interpreter) {
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,41 @@ class MChapter {
|
||||||
String? dateUpload;
|
String? dateUpload;
|
||||||
|
|
||||||
String? scanlator;
|
String? scanlator;
|
||||||
MChapter({this.name, this.url, this.dateUpload, this.scanlator});
|
|
||||||
|
bool? isFiller;
|
||||||
|
|
||||||
|
String? thumbnailUrl;
|
||||||
|
|
||||||
|
String? description;
|
||||||
|
|
||||||
|
/// video size
|
||||||
|
String? downloadSize;
|
||||||
|
|
||||||
|
/// video duration
|
||||||
|
String? duration;
|
||||||
|
|
||||||
|
MChapter({
|
||||||
|
this.name,
|
||||||
|
this.url,
|
||||||
|
this.dateUpload,
|
||||||
|
this.scanlator,
|
||||||
|
this.isFiller = false,
|
||||||
|
this.thumbnailUrl,
|
||||||
|
this.description,
|
||||||
|
this.downloadSize,
|
||||||
|
this.duration,
|
||||||
|
});
|
||||||
factory MChapter.fromJson(Map<String, dynamic> json) {
|
factory MChapter.fromJson(Map<String, dynamic> json) {
|
||||||
return MChapter(
|
return MChapter(
|
||||||
name: json['name'],
|
name: json['name'],
|
||||||
url: json['url'],
|
url: json['url'],
|
||||||
dateUpload: json['dateUpload'],
|
dateUpload: json['dateUpload'],
|
||||||
scanlator: json['scanlator'],
|
scanlator: json['scanlator'],
|
||||||
|
isFiller: json['isFiller'] ?? false,
|
||||||
|
thumbnailUrl: json['thumbnailUrl'],
|
||||||
|
description: json['description'],
|
||||||
|
downloadSize: json['downloadSize'],
|
||||||
|
duration: json['duration'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
|
|
@ -20,5 +48,10 @@ class MChapter {
|
||||||
'url': url,
|
'url': url,
|
||||||
'dateUpload': dateUpload,
|
'dateUpload': dateUpload,
|
||||||
'scanlator': scanlator,
|
'scanlator': scanlator,
|
||||||
|
'isFiller': isFiller,
|
||||||
|
'thumbnailUrl': thumbnailUrl,
|
||||||
|
'description': description,
|
||||||
|
'downloadSize': downloadSize,
|
||||||
|
'duration': duration,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -472,4 +472,7 @@
|
||||||
"sync_enable_histories": "Sync history data",
|
"sync_enable_histories": "Sync history data",
|
||||||
"sync_enable_updates": "Sync update data",
|
"sync_enable_updates": "Sync update data",
|
||||||
"sync_enable_settings": "Sync settings"
|
"sync_enable_settings": "Sync settings"
|
||||||
|
"anime4K": "Enable Anime4K",
|
||||||
|
"anime4K_info": "Supports .js scripts under /mpv/scripts/",
|
||||||
|
"anime4K_download": "MPV config files are required!\nDownload now?"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2902,6 +2902,24 @@ abstract class AppLocalizations {
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Sync settings'**
|
/// **'Sync settings'**
|
||||||
String get sync_enable_settings;
|
String get sync_enable_settings;
|
||||||
|
|
||||||
|
/// No description provided for @anime4K.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Enable Anime4K'**
|
||||||
|
String get anime4K;
|
||||||
|
|
||||||
|
/// No description provided for @anime4K_info.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Supports .js scripts under /mpv/scripts/'**
|
||||||
|
String get anime4K_info;
|
||||||
|
|
||||||
|
/// No description provided for @anime4K_download.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'MPV config files are required!\nDownload now?'**
|
||||||
|
String get anime4K_download;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
class _AppLocalizationsDelegate
|
||||||
|
|
|
||||||
|
|
@ -1494,4 +1494,14 @@ class AppLocalizationsAr extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1507,4 +1507,14 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1495,4 +1495,14 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1512,6 +1512,16 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The translations for Spanish Castilian, as used in Latin America and the Caribbean (`es_419`).
|
/// The translations for Spanish Castilian, as used in Latin America and the Caribbean (`es_419`).
|
||||||
|
|
|
||||||
|
|
@ -1513,4 +1513,14 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1501,4 +1501,14 @@ class AppLocalizationsId extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1510,4 +1510,14 @@ class AppLocalizationsIt extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1509,6 +1509,16 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The translations for Portuguese, as used in Brazil (`pt_BR`).
|
/// The translations for Portuguese, as used in Brazil (`pt_BR`).
|
||||||
|
|
|
||||||
|
|
@ -1511,4 +1511,14 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1495,4 +1495,14 @@ class AppLocalizationsTh extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1501,4 +1501,14 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1466,4 +1466,14 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sync_enable_settings => 'Sync settings';
|
String get sync_enable_settings => 'Sync settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K => 'Enable Anime4K';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_info => 'Supports .js scripts under /mpv/scripts/';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get anime4K_download =>
|
||||||
|
'MPV config files are required!\nDownload now?';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import 'package:mangayomi/utils/discord_rpc.dart';
|
||||||
import 'package:mangayomi/utils/url_protocol/api.dart';
|
import 'package:mangayomi/utils/url_protocol/api.dart';
|
||||||
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_provider.dart';
|
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_provider.dart';
|
||||||
import 'package:mangayomi/modules/library/providers/file_scanner.dart';
|
import 'package:mangayomi/modules/library/providers/file_scanner.dart';
|
||||||
|
// ignore: depend_on_referenced_packages
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,18 @@ class Chapter {
|
||||||
///Only for local archive Comic
|
///Only for local archive Comic
|
||||||
String? archivePath;
|
String? archivePath;
|
||||||
|
|
||||||
|
bool? isFiller;
|
||||||
|
|
||||||
|
String? thumbnailUrl;
|
||||||
|
|
||||||
|
String? description;
|
||||||
|
|
||||||
|
/// video size
|
||||||
|
String? downloadSize;
|
||||||
|
|
||||||
|
/// video duration
|
||||||
|
String? duration;
|
||||||
|
|
||||||
int? updatedAt;
|
int? updatedAt;
|
||||||
|
|
||||||
final manga = IsarLink<Manga>();
|
final manga = IsarLink<Manga>();
|
||||||
|
|
@ -41,6 +53,11 @@ class Chapter {
|
||||||
this.isRead = false,
|
this.isRead = false,
|
||||||
this.lastPageRead = '',
|
this.lastPageRead = '',
|
||||||
this.archivePath = '',
|
this.archivePath = '',
|
||||||
|
this.isFiller = false,
|
||||||
|
this.thumbnailUrl,
|
||||||
|
this.description,
|
||||||
|
this.downloadSize,
|
||||||
|
this.duration,
|
||||||
this.updatedAt = 0,
|
this.updatedAt = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -55,6 +72,11 @@ class Chapter {
|
||||||
name = json['name'];
|
name = json['name'];
|
||||||
scanlator = json['scanlator'];
|
scanlator = json['scanlator'];
|
||||||
url = json['url'];
|
url = json['url'];
|
||||||
|
isFiller = json['isFiller'] ?? false;
|
||||||
|
thumbnailUrl = json['thumbnailUrl'];
|
||||||
|
description = json['description'];
|
||||||
|
downloadSize = json['downloadSize'];
|
||||||
|
duration = json['duration'];
|
||||||
updatedAt = json['updatedAt'];
|
updatedAt = json['updatedAt'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,6 +91,11 @@ class Chapter {
|
||||||
'name': name,
|
'name': name,
|
||||||
'scanlator': scanlator,
|
'scanlator': scanlator,
|
||||||
'url': url,
|
'url': url,
|
||||||
|
'isFiller': isFiller,
|
||||||
|
'thumbnailUrl': thumbnailUrl,
|
||||||
|
'description': description,
|
||||||
|
'downloadSize': downloadSize,
|
||||||
|
'duration': duration,
|
||||||
'updatedAt': updatedAt ?? 0,
|
'updatedAt': updatedAt ?? 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -258,6 +258,8 @@ class Settings {
|
||||||
|
|
||||||
bool? rpcShowCoverImage;
|
bool? rpcShowCoverImage;
|
||||||
|
|
||||||
|
bool? useAnime4K;
|
||||||
|
|
||||||
Settings({
|
Settings({
|
||||||
this.id = 227,
|
this.id = 227,
|
||||||
this.updatedAt = 0,
|
this.updatedAt = 0,
|
||||||
|
|
@ -373,6 +375,7 @@ class Settings {
|
||||||
this.rpcShowReadingWatchingProgress = true,
|
this.rpcShowReadingWatchingProgress = true,
|
||||||
this.rpcShowTitle = true,
|
this.rpcShowTitle = true,
|
||||||
this.rpcShowCoverImage = true,
|
this.rpcShowCoverImage = true,
|
||||||
|
this.useAnime4K = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Settings.fromJson(Map<String, dynamic> json) {
|
Settings.fromJson(Map<String, dynamic> json) {
|
||||||
|
|
@ -594,6 +597,7 @@ class Settings {
|
||||||
rpcShowReadingWatchingProgress = json['rpcShowReadingWatchingProgress'];
|
rpcShowReadingWatchingProgress = json['rpcShowReadingWatchingProgress'];
|
||||||
rpcShowTitle = json['rpcShowTitle'];
|
rpcShowTitle = json['rpcShowTitle'];
|
||||||
rpcShowCoverImage = json['rpcShowCoverImage'];
|
rpcShowCoverImage = json['rpcShowCoverImage'];
|
||||||
|
useAnime4K = json['useAnime4K'];
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
|
|
@ -732,6 +736,7 @@ class Settings {
|
||||||
'rpcShowReadingWatchingProgress': rpcShowReadingWatchingProgress,
|
'rpcShowReadingWatchingProgress': rpcShowReadingWatchingProgress,
|
||||||
'rpcShowTitle': rpcShowTitle,
|
'rpcShowTitle': rpcShowTitle,
|
||||||
'rpcShowCoverImage': rpcShowCoverImage,
|
'rpcShowCoverImage': rpcShowCoverImage,
|
||||||
|
'useAnime4K': useAnime4K,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:ffi';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:bot_toast/bot_toast.dart';
|
import 'package:bot_toast/bot_toast.dart';
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
@ -31,6 +34,7 @@ import 'package:mangayomi/services/torrent_server.dart';
|
||||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||||
import 'package:mangayomi/utils/language.dart';
|
import 'package:mangayomi/utils/language.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
|
import 'package:media_kit/generated/libmpv/bindings.dart' as generated;
|
||||||
import 'package:media_kit_video/media_kit_video.dart';
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
|
@ -74,7 +78,7 @@ class _AnimePlayerViewState extends riv.ConsumerState<AnimePlayerView> {
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
||||||
return serversData.when(
|
return serversData.when(
|
||||||
data: (data) {
|
data: (data) {
|
||||||
final (videos, isLocal, infoHashList) = data;
|
final (videos, isLocal, infoHashList, mpvDirectory) = data;
|
||||||
_infoHashList = infoHashList;
|
_infoHashList = infoHashList;
|
||||||
if (videos.isEmpty && !(episode.manga.value!.isLocalArchive ?? false)) {
|
if (videos.isEmpty && !(episode.manga.value!.isLocalArchive ?? false)) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
@ -102,6 +106,7 @@ class _AnimePlayerViewState extends riv.ConsumerState<AnimePlayerView> {
|
||||||
desktopFullScreenPlayer: (value) {
|
desktopFullScreenPlayer: (value) {
|
||||||
desktopFullScreenPlayer = value;
|
desktopFullScreenPlayer = value;
|
||||||
},
|
},
|
||||||
|
mpvDirectory: mpvDirectory,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
error: (error, stackTrace) => Scaffold(
|
error: (error, stackTrace) => Scaffold(
|
||||||
|
|
@ -150,6 +155,7 @@ class AnimeStreamPage extends riv.ConsumerStatefulWidget {
|
||||||
final String defaultSubtitle;
|
final String defaultSubtitle;
|
||||||
final bool isLocal;
|
final bool isLocal;
|
||||||
final bool isTorrent;
|
final bool isTorrent;
|
||||||
|
final Directory? mpvDirectory;
|
||||||
final void Function(bool) desktopFullScreenPlayer;
|
final void Function(bool) desktopFullScreenPlayer;
|
||||||
const AnimeStreamPage({
|
const AnimeStreamPage({
|
||||||
super.key,
|
super.key,
|
||||||
|
|
@ -159,6 +165,7 @@ class AnimeStreamPage extends riv.ConsumerStatefulWidget {
|
||||||
required this.episode,
|
required this.episode,
|
||||||
required this.isTorrent,
|
required this.isTorrent,
|
||||||
required this.desktopFullScreenPlayer,
|
required this.desktopFullScreenPlayer,
|
||||||
|
required this.mpvDirectory,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -175,8 +182,15 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
with TickerProviderStateMixin, WidgetsBindingObserver {
|
with TickerProviderStateMixin, WidgetsBindingObserver {
|
||||||
late final GlobalKey<VideoState> _key = GlobalKey<VideoState>();
|
late final GlobalKey<VideoState> _key = GlobalKey<VideoState>();
|
||||||
late final useLibass = ref.read(useLibassStateProvider);
|
late final useLibass = ref.read(useLibassStateProvider);
|
||||||
|
late final useAnime4K = ref.read(useAnime4KStateProvider);
|
||||||
late final Player _player = Player(
|
late final Player _player = Player(
|
||||||
configuration: PlayerConfiguration(libass: useLibass),
|
configuration: PlayerConfiguration(
|
||||||
|
libass: useLibass,
|
||||||
|
config: true,
|
||||||
|
configDir: useAnime4K ? widget.mpvDirectory?.path ?? "" : "",
|
||||||
|
observeProperties: {},
|
||||||
|
eventHandler: _handleMpvEvents,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
late final hwdecMode = ref.read(hwdecModeStateProvider());
|
late final hwdecMode = ref.read(hwdecModeStateProvider());
|
||||||
late final VideoController _controller = VideoController(
|
late final VideoController _controller = VideoController(
|
||||||
|
|
@ -249,6 +263,25 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Future<void> _handleMpvEvents(Pointer<generated.mpv_event> event) async {
|
||||||
|
try {
|
||||||
|
if (event.ref.event_id ==
|
||||||
|
generated.mpv_event_id.MPV_EVENT_PROPERTY_CHANGE) {
|
||||||
|
final prop = event.ref.data.cast<generated.mpv_event_property>();
|
||||||
|
if (prop.ref.name.cast<Utf8>().toDartString() ==
|
||||||
|
"user-data/aniyomi/dummy_number" &&
|
||||||
|
prop.ref.format == generated.mpv_format.MPV_FORMAT_INT64) {
|
||||||
|
final number = prop.ref.data.cast<Int64>().value;
|
||||||
|
botToast("Dummy number: $number");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrint(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void pushToNewEpisode(BuildContext context, Chapter episode) {
|
void pushToNewEpisode(BuildContext context, Chapter episode) {
|
||||||
widget.desktopFullScreenPlayer.call(ref.read(fullscreenProvider));
|
widget.desktopFullScreenPlayer.call(ref.read(fullscreenProvider));
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
|
|
@ -1033,6 +1066,34 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
onPressed: () => _videoSettingDraggableMenu(context),
|
onPressed: () => _videoSettingDraggableMenu(context),
|
||||||
icon: const Icon(Icons.video_settings, color: Colors.white),
|
icon: const Icon(Icons.video_settings, color: Colors.white),
|
||||||
),
|
),
|
||||||
|
if (useAnime4K)
|
||||||
|
PopupMenuButton<String>(
|
||||||
|
tooltip: '', // Remove default tooltip "Show menu" for consistency
|
||||||
|
icon: const Icon(Icons.high_quality, color: Colors.white),
|
||||||
|
itemBuilder: (context) =>
|
||||||
|
[
|
||||||
|
("Anime4K: Mode A (Fast)", "CTRL+1"),
|
||||||
|
("Anime4K: Mode B (Fast)", "CTRL+2"),
|
||||||
|
("Anime4K: Mode C (Fast)", "CTRL+3"),
|
||||||
|
("Anime4K: Mode A+A (Fast)", "CTRL+4"),
|
||||||
|
("Anime4K: Mode B+B (Fast)", "CTRL+5"),
|
||||||
|
("Anime4K: Mode C+A (Fast)", "CTRL+6"),
|
||||||
|
("Clear GLSL shaders", "CTRL+0"),
|
||||||
|
]
|
||||||
|
.map(
|
||||||
|
(mode) => PopupMenuItem<String>(
|
||||||
|
value: mode.$1,
|
||||||
|
child: Text(mode.$1),
|
||||||
|
onTap: () {
|
||||||
|
(_player.platform as dynamic).command([
|
||||||
|
"keydown",
|
||||||
|
mode.$2,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
PopupMenuButton<double>(
|
PopupMenuButton<double>(
|
||||||
tooltip: '', // Remove default tooltip "Show menu" for consistency
|
tooltip: '', // Remove default tooltip "Show menu" for consistency
|
||||||
icon: const Icon(Icons.speed, color: Colors.white),
|
icon: const Icon(Icons.speed, color: Colors.white),
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,27 @@ class _DesktopControllerWidgetState
|
||||||
final desktopFullScreenPlayer = widget.desktopFullScreenPlayer;
|
final desktopFullScreenPlayer = widget.desktopFullScreenPlayer;
|
||||||
await _changeFullScreen(ref, desktopFullScreenPlayer, value: false);
|
await _changeFullScreen(ref, desktopFullScreenPlayer, value: false);
|
||||||
},
|
},
|
||||||
|
const SingleActivator(LogicalKeyboardKey.digit0, control: true): () {
|
||||||
|
(widget.videoController.player.platform as dynamic).command(["keydown", "CTRL+0"]);
|
||||||
|
},
|
||||||
|
const SingleActivator(LogicalKeyboardKey.digit1, control: true): () {
|
||||||
|
(widget.videoController.player.platform as dynamic).command(["keydown", "CTRL+1"]);
|
||||||
|
},
|
||||||
|
const SingleActivator(LogicalKeyboardKey.digit2, control: true): () {
|
||||||
|
(widget.videoController.player.platform as dynamic).command(["keydown", "CTRL+2"]);
|
||||||
|
},
|
||||||
|
const SingleActivator(LogicalKeyboardKey.digit3, control: true): () {
|
||||||
|
(widget.videoController.player.platform as dynamic).command(["keydown", "CTRL+3"]);
|
||||||
|
},
|
||||||
|
const SingleActivator(LogicalKeyboardKey.digit4, control: true): () {
|
||||||
|
(widget.videoController.player.platform as dynamic).command(["keydown", "CTRL+4"]);
|
||||||
|
},
|
||||||
|
const SingleActivator(LogicalKeyboardKey.digit5, control: true): () {
|
||||||
|
(widget.videoController.player.platform as dynamic).command(["keydown", "CTRL+5"]);
|
||||||
|
},
|
||||||
|
const SingleActivator(LogicalKeyboardKey.digit6, control: true): () {
|
||||||
|
(widget.videoController.player.platform as dynamic).command(["keydown", "CTRL+6"]);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,11 @@ Future<dynamic> updateMangaDetail(
|
||||||
scanlator: chaps[i].scanlator ?? '',
|
scanlator: chaps[i].scanlator ?? '',
|
||||||
mangaId: mangaId,
|
mangaId: mangaId,
|
||||||
updatedAt: DateTime.now().millisecondsSinceEpoch,
|
updatedAt: DateTime.now().millisecondsSinceEpoch,
|
||||||
|
isFiller: chaps[i].isFiller,
|
||||||
|
thumbnailUrl: chaps[i].thumbnailUrl,
|
||||||
|
description: chaps[i].description,
|
||||||
|
downloadSize: chaps[i].downloadSize,
|
||||||
|
duration: chaps[i].duration,
|
||||||
)..manga.value = manga;
|
)..manga.value = manga;
|
||||||
chapters.add(chapter);
|
chapters.add(chapter);
|
||||||
}
|
}
|
||||||
|
|
@ -115,6 +120,11 @@ Future<dynamic> updateMangaDetail(
|
||||||
oldChap.url = newChap.url;
|
oldChap.url = newChap.url;
|
||||||
oldChap.scanlator = newChap.scanlator;
|
oldChap.scanlator = newChap.scanlator;
|
||||||
oldChap.updatedAt = DateTime.now().millisecondsSinceEpoch;
|
oldChap.updatedAt = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
oldChap.isFiller = newChap.isFiller;
|
||||||
|
oldChap.thumbnailUrl = newChap.thumbnailUrl;
|
||||||
|
oldChap.description = newChap.description;
|
||||||
|
oldChap.downloadSize = newChap.downloadSize;
|
||||||
|
oldChap.duration = newChap.duration;
|
||||||
isar.chapters.putSync(oldChap);
|
isar.chapters.putSync(oldChap);
|
||||||
oldChap.manga.saveSync();
|
oldChap.manga.saveSync();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ part of 'update_manga_detail_providers.dart';
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$updateMangaDetailHash() => r'ce51918a48b315c3555b3de4e602bd998e00a992';
|
String _$updateMangaDetailHash() => r'f75938777640ae0cfee181a2df7a12a56c42db41';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:mangayomi/main.dart';
|
import 'package:mangayomi/main.dart';
|
||||||
import 'package:mangayomi/models/settings.dart';
|
import 'package:mangayomi/models/settings.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/image_view_paged.dart';
|
import 'package:mangayomi/modules/manga/reader/image_view_paged.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.dart';
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/circular_progress_indicator_animate_rotate.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/circular_progress_indicator_animate_rotate.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/transition_view_paged.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/transition_view_paged.dart';
|
||||||
import 'package:mangayomi/modules/more/settings/reader/reader_screen.dart';
|
import 'package:mangayomi/modules/more/settings/reader/reader_screen.dart';
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'package:extended_image/extended_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:mangayomi/models/settings.dart';
|
import 'package:mangayomi/models/settings.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/image_view_paged.dart';
|
import 'package:mangayomi/modules/manga/reader/image_view_paged.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.dart';
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/circular_progress_indicator_animate_rotate.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/circular_progress_indicator_animate_rotate.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/transition_view_vertical.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/transition_view_vertical.dart';
|
||||||
import 'package:mangayomi/modules/more/settings/reader/reader_screen.dart';
|
import 'package:mangayomi/modules/more/settings/reader/reader_screen.dart';
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'package:extended_image/extended_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart';
|
import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.dart';
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/color_filter_widget.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/color_filter_widget.dart';
|
||||||
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
||||||
import 'package:mangayomi/utils/extensions/others.dart';
|
import 'package:mangayomi/utils/extensions/others.dart';
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'package:extended_image/extended_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart';
|
import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.dart';
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/color_filter_widget.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/color_filter_widget.dart';
|
||||||
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
||||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.dart';
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
import 'package:mangayomi/src/rust/api/image.dart';
|
import 'package:mangayomi/src/rust/api/image.dart';
|
||||||
import 'package:mangayomi/src/rust/frb_generated.dart';
|
import 'package:mangayomi/src/rust/frb_generated.dart';
|
||||||
import 'package:mangayomi/utils/extensions/others.dart';
|
import 'package:mangayomi/utils/extensions/others.dart';
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,16 @@ import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||||
import 'package:mangayomi/main.dart';
|
import 'package:mangayomi/main.dart';
|
||||||
import 'package:mangayomi/models/chapter.dart';
|
import 'package:mangayomi/models/chapter.dart';
|
||||||
import 'package:mangayomi/models/manga.dart';
|
import 'package:mangayomi/models/manga.dart';
|
||||||
import 'package:mangayomi/models/page.dart';
|
|
||||||
import 'package:mangayomi/models/settings.dart';
|
import 'package:mangayomi/models/settings.dart';
|
||||||
import 'package:mangayomi/modules/anime/widgets/desktop.dart';
|
import 'package:mangayomi/modules/anime/widgets/desktop.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/providers/crop_borders_provider.dart';
|
import 'package:mangayomi/modules/manga/reader/providers/crop_borders_provider.dart';
|
||||||
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/btn_chapter_list_dialog.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/btn_chapter_list_dialog.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/double_columm_view_center.dart';
|
import 'package:mangayomi/modules/manga/reader/double_columm_view_center.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/providers/color_filter_provider.dart';
|
import 'package:mangayomi/modules/manga/reader/providers/color_filter_provider.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/color_filter_widget.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/color_filter_widget.dart';
|
||||||
|
import 'package:mangayomi/modules/manga/reader/widgets/custom_popup_menu_button.dart';
|
||||||
|
import 'package:mangayomi/modules/manga/reader/widgets/custom_value_indicator_shape.dart';
|
||||||
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
||||||
import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart';
|
import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart';
|
||||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||||
|
|
@ -1633,7 +1635,7 @@ class _MangaChapterPageGalleryState
|
||||||
return SliderTheme(
|
return SliderTheme(
|
||||||
data: SliderTheme.of(context).copyWith(
|
data: SliderTheme.of(context).copyWith(
|
||||||
valueIndicatorShape:
|
valueIndicatorShape:
|
||||||
_CustomValueIndicatorShape(
|
CustomValueIndicatorShape(
|
||||||
tranform: _isReverseHorizontal,
|
tranform: _isReverseHorizontal,
|
||||||
),
|
),
|
||||||
overlayShape:
|
overlayShape:
|
||||||
|
|
@ -2504,190 +2506,3 @@ class _MangaChapterPageGalleryState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UChapDataPreload {
|
|
||||||
Chapter? chapter;
|
|
||||||
Directory? directory;
|
|
||||||
PageUrl? pageUrl;
|
|
||||||
bool? isLocale;
|
|
||||||
Uint8List? archiveImage;
|
|
||||||
int? index;
|
|
||||||
GetChapterPagesModel? chapterUrlModel;
|
|
||||||
int? pageIndex;
|
|
||||||
Uint8List? cropImage;
|
|
||||||
bool isTransitionPage;
|
|
||||||
Chapter? nextChapter;
|
|
||||||
String? mangaName;
|
|
||||||
bool? isLastChapter;
|
|
||||||
|
|
||||||
UChapDataPreload(
|
|
||||||
this.chapter,
|
|
||||||
this.directory,
|
|
||||||
this.pageUrl,
|
|
||||||
this.isLocale,
|
|
||||||
this.archiveImage,
|
|
||||||
this.index,
|
|
||||||
this.chapterUrlModel,
|
|
||||||
this.pageIndex, {
|
|
||||||
this.cropImage,
|
|
||||||
this.isTransitionPage = false,
|
|
||||||
this.nextChapter,
|
|
||||||
this.mangaName,
|
|
||||||
this.isLastChapter = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
UChapDataPreload.transition({
|
|
||||||
required Chapter currentChapter,
|
|
||||||
required this.nextChapter,
|
|
||||||
required String this.mangaName,
|
|
||||||
required int this.pageIndex,
|
|
||||||
this.isLastChapter = false,
|
|
||||||
}) : chapter = currentChapter,
|
|
||||||
isTransitionPage = true,
|
|
||||||
directory = null,
|
|
||||||
pageUrl = null,
|
|
||||||
isLocale = null,
|
|
||||||
archiveImage = null,
|
|
||||||
index = null,
|
|
||||||
chapterUrlModel = null,
|
|
||||||
cropImage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CustomPopupMenuButton<T> extends StatelessWidget {
|
|
||||||
final String label;
|
|
||||||
final String title;
|
|
||||||
final ValueChanged<T> onSelected;
|
|
||||||
final T value;
|
|
||||||
final List<T> list;
|
|
||||||
final String Function(T) itemText;
|
|
||||||
const CustomPopupMenuButton({
|
|
||||||
super.key,
|
|
||||||
required this.label,
|
|
||||||
required this.title,
|
|
||||||
required this.onSelected,
|
|
||||||
required this.value,
|
|
||||||
required this.list,
|
|
||||||
required this.itemText,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
||||||
child: PopupMenuButton(
|
|
||||||
popUpAnimationStyle: popupAnimationStyle,
|
|
||||||
tooltip: "",
|
|
||||||
offset: Offset.fromDirection(1),
|
|
||||||
color: Colors.black,
|
|
||||||
onSelected: onSelected,
|
|
||||||
itemBuilder: (context) => [
|
|
||||||
for (var d in list)
|
|
||||||
PopupMenuItem(
|
|
||||||
value: d,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.check,
|
|
||||||
color: d == value ? Colors.white : Colors.transparent,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 7),
|
|
||||||
Text(
|
|
||||||
itemText(d),
|
|
||||||
style: const TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
label,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).textTheme.bodyLarge!.color!.withValues(alpha: 0.9),
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(title),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
const Icon(Icons.keyboard_arrow_down_outlined),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CustomValueIndicatorShape extends SliderComponentShape {
|
|
||||||
final _indicatorShape = const PaddleSliderValueIndicatorShape();
|
|
||||||
final bool tranform;
|
|
||||||
const _CustomValueIndicatorShape({this.tranform = false});
|
|
||||||
@override
|
|
||||||
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
|
|
||||||
return const Size(40, 40);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(
|
|
||||||
PaintingContext context,
|
|
||||||
Offset center, {
|
|
||||||
required Animation<double> activationAnimation,
|
|
||||||
required Animation<double> enableAnimation,
|
|
||||||
required bool isDiscrete,
|
|
||||||
required TextPainter labelPainter,
|
|
||||||
required RenderBox parentBox,
|
|
||||||
required SliderThemeData sliderTheme,
|
|
||||||
required TextDirection textDirection,
|
|
||||||
required double value,
|
|
||||||
required double textScaleFactor,
|
|
||||||
required Size sizeWithOverflow,
|
|
||||||
}) {
|
|
||||||
final textSpan = TextSpan(
|
|
||||||
text: labelPainter.text?.toPlainText(),
|
|
||||||
style: sliderTheme.valueIndicatorTextStyle,
|
|
||||||
);
|
|
||||||
|
|
||||||
final textPainter = TextPainter(
|
|
||||||
text: textSpan,
|
|
||||||
textAlign: labelPainter.textAlign,
|
|
||||||
textDirection: textDirection,
|
|
||||||
);
|
|
||||||
|
|
||||||
textPainter.layout();
|
|
||||||
|
|
||||||
context.canvas.save();
|
|
||||||
context.canvas.translate(center.dx, center.dy);
|
|
||||||
context.canvas.scale(tranform ? -1.0 : 1.0, 1.0);
|
|
||||||
context.canvas.translate(-center.dx, -center.dy);
|
|
||||||
|
|
||||||
_indicatorShape.paint(
|
|
||||||
context,
|
|
||||||
center,
|
|
||||||
activationAnimation: activationAnimation,
|
|
||||||
enableAnimation: enableAnimation,
|
|
||||||
labelPainter: textPainter,
|
|
||||||
parentBox: parentBox,
|
|
||||||
sliderTheme: sliderTheme,
|
|
||||||
value: value,
|
|
||||||
textScaleFactor: textScaleFactor,
|
|
||||||
sizeWithOverflow: sizeWithOverflow,
|
|
||||||
isDiscrete: isDiscrete,
|
|
||||||
textDirection: textDirection,
|
|
||||||
);
|
|
||||||
|
|
||||||
context.canvas.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
54
lib/modules/manga/reader/u_chap_data_preload.dart
Normal file
54
lib/modules/manga/reader/u_chap_data_preload.dart
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:mangayomi/models/chapter.dart';
|
||||||
|
import 'package:mangayomi/models/page.dart';
|
||||||
|
import 'package:mangayomi/services/get_chapter_pages.dart';
|
||||||
|
|
||||||
|
class UChapDataPreload {
|
||||||
|
Chapter? chapter;
|
||||||
|
Directory? directory;
|
||||||
|
PageUrl? pageUrl;
|
||||||
|
bool? isLocale;
|
||||||
|
Uint8List? archiveImage;
|
||||||
|
int? index;
|
||||||
|
GetChapterPagesModel? chapterUrlModel;
|
||||||
|
int? pageIndex;
|
||||||
|
Uint8List? cropImage;
|
||||||
|
bool isTransitionPage;
|
||||||
|
Chapter? nextChapter;
|
||||||
|
String? mangaName;
|
||||||
|
bool? isLastChapter;
|
||||||
|
|
||||||
|
UChapDataPreload(
|
||||||
|
this.chapter,
|
||||||
|
this.directory,
|
||||||
|
this.pageUrl,
|
||||||
|
this.isLocale,
|
||||||
|
this.archiveImage,
|
||||||
|
this.index,
|
||||||
|
this.chapterUrlModel,
|
||||||
|
this.pageIndex, {
|
||||||
|
this.cropImage,
|
||||||
|
this.isTransitionPage = false,
|
||||||
|
this.nextChapter,
|
||||||
|
this.mangaName,
|
||||||
|
this.isLastChapter = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
UChapDataPreload.transition({
|
||||||
|
required Chapter currentChapter,
|
||||||
|
required this.nextChapter,
|
||||||
|
required String this.mangaName,
|
||||||
|
required int this.pageIndex,
|
||||||
|
this.isLastChapter = false,
|
||||||
|
}) : chapter = currentChapter,
|
||||||
|
isTransitionPage = true,
|
||||||
|
directory = null,
|
||||||
|
pageUrl = null,
|
||||||
|
isLocale = null,
|
||||||
|
archiveImage = null,
|
||||||
|
index = null,
|
||||||
|
chapterUrlModel = null,
|
||||||
|
cropImage = null;
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
import 'package:mangayomi/models/settings.dart';
|
import 'package:mangayomi/models/settings.dart';
|
||||||
import 'package:mangayomi/models/chapter.dart';
|
import 'package:mangayomi/models/chapter.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/virtual_scrolling/virtual_page_manager.dart';
|
import 'package:mangayomi/modules/manga/reader/virtual_scrolling/virtual_page_manager.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.dart' as reader;
|
|
||||||
import 'package:mangayomi/modules/manga/reader/image_view_vertical.dart';
|
import 'package:mangayomi/modules/manga/reader/image_view_vertical.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/double_columm_view_vertical.dart';
|
import 'package:mangayomi/modules/manga/reader/double_columm_view_vertical.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/transition_view_vertical.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/transition_view_vertical.dart';
|
||||||
|
|
@ -21,7 +21,7 @@ class VirtualMangaList extends ConsumerStatefulWidget {
|
||||||
final double minCacheExtent;
|
final double minCacheExtent;
|
||||||
final int initialScrollIndex;
|
final int initialScrollIndex;
|
||||||
final ScrollPhysics physics;
|
final ScrollPhysics physics;
|
||||||
final Function(reader.UChapDataPreload data) onLongPressData;
|
final Function(UChapDataPreload data) onLongPressData;
|
||||||
final Function(bool) onFailedToLoadImage;
|
final Function(bool) onFailedToLoadImage;
|
||||||
final BackgroundColor backgroundColor;
|
final BackgroundColor backgroundColor;
|
||||||
final bool isDoublePageMode;
|
final bool isDoublePageMode;
|
||||||
|
|
@ -215,7 +215,7 @@ class _VirtualMangaListState extends ConsumerState<VirtualMangaList> {
|
||||||
final int index1 = index * 2 - 1;
|
final int index1 = index * 2 - 1;
|
||||||
final int index2 = index1 + 1;
|
final int index2 = index1 + 1;
|
||||||
|
|
||||||
final List<reader.UChapDataPreload?> datas = index == 0
|
final List<UChapDataPreload?> datas = index == 0
|
||||||
? [widget.pageManager.getOriginalPage(0), null]
|
? [widget.pageManager.getOriginalPage(0), null]
|
||||||
: [
|
: [
|
||||||
index1 < widget.pageManager.pageCount
|
index1 < widget.pageManager.pageCount
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.dart' as reader;
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
|
|
||||||
/// Page loading states for virtual scrolling
|
/// Page loading states for virtual scrolling
|
||||||
enum PageLoadState { notLoaded, loading, loaded, error, cached }
|
enum PageLoadState { notLoaded, loading, loaded, error, cached }
|
||||||
|
|
@ -10,7 +10,7 @@ enum PageLoadState { notLoaded, loading, loaded, error, cached }
|
||||||
/// Virtual page information for tracking state
|
/// Virtual page information for tracking state
|
||||||
class VirtualPageInfo {
|
class VirtualPageInfo {
|
||||||
final int index;
|
final int index;
|
||||||
final reader.UChapDataPreload originalData;
|
final UChapDataPreload originalData;
|
||||||
PageLoadState loadState;
|
PageLoadState loadState;
|
||||||
DateTime? lastAccessTime;
|
DateTime? lastAccessTime;
|
||||||
Object? error;
|
Object? error;
|
||||||
|
|
@ -56,7 +56,7 @@ class VirtualPageConfig {
|
||||||
|
|
||||||
/// Manages virtual page loading and memory optimization
|
/// Manages virtual page loading and memory optimization
|
||||||
class VirtualPageManager extends ChangeNotifier {
|
class VirtualPageManager extends ChangeNotifier {
|
||||||
final List<reader.UChapDataPreload> _originalPages;
|
final List<UChapDataPreload> _originalPages;
|
||||||
final VirtualPageConfig config;
|
final VirtualPageConfig config;
|
||||||
final Map<int, VirtualPageInfo> _pageInfoMap = {};
|
final Map<int, VirtualPageInfo> _pageInfoMap = {};
|
||||||
final Set<int> _preloadQueue = {};
|
final Set<int> _preloadQueue = {};
|
||||||
|
|
@ -65,7 +65,7 @@ class VirtualPageManager extends ChangeNotifier {
|
||||||
Timer? _cleanupTimer;
|
Timer? _cleanupTimer;
|
||||||
|
|
||||||
VirtualPageManager({
|
VirtualPageManager({
|
||||||
required List<reader.UChapDataPreload> pages,
|
required List<UChapDataPreload> pages,
|
||||||
this.config = const VirtualPageConfig(),
|
this.config = const VirtualPageConfig(),
|
||||||
}) : _originalPages = List.from(pages) {
|
}) : _originalPages = List.from(pages) {
|
||||||
_initializePages();
|
_initializePages();
|
||||||
|
|
@ -108,7 +108,7 @@ class VirtualPageManager extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get original page data
|
/// Get original page data
|
||||||
reader.UChapDataPreload? getOriginalPage(int index) {
|
UChapDataPreload? getOriginalPage(int index) {
|
||||||
if (index < 0 || index >= _originalPages.length) return null;
|
if (index < 0 || index >= _originalPages.length) return null;
|
||||||
return _originalPages[index];
|
return _originalPages[index];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
import 'package:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
import 'package:photo_view/photo_view_gallery.dart';
|
import 'package:photo_view/photo_view_gallery.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
|
|
@ -7,11 +8,10 @@ import 'package:mangayomi/models/settings.dart';
|
||||||
import 'package:mangayomi/models/chapter.dart';
|
import 'package:mangayomi/models/chapter.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/virtual_scrolling/virtual_page_manager.dart';
|
import 'package:mangayomi/modules/manga/reader/virtual_scrolling/virtual_page_manager.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/virtual_scrolling/virtual_manga_list.dart';
|
import 'package:mangayomi/modules/manga/reader/virtual_scrolling/virtual_manga_list.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.dart' as reader;
|
|
||||||
|
|
||||||
/// Provides virtual page manager instances
|
/// Provides virtual page manager instances
|
||||||
final virtualPageManagerProvider =
|
final virtualPageManagerProvider =
|
||||||
Provider.family<VirtualPageManager, List<reader.UChapDataPreload>>((
|
Provider.family<VirtualPageManager, List<UChapDataPreload>>((
|
||||||
ref,
|
ref,
|
||||||
pages,
|
pages,
|
||||||
) {
|
) {
|
||||||
|
|
@ -20,7 +20,7 @@ final virtualPageManagerProvider =
|
||||||
|
|
||||||
/// Main widget for virtual reading that replaces ScrollablePositionedList
|
/// Main widget for virtual reading that replaces ScrollablePositionedList
|
||||||
class VirtualReaderView extends ConsumerStatefulWidget {
|
class VirtualReaderView extends ConsumerStatefulWidget {
|
||||||
final List<reader.UChapDataPreload> pages;
|
final List<UChapDataPreload> pages;
|
||||||
final ItemScrollController itemScrollController;
|
final ItemScrollController itemScrollController;
|
||||||
final ScrollOffsetController scrollOffsetController;
|
final ScrollOffsetController scrollOffsetController;
|
||||||
final ItemPositionsListener itemPositionsListener;
|
final ItemPositionsListener itemPositionsListener;
|
||||||
|
|
@ -28,7 +28,7 @@ class VirtualReaderView extends ConsumerStatefulWidget {
|
||||||
final double minCacheExtent;
|
final double minCacheExtent;
|
||||||
final int initialScrollIndex;
|
final int initialScrollIndex;
|
||||||
final ScrollPhysics physics;
|
final ScrollPhysics physics;
|
||||||
final Function(reader.UChapDataPreload data) onLongPressData;
|
final Function(UChapDataPreload data) onLongPressData;
|
||||||
final Function(bool) onFailedToLoadImage;
|
final Function(bool) onFailedToLoadImage;
|
||||||
final BackgroundColor backgroundColor;
|
final BackgroundColor backgroundColor;
|
||||||
final bool isDoublePageMode;
|
final bool isDoublePageMode;
|
||||||
|
|
@ -169,10 +169,10 @@ mixin VirtualPageManagerMixin<T extends ConsumerStatefulWidget>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Override this method to provide the pages list
|
/// Override this method to provide the pages list
|
||||||
List<reader.UChapDataPreload> getPages();
|
List<UChapDataPreload> getPages();
|
||||||
|
|
||||||
/// Call this when pages change
|
/// Call this when pages change
|
||||||
void updateVirtualPages(List<reader.UChapDataPreload> newPages) {
|
void updateVirtualPages(List<UChapDataPreload> newPages) {
|
||||||
_virtualPageManager?.dispose();
|
_virtualPageManager?.dispose();
|
||||||
_virtualPageManager = VirtualPageManager(pages: newPages);
|
_virtualPageManager = VirtualPageManager(pages: newPages);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mangayomi/utils/global_style.dart';
|
||||||
|
|
||||||
|
class CustomPopupMenuButton<T> extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
final String title;
|
||||||
|
final ValueChanged<T> onSelected;
|
||||||
|
final T value;
|
||||||
|
final List<T> list;
|
||||||
|
final String Function(T) itemText;
|
||||||
|
const CustomPopupMenuButton({
|
||||||
|
super.key,
|
||||||
|
required this.label,
|
||||||
|
required this.title,
|
||||||
|
required this.onSelected,
|
||||||
|
required this.value,
|
||||||
|
required this.list,
|
||||||
|
required this.itemText,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
|
child: PopupMenuButton(
|
||||||
|
popUpAnimationStyle: popupAnimationStyle,
|
||||||
|
tooltip: "",
|
||||||
|
offset: Offset.fromDirection(1),
|
||||||
|
color: Colors.black,
|
||||||
|
onSelected: onSelected,
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
for (var d in list)
|
||||||
|
PopupMenuItem(
|
||||||
|
value: d,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.check,
|
||||||
|
color: d == value ? Colors.white : Colors.transparent,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 7),
|
||||||
|
Text(
|
||||||
|
itemText(d),
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodyLarge!.color!.withValues(alpha: 0.9),
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(title),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
const Icon(Icons.keyboard_arrow_down_outlined),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CustomValueIndicatorShape extends SliderComponentShape {
|
||||||
|
final _indicatorShape = const PaddleSliderValueIndicatorShape();
|
||||||
|
final bool tranform;
|
||||||
|
const CustomValueIndicatorShape({this.tranform = false});
|
||||||
|
@override
|
||||||
|
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
|
||||||
|
return const Size(40, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(
|
||||||
|
PaintingContext context,
|
||||||
|
Offset center, {
|
||||||
|
required Animation<double> activationAnimation,
|
||||||
|
required Animation<double> enableAnimation,
|
||||||
|
required bool isDiscrete,
|
||||||
|
required TextPainter labelPainter,
|
||||||
|
required RenderBox parentBox,
|
||||||
|
required SliderThemeData sliderTheme,
|
||||||
|
required TextDirection textDirection,
|
||||||
|
required double value,
|
||||||
|
required double textScaleFactor,
|
||||||
|
required Size sizeWithOverflow,
|
||||||
|
}) {
|
||||||
|
final textSpan = TextSpan(
|
||||||
|
text: labelPainter.text?.toPlainText(),
|
||||||
|
style: sliderTheme.valueIndicatorTextStyle,
|
||||||
|
);
|
||||||
|
|
||||||
|
final textPainter = TextPainter(
|
||||||
|
text: textSpan,
|
||||||
|
textAlign: labelPainter.textAlign,
|
||||||
|
textDirection: textDirection,
|
||||||
|
);
|
||||||
|
|
||||||
|
textPainter.layout();
|
||||||
|
|
||||||
|
context.canvas.save();
|
||||||
|
context.canvas.translate(center.dx, center.dy);
|
||||||
|
context.canvas.scale(tranform ? -1.0 : 1.0, 1.0);
|
||||||
|
context.canvas.translate(-center.dx, -center.dy);
|
||||||
|
|
||||||
|
_indicatorShape.paint(
|
||||||
|
context,
|
||||||
|
center,
|
||||||
|
activationAnimation: activationAnimation,
|
||||||
|
enableAnimation: enableAnimation,
|
||||||
|
labelPainter: textPainter,
|
||||||
|
parentBox: parentBox,
|
||||||
|
sliderTheme: sliderTheme,
|
||||||
|
value: value,
|
||||||
|
textScaleFactor: textScaleFactor,
|
||||||
|
sizeWithOverflow: sizeWithOverflow,
|
||||||
|
isDiscrete: isDiscrete,
|
||||||
|
textDirection: textDirection,
|
||||||
|
);
|
||||||
|
|
||||||
|
context.canvas.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.dart';
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/chapter_transition_page.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/chapter_transition_page.dart';
|
||||||
|
|
||||||
class TransitionViewPaged extends ConsumerWidget {
|
class TransitionViewPaged extends ConsumerWidget {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.dart';
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/chapter_transition_page.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/chapter_transition_page.dart';
|
||||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,43 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:archive/archive.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:mangayomi/modules/more/settings/player/providers/player_state_provider.dart';
|
import 'package:mangayomi/modules/more/settings/player/providers/player_state_provider.dart';
|
||||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||||
|
import 'package:mangayomi/providers/storage_provider.dart';
|
||||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||||
import 'package:mangayomi/utils/language.dart';
|
import 'package:mangayomi/utils/language.dart';
|
||||||
import 'package:numberpicker/numberpicker.dart';
|
import 'package:numberpicker/numberpicker.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:super_sliver_list/super_sliver_list.dart';
|
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||||
import 'package:mangayomi/l10n/generated/app_localizations.dart';
|
import 'package:mangayomi/l10n/generated/app_localizations.dart';
|
||||||
|
|
||||||
class PlayerScreen extends ConsumerWidget {
|
class PlayerScreen extends ConsumerStatefulWidget {
|
||||||
const PlayerScreen({super.key});
|
const PlayerScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
ConsumerState<PlayerScreen> createState() => _PlayerScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
||||||
|
int _total = 0;
|
||||||
|
int _received = 0;
|
||||||
|
late http.StreamedResponse _response;
|
||||||
|
final List<int> _bytes = [];
|
||||||
|
late StreamSubscription<List<int>>? _subscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_subscription?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
final defaultSubtitleLang = ref.watch(defaultSubtitleLangStateProvider);
|
final defaultSubtitleLang = ref.watch(defaultSubtitleLangStateProvider);
|
||||||
final markEpisodeAsSeenType = ref.watch(markEpisodeAsSeenTypeStateProvider);
|
final markEpisodeAsSeenType = ref.watch(markEpisodeAsSeenTypeStateProvider);
|
||||||
final defaultSkipIntroLength = ref.watch(
|
final defaultSkipIntroLength = ref.watch(
|
||||||
|
|
@ -26,6 +51,7 @@ class PlayerScreen extends ConsumerWidget {
|
||||||
final enableAutoSkip = ref.watch(enableAutoSkipStateProvider);
|
final enableAutoSkip = ref.watch(enableAutoSkipStateProvider);
|
||||||
final aniSkipTimeoutLength = ref.watch(aniSkipTimeoutLengthStateProvider);
|
final aniSkipTimeoutLength = ref.watch(aniSkipTimeoutLengthStateProvider);
|
||||||
final useLibass = ref.watch(useLibassStateProvider);
|
final useLibass = ref.watch(useLibassStateProvider);
|
||||||
|
final useAnime4K = ref.watch(useAnime4KStateProvider);
|
||||||
final hwdecMode = ref.watch(hwdecModeStateProvider(rawValue: true));
|
final hwdecMode = ref.watch(hwdecModeStateProvider(rawValue: true));
|
||||||
|
|
||||||
final fullScreenPlayer = ref.watch(fullScreenPlayerStateProvider);
|
final fullScreenPlayer = ref.watch(fullScreenPlayerStateProvider);
|
||||||
|
|
@ -457,6 +483,20 @@ class PlayerScreen extends ConsumerWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
SwitchListTile(
|
||||||
|
value: useAnime4K,
|
||||||
|
title: Text(context.l10n.anime4K),
|
||||||
|
subtitle: Text(
|
||||||
|
context.l10n.anime4K_info,
|
||||||
|
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||||
|
),
|
||||||
|
onChanged: (value) async {
|
||||||
|
if (value && !(await _checkAnime4K(context))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ref.read(useAnime4KStateProvider.notifier).set(value);
|
||||||
|
},
|
||||||
|
),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
value: fullScreenPlayer,
|
value: fullScreenPlayer,
|
||||||
title: Text(context.l10n.full_screen_player),
|
title: Text(context.l10n.full_screen_player),
|
||||||
|
|
@ -501,7 +541,11 @@ class PlayerScreen extends ConsumerWidget {
|
||||||
groupValue: hwdecMode,
|
groupValue: hwdecMode,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
ref
|
ref
|
||||||
.read(hwdecModeStateProvider(rawValue: true).notifier)
|
.read(
|
||||||
|
hwdecModeStateProvider(
|
||||||
|
rawValue: true,
|
||||||
|
).notifier,
|
||||||
|
)
|
||||||
.set(value!);
|
.set(value!);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
|
|
@ -547,4 +591,130 @@ class PlayerScreen extends ConsumerWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> _checkAnime4K(BuildContext context) async {
|
||||||
|
var status = await Permission.storage.status;
|
||||||
|
if (!status.isGranted) {
|
||||||
|
await Permission.storage.request();
|
||||||
|
}
|
||||||
|
final provider = StorageProvider();
|
||||||
|
final dir = await provider.getMpvDirectory();
|
||||||
|
final mpvFile = File('${dir!.path}/mpv.conf');
|
||||||
|
final inputFile = File('${dir.path}/input.conf');
|
||||||
|
if (!(await mpvFile.exists()) &&
|
||||||
|
!(await inputFile.exists()) &&
|
||||||
|
context.mounted) {
|
||||||
|
final res = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(context.l10n.anime4K_download),
|
||||||
|
_total > 0
|
||||||
|
? Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: LinearProgressIndicator(
|
||||||
|
value: _total > 0
|
||||||
|
? (_received * 1.0) / _total
|
||||||
|
: 0.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
'${(_received / 1048576.0).toStringAsFixed(2)}/${(_total / 1048576.0).toStringAsFixed(2)} MB',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: SizedBox.shrink(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
try {
|
||||||
|
await _subscription?.cancel();
|
||||||
|
} catch (_) {}
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(context.l10n.cancel),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _total == 0
|
||||||
|
? () async {
|
||||||
|
_response = await http.Client().send(
|
||||||
|
http.Request(
|
||||||
|
'GET',
|
||||||
|
Uri.parse(
|
||||||
|
"https://github.com/Tama47/Anime4K/releases/download/v4.0.1/GLSL_Windows_Low-end.zip",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_total = _response.contentLength ?? 0;
|
||||||
|
_subscription = _response.stream.listen((value) {
|
||||||
|
setState(() {
|
||||||
|
_bytes.addAll(value);
|
||||||
|
_received += value.length;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
_subscription?.onDone(() async {
|
||||||
|
final archive = ZipDecoder().decodeBytes(_bytes);
|
||||||
|
String shadersDir = path.join(
|
||||||
|
dir.path,
|
||||||
|
'shaders',
|
||||||
|
);
|
||||||
|
await Directory(
|
||||||
|
shadersDir,
|
||||||
|
).create(recursive: true);
|
||||||
|
String scriptsDir = path.join(
|
||||||
|
dir.path,
|
||||||
|
'scripts',
|
||||||
|
);
|
||||||
|
await Directory(
|
||||||
|
scriptsDir,
|
||||||
|
).create(recursive: true);
|
||||||
|
for (final file in archive.files) {
|
||||||
|
if (file.name == "mpv.conf") {
|
||||||
|
await mpvFile.writeAsBytes(file.content);
|
||||||
|
} else if (file.name == "input.conf") {
|
||||||
|
await inputFile.writeAsBytes(file.content);
|
||||||
|
} else if (file.name.endsWith(".glsl")) {
|
||||||
|
final shaderFile = File(
|
||||||
|
'$shadersDir/${file.name.split("/").last}',
|
||||||
|
);
|
||||||
|
await shaderFile.writeAsBytes(file.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_total = 0;
|
||||||
|
_received = 0;
|
||||||
|
_bytes.clear();
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.pop(context, "ok");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: Text(context.l10n.download),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return res != null && res == "ok";
|
||||||
|
}
|
||||||
|
return context.mounted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,22 @@ class UseLibassState extends _$UseLibassState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
class UseAnime4KState extends _$UseAnime4KState {
|
||||||
|
@override
|
||||||
|
bool build() {
|
||||||
|
return isar.settings.getSync(227)!.useAnime4K ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(bool value) {
|
||||||
|
final settings = isar.settings.getSync(227);
|
||||||
|
state = value;
|
||||||
|
isar.writeTxnSync(
|
||||||
|
() => isar.settings.putSync(settings!..useAnime4K = value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final hwdecs = {
|
final hwdecs = {
|
||||||
"no": ["all"],
|
"no": ["all"],
|
||||||
"auto": ["all"],
|
"auto": ["all"],
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:mangayomi/main.dart';
|
import 'package:mangayomi/main.dart';
|
||||||
import 'package:mangayomi/models/chapter.dart';
|
import 'package:mangayomi/models/chapter.dart';
|
||||||
import 'package:mangayomi/models/page.dart';
|
|
||||||
import 'package:mangayomi/models/settings.dart';
|
import 'package:mangayomi/models/settings.dart';
|
||||||
import 'package:mangayomi/modules/anime/widgets/desktop.dart';
|
import 'package:mangayomi/modules/anime/widgets/desktop.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/widgets/btn_chapter_list_dialog.dart';
|
import 'package:mangayomi/modules/manga/reader/widgets/btn_chapter_list_dialog.dart';
|
||||||
|
|
@ -22,9 +21,7 @@ import 'package:mangayomi/services/get_html_content.dart';
|
||||||
import 'package:mangayomi/utils/extensions/dom_extensions.dart';
|
import 'package:mangayomi/utils/extensions/dom_extensions.dart';
|
||||||
import 'package:mangayomi/utils/utils.dart';
|
import 'package:mangayomi/utils/utils.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
|
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
|
||||||
import 'package:mangayomi/services/get_chapter_pages.dart';
|
|
||||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||||
import 'package:mangayomi/utils/global_style.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||||
|
|
@ -759,104 +756,3 @@ class _NovelWebViewState extends ConsumerState<NovelWebView>
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UChapDataPreload {
|
|
||||||
Chapter? chapter;
|
|
||||||
Directory? directory;
|
|
||||||
PageUrl? pageUrl;
|
|
||||||
bool? isLocale;
|
|
||||||
Uint8List? archiveImage;
|
|
||||||
int? index;
|
|
||||||
GetChapterPagesModel? chapterUrlModel;
|
|
||||||
int? pageIndex;
|
|
||||||
Uint8List? cropImage;
|
|
||||||
UChapDataPreload(
|
|
||||||
this.chapter,
|
|
||||||
this.directory,
|
|
||||||
this.pageUrl,
|
|
||||||
this.isLocale,
|
|
||||||
this.archiveImage,
|
|
||||||
this.index,
|
|
||||||
this.chapterUrlModel,
|
|
||||||
this.pageIndex, {
|
|
||||||
this.cropImage,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class CustomPopupMenuButton<T> extends StatelessWidget {
|
|
||||||
final String label;
|
|
||||||
final String title;
|
|
||||||
final ValueChanged<T> onSelected;
|
|
||||||
final T value;
|
|
||||||
final List<T> list;
|
|
||||||
final String Function(T) itemText;
|
|
||||||
const CustomPopupMenuButton({
|
|
||||||
super.key,
|
|
||||||
required this.label,
|
|
||||||
required this.title,
|
|
||||||
required this.onSelected,
|
|
||||||
required this.value,
|
|
||||||
required this.list,
|
|
||||||
required this.itemText,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
||||||
child: PopupMenuButton(
|
|
||||||
popUpAnimationStyle: popupAnimationStyle,
|
|
||||||
tooltip: "",
|
|
||||||
offset: Offset.fromDirection(1),
|
|
||||||
color: Colors.black,
|
|
||||||
onSelected: onSelected,
|
|
||||||
itemBuilder: (context) => [
|
|
||||||
for (var d in list)
|
|
||||||
PopupMenuItem(
|
|
||||||
value: d,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.check,
|
|
||||||
color: d == value ? Colors.white : Colors.transparent,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 7),
|
|
||||||
Text(
|
|
||||||
itemText(d),
|
|
||||||
style: const TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
label,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).textTheme.bodyLarge!.color!.withValues(alpha: 0.9),
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(title),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
const Icon(Icons.keyboard_arrow_down_outlined),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,13 @@ class StorageProvider {
|
||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Directory?> getMpvDirectory() async {
|
||||||
|
final defaultDirectory = await getDefaultDirectory();
|
||||||
|
String dbDir = path.join(defaultDirectory!.path, 'mpv');
|
||||||
|
await Directory(dbDir).create(recursive: true);
|
||||||
|
return Directory(dbDir);
|
||||||
|
}
|
||||||
|
|
||||||
Future<Directory?> getBtDirectory() async {
|
Future<Directory?> getBtDirectory() async {
|
||||||
final gefaultDirectory = await getDefaultDirectory();
|
final gefaultDirectory = await getDefaultDirectory();
|
||||||
String dbDir = path.join(gefaultDirectory!.path, 'torrents');
|
String dbDir = path.join(gefaultDirectory!.path, 'torrents');
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:mangayomi/eval/lib.dart';
|
import 'package:mangayomi/eval/lib.dart';
|
||||||
import 'package:mangayomi/eval/javascript/http.dart';
|
import 'package:mangayomi/eval/javascript/http.dart';
|
||||||
|
|
@ -9,7 +10,6 @@ import 'package:mangayomi/models/chapter.dart';
|
||||||
import 'package:mangayomi/models/page.dart';
|
import 'package:mangayomi/models/page.dart';
|
||||||
import 'package:mangayomi/models/settings.dart';
|
import 'package:mangayomi/models/settings.dart';
|
||||||
import 'package:mangayomi/modules/manga/archive_reader/providers/archive_reader_providers.dart';
|
import 'package:mangayomi/modules/manga/archive_reader/providers/archive_reader_providers.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.dart';
|
|
||||||
import 'package:mangayomi/providers/storage_provider.dart';
|
import 'package:mangayomi/providers/storage_provider.dart';
|
||||||
import 'package:mangayomi/utils/utils.dart';
|
import 'package:mangayomi/utils/utils.dart';
|
||||||
import 'package:mangayomi/utils/reg_exp_matcher.dart';
|
import 'package:mangayomi/utils/reg_exp_matcher.dart';
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,12 @@ import 'package:path/path.dart' as p;
|
||||||
part 'get_video_list.g.dart';
|
part 'get_video_list.g.dart';
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
Future<(List<Video>, bool, List<String>)> getVideoList(
|
Future<(List<Video>, bool, List<String>, Directory?)> getVideoList(
|
||||||
Ref ref, {
|
Ref ref, {
|
||||||
required Chapter episode,
|
required Chapter episode,
|
||||||
}) async {
|
}) async {
|
||||||
final storageProvider = StorageProvider();
|
final storageProvider = StorageProvider();
|
||||||
|
final mpvDirectory = await storageProvider.getMpvDirectory();
|
||||||
final mangaDirectory = await storageProvider.getMangaMainDirectory(episode);
|
final mangaDirectory = await storageProvider.getMangaMainDirectory(episode);
|
||||||
final isLocalArchive =
|
final isLocalArchive =
|
||||||
episode.manga.value!.isLocalArchive! &&
|
episode.manga.value!.isLocalArchive! &&
|
||||||
|
|
@ -52,6 +53,7 @@ Future<(List<Video>, bool, List<String>)> getVideoList(
|
||||||
[Video(path!, episode.name!, path, subtitles: subtitles)],
|
[Video(path!, episode.name!, path, subtitles: subtitles)],
|
||||||
true,
|
true,
|
||||||
infoHashes,
|
infoHashes,
|
||||||
|
mpvDirectory
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final source = getSource(
|
final source = getSource(
|
||||||
|
|
@ -68,7 +70,7 @@ Future<(List<Video>, bool, List<String>)> getVideoList(
|
||||||
episode.url,
|
episode.url,
|
||||||
episode.archivePath,
|
episode.archivePath,
|
||||||
);
|
);
|
||||||
return (videos, false, [infohash ?? ""]);
|
return (videos, false, [infohash ?? ""], mpvDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -91,7 +93,7 @@ Future<(List<Video>, bool, List<String>)> getVideoList(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (torrentList, false, infoHashes);
|
return (torrentList, false, infoHashes, mpvDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Video> list = await getExtensionService(
|
List<Video> list = await getExtensionService(
|
||||||
|
|
@ -105,5 +107,5 @@ Future<(List<Video>, bool, List<String>)> getVideoList(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (videos, false, infoHashes);
|
return (videos, false, infoHashes, mpvDirectory);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ const getVideoListProvider = GetVideoListFamily();
|
||||||
|
|
||||||
/// See also [getVideoList].
|
/// See also [getVideoList].
|
||||||
class GetVideoListFamily
|
class GetVideoListFamily
|
||||||
extends Family<AsyncValue<(List<Video>, bool, List<String>)>> {
|
extends Family<AsyncValue<(List<Video>, bool, List<String>, Directory?)>> {
|
||||||
/// See also [getVideoList].
|
/// See also [getVideoList].
|
||||||
const GetVideoListFamily();
|
const GetVideoListFamily();
|
||||||
|
|
||||||
|
|
@ -73,8 +73,8 @@ class GetVideoListFamily
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See also [getVideoList].
|
/// See also [getVideoList].
|
||||||
class GetVideoListProvider
|
class GetVideoListProvider extends AutoDisposeFutureProvider<
|
||||||
extends AutoDisposeFutureProvider<(List<Video>, bool, List<String>)> {
|
(List<Video>, bool, List<String>, Directory?)> {
|
||||||
/// See also [getVideoList].
|
/// See also [getVideoList].
|
||||||
GetVideoListProvider({
|
GetVideoListProvider({
|
||||||
required Chapter episode,
|
required Chapter episode,
|
||||||
|
|
@ -109,7 +109,7 @@ class GetVideoListProvider
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Override overrideWith(
|
Override overrideWith(
|
||||||
FutureOr<(List<Video>, bool, List<String>)> Function(
|
FutureOr<(List<Video>, bool, List<String>, Directory?)> Function(
|
||||||
GetVideoListRef provider)
|
GetVideoListRef provider)
|
||||||
create,
|
create,
|
||||||
) {
|
) {
|
||||||
|
|
@ -128,8 +128,8 @@ class GetVideoListProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
AutoDisposeFutureProviderElement<(List<Video>, bool, List<String>)>
|
AutoDisposeFutureProviderElement<
|
||||||
createElement() {
|
(List<Video>, bool, List<String>, Directory?)> createElement() {
|
||||||
return _GetVideoListProviderElement(this);
|
return _GetVideoListProviderElement(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,15 +149,14 @@ class GetVideoListProvider
|
||||||
|
|
||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
mixin GetVideoListRef
|
mixin GetVideoListRef on AutoDisposeFutureProviderRef<
|
||||||
on AutoDisposeFutureProviderRef<(List<Video>, bool, List<String>)> {
|
(List<Video>, bool, List<String>, Directory?)> {
|
||||||
/// The parameter `episode` of this provider.
|
/// The parameter `episode` of this provider.
|
||||||
Chapter get episode;
|
Chapter get episode;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GetVideoListProviderElement
|
class _GetVideoListProviderElement extends AutoDisposeFutureProviderElement<
|
||||||
extends AutoDisposeFutureProviderElement<(List<Video>, bool, List<String>)>
|
(List<Video>, bool, List<String>, Directory?)> with GetVideoListRef {
|
||||||
with GetVideoListRef {
|
|
||||||
_GetVideoListProviderElement(super.provider);
|
_GetVideoListProviderElement(super.provider);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import 'dart:ui';
|
||||||
import 'package:extended_image/extended_image.dart';
|
import 'package:extended_image/extended_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/reader_view.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/more/settings/reader/providers/reader_state_provider.dart';
|
||||||
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
|
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
|
||||||
import 'package:mangayomi/utils/headers.dart';
|
import 'package:mangayomi/utils/headers.dart';
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
import 'dart:ffi';
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
extension StringExtensions on String {
|
extension StringExtensions on String {
|
||||||
String substringAfter(String pattern) {
|
String substringAfter(String pattern) {
|
||||||
final startIndex = indexOf(pattern);
|
final startIndex = indexOf(pattern);
|
||||||
|
|
@ -73,3 +76,22 @@ extension StringExtensions on String {
|
||||||
].any((extension) => toLowerCase().endsWith(extension));
|
].any((extension) => toLowerCase().endsWith(extension));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension NativeStringExtensions on List<String> {
|
||||||
|
Pointer<Pointer<Int8>> strListToPointer() {
|
||||||
|
final strings = this;
|
||||||
|
List<Pointer<Int8>> int8PointerList = strings
|
||||||
|
.map((str) => str.toNativeUtf8().cast<Int8>())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final Pointer<Pointer<Int8>> pointerPointer = malloc.allocate(
|
||||||
|
int8PointerList.length,
|
||||||
|
);
|
||||||
|
|
||||||
|
strings.asMap().forEach((index, utf) {
|
||||||
|
pointerPointer[index] = int8PointerList[index];
|
||||||
|
});
|
||||||
|
|
||||||
|
return pointerPointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
21
pubspec.lock
21
pubspec.lock
|
|
@ -1160,12 +1160,13 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.3"
|
version: "0.5.3"
|
||||||
media_kit:
|
media_kit:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: media_kit
|
path: media_kit
|
||||||
sha256: "48c10c3785df5d88f0eef970743f8c99b2e5da2b34b9d8f9876e598f62d9e776"
|
ref: HEAD
|
||||||
url: "https://pub.dev"
|
resolved-ref: "746465b7914fa524781813dc6e50ea87dcd686e5"
|
||||||
source: hosted
|
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||||
|
source: git
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
media_kit_libs_android_video:
|
media_kit_libs_android_video:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
|
@ -1219,9 +1220,9 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: media_kit_video
|
path: media_kit_video
|
||||||
ref: aeb29faa8ea93a386ad1185b69fd6225fa331c74
|
ref: HEAD
|
||||||
resolved-ref: aeb29faa8ea93a386ad1185b69fd6225fa331c74
|
resolved-ref: "746465b7914fa524781813dc6e50ea87dcd686e5"
|
||||||
url: "https://github.com/media-kit/media-kit.git"
|
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||||
source: git
|
source: git
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
meta:
|
meta:
|
||||||
|
|
@ -2081,10 +2082,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: volume_controller
|
name: volume_controller
|
||||||
sha256: e82fd689bb8e1fe8e64be3fa5946ff8699058f8cf9f4c1679acdba20cda7f5bd
|
sha256: d75039e69c0d90e7810bfd47e3eedf29ff8543ea7a10392792e81f9bded7edf5
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.3"
|
version: "3.4.0"
|
||||||
wakelock_plus:
|
wakelock_plus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,10 @@ dependencies:
|
||||||
flutter_web_auth_2: ^3.1.2
|
flutter_web_auth_2: ^3.1.2
|
||||||
numberpicker: ^2.1.2
|
numberpicker: ^2.1.2
|
||||||
encrypt: ^5.0.3
|
encrypt: ^5.0.3
|
||||||
media_kit: ^1.2.0
|
|
||||||
media_kit_video:
|
media_kit_video:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/media-kit/media-kit.git
|
url: https://github.com/Schnitzel5/media-kit.git
|
||||||
path: media_kit_video
|
path: media_kit_video
|
||||||
ref: aeb29faa8ea93a386ad1185b69fd6225fa331c74
|
|
||||||
media_kit_libs_video: ^1.0.6
|
media_kit_libs_video: ^1.0.6
|
||||||
crypto: ^3.0.6
|
crypto: ^3.0.6
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
|
|
@ -157,4 +155,3 @@ inno_bundle:
|
||||||
- german
|
- german
|
||||||
admin: false
|
admin: false
|
||||||
version: 0.6.3
|
version: 0.6.3
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue