mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-01-11 22:40:36 +00:00
added filler, thumbnail and description info to chapter list
This commit is contained in:
parent
3dae39a86c
commit
6be2775fee
14 changed files with 313 additions and 22 deletions
|
|
@ -30,7 +30,6 @@ import 'package:mangayomi/utils/discord_rpc.dart';
|
|||
import 'package:mangayomi/utils/url_protocol/api.dart';
|
||||
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_provider.dart';
|
||||
import 'package:mangayomi/modules/library/providers/file_scanner.dart';
|
||||
// ignore: depend_on_referenced_packages
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ class Manga {
|
|||
|
||||
String? customCoverFromTracker;
|
||||
|
||||
/// only update X days after `lastUpdate`
|
||||
int? smartUpdateDays;
|
||||
|
||||
int? updatedAt;
|
||||
|
||||
@Backlink(to: "manga")
|
||||
|
|
@ -76,6 +79,7 @@ class Manga {
|
|||
this.isLocalArchive = false,
|
||||
this.customCoverImage,
|
||||
this.customCoverFromTracker,
|
||||
this.smartUpdateDays,
|
||||
this.updatedAt = 0,
|
||||
});
|
||||
|
||||
|
|
@ -101,6 +105,7 @@ class Manga {
|
|||
source = json['source'];
|
||||
status = Status.values[json['status']];
|
||||
customCoverFromTracker = json['customCoverFromTracker'];
|
||||
smartUpdateDays = json['smartUpdateDays'];
|
||||
updatedAt = json['updatedAt'];
|
||||
}
|
||||
|
||||
|
|
@ -125,6 +130,7 @@ class Manga {
|
|||
'source': source,
|
||||
'status': status.index,
|
||||
'customCoverFromTracker': customCoverFromTracker,
|
||||
'smartUpdateDays': smartUpdateDays,
|
||||
'updatedAt': updatedAt ?? 0,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,19 +108,24 @@ const MangaSchema = CollectionSchema(
|
|||
name: r'name',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'source': PropertySchema(
|
||||
r'smartUpdateDays': PropertySchema(
|
||||
id: 18,
|
||||
name: r'smartUpdateDays',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'source': PropertySchema(
|
||||
id: 19,
|
||||
name: r'source',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'status': PropertySchema(
|
||||
id: 19,
|
||||
id: 20,
|
||||
name: r'status',
|
||||
type: IsarType.byte,
|
||||
enumMap: _MangastatusEnumValueMap,
|
||||
),
|
||||
r'updatedAt': PropertySchema(
|
||||
id: 20,
|
||||
id: 21,
|
||||
name: r'updatedAt',
|
||||
type: IsarType.long,
|
||||
)
|
||||
|
|
@ -258,9 +263,10 @@ void _mangaSerialize(
|
|||
writer.writeLong(offsets[15], object.lastUpdate);
|
||||
writer.writeString(offsets[16], object.link);
|
||||
writer.writeString(offsets[17], object.name);
|
||||
writer.writeString(offsets[18], object.source);
|
||||
writer.writeByte(offsets[19], object.status.index);
|
||||
writer.writeLong(offsets[20], object.updatedAt);
|
||||
writer.writeLong(offsets[18], object.smartUpdateDays);
|
||||
writer.writeString(offsets[19], object.source);
|
||||
writer.writeByte(offsets[20], object.status.index);
|
||||
writer.writeLong(offsets[21], object.updatedAt);
|
||||
}
|
||||
|
||||
Manga _mangaDeserialize(
|
||||
|
|
@ -290,10 +296,11 @@ Manga _mangaDeserialize(
|
|||
lastUpdate: reader.readLongOrNull(offsets[15]),
|
||||
link: reader.readStringOrNull(offsets[16]),
|
||||
name: reader.readStringOrNull(offsets[17]),
|
||||
source: reader.readStringOrNull(offsets[18]),
|
||||
status: _MangastatusValueEnumMap[reader.readByteOrNull(offsets[19])] ??
|
||||
smartUpdateDays: reader.readLongOrNull(offsets[18]),
|
||||
source: reader.readStringOrNull(offsets[19]),
|
||||
status: _MangastatusValueEnumMap[reader.readByteOrNull(offsets[20])] ??
|
||||
Status.ongoing,
|
||||
updatedAt: reader.readLongOrNull(offsets[20]),
|
||||
updatedAt: reader.readLongOrNull(offsets[21]),
|
||||
);
|
||||
return object;
|
||||
}
|
||||
|
|
@ -343,11 +350,13 @@ P _mangaDeserializeProp<P>(
|
|||
case 17:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 18:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 19:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 20:
|
||||
return (_MangastatusValueEnumMap[reader.readByteOrNull(offset)] ??
|
||||
Status.ongoing) as P;
|
||||
case 20:
|
||||
case 21:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
|
|
@ -2591,6 +2600,75 @@ extension MangaQueryFilter on QueryBuilder<Manga, Manga, QFilterCondition> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterFilterCondition> smartUpdateDaysIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
property: r'smartUpdateDays',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterFilterCondition> smartUpdateDaysIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||
property: r'smartUpdateDays',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterFilterCondition> smartUpdateDaysEqualTo(
|
||||
int? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'smartUpdateDays',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterFilterCondition> smartUpdateDaysGreaterThan(
|
||||
int? value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'smartUpdateDays',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterFilterCondition> smartUpdateDaysLessThan(
|
||||
int? value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'smartUpdateDays',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterFilterCondition> smartUpdateDaysBetween(
|
||||
int? lower,
|
||||
int? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'smartUpdateDays',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterFilterCondition> sourceIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
|
|
@ -3100,6 +3178,18 @@ extension MangaQuerySortBy on QueryBuilder<Manga, Manga, QSortBy> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterSortBy> sortBySmartUpdateDays() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'smartUpdateDays', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterSortBy> sortBySmartUpdateDaysDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'smartUpdateDays', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterSortBy> sortBySource() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'source', Sort.asc);
|
||||
|
|
@ -3330,6 +3420,18 @@ extension MangaQuerySortThenBy on QueryBuilder<Manga, Manga, QSortThenBy> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterSortBy> thenBySmartUpdateDays() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'smartUpdateDays', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterSortBy> thenBySmartUpdateDaysDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'smartUpdateDays', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QAfterSortBy> thenBySource() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'source', Sort.asc);
|
||||
|
|
@ -3485,6 +3587,12 @@ extension MangaQueryWhereDistinct on QueryBuilder<Manga, Manga, QDistinct> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QDistinct> distinctBySmartUpdateDays() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'smartUpdateDays');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, Manga, QDistinct> distinctBySource(
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
|
|
@ -3621,6 +3729,12 @@ extension MangaQueryProperty on QueryBuilder<Manga, Manga, QQueryProperty> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, int?, QQueryOperations> smartUpdateDaysProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'smartUpdateDays');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Manga, String?, QQueryOperations> sourceProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'source');
|
||||
|
|
|
|||
|
|
@ -1855,6 +1855,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
child: Row(
|
||||
children: [
|
||||
Expanded(child: widget.action!),
|
||||
Expanded(child: _smartUpdateDays()),
|
||||
Expanded(
|
||||
child: widget.itemType == ItemType.novel
|
||||
? SizedBox.shrink()
|
||||
|
|
@ -1905,6 +1906,33 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
);
|
||||
}
|
||||
|
||||
Widget _smartUpdateDays() {
|
||||
return SizedBox(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
elevation: 0,
|
||||
),
|
||||
onPressed: () {},
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.hourglass_empty,
|
||||
size: 20,
|
||||
color: context.secondaryColor,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
"${widget.manga?.smartUpdateDays ?? "N/A"}",
|
||||
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Tracker button
|
||||
Widget _action() {
|
||||
return StreamBuilder(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:mangayomi/models/chapter.dart';
|
|||
import 'package:mangayomi/models/update.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/services/get_detail.dart';
|
||||
import 'package:mangayomi/utils/extensions/others.dart';
|
||||
import 'package:mangayomi/utils/utils.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
|
@ -62,7 +63,7 @@ Future<dynamic> updateMangaDetail(
|
|||
return;
|
||||
}
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.putSync(manga);
|
||||
final mangaId = isar.mangas.putSync(manga);
|
||||
manga.lastUpdate = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
List<Chapter> chapters = [];
|
||||
|
|
@ -129,6 +130,28 @@ Future<dynamic> updateMangaDetail(
|
|||
oldChap.manga.saveSync();
|
||||
}
|
||||
}
|
||||
final List<int> daysBetweenUploads = [];
|
||||
for (var i = 0; i + 1 < chaps.length; i++) {
|
||||
if (chaps[i].dateUpload != null && chaps[i + 1].dateUpload != null) {
|
||||
final date1 = DateTime.fromMillisecondsSinceEpoch(
|
||||
int.parse(chaps[i].dateUpload!),
|
||||
);
|
||||
final date2 = DateTime.fromMillisecondsSinceEpoch(
|
||||
int.parse(chaps[i + 1].dateUpload!),
|
||||
);
|
||||
daysBetweenUploads.add(date1.difference(date2).abs().inDays);
|
||||
}
|
||||
}
|
||||
if (daysBetweenUploads.isNotEmpty) {
|
||||
final median = daysBetweenUploads.median();
|
||||
isar.mangas.putSync(
|
||||
manga
|
||||
..id = mangaId
|
||||
..smartUpdateDays = median != 0
|
||||
? median
|
||||
: daysBetweenUploads.arithmeticMean(),
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (e, s) {
|
||||
if (showToast) botToast('$e\n$s');
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'update_manga_detail_providers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$updateMangaDetailHash() => r'f75938777640ae0cfee181a2df7a12a56c42db41';
|
||||
String _$updateMangaDetailHash() => r'769afb98684ba7d53c36d14637a51d1be9e6826d';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
|
||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
import 'package:marquee/marquee.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
|
|
@ -10,6 +13,8 @@ import 'package:mangayomi/utils/extensions/chapter.dart';
|
|||
import 'package:mangayomi/utils/extensions/string_extensions.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/download/download_page_widget.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:photo_view/photo_view_gallery.dart';
|
||||
|
||||
class ChapterListTileWidget extends ConsumerWidget {
|
||||
final Chapter chapter;
|
||||
|
|
@ -33,6 +38,9 @@ class ChapterListTileWidget extends ConsumerWidget {
|
|||
onLongPress: () => _handleInteraction(ref),
|
||||
onSecondaryTap: () => _handleInteraction(ref),
|
||||
child: ListTile(
|
||||
tileColor: (chapter.isFiller ?? false)
|
||||
? context.primaryColor.withValues(alpha: 0.15)
|
||||
: null,
|
||||
textColor: chapter.isRead!
|
||||
? context.isLight
|
||||
? Colors.black.withValues(alpha: 0.4)
|
||||
|
|
@ -44,14 +52,43 @@ class ChapterListTileWidget extends ConsumerWidget {
|
|||
onTap: () async => _handleInteraction(ref, context),
|
||||
title: Row(
|
||||
children: [
|
||||
if (chapter.thumbnailUrl != null)
|
||||
_thumbnailPreview(context, chapter.thumbnailUrl),
|
||||
chapter.isBookmarked!
|
||||
? Icon(Icons.bookmark, size: 16, color: context.primaryColor)
|
||||
: Container(),
|
||||
Flexible(child: _buildTitle(chapter.name!, context)),
|
||||
chapter.description != null
|
||||
? Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildTitle(chapter.name!, context),
|
||||
Text(
|
||||
chapter.description!,
|
||||
style: const TextStyle(fontSize: 11),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Flexible(child: _buildTitle(chapter.name!, context)),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
if (chapter.isFiller ?? false)
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.label, size: 16, color: context.primaryColor),
|
||||
Text(
|
||||
" Filler ",
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if ((chapter.manga.value!.isLocalArchive ?? false) == false)
|
||||
Text(
|
||||
chapter.dateUpload == null || chapter.dateUpload!.isEmpty
|
||||
|
|
@ -172,4 +209,62 @@ class ChapterListTileWidget extends ConsumerWidget {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _thumbnailPreview(BuildContext context, String? imageUrl) {
|
||||
final imageProvider = CustomExtendedNetworkImageProvider(
|
||||
toImgUrl(imageUrl ?? ""),
|
||||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_openImage(context, imageProvider);
|
||||
},
|
||||
child: SizedBox(
|
||||
width: 50,
|
||||
height: 65,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||
image: DecorationImage(image: imageProvider, fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _openImage(BuildContext context, ImageProvider imageProvider) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
body: Stack(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: PhotoViewGallery.builder(
|
||||
backgroundDecoration: const BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
itemCount: 1,
|
||||
builder: (context, index) {
|
||||
return PhotoViewGalleryPageOptions(
|
||||
imageProvider: imageProvider,
|
||||
minScale: PhotoViewComputedScale.contained,
|
||||
maxScale: 2.0,
|
||||
);
|
||||
},
|
||||
loadingBuilder: (context, event) {
|
||||
return const ProgressCenter();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ class PlayerScreen extends ConsumerStatefulWidget {
|
|||
class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
||||
int _total = 0;
|
||||
int _received = 0;
|
||||
late http.StreamedResponse _response;
|
||||
http.StreamedResponse? _response;
|
||||
final List<int> _bytes = [];
|
||||
late StreamSubscription<List<int>>? _subscription;
|
||||
StreamSubscription<List<int>>? _subscription;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
|
@ -661,8 +661,8 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
|||
),
|
||||
),
|
||||
);
|
||||
_total = _response.contentLength ?? 0;
|
||||
_subscription = _response.stream.listen((value) {
|
||||
_total = _response?.contentLength ?? 0;
|
||||
_subscription = _response?.stream.listen((value) {
|
||||
setState(() {
|
||||
_bytes.addAll(value);
|
||||
_received += value.length;
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ class DiscordRPC {
|
|||
: "Reading";
|
||||
final title = chapter.manga.value!.name;
|
||||
final chapterTitle = chapter.name;
|
||||
final imageUrl = chapter.manga.value!.imageUrl;
|
||||
final rpcShowTitle = ref.read(rpcShowTitleStateProvider);
|
||||
final rpcShowCoverImage = ref.read(rpcShowCoverImageStateProvider);
|
||||
await updateActivity(
|
||||
|
|
@ -98,9 +99,10 @@ class DiscordRPC {
|
|||
state: rpcShowTitle && rpcShowReadingWatchingProgress
|
||||
? chapterTitle
|
||||
: "-----",
|
||||
assets: rpcShowCoverImage
|
||||
assets:
|
||||
rpcShowCoverImage && imageUrl != null && imageUrl.startsWith("http")
|
||||
? RPCAssets(
|
||||
largeImage: chapter.manga.value!.imageUrl,
|
||||
largeImage: imageUrl,
|
||||
largeText: rpcShowTitle ? chapter.manga.value!.name : "-----",
|
||||
smallImage: "app-icon",
|
||||
smallText: "Mangayomi",
|
||||
|
|
|
|||
|
|
@ -20,6 +20,21 @@ extension LetExtension<T> on T {
|
|||
}
|
||||
}
|
||||
|
||||
extension MedianExtension on List<int> {
|
||||
int median() {
|
||||
var middle = length ~/ 2;
|
||||
if (length % 2 == 1) {
|
||||
return this[middle];
|
||||
} else {
|
||||
return ((this[middle - 1] + this[middle]) / 2).round();
|
||||
}
|
||||
}
|
||||
|
||||
int arithmeticMean() {
|
||||
return isNotEmpty ? (reduce((e1, e2) => e1 + e2) / length).round() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
extension ImageProviderExtension on ImageProvider {
|
||||
Future<Uint8List?> getBytes(
|
||||
BuildContext context, {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <media_kit_video/media_kit_video_plugin.h>
|
||||
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
#include <volume_controller/volume_controller_plugin.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
#include <window_to_front/window_to_front_plugin.h>
|
||||
|
||||
|
|
@ -42,6 +43,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
|||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) volume_controller_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "VolumeControllerPlugin");
|
||||
volume_controller_plugin_register_with_registrar(volume_controller_registrar);
|
||||
g_autoptr(FlPluginRegistrar) window_manager_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
|
||||
window_manager_plugin_register_with_registrar(window_manager_registrar);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||
media_kit_video
|
||||
screen_retriever_linux
|
||||
url_launcher_linux
|
||||
volume_controller
|
||||
window_manager
|
||||
window_to_front
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1160,7 +1160,7 @@ packages:
|
|||
source: hosted
|
||||
version: "0.5.3"
|
||||
media_kit:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: media_kit
|
||||
ref: HEAD
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ dependencies:
|
|||
flutter_web_auth_2: ^3.1.2
|
||||
numberpicker: ^2.1.2
|
||||
encrypt: ^5.0.3
|
||||
media_kit:
|
||||
git:
|
||||
url: https://github.com/Schnitzel5/media-kit.git
|
||||
path: media_kit
|
||||
media_kit_video:
|
||||
git:
|
||||
url: https://github.com/Schnitzel5/media-kit.git
|
||||
|
|
|
|||
Loading…
Reference in a new issue