diff --git a/lib/main.dart b/lib/main.dart index 4d44934..3976f4f 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,7 +6,6 @@ import 'pages/later_page.dart'; import '/novel/novel_reader.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:isar/isar.dart'; import 'package:media_kit/media_kit.dart'; import 'package:path_provider/path_provider.dart'; @@ -82,21 +81,21 @@ class Navigation extends StatelessWidget { showUnselectedLabels: false, currentIndex: shell.currentIndex, onTap: (value) => shell.goBranch(value), - items: [ + items: const [ BottomNavigationBarItem( - icon: Icon(MdiIcons.youtubeTv), + icon: Icon(Icons.ondemand_video_rounded), label: 'Anime', ), BottomNavigationBarItem( - icon: Icon(MdiIcons.bookOpenOutline), + icon: Icon(Icons.menu_book_rounded), label: 'Manga', ), BottomNavigationBarItem( - icon: Icon(MdiIcons.book), + icon: Icon(Icons.book_rounded), label: 'Novels', ), BottomNavigationBarItem( - icon: Icon(MdiIcons.bookmark), + icon: Icon(Icons.bookmark), label: 'Later', ) ], @@ -114,11 +113,11 @@ class Navigation extends StatelessWidget { path: '/anime', pageBuilder: (context, state) => MaterialPage( child: AniPage( - key: (state.queryParameters.isEmpty) + key: (state.uri.queryParameters.isEmpty) ? null - : Key(state.queryParameters['tag']!), + : Key(state.uri.queryParameters['tag']!), type: 'anime', - tag: state.queryParameters['tag'], + tag: state.uri.queryParameters['tag'], ), ), routes: [ @@ -150,11 +149,11 @@ class Navigation extends StatelessWidget { path: '/manga', builder: (context, state) { return AniPage( - key: (state.queryParameters.isEmpty) + key: (state.uri.queryParameters.isEmpty) ? null - : Key(state.queryParameters['tag']!), + : Key(state.uri.queryParameters['tag']!), type: 'manga', - tag: state.queryParameters['tag'], + tag: state.uri.queryParameters['tag'], ); }, routes: [ diff --git a/lib/media/media_anime.dart b/lib/media/media_anime.dart index 26b1c7e..6bb3216 100644 --- a/lib/media/media_anime.dart +++ b/lib/media/media_anime.dart @@ -1,18 +1,14 @@ import 'dart:async'; import 'package:go_router/go_router.dart'; -import 'package:wakelock_plus/wakelock_plus.dart'; import '/models/info_models.dart'; -import 'package:dio/dio.dart'; -import 'package:path/path.dart' as p; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; -import 'package:path_provider/path_provider.dart'; import 'dart:io'; import 'package:window_manager/window_manager.dart'; -const blank = Source(qualities: {}, subtitles: []); +const Source blank = Source(qualities: {}, subtitles: {}); class AniViewer extends StatefulWidget { final List episodes; @@ -25,11 +21,10 @@ class AniViewer extends StatefulWidget { } class AniViewerState extends State { - static final Player player = Player(); + final Player player = Player(); late final VideoController controller = VideoController(player); - final List subTracks = []; late int currentEpisode = widget.episode; - Source getMedia = blank; + Source media = blank; bool fullscreen = false; bool show = false; @@ -45,45 +40,18 @@ class AniViewerState extends State { ); Future.microtask( () async { - if (!Platform.isLinux) { - await WakelockPlus.enable(); - } await play(); }, ); } Future play() async { - getMedia = await widget.episodes[currentEpisode].call!(); - if (getMedia.qualities.isNotEmpty) { - final Directory dir = Directory( - p.join( - (await getTemporaryDirectory()).path, - 'anisubs', - ), - ); - if (getMedia.subtitles.isNotEmpty) { - for (final Map i in getMedia.subtitles) { - await Dio().download( - i['url'], - p.join(dir.path, "${i['lang']}.vtt"), - ); - } - (player.platform as NativePlayer) - ..setProperty("sub-auto", 'all') - ..setProperty("sub-file-paths", dir.path) - ..setProperty( - 'sid', - '${getMedia.subtitles.lastIndexWhere( - (element) => - element['lang']!.toLowerCase().contains('english'), - ) + 1}', - ); - } + media = await widget.episodes[currentEpisode].call!(); + if (media.qualities.isNotEmpty) { await player.open( Media( - getMedia.qualities.values.first, - httpHeaders: getMedia.headers ?? {}, + media.qualities.values.first, + httpHeaders: media.headers ?? {}, ), ); setState(() {}); @@ -96,20 +64,10 @@ class AniViewerState extends State { SystemChrome.setPreferredOrientations([]); Future.microtask( () async { - if (!Platform.isLinux) { - await WakelockPlus.disable(); - } - try { - Directory( - p.join((await getTemporaryDirectory()).path, 'anisubs'), - ).deleteSync( - recursive: true, - ); - } catch (_) {} if (!Platform.isAndroid && !Platform.isIOS) { await windowManager.setFullScreen(false); } - await player.stop(); + await player.dispose(); }, ); super.dispose(); @@ -119,26 +77,29 @@ class AniViewerState extends State { Widget build(context) { final Size size = MediaQuery.of(context).size; return Scaffold( - body: (getMedia.qualities.isNotEmpty) + body: (media.qualities.isNotEmpty) ? Stack( children: [ Video( controller: controller, + pauseUponEnteringBackgroundMode: false, ), CallbackShortcuts( bindings: { - const SingleActivator(LogicalKeyboardKey.arrowRight): () { - player.seek( - const Duration(seconds: 2), + const SingleActivator(LogicalKeyboardKey.arrowRight): + () async { + await player.seek( + player.state.position + const Duration(seconds: 5), ); }, - const SingleActivator(LogicalKeyboardKey.arrowLeft): () { - player.seek( - const Duration(seconds: -2), + const SingleActivator(LogicalKeyboardKey.arrowLeft): + () async { + await player.seek( + player.state.position - const Duration(seconds: 5), ); }, - const SingleActivator(LogicalKeyboardKey.space): () { - player.playOrPause(); + const SingleActivator(LogicalKeyboardKey.space): () async { + await player.playOrPause(); }, }, child: Focus( @@ -184,111 +145,12 @@ class AniViewerState extends State { top: 0, right: 0, left: 0, - child: Row( - children: [ - const BackButton(), - const Spacer(), - Text( - "Episode ${widget.episodes[currentEpisode].number}", - ), - const Spacer(), - if (widget.episodes[currentEpisode].title - .isNotEmpty) - Text( - widget.episodes[currentEpisode].title, - ), - const Spacer( - flex: 100, - ), - PopupMenuButton( - itemBuilder: (context) { - return [ - PopupMenuItem( - child: const Text('Subtitles'), - onTap: () => showBottomSheet( - context: context, - constraints: - BoxConstraints.tightFor( - height: size.height / 2, - ), - builder: (context) => ListView( - children: List.generate( - player.state.tracks.subtitle - .length, - (index) { - return ListTile( - title: Text( - // I miss you state sama - player - .state - .tracks - .subtitle[index] - .title ?? - player - .state - .tracks - .subtitle[index] - .id, - ), - onTap: () { - player.setSubtitleTrack( - player.state.tracks - .subtitle[index], - ); - context.pop(); - }, - ); - }, - ), - ), - ), - ), - PopupMenuItem( - child: const Text('Quality'), - onTap: () => showBottomSheet( - context: context, - constraints: - BoxConstraints.tightFor( - height: size.height / 2, - ), - builder: (context) => ListView( - children: List.generate( - player.state.tracks.video - .length - - 2, - (index) { - return ListTile( - title: Text( - player - .state - .tracks - .video[ - index + 2] - .title ?? - player - .state - .tracks - .video[ - index + 2] - .id, - ), - onTap: () { - player.setVideoTrack( - player.state.tracks - .video[index + 2], - ); - context.pop(); - }, - ); - }, - ), - ), - ), - ), - ]; - }, - ), - ], + child: TopBar( + currentEpisode: currentEpisode, + episodes: widget.episodes, + media: media, + player: player, + size: size, ), ), Positioned( @@ -307,20 +169,25 @@ class AniViewerState extends State { : () async { setState(() { currentEpisode -= 1; - getMedia = blank; + media = blank; }); await play(); }, icon: const Icon(Icons.skip_previous), ), - IconButton( - onPressed: () async { - await player.playOrPause(); - setState(() {}); + StreamBuilder( + stream: player.stream.playing, + initialData: true, + builder: (context, snapshot) { + return IconButton( + onPressed: () async { + await player.playOrPause(); + }, + icon: Icon((snapshot.data == true) + ? Icons.pause + : Icons.play_arrow), + ); }, - icon: Icon((player.state.playing) - ? Icons.pause - : Icons.play_arrow), ), IconButton( onPressed: (currentEpisode == @@ -329,7 +196,7 @@ class AniViewerState extends State { : () async { setState(() { currentEpisode += 1; - getMedia = blank; + media = blank; }); await play(); }, @@ -409,7 +276,7 @@ class ProgressBar extends StatefulWidget { } class ProgressBarState extends State { - late StreamSubscription position; + late final StreamSubscription position; double spot = 0; @override @@ -462,3 +329,109 @@ class ProgressBarState extends State { ); } } + +class TopBar extends StatelessWidget { + final int currentEpisode; + final List episodes; + final Source media; + final Player player; + final Size size; + + const TopBar({ + super.key, + required this.currentEpisode, + required this.episodes, + required this.player, + required this.size, + required this.media, + }); + + @override + Widget build(context) => Row( + children: [ + const BackButton(), + const Spacer(), + Text( + "Episode ${episodes[currentEpisode].number}", + ), + const Spacer(), + if (episodes[currentEpisode].title.isNotEmpty) + Text( + episodes[currentEpisode].title, + ), + const Spacer( + flex: 100, + ), + PopupMenuButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + itemBuilder: (context) => [ + if (media.subtitles.isNotEmpty) + PopupMenuItem( + child: const Text('Subtitles'), + onTap: () => showModalBottomSheet( + context: context, + constraints: BoxConstraints.tightFor( + height: size.height / 2, + ), + showDragHandle: true, + builder: (context) => ListView( + children: List.generate( + media.subtitles.length, + (index) { + return ListTile( + title: Text( + media.subtitles.keys.elementAt(index), + ), + onTap: () { + player.setSubtitleTrack( + SubtitleTrack.uri( + media.subtitles.values.elementAt(index), + ), + ); + context.pop(); + }, + ); + }, + ), + ), + ), + ), + PopupMenuItem( + child: const Text('Quality'), + onTap: () => showModalBottomSheet( + showDragHandle: true, + context: context, + constraints: BoxConstraints.tightFor( + height: size.height / 2, + ), + builder: (context) => ListView( + children: List.generate( + // player.state.tracks.video.length - 2, + media.qualities.length, + (index) { + return ListTile( + title: Text(media.qualities.keys.elementAt(index)), + onTap: () async { + final current = await player.stream.buffer.first; + print(current); + await player.open( + Media( + media.qualities.values.elementAt(index), + httpHeaders: media.headers, + ), + ); + await player.seek(current); + }, + ); + }, + ), + ), + ), + ), + ], + ), + ], + ); +} diff --git a/lib/media/media_manga.dart b/lib/media/media_manga.dart index 110aff1..1c811a4 100644 --- a/lib/media/media_manga.dart +++ b/lib/media/media_manga.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:photo_view/photo_view_gallery.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; -import '../models/info_models.dart'; class MangaReader extends StatelessWidget { final int chapter; diff --git a/lib/media/providers/anime/animepahe.dart b/lib/media/providers/anime/animepahe.dart index 142b662..9ce2f37 100644 --- a/lib/media/providers/anime/animepahe.dart +++ b/lib/media/providers/anime/animepahe.dart @@ -4,8 +4,7 @@ import 'package:html/dom.dart'; import 'package:html/parser.dart'; import '../../../models/info_models.dart'; -Future> paheList(final AniData data) async { - const String malsync = 'https://api.malsync.moe/mal/anime'; +Provider paheList(final AniData data) async { try { final Map response = (await Dio().get('$malsync/${data.malid}')).data; final String syncId = parse( @@ -22,8 +21,16 @@ Future> paheList(final AniData data) async { final String link = 'https://animepahe.ru/api?m=release&id=$syncId&sort=episode_asc'; final Map anime = (await Dio().get(link)).data; - for (int i = 2; i <= anime['last_page']; i++) { - anime['data'].addAll((await Dio().get('$link&page=$i')).data['data']); + if (anime['last_page'] > 1) { + final List>> requests = []; + for (int i = 2; i <= anime['last_page']; i++) { + requests.add( + Dio().get('$link&page=$i'), //.data['data'] + ); + } + for (Response i in await Future.wait(requests)) { + anime['data'].addAll(i.data['data']); + } } return List.generate( anime['data'].length, @@ -40,9 +47,7 @@ Future> paheList(final AniData data) async { } } -Future paheInfo(final String id) async { - final Map qualities = {}; - final Map headers = {}; +Anime paheInfo(final String id) async { final Document data = parse( (await Dio().get( 'https://animepahe.ru/play/$id', @@ -58,16 +63,17 @@ Future paheInfo(final String id) async { .getElementsByTagName('button') .where((element) => element.attributes.containsKey('data-src'))) { if (i.attributes['data-src']?.contains('kwik') ?? false) { - Document arr = parse((await Dio().get( - i.attributes['data-src']!, - options: Options( - headers: { - 'referer': 'https://animepahe.ru', - }, - ), - )) - .data); - headers.addAll({'referer': 'https://kwik.cx'}); + Document arr = parse( + (await Dio().get( + i.attributes['data-src']!, + options: Options( + headers: { + 'referer': 'https://animepahe.ru', + }, + ), + )) + .data, + ); qualities.addAll( { '${i.attributes['data-fansub']}-${i.attributes['data-resolution']}': @@ -78,7 +84,11 @@ Future paheInfo(final String id) async { ); } } - return Source(qualities: qualities, subtitles: [], headers: headers); + return Source( + qualities: qualities, + subtitles: {}, + headers: {'referer': 'https://kwik.cx'}, + ); } class JsUnpack { diff --git a/lib/media/providers/anime/aniwatch.dart b/lib/media/providers/anime/aniwatch.dart index cbf9f25..265619a 100644 --- a/lib/media/providers/anime/aniwatch.dart +++ b/lib/media/providers/anime/aniwatch.dart @@ -8,8 +8,7 @@ import '../aes_decrypt.dart'; const String zoro = "https://aniwatch.to/"; -Future> zoroList(final AniData data) async { - const String malsync = 'https://api.malsync.moe/mal/anime'; +Provider zoroList(final AniData data) async { final List episodes = []; try { final Map response = (await Dio().get( @@ -41,7 +40,7 @@ Future> zoroList(final AniData data) async { } } -Future zoroInfo(final id) async { +Anime zoroInfo(final id) async { final Options options = Options(responseType: ResponseType.plain); final Element server = parse( jsonDecode( @@ -91,18 +90,12 @@ Future zoroInfo(final id) async { qualities: { 'default': sources['sources'][0]['file'], }, - subtitles: List.generate( - sources['tracks'].length, - (index) { - return { - 'lang': sources['tracks'][index]['label'], - 'url': sources['tracks'][index]['file'] - }; - }, - ), + subtitles: { + for (Map i in sources['tracks']) i['label']: i['file'], + }, ); } catch (e) { print(e); - return Source(qualities: {}, subtitles: []); + return const Source(qualities: {}, subtitles: {}); } } diff --git a/lib/media/providers/anime/hanime.dart b/lib/media/providers/anime/hanime.dart index 6095eed..f0788e1 100644 --- a/lib/media/providers/anime/hanime.dart +++ b/lib/media/providers/anime/hanime.dart @@ -3,7 +3,7 @@ import 'package:dio/dio.dart'; import 'package:string_similarity/string_similarity.dart'; import '../../../models/info_models.dart'; -Future> haniList(final AniData data) async { +Provider haniList(final AniData data) async { Response json = await Dio().post( "https://search.htv-services.com/", data: jsonEncode( @@ -27,10 +27,10 @@ Future> haniList(final AniData data) async { final List results = jsonDecode(json.data['hits']); final List videos = []; for (Map i in results) { - final Response v = await Dio().get( - "https://hanime.tv/api/v8/video?id=${i['id']}", - ); if (i['name'].toString().similarityTo(data.title) > 0.2) { + final Response v = await Dio().get( + "https://hanime.tv/api/v8/video?id=${i['id']}", + ); videos.add(v.data); } } @@ -42,10 +42,15 @@ Future> haniList(final AniData data) async { provId: '', title: (videos[index])['hentai_video']['name'], number: (index + 1).toString(), - call: () => Source(qualities: { - "default": videos[index]['videos_manifest']['servers'][0]['streams'] - [1]['url'], - }, subtitles: []), + call: () => Future( + () => Source( + qualities: { + "default": videos[index]['videos_manifest']['servers'][0] + ['streams'][1]['url'], + }, + subtitles: {}, + ), + ), ); }, ); diff --git a/lib/media/providers/anime/marin.dart b/lib/media/providers/anime/marin.dart new file mode 100644 index 0000000..472e627 --- /dev/null +++ b/lib/media/providers/anime/marin.dart @@ -0,0 +1,125 @@ +import 'dart:convert'; +import 'package:dio/dio.dart'; +import 'package:tokuwari/models/info_models.dart'; + +const String baseUrl = 'https://marin.moe/anime'; + +Provider marinList(final AniData data) async { + try { + final List episodes = []; + final Map syncResponse = + (await Dio().get('https://api.malsync.moe/mal/anime/${data.malid}')) + .data; + final String id = syncResponse['Sites']['Marin'].keys.first; + // I have a better way oh? + final Map info = await request(id, 1); + if (info['props']['episode_list']['meta']['last_page'] > 1) { + final List> requests = []; + for (int i = 2; + i <= info['props']['episode_list']['meta']['last_page']; + i++) { + requests.add(request(id, i)); + for (Map i in await Future.wait(requests)) { + info['props']['episode_list']['data'].addAll( + i['props']['episode_list']['data'], + ); + } + } + } + for (final Map i in info['props']['episode_list']['data']) { + episodes.add( + MediaProv( + provider: 'marin', + provId: '$id/${i['sort']}', + title: (i['title'] == 'No Title') ? "" : i['title'], + number: i['sort'].toString(), + call: () => marinInfo('$id/${i['sort']}'), + ), + ); + } + return episodes; + } catch (_) { + return []; + } +} + +Anime marinInfo(final String id) async { + final List headers = await getToken(); + final Options options = Options( + headers: { + 'Origin': 'https://marin.moe/', + 'Referer': 'https://marin.moe/anime/$id', + 'Cookie': + '__ddg1=;__ddg2_=; XSRF-TOKEN=${headers[0]}; marin_session=${headers[1]}', + 'User-Agent': + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36', + 'x-inertia': 'true', + 'x-inertia-version': '884345c4d568d16e3bb2fb3ae350cca9', + 'x-requested-with': 'XMLHttpRequest', + 'x-xsrf-token': headers[0].replaceAll('%3D', '='), + }, + ); + final Map info = (await Dio().post( + '$baseUrl/$id', + options: options, + )) + .data; + return Source( + qualities: { + for (Map i in info['props']['video']['data']['mirror']) + i['code']['height'].toString(): i['code']['file'], + }, + subtitles: {}, + headers: options.headers as Map, + ); +} + +Future request(final String id, final int index) async { + final List headers = await getToken(); + final Map info = (await Dio().post( + '$baseUrl/$id', + // WOOOOOOOOOoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo + data: jsonEncode({ + 'filter': { + 'episodes': true, + 'specials': true, + }, + 'eps_page': index, //why :() + }), + options: Options( + headers: { + 'Origin': 'https://marin.moe/', + 'Referer': 'https://marin.moe/anime/$id', + 'Cookie': + '__ddg1=;__ddg2_=; XSRF-TOKEN=${headers[0]}; marin_session=${headers[1]}', + 'User-Agent': + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36', + 'x-inertia': true, + 'x-inertia-version': '884345c4d568d16e3bb2fb3ae350cca9', + 'x-requested-with': 'XMLHttpRequest', + 'x-xsrf-token': headers[0].replaceAll('%3D', '='), + }, + ), + )) + .data; // so lonely down here. I'll await for the day when I can finally meet my beloved brackets again; + return info; +} + +Future> getToken() async { + final String headers = (await Dio().get( + 'https://marin.moe/anime', + options: Options( + headers: { + 'Referer': 'https://marin.moe/anime', + 'Cookie': '__ddg1_=;__ddg2_=;', + }, + ), + )) + .headers + .toString(); + + return [ + headers.split('XSRF-TOKEN=')[1].split(';')[0], + headers.split('marin_session=')[1].split(';')[0] + ]; +} diff --git a/lib/media/providers/providers.dart b/lib/media/providers/providers.dart index 477b6e8..423d546 100644 --- a/lib/media/providers/providers.dart +++ b/lib/media/providers/providers.dart @@ -1,10 +1,12 @@ import 'package:tokuwari/media/providers/anime/animepahe.dart'; +import 'package:tokuwari/media/providers/anime/marin.dart'; import 'package:tokuwari/models/info_models.dart'; import 'anime/hanime.dart'; import 'anime/aniwatch.dart'; import 'manga/mangadex.dart'; +/// Must return of type [Provider] final Map>> providers = { 'anime': [ { @@ -15,6 +17,10 @@ final Map>> providers = { 'name': 'AnimePahe', 'data': (final AniData data) => paheList(data), }, + { + 'name': 'Marin', + 'data': (final AniData data) => marinList(data), + }, { 'name': 'Hanime', 'data': (final AniData data) => haniList(data), diff --git a/lib/models/info_models.dart b/lib/models/info_models.dart index 3c6981b..06e8964 100644 --- a/lib/models/info_models.dart +++ b/lib/models/info_models.dart @@ -2,6 +2,12 @@ import 'package:isar/isar.dart'; part 'info_models.g.dart'; +const String malsync = 'https://api.malsync.moe/mal/anime'; + +typedef Provider = Future>; +typedef Call = Future Function(); +typedef Anime = Future; + @collection class AniData { final Id id = Isar.autoIncrement; @@ -72,8 +78,15 @@ class MediaProv { final String title; final String number; @ignore - final Function()? call; + + ///Call must be a function for the simple reason if it's not it will run when + ///the widget is built + final Call? call; + + /// Whether the media is completed bool consumed; + + ///The last known position of the media after consumption has started String? position; MediaProv({ @@ -87,8 +100,22 @@ class MediaProv { } class Source { + /// The map should be in the format of + /// ```dart + /// {quality_name: link_to_file} + /// ``` + /// If there exists only one link and that link is hls and has multiple + /// qualities please name the key as hls so the media player knows, otherwise + /// name it default. final Map qualities; - final List> subtitles; + + ///Subtitles is a map consisting of the language and the url + ///```dart + /// [{lang: url}] + ///``` + final Map subtitles; + + /// Any headers you might need for the request final Map? headers; const Source({ diff --git a/lib/pages/info_page.dart b/lib/pages/info_page.dart index 279ee3e..91a7ce7 100644 --- a/lib/pages/info_page.dart +++ b/lib/pages/info_page.dart @@ -1,10 +1,11 @@ +import 'package:sliver_tools/sliver_tools.dart'; + import '../media/providers/providers.dart'; import '/models/info_models.dart'; import 'package:expandable_text/expandable_text.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:isar/isar.dart'; -import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import '../widgets/image.dart'; extension on Widget { @@ -13,154 +14,10 @@ extension on Widget { } } -class InfoPage extends StatefulWidget { +class InfoPage extends StatelessWidget { final AniData data; - final Isar isar = Isar.getInstance('later')!; - - InfoPage({required this.data, super.key}); - - @override - State createState() => InfoPageState(); -} - -class InfoPageState extends State { - final List content = []; - late Function init = providers[widget.data.type]![0]['data']; - - @override - void initState() { - Future.microtask( - () async => loadEpisodes(), - ); - super.initState(); - } - Future loadEpisodes() async { - content - ..clear() - ..addAll( - await init(widget.data), - ); - if (mounted) { - setState(() {}); - } - } - - @override - Widget build(context) { - return Scaffold( - body: SafeArea( - child: CustomScrollView( - slivers: [ - SliverAppBar( - floating: true, - title: Text(widget.data.title), - ), - SliverPadding( - padding: const EdgeInsets.only( - left: 20, - right: 20, - top: 10, - bottom: 10, - ), - sliver: InfoArea( - data: widget.data, - button: ActionChip( - shape: const StadiumBorder(), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - visualDensity: VisualDensity.compact, - onPressed: () => setState( - () { - final media = - widget.isar.aniDatas.filter().mediaIdMatches( - widget.data.mediaId, - ); - if (media.isEmptySync()) { - widget.isar.writeTxnSync( - () => widget.isar.aniDatas.putSync(widget.data), - ); - } else { - widget.isar.writeTxnSync( - () => media.deleteAllSync(), - ); - } - }, - ), - avatar: (widget.isar.aniDatas - .filter() - .mediaIdMatches(widget.data.mediaId) - .isEmptySync()) - ? Icon(MdiIcons.bookmarkOutline) - : Icon(MdiIcons.bookmark), - label: const Text("Later"), - ), - selector: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - DropdownButton( - value: init, - padding: const EdgeInsets.only(left: 15), - underline: const SizedBox.shrink(), - focusColor: const Color.fromARGB(0, 0, 0, 0), - borderRadius: BorderRadius.circular(30), - icon: IconButton( - onPressed: () {}, - icon: const Icon(Icons.refresh_rounded), - ), - items: List.generate( - providers[widget.data.type]!.length, - (index) => DropdownMenuItem( - value: providers[widget.data.type]![index]['data'], - child: Text( - providers[widget.data.type]![index]['name'], - ), - ), - growable: false, - ), - onChanged: (value) => Future.microtask( - () async { - init = (value as Function); - await loadEpisodes(); - }, - ), - ), - if (widget.data.type == 'anime') - Flexible( - child: SwitchListTile( - title: const Text('Dub?'), - value: widget.data.dub, - shape: const StadiumBorder(), - onChanged: (value) => setState( - () => widget.data.dub = !widget.data.dub), - materialTapTargetSize: - MaterialTapTargetSize.shrinkWrap, - ), - ), - ], - ), - ), - ), - if (content.isNotEmpty) - EpisodeList(content: content, type: widget.data.type) - ], - ), - ), - ); - } -} - -class InfoArea extends StatelessWidget { - final AniData data; - final Widget button; - final Widget selector; - - const InfoArea({ - super.key, - required this.data, - required this.button, - required this.selector, - }); + const InfoPage({super.key, required this.data}); @override Widget build(context) { @@ -168,7 +25,9 @@ class InfoArea extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - button.padBottom(), + LaterButton( + data: data, + ).padBottom(), ExpandableText( data.description, expandText: "More", @@ -204,140 +63,277 @@ class InfoArea extends StatelessWidget { ); final double ratio = MediaQuery.of(context).size.width / MediaQuery.of(context).size.height; - return SliverList.list( - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - flex: 7, - fit: FlexFit.tight, - child: AniImage( - image: data.image, - ).padBottom(), - ), - const SizedBox( - width: 20, + return Scaffold( + body: SafeArea( + child: CustomScrollView( + slivers: [ + SliverAppBar( + floating: true, + title: Text(data.title), ), - Expanded( - flex: 15, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + SliverPadding( + padding: const EdgeInsets.only( + left: 20, + right: 20, + top: 10, + bottom: 10, + ), + sliver: SliverList.list( children: [ - Text( - data.title, - style: const TextStyle( - fontSize: 20, - ), - ), - Text( - data.status, - style: const TextStyle( - fontSize: 12, - ), - ), - Text( - 'Score: ${data.score}', - style: const TextStyle( - fontSize: 12, - ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + flex: 7, + fit: FlexFit.tight, + child: AniImage( + image: data.image, + ).padBottom(), + ), + const SizedBox( + width: 20, + ), + Expanded( + flex: 15, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + data.title, + style: const TextStyle( + fontSize: 20, + ), + ), + Text( + data.status, + style: const TextStyle( + fontSize: 12, + ), + ), + Text( + 'Score: ${data.score}', + style: const TextStyle( + fontSize: 12, + ), + ), + Text( + 'Count: ${data.count}', + style: const TextStyle( + fontSize: 12, + ), + ).padBottom(), + if (ratio > 1.2) expands, + ], + ), + ), + ], ), - Text( - 'Count: ${data.count}', - style: const TextStyle( - fontSize: 12, - ), - ).padBottom(), - if (ratio > 1.2) expands, + if (ratio < 1.2) expands, + // ⠄⠄⡔⠙⠢⡀⠄⠄⠄⢀⠼⠅⠈⢂⠄⠄⠄⠄ + // ⠄⠄⡌⠄⢰⠉⢙⢗⣲⡖⡋⢐⡺⡄⠈⢆⠄⠄⠄ + // ⠄⡜⠄⢀⠆⢠⣿⣿⣿⣿⢡⢣⢿⡱⡀⠈⠆⠄⠄ + // ⠄⠧⠤⠂⠄⣼⢧⢻⣿⣿⣞⢸⣮⠳⣕⢤⡆⠄⠄ + // ⢺⣿⣿⣶⣦⡇⡌⣰⣍⠚⢿⠄⢩⣧⠉⢷⡇⠄⠄ + // ⠘⣿⣿⣯⡙⣧⢎⢨⣶⣶⣶⣶⢸⣼⡻⡎⡇⠄⠄ + // ⠄⠘⣿⣿⣷⡀⠎⡮⡙⠶⠟⣫⣶⠛⠧⠁⠄⠄⠄ + // ⠄⠄⠘⣿⣿⣿⣦⣤⡀⢿⣿⣿⣿⣄⠄⠄⠄⠄⠄ + // ⠄⠄⠄⠈⢿⣿⣿⣿⣿⣷⣯⣿⣿⣷⣾⣿⣷⡄⠄ + // ⠄⠄⠄⠄⠄⢻⠏⣼⣿⣿⣿⣿⡿⣿⣿⣏⢾⠇⠄ + // ⠄⠄⠄⠄⠄⠈⡼⠿⠿⢿⣿⣦⡝⣿⣿⣿⠷⢀⠄ + // ⠄⠄⠄⠄⠄⠄⡇⠄⠄⠄⠈⠻⠇⠿⠋⠄⠄⢘⡆ + // ⠄⠄⠄⠄⠄⠄⠱⣀⠄⠄⠄⣀⢼⡀⠄⢀⣀⡜⠄ + // ⠄⠄⠄⠄⠄⠄⠄⢸⣉⠉⠉⠄⢀⠈⠉⢏⠁⠄⠄ + // ⠄⠄⠄⠄⠄⠄⡰⠃⠄⠄⠄⠄⢸⠄⠄⢸⣧⠄⠄ + // ⠄⠄⠄⠄⠄⣼⣧⠄⠄⠄⠄⠄⣼⠄⠄⡘⣿⡆⠄ + // ⠄⠄⠄⢀⣼⣿⡙⣷⡄⠄⠄⠄⠃⠄⢠⣿⢸⣿⡀ + // ⠄⠄⢀⣾⣿⣿⣷⣝⠿⡀⠄⠄⠄⢀⡞⢍⣼⣿⠇ + // ⠄⠄⣼⣿⣿⣿⣿⣿⣷⣄⠄⠄⠠⡊⠴⠋⠹⡜⠄ + // ⠄⠄⣿⣿⣿⣿⣿⣿⣿⣿⡆⣤⣾⣿⣿⣧⠹⠄⠄ + // ⠄⠄⢿⣿⣿⣿⣿⣿⣿⣿⢃⣿⣿⣿⣿⣿⡇⠄⠄ + // ⠄⠄⠐⡏⠉⠉⠉⠉⠉⠄⢸⠛⠿⣿⣿⡟⠄⠄⠄ + // ⠄⠄⠄⠹⡖⠒⠒⠒⠒⠊⢹⠒⠤⢤⡜⠁⠄⠄⠄ + // ⠄⠄⠄⠄⠱⠄⠄⠄⠄⠄⢸ ], ), ), + EpisodeList( + data: data, + ), ], ), - if (ratio < 1.2) expands, - // ⠄⠄⡔⠙⠢⡀⠄⠄⠄⢀⠼⠅⠈⢂⠄⠄⠄⠄ - // ⠄⠄⡌⠄⢰⠉⢙⢗⣲⡖⡋⢐⡺⡄⠈⢆⠄⠄⠄ - // ⠄⡜⠄⢀⠆⢠⣿⣿⣿⣿⢡⢣⢿⡱⡀⠈⠆⠄⠄ - // ⠄⠧⠤⠂⠄⣼⢧⢻⣿⣿⣞⢸⣮⠳⣕⢤⡆⠄⠄ - // ⢺⣿⣿⣶⣦⡇⡌⣰⣍⠚⢿⠄⢩⣧⠉⢷⡇⠄⠄ - // ⠘⣿⣿⣯⡙⣧⢎⢨⣶⣶⣶⣶⢸⣼⡻⡎⡇⠄⠄ - // ⠄⠘⣿⣿⣷⡀⠎⡮⡙⠶⠟⣫⣶⠛⠧⠁⠄⠄⠄ - // ⠄⠄⠘⣿⣿⣿⣦⣤⡀⢿⣿⣿⣿⣄⠄⠄⠄⠄⠄ - // ⠄⠄⠄⠈⢿⣿⣿⣿⣿⣷⣯⣿⣿⣷⣾⣿⣷⡄⠄ - // ⠄⠄⠄⠄⠄⢻⠏⣼⣿⣿⣿⣿⡿⣿⣿⣏⢾⠇⠄ - // ⠄⠄⠄⠄⠄⠈⡼⠿⠿⢿⣿⣦⡝⣿⣿⣿⠷⢀⠄ - // ⠄⠄⠄⠄⠄⠄⡇⠄⠄⠄⠈⠻⠇⠿⠋⠄⠄⢘⡆ - // ⠄⠄⠄⠄⠄⠄⠱⣀⠄⠄⠄⣀⢼⡀⠄⢀⣀⡜⠄ - // ⠄⠄⠄⠄⠄⠄⠄⢸⣉⠉⠉⠄⢀⠈⠉⢏⠁⠄⠄ - // ⠄⠄⠄⠄⠄⠄⡰⠃⠄⠄⠄⠄⢸⠄⠄⢸⣧⠄⠄ - // ⠄⠄⠄⠄⠄⣼⣧⠄⠄⠄⠄⠄⣼⠄⠄⡘⣿⡆⠄ - // ⠄⠄⠄⢀⣼⣿⡙⣷⡄⠄⠄⠄⠃⠄⢠⣿⢸⣿⡀ - // ⠄⠄⢀⣾⣿⣿⣷⣝⠿⡀⠄⠄⠄⢀⡞⢍⣼⣿⠇ - // ⠄⠄⣼⣿⣿⣿⣿⣿⣷⣄⠄⠄⠠⡊⠴⠋⠹⡜⠄ - // ⠄⠄⣿⣿⣿⣿⣿⣿⣿⣿⡆⣤⣾⣿⣿⣧⠹⠄⠄ - // ⠄⠄⢿⣿⣿⣿⣿⣿⣿⣿⢃⣿⣿⣿⣿⣿⡇⠄⠄ - // ⠄⠄⠐⡏⠉⠉⠉⠉⠉⠄⢸⠛⠿⣿⣿⡟⠄⠄⠄ - // ⠄⠄⠄⠹⡖⠒⠒⠒⠒⠊⢹⠒⠤⢤⡜⠁⠄⠄⠄ - // ⠄⠄⠄⠄⠱⠄⠄⠄⠄⠄⢸ - selector, - ], + ), ); } } -class EpisodeList extends StatelessWidget { - final String type; - final List content; +class LaterButton extends StatefulWidget { + final Isar isar = Isar.getInstance('later')!; + final AniData data; + + LaterButton({super.key, required this.data}); + + @override + State createState() => LaterButtonState(); +} + +class LaterButtonState extends State { + @override + Widget build(context) => ActionChip( + shape: const StadiumBorder(), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + visualDensity: VisualDensity.compact, + onPressed: () => setState( + () { + final media = widget.isar.aniDatas.filter().mediaIdMatches( + widget.data.mediaId, + ); + if (media.isEmptySync()) { + widget.isar.writeTxnSync( + () => widget.isar.aniDatas.putSync(widget.data), + ); + } else { + widget.isar.writeTxnSync( + () => media.deleteAllSync(), + ); + } + }, + ), + avatar: (widget.isar.aniDatas + .filter() + .mediaIdMatches(widget.data.mediaId) + .isEmptySync()) + ? const Icon(Icons.bookmark_add_outlined) + : const Icon(Icons.bookmark_added_rounded), + label: const Text("Later"), + ); +} + +class EpisodeList extends StatefulWidget { + final AniData data; + + const EpisodeList({ + super.key, + required this.data, + }); + + @override + State createState() => EpisodeListState(); +} + +class EpisodeListState extends State { + final List content = []; + late Function init = providers[widget.data.type]![0]['data']; + + @override + void initState() { + Future.microtask( + () async => await loadEpisodes(), + ); + super.initState(); + } - const EpisodeList({super.key, required this.content, required this.type}); + Future loadEpisodes() async { + content + ..clear() + ..addAll( + await init(widget.data), + ); + if (mounted) { + setState(() {}); + } + } @override Widget build(context) { return SliverPadding( padding: const EdgeInsets.only(left: 15, right: 15), - sliver: SliverGrid( - gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - mainAxisSpacing: 5, - crossAxisSpacing: 6, - maxCrossAxisExtent: 400, - mainAxisExtent: 100, - ), - delegate: SliverChildBuilderDelegate( - childCount: content.length, - (context, index) { - return GestureDetector( - onTap: () => context.push( - '/$type/info/viewer', - extra: { - 'content': index, - 'contents': content, - }, - ), - child: Card( - elevation: 3, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - child: Text( - content[index].title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - Flexible( - child: Text( - '${(type == 'anime') ? 'Episode:' : 'Chapter:'} ${content[index].number}', - ), + sliver: MultiSliver( + children: [ + SliverList.list( + children: [ + DropdownButton( + value: init, + padding: const EdgeInsets.only(left: 15), + underline: const SizedBox.shrink(), + focusColor: const Color.fromARGB(0, 0, 0, 0), + borderRadius: BorderRadius.circular(30), + icon: IconButton( + onPressed: () {}, + icon: const Icon(Icons.refresh_rounded), + ), + items: List.generate( + providers[widget.data.type]!.length, + (index) => DropdownMenuItem( + value: providers[widget.data.type]![index]['data'], + child: Text( + providers[widget.data.type]![index]['name'], ), - ], + ), + growable: false, + ), + onChanged: (value) => Future.microtask( + () async { + init = (value as Function); + await loadEpisodes(); + }, ), ), - ); - }, - ), + if (widget.data.type == 'anime') + SwitchListTile( + title: const Text('Dub?'), + value: widget.data.dub, + shape: const StadiumBorder(), + onChanged: (value) => + setState(() => widget.data.dub = !widget.data.dub), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + ], + ), + SliverGrid( + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + mainAxisSpacing: 5, + crossAxisSpacing: 6, + maxCrossAxisExtent: 400, + mainAxisExtent: 100, + ), + delegate: SliverChildBuilderDelegate( + childCount: content.length, + (context, index) { + return GestureDetector( + onTap: () => context.push( + '/${widget.data.type}/info/viewer', + extra: { + 'content': index, + 'contents': content, + }, + ), + child: Card( + elevation: 3, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + content[index].title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + Flexible( + child: Text( + '${(widget.data.type == 'anime') ? 'Episode:' : 'Chapter:'} ${content[index].number}', + ), + ), + ], + ), + ), + ); + }, + ), + ), + ], ), ); } diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index f44dbfb..70e2273 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -12,6 +12,6 @@ class Settings extends StatefulWidget { class SettingsState extends State { @override Widget build(context) { - return Scaffold(); + return const Scaffold(); } } diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index da3f8bc..71611cb 100755 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -58,6 +58,7 @@ set_target_properties(${BINARY_NAME} # them to the application. include(flutter/generated_plugins.cmake) +target_link_libraries(${BINARY_NAME} PRIVATE ${MIMALLOC_LIB}) # === Installation === # By default, "installing" just makes a relocatable bundle in the build diff --git a/pubspec.lock b/pubspec.lock index 3627007..b085a71 100755 --- a/pubspec.lock +++ b/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: asn1lib - sha256: b74e3842a52c61f8819a1ec8444b4de5419b41a7465e69d4aa681445377398b0 + sha256: "21afe4333076c02877d14f4a89df111e658a6d466cbfc802eb705eb91bd5adfd" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.5.0" async: dependency: transitive description: @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: transitive description: @@ -237,10 +237,10 @@ packages: dependency: "direct main" description: name: dio - sha256: "3866d67f93523161b643187af65f5ac08bc991a5bcdaf41a2d587fe4ccb49993" + sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197 url: "https://pub.dev" source: hosted - version: "5.3.0" + version: "5.3.2" encrypt: dependency: "direct main" description: @@ -261,10 +261,10 @@ packages: dependency: transitive description: name: ffi - sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.1.0" file: dependency: transitive description: @@ -277,10 +277,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: b1729fc96627dd44012d0a901558177418818d6bd428df59dcfeb594e5f66432 + sha256: "21145c9c268d54b1f771d8380c195d2d6f655e0567dc1ca2f9c134c02c819e0a" url: "https://pub.dev" source: hosted - version: "5.3.2" + version: "5.3.3" fixnum: dependency: transitive description: @@ -367,26 +367,26 @@ packages: dependency: "direct main" description: name: go_router - sha256: "5927202c23bec18ba93f662b5e1f81f2caa2e0cfa472d857d6229f63d59f1730" + sha256: b3cadd2cd59a4103fd5f6bc572ca75111264698784e927aa471921c3477d5475 url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "10.0.0" gql: dependency: transitive description: name: gql - sha256: "07ae289bc5ccfbfd143883bfe9a380f5bf52c94559ded740cf3fb152fcbdbe2e" + sha256: c2d4248adf2cc568976d9bb42803531232a981eff205b9931b73389e0665ac63 url: "https://pub.dev" source: hosted - version: "1.0.1-alpha+1686240655988" + version: "1.0.1-alpha+1690479830964" gql_dedupe_link: dependency: transitive description: name: gql_dedupe_link - sha256: dac8e5eec5e9f6274302e5c01f77c7558f89d727f2c0dff7cda568c440a171cd + sha256: "307a33a0723edd5d9de652e38f6ccb01fbf04b90464e32efca7a3fa9027367d3" url: "https://pub.dev" source: hosted - version: "2.0.4-alpha+1686240656097" + version: "2.0.4-alpha+1690479831065" gql_error_link: dependency: transitive description: @@ -399,10 +399,10 @@ packages: dependency: transitive description: name: gql_exec - sha256: "04fb14e41fdc3fafa80fa7218224bceba019e00b80de9130c2dd0a9a77c6392d" + sha256: "257b6eeb206343349b188a4bfe874ba5826ec7992461a97890ebf4802eaa9a86" url: "https://pub.dev" source: hosted - version: "1.0.1-alpha+1686240655996" + version: "1.0.1-alpha+1690479830973" gql_http_link: dependency: transitive description: @@ -415,10 +415,10 @@ packages: dependency: transitive description: name: gql_link - sha256: ecf2419e6c543d1b8dedd7dbbc538ce037f4d53a3b5b7d694a98a7d636da2817 + sha256: "6e429187a7c199c9a7bb7fc26c8ed0673dae9be3a00c8be6ecf15ba9b713b3cb" url: "https://pub.dev" source: hosted - version: "1.0.1-alpha+1686240656001" + version: "1.0.1-alpha+1690479830981" gql_transform_link: dependency: transitive description: @@ -579,77 +579,69 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" - material_design_icons_flutter: - dependency: "direct main" - description: - name: material_design_icons_flutter - sha256: "6f986b7a51f3ad4c00e33c5c84e8de1bdd140489bbcdc8b66fc1283dad4dea5a" - url: "https://pub.dev" - source: hosted - version: "7.0.7296" media_kit: dependency: "direct overridden" description: path: media_kit ref: main - resolved-ref: c5459ec11bcc5e56bc0e3ed5c4999d695bacc8f7 + resolved-ref: e117b5f3aec324980519bbb18c9c51b29be9a7ca url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "1.0.2" + version: "1.1.3+1" media_kit_libs_android_video: dependency: "direct main" description: path: "libs/android/media_kit_libs_android_video" ref: main - resolved-ref: c5459ec11bcc5e56bc0e3ed5c4999d695bacc8f7 + resolved-ref: e117b5f3aec324980519bbb18c9c51b29be9a7ca url: "https://github.com/alexmercerind/media_kit" source: git - version: "1.2.0" + version: "1.3.1" media_kit_libs_ios_video: dependency: "direct main" description: path: "libs/ios/media_kit_libs_ios_video" ref: main - resolved-ref: c5459ec11bcc5e56bc0e3ed5c4999d695bacc8f7 + resolved-ref: e117b5f3aec324980519bbb18c9c51b29be9a7ca url: "https://github.com/alexmercerind/media_kit" source: git - version: "1.0.4" + version: "1.1.2" media_kit_libs_linux: dependency: "direct main" description: path: "libs/linux/media_kit_libs_linux" ref: main - resolved-ref: c5459ec11bcc5e56bc0e3ed5c4999d695bacc8f7 + resolved-ref: e117b5f3aec324980519bbb18c9c51b29be9a7ca url: "https://github.com/alexmercerind/media_kit" source: git - version: "1.0.2" + version: "1.1.0" media_kit_libs_macos_video: dependency: "direct main" description: path: "libs/macos/media_kit_libs_macos_video" ref: main - resolved-ref: c5459ec11bcc5e56bc0e3ed5c4999d695bacc8f7 + resolved-ref: e117b5f3aec324980519bbb18c9c51b29be9a7ca url: "https://github.com/alexmercerind/media_kit" source: git - version: "1.0.5" + version: "1.1.2" media_kit_libs_windows_video: dependency: "direct main" description: path: "libs/windows/media_kit_libs_windows_video" ref: main - resolved-ref: c5459ec11bcc5e56bc0e3ed5c4999d695bacc8f7 + resolved-ref: e117b5f3aec324980519bbb18c9c51b29be9a7ca url: "https://github.com/alexmercerind/media_kit" source: git - version: "1.0.4" + version: "1.0.6" media_kit_video: dependency: "direct main" description: path: media_kit_video ref: main - resolved-ref: c5459ec11bcc5e56bc0e3ed5c4999d695bacc8f7 + resolved-ref: e117b5f3aec324980519bbb18c9c51b29be9a7ca url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "1.0.2" + version: "1.1.3" meta: dependency: transitive description: @@ -694,10 +686,10 @@ packages: dependency: transitive description: name: package_info_plus - sha256: ceb027f6bc6a60674a233b4a90a7658af1aebdea833da0b5b53c1e9821a78c7b + sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.0" package_info_plus_platform_interface: dependency: transitive description: @@ -718,50 +710,50 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" + sha256: "909b84830485dbcd0308edf6f7368bc8fd76afa26a270420f34cabea2a6467a0" url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.0" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + sha256: "5d44fc3314d969b84816b569070d7ace0f1dea04bd94a83f74c4829615d22ad8" url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.1.0" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" + sha256: "1b744d3d774e5a879bb76d6cd1ecee2ba2c6960c03b1020cd35212f6aa267ac5" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.3.0" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 + sha256: ba2b77f0c52a33db09fc8caf85b12df691bf28d983e84cf87ff6d693cfa007b3 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.0" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: bced5679c7df11190e1ddc35f3222c858f328fff85c3942e46e7f5589bf9eb84 url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.0" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" + sha256: ee0e0d164516b90ae1f970bdf29f726f1aa730d7cfc449ecc74c495378b705da url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.0" permission_handler: dependency: "direct main" description: @@ -774,10 +766,10 @@ packages: dependency: transitive description: name: permission_handler_android - sha256: c0c9754479a4c4b1c1f3862ddc11930c9b3f03bef2816bb4ea6eed1e13551d6f + sha256: "2ffaf52a21f64ac9b35fe7369bb9533edbd4f698e5604db8645b1064ff4cf221" url: "https://pub.dev" source: hosted - version: "10.3.2" + version: "10.3.3" permission_handler_apple: dependency: transitive description: @@ -830,10 +822,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" pointycastle: dependency: transitive description: @@ -959,6 +951,14 @@ packages: description: flutter source: sdk version: "0.0.99" + sliver_tools: + dependency: "direct main" + description: + name: sliver_tools + sha256: eae28220badfb9d0559207badcbbc9ad5331aac829a88cb0964d330d2a4636a6 + url: "https://pub.dev" + source: hosted + version: "0.2.12" source_gen: dependency: transitive description: @@ -979,18 +979,18 @@ packages: dependency: transitive description: name: sqflite - sha256: b4d6710e1200e96845747e37338ea8a819a12b51689a3bcf31eff0003b37a0b9 + sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" url: "https://pub.dev" source: hosted - version: "2.2.8+4" + version: "2.3.0" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "8f7603f3f8f126740bc55c4ca2d1027aab4b74a1267a3e31ce51fe40e3b65b8f" + sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" url: "https://pub.dev" source: hosted - version: "2.4.5+1" + version: "2.5.0" stack_trace: dependency: transitive description: @@ -1120,7 +1120,7 @@ packages: source: hosted version: "2.0.7" wakelock_plus: - dependency: "direct main" + dependency: transitive description: name: wakelock_plus sha256: aac3f3258f01781ec9212df94eecef1eb9ba9350e106728def405baa096ba413 @@ -1163,10 +1163,10 @@ packages: dependency: transitive description: name: win32 - sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee + sha256: f2add6fa510d3ae152903412227bda57d0d5a8da61d2c39c1fb022c9429a41c0 url: "https://pub.dev" source: hosted - version: "5.0.5" + version: "5.0.6" window_manager: dependency: "direct main" description: @@ -1179,10 +1179,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff + sha256: f0c26453a2d47aa4c2570c6a033246a3fc62da2fe23c7ffdd0a7495086dc0247 url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.2" xml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b4c4335..cbe4e7c 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: Something I guess # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 0.3.0+2 +version: 0.4.0 environment: sdk: ">=3.0.0" @@ -13,13 +13,11 @@ environment: dependencies: flutter: sdk: flutter + sliver_tools: flutter_html: - archive: - image: path: cached_network_image: dio: - file_picker: photo_view: media_kit_video: git: @@ -52,19 +50,21 @@ dependencies: ref: main path: libs/android/media_kit_libs_android_video path_provider: - permission_handler: - xml2json: window_manager: flutter_image: encrypt: isar: ^3.1.0+1 isar_flutter_libs: ^3.1.0+1 - wakelock_plus: expandable_text: - material_design_icons_flutter: string_similarity: go_router: + # For novels + archive: + image: html: + file_picker: + xml2json: + permission_handler: dependency_overrides: graphql: ^5.2.0-beta.5