adding custom buttons

This commit is contained in:
Schnitzel5 2025-07-28 02:24:32 +02:00
parent 5e1e526785
commit 1f0938fab9
28 changed files with 2360 additions and 95 deletions

View file

@ -477,5 +477,14 @@
"mpv_redownload": "Redownload mpv config files",
"mpv_redownload_info": "Replaces old config files with new one!",
"mpv_download": "MPV config files are required!\nDownload now?",
"custom_buttons": "Custom buttons",
"custom_buttons_info": "Execute Javascript code with custom buttons",
"custom_buttons_edit": "Edit custom buttons",
"custom_buttons_add": "Add custom button",
"custom_buttons_edit": "Add custom button",
"custom_buttons_text": "Button text",
"custom_buttons_js_code": "Javascript code",
"custom_buttons_js_code_long": "Javascript code (on long press)",
"custom_buttons_startup": "On startup",
"n_days": "{n} days"
}

View file

@ -2933,6 +2933,54 @@ abstract class AppLocalizations {
/// **'MPV config files are required!\nDownload now?'**
String get mpv_download;
/// No description provided for @custom_buttons.
///
/// In en, this message translates to:
/// **'Custom buttons'**
String get custom_buttons;
/// No description provided for @custom_buttons_info.
///
/// In en, this message translates to:
/// **'Execute Javascript code with custom buttons'**
String get custom_buttons_info;
/// No description provided for @custom_buttons_edit.
///
/// In en, this message translates to:
/// **'Add custom button'**
String get custom_buttons_edit;
/// No description provided for @custom_buttons_add.
///
/// In en, this message translates to:
/// **'Add custom button'**
String get custom_buttons_add;
/// No description provided for @custom_buttons_text.
///
/// In en, this message translates to:
/// **'Button text'**
String get custom_buttons_text;
/// No description provided for @custom_buttons_js_code.
///
/// In en, this message translates to:
/// **'Javascript code'**
String get custom_buttons_js_code;
/// No description provided for @custom_buttons_js_code_long.
///
/// In en, this message translates to:
/// **'Javascript code (on long press)'**
String get custom_buttons_js_code_long;
/// No description provided for @custom_buttons_startup.
///
/// In en, this message translates to:
/// **'On startup'**
String get custom_buttons_startup;
/// No description provided for @n_days.
///
/// In en, this message translates to:

View file

@ -1510,6 +1510,31 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -1523,6 +1523,31 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -1511,6 +1511,31 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -1528,6 +1528,31 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -1529,6 +1529,31 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -1517,6 +1517,31 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -1526,6 +1526,31 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -1525,6 +1525,31 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -1527,6 +1527,31 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -1511,6 +1511,31 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -1517,6 +1517,31 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -1482,6 +1482,31 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get mpv_download => 'MPV config files are required!\nDownload now?';
@override
String get custom_buttons => 'Custom buttons';
@override
String get custom_buttons_info =>
'Execute Javascript code with custom buttons';
@override
String get custom_buttons_edit => 'Add custom button';
@override
String get custom_buttons_add => 'Add custom button';
@override
String get custom_buttons_text => 'Button text';
@override
String get custom_buttons_js_code => 'Javascript code';
@override
String get custom_buttons_js_code_long => 'Javascript code (on long press)';
@override
String get custom_buttons_startup => 'On startup';
@override
String n_days(Object n) {
return '$n days';

View file

@ -0,0 +1,55 @@
import 'package:isar/isar.dart';
part 'custom_button.g.dart';
@collection
@Name("CustomButton")
class CustomButton {
Id? id;
String? title;
String? codePress;
String? codeLongPress;
String? codeStartup;
bool? isFavourite;
int? pos;
int? updatedAt;
CustomButton({
this.id = Isar.autoIncrement,
required this.title,
required this.codePress,
this.codeLongPress = "",
this.codeStartup = "",
this.isFavourite = false,
required this.pos,
this.updatedAt = 0,
});
CustomButton.fromJson(Map<String, dynamic> json) {
id = json['id'];
title = json['title'];
codePress = json['codePress'];
codeLongPress = json['codeLongPress'];
codeStartup = json['codeStartup'];
isFavourite = json['isFavourite'];
pos = json['pos'];
updatedAt = json['updatedAt'];
}
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
'codePress': codePress,
'codeLongPress': codeLongPress,
'codeStartup': codeStartup,
'isFavourite': isFavourite,
'pos': pos,
'updatedAt': updatedAt ?? 0,
};
}

File diff suppressed because it is too large Load diff

View file

@ -258,7 +258,7 @@ class Settings {
bool? rpcShowCoverImage;
bool? useAnime4K;
bool? useMpvConfig;
Settings({
this.id = 227,
@ -375,7 +375,7 @@ class Settings {
this.rpcShowReadingWatchingProgress = true,
this.rpcShowTitle = true,
this.rpcShowCoverImage = true,
this.useAnime4K = false,
this.useMpvConfig = false,
});
Settings.fromJson(Map<String, dynamic> json) {
@ -597,7 +597,7 @@ class Settings {
rpcShowReadingWatchingProgress = json['rpcShowReadingWatchingProgress'];
rpcShowTitle = json['rpcShowTitle'];
rpcShowCoverImage = json['rpcShowCoverImage'];
useAnime4K = json['useAnime4K'];
useMpvConfig = json['useMpvConfig'];
}
Map<String, dynamic> toJson() => {
@ -736,7 +736,7 @@ class Settings {
'rpcShowReadingWatchingProgress': rpcShowReadingWatchingProgress,
'rpcShowTitle': rpcShowTitle,
'rpcShowCoverImage': rpcShowCoverImage,
'useAnime4K': useAnime4K,
'useMpvConfig': useMpvConfig,
};
}

View file

@ -628,14 +628,14 @@ const SettingsSchema = CollectionSchema(
name: r'updatedAt',
type: IsarType.long,
),
r'useAnime4K': PropertySchema(
r'useLibass': PropertySchema(
id: 116,
name: r'useAnime4K',
name: r'useLibass',
type: IsarType.bool,
),
r'useLibass': PropertySchema(
r'useMpvConfig': PropertySchema(
id: 117,
name: r'useLibass',
name: r'useMpvConfig',
type: IsarType.bool,
),
r'usePageTapZones': PropertySchema(
@ -1249,8 +1249,8 @@ void _settingsSerialize(
writer.writeBool(offsets[113], object.themeIsDark);
writer.writeBool(offsets[114], object.updateProgressAfterReading);
writer.writeLong(offsets[115], object.updatedAt);
writer.writeBool(offsets[116], object.useAnime4K);
writer.writeBool(offsets[117], object.useLibass);
writer.writeBool(offsets[116], object.useLibass);
writer.writeBool(offsets[117], object.useMpvConfig);
writer.writeBool(offsets[118], object.usePageTapZones);
writer.writeString(offsets[119], object.userAgent);
}
@ -1468,8 +1468,8 @@ Settings _settingsDeserialize(
themeIsDark: reader.readBoolOrNull(offsets[113]),
updateProgressAfterReading: reader.readBoolOrNull(offsets[114]),
updatedAt: reader.readLongOrNull(offsets[115]),
useAnime4K: reader.readBoolOrNull(offsets[116]),
useLibass: reader.readBoolOrNull(offsets[117]),
useLibass: reader.readBoolOrNull(offsets[116]),
useMpvConfig: reader.readBoolOrNull(offsets[117]),
usePageTapZones: reader.readBoolOrNull(offsets[118]),
userAgent: reader.readStringOrNull(offsets[119]),
);
@ -9591,33 +9591,6 @@ extension SettingsQueryFilter
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> useAnime4KIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'useAnime4K',
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition>
useAnime4KIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'useAnime4K',
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> useAnime4KEqualTo(
bool? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'useAnime4K',
value: value,
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> useLibassIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
@ -9644,6 +9617,33 @@ extension SettingsQueryFilter
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> useMpvConfigIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'useMpvConfig',
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition>
useMpvConfigIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'useMpvConfig',
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> useMpvConfigEqualTo(
bool? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'useMpvConfig',
value: value,
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition>
usePageTapZonesIsNull() {
return QueryBuilder.apply(this, (query) {
@ -11239,18 +11239,6 @@ extension SettingsQuerySortBy on QueryBuilder<Settings, Settings, QSortBy> {
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByUseAnime4K() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'useAnime4K', Sort.asc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByUseAnime4KDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'useAnime4K', Sort.desc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByUseLibass() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'useLibass', Sort.asc);
@ -11263,6 +11251,18 @@ extension SettingsQuerySortBy on QueryBuilder<Settings, Settings, QSortBy> {
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByUseMpvConfig() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'useMpvConfig', Sort.asc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByUseMpvConfigDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'useMpvConfig', Sort.desc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByUsePageTapZones() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'usePageTapZones', Sort.asc);
@ -12508,18 +12508,6 @@ extension SettingsQuerySortThenBy
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByUseAnime4K() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'useAnime4K', Sort.asc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByUseAnime4KDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'useAnime4K', Sort.desc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByUseLibass() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'useLibass', Sort.asc);
@ -12532,6 +12520,18 @@ extension SettingsQuerySortThenBy
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByUseMpvConfig() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'useMpvConfig', Sort.asc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByUseMpvConfigDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'useMpvConfig', Sort.desc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByUsePageTapZones() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'usePageTapZones', Sort.asc);
@ -13184,18 +13184,18 @@ extension SettingsQueryWhereDistinct
});
}
QueryBuilder<Settings, Settings, QDistinct> distinctByUseAnime4K() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'useAnime4K');
});
}
QueryBuilder<Settings, Settings, QDistinct> distinctByUseLibass() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'useLibass');
});
}
QueryBuilder<Settings, Settings, QDistinct> distinctByUseMpvConfig() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'useMpvConfig');
});
}
QueryBuilder<Settings, Settings, QDistinct> distinctByUsePageTapZones() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'usePageTapZones');
@ -13993,18 +13993,18 @@ extension SettingsQueryProperty
});
}
QueryBuilder<Settings, bool?, QQueryOperations> useAnime4KProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'useAnime4K');
});
}
QueryBuilder<Settings, bool?, QQueryOperations> useLibassProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'useLibass');
});
}
QueryBuilder<Settings, bool?, QQueryOperations> useMpvConfigProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'useMpvConfig');
});
}
QueryBuilder<Settings, bool?, QQueryOperations> usePageTapZonesProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'usePageTapZones');

View file

@ -184,12 +184,12 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
with TickerProviderStateMixin, WidgetsBindingObserver {
late final GlobalKey<VideoState> _key = GlobalKey<VideoState>();
late final useLibass = ref.read(useLibassStateProvider);
late final useAnime4K = ref.read(useAnime4KStateProvider);
late final useMpvConfig = ref.read(useMpvConfigStateProvider);
late final Player _player = Player(
configuration: PlayerConfiguration(
libass: useLibass,
config: true,
configDir: useAnime4K ? widget.mpvDirectory?.path ?? "" : "",
configDir: useMpvConfig ? widget.mpvDirectory?.path ?? "" : "",
observeProperties: {
"user-data/aniyomi/show_text": generated.mpv_format.MPV_FORMAT_NODE,
"user-data/aniyomi/toggle_ui": generated.mpv_format.MPV_FORMAT_NODE,
@ -329,12 +329,84 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
botToast(text);
}
break;
case "user-data/aniyomi/toggle_ui":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/show_panel":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/software_keyboard":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/set_button_title":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/reset_button_title":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/toggle_button":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/switch_episode":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/pause":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/seek_by":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/seek_to":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/seek_by_with_text":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/seek_to_with_text":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "user-data/aniyomi/launch_int_picker":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
}
break;
case "mangayomi/chapter_titles":
if (value.ref.format == generated.mpv_format.MPV_FORMAT_STRING) {
final text = value.ref.u.string.cast<Utf8>().toDartString();
final data = jsonDecode(text) as List<dynamic>;
_chapterMarks.value = data
.map((e) => (e["title"] as String, (e["time"] as int) * 1000))
.map(
(e) => (
e["title"] as String,
e["time"] is double
? (e["time"] as double).toInt() * 1000
: (e["time"] as int) * 1000,
),
)
.toList();
}
break;
@ -1177,7 +1249,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
onPressed: () => _videoSettingDraggableMenu(context),
icon: const Icon(Icons.video_settings, color: Colors.white),
),
if (useAnime4K)
if (useMpvConfig)
PopupMenuButton<String>(
tooltip: '', // Remove default tooltip "Show menu" for consistency
icon: const Icon(Icons.high_quality, color: Colors.white),

View file

@ -6,7 +6,7 @@ part of 'update_manga_detail_providers.dart';
// RiverpodGenerator
// **************************************************************************
String _$updateMangaDetailHash() => r'769afb98684ba7d53c36d14637a51d1be9e6826d';
String _$updateMangaDetailHash() => r'33c6bd0f1de57e2e839ae695a0301893b9a94624';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -0,0 +1,287 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/models/custom_button.dart';
import 'package:mangayomi/modules/more/settings/player/providers/custom_buttons_provider.dart';
import 'package:mangayomi/modules/widgets/progress_center.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
class CustomButtonScreen extends ConsumerStatefulWidget {
const CustomButtonScreen({super.key});
@override
ConsumerState<CustomButtonScreen> createState() => _CustomButtonScreenState();
}
class _CustomButtonScreenState extends ConsumerState<CustomButtonScreen> {
List<CustomButton> _entries = [];
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final customButtons = ref.watch(getCustomButtonsStreamProvider);
return Scaffold(
appBar: AppBar(title: Text(l10n.custom_buttons_edit)),
body: customButtons.when(
data: (data) {
if (data.isEmpty) {
_entries = [];
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
l10n.custom_buttons_edit,
textAlign: TextAlign.center,
),
),
);
}
data.sort((a, b) => (a.pos ?? 0).compareTo(b.pos ?? 0));
_entries = data;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ReorderableListView.builder(
buildDefaultDragHandles: false,
itemCount: _entries.length,
itemBuilder: (context, index) {
final customButton = _entries[index];
return Row(
key: Key('custom_btn_${customButton.id}'),
children: [
ReorderableDragStartListener(
index: index,
child: const Icon(Icons.drag_handle),
),
Expanded(
child: Row(
children: [
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) {
/*if (oldIndex < newIndex) {
final draggedItem = navigationOrder[oldIndex];
for (var i = oldIndex; i < newIndex - 1; i++) {
navigationOrder[i] = navigationOrder[i + 1];
}
navigationOrder[newIndex - 1] = draggedItem;
} else {
final draggedItem = navigationOrder[oldIndex];
for (var i = oldIndex; i > newIndex; i--) {
navigationOrder[i] = navigationOrder[i - 1];
}
navigationOrder[newIndex] = draggedItem;
}
ref
.read(navigationOrderStateProvider.notifier)
.set(navigationOrder);*/
},
),
);
},
error: (Object error, StackTrace stackTrace) {
_entries = [];
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
l10n.custom_buttons_edit,
textAlign: TextAlign.center,
),
),
);
},
loading: () {
return const ProgressCenter();
},
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
bool isExist = false;
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(
children: [
const Icon(Icons.add),
const SizedBox(width: 10),
Text(l10n.add),
],
),
),
);
}
}
class CustomTextFormField extends StatelessWidget {
final TextEditingController controller;
final List<CustomButton> entries;
final BuildContext context;
final Function(bool) exist;
final bool isExist;
final String name;
final Function(String) val;
const CustomTextFormField({
super.key,
required this.controller,
required this.entries,
required this.context,
required this.exist,
required this.isExist,
this.name = "",
required this.val,
});
@override
Widget build(BuildContext context) {
final l10n = l10nLocalizations(context);
return TextFormField(
autofocus: true,
controller: controller,
keyboardType: TextInputType.text,
onChanged: (value) {
if (name != controller.text) {
exist(
entries
.where((element) => element.title == controller.text)
.toList()
.isNotEmpty,
);
}
val(value);
},
onFieldSubmitted: (s) {},
decoration: InputDecoration(
helperText: isExist == true
? l10n!.add_category_error_exist
: l10n!.category_name_required,
helperStyle: TextStyle(color: isExist == true ? Colors.red : null),
isDense: true,
label: Text(
l10n.name,
style: TextStyle(color: isExist == true ? Colors.red : null),
),
filled: true,
fillColor: Colors.transparent,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: isExist == true
? Colors.red
: Theme.of(context).primaryColor,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: isExist == true
? Colors.red
: Theme.of(context).primaryColor,
),
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: isExist == true
? Colors.red
: Theme.of(context).primaryColor,
),
),
),
);
}
}

View file

@ -4,8 +4,10 @@ import 'dart:io';
import 'package:archive/archive.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.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/widgets/list_tile_widget.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
@ -51,7 +53,7 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
final enableAutoSkip = ref.watch(enableAutoSkipStateProvider);
final aniSkipTimeoutLength = ref.watch(aniSkipTimeoutLengthStateProvider);
final useLibass = ref.watch(useLibassStateProvider);
final useAnime4K = ref.watch(useAnime4KStateProvider);
final useMpvConfig = ref.watch(useMpvConfigStateProvider);
final hwdecMode = ref.watch(hwdecModeStateProvider(rawValue: true));
final fullScreenPlayer = ref.watch(fullScreenPlayerStateProvider);
@ -484,7 +486,7 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
],
),
SwitchListTile(
value: useAnime4K,
value: useMpvConfig,
title: Text(context.l10n.enable_mpv),
subtitle: Text(
context.l10n.mpv_info,
@ -494,7 +496,7 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
if (value && !(await _checkMpvConfig(context))) {
return;
}
ref.read(useAnime4KStateProvider.notifier).set(value);
ref.read(useMpvConfigStateProvider.notifier).set(value);
},
),
ListTile(
@ -507,6 +509,14 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
style: TextStyle(fontSize: 11, color: context.secondaryColor),
),
),
ListTileWidget(
onTap: () {
context.push("/customButtonScreen");
},
icon: Icons.terminal,
title: context.l10n.custom_buttons,
subtitle: context.l10n.custom_buttons_info,
),
SwitchListTile(
value: fullScreenPlayer,
title: Text(context.l10n.full_screen_player),

View file

@ -0,0 +1,11 @@
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/custom_button.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
part 'custom_buttons_provider.g.dart';
@riverpod
Stream<List<CustomButton>> getCustomButtonsStream(Ref ref) async* {
yield* isar.customButtons.filter().idIsNotNull().watch(fireImmediately: true);
}

View file

@ -0,0 +1,30 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'custom_buttons_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$getCustomButtonsStreamHash() =>
r'463d2142793ffb5a905f6f90c3a756445be8b133';
/// See also [getCustomButtonsStream].
@ProviderFor(getCustomButtonsStream)
final getCustomButtonsStreamProvider =
AutoDisposeStreamProvider<List<CustomButton>>.internal(
getCustomButtonsStream,
name: r'getCustomButtonsStreamProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$getCustomButtonsStreamHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef GetCustomButtonsStreamRef
= AutoDisposeStreamProviderRef<List<CustomButton>>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View file

@ -219,17 +219,17 @@ class UseLibassState extends _$UseLibassState {
}
@riverpod
class UseAnime4KState extends _$UseAnime4KState {
class UseMpvConfigState extends _$UseMpvConfigState {
@override
bool build() {
return isar.settings.getSync(227)!.useAnime4K ?? false;
return isar.settings.getSync(227)!.useMpvConfig ?? false;
}
void set(bool value) {
final settings = isar.settings.getSync(227);
state = value;
isar.writeTxnSync(
() => isar.settings.putSync(settings!..useAnime4K = value),
() => isar.settings.putSync(settings!..useMpvConfig = value),
);
}
}

View file

@ -175,22 +175,22 @@ final useLibassStateProvider =
);
typedef _$UseLibassState = AutoDisposeNotifier<bool>;
String _$useAnime4KStateHash() => r'3902552d399794bf7c78d5f18adcf59f267b3cf6';
String _$useMpvConfigStateHash() => r'f91e6a7dbd3c5f7674ba74842521ecfca01c78b0';
/// See also [UseAnime4KState].
@ProviderFor(UseAnime4KState)
final useAnime4KStateProvider =
AutoDisposeNotifierProvider<UseAnime4KState, bool>.internal(
UseAnime4KState.new,
name: r'useAnime4KStateProvider',
/// See also [UseMpvConfigState].
@ProviderFor(UseMpvConfigState)
final useMpvConfigStateProvider =
AutoDisposeNotifierProvider<UseMpvConfigState, bool>.internal(
UseMpvConfigState.new,
name: r'useMpvConfigStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$useAnime4KStateHash,
: _$useMpvConfigStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$UseAnime4KState = AutoDisposeNotifier<bool>;
typedef _$UseMpvConfigState = AutoDisposeNotifier<bool>;
String _$hwdecModeStateHash() => r'8186e3c5f3db0e952f629d56b2e580e546aed65e';
/// Copied from Dart SDK

View file

@ -6,6 +6,7 @@ import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/category.dart';
import 'package:mangayomi/models/changed.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/custom_button.dart';
import 'package:mangayomi/models/download.dart';
import 'package:mangayomi/models/update.dart';
import 'package:mangayomi/models/history.dart';
@ -176,6 +177,7 @@ class StorageProvider {
ChangedPartSchema,
ChapterSchema,
CategorySchema,
CustomButtonSchema,
UpdateSchema,
HistorySchema,
DownloadSchema,
@ -199,6 +201,29 @@ class StorageProvider {
});
}
final customButton = await isar.customButtons
.filter()
.idEqualTo(1)
.findFirst();
if (customButton == null) {
await isar.writeTxn(() async {
isar.customButtons.put(
CustomButton(
title: "+85 s",
codePress:
"var intro_length = mp.get_property_number(\"user-data/current-anime/intro-length\")\naniyomi.right_seek_by(intro_length)",
codeLongPress:
"aniyomi.int_picker(\"Change intro length\", \"%ds\", 0, 255, 1, \"user-data/current-anime/intro-length\")",
codeStartup:
"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\")",
isFavourite: true,
pos: 0,
updatedAt: DateTime.now().millisecondsSinceEpoch,
),
);
});
}
return isar;
}
}

View file

@ -16,6 +16,7 @@ import 'package:mangayomi/modules/more/data_and_storage/create_backup.dart';
import 'package:mangayomi/modules/more/data_and_storage/data_and_storage.dart';
import 'package:mangayomi/modules/more/settings/appearance/custom_navigation_settings.dart';
import 'package:mangayomi/modules/more/settings/browse/source_repositories.dart';
import 'package:mangayomi/modules/more/settings/player/custom_button_screen.dart';
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
import 'package:mangayomi/modules/more/statistics/statistics_screen.dart';
import 'package:mangayomi/modules/novel/novel_reader_view.dart';
@ -214,6 +215,10 @@ class RouterNotifier extends ChangeNotifier {
name: "customNavigationSettings",
child: const CustomNavigationSettings(),
),
_genericRoute(
name: "customButtonScreen",
child: const CustomButtonScreen(),
),
_genericRoute<Manga>(
name: "migrate",
builder: (manga) => MigrationScreen(manga: manga),