mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-21 20:12:00 +00:00
added custom buttons
This commit is contained in:
parent
1f0938fab9
commit
4682ab3577
26 changed files with 926 additions and 349 deletions
BIN
assets/mangayomi_mpv.zip
Normal file
BIN
assets/mangayomi_mpv.zip
Normal file
Binary file not shown.
|
|
@ -481,10 +481,12 @@
|
||||||
"custom_buttons_info": "Execute Javascript code with custom buttons",
|
"custom_buttons_info": "Execute Javascript code with custom buttons",
|
||||||
"custom_buttons_edit": "Edit custom buttons",
|
"custom_buttons_edit": "Edit custom buttons",
|
||||||
"custom_buttons_add": "Add custom button",
|
"custom_buttons_add": "Add custom button",
|
||||||
"custom_buttons_edit": "Add custom button",
|
"custom_buttons_delete": "Delete custom button",
|
||||||
"custom_buttons_text": "Button text",
|
"custom_buttons_text": "Button text",
|
||||||
|
"custom_buttons_text_req": "Button text required",
|
||||||
"custom_buttons_js_code": "Javascript code",
|
"custom_buttons_js_code": "Javascript code",
|
||||||
|
"custom_buttons_js_code_req": "Javascript code required",
|
||||||
"custom_buttons_js_code_long": "Javascript code (on long press)",
|
"custom_buttons_js_code_long": "Javascript code (on long press)",
|
||||||
"custom_buttons_startup": "On startup",
|
"custom_buttons_startup": "Javascript code (on startup)",
|
||||||
"n_days": "{n} days"
|
"n_days": "{n} days"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2948,7 +2948,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @custom_buttons_edit.
|
/// No description provided for @custom_buttons_edit.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Add custom button'**
|
/// **'Edit custom buttons'**
|
||||||
String get custom_buttons_edit;
|
String get custom_buttons_edit;
|
||||||
|
|
||||||
/// No description provided for @custom_buttons_add.
|
/// No description provided for @custom_buttons_add.
|
||||||
|
|
@ -2957,18 +2957,36 @@ abstract class AppLocalizations {
|
||||||
/// **'Add custom button'**
|
/// **'Add custom button'**
|
||||||
String get custom_buttons_add;
|
String get custom_buttons_add;
|
||||||
|
|
||||||
|
/// No description provided for @custom_buttons_delete.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Delete custom button'**
|
||||||
|
String get custom_buttons_delete;
|
||||||
|
|
||||||
/// No description provided for @custom_buttons_text.
|
/// No description provided for @custom_buttons_text.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Button text'**
|
/// **'Button text'**
|
||||||
String get custom_buttons_text;
|
String get custom_buttons_text;
|
||||||
|
|
||||||
|
/// No description provided for @custom_buttons_text_req.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Button text required'**
|
||||||
|
String get custom_buttons_text_req;
|
||||||
|
|
||||||
/// No description provided for @custom_buttons_js_code.
|
/// No description provided for @custom_buttons_js_code.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Javascript code'**
|
/// **'Javascript code'**
|
||||||
String get custom_buttons_js_code;
|
String get custom_buttons_js_code;
|
||||||
|
|
||||||
|
/// No description provided for @custom_buttons_js_code_req.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Javascript code required'**
|
||||||
|
String get custom_buttons_js_code_req;
|
||||||
|
|
||||||
/// No description provided for @custom_buttons_js_code_long.
|
/// No description provided for @custom_buttons_js_code_long.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -2978,7 +2996,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @custom_buttons_startup.
|
/// No description provided for @custom_buttons_startup.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'On startup'**
|
/// **'Javascript code (on startup)'**
|
||||||
String get custom_buttons_startup;
|
String get custom_buttons_startup;
|
||||||
|
|
||||||
/// No description provided for @n_days.
|
/// No description provided for @n_days.
|
||||||
|
|
|
||||||
|
|
@ -1518,22 +1518,31 @@ class AppLocalizationsAr extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -1531,22 +1531,31 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -1519,22 +1519,31 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -1536,22 +1536,31 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -1537,22 +1537,31 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -1525,22 +1525,31 @@ class AppLocalizationsId extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -1534,22 +1534,31 @@ class AppLocalizationsIt extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -1533,22 +1533,31 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -1535,22 +1535,31 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -1519,22 +1519,31 @@ class AppLocalizationsTh extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -1525,22 +1525,31 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -1490,22 +1490,31 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||||
'Execute Javascript code with custom buttons';
|
'Execute Javascript code with custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_edit => 'Add custom button';
|
String get custom_buttons_edit => 'Edit custom buttons';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_add => 'Add custom button';
|
String get custom_buttons_add => 'Add custom button';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_delete => 'Delete custom button';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_text => 'Button text';
|
String get custom_buttons_text => 'Button text';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_text_req => 'Button text required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code => 'Javascript code';
|
String get custom_buttons_js_code => 'Javascript code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get custom_buttons_js_code_req => 'Javascript code required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get custom_buttons_startup => 'On startup';
|
String get custom_buttons_startup => 'Javascript code (on startup)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String n_days(Object n) {
|
String n_days(Object n) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:app_links/app_links.dart';
|
import 'package:app_links/app_links.dart';
|
||||||
|
import 'package:archive/archive.dart';
|
||||||
import 'package:bot_toast/bot_toast.dart';
|
import 'package:bot_toast/bot_toast.dart';
|
||||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
@ -31,9 +32,11 @@ 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';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
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';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:flutter/services.dart' show rootBundle;
|
||||||
|
|
||||||
late Isar isar;
|
late Isar isar;
|
||||||
DiscordRPC? discordRpc;
|
DiscordRPC? discordRpc;
|
||||||
|
|
@ -94,6 +97,7 @@ class _MyAppState extends ConsumerState<MyApp> {
|
||||||
super.initState();
|
super.initState();
|
||||||
initializeDateFormatting();
|
initializeDateFormatting();
|
||||||
_initDeepLinks();
|
_initDeepLinks();
|
||||||
|
_setupMpvConfig();
|
||||||
unawaited(ref.read(scanLocalLibraryProvider.future));
|
unawaited(ref.read(scanLocalLibraryProvider.future));
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
|
@ -242,6 +246,38 @@ class _MyAppState extends ConsumerState<MyApp> {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _setupMpvConfig() async {
|
||||||
|
final provider = StorageProvider();
|
||||||
|
final dir = await provider.getMpvDirectory();
|
||||||
|
final mpvFile = File('${dir!.path}/mpv.conf');
|
||||||
|
final inputFile = File('${dir.path}/input.conf');
|
||||||
|
final filesMissing =
|
||||||
|
!(await mpvFile.exists()) && !(await inputFile.exists());
|
||||||
|
if (filesMissing) {
|
||||||
|
final bytes = await rootBundle.load("assets/mangayomi_mpv.zip");
|
||||||
|
final archive = ZipDecoder().decodeBytes(bytes.buffer.asUint8List());
|
||||||
|
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.startsWith("shaders/") &&
|
||||||
|
file.name.endsWith(".glsl")) {
|
||||||
|
final shaderFile = File('$shadersDir/${file.name.split("/").last}');
|
||||||
|
await shaderFile.writeAsBytes(file.content);
|
||||||
|
} else if (file.name.startsWith("scripts/") &&
|
||||||
|
file.name.endsWith(".js")) {
|
||||||
|
final scriptFile = File('$scriptsDir/${file.name.split("/").last}');
|
||||||
|
await scriptFile.writeAsBytes(file.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AllowScrollBehavior extends MaterialScrollBehavior {
|
class AllowScrollBehavior extends MaterialScrollBehavior {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,30 @@ class CustomButton {
|
||||||
this.updatedAt = 0,
|
this.updatedAt = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
String getButtonStartup(int primaryId) {
|
||||||
|
final isPrimary = primaryId == id ? "true" : "false";
|
||||||
|
return codeStartup
|
||||||
|
?.replaceAll("\$id", "$id")
|
||||||
|
.replaceAll("\$isPrimary", isPrimary) ??
|
||||||
|
"";
|
||||||
|
}
|
||||||
|
|
||||||
|
String getButtonPress(int primaryId) {
|
||||||
|
final isPrimary = primaryId == id ? "true" : "false";
|
||||||
|
return codePress
|
||||||
|
?.replaceAll("\$id", "$id")
|
||||||
|
.replaceAll("\$isPrimary", isPrimary) ??
|
||||||
|
"";
|
||||||
|
}
|
||||||
|
|
||||||
|
String getButtonLongPress(int primaryId) {
|
||||||
|
final isPrimary = primaryId == id ? "true" : "false";
|
||||||
|
return codeLongPress
|
||||||
|
?.replaceAll("\$id", "$id")
|
||||||
|
.replaceAll("\$isPrimary", isPrimary) ??
|
||||||
|
"";
|
||||||
|
}
|
||||||
|
|
||||||
CustomButton.fromJson(Map<String, dynamic> json) {
|
CustomButton.fromJson(Map<String, dynamic> json) {
|
||||||
id = json['id'];
|
id = json['id'];
|
||||||
title = json['title'];
|
title = json['title'];
|
||||||
|
|
@ -53,3 +77,19 @@ class CustomButton {
|
||||||
'updatedAt': updatedAt ?? 0,
|
'updatedAt': updatedAt ?? 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ActiveCustomButton {
|
||||||
|
String currentTitle;
|
||||||
|
bool visible;
|
||||||
|
CustomButton button;
|
||||||
|
Function() onPress;
|
||||||
|
Function() onLongPress;
|
||||||
|
|
||||||
|
ActiveCustomButton({
|
||||||
|
required this.currentTitle,
|
||||||
|
required this.visible,
|
||||||
|
required this.button,
|
||||||
|
required this.onPress,
|
||||||
|
required this.onLongPress,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -375,7 +375,7 @@ class Settings {
|
||||||
this.rpcShowReadingWatchingProgress = true,
|
this.rpcShowReadingWatchingProgress = true,
|
||||||
this.rpcShowTitle = true,
|
this.rpcShowTitle = true,
|
||||||
this.rpcShowCoverImage = true,
|
this.rpcShowCoverImage = true,
|
||||||
this.useMpvConfig = false,
|
this.useMpvConfig = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
Settings.fromJson(Map<String, dynamic> json) {
|
Settings.fromJson(Map<String, dynamic> json) {
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,13 @@ import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/foundation.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_qjs/quickjs/ffi.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart' as riv;
|
import 'package:flutter_riverpod/flutter_riverpod.dart' as riv;
|
||||||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
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/custom_button.dart';
|
||||||
import 'package:mangayomi/models/manga.dart';
|
import 'package:mangayomi/models/manga.dart';
|
||||||
import 'package:mangayomi/models/video.dart' as vid;
|
import 'package:mangayomi/models/video.dart' as vid;
|
||||||
import 'package:mangayomi/modules/anime/providers/anime_player_controller_provider.dart';
|
import 'package:mangayomi/modules/anime/providers/anime_player_controller_provider.dart';
|
||||||
|
|
@ -25,6 +27,7 @@ import 'package:mangayomi/modules/anime/widgets/mobile.dart';
|
||||||
import 'package:mangayomi/modules/anime/widgets/subtitle_view.dart';
|
import 'package:mangayomi/modules/anime/widgets/subtitle_view.dart';
|
||||||
import 'package:mangayomi/modules/anime/widgets/subtitle_setting_widget.dart';
|
import 'package:mangayomi/modules/anime/widgets/subtitle_setting_widget.dart';
|
||||||
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
|
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
|
||||||
|
import 'package:mangayomi/modules/more/settings/player/providers/custom_buttons_provider.dart';
|
||||||
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/modules/widgets/custom_draggable_tabbar.dart';
|
import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart';
|
||||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||||
|
|
@ -39,8 +42,11 @@ import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:media_kit/generated/libmpv/bindings.dart' as generated;
|
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:numberpicker/numberpicker.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:super_sliver_list/super_sliver_list.dart';
|
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||||
|
|
||||||
|
|
@ -212,12 +218,12 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
generated.mpv_format.MPV_FORMAT_NODE,
|
generated.mpv_format.MPV_FORMAT_NODE,
|
||||||
"user-data/aniyomi/launch_int_picker":
|
"user-data/aniyomi/launch_int_picker":
|
||||||
generated.mpv_format.MPV_FORMAT_NODE,
|
generated.mpv_format.MPV_FORMAT_NODE,
|
||||||
"user-data/current-anime/intro-length":
|
|
||||||
generated.mpv_format.MPV_FORMAT_INT64,
|
|
||||||
"user-data/mangayomi/chapter_titles":
|
"user-data/mangayomi/chapter_titles":
|
||||||
generated.mpv_format.MPV_FORMAT_NODE,
|
generated.mpv_format.MPV_FORMAT_NODE,
|
||||||
"user-data/mangayomi/current_chapter":
|
"user-data/mangayomi/current_chapter":
|
||||||
generated.mpv_format.MPV_FORMAT_INT64,
|
generated.mpv_format.MPV_FORMAT_INT64,
|
||||||
|
"user-data/mangayomi/selected_shader":
|
||||||
|
generated.mpv_format.MPV_FORMAT_NODE,
|
||||||
},
|
},
|
||||||
eventHandler: _handleMpvEvents,
|
eventHandler: _handleMpvEvents,
|
||||||
),
|
),
|
||||||
|
|
@ -253,6 +259,8 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
final ValueNotifier<BoxFit> _fit = ValueNotifier(BoxFit.contain);
|
final ValueNotifier<BoxFit> _fit = ValueNotifier(BoxFit.contain);
|
||||||
final ValueNotifier<List<(String, int)>> _chapterMarks = ValueNotifier([]);
|
final ValueNotifier<List<(String, int)>> _chapterMarks = ValueNotifier([]);
|
||||||
final ValueNotifier<int?> _currentChapterMark = ValueNotifier(null);
|
final ValueNotifier<int?> _currentChapterMark = ValueNotifier(null);
|
||||||
|
final ValueNotifier<String> _selectedShader = ValueNotifier("");
|
||||||
|
final ValueNotifier<ActiveCustomButton?> _customButton = ValueNotifier(null);
|
||||||
late final ValueNotifier<_AniSkipPhase> _skipPhase = ValueNotifier(
|
late final ValueNotifier<_AniSkipPhase> _skipPhase = ValueNotifier(
|
||||||
_AniSkipPhase.none,
|
_AniSkipPhase.none,
|
||||||
);
|
);
|
||||||
|
|
@ -301,6 +309,9 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
generated.mpv_event_id.MPV_EVENT_PROPERTY_CHANGE) {
|
generated.mpv_event_id.MPV_EVENT_PROPERTY_CHANGE) {
|
||||||
final prop = event.ref.data.cast<generated.mpv_event_property>();
|
final prop = event.ref.data.cast<generated.mpv_event_property>();
|
||||||
final propName = prop.ref.name.cast<Utf8>().toDartString();
|
final propName = prop.ref.name.cast<Utf8>().toDartString();
|
||||||
|
if (propName.startsWith("user-data/")) {
|
||||||
|
print("DEBUG 00: $propName - ${prop.ref.format}");
|
||||||
|
}
|
||||||
if (propName.startsWith("user-data/") &&
|
if (propName.startsWith("user-data/") &&
|
||||||
prop.ref.format == generated.mpv_format.MPV_FORMAT_NODE) {
|
prop.ref.format == generated.mpv_format.MPV_FORMAT_NODE) {
|
||||||
final value = prop.ref.data.cast<generated.mpv_node>();
|
final value = prop.ref.data.cast<generated.mpv_node>();
|
||||||
|
|
@ -322,76 +333,269 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
String propName,
|
String propName,
|
||||||
Pointer<generated.mpv_node> value,
|
Pointer<generated.mpv_node> value,
|
||||||
) async {
|
) async {
|
||||||
|
final nativePlayer = _player.platform as NativePlayer;
|
||||||
switch (propName.substring(10)) {
|
switch (propName.substring(10)) {
|
||||||
case "aniyomi/show_text":
|
case "aniyomi/show_text":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
botToast(text);
|
botToast(text);
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/show_text", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/toggle_ui":
|
case "aniyomi/toggle_ui":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
switch (text) {
|
||||||
|
// WIP
|
||||||
|
case "show":
|
||||||
|
break;
|
||||||
|
case "hide":
|
||||||
|
break;
|
||||||
|
case "toggle":
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/toggle_ui", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/show_panel":
|
case "aniyomi/show_panel":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
switch (text) {
|
||||||
|
// WIP
|
||||||
|
case "subtitle_settings":
|
||||||
|
break;
|
||||||
|
case "subtitle_delay":
|
||||||
|
break;
|
||||||
|
case "audio_delay":
|
||||||
|
break;
|
||||||
|
case "video_filters":
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/show_panel", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/software_keyboard":
|
case "aniyomi/software_keyboard":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
switch (text) {
|
||||||
|
// WIP
|
||||||
|
case "show":
|
||||||
|
break;
|
||||||
|
case "hide":
|
||||||
|
break;
|
||||||
|
case "toggle":
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/software_keyboard", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/set_button_title":
|
case "aniyomi/set_button_title":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
print("DEBUG SET BUTTON TITLE: $text");
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
final temp = _customButton.value;
|
||||||
|
if (temp == null) break;
|
||||||
|
_customButton.value = temp..currentTitle = text;
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/set_button_title", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/reset_button_title":
|
case "aniyomi/reset_button_title":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
final temp = _customButton.value;
|
||||||
|
if (temp == null) break;
|
||||||
|
_customButton.value = temp..currentTitle = temp.button.title ?? "";
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/reset_button_title", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/toggle_button":
|
case "aniyomi/toggle_button":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
final temp = _customButton.value;
|
||||||
|
if (temp == null) break;
|
||||||
|
switch (text) {
|
||||||
|
case "show":
|
||||||
|
_customButton.value = temp..visible = true;
|
||||||
|
break;
|
||||||
|
case "hide":
|
||||||
|
_customButton.value = temp..visible = false;
|
||||||
|
break;
|
||||||
|
case "toggle":
|
||||||
|
_customButton.value = temp..visible = !temp.visible;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/toggle_button", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/switch_episode":
|
case "aniyomi/switch_episode":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
switch (text) {
|
||||||
|
case "n":
|
||||||
|
pushToNewEpisode(context, _streamController.getNextEpisode());
|
||||||
|
break;
|
||||||
|
case "p":
|
||||||
|
pushToNewEpisode(context, _streamController.getPrevEpisode());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/switch_episode", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/pause":
|
case "aniyomi/pause":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
switch (text) {
|
||||||
|
case "pause":
|
||||||
|
await _player.pause();
|
||||||
|
break;
|
||||||
|
case "unpause":
|
||||||
|
await _player.play();
|
||||||
|
break;
|
||||||
|
case "pauseunpause":
|
||||||
|
await _player.playOrPause();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/pause", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/seek_by":
|
case "aniyomi/seek_by":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
final tt = await nativePlayer.getProperty(
|
||||||
|
"user-data/current-anime/intro-length",
|
||||||
|
);
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
final data = int.parse(text.replaceAll("\"", ""));
|
||||||
|
final pos = _currentPosition.value.inSeconds + data;
|
||||||
|
_tempPosition.value = Duration(seconds: pos);
|
||||||
|
await _player.seek(Duration(seconds: pos));
|
||||||
|
_tempPosition.value = null;
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/seek_by", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/seek_to":
|
case "aniyomi/seek_to":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
final data = int.parse(text.replaceAll("\"", ""));
|
||||||
|
_tempPosition.value = Duration(seconds: data);
|
||||||
|
await _player.seek(Duration(seconds: data));
|
||||||
|
_tempPosition.value = null;
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/seek_to", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/seek_by_with_text":
|
case "aniyomi/seek_by_with_text":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
final data = text.split("|");
|
||||||
|
final pos =
|
||||||
|
_currentPosition.value.inSeconds +
|
||||||
|
int.parse(data[0].replaceAll("\"", ""));
|
||||||
|
_tempPosition.value = Duration(seconds: pos);
|
||||||
|
await _player.seek(Duration(seconds: pos));
|
||||||
|
_tempPosition.value = null;
|
||||||
|
(_player.platform as NativePlayer).command(["show-text", data[1]]);
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/seek_by_with_text", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/seek_to_with_text":
|
case "aniyomi/seek_to_with_text":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
final data = text.split("|");
|
||||||
|
final pos = int.parse(data[0].replaceAll("\"", ""));
|
||||||
|
_tempPosition.value = Duration(seconds: pos);
|
||||||
|
await _player.seek(Duration(seconds: pos));
|
||||||
|
_tempPosition.value = null;
|
||||||
|
(_player.platform as NativePlayer).command(["show-text", data[1]]);
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/seek_to_with_text", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "user-data/aniyomi/launch_int_picker":
|
case "aniyomi/launch_int_picker":
|
||||||
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
if (text.isEmpty) break;
|
||||||
|
final data = text.split("|");
|
||||||
|
final start = int.parse(data[2]);
|
||||||
|
final stop = int.parse(data[3]);
|
||||||
|
final step = int.parse(data[4]);
|
||||||
|
int currentValue = start;
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(data[0]),
|
||||||
|
content: StatefulBuilder(
|
||||||
|
builder: (context, setState) => SizedBox(
|
||||||
|
height: 200,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
NumberPicker(
|
||||||
|
value: currentValue,
|
||||||
|
minValue: start,
|
||||||
|
maxValue: stop,
|
||||||
|
step: step,
|
||||||
|
haptics: true,
|
||||||
|
textMapper: (numberText) =>
|
||||||
|
data[1].replaceAll("%d", numberText),
|
||||||
|
onChanged: (value) =>
|
||||||
|
setState(() => currentValue = value),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
context.l10n.cancel,
|
||||||
|
style: TextStyle(color: context.primaryColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final namePtr = data[5].toNativeUtf8();
|
||||||
|
final valuePtr = calloc<Int64>(1)
|
||||||
|
..value = currentValue;
|
||||||
|
nativePlayer.mpv.mpv_set_property(
|
||||||
|
nativePlayer.ctx,
|
||||||
|
namePtr.cast(),
|
||||||
|
generated.mpv_format.MPV_FORMAT_INT64,
|
||||||
|
valuePtr.cast(),
|
||||||
|
);
|
||||||
|
malloc.free(namePtr);
|
||||||
|
malloc.free(valuePtr);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
context.l10n.ok,
|
||||||
|
style: TextStyle(color: context.primaryColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
nativePlayer.setProperty("user-data/aniyomi/launch_int_picker", "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "mangayomi/chapter_titles":
|
case "mangayomi/chapter_titles":
|
||||||
|
|
@ -410,6 +614,12 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "mangayomi/selected_shader":
|
||||||
|
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
|
||||||
|
final text = value.ref.u.string.cast<Utf8>().toDartString();
|
||||||
|
_selectedShader.value = text;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -421,6 +631,57 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _initCustomButton() async {
|
||||||
|
if (!useMpvConfig) return;
|
||||||
|
final customButtons = await ref.read(getCustomButtonsStreamProvider.future);
|
||||||
|
if (customButtons.isEmpty) return;
|
||||||
|
final primaryButton =
|
||||||
|
customButtons.firstWhereOrNull((e) => e.isFavourite ?? false) ??
|
||||||
|
customButtons.first;
|
||||||
|
var status = await Permission.storage.status;
|
||||||
|
if (!status.isGranted) {
|
||||||
|
await Permission.storage.request();
|
||||||
|
}
|
||||||
|
final provider = StorageProvider();
|
||||||
|
final dir = await provider.getMpvDirectory();
|
||||||
|
String scriptsDir = path.join(dir!.path, 'scripts');
|
||||||
|
final mpvFile = File('$scriptsDir/init_custom_buttons.js');
|
||||||
|
final content = StringBuffer();
|
||||||
|
content.write("var aniyomi = require('./init_aniyomi_functions');");
|
||||||
|
for (final button in customButtons) {
|
||||||
|
content.write(
|
||||||
|
"""
|
||||||
|
${button.getButtonStartup(primaryButton.id!).trim()}
|
||||||
|
function button${button.id}() {
|
||||||
|
${button.getButtonPress(primaryButton.id!).trim()}
|
||||||
|
}
|
||||||
|
mp.register_script_message('call_button_${button.id}', button${button.id})
|
||||||
|
function button${button.id}long() {
|
||||||
|
${button.getButtonLongPress(primaryButton.id!).trim()}
|
||||||
|
}
|
||||||
|
mp.register_script_message('call_button_${button.id}_long', button${button.id}long)""",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await mpvFile.writeAsString(content.toString());
|
||||||
|
await (_player.platform as NativePlayer).command([
|
||||||
|
"load-script",
|
||||||
|
mpvFile.path,
|
||||||
|
]);
|
||||||
|
_customButton.value = ActiveCustomButton(
|
||||||
|
currentTitle: primaryButton.title!,
|
||||||
|
visible: true,
|
||||||
|
button: primaryButton,
|
||||||
|
onPress: () => (_player.platform as NativePlayer).command([
|
||||||
|
"script-message",
|
||||||
|
"call_button_${primaryButton.id}",
|
||||||
|
]),
|
||||||
|
onLongPress: () => (_player.platform as NativePlayer).command([
|
||||||
|
"script-message",
|
||||||
|
"call_button_${primaryButton.id}_long",
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
|
@ -526,6 +787,22 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
_setPlaybackSpeed(ref.read(defaultPlayBackSpeedStateProvider));
|
_setPlaybackSpeed(ref.read(defaultPlayBackSpeedStateProvider));
|
||||||
if (ref.read(enableAniSkipStateProvider)) _initAniSkip();
|
if (ref.read(enableAniSkipStateProvider)) _initAniSkip();
|
||||||
});
|
});
|
||||||
|
final defaultSkipIntroLength = ref.read(
|
||||||
|
defaultSkipIntroLengthStateProvider,
|
||||||
|
);
|
||||||
|
(_player.platform as NativePlayer).setProperty(
|
||||||
|
"user-data/current-anime/intro-length",
|
||||||
|
"$defaultSkipIntroLength",
|
||||||
|
);
|
||||||
|
(_player.platform as NativePlayer).command([
|
||||||
|
"script-binding",
|
||||||
|
"stats/display-stats-toggle",
|
||||||
|
]);
|
||||||
|
(_player.platform as NativePlayer).command([
|
||||||
|
"script-binding",
|
||||||
|
"stats/display-page-1",
|
||||||
|
]);
|
||||||
|
_initCustomButton();
|
||||||
discordRpc?.showChapterDetails(ref, widget.episode);
|
discordRpc?.showChapterDetails(ref, widget.episode);
|
||||||
_currentPosition.addListener(_updateRpcTimestamp);
|
_currentPosition.addListener(_updateRpcTimestamp);
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
|
@ -1006,27 +1283,39 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
padding: const EdgeInsets.symmetric(vertical: 5),
|
padding: const EdgeInsets.symmetric(vertical: 5),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 35,
|
height: 35,
|
||||||
child: ElevatedButton(
|
child: ValueListenableBuilder(
|
||||||
onPressed: () async {
|
valueListenable: _customButton,
|
||||||
_tempPosition.value = Duration(
|
builder: (context, value, child) => (value?.visible ?? true)
|
||||||
seconds:
|
? ElevatedButton(
|
||||||
defaultSkipIntroLength + _currentPosition.value.inSeconds,
|
onPressed:
|
||||||
);
|
value?.onPress ??
|
||||||
await _player.seek(
|
() async {
|
||||||
Duration(
|
_tempPosition.value = Duration(
|
||||||
seconds:
|
seconds:
|
||||||
_currentPosition.value.inSeconds + defaultSkipIntroLength,
|
defaultSkipIntroLength +
|
||||||
),
|
_currentPosition.value.inSeconds,
|
||||||
);
|
);
|
||||||
_tempPosition.value = null;
|
await _player.seek(
|
||||||
},
|
Duration(
|
||||||
child: Padding(
|
seconds:
|
||||||
padding: const EdgeInsets.all(8.0),
|
_currentPosition.value.inSeconds +
|
||||||
child: Text(
|
defaultSkipIntroLength,
|
||||||
"+$defaultSkipIntroLength",
|
),
|
||||||
style: const TextStyle(fontWeight: FontWeight.w100),
|
);
|
||||||
),
|
_tempPosition.value = null;
|
||||||
),
|
},
|
||||||
|
onLongPress: value?.onLongPress,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
value != null
|
||||||
|
? value.currentTitle
|
||||||
|
: "+$defaultSkipIntroLength",
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.w100),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -1061,6 +1350,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -1276,9 +1566,16 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
||||||
.map(
|
.map(
|
||||||
(mode) => PopupMenuItem<String>(
|
(mode) => PopupMenuItem<String>(
|
||||||
value: mode.$1,
|
value: mode.$1,
|
||||||
child: Text(mode.$1),
|
child: Text(
|
||||||
|
mode.$1,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: _selectedShader.value == mode.$1
|
||||||
|
? FontWeight.w900
|
||||||
|
: FontWeight.normal,
|
||||||
|
),
|
||||||
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
(_player.platform as dynamic).command([
|
(_player.platform as NativePlayer).command([
|
||||||
"script-message",
|
"script-message",
|
||||||
mode.$2,
|
mode.$2,
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import 'package:mangayomi/modules/anime/providers/anime_player_controller_provid
|
||||||
import 'package:mangayomi/modules/anime/widgets/custom_seekbar.dart';
|
import 'package:mangayomi/modules/anime/widgets/custom_seekbar.dart';
|
||||||
import 'package:mangayomi/modules/anime/widgets/subtitle_view.dart';
|
import 'package:mangayomi/modules/anime/widgets/subtitle_view.dart';
|
||||||
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:media_kit/media_kit.dart';
|
||||||
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:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
@ -218,43 +219,43 @@ class _DesktopControllerWidgetState
|
||||||
await _changeFullScreen(ref, desktopFullScreenPlayer, value: false);
|
await _changeFullScreen(ref, desktopFullScreenPlayer, value: false);
|
||||||
},
|
},
|
||||||
const SingleActivator(LogicalKeyboardKey.digit0, control: true): () {
|
const SingleActivator(LogicalKeyboardKey.digit0, control: true): () {
|
||||||
(widget.videoController.player.platform as dynamic).command([
|
(widget.videoController.player.platform as NativePlayer).command([
|
||||||
"script-message",
|
"script-message",
|
||||||
"clear_anime",
|
"clear_anime",
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
const SingleActivator(LogicalKeyboardKey.digit1, control: true): () {
|
const SingleActivator(LogicalKeyboardKey.digit1, control: true): () {
|
||||||
(widget.videoController.player.platform as dynamic).command([
|
(widget.videoController.player.platform as NativePlayer).command([
|
||||||
"script-message",
|
"script-message",
|
||||||
"set_anime_a",
|
"set_anime_a",
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
const SingleActivator(LogicalKeyboardKey.digit2, control: true): () {
|
const SingleActivator(LogicalKeyboardKey.digit2, control: true): () {
|
||||||
(widget.videoController.player.platform as dynamic).command([
|
(widget.videoController.player.platform as NativePlayer).command([
|
||||||
"script-message",
|
"script-message",
|
||||||
"set_anime_b",
|
"set_anime_b",
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
const SingleActivator(LogicalKeyboardKey.digit3, control: true): () {
|
const SingleActivator(LogicalKeyboardKey.digit3, control: true): () {
|
||||||
(widget.videoController.player.platform as dynamic).command([
|
(widget.videoController.player.platform as NativePlayer).command([
|
||||||
"script-message",
|
"script-message",
|
||||||
"set_anime_c",
|
"set_anime_c",
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
const SingleActivator(LogicalKeyboardKey.digit4, control: true): () {
|
const SingleActivator(LogicalKeyboardKey.digit4, control: true): () {
|
||||||
(widget.videoController.player.platform as dynamic).command([
|
(widget.videoController.player.platform as NativePlayer).command([
|
||||||
"script-message",
|
"script-message",
|
||||||
"set_anime_aa",
|
"set_anime_aa",
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
const SingleActivator(LogicalKeyboardKey.digit5, control: true): () {
|
const SingleActivator(LogicalKeyboardKey.digit5, control: true): () {
|
||||||
(widget.videoController.player.platform as dynamic).command([
|
(widget.videoController.player.platform as NativePlayer).command([
|
||||||
"script-message",
|
"script-message",
|
||||||
"set_anime_bb",
|
"set_anime_bb",
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
const SingleActivator(LogicalKeyboardKey.digit6, control: true): () {
|
const SingleActivator(LogicalKeyboardKey.digit6, control: true): () {
|
||||||
(widget.videoController.player.platform as dynamic).command([
|
(widget.videoController.player.platform as NativePlayer).command([
|
||||||
"script-message",
|
"script-message",
|
||||||
"set_anime_ca",
|
"set_anime_ca",
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
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:isar/isar.dart';
|
||||||
|
import 'package:mangayomi/main.dart';
|
||||||
import 'package:mangayomi/models/custom_button.dart';
|
import 'package:mangayomi/models/custom_button.dart';
|
||||||
import 'package:mangayomi/modules/more/settings/player/providers/custom_buttons_provider.dart';
|
import 'package:mangayomi/modules/more/settings/player/providers/custom_buttons_provider.dart';
|
||||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||||
|
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||||
|
|
||||||
class CustomButtonScreen extends ConsumerStatefulWidget {
|
class CustomButtonScreen extends ConsumerStatefulWidget {
|
||||||
const CustomButtonScreen({super.key});
|
const CustomButtonScreen({super.key});
|
||||||
|
|
@ -13,7 +16,6 @@ class CustomButtonScreen extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CustomButtonScreenState extends ConsumerState<CustomButtonScreen> {
|
class _CustomButtonScreenState extends ConsumerState<CustomButtonScreen> {
|
||||||
List<CustomButton> _entries = [];
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
|
|
@ -23,7 +25,6 @@ class _CustomButtonScreenState extends ConsumerState<CustomButtonScreen> {
|
||||||
body: customButtons.when(
|
body: customButtons.when(
|
||||||
data: (data) {
|
data: (data) {
|
||||||
if (data.isEmpty) {
|
if (data.isEmpty) {
|
||||||
_entries = [];
|
|
||||||
return Center(
|
return Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
|
@ -34,71 +35,107 @@ class _CustomButtonScreenState extends ConsumerState<CustomButtonScreen> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
data.sort((a, b) => (a.pos ?? 0).compareTo(b.pos ?? 0));
|
|
||||||
_entries = data;
|
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: ReorderableListView.builder(
|
child: ReorderableListView.builder(
|
||||||
buildDefaultDragHandles: false,
|
buildDefaultDragHandles: false,
|
||||||
itemCount: _entries.length,
|
itemCount: data.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final customButton = _entries[index];
|
final customButton = data[index];
|
||||||
return Row(
|
return Column(
|
||||||
key: Key('custom_btn_${customButton.id}'),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
key: Key("custom_btn_col_${customButton.id}"),
|
||||||
children: [
|
children: [
|
||||||
ReorderableDragStartListener(
|
Row(
|
||||||
index: index,
|
key: Key("custom_btn_row_${customButton.id}"),
|
||||||
child: const Icon(Icons.drag_handle),
|
children: [
|
||||||
|
ReorderableDragStartListener(
|
||||||
|
index: index,
|
||||||
|
child: const Icon(Icons.drag_handle),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
key: Key("custom_btn_row1_${customButton.id}"),
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||||
|
textBaseline: TextBaseline.alphabetic,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ListTile(
|
||||||
|
key: Key(
|
||||||
|
"custom_btn_tile_${customButton.id}",
|
||||||
|
),
|
||||||
|
dense: true,
|
||||||
|
title: Text(
|
||||||
|
customButton.title!,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
for (final button in data) {
|
||||||
|
button.isFavourite =
|
||||||
|
button.id == customButton.id;
|
||||||
|
}
|
||||||
|
await isar.writeTxn(
|
||||||
|
() async =>
|
||||||
|
await isar.customButtons.putAll(data),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
(customButton.isFavourite ?? false)
|
||||||
|
? Icons.star
|
||||||
|
: Icons.star_border,
|
||||||
|
color: context.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await _showEditForm(customButton);
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.mode_edit_outlined),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await _showDeleteButton(customButton);
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.delete_outline),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Expanded(
|
Text(
|
||||||
child: Row(
|
customButton.codePress ?? "",
|
||||||
children: [
|
overflow: TextOverflow.ellipsis,
|
||||||
IconButton(
|
|
||||||
onPressed: () {},
|
|
||||||
icon: Icon(
|
|
||||||
(customButton.isFavourite ?? false)
|
|
||||||
? Icons.star
|
|
||||||
: Icons.star_border,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {},
|
|
||||||
icon: Icon(Icons.mode_edit_outlined),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {},
|
|
||||||
icon: Icon(Icons.delete_outline),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onReorder: (oldIndex, newIndex) {
|
onReorder: (oldIndex, newIndex) async {
|
||||||
/*if (oldIndex < newIndex) {
|
if (oldIndex < newIndex) {
|
||||||
final draggedItem = navigationOrder[oldIndex];
|
final draggedItemPos = data[oldIndex].pos;
|
||||||
for (var i = oldIndex; i < newIndex - 1; i++) {
|
for (var i = oldIndex; i < newIndex - 1; i++) {
|
||||||
navigationOrder[i] = navigationOrder[i + 1];
|
data[i].pos = data[i + 1].pos;
|
||||||
}
|
}
|
||||||
navigationOrder[newIndex - 1] = draggedItem;
|
data[newIndex - 1].pos = draggedItemPos;
|
||||||
} else {
|
} else {
|
||||||
final draggedItem = navigationOrder[oldIndex];
|
final draggedItemPos = data[oldIndex].pos;
|
||||||
for (var i = oldIndex; i > newIndex; i--) {
|
for (var i = oldIndex; i > newIndex; i--) {
|
||||||
navigationOrder[i] = navigationOrder[i - 1];
|
data[i].pos = data[i - 1].pos;
|
||||||
}
|
}
|
||||||
navigationOrder[newIndex] = draggedItem;
|
data[newIndex].pos = draggedItemPos;
|
||||||
}
|
}
|
||||||
ref
|
await isar.writeTxn(
|
||||||
.read(navigationOrderStateProvider.notifier)
|
() async => await isar.customButtons.putAll(data),
|
||||||
.set(navigationOrder);*/
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
error: (Object error, StackTrace stackTrace) {
|
error: (Object error, StackTrace stackTrace) {
|
||||||
_entries = [];
|
|
||||||
return Center(
|
return Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
|
@ -114,89 +151,8 @@ class _CustomButtonScreenState extends ConsumerState<CustomButtonScreen> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
bool isExist = false;
|
await _showEditForm(null);
|
||||||
final controller = TextEditingController();
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return SizedBox(
|
|
||||||
child: StatefulBuilder(
|
|
||||||
builder: (context, setState) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(l10n.add_category),
|
|
||||||
content: CustomTextFormField(
|
|
||||||
controller: controller,
|
|
||||||
entries: _entries,
|
|
||||||
context: context,
|
|
||||||
exist: (value) {
|
|
||||||
setState(() {
|
|
||||||
isExist = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
isExist: isExist,
|
|
||||||
val: (val) {},
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
child: Text(l10n.cancel),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
TextButton(
|
|
||||||
onPressed: controller.text.isEmpty || isExist
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
/*final category = Category(
|
|
||||||
forItemType: widget.itemType,
|
|
||||||
name: controller.text,
|
|
||||||
updatedAt: DateTime.now()
|
|
||||||
.millisecondsSinceEpoch,
|
|
||||||
);
|
|
||||||
isar.writeTxnSync(() {
|
|
||||||
isar.categorys.putSync(
|
|
||||||
category..pos = category.id,
|
|
||||||
);
|
|
||||||
final categories = isar.categorys
|
|
||||||
.filter()
|
|
||||||
.posIsNull()
|
|
||||||
.findAllSync();
|
|
||||||
for (var category in categories) {
|
|
||||||
isar.categorys.putSync(
|
|
||||||
category..pos = category.id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
|
|
||||||
if (context.mounted) {
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
l10n.add,
|
|
||||||
style: TextStyle(
|
|
||||||
color: controller.text.isEmpty || isExist
|
|
||||||
? Theme.of(
|
|
||||||
context,
|
|
||||||
).primaryColor.withValues(alpha: 0.2)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
label: Row(
|
label: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -208,77 +164,250 @@ class _CustomButtonScreenState extends ConsumerState<CustomButtonScreen> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _showEditForm(CustomButton? customButton) async {
|
||||||
|
bool isTitleMissing = customButton == null;
|
||||||
|
bool isCodePressMissing = customButton == null;
|
||||||
|
final titleController = TextEditingController(
|
||||||
|
text: customButton?.title ?? "",
|
||||||
|
);
|
||||||
|
final codePressController = TextEditingController(
|
||||||
|
text: customButton?.codePress ?? "",
|
||||||
|
);
|
||||||
|
final codeLongPressController = TextEditingController(
|
||||||
|
text: customButton?.codeLongPress ?? "",
|
||||||
|
);
|
||||||
|
final codeStartupController = TextEditingController(
|
||||||
|
text: customButton?.codeStartup ?? "",
|
||||||
|
);
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return SizedBox(
|
||||||
|
child: StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(
|
||||||
|
"${context.l10n.custom_buttons_add}${customButton != null ? " (ID: ${customButton.id})" : ""}",
|
||||||
|
),
|
||||||
|
scrollable: true,
|
||||||
|
content: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
CustomTextFormField(
|
||||||
|
name: context.l10n.custom_buttons_text,
|
||||||
|
helperText: context.l10n.custom_buttons_text_req,
|
||||||
|
allowEnterNewLine: false,
|
||||||
|
controller: titleController,
|
||||||
|
context: context,
|
||||||
|
missing: (value) {
|
||||||
|
setState(() {
|
||||||
|
isTitleMissing = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isMissing: isTitleMissing,
|
||||||
|
val: (val) {},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
CustomTextFormField(
|
||||||
|
name: context.l10n.custom_buttons_js_code,
|
||||||
|
helperText: context.l10n.custom_buttons_js_code_req,
|
||||||
|
minLines: 4,
|
||||||
|
controller: codePressController,
|
||||||
|
context: context,
|
||||||
|
missing: (value) {
|
||||||
|
setState(() {
|
||||||
|
isCodePressMissing = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isMissing: isCodePressMissing,
|
||||||
|
val: (val) {},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
CustomTextFormField(
|
||||||
|
name: context.l10n.custom_buttons_js_code_long,
|
||||||
|
minLines: 4,
|
||||||
|
controller: codeLongPressController,
|
||||||
|
context: context,
|
||||||
|
missing: (value) {},
|
||||||
|
isMissing: false,
|
||||||
|
val: (val) {},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
CustomTextFormField(
|
||||||
|
name: context.l10n.custom_buttons_startup,
|
||||||
|
minLines: 4,
|
||||||
|
controller: codeStartupController,
|
||||||
|
context: context,
|
||||||
|
missing: (value) {},
|
||||||
|
isMissing: false,
|
||||||
|
val: (val) {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text(context.l10n.cancel),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
TextButton(
|
||||||
|
onPressed: isTitleMissing || isCodePressMissing
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
final temp = await isar.customButtons
|
||||||
|
.filter()
|
||||||
|
.idEqualTo(customButton?.id)
|
||||||
|
.findFirst();
|
||||||
|
final button =
|
||||||
|
temp ??
|
||||||
|
CustomButton(
|
||||||
|
title: "",
|
||||||
|
codePress: "",
|
||||||
|
codeLongPress: "",
|
||||||
|
codeStartup: "",
|
||||||
|
pos: await isar.customButtons.count(),
|
||||||
|
);
|
||||||
|
await isar.writeTxn(() async {
|
||||||
|
await isar.customButtons.put(
|
||||||
|
button
|
||||||
|
..title = titleController.text
|
||||||
|
..codePress = codePressController.text
|
||||||
|
..codeLongPress =
|
||||||
|
codeLongPressController.text
|
||||||
|
..codeStartup =
|
||||||
|
codeStartupController.text,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
customButton == null
|
||||||
|
? context.l10n.add
|
||||||
|
: context.l10n.edit,
|
||||||
|
style: TextStyle(
|
||||||
|
color: isTitleMissing || isCodePressMissing
|
||||||
|
? Theme.of(
|
||||||
|
context,
|
||||||
|
).primaryColor.withValues(alpha: 0.2)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showDeleteButton(CustomButton customButton) async {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
content: Text(context.l10n.custom_buttons_delete),
|
||||||
|
actions: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: Text(context.l10n.cancel),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await isar.writeTxn(
|
||||||
|
() async =>
|
||||||
|
await isar.customButtons.delete(customButton.id!),
|
||||||
|
);
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.pop(context, "ok");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(context.l10n.ok),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomTextFormField extends StatelessWidget {
|
class CustomTextFormField extends StatelessWidget {
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
final List<CustomButton> entries;
|
|
||||||
final BuildContext context;
|
final BuildContext context;
|
||||||
final Function(bool) exist;
|
final Function(bool) missing;
|
||||||
final bool isExist;
|
final bool isMissing;
|
||||||
final String name;
|
final String name;
|
||||||
|
final String helperText;
|
||||||
|
final int minLines;
|
||||||
|
final bool allowEnterNewLine;
|
||||||
final Function(String) val;
|
final Function(String) val;
|
||||||
const CustomTextFormField({
|
const CustomTextFormField({
|
||||||
super.key,
|
super.key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.entries,
|
|
||||||
required this.context,
|
required this.context,
|
||||||
required this.exist,
|
required this.missing,
|
||||||
required this.isExist,
|
required this.isMissing,
|
||||||
this.name = "",
|
this.name = "",
|
||||||
|
this.helperText = "",
|
||||||
|
this.minLines = 1,
|
||||||
|
this.allowEnterNewLine = true,
|
||||||
required this.val,
|
required this.val,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = l10nLocalizations(context);
|
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
autofocus: true,
|
minLines: minLines,
|
||||||
|
maxLines: null,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: allowEnterNewLine
|
||||||
|
? TextInputType.multiline
|
||||||
|
: TextInputType.text,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (name != controller.text) {
|
missing(controller.text.isEmpty);
|
||||||
exist(
|
|
||||||
entries
|
|
||||||
.where((element) => element.title == controller.text)
|
|
||||||
.toList()
|
|
||||||
.isNotEmpty,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
val(value);
|
val(value);
|
||||||
},
|
},
|
||||||
onFieldSubmitted: (s) {},
|
onFieldSubmitted: (s) {},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
helperText: isExist == true
|
helperText: helperText,
|
||||||
? l10n!.add_category_error_exist
|
helperStyle: TextStyle(color: isMissing ? Colors.red : null),
|
||||||
: l10n!.category_name_required,
|
|
||||||
helperStyle: TextStyle(color: isExist == true ? Colors.red : null),
|
|
||||||
isDense: true,
|
isDense: true,
|
||||||
label: Text(
|
label: Text(
|
||||||
l10n.name,
|
name,
|
||||||
style: TextStyle(color: isExist == true ? Colors.red : null),
|
style: TextStyle(color: isMissing ? Colors.red : null),
|
||||||
),
|
),
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: Colors.transparent,
|
fillColor: Colors.transparent,
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: isExist == true
|
color: isMissing ? Colors.red : Theme.of(context).primaryColor,
|
||||||
? Colors.red
|
|
||||||
: Theme.of(context).primaryColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: isExist == true
|
color: isMissing ? Colors.red : Theme.of(context).primaryColor,
|
||||||
? Colors.red
|
|
||||||
: Theme.of(context).primaryColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: isExist == true
|
color: isMissing ? Colors.red : Theme.of(context).primaryColor,
|
||||||
? Colors.red
|
|
||||||
: Theme.of(context).primaryColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:archive/archive.dart';
|
import 'package:archive/archive.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart' show rootBundle;
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.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/modules/more/widgets/list_tile_widget.dart';
|
import 'package:mangayomi/modules/more/widgets/list_tile_widget.dart';
|
||||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||||
|
|
@ -26,18 +26,6 @@ class PlayerScreen extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
||||||
int _total = 0;
|
|
||||||
int _received = 0;
|
|
||||||
http.StreamedResponse? _response;
|
|
||||||
final List<int> _bytes = [];
|
|
||||||
StreamSubscription<List<int>>? _subscription;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_subscription?.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final defaultSubtitleLang = ref.watch(defaultSubtitleLangStateProvider);
|
final defaultSubtitleLang = ref.watch(defaultSubtitleLangStateProvider);
|
||||||
|
|
@ -631,110 +619,51 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
content: SingleChildScrollView(
|
content: Text(context.l10n.mpv_download),
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(context.l10n.mpv_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: [
|
actions: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () => Navigator.pop(context),
|
||||||
try {
|
|
||||||
await _subscription?.cancel();
|
|
||||||
} catch (_) {}
|
|
||||||
if (context.mounted) {
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(context.l10n.cancel),
|
child: Text(context.l10n.cancel),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _total == 0
|
onPressed: () async {
|
||||||
? () async {
|
final bytes = await rootBundle.load(
|
||||||
_response = await http.Client().send(
|
"assets/mangayomi_mpv.zip",
|
||||||
http.Request(
|
);
|
||||||
'GET',
|
final archive = ZipDecoder().decodeBytes(
|
||||||
Uri.parse(
|
bytes.buffer.asUint8List(),
|
||||||
"https://github.com/Schnitzel5/mangayomi/releases/download/v0.6.3-anime4k/mangayomi_mpv.zip",
|
);
|
||||||
),
|
String shadersDir = path.join(dir.path, 'shaders');
|
||||||
),
|
await Directory(shadersDir).create(recursive: true);
|
||||||
);
|
String scriptsDir = path.join(dir.path, 'scripts');
|
||||||
_total = _response?.contentLength ?? 0;
|
await Directory(scriptsDir).create(recursive: true);
|
||||||
_subscription = _response?.stream.listen((value) {
|
for (final file in archive.files) {
|
||||||
setState(() {
|
if (file.name == "mpv.conf") {
|
||||||
_bytes.addAll(value);
|
await mpvFile.writeAsBytes(file.content);
|
||||||
_received += value.length;
|
} else if (file.name == "input.conf") {
|
||||||
});
|
await inputFile.writeAsBytes(file.content);
|
||||||
});
|
} else if (file.name.startsWith("shaders/") &&
|
||||||
_subscription?.onDone(() async {
|
file.name.endsWith(".glsl")) {
|
||||||
final archive = ZipDecoder().decodeBytes(_bytes);
|
final shaderFile = File(
|
||||||
String shadersDir = path.join(
|
'$shadersDir/${file.name.split("/").last}',
|
||||||
dir.path,
|
);
|
||||||
'shaders',
|
await shaderFile.writeAsBytes(file.content);
|
||||||
);
|
} else if (file.name.startsWith("scripts/") &&
|
||||||
await Directory(
|
file.name.endsWith(".js")) {
|
||||||
shadersDir,
|
final scriptFile = File(
|
||||||
).create(recursive: true);
|
'$scriptsDir/${file.name.split("/").last}',
|
||||||
String scriptsDir = path.join(
|
);
|
||||||
dir.path,
|
await scriptFile.writeAsBytes(file.content);
|
||||||
'scripts',
|
}
|
||||||
);
|
}
|
||||||
await Directory(
|
if (context.mounted) {
|
||||||
scriptsDir,
|
Navigator.pop(context, "ok");
|
||||||
).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.startsWith("shaders/") &&
|
|
||||||
file.name.endsWith(".glsl")) {
|
|
||||||
final shaderFile = File(
|
|
||||||
'$shadersDir/${file.name.split("/").last}',
|
|
||||||
);
|
|
||||||
await shaderFile.writeAsBytes(file.content);
|
|
||||||
} else if (file.name.startsWith("scripts/") &&
|
|
||||||
file.name.endsWith(".js")) {
|
|
||||||
final scriptFile = File(
|
|
||||||
'$scriptsDir/${file.name.split("/").last}',
|
|
||||||
);
|
|
||||||
await scriptFile.writeAsBytes(file.content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_total = 0;
|
|
||||||
_received = 0;
|
|
||||||
_bytes.clear();
|
|
||||||
if (context.mounted) {
|
|
||||||
Navigator.pop(context, "ok");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
child: Text(context.l10n.download),
|
child: Text(context.l10n.download),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,7 @@ part 'custom_buttons_provider.g.dart';
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
Stream<List<CustomButton>> getCustomButtonsStream(Ref ref) async* {
|
Stream<List<CustomButton>> getCustomButtonsStream(Ref ref) async* {
|
||||||
yield* isar.customButtons.filter().idIsNotNull().watch(fireImmediately: true);
|
yield* isar.customButtons.filter().idIsNotNull().sortByPos().watch(
|
||||||
|
fireImmediately: true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ part of 'custom_buttons_provider.dart';
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$getCustomButtonsStreamHash() =>
|
String _$getCustomButtonsStreamHash() =>
|
||||||
r'463d2142793ffb5a905f6f90c3a756445be8b133';
|
r'476c26eb3d20e9e9eed2e1d8bb15fa74ce357ba3';
|
||||||
|
|
||||||
/// See also [getCustomButtonsStream].
|
/// See also [getCustomButtonsStream].
|
||||||
@ProviderFor(getCustomButtonsStream)
|
@ProviderFor(getCustomButtonsStream)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,12 @@ class ListTileWidget extends StatelessWidget {
|
||||||
child: Icon(icon, color: context.primaryColor),
|
child: Icon(icon, color: context.primaryColor),
|
||||||
),
|
),
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
|
subtitle: subtitle != null
|
||||||
|
? Text(
|
||||||
|
subtitle!,
|
||||||
|
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
trailing: trailing,
|
trailing: trailing,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -203,19 +203,28 @@ class StorageProvider {
|
||||||
|
|
||||||
final customButton = await isar.customButtons
|
final customButton = await isar.customButtons
|
||||||
.filter()
|
.filter()
|
||||||
.idEqualTo(1)
|
.idIsNotNull()
|
||||||
.findFirst();
|
.findFirst();
|
||||||
if (customButton == null) {
|
if (customButton == null) {
|
||||||
await isar.writeTxn(() async {
|
await isar.writeTxn(() async {
|
||||||
isar.customButtons.put(
|
await isar.customButtons.put(
|
||||||
CustomButton(
|
CustomButton(
|
||||||
title: "+85 s",
|
title: "+85 s",
|
||||||
codePress:
|
codePress:
|
||||||
"var intro_length = mp.get_property_number(\"user-data/current-anime/intro-length\")\naniyomi.right_seek_by(intro_length)",
|
"""var intro_length = mp.get_property_number("user-data/current-anime/intro-length")
|
||||||
|
aniyomi.right_seek_by(intro_length)""",
|
||||||
codeLongPress:
|
codeLongPress:
|
||||||
"aniyomi.int_picker(\"Change intro length\", \"%ds\", 0, 255, 1, \"user-data/current-anime/intro-length\")",
|
"""aniyomi.int_picker("Change intro length", "%ds", 0, 255, 1, "user-data/current-anime/intro-length")""",
|
||||||
codeStartup:
|
codeStartup: """function update_button(_, length) {
|
||||||
"function update_button(_, length) {\n if (!length || length == 0) {\n aniyomi.hide_button()\n } else {\n aniyomi.show_button()\n }\n aniyomi.set_button_title(\"+\" + length + \" s\")",
|
if (length && length == 0) {
|
||||||
|
aniyomi.hide_button()
|
||||||
|
} else {
|
||||||
|
aniyomi.show_button()
|
||||||
|
}
|
||||||
|
aniyomi.set_button_title("+" + length + " s")
|
||||||
|
if (\$isPrimary) {
|
||||||
|
mp.observe_property("user-data/current-anime/intro-length", "number", update_button)
|
||||||
|
}""",
|
||||||
isFavourite: true,
|
isFavourite: true,
|
||||||
pos: 0,
|
pos: 0,
|
||||||
updatedAt: DateTime.now().millisecondsSinceEpoch,
|
updatedAt: DateTime.now().millisecondsSinceEpoch,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue