mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-03-11 17:25:32 +00:00
mangathemesia multi source , impl search feature
This commit is contained in:
parent
933ba5461c
commit
1ea161c615
20 changed files with 679 additions and 19 deletions
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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<GetMangaChapterUrlModel> 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'<img[^>]+src="([^"]+)"');
|
||||
final Iterable<Match> matches = regex.allMatches(ta.first);
|
||||
|
||||
final List<String?> urls = matches.map((m) => m.group(1)).toList();
|
||||
Iterable<Match> 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<String?> 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*/
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ part of 'get_manga_chapter_url.dart';
|
|||
// **************************************************************************
|
||||
|
||||
String _$getMangaChapterUrlHash() =>
|
||||
r'38cf836814df00df1a3a269c0c2fc0c85debff78';
|
||||
r'a41949b68549e776832151d8b3a595db72fabdd9';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -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<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
|
|||
List<String> 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: {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
|
||||
{required String source, required int page}) async {
|
||||
|
|
@ -21,6 +43,46 @@ Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
|
|||
List<String?> name = [];
|
||||
List<String?> 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<Match> 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<Match> 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<Match> matches = exp.allMatches(e.innerHtml);
|
||||
String? firstMatch = matches.first.group(1);
|
||||
return firstMatch;
|
||||
}).toList();
|
||||
}
|
||||
} else
|
||||
//mangahere
|
||||
if (source == "mangahere") {
|
||||
final dom = await httpResToDom(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
96
lib/services/search_manga.dart
Normal file
96
lib/services/search_manga.dart
Normal file
|
|
@ -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<String?> url;
|
||||
late List<String?> name;
|
||||
late List<String?> image;
|
||||
|
||||
SearchMangaModel({
|
||||
required this.name,
|
||||
required this.url,
|
||||
required this.image,
|
||||
});
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<SearchMangaModel> searchManga(SearchMangaRef ref,
|
||||
{required String source, required String query}) async {
|
||||
List<String?> url = [];
|
||||
List<String?> name = [];
|
||||
List<String?> 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);
|
||||
}
|
||||
121
lib/services/search_manga.g.dart
Normal file
121
lib/services/search_manga.g.dart
Normal file
|
|
@ -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<SearchMangaModel>;
|
||||
|
||||
/// See also [searchManga].
|
||||
@ProviderFor(searchManga)
|
||||
const searchMangaProvider = SearchMangaFamily();
|
||||
|
||||
/// See also [searchManga].
|
||||
class SearchMangaFamily extends Family<AsyncValue<SearchMangaModel>> {
|
||||
/// 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<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'searchMangaProvider';
|
||||
}
|
||||
|
||||
/// See also [searchManga].
|
||||
class SearchMangaProvider extends AutoDisposeFutureProvider<SearchMangaModel> {
|
||||
/// 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
|
||||
|
|
@ -378,4 +378,53 @@ List<SourceModel> 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: ''),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@ enum TypeSource {
|
|||
@HiveField(2)
|
||||
mangathemesia,
|
||||
@HiveField(3)
|
||||
comick
|
||||
comick,
|
||||
@HiveField(4)
|
||||
mmrcms
|
||||
}
|
||||
|
||||
// @HiveType(typeId: 3)
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ class TypeSourceAdapter extends TypeAdapter<TypeSource> {
|
|||
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<TypeSource> {
|
|||
case TypeSource.comick:
|
||||
writer.writeByte(3);
|
||||
break;
|
||||
case TypeSource.mmrcms:
|
||||
writer.writeByte(4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
22
lib/utils/headers.dart
Normal file
22
lib/utils/headers.dart
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
Map<String, String>? 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'
|
||||
}
|
||||
: {};
|
||||
}
|
||||
|
|
@ -63,6 +63,7 @@ class _BrowseScreenState extends State<BrowseScreen>
|
|||
_isSearch = false;
|
||||
});
|
||||
_textEditingController.clear();
|
||||
entriesFilter = entries;
|
||||
},
|
||||
controller: _textEditingController,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
setState(() {
|
||||
isSearch = false;
|
||||
});
|
||||
_textEditingController.clear();
|
||||
|
||||
},
|
||||
controller: _textEditingController,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<MangaHomeScreen> {
|
|||
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) {
|
||||
128
lib/views/manga/home/manga_search_screen.dart
Normal file
128
lib/views/manga/home/manga_search_screen.dart
Normal file
|
|
@ -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<dynamic>(
|
||||
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<Widget>? 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"),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ImageViewHorizontal> {
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -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<ImageViewVertical>
|
|||
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),
|
||||
|
|
|
|||
|
|
@ -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<MangaImageCardWidget> {
|
|||
},
|
||||
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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue