diff --git a/lib/router/router.dart b/lib/router/router.dart index 410e4baf..6b1931e1 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -10,7 +10,7 @@ import 'package:mangayomi/views/general/general_screen.dart'; import 'package:mangayomi/views/history/history_screen.dart'; import 'package:mangayomi/views/library/library_screen.dart'; import 'package:mangayomi/views/manga/detail/manga_reader_detail.dart'; -import 'package:mangayomi/views/manga/home/home.dart'; +import 'package:mangayomi/views/manga/home/mang_home_screen.dart'; import 'package:mangayomi/views/manga/reader/manga_reader_view.dart'; import 'package:mangayomi/views/more/more_screen.dart'; import 'package:mangayomi/views/more/settings/appearance/appearance_screen.dart'; diff --git a/lib/services/get_manga_chapter_url.dart b/lib/services/get_manga_chapter_url.dart index a620525a..4a8d0c3a 100644 --- a/lib/services/get_manga_chapter_url.dart +++ b/lib/services/get_manga_chapter_url.dart @@ -6,6 +6,9 @@ import 'package:http/http.dart' as http; import 'package:html/dom.dart' as dom; import 'package:mangayomi/models/model_manga.dart'; import 'package:mangayomi/providers/hive_provider.dart'; +import 'package:mangayomi/services/get_popular_manga.dart'; +import 'package:mangayomi/services/http_res_to_dom_html.dart'; +import 'package:mangayomi/source/source_model.dart'; import 'package:path_provider/path_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:flutter_js/flutter_js.dart'; @@ -37,6 +40,52 @@ Future getMangaChapterUrl( "${pathh!.path}/${modelManga.source}/${modelManga.name}/${modelManga.chapterTitle![index]}/"); if (hiveUrl.isNotEmpty) { urll = hiveUrl; + } else if (getWpMangTypeSource(source) == TypeSource.mangathemesia) { + final htmll = + await httpResToDom(url: modelManga.chapterUrl![index], headers: {}); + + if (htmll.querySelectorAll('#readerarea').isNotEmpty) { + final ta = htmll + .querySelectorAll('#readerarea') + .map((e) => e.outerHtml) + .toList(); + final RegExp regex = RegExp(r']+src="([^"]+)"'); + final Iterable matches = regex.allMatches(ta.first); + + final List urls = matches.map((m) => m.group(1)).toList(); + Iterable matchess = []; + if (htmll.querySelectorAll(' #select-paged ').isNotEmpty) { + final ee = htmll + .querySelectorAll(' #select-paged ') + .map((e) => e.outerHtml) + .toList(); + final RegExp regexx = RegExp(r'value="([^"]+)"'); + matchess = regexx.allMatches(ee.first); + } + + final List urlss = matchess.map((m) => m.group(1)).toList(); + if (urls.length == 1 && urls.isNotEmpty) { + for (var i = 0; i < urlss.length; i++) { + if (urlss[i]!.length == 1) { + urll.add( + urls.first!.replaceAll("001", '00${int.parse(urlss[i]!) + 1}')); + } else if (urlss[i]!.length == 2) { + urll.add( + urls.first!.replaceAll("001", '0${int.parse(urlss[i]!) + 1}')); + } else if (urlss[i]!.length == 3) { + urll.add( + urls.first!.replaceAll("001", '${int.parse(urlss[i]!) + 1}')); + } + } + } else if (urls.length > 1 && urls.isNotEmpty) { + for (var tt in urls) { + urll.add(tt); + } + } + ref.watch(hiveBoxMangaInfo).put( + "${modelManga.source}/${modelManga.name}/${modelManga.chapterTitle![index]}-pageurl", + urll); + } } /***********/ /*mangahere*/ diff --git a/lib/services/get_manga_chapter_url.g.dart b/lib/services/get_manga_chapter_url.g.dart index 71341e35..cdff3f17 100644 --- a/lib/services/get_manga_chapter_url.g.dart +++ b/lib/services/get_manga_chapter_url.g.dart @@ -7,7 +7,7 @@ part of 'get_manga_chapter_url.dart'; // ************************************************************************** String _$getMangaChapterUrlHash() => - r'38cf836814df00df1a3a269c0c2fc0c85debff78'; + r'a41949b68549e776832151d8b3a595db72fabdd9'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/services/get_manga_detail.dart b/lib/services/get_manga_detail.dart index 16118525..5dc12ae7 100644 --- a/lib/services/get_manga_detail.dart +++ b/lib/services/get_manga_detail.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'dart:developer'; +import 'package:mangayomi/services/get_popular_manga.dart'; import 'package:mangayomi/services/http_res_to_dom_html.dart'; +import 'package:mangayomi/source/source_model.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'get_manga_detail.g.dart'; @@ -46,7 +48,130 @@ Future getMangaDetail(GetMangaDetailRef ref, List chapterDate = []; source = source.toLowerCase(); String? description; - if (source == "mangahere") { + if (getWpMangTypeSource(source) == TypeSource.mangathemesia) { + final dom = await httpResToDom(url: url, headers: {}); + if (dom + .querySelectorAll( + 'div.bigcontent, div.animefull, div.main-info, div.postbody') + .isNotEmpty) { + final resHtml = dom.querySelector( + 'div.bigcontent, div.animefull, div.main-info, div.postbody'); + if (resHtml!.querySelectorAll('.tsinfo .imptdt').isNotEmpty) { + status = resHtml + .querySelectorAll('.tsinfo .imptdt') + .where((e) => + e.innerHtml.contains("Status") || + e.innerHtml.contains("Situação")) + .map((e) => e.innerHtml.contains("Situação") + ? e.text.replaceAll('Situação', '').trim() + : e.text.replaceAll('Status', '').trim()) + .toList() + .last; + } else if (resHtml.querySelectorAll('.infotable tr').isNotEmpty) { + status = resHtml + .querySelectorAll('.infotable tr') + .where((e) => + e.innerHtml.toLowerCase().contains('statut') || + e.innerHtml.toLowerCase().contains('status')) + .map((e) => e.querySelector('td:last-child')!.text) + .toList() + .first; + } else if (resHtml.querySelectorAll('.fmed').isNotEmpty) { + status = resHtml + .querySelectorAll('.tsinfo .imptdt') + .map((e) => e.text.replaceAll('Status', '').trim()) + .toList() + .first; + } else { + status = ""; + } + + //2 + if (resHtml.querySelectorAll('.fmed').isNotEmpty) { + author = resHtml + .querySelectorAll('.fmed') + .where((e) => e.innerHtml.contains("Author")) + .map((e) => e.text.replaceAll('Author', '').trim()) + .toList() + .first; + } else if (resHtml.querySelectorAll('.tsinfo .imptdt').isNotEmpty) { + author = resHtml + .querySelectorAll('.tsinfo .imptdt') + .where((e) => + e.innerHtml.contains("Author") || + e.innerHtml.contains("Auteur") || + e.innerHtml.contains("Autor")) + .map((e) => e.innerHtml.contains("Autor") + ? e.text.replaceAll('Autor', '').trim() + : e.innerHtml.contains("Autheur") + ? e.text.replaceAll('Auteur', '').trim() + : e.text.replaceAll('Author', '').trim()) + .toList() + .first; + } else if (resHtml.querySelectorAll('.infotable tr').isNotEmpty) { + author = resHtml + .querySelectorAll('.infotable tr') + .where((e) => + e.innerHtml.toLowerCase().contains('auteur') || + e.innerHtml.toLowerCase().contains('author')) + .map((e) => e.querySelector('td:last-child')!.text) + .toList() + .first; + } else { + author = ""; + } + + description = resHtml + .querySelector(".desc, .entry-content[itemprop=description]")! + .text; + if (resHtml + .querySelectorAll('div.gnr a, .mgen a, .seriestugenre a') + .isNotEmpty) { + final tt = resHtml + .querySelectorAll('div.gnr a, .mgen a, .seriestugenre a') + .map((e) => e.text.trim()) + .toList(); + + for (var ok in tt) { + genre.add(ok); + } + } else { + genre.add(''); + } + if (resHtml.querySelectorAll('#chapterlist a').isNotEmpty) { + final udl = resHtml + .querySelectorAll('#chapterlist a ') + .where((e) => e.attributes.containsKey('href')) + .map((e) => e.attributes['href']) + .toList(); + + for (var ok in udl) { + chapterUrl.add(ok!); + } + } + if (resHtml.querySelectorAll('.lch a, .chapternum').isNotEmpty) { + final tt = resHtml + .querySelectorAll('.lch a, .chapternum') + .map((e) => e.text.trim()) + .toList(); + + tt.removeWhere((element) => element.contains('{{number}}')); + for (var ok in tt) { + chapterTitle.add(ok.trimLeft()); + } + } + if (resHtml.querySelectorAll('.chapterdate').isNotEmpty) { + final tt = resHtml + .querySelectorAll('.chapterdate') + .map((e) => e.text.trim()) + .toList(); + tt.removeWhere((element) => element.contains('{{date}}')); + for (var ok in tt) { + chapterDate.add(ok); + } + } + } + } else if (source == "mangahere") { final dom = await httpResToDom( url: "http://www.mangahere.cc$url", headers: { diff --git a/lib/services/get_manga_detail.g.dart b/lib/services/get_manga_detail.g.dart index 85930b50..b090c9e8 100644 --- a/lib/services/get_manga_detail.g.dart +++ b/lib/services/get_manga_detail.g.dart @@ -6,7 +6,7 @@ part of 'get_manga_detail.dart'; // RiverpodGenerator // ************************************************************************** -String _$getMangaDetailHash() => r'bbaec699742fa0b06f698d9c3be501564d9a9352'; +String _$getMangaDetailHash() => r'4b0dd5315b3a1270722494045a7f9e3a3f455484'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/services/get_popular_manga.dart b/lib/services/get_popular_manga.dart index 1d12ea75..8d09d757 100644 --- a/lib/services/get_popular_manga.dart +++ b/lib/services/get_popular_manga.dart @@ -1,5 +1,7 @@ import 'dart:async'; import 'package:mangayomi/services/http_res_to_dom_html.dart'; +import 'package:mangayomi/source/source_list.dart'; +import 'package:mangayomi/source/source_model.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'get_popular_manga.g.dart'; @@ -14,6 +16,26 @@ class GetMangaModel { }); } +String getWpMangaUrl(String source) { + String url = ""; + for (var i = 0; i < sourcesList.length; i++) { + if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) { + url = sourcesList[i].url; + } + } + return url; +} + +TypeSource getWpMangTypeSource(String source) { + TypeSource? typeSource; + for (var i = 0; i < sourcesList.length; i++) { + if (sourcesList[i].sourceName.toLowerCase() == source.toLowerCase()) { + typeSource = sourcesList[i].typeSource; + } + } + return typeSource!; +} + @riverpod Future getPopularManga(GetPopularMangaRef ref, {required String source, required int page}) async { @@ -21,6 +43,46 @@ Future getPopularManga(GetPopularMangaRef ref, List name = []; List image = []; source = source.toLowerCase(); + if (getWpMangTypeSource(source) == TypeSource.mangathemesia) { + final dom = await httpResToDom( + url: '${getWpMangaUrl(source)}/manga/?title=&page=$page&order=popular', + headers: {}); + + if (dom + .querySelectorAll( + '.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx') + .isNotEmpty) { + url = dom + .querySelectorAll( + '.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx') + .map((e) { + RegExp exp = RegExp(r'href="([^"]+)"'); + Iterable matches = exp.allMatches(e.innerHtml); + String? firstMatch = matches.first.group(1); + return firstMatch; + }).toList(); + + image = dom + .querySelectorAll( + '.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx') + .map((e) { + RegExp exp = RegExp(r'src="([^"]+)"'); + Iterable matches = exp.allMatches(e.innerHtml); + String? firstMatch = matches.first.group(1); + return firstMatch; + }).toList(); + + name = dom + .querySelectorAll( + '.utao .uta .imgu, .listupd .bs .bsx, .listo .bs .bsx ') + .map((e) { + RegExp exp = RegExp(r'title="([^"]+)"'); + Iterable matches = exp.allMatches(e.innerHtml); + String? firstMatch = matches.first.group(1); + return firstMatch; + }).toList(); + } + } else //mangahere if (source == "mangahere") { final dom = await httpResToDom( diff --git a/lib/services/get_popular_manga.g.dart b/lib/services/get_popular_manga.g.dart index 1745720e..0985b820 100644 --- a/lib/services/get_popular_manga.g.dart +++ b/lib/services/get_popular_manga.g.dart @@ -6,7 +6,7 @@ part of 'get_popular_manga.dart'; // RiverpodGenerator // ************************************************************************** -String _$getPopularMangaHash() => r'0b6445ff81dbbe1d337f37ed46544f5834805088'; +String _$getPopularMangaHash() => r'5addb8826c01045b532a53de957c6470294833ad'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/services/search_manga.dart b/lib/services/search_manga.dart new file mode 100644 index 00000000..a54aeb21 --- /dev/null +++ b/lib/services/search_manga.dart @@ -0,0 +1,96 @@ +import 'dart:developer'; + +import 'package:http/http.dart' as http; +import 'package:html/dom.dart' as dom; +import 'package:mangayomi/services/get_popular_manga.dart'; +import 'package:mangayomi/services/http_res_to_dom_html.dart'; +import 'package:mangayomi/source/source_model.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +part 'search_manga.g.dart'; + +class SearchMangaModel { + late List url; + late List name; + late List image; + + SearchMangaModel({ + required this.name, + required this.url, + required this.image, + }); +} + +@riverpod +Future searchManga(SearchMangaRef ref, + {required String source, required String query}) async { + List url = []; + List name = []; + List image = []; + source = source.toLowerCase(); + //mangathemesia + if (getWpMangTypeSource(source) == TypeSource.mangathemesia) { + final dom = await httpResToDom( + url: '${getWpMangaUrl(source)}/?s=${query.trim()}', headers: {}); + + if (dom + .querySelectorAll( + '#content > div > div.postbody > div > div.listupd > div > div > a') + .isNotEmpty) { + url = dom + .querySelectorAll( + '#content > div > div.postbody > div > div.listupd > div > div > a ') + .where((e) => e.attributes.containsKey('href')) + .map((e) => e.attributes['href']) + .toList(); + + image = dom + .querySelectorAll( + ' #content > div > div.postbody > div > div.listupd > div > div > a > div > img') + .where((e) => e.attributes.containsKey('src')) + .map((e) => e.attributes['src']) + .toList(); + + name = dom + .querySelectorAll( + '#content > div > div.postbody > div > div.listupd > div > div > a ') + .where((e) => e.attributes.containsKey('title')) + .map((e) => e.attributes['title']) + .toList(); + } + } + //mangahere + else if (source == "mangahere") { + log("message"); + final dom = await httpResToDom( + url: + '${getWpMangaUrl(source)}/search?title=${query.trim()}&genres=&nogenres=&sort=&stype=1&name=&type=0&author_method=cw&author=&artist_method=cw&artist=&rating_method=eq&rating=&released_method=eq&released=&st=0', + headers: {}); + if (dom + .querySelectorAll( + 'body > div.container > div > div > ul > li > p.manga-list-4-item-title > a') + .isNotEmpty) { + url = dom + .querySelectorAll( + 'body > div.container > div > div > ul > li > p.manga-list-4-item-title > a') + .where((e) => e.attributes.containsKey('href')) + .map((e) => e.attributes['href']) + .toList(); + + image = dom + .querySelectorAll( + 'body > div.container > div > div > ul > li > a > img') + .where((e) => e.attributes.containsKey('src')) + .where((e) => e.attributes['src']!.contains("cover")) + .map((e) => e.attributes['src']) + .toList(); + + name = dom + .querySelectorAll( + 'body > div.container > div > div > ul > li > p.manga-list-4-item-title > a') + .where((e) => e.attributes.containsKey('title')) + .map((e) => e.attributes['title']) + .toList(); + } + } + return SearchMangaModel(name: name, url: url, image: image); +} diff --git a/lib/services/search_manga.g.dart b/lib/services/search_manga.g.dart new file mode 100644 index 00000000..b18180a8 --- /dev/null +++ b/lib/services/search_manga.g.dart @@ -0,0 +1,121 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'search_manga.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$searchMangaHash() => r'18866e3523f085404fecaa91fa3bc1a7149724e6'; + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +typedef SearchMangaRef = AutoDisposeFutureProviderRef; + +/// See also [searchManga]. +@ProviderFor(searchManga) +const searchMangaProvider = SearchMangaFamily(); + +/// See also [searchManga]. +class SearchMangaFamily extends Family> { + /// See also [searchManga]. + const SearchMangaFamily(); + + /// See also [searchManga]. + SearchMangaProvider call({ + required String source, + required String query, + }) { + return SearchMangaProvider( + source: source, + query: query, + ); + } + + @override + SearchMangaProvider getProviderOverride( + covariant SearchMangaProvider provider, + ) { + return call( + source: provider.source, + query: provider.query, + ); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'searchMangaProvider'; +} + +/// See also [searchManga]. +class SearchMangaProvider extends AutoDisposeFutureProvider { + /// See also [searchManga]. + SearchMangaProvider({ + required this.source, + required this.query, + }) : super.internal( + (ref) => searchManga( + ref, + source: source, + query: query, + ), + from: searchMangaProvider, + name: r'searchMangaProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$searchMangaHash, + dependencies: SearchMangaFamily._dependencies, + allTransitiveDependencies: + SearchMangaFamily._allTransitiveDependencies, + ); + + final String source; + final String query; + + @override + bool operator ==(Object other) { + return other is SearchMangaProvider && + other.source == source && + other.query == query; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, source.hashCode); + hash = _SystemHash.combine(hash, query.hashCode); + + return _SystemHash.finish(hash); + } +} +// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions diff --git a/lib/source/source_list.dart b/lib/source/source_list.dart index f900a175..65535c84 100644 --- a/lib/source/source_list.dart +++ b/lib/source/source_list.dart @@ -378,4 +378,53 @@ List sourcesList = [ typeSource: TypeSource.mangathemesia, logoUrl: 'https://xcalibrscans.com/wp-content/uploads/2021/06/xcalibr-dark-v3.png'), + + SourceModel( + sourceName: "Fallen Angels", + url: "https://manga.fascans.com", + lang: "en", + typeSource: TypeSource.mmrcms, + logoUrl: ''), + SourceModel( + sourceName: "Scan FR", + url: "https://www.scan-fr.org", + lang: "fr", + typeSource: TypeSource.mmrcms, + logoUrl: ''), + SourceModel( + sourceName: "Scan VF", + url: "https://www.scan-vf.net", + lang: "fr", + typeSource: TypeSource.mmrcms, + logoUrl: ''), // + SourceModel( + sourceName: "Komikid", + url: "https://www.komikid.com", + lang: "id", + typeSource: TypeSource.mmrcms, + logoUrl: ''), + SourceModel( + sourceName: "MangaHanta", + url: "http://mangahanta.com", + lang: "tr", + typeSource: TypeSource.mmrcms, + logoUrl: ''), + SourceModel( + sourceName: "MangaID", + url: "https://mangaid.click", + lang: "id", + typeSource: TypeSource.mmrcms, + logoUrl: ''), + SourceModel( + sourceName: "Jpmangas", + url: "https://jpmangas.cc", + lang: "fr", + typeSource: TypeSource.mmrcms, + logoUrl: ''), + SourceModel( + sourceName: "FR Scan", + url: "https://frscan.ws", + lang: "fr", + typeSource: TypeSource.mmrcms, + logoUrl: ''), ]; diff --git a/lib/source/source_model.dart b/lib/source/source_model.dart index a09a483a..1d22ed89 100644 --- a/lib/source/source_model.dart +++ b/lib/source/source_model.dart @@ -41,7 +41,9 @@ enum TypeSource { @HiveField(2) mangathemesia, @HiveField(3) - comick + comick, + @HiveField(4) + mmrcms } // @HiveType(typeId: 3) diff --git a/lib/source/source_model.g.dart b/lib/source/source_model.g.dart index c49aeaf1..2aa7bccb 100644 --- a/lib/source/source_model.g.dart +++ b/lib/source/source_model.g.dart @@ -77,6 +77,8 @@ class TypeSourceAdapter extends TypeAdapter { return TypeSource.mangathemesia; case 3: return TypeSource.comick; + case 4: + return TypeSource.mmrcms; default: return TypeSource.single; } @@ -94,6 +96,9 @@ class TypeSourceAdapter extends TypeAdapter { case TypeSource.comick: writer.writeByte(3); break; + case TypeSource.mmrcms: + writer.writeByte(4); + break; } } diff --git a/lib/utils/headers.dart b/lib/utils/headers.dart new file mode 100644 index 00000000..0c9bff03 --- /dev/null +++ b/lib/utils/headers.dart @@ -0,0 +1,22 @@ +Map? headers(String source) { + return source == 'japscan' + ? { + 'referer': 'https://www.japscan.lol/', + } + : source == 'mangakawaii' + ? { + 'Referer': 'https://www.mangakawaii.io/', + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/8\$userAgentRandomizer1.0.4\$userAgentRandomizer3.1\$userAgentRandomizer2 Safari/537.36', + 'Accept-Language': 'fr' + } + : source == 'mangahere' + ? {"Referer": "https://www.mangahere.cc/", "Cookie": "isAdult=1"} + : source == 'comick' + ? { + 'Referer': 'https://comick.app/', + 'User-Agent': + 'Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/8\\\$userAgentRandomizer1.0.4\\\$userAgentRandomizer3.1\\\$userAgentRandomizer2 Safari/537.36' + } + : {}; +} \ No newline at end of file diff --git a/lib/views/browse/browse_screen.dart b/lib/views/browse/browse_screen.dart index 6bd117b5..24228e05 100644 --- a/lib/views/browse/browse_screen.dart +++ b/lib/views/browse/browse_screen.dart @@ -63,6 +63,7 @@ class _BrowseScreenState extends State _isSearch = false; }); _textEditingController.clear(); + entriesFilter = entries; }, controller: _textEditingController, ) diff --git a/lib/views/library/library_screen.dart b/lib/views/library/library_screen.dart index 32d9f5ce..14f92326 100644 --- a/lib/views/library/library_screen.dart +++ b/lib/views/library/library_screen.dart @@ -55,6 +55,8 @@ class _LibraryScreenState extends ConsumerState setState(() { isSearch = false; }); + _textEditingController.clear(); + }, controller: _textEditingController, ) diff --git a/lib/views/manga/home/home.dart b/lib/views/manga/home/mang_home_screen.dart similarity index 96% rename from lib/views/manga/home/home.dart rename to lib/views/manga/home/mang_home_screen.dart index ceed2453..0b0fef6c 100644 --- a/lib/views/manga/home/home.dart +++ b/lib/views/manga/home/mang_home_screen.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mangayomi/models/manga_type.dart'; import 'package:mangayomi/services/get_manga_detail.dart'; import 'package:mangayomi/services/get_popular_manga.dart'; +import 'package:mangayomi/views/manga/home/manga_search_screen.dart'; import 'package:mangayomi/views/widgets/bottom_text_widget.dart'; import 'package:mangayomi/views/widgets/cover_view_widget.dart'; import 'package:mangayomi/views/widgets/gridview_widget.dart'; @@ -29,7 +30,10 @@ class _MangaHomeScreenState extends ConsumerState { return Scaffold( appBar: AppBar( title: Text('${widget.mangaType.source}'), - actions: [], + actions: [ + MangaSearchButton( + source: widget.mangaType.source!, lang: widget.mangaType.lang!) + ], ), body: getManga.when( data: (data) { diff --git a/lib/views/manga/home/manga_search_screen.dart b/lib/views/manga/home/manga_search_screen.dart new file mode 100644 index 00000000..4e7f8a76 --- /dev/null +++ b/lib/views/manga/home/manga_search_screen.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mangayomi/services/search_manga.dart'; +import 'package:mangayomi/views/manga/home/mang_home_screen.dart'; +import 'package:mangayomi/views/widgets/gridview_widget.dart'; + +class MangaSearchButton extends StatelessWidget { + final String source; + final String lang; + MangaSearchButton({super.key, required this.source, required this.lang}); + + late final CustomSearchDelegate _delegate = + CustomSearchDelegate(source, lang); + + @override + Widget build(BuildContext context) { + return IconButton( + icon: const Icon(Icons.search), + onPressed: () async { + await showSearch( + context: context, + delegate: _delegate, + ); + }, + ); + } +} + +class CustomSearchDelegate extends SearchDelegate { + final String source; + final String lang; + CustomSearchDelegate(this.source, this.lang); + + @override + Widget buildLeading(BuildContext context) { + return IconButton( + icon: const BackButtonIcon(), + onPressed: () { + Navigator.pop(context); + }, + ); + } + + @override + Widget buildSuggestions(BuildContext context) { + return Container(); + } + + @override + Widget buildResults(BuildContext context) { + if (query.isEmpty) { + return const Center(child: Text("Empty")); + } + + return SearchResult( + query: query, + source: source, + lang: lang, + ); + } + + @override + List? buildActions(BuildContext context) { + if (query.isNotEmpty) { + return [ + IconButton( + icon: const Icon(Icons.clear), + onPressed: () { + query = ''; + }, + ), + ]; + } + return []; + } + + @override + ThemeData appBarTheme(BuildContext context) { + final ThemeData theme = Theme.of(context); + return theme.copyWith( + appBarTheme: const AppBarTheme(), + inputDecorationTheme: const InputDecorationTheme(hintStyle: TextStyle()), + textTheme: theme.textTheme + .copyWith(titleLarge: theme.textTheme.titleLarge!.copyWith()), + ); + } +} + +class SearchResult extends ConsumerWidget { + final String query; + final String source; + final String lang; + const SearchResult({ + super.key, + required this.query, + required this.source, + required this.lang, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final search = ref.watch(searchMangaProvider(source: source, query: query)); + return search.when( + loading: () => const Center( + child: CircularProgressIndicator(), + ), + error: (error, stackTrace) => const Center(child: Text("Error")), + data: (data) { + if (data.name.isNotEmpty) { + return GridViewWidget( + itemCount: data.name.length, + itemBuilder: (context, index) { + return MangaHomeImageCard( + url: data.url[index]!, + name: data.name[index]!, + image: data.image[index]!, + source: source, + lang: lang, + ); + }, + ); + } + return const Center( + child: Text("Empty"), + ); + }); + } +} diff --git a/lib/views/manga/reader/image_view_horizontal.dart b/lib/views/manga/reader/image_view_horizontal.dart index 944fcd3a..e76a8444 100644 --- a/lib/views/manga/reader/image_view_horizontal.dart +++ b/lib/views/manga/reader/image_view_horizontal.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:extended_image/extended_image.dart'; import 'package:flutter/material.dart'; +import 'package:mangayomi/utils/headers.dart'; class ImageViewHorizontal extends StatefulWidget { final int length; @@ -43,10 +44,7 @@ class _ImageViewHorizontalState extends State { clearMemoryCacheWhenDispose: true, enableMemoryCache: false, cacheMaxAge: const Duration(days: 7), - headers: const { - "Referer": "https://www.mangahere.cc/", - "Cookie": "isAdult=1" - }, + headers: headers(widget.source), mode: ExtendedImageMode.gesture, initGestureConfigHandler: widget.initGestureConfigHandler, onDoubleTap: widget.onDoubleTap, diff --git a/lib/views/manga/reader/image_view_vertical.dart b/lib/views/manga/reader/image_view_vertical.dart index 548c6110..305e3013 100644 --- a/lib/views/manga/reader/image_view_vertical.dart +++ b/lib/views/manga/reader/image_view_vertical.dart @@ -3,6 +3,7 @@ import 'package:extended_image/extended_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:mangayomi/utils/headers.dart'; import 'package:mangayomi/utils/media_query.dart'; class ImageViewVertical extends ConsumerStatefulWidget { @@ -44,10 +45,7 @@ class _ImageViewVerticalState extends ConsumerState height: MediaQuery.of(context).padding.top, ), ExtendedImage.network(widget.url, - headers: const { - "Referer": "https://www.mangahere.cc/", - "Cookie": "isAdult=1" - }, + headers: headers(widget.source), handleLoadingProgress: true, fit: BoxFit.contain, cacheMaxAge: const Duration(days: 7), diff --git a/lib/views/widgets/manga_image_card_widget.dart b/lib/views/widgets/manga_image_card_widget.dart index 2ffa5f76..a5b30351 100644 --- a/lib/views/widgets/manga_image_card_widget.dart +++ b/lib/views/widgets/manga_image_card_widget.dart @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:mangayomi/models/model_manga.dart'; import 'package:mangayomi/services/get_manga_detail.dart'; import 'package:mangayomi/utils/cached_network.dart'; +import 'package:mangayomi/utils/headers.dart'; import 'package:mangayomi/views/widgets/bottom_text_widget.dart'; import 'package:mangayomi/views/widgets/cover_view_widget.dart'; @@ -50,10 +51,7 @@ class _MangaImageCardWidgetState extends ConsumerState { }, child: CoverViewWidget(children: [ cachedNetworkImage( - headers: { - "Referer": "https://www.mangahere.cc/", - "Cookie": "isAdult=1" - }, + headers: headers(widget.getMangaDetailModel!.source!), imageUrl: widget.getMangaDetailModel!.imageUrl!, width: 200, height: 270,