mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-01-11 22:40:36 +00:00
Big changes ( added support for external sources )
This commit is contained in:
parent
86ebc32a0d
commit
b38f737c96
109 changed files with 5486 additions and 8171 deletions
|
|
@ -4,11 +4,12 @@
|
|||
|
||||
<h1 align="center"> Mangayomi </h1>
|
||||
|
||||
Mangayomi is free and open source manga reader cross-plateform app inspired by Tachiyomi made with flutter. It allows users to read manga from a variety of sources. Currently, all content sources are directly integrated into the application.
|
||||
Mangayomi is free and open source manga reader cross-plateform app inspired by Tachiyomi made with flutter. It allows users to read manga from a variety of sources.
|
||||
|
||||
## Features
|
||||
|
||||
Features include:
|
||||
* External sources
|
||||
* Online reading from a variety of sources
|
||||
* Local reading of downloaded content
|
||||
* Read and manage local archives (.cbz, .zip)
|
||||
|
|
|
|||
609
lib/eval/bridge_class/manga_model.dart
Normal file
609
lib/eval/bridge_class/manga_model.dart
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
import 'package:dart_eval/dart_eval.dart';
|
||||
import 'package:dart_eval/dart_eval_bridge.dart';
|
||||
import 'package:dart_eval/stdlib/core.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/model.dart';
|
||||
|
||||
class $MangaModel implements MangaModel, $Instance {
|
||||
$MangaModel.wrap(this.$value) : _superclass = $Object($value);
|
||||
|
||||
static const $type = BridgeTypeRef(
|
||||
BridgeTypeSpec('package:bridge_lib/bridge_lib.dart', 'MangaModel'));
|
||||
|
||||
static const $declaration = BridgeClassDef(BridgeClassType($type),
|
||||
constructors: {
|
||||
'': BridgeConstructorDef(BridgeFunctionDef(
|
||||
returns: BridgeTypeAnnotation($type),
|
||||
params: [],
|
||||
namedParams: [
|
||||
BridgeParameter(
|
||||
'source',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'author',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'status',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.intType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'genre',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(CoreTypes.list,
|
||||
[BridgeTypeRef.type(RuntimeTypes.stringType)]),
|
||||
),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'imageUrl',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'lang',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'name',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'link',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'description',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'baseUrl',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'dateFormat',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'dateFormatLocale',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'apiUrl',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'page',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.intType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'query',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.stringType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'sourceId',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef.type(RuntimeTypes.intType)),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'names',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(CoreTypes.list,
|
||||
[BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'urls',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(CoreTypes.list,
|
||||
[BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'chaptersScanlators',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(CoreTypes.list,
|
||||
[BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'chaptersDateUploads',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(CoreTypes.list,
|
||||
[BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'chaptersVolumes',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(CoreTypes.list,
|
||||
[BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'chaptersChaps',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(CoreTypes.list,
|
||||
[BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'images',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(CoreTypes.list,
|
||||
[BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
false),
|
||||
BridgeParameter(
|
||||
'statusList',
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(CoreTypes.list,
|
||||
[BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
false),
|
||||
]))
|
||||
},
|
||||
// Specify class fields
|
||||
fields: {
|
||||
'source': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'author': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'status': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.intType))),
|
||||
'genre': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(
|
||||
CoreTypes.list, [BridgeTypeRef.type(RuntimeTypes.stringType)]),
|
||||
),
|
||||
),
|
||||
'imageUrl': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'lang': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'name': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'link': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'description': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'baseUrl': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'dateFormat': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'dateFormatLocale': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'apiUrl': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'page': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.intType))),
|
||||
'query': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.stringType))),
|
||||
'sourceId': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(BridgeTypeRef.type(RuntimeTypes.intType))),
|
||||
'names': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(
|
||||
CoreTypes.list, [BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
),
|
||||
'urls': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(
|
||||
CoreTypes.list, [BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
),
|
||||
'chaptersScanlators': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(
|
||||
CoreTypes.list, [BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
),
|
||||
'chaptersDateUploads': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(
|
||||
CoreTypes.list, [BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
),
|
||||
'chaptersVolumes': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(
|
||||
CoreTypes.list, [BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
),
|
||||
'chaptersChaps': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(
|
||||
CoreTypes.list, [BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
),
|
||||
'images': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(
|
||||
CoreTypes.list, [BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
),
|
||||
'statusList': BridgeFieldDef(
|
||||
BridgeTypeAnnotation(
|
||||
BridgeTypeRef(
|
||||
CoreTypes.list, [BridgeTypeRef.type(RuntimeTypes.dynamicType)]),
|
||||
),
|
||||
),
|
||||
},
|
||||
wrap: true);
|
||||
|
||||
static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) {
|
||||
return $MangaModel.wrap(MangaModel());
|
||||
}
|
||||
|
||||
@override
|
||||
final MangaModel $value;
|
||||
|
||||
@override
|
||||
MangaModel get $reified => $value;
|
||||
|
||||
final $Instance _superclass;
|
||||
|
||||
@override
|
||||
$Value? $getProperty(Runtime runtime, String identifier) {
|
||||
switch (identifier) {
|
||||
case 'source':
|
||||
return $String($value.source!);
|
||||
case 'author':
|
||||
return $String($value.author!);
|
||||
case 'status':
|
||||
return $int($value.status!);
|
||||
case 'genre':
|
||||
return $List.wrap($value.genre!.map((e) {
|
||||
if (e is String) {
|
||||
return $String(e);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}).toList());
|
||||
case 'imageUrl':
|
||||
return $String($value.imageUrl!);
|
||||
case 'lang':
|
||||
return $String($value.lang!);
|
||||
case 'name':
|
||||
return $String($value.name!);
|
||||
case 'link':
|
||||
return $String($value.link!);
|
||||
case 'description':
|
||||
return $String($value.description!);
|
||||
case 'baseUrl':
|
||||
return $String($value.baseUrl!);
|
||||
case 'dateFormat':
|
||||
return $String($value.dateFormat!);
|
||||
case 'dateFormatLocale':
|
||||
return $String($value.dateFormatLocale!);
|
||||
case 'apiUrl':
|
||||
return $String($value.apiUrl!);
|
||||
case 'page':
|
||||
return $int($value.page!);
|
||||
case 'query':
|
||||
return $String($value.query!);
|
||||
case 'sourceId':
|
||||
return $int($value.sourceId!);
|
||||
case 'names':
|
||||
return $List.wrap($value.names!.map((e) {
|
||||
if (e is String) {
|
||||
return $String(e);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}).toList());
|
||||
case 'chaptersDateUploads':
|
||||
return $List.wrap($value.chaptersDateUploads!.map((e) {
|
||||
if (e is String) {
|
||||
return $String(e);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}).toList());
|
||||
case 'chaptersScanlators':
|
||||
return $List.wrap($value.chaptersScanlators!.map((e) {
|
||||
if (e is String) {
|
||||
return $String(e);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}).toList());
|
||||
case 'urls':
|
||||
return $List.wrap($value.urls!.map((e) {
|
||||
if (e is String) {
|
||||
return $String(e);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}).toList());
|
||||
case 'chaptersVolumes':
|
||||
return $List.wrap($value.chaptersVolumes!.map((e) {
|
||||
if (e is String) {
|
||||
return $String(e);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}).toList());
|
||||
case 'chaptersChaps':
|
||||
return $List.wrap($value.chaptersChaps!.map((e) {
|
||||
if (e is String) {
|
||||
return $String(e);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}).toList());
|
||||
case 'images':
|
||||
return $List.wrap($value.images!.map((e) {
|
||||
if (e is String) {
|
||||
return $String(e);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
}).toList());
|
||||
case 'statusList':
|
||||
return $List.wrap($value.statusList!.map((e) {
|
||||
return $int(e);
|
||||
}).toList());
|
||||
|
||||
default:
|
||||
return _superclass.$getProperty(runtime, identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int $getRuntimeType(Runtime runtime) => runtime.lookupType($type.spec!);
|
||||
|
||||
@override
|
||||
void $setProperty(Runtime runtime, String identifier, $Value value) {
|
||||
switch (identifier) {
|
||||
case 'source':
|
||||
$value.source = value.$reified;
|
||||
case 'author':
|
||||
$value.author = value.$reified;
|
||||
case 'status':
|
||||
$value.status = value.$reified;
|
||||
case 'genre':
|
||||
$value.genre = value.$reified as List<dynamic>;
|
||||
case 'imageUrl':
|
||||
$value.imageUrl = value.$reified;
|
||||
case 'lang':
|
||||
$value.lang = value.$reified;
|
||||
case 'name':
|
||||
$value.name = value.$reified;
|
||||
case 'link':
|
||||
$value.link = value.$reified;
|
||||
case 'description':
|
||||
$value.description = value.$reified;
|
||||
case 'baseUrl':
|
||||
$value.baseUrl = value.$reified;
|
||||
case 'dateFormat':
|
||||
$value.dateFormat = value.$reified;
|
||||
case 'dateFormatLocale':
|
||||
$value.dateFormatLocale = value.$reified;
|
||||
case 'apiUrl':
|
||||
$value.apiUrl = value.$reified;
|
||||
case 'page':
|
||||
$value.page = value.$reified;
|
||||
case 'query':
|
||||
$value.query = value.$reified;
|
||||
case 'sourceId':
|
||||
$value.sourceId = value.$reified;
|
||||
case 'names':
|
||||
$value.names = value.$reified as List<dynamic>;
|
||||
case 'chaptersDateUploads':
|
||||
$value.chaptersDateUploads = value.$reified as List<dynamic>;
|
||||
case 'chaptersScanlators':
|
||||
$value.chaptersScanlators = value.$reified as List<dynamic>;
|
||||
case 'urls':
|
||||
$value.urls = value.$reified as List<dynamic>;
|
||||
case 'chaptersVolumes':
|
||||
$value.chaptersVolumes = value.$reified as List<dynamic>;
|
||||
case 'chaptersChaps':
|
||||
$value.chaptersChaps = value.$reified as List<dynamic>;
|
||||
case 'images':
|
||||
$value.images = value.$reified as List<dynamic>;
|
||||
case 'statusList':
|
||||
$value.statusList = value.$reified as List<dynamic>;
|
||||
|
||||
default:
|
||||
_superclass.$setProperty(runtime, identifier, value);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String? get author => $value.author;
|
||||
|
||||
@override
|
||||
String? get description => $value.description;
|
||||
|
||||
@override
|
||||
String? get imageUrl => $value.imageUrl;
|
||||
|
||||
@override
|
||||
String? get name => $value.name;
|
||||
|
||||
@override
|
||||
String? get source => $value.source;
|
||||
|
||||
@override
|
||||
String? get link => $value.link;
|
||||
|
||||
@override
|
||||
List<dynamic>? get genre => $value.genre;
|
||||
|
||||
@override
|
||||
String? get lang => $value.lang;
|
||||
@override
|
||||
int? get sourceId => $value.sourceId;
|
||||
@override
|
||||
int? get status => $value.status;
|
||||
|
||||
@override
|
||||
set author(String? author) {
|
||||
// implement author
|
||||
}
|
||||
|
||||
@override
|
||||
set description(String? description) {
|
||||
// implement description
|
||||
}
|
||||
|
||||
@override
|
||||
set imageUrl(String? imageUrl) {
|
||||
// implement imageUrl
|
||||
}
|
||||
|
||||
@override
|
||||
set link(String? link) {
|
||||
// implement link
|
||||
}
|
||||
|
||||
@override
|
||||
set name(String? name) {
|
||||
// implement name
|
||||
}
|
||||
|
||||
@override
|
||||
set source(String? source) {
|
||||
// implement source
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic>? get chaptersDateUploads => $value.chaptersDateUploads;
|
||||
|
||||
@override
|
||||
List<dynamic>? get names => $value.names;
|
||||
|
||||
@override
|
||||
List<dynamic>? get chaptersScanlators => $value.chaptersScanlators;
|
||||
|
||||
@override
|
||||
List<dynamic>? get urls => $value.urls;
|
||||
@override
|
||||
List<dynamic>? get chaptersVolumes => $value.chaptersVolumes;
|
||||
|
||||
@override
|
||||
List<dynamic>? get chaptersChaps => $value.chaptersChaps;
|
||||
@override
|
||||
List<dynamic>? get images => $value.images;
|
||||
@override
|
||||
List<dynamic>? get statusList => $value.statusList;
|
||||
|
||||
@override
|
||||
set chaptersDateUploads(List? chaptersDateUploads) {
|
||||
// implement chaptersDateUploads
|
||||
}
|
||||
|
||||
@override
|
||||
set names(List? names) {
|
||||
// implement names
|
||||
}
|
||||
|
||||
@override
|
||||
set chaptersScanlators(List? chaptersScanlators) {
|
||||
// implement chaptersScanlators
|
||||
}
|
||||
|
||||
@override
|
||||
set urls(List? urls) {
|
||||
// implement urls
|
||||
}
|
||||
|
||||
@override
|
||||
set genre(List? genre) {
|
||||
// implement genre
|
||||
}
|
||||
|
||||
@override
|
||||
set lang(String? lang) {
|
||||
// implement lang
|
||||
}
|
||||
|
||||
@override
|
||||
set status(int? status) {
|
||||
// implement status
|
||||
}
|
||||
|
||||
@override
|
||||
String? get apiUrl => $value.apiUrl;
|
||||
|
||||
@override
|
||||
String? get baseUrl => $value.baseUrl;
|
||||
|
||||
@override
|
||||
String? get dateFormat => $value.dateFormat;
|
||||
|
||||
@override
|
||||
String? get dateFormatLocale => $value.dateFormatLocale;
|
||||
|
||||
@override
|
||||
set apiUrl(String? apiUrl) {
|
||||
// implement apiUrl
|
||||
}
|
||||
|
||||
@override
|
||||
set baseUrl(String? baseUrl) {
|
||||
// implement baseUrl
|
||||
}
|
||||
|
||||
@override
|
||||
set dateFormat(String? dateFormat) {
|
||||
// implement dateFormat
|
||||
}
|
||||
|
||||
@override
|
||||
set dateFormatLocale(String? dateFormatLocale) {
|
||||
// implement dateFormatLocale
|
||||
}
|
||||
|
||||
@override
|
||||
int? get page => $value.page;
|
||||
|
||||
@override
|
||||
set page(int? page) {
|
||||
// implement page
|
||||
}
|
||||
@override
|
||||
String? get query => $value.query;
|
||||
|
||||
@override
|
||||
set query(String? query) {
|
||||
// implement page
|
||||
}
|
||||
|
||||
@override
|
||||
set chaptersChaps(List? chaptersChaps) {
|
||||
// implement chaptersChaps
|
||||
}
|
||||
|
||||
@override
|
||||
set chaptersVolumes(List? chaptersVolumes) {
|
||||
// implement chaptersVolumes
|
||||
}
|
||||
@override
|
||||
set images(List? images) {
|
||||
// implement images
|
||||
}
|
||||
@override
|
||||
set statusList(List? images) {
|
||||
// implement statusList
|
||||
}
|
||||
|
||||
@override
|
||||
set sourceId(int? sourceId) {
|
||||
// implement sourceId
|
||||
}
|
||||
}
|
||||
67
lib/eval/bridge_class/model.dart
Normal file
67
lib/eval/bridge_class/model.dart
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
class MangaModel {
|
||||
String? name;
|
||||
|
||||
String? link;
|
||||
|
||||
String? imageUrl;
|
||||
|
||||
String? description;
|
||||
|
||||
String? author;
|
||||
|
||||
int? status;
|
||||
|
||||
List<dynamic>? genre = [];
|
||||
|
||||
String? source;
|
||||
|
||||
String? lang;
|
||||
|
||||
String? baseUrl;
|
||||
|
||||
String? dateFormat;
|
||||
|
||||
String? dateFormatLocale;
|
||||
|
||||
String? apiUrl;
|
||||
|
||||
int? page;
|
||||
|
||||
String? query;
|
||||
|
||||
int? sourceId;
|
||||
|
||||
List<dynamic>? names;
|
||||
List<dynamic>? urls;
|
||||
List<dynamic>? chaptersScanlators;
|
||||
List<dynamic>? chaptersDateUploads;
|
||||
List<dynamic>? chaptersVolumes;
|
||||
List<dynamic>? chaptersChaps;
|
||||
List<dynamic>? images;
|
||||
List<dynamic>? statusList;
|
||||
MangaModel(
|
||||
{this.source = "",
|
||||
this.author = "",
|
||||
this.genre,
|
||||
this.imageUrl = "",
|
||||
this.lang = "",
|
||||
this.link = "",
|
||||
this.name = "",
|
||||
this.status = 0,
|
||||
this.description = "",
|
||||
this.apiUrl = "",
|
||||
this.baseUrl = "",
|
||||
this.dateFormat = "",
|
||||
this.dateFormatLocale = "",
|
||||
this.page = 1,
|
||||
this.query = "",
|
||||
this.sourceId = 0,
|
||||
this.names,
|
||||
this.chaptersDateUploads,
|
||||
this.chaptersScanlators,
|
||||
this.urls,
|
||||
this.chaptersVolumes,
|
||||
this.chaptersChaps,
|
||||
this.images,
|
||||
this.statusList});
|
||||
}
|
||||
18
lib/eval/compiler/compiler.dart
Normal file
18
lib/eval/compiler/compiler.dart
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:dart_eval/dart_eval.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/manga_model.dart';
|
||||
import 'package:mangayomi/eval/m_bridge.dart';
|
||||
|
||||
Uint8List compilerEval(String sourceCode) {
|
||||
final compiler = Compiler();
|
||||
compiler.defineBridgeClasses([
|
||||
$MBridge.$declaration,
|
||||
$MangaModel.$declaration,
|
||||
]);
|
||||
final program = compiler.compile({
|
||||
'package:mangayomi': {'main.dart': sourceCode}
|
||||
});
|
||||
|
||||
final bytecode = program.write();
|
||||
return bytecode;
|
||||
}
|
||||
1163
lib/eval/m_bridge.dart
Normal file
1163
lib/eval/m_bridge.dart
Normal file
File diff suppressed because it is too large
Load diff
56
lib/eval/runtime/runtime.dart
Normal file
56
lib/eval/runtime/runtime.dart
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dart_eval/dart_eval.dart';
|
||||
import 'package:mangayomi/eval/m_bridge.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/manga_model.dart';
|
||||
|
||||
Runtime runtimeEval(Uint8List bytecode) {
|
||||
final runtime = Runtime(bytecode.buffer.asByteData());
|
||||
runtime.registerBridgeFunc(
|
||||
'package:bridge_lib/bridge_lib.dart', 'MBridge.', $MBridge.$construct,
|
||||
isBridge: true);
|
||||
runtime.registerBridgeFunc(
|
||||
'package:bridge_lib/bridge_lib.dart', 'MangaModel.', $MangaModel.$new);
|
||||
runtime.registerBridgeFunc(
|
||||
'package:bridge_lib/bridge_lib.dart', 'MBridge.http', $MBridge.$http);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.listParseDateTime', $MBridge.$listParseDateTime);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.jsonPathToString', $MBridge.$jsonPathToString);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.jsonPathToList', $MBridge.$jsonPathToList);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.jsonDecodeToList', $MBridge.$jsonDecodeToList);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.jsonPathToMap', $MBridge.$jsonPathToMap);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.parseStatus', $MBridge.$parseStatus);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.stringParseValue', $MBridge.$stringParseValue);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.getMapValue', $MBridge.$getMapValue);
|
||||
runtime.registerBridgeFunc(
|
||||
'package:bridge_lib/bridge_lib.dart', 'MBridge.regExp', $MBridge.$regExp);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.parseChapterDate', $MBridge.$parseChapterDate);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.stringParse', $MBridge.$stringParse);
|
||||
runtime.registerBridgeFunc(
|
||||
'package:bridge_lib/bridge_lib.dart', 'MBridge.evalJs', $MBridge.$evalJs);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.intParse', $MBridge.$intParse);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.listParse', $MBridge.$listParse);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.getHtmlViaWebview', $MBridge.$getHtmlViaWebview);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.listContain', $MBridge.$listContain);
|
||||
runtime.registerBridgeFunc(
|
||||
'package:bridge_lib/bridge_lib.dart', 'MBridge.xpath', $MBridge.$xpath);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.querySelector', $MBridge.$querySelector);
|
||||
runtime.registerBridgeFunc('package:bridge_lib/bridge_lib.dart',
|
||||
'MBridge.querySelectorAll', $MBridge.$querySelectorAll);
|
||||
runtime.setup();
|
||||
return runtime;
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
// ignore_for_file: depend_on_referenced_packages
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||
import 'package:flex_color_scheme/flex_color_scheme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -16,7 +15,18 @@ import 'package:mangayomi/modules/more/settings/appearance/providers/pure_black_
|
|||
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_mode_state_provider.dart';
|
||||
|
||||
late Isar isar;
|
||||
|
||||
class MyHttpoverrides extends HttpOverrides {
|
||||
@override
|
||||
HttpClient createHttpClient(SecurityContext? context) {
|
||||
return super.createHttpClient(context)
|
||||
..badCertificateCallback =
|
||||
(X509Certificate cert, String host, int port) => true;
|
||||
}
|
||||
}
|
||||
|
||||
void main(List<String> args) async {
|
||||
HttpOverrides.global = MyHttpoverrides();
|
||||
if (Platform.isLinux || Platform.isWindows) {
|
||||
if (runWebViewTitleBarWidget(args)) {
|
||||
return;
|
||||
|
|
@ -44,6 +54,7 @@ _iniDateFormatting() {
|
|||
initializeDateFormatting("zh", null);
|
||||
initializeDateFormatting("pl", null);
|
||||
initializeDateFormatting("tr", null);
|
||||
initializeDateFormatting("bg", null);
|
||||
}
|
||||
|
||||
class MyApp extends ConsumerStatefulWidget {
|
||||
|
|
@ -109,6 +120,7 @@ class _MyAppState extends ConsumerState<MyApp> {
|
|||
themeMode: isDarkTheme ? ThemeMode.dark : ThemeMode.light,
|
||||
theme: themeLight,
|
||||
debugShowCheckedModeBanner: false,
|
||||
builder: BotToastInit(),
|
||||
routeInformationParser: router.routeInformationParser,
|
||||
routerDelegate: router.routerDelegate,
|
||||
routeInformationProvider: router.routeInformationProvider,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
class MangaType {
|
||||
String? lang;
|
||||
bool? isFullData;
|
||||
String? source;
|
||||
Source? source;
|
||||
MangaType(
|
||||
{required this.isFullData, required this.lang, required this.source});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
part 'settings.g.dart';
|
||||
|
||||
|
|
@ -85,6 +86,10 @@ class Settings {
|
|||
|
||||
List<FilterScanlator>? filterScanlatorList;
|
||||
|
||||
final sources = IsarLinks<Source>();
|
||||
|
||||
bool? autoUpdateExtensions;
|
||||
|
||||
bool? cropBorders;
|
||||
|
||||
Settings(
|
||||
|
|
@ -124,7 +129,8 @@ class Settings {
|
|||
this.saveAsCBZArchive = false,
|
||||
this.downloadLocation = "",
|
||||
this.cropBorders = false,
|
||||
this.libraryLocalSource});
|
||||
this.libraryLocalSource,
|
||||
this.autoUpdateExtensions = false});
|
||||
}
|
||||
|
||||
enum DisplayType {
|
||||
|
|
@ -184,7 +190,7 @@ class ChapterPageIndex {
|
|||
|
||||
@embedded
|
||||
class Cookie {
|
||||
String? source;
|
||||
String? idSource;
|
||||
String? cookie;
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -6,7 +6,7 @@ part 'source.g.dart';
|
|||
class Source {
|
||||
Id? id;
|
||||
|
||||
String? sourceName;
|
||||
String? name;
|
||||
|
||||
String? baseUrl;
|
||||
|
||||
|
|
@ -20,14 +20,17 @@ class Source {
|
|||
|
||||
bool? isNsfw;
|
||||
|
||||
@enumerated
|
||||
TypeSource typeSource;
|
||||
String? sourceCode;
|
||||
|
||||
String? logoUrl;
|
||||
String? sourceCodeUrl;
|
||||
|
||||
String? typeSource;
|
||||
|
||||
String? iconUrl;
|
||||
|
||||
bool? isFullData;
|
||||
|
||||
bool? isCloudflare;
|
||||
bool? hasCloudflare;
|
||||
|
||||
bool? lastUsed;
|
||||
|
||||
|
|
@ -37,38 +40,136 @@ class Source {
|
|||
|
||||
String? apiUrl;
|
||||
|
||||
String? version;
|
||||
|
||||
String? versionLast;
|
||||
|
||||
String? headers;
|
||||
|
||||
Source({
|
||||
this.id = Isar.autoIncrement,
|
||||
required this.sourceName,
|
||||
required this.baseUrl,
|
||||
required this.lang,
|
||||
required this.typeSource,
|
||||
required this.logoUrl,
|
||||
required this.dateFormat,
|
||||
required this.dateFormatLocale,
|
||||
this.id = 0,
|
||||
this.name = '',
|
||||
this.baseUrl = '',
|
||||
this.lang = '',
|
||||
this.typeSource = '',
|
||||
this.iconUrl = '',
|
||||
this.dateFormat = '',
|
||||
this.dateFormatLocale = '',
|
||||
this.isActive = true,
|
||||
this.isAdded = false,
|
||||
this.isNsfw = false,
|
||||
this.isFullData = false,
|
||||
this.isCloudflare = false,
|
||||
this.hasCloudflare = false,
|
||||
this.isPinned = false,
|
||||
this.lastUsed = false,
|
||||
this.apiUrl = "",
|
||||
this.sourceCodeUrl = "",
|
||||
this.version = "",
|
||||
this.versionLast = "",
|
||||
this.sourceCode = '',
|
||||
this.headers = '',
|
||||
});
|
||||
Source.fromJson(Map<String, dynamic> json) {
|
||||
name = json['name'];
|
||||
id = json['id'];
|
||||
baseUrl = json['baseUrl'];
|
||||
lang = json['lang'];
|
||||
typeSource = json['typeSource'];
|
||||
iconUrl = json['iconUrl'];
|
||||
dateFormat = json['dateFormat'];
|
||||
dateFormatLocale = json['dateFormatLocale'];
|
||||
isNsfw = json['isNsfw'];
|
||||
hasCloudflare = json['hasCloudflare'];
|
||||
sourceCodeUrl = json['sourceCodeUrl'];
|
||||
apiUrl = json['apiUrl'];
|
||||
version = json['version'];
|
||||
}
|
||||
}
|
||||
|
||||
enum TypeSource {
|
||||
single,
|
||||
// enum TypeSource {
|
||||
// single,
|
||||
|
||||
mangathemesia,
|
||||
// mangathemesia,
|
||||
|
||||
comick,
|
||||
// comick,
|
||||
|
||||
mmrcms,
|
||||
// mmrcms,
|
||||
|
||||
heancms,
|
||||
// heancms,
|
||||
|
||||
madara,
|
||||
// madara,
|
||||
|
||||
mangadex
|
||||
}
|
||||
// mangadex
|
||||
// }
|
||||
|
||||
// class Source {
|
||||
// String? name;
|
||||
|
||||
// String? baseUrl;
|
||||
|
||||
// String? lang;
|
||||
|
||||
// bool? isActive;
|
||||
|
||||
// bool? isAdded;
|
||||
|
||||
// bool? isPinned;
|
||||
|
||||
// bool? lastUsed;
|
||||
|
||||
// bool? isFullData;
|
||||
|
||||
// bool? isNsfw;
|
||||
|
||||
// String? sourceCodeUrl;
|
||||
|
||||
// String? typeSource;
|
||||
|
||||
// String? iconUrl;
|
||||
|
||||
// bool? hasCloudflare;
|
||||
|
||||
// String? dateFormat;
|
||||
|
||||
// String? dateFormatLocale;
|
||||
|
||||
// String? apiUrl;
|
||||
|
||||
// String? version;
|
||||
|
||||
// Source({
|
||||
// this.name = "",
|
||||
// this.baseUrl = "",
|
||||
// this.lang = "",
|
||||
// this.typeSource = "",
|
||||
// this.iconUrl = "",
|
||||
// this.dateFormat,
|
||||
// this.dateFormatLocale,
|
||||
// this.isActive = true,
|
||||
// this.isAdded = false,
|
||||
// this.isNsfw = false,
|
||||
// this.isFullData = false,
|
||||
// this.hasCloudflare = false,
|
||||
// this.isPinned = false,
|
||||
// this.lastUsed = false,
|
||||
// this.sourceCodeUrl = "",
|
||||
// this.apiUrl = "",
|
||||
// this.version = "",
|
||||
// });
|
||||
|
||||
// Source.fromJson(Map<String, dynamic> json) {
|
||||
// name = json['name'];
|
||||
// id = json['id'];
|
||||
// baseUrl = json['baseUrl'];
|
||||
// lang = json['lang'];
|
||||
// typeSource = json['typeSource'];
|
||||
// iconUrl = json['iconUrl'];
|
||||
// dateFormat = json['dateFormat'];
|
||||
// dateFormatLocale = json['dateFormatLocale'];
|
||||
// isNsfw = json['isNsfw'];
|
||||
// hasCloudflare = json['hasCloudflare'];
|
||||
// sourceCodeUrl = json['sourceCodeUrl'];
|
||||
// apiUrl = json['apiUrl'];
|
||||
// version = json['version'];
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,9 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mangayomi/modules/browse/extension/providers/refresh_source_list_data.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/browse/extension/extension_screen.dart';
|
||||
import 'package:mangayomi/modules/browse/migrate_screen.dart';
|
||||
import 'package:mangayomi/modules/browse/sources/sources_screen.dart';
|
||||
|
|
@ -28,7 +26,6 @@ class _BrowseScreenState extends ConsumerState<BrowseScreen>
|
|||
_chekPermission();
|
||||
setState(() {
|
||||
_textEditingController.clear();
|
||||
_entriesFilter = [];
|
||||
_isSearch = false;
|
||||
});
|
||||
});
|
||||
|
|
@ -39,13 +36,10 @@ class _BrowseScreenState extends ConsumerState<BrowseScreen>
|
|||
await StorageProvider().requestPermission();
|
||||
}
|
||||
|
||||
List<Source> _entries = [];
|
||||
List<Source> _entriesFilter = [];
|
||||
final _textEditingController = TextEditingController();
|
||||
bool _isSearch = false;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ref.watch(refreshSourceListDataProvider);
|
||||
return DefaultTabController(
|
||||
animationDuration: Duration.zero,
|
||||
length: 3,
|
||||
|
|
@ -61,13 +55,7 @@ class _BrowseScreenState extends ConsumerState<BrowseScreen>
|
|||
_isSearch
|
||||
? SeachFormTextField(
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_entriesFilter = _entries
|
||||
.where((element) => element.sourceName!
|
||||
.toLowerCase()
|
||||
.contains(value.toLowerCase()))
|
||||
.toList();
|
||||
});
|
||||
setState(() {});
|
||||
},
|
||||
onSuffixPressed: () {
|
||||
_textEditingController.clear();
|
||||
|
|
@ -77,7 +65,6 @@ class _BrowseScreenState extends ConsumerState<BrowseScreen>
|
|||
_isSearch = false;
|
||||
});
|
||||
_textEditingController.clear();
|
||||
_entriesFilter = _entries;
|
||||
},
|
||||
controller: _textEditingController,
|
||||
)
|
||||
|
|
@ -105,6 +92,7 @@ class _BrowseScreenState extends ConsumerState<BrowseScreen>
|
|||
splashRadius: 20,
|
||||
onPressed: () {
|
||||
if (_tabBarController.index == 0) {
|
||||
context.push('/sourceFilter');
|
||||
} else if (_tabBarController.index == 1) {
|
||||
_textEditingController.clear();
|
||||
context.push('/extensionLang');
|
||||
|
|
@ -131,10 +119,7 @@ class _BrowseScreenState extends ConsumerState<BrowseScreen>
|
|||
body: TabBarView(controller: _tabBarController, children: [
|
||||
const SourcesScreen(),
|
||||
ExtensionScreen(
|
||||
entriesData: (val) {
|
||||
_entries = val as List<Source>;
|
||||
},
|
||||
entriesFilter: _entriesFilter,
|
||||
query: _textEditingController.text,
|
||||
),
|
||||
const MigrateScreen()
|
||||
]),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ class ExtensionsLang extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
language.sort((a, b) => a.compareTo(b));
|
||||
final languages = languagesMap.entries.map((e) => e.value).toList();
|
||||
languages.sort((a, b) => a.compareTo(b));
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Extensions"),
|
||||
|
|
@ -22,22 +23,22 @@ class ExtensionsLang extends ConsumerWidget {
|
|||
builder: (context, snapshot) {
|
||||
List<Source>? entries = snapshot.hasData ? snapshot.data : [];
|
||||
return ListView.builder(
|
||||
itemCount: language.length,
|
||||
itemCount: languages.length,
|
||||
itemBuilder: (context, index) {
|
||||
final lang = languages[index];
|
||||
return ExtensionLangListTileWidget(
|
||||
lang: lang(language[index]),
|
||||
lang: lang,
|
||||
onChanged: (val) {
|
||||
isar.writeTxnSync(() {
|
||||
for (var source in entries) {
|
||||
if (source.lang == lang(language[index])) {
|
||||
if (source.lang == lang) {
|
||||
isar.sources.putSync(source..isActive = val == true);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
value: entries!
|
||||
.where((element) =>
|
||||
element.lang == "${lang(language[index])}")
|
||||
.where((element) => element.lang == lang)
|
||||
.where((element) => element.isActive!)
|
||||
.isNotEmpty,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,72 +4,74 @@ import 'package:grouped_list/grouped_list.dart';
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/browse/extension/providers/fetch_sources.dart';
|
||||
import 'package:mangayomi/utils/lang.dart';
|
||||
import 'package:mangayomi/modules/browse/extension/widgets/extension_list_tile_widget.dart';
|
||||
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
|
||||
|
||||
class ExtensionScreen extends ConsumerWidget {
|
||||
final Function(dynamic) entriesData;
|
||||
final List<Source> entriesFilter;
|
||||
const ExtensionScreen(
|
||||
{required this.entriesData, required this.entriesFilter, super.key});
|
||||
final String query;
|
||||
const ExtensionScreen({required this.query, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: StreamBuilder(
|
||||
stream: isar.sources
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.and()
|
||||
.isActiveEqualTo(true)
|
||||
.watch(fireImmediately: true),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
|
||||
final entries = snapshot.data!
|
||||
.where((element) => ref.watch(showNSFWStateProvider)
|
||||
? true
|
||||
: element.isNsfw == false)
|
||||
.toList();
|
||||
entriesData(entries);
|
||||
return GroupedListView<Source, String>(
|
||||
elements: entriesFilter.isNotEmpty ? entriesFilter : entries,
|
||||
groupBy: (element) => completeLang(element.lang!.toLowerCase()),
|
||||
groupSeparatorBuilder: (String groupByValue) => Padding(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
groupByValue,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, fontSize: 13),
|
||||
),
|
||||
],
|
||||
ref.watch(fetchSourcesListProvider(id: null));
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => ref.refresh(fetchSourcesListProvider(id: null).future),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: StreamBuilder(
|
||||
stream: query.isNotEmpty
|
||||
? isar.sources
|
||||
.filter()
|
||||
.nameContains(query.toLowerCase(), caseSensitive: false)
|
||||
.idIsNotNull()
|
||||
.and()
|
||||
.isActiveEqualTo(true)
|
||||
.watch(fireImmediately: true)
|
||||
: isar.sources
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.and()
|
||||
.isActiveEqualTo(true)
|
||||
.watch(fireImmediately: true),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
|
||||
final entries = snapshot.data!
|
||||
.where((element) => ref.watch(showNSFWStateProvider)
|
||||
? true
|
||||
: element.isNsfw == false)
|
||||
.toList();
|
||||
return GroupedListView<Source, String>(
|
||||
elements: entries,
|
||||
groupBy: (element) =>
|
||||
completeLang(element.lang!.toLowerCase()),
|
||||
groupSeparatorBuilder: (String groupByValue) => Padding(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
groupByValue,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, fontSize: 13),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
itemBuilder: (context, Source element) {
|
||||
return ExtensionListTileWidget(
|
||||
lang: element.lang!,
|
||||
onChanged: (val) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.sources.putSync(element..isAdded = val);
|
||||
});
|
||||
},
|
||||
sourceName: element.sourceName!,
|
||||
value: element.isAdded!,
|
||||
logoUrl: element.logoUrl!,
|
||||
isNsfw: element.isNsfw!,
|
||||
);
|
||||
},
|
||||
groupComparator: (group1, group2) => group1.compareTo(group2),
|
||||
itemComparator: (item1, item2) =>
|
||||
item1.sourceName!.compareTo(item2.sourceName!),
|
||||
order: GroupedListOrder.ASC,
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
}),
|
||||
itemBuilder: (context, Source element) {
|
||||
return ExtensionListTileWidget(
|
||||
source: element,
|
||||
);
|
||||
},
|
||||
groupComparator: (group1, group2) => group1.compareTo(group2),
|
||||
itemComparator: (item1, item2) =>
|
||||
item1.name!.compareTo(item2.name!),
|
||||
order: GroupedListOrder.ASC,
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
147
lib/modules/browse/extension/providers/fetch_sources.dart
Normal file
147
lib/modules/browse/extension/providers/fetch_sources.dart
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
import 'dart:convert';
|
||||
import 'package:dart_eval/stdlib/core.dart';
|
||||
import 'package:mangayomi/eval/compiler/compiler.dart';
|
||||
import 'package:mangayomi/eval/runtime/runtime.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
part 'fetch_sources.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future fetchSourcesList(FetchSourcesListRef ref, {int? id}) async {
|
||||
final req = await http
|
||||
.get(Uri.parse("https://kodjodevf.github.io/mangayomi-extensions/index.json"));
|
||||
final res = jsonDecode(req.body) as List;
|
||||
|
||||
final sourceList = res.map((e) => Source.fromJson(e)).toList();
|
||||
|
||||
isar.writeTxnSync(() async {
|
||||
for (var source in sourceList) {
|
||||
if (id != null) {
|
||||
if (id == source.id) {
|
||||
final sourc = isar.sources.getSync(id)!;
|
||||
final req = await http.get(Uri.parse(source.sourceCodeUrl!));
|
||||
final headers = await getHeaders(req.body, source.baseUrl!);
|
||||
isar.writeTxnSync(() {
|
||||
isar.sources.putSync(sourc
|
||||
..headers = headers ?? ""
|
||||
..isAdded = true
|
||||
..sourceCode = req.body
|
||||
..sourceCodeUrl = source.sourceCodeUrl
|
||||
..id = id
|
||||
..apiUrl = source.apiUrl
|
||||
..baseUrl = source.baseUrl
|
||||
..dateFormat = source.dateFormat
|
||||
..dateFormatLocale = source.dateFormatLocale
|
||||
..hasCloudflare = source.hasCloudflare
|
||||
..iconUrl = source.iconUrl
|
||||
..typeSource = source.typeSource
|
||||
..lang = source.lang
|
||||
..isNsfw = source.isNsfw
|
||||
..name = source.name
|
||||
..version = source.version
|
||||
..versionLast = source.version);
|
||||
});
|
||||
// log("successfully installed");
|
||||
}
|
||||
} else if (isar.sources.getSync(source.id!) != null) {
|
||||
// log("exist");
|
||||
final sourc = isar.sources.getSync(source.id!)!;
|
||||
if (compareVersions(sourc.version!, source.version!) < 0) {
|
||||
// log("update aivalable auto update");
|
||||
if (isar.settings.getSync(227)!.autoUpdateExtensions ?? false) {
|
||||
final req = await http.get(Uri.parse(source.sourceCodeUrl!));
|
||||
final headers = await getHeaders(req.body, source.baseUrl!);
|
||||
isar.writeTxnSync(() {
|
||||
isar.sources.putSync(sourc
|
||||
..headers = headers
|
||||
..sourceCode = req.body
|
||||
..sourceCodeUrl = source.sourceCodeUrl
|
||||
..id = source.id
|
||||
..apiUrl = source.apiUrl
|
||||
..baseUrl = source.baseUrl
|
||||
..dateFormat = source.dateFormat
|
||||
..dateFormatLocale = source.dateFormatLocale
|
||||
..hasCloudflare = source.hasCloudflare
|
||||
..iconUrl = source.iconUrl
|
||||
..typeSource = source.typeSource
|
||||
..isFullData = source.isFullData
|
||||
..lang = source.lang
|
||||
..isNsfw = source.isNsfw
|
||||
..name = source.name
|
||||
..version = source.version
|
||||
..versionLast = source.version);
|
||||
});
|
||||
} else {
|
||||
// log("update aivalable");
|
||||
isar.sources.putSync(sourc..versionLast = source.version);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isar.sources.putSync(Source()
|
||||
..sourceCodeUrl = source.sourceCodeUrl
|
||||
..id = source.id
|
||||
..sourceCode = source.sourceCode
|
||||
..apiUrl = source.apiUrl
|
||||
..baseUrl = source.baseUrl
|
||||
..dateFormat = source.dateFormat
|
||||
..dateFormatLocale = source.dateFormatLocale
|
||||
..hasCloudflare = source.hasCloudflare
|
||||
..iconUrl = source.iconUrl
|
||||
..typeSource = source.typeSource
|
||||
..lang = source.lang
|
||||
..isNsfw = source.isNsfw
|
||||
..name = source.name
|
||||
..version = source.version
|
||||
..versionLast = source.version);
|
||||
// log("new source");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int compareVersions(String version1, String version2) {
|
||||
List<String> v1Components = version1.split('.');
|
||||
List<String> v2Components = version2.split('.');
|
||||
|
||||
for (int i = 0; i < v1Components.length && i < v2Components.length; i++) {
|
||||
int v1Value = int.parse(v1Components[i]);
|
||||
int v2Value = int.parse(v2Components[i]);
|
||||
|
||||
if (v1Value < v2Value) {
|
||||
return -1;
|
||||
} else if (v1Value > v2Value) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (v1Components.length < v2Components.length) {
|
||||
return -1;
|
||||
} else if (v1Components.length > v2Components.length) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Future<String?> getHeaders(String codeSource, String baseUrl) async {
|
||||
try {
|
||||
final bytecode = compilerEval(codeSource);
|
||||
final runtime = runtimeEval(bytecode);
|
||||
runtime.args = [$String(baseUrl)];
|
||||
var result2 = await runtime.executeLib(
|
||||
'package:package:mangayomi/main.dart',
|
||||
'getHeader',
|
||||
);
|
||||
Map<String, String> headers = {};
|
||||
if (result2 is $Map) {
|
||||
headers = result2.$reified
|
||||
.map((key, value) => MapEntry(key.toString(), value.toString()));
|
||||
}
|
||||
return jsonEncode(headers);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
112
lib/modules/browse/extension/providers/fetch_sources.g.dart
Normal file
112
lib/modules/browse/extension/providers/fetch_sources.g.dart
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'fetch_sources.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$fetchSourcesListHash() => r'5cb7bececf304fc891319051c39cbf4566127a14';
|
||||
|
||||
/// 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 FetchSourcesListRef = AutoDisposeFutureProviderRef<dynamic>;
|
||||
|
||||
/// See also [fetchSourcesList].
|
||||
@ProviderFor(fetchSourcesList)
|
||||
const fetchSourcesListProvider = FetchSourcesListFamily();
|
||||
|
||||
/// See also [fetchSourcesList].
|
||||
class FetchSourcesListFamily extends Family<AsyncValue<dynamic>> {
|
||||
/// See also [fetchSourcesList].
|
||||
const FetchSourcesListFamily();
|
||||
|
||||
/// See also [fetchSourcesList].
|
||||
FetchSourcesListProvider call({
|
||||
int? id,
|
||||
}) {
|
||||
return FetchSourcesListProvider(
|
||||
id: id,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FetchSourcesListProvider getProviderOverride(
|
||||
covariant FetchSourcesListProvider provider,
|
||||
) {
|
||||
return call(
|
||||
id: provider.id,
|
||||
);
|
||||
}
|
||||
|
||||
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'fetchSourcesListProvider';
|
||||
}
|
||||
|
||||
/// See also [fetchSourcesList].
|
||||
class FetchSourcesListProvider extends AutoDisposeFutureProvider<dynamic> {
|
||||
/// See also [fetchSourcesList].
|
||||
FetchSourcesListProvider({
|
||||
this.id,
|
||||
}) : super.internal(
|
||||
(ref) => fetchSourcesList(
|
||||
ref,
|
||||
id: id,
|
||||
),
|
||||
from: fetchSourcesListProvider,
|
||||
name: r'fetchSourcesListProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$fetchSourcesListHash,
|
||||
dependencies: FetchSourcesListFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
FetchSourcesListFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final int? id;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is FetchSourcesListProvider && other.id == id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, id.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
|
||||
|
|
@ -1,36 +1,36 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/sources/source_list.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'refresh_source_list_data.g.dart';
|
||||
// import 'package:isar/isar.dart';
|
||||
// import 'package:mangayomi/main.dart';
|
||||
// import 'package:mangayomi/models/source.dart';
|
||||
// import 'package:mangayomi/sources/source_list.dart';
|
||||
// import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
// part 'refresh_source_list_data.g.dart';
|
||||
|
||||
@riverpod
|
||||
refreshSourceListData(RefreshSourceListDataRef ref) {
|
||||
isar.writeTxnSync(() {
|
||||
for (var source in sourcesList) {
|
||||
final sourceF = isar.sources
|
||||
.filter()
|
||||
.sourceNameEqualTo(source.sourceName)
|
||||
.and()
|
||||
.langEqualTo(source.lang)
|
||||
.findAllSync();
|
||||
if (sourceF.isEmpty) {
|
||||
isar.sources.putSync(source);
|
||||
} else {
|
||||
isar.sources.putSync(sourceF.first
|
||||
..apiUrl = source.apiUrl
|
||||
..baseUrl = source.baseUrl
|
||||
..dateFormat = source.dateFormat
|
||||
..dateFormatLocale = source.dateFormatLocale
|
||||
..isCloudflare = source.isCloudflare
|
||||
..logoUrl = source.logoUrl
|
||||
..typeSource = source.typeSource
|
||||
..isFullData = source.isFullData
|
||||
..lang = source.lang
|
||||
..isNsfw = source.isNsfw
|
||||
..sourceName = source.sourceName);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// @riverpod
|
||||
// refreshSourceListData(RefreshSourceListDataRef ref) {
|
||||
// isar.writeTxnSync(() {
|
||||
// for (var source in sourcesList) {
|
||||
// final sourceF = isar.sources
|
||||
// .filter()
|
||||
// .nameEqualTo(source.name)
|
||||
// .and()
|
||||
// .langEqualTo(source.lang)
|
||||
// .findAllSync();
|
||||
// if (sourceF.isEmpty) {
|
||||
// isar.sources.putSync(source);
|
||||
// } else {
|
||||
// isar.sources.putSync(sourceF.first
|
||||
// ..apiUrl = source.apiUrl
|
||||
// ..baseUrl = source.baseUrl
|
||||
// ..dateFormat = source.dateFormat
|
||||
// ..dateFormatLocale = source.dateFormatLocale
|
||||
// ..hasCloudflare = source.hasCloudflare
|
||||
// ..logoUrl = source.logoUrl
|
||||
// ..typeSource = source.typeSource
|
||||
// ..isFullData = source.isFullData
|
||||
// ..lang = source.lang
|
||||
// ..isNsfw = source.isNsfw
|
||||
// ..name = source.name);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'refresh_source_list_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$refreshSourceListDataHash() =>
|
||||
r'c6ab103b0519d79afc8530ab7027a988203de5d3';
|
||||
|
||||
/// See also [refreshSourceListData].
|
||||
@ProviderFor(refreshSourceListData)
|
||||
final refreshSourceListDataProvider = AutoDisposeProvider<dynamic>.internal(
|
||||
refreshSourceListData,
|
||||
name: r'refreshSourceListDataProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$refreshSourceListDataHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef RefreshSourceListDataRef = AutoDisposeProviderRef<dynamic>;
|
||||
// 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
|
||||
|
|
@ -1,63 +1,72 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/browse/extension/providers/fetch_sources.dart';
|
||||
import 'package:mangayomi/utils/lang.dart';
|
||||
|
||||
class ExtensionListTileWidget extends StatelessWidget {
|
||||
final String sourceName;
|
||||
final String lang;
|
||||
final bool value;
|
||||
final String logoUrl;
|
||||
final bool isNsfw;
|
||||
final Function(bool) onChanged;
|
||||
const ExtensionListTileWidget(
|
||||
{super.key,
|
||||
required this.sourceName,
|
||||
required this.lang,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
required this.logoUrl,
|
||||
required this.isNsfw});
|
||||
class ExtensionListTileWidget extends ConsumerStatefulWidget {
|
||||
final Source source;
|
||||
const ExtensionListTileWidget({
|
||||
super.key,
|
||||
required this.source,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ConsumerState<ExtensionListTileWidget> createState() =>
|
||||
_ExtensionListTileWidgetState();
|
||||
}
|
||||
|
||||
class _ExtensionListTileWidgetState
|
||||
extends ConsumerState<ExtensionListTileWidget> {
|
||||
bool _isLoading = false;
|
||||
@override
|
||||
Widget build(
|
||||
BuildContext context,
|
||||
) {
|
||||
final updateAivalable =
|
||||
compareVersions(widget.source.version!, widget.source.versionLast!) < 0;
|
||||
final sourceNotEmpty = widget.source.sourceCode != null &&
|
||||
widget.source.sourceCode!.isNotEmpty;
|
||||
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
onChanged(!value);
|
||||
// onChanged(!value);
|
||||
},
|
||||
leading: Container(
|
||||
height: 37,
|
||||
width: 37,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).secondaryHeaderColor.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(5)),
|
||||
child:
|
||||
// logoUrl.isEmpty
|
||||
// ?
|
||||
const Icon(Icons.source_outlined)
|
||||
// : CachedNetworkImage(
|
||||
// imageUrl: logoUrl,
|
||||
// fit: BoxFit.contain,
|
||||
// width: 37,
|
||||
// height: 37,
|
||||
// errorWidget: (context, url, error) {
|
||||
// return const SizedBox(
|
||||
// width: 37,
|
||||
// height: 37,
|
||||
// child: Center(
|
||||
// child: Icon(Icons.source_outlined),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
),
|
||||
title: Text(sourceName),
|
||||
height: 37,
|
||||
width: 37,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).secondaryHeaderColor.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(5)),
|
||||
child:
|
||||
// iconUrl.isEmpty
|
||||
// ?
|
||||
const Icon(Icons.source_outlined)
|
||||
// : CachedNetworkImage(
|
||||
// imageUrl: iconUrl,
|
||||
// fit: BoxFit.contain,
|
||||
// width: 37,
|
||||
// height: 37,
|
||||
// errorWidget: (context, url, error) {
|
||||
// return const SizedBox(
|
||||
// width: 37,
|
||||
// height: 37,
|
||||
// child: Center(
|
||||
// child: Icon(Icons.source_outlined),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
),
|
||||
title: Text(widget.source.name!),
|
||||
subtitle: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
completeLang(lang.toLowerCase()),
|
||||
completeLang(widget.source.lang!.toLowerCase()),
|
||||
style: const TextStyle(fontWeight: FontWeight.w300, fontSize: 12),
|
||||
),
|
||||
if (isNsfw)
|
||||
if (widget.source.isNsfw!)
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
|
|
@ -74,10 +83,33 @@ class ExtensionListTileWidget extends StatelessWidget {
|
|||
)
|
||||
],
|
||||
),
|
||||
trailing: Switch(
|
||||
value: value,
|
||||
onChanged: (value) {
|
||||
onChanged(value);
|
||||
}));
|
||||
trailing: TextButton(
|
||||
onPressed: !updateAivalable && sourceNotEmpty
|
||||
? null
|
||||
: () async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
await ref.watch(
|
||||
fetchSourcesListProvider(id: widget.source.id).future);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: _isLoading
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2.0,
|
||||
))
|
||||
: Text(!sourceNotEmpty
|
||||
? "Install"
|
||||
: updateAivalable
|
||||
? "Update"
|
||||
: "Latest"),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,10 @@ import 'package:go_router/go_router.dart';
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/model.dart';
|
||||
import 'package:mangayomi/services/get_manga_detail.dart';
|
||||
import 'package:mangayomi/services/search_manga.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/source_list.dart';
|
||||
import 'package:mangayomi/utils/cached_network.dart';
|
||||
import 'package:mangayomi/utils/colors.dart';
|
||||
import 'package:mangayomi/utils/headers.dart';
|
||||
|
|
@ -34,7 +33,12 @@ class _GlobalSearchScreenState extends ConsumerState<GlobalSearchScreen> {
|
|||
Widget build(BuildContext context) {
|
||||
final sourceList = ref.watch(onlyIncludePinnedSourceStateProvider)
|
||||
? isar.sources.filter().isPinnedEqualTo(true).findAllSync()
|
||||
: sourcesList;
|
||||
: isar.sources
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.and()
|
||||
.isAddedEqualTo(true)
|
||||
.findAllSync();
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: Container(),
|
||||
|
|
@ -90,7 +94,10 @@ class SourceSearchScreen extends ConsumerWidget {
|
|||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final search = ref.watch(searchMangaProvider(
|
||||
source: source.sourceName!, query: query, lang: source.lang!));
|
||||
source: source,
|
||||
page: 1,
|
||||
query: query,
|
||||
));
|
||||
return Scaffold(
|
||||
body: SizedBox(
|
||||
height: 260,
|
||||
|
|
@ -101,13 +108,12 @@ class SourceSearchScreen extends ConsumerWidget {
|
|||
onTap: () {
|
||||
Map<String, dynamic> data = {
|
||||
'query': query,
|
||||
'source': source.sourceName,
|
||||
'lang': source.lang,
|
||||
'source': source,
|
||||
'viewOnly': true,
|
||||
};
|
||||
context.push('/searchResult', extra: data);
|
||||
},
|
||||
title: Text(source.sourceName!),
|
||||
title: Text(source.name!),
|
||||
subtitle: Text(
|
||||
completeLang(source.lang!),
|
||||
style: const TextStyle(fontSize: 10),
|
||||
|
|
@ -129,8 +135,7 @@ class SourceSearchScreen extends ConsumerWidget {
|
|||
itemBuilder: (context, index) {
|
||||
return MangaGlobalImageCard(
|
||||
manga: data[index]!,
|
||||
source: source.sourceName!,
|
||||
lang: source.lang!,
|
||||
source: source,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
@ -147,14 +152,13 @@ class SourceSearchScreen extends ConsumerWidget {
|
|||
}
|
||||
|
||||
class MangaGlobalImageCard extends ConsumerStatefulWidget {
|
||||
final GetManga manga;
|
||||
final String source;
|
||||
final String lang;
|
||||
final MangaModel manga;
|
||||
final Source source;
|
||||
|
||||
const MangaGlobalImageCard({
|
||||
super.key,
|
||||
required this.manga,
|
||||
required this.source,
|
||||
required this.lang,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -168,19 +172,22 @@ class _MangaGlobalImageCardState extends ConsumerState<MangaGlobalImageCard>
|
|||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final getMangaDetail = ref.watch(getMangaDetailProvider(
|
||||
source: widget.source, manga: widget.manga, lang: widget.lang));
|
||||
manga: widget.manga
|
||||
..lang = widget.source.lang
|
||||
..source = widget.source.name,
|
||||
source: widget.source));
|
||||
|
||||
return getMangaDetail.when(
|
||||
data: (data) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
pushToMangaReaderDetail(
|
||||
context: context, getManga: data, lang: widget.lang);
|
||||
context: context, getManga: data, lang: widget.source.lang!);
|
||||
},
|
||||
child: StreamBuilder(
|
||||
stream: isar.mangas
|
||||
.filter()
|
||||
.langEqualTo(widget.lang)
|
||||
.langEqualTo(widget.source.lang)
|
||||
.nameEqualTo(data.name)
|
||||
.sourceEqualTo(data.source)
|
||||
.favoriteEqualTo(true)
|
||||
|
|
@ -196,8 +203,8 @@ class _MangaGlobalImageCardState extends ConsumerState<MangaGlobalImageCard>
|
|||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: cachedNetworkImage(
|
||||
headers: ref.watch(
|
||||
headersProvider(source: data.source!)),
|
||||
headers: ref.watch(headersProvider(
|
||||
source: data.source!, lang: data.lang!)),
|
||||
imageUrl: data.imageUrl!,
|
||||
width: 100,
|
||||
height: 140,
|
||||
|
|
|
|||
99
lib/modules/browse/sources/sources_filter_screen.dart
Normal file
99
lib/modules/browse/sources/sources_filter_screen.dart
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:grouped_list/grouped_list.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
|
||||
import 'package:mangayomi/utils/lang.dart';
|
||||
|
||||
class SourcesFilterScreen extends ConsumerWidget {
|
||||
const SourcesFilterScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Sources"),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: StreamBuilder(
|
||||
stream: isar.sources
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.and()
|
||||
.sourceCodeIsNotEmpty()
|
||||
.watch(fireImmediately: true),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
|
||||
final entries = snapshot.data!
|
||||
.where((element) => ref.watch(showNSFWStateProvider)
|
||||
? true
|
||||
: element.isNsfw == false)
|
||||
.toList();
|
||||
|
||||
return GroupedListView<Source, String>(
|
||||
elements: entries,
|
||||
groupBy: (element) => element.lang!,
|
||||
groupSeparatorBuilder: (String groupByValue) =>
|
||||
SwitchListTile(
|
||||
value: entries
|
||||
.where((element) =>
|
||||
element.lang == groupByValue && element.isActive!)
|
||||
.isNotEmpty,
|
||||
onChanged: (val) {
|
||||
isar.writeTxnSync(() {
|
||||
for (var source in entries) {
|
||||
if (source.lang == groupByValue) {
|
||||
isar.sources
|
||||
.putSync(source..isActive = val == true);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
title: Text(
|
||||
completeLang(groupByValue),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, fontSize: 13),
|
||||
),
|
||||
),
|
||||
itemBuilder: (context, Source element) {
|
||||
if (entries
|
||||
.where(
|
||||
(ele) => ele.lang == element.lang && ele.isActive!)
|
||||
.isEmpty) {
|
||||
return Container();
|
||||
}
|
||||
return CheckboxListTile(
|
||||
secondary: Container(
|
||||
height: 37,
|
||||
width: 37,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.secondaryHeaderColor
|
||||
.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(5)),
|
||||
child: const Icon(Icons.source_outlined),
|
||||
),
|
||||
onChanged: (bool? value) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.sources.putSync(element..isAdded = value);
|
||||
});
|
||||
},
|
||||
value: element.isAdded!,
|
||||
title: Text(element.name!),
|
||||
);
|
||||
},
|
||||
groupComparator: (group1, group2) => group1.compareTo(group2),
|
||||
itemComparator: (item1, item2) =>
|
||||
item1.name!.compareTo(item2.name!),
|
||||
order: GroupedListOrder.ASC,
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ class SourcesScreen extends ConsumerWidget {
|
|||
groupComparator: (group1, group2) =>
|
||||
group1.compareTo(group2),
|
||||
itemComparator: (item1, item2) =>
|
||||
item1.sourceName!.compareTo(item2.sourceName!),
|
||||
item1.name!.compareTo(item2.name!),
|
||||
order: GroupedListOrder.ASC,
|
||||
);
|
||||
}),
|
||||
|
|
@ -110,7 +110,7 @@ class SourcesScreen extends ConsumerWidget {
|
|||
groupComparator: (group1, group2) =>
|
||||
group1.compareTo(group2),
|
||||
itemComparator: (item1, item2) =>
|
||||
item1.sourceName!.compareTo(item2.sourceName!),
|
||||
item1.name!.compareTo(item2.name!),
|
||||
order: GroupedListOrder.ASC,
|
||||
);
|
||||
}),
|
||||
|
|
@ -158,7 +158,7 @@ class SourcesScreen extends ConsumerWidget {
|
|||
groupComparator: (group1, group2) =>
|
||||
group1.compareTo(group2),
|
||||
itemComparator: (item1, item2) =>
|
||||
item1.sourceName!.compareTo(item2.sourceName!),
|
||||
item1.name!.compareTo(item2.name!),
|
||||
order: GroupedListOrder.ASC,
|
||||
);
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/manga_type.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/utils/colors.dart';
|
||||
import 'package:mangayomi/utils/lang.dart';
|
||||
|
|
@ -23,11 +22,7 @@ class SourceListTile extends StatelessWidget {
|
|||
}
|
||||
});
|
||||
|
||||
context.push('/mangaHome',
|
||||
extra: MangaType(
|
||||
isFullData: source.isFullData,
|
||||
lang: source.lang,
|
||||
source: source.sourceName));
|
||||
context.push('/mangaHome', extra: source);
|
||||
},
|
||||
leading: Container(
|
||||
height: 37,
|
||||
|
|
@ -41,7 +36,7 @@ class SourceListTile extends StatelessWidget {
|
|||
const Icon(Icons.source_outlined)
|
||||
// : CachedNetworkImage(
|
||||
// httpHeaders: ref.watch(
|
||||
// headersProvider(source: source.sourceName!)),
|
||||
// headersProvider(source: source.name!)),
|
||||
// imageUrl: source.logoUrl!,
|
||||
// fit: BoxFit.contain,
|
||||
// width: 37,
|
||||
|
|
@ -80,7 +75,7 @@ class SourceListTile extends StatelessWidget {
|
|||
)
|
||||
],
|
||||
),
|
||||
title: Text(source.sourceName!),
|
||||
title: Text(source.name!),
|
||||
trailing: IconButton(
|
||||
onPressed: () {
|
||||
isar.writeTxnSync(() =>
|
||||
|
|
|
|||
|
|
@ -179,7 +179,8 @@ class _HistoryScreenState extends ConsumerState<HistoryScreen> {
|
|||
manga.customCoverImage as Uint8List)
|
||||
: cachedNetworkImage(
|
||||
headers: ref.watch(headersProvider(
|
||||
source: manga.source!)),
|
||||
source: manga.source!,
|
||||
lang: manga.lang!)),
|
||||
imageUrl: manga.imageUrl!,
|
||||
width: 60,
|
||||
height: 90,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ final importArchivesFromDirectoryProvider =
|
|||
|
||||
typedef ImportArchivesFromDirectoryRef = AutoDisposeFutureProviderRef<dynamic>;
|
||||
String _$importArchivesFromFileHash() =>
|
||||
r'bc892e1fb32c5d5ec639ad5f76c27996605181f5';
|
||||
r'ee38771c056b3f15d856ed0b91cd559ab22dc236';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -64,8 +64,9 @@ class LibraryGridViewWidget extends StatelessWidget {
|
|||
as ImageProvider
|
||||
: CachedNetworkImageProvider(
|
||||
entriesManga[index].imageUrl!,
|
||||
headers: ref.watch(
|
||||
headersProvider(source: entriesManga[index].source!)),
|
||||
headers: ref.watch(headersProvider(
|
||||
source: entriesManga[index].source!,
|
||||
lang: entriesManga[index].lang!)),
|
||||
),
|
||||
onTap: () {
|
||||
if (isLongPressed) {
|
||||
|
|
|
|||
|
|
@ -107,8 +107,10 @@ class LibraryListViewWidget extends StatelessWidget {
|
|||
: CachedNetworkImageProvider(
|
||||
entriesManga[index].imageUrl!,
|
||||
headers: ref.watch(headersProvider(
|
||||
source: entriesManga[index]
|
||||
.source!)),
|
||||
source:
|
||||
entriesManga[index].source!,
|
||||
lang:
|
||||
entriesManga[index].lang!)),
|
||||
),
|
||||
child: InkWell(
|
||||
child: Container(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/services/get_manga_detail.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/manga_details_view.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/update_manga_detail_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/isar_providers.dart';
|
||||
import 'package:mangayomi/modules/widgets/error_text.dart';
|
||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||
|
|
@ -18,6 +15,23 @@ class MangaReaderDetail extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
|
||||
@override
|
||||
void initState() {
|
||||
_init();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
_init() async {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
await ref.read(
|
||||
updateMangaDetailProvider(mangaId: widget.mangaId, isInit: true)
|
||||
.future);
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
bool _isLoading = true;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final manga =
|
||||
|
|
@ -27,61 +41,25 @@ class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
|
|||
data: (manga) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
final mangaS = GetManga(
|
||||
genre: manga.genre!,
|
||||
author: manga.author,
|
||||
status: manga.status,
|
||||
chapters: manga.chapters.toList(),
|
||||
imageUrl: manga.imageUrl,
|
||||
description: manga.description,
|
||||
url: manga.link,
|
||||
name: manga.name,
|
||||
source: manga.source);
|
||||
bool isOk = false;
|
||||
ref
|
||||
.watch(getMangaDetailProvider(
|
||||
manga: mangaS,
|
||||
lang: manga.lang!,
|
||||
source: manga.source!,
|
||||
).future)
|
||||
.then((value) async {
|
||||
if (value.chapters.isNotEmpty &&
|
||||
value.chapters.length > manga.chapters.length) {
|
||||
await isar.writeTxn(() async {
|
||||
int newChapsIndex =
|
||||
value.chapters.length - manga.chapters.length;
|
||||
manga.lastUpdate = DateTime.now().millisecondsSinceEpoch;
|
||||
for (var i = 0; i < newChapsIndex; i++) {
|
||||
final chapters = Chapter(
|
||||
name: value.chapters[i].name,
|
||||
url: value.chapters[i].url,
|
||||
dateUpload: value.chapters[i].dateUpload,
|
||||
isBookmarked: false,
|
||||
scanlator: value.chapters[i].scanlator,
|
||||
isRead: false,
|
||||
lastPageRead: '',
|
||||
)..manga.value = manga;
|
||||
await isar.chapters.put(chapters);
|
||||
await chapters.manga.save();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
isOk = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
await Future.doWhile(() async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
if (isOk == true) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
await ref.read(
|
||||
updateMangaDetailProvider(mangaId: manga.id, isInit: false)
|
||||
.future);
|
||||
},
|
||||
child: MangaDetailsView(
|
||||
manga: manga!,
|
||||
child: Stack(
|
||||
children: [
|
||||
MangaDetailsView(
|
||||
manga: manga!,
|
||||
),
|
||||
if (_isLoading)
|
||||
const Positioned(
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -202,7 +202,8 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
fit: BoxFit.cover)
|
||||
: cachedNetworkImage(
|
||||
headers: ref.watch(headersProvider(
|
||||
source: widget.manga!.source!)),
|
||||
source: widget.manga!.source!,
|
||||
lang: widget.manga!.lang!)),
|
||||
imageUrl: widget.manga!.imageUrl!,
|
||||
width: mediaWidth(context, 1),
|
||||
height: 300,
|
||||
|
|
@ -365,11 +366,12 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
context.push("/categories");
|
||||
} else if (value == 1) {
|
||||
} else if (value == 2) {
|
||||
String url = getMangaAPIUrl(
|
||||
widget.manga!.source!)
|
||||
.isEmpty
|
||||
final source = getSource(widget.manga!.lang!,
|
||||
widget.manga!.source!);
|
||||
String url = source.apiUrl!.isEmpty
|
||||
? widget.manga!.link!
|
||||
: "${getMangaBaseUrl(widget.manga!.source!)}${widget.manga!.link!}";
|
||||
: "${source.baseUrl}${widget.manga!.link!}";
|
||||
|
||||
Share.share(url);
|
||||
}
|
||||
}),
|
||||
|
|
@ -426,14 +428,22 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
.spaceBetween
|
||||
: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets
|
||||
.symmetric(horizontal: 8),
|
||||
child: Text(
|
||||
'${chapters.length} chapters',
|
||||
style: const TextStyle(
|
||||
fontWeight:
|
||||
FontWeight.bold),
|
||||
Container(
|
||||
height: chapters.isEmpty
|
||||
? mediaHeight(context, 1)
|
||||
: null,
|
||||
color: Theme.of(context)
|
||||
.scaffoldBackgroundColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 8),
|
||||
child: Text(
|
||||
'${chapters.length} chapters',
|
||||
style: const TextStyle(
|
||||
fontWeight:
|
||||
FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isLocalArchive)
|
||||
|
|
@ -1045,7 +1055,6 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
Stack(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 180,
|
||||
width: mediaWidth(context, 1),
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
@ -1211,6 +1220,11 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
],
|
||||
),
|
||||
),
|
||||
if (chapterLength == 0)
|
||||
Container(
|
||||
width: mediaWidth(context, 1),
|
||||
height: mediaHeight(context, 1),
|
||||
color: Theme.of(context).scaffoldBackgroundColor)
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
@ -1223,7 +1237,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
as ImageProvider
|
||||
: CachedNetworkImageProvider(widget.manga!.imageUrl!);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 13),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 20),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_openImage(imageProvider);
|
||||
|
|
@ -1277,12 +1291,15 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
elevation: 0),
|
||||
onPressed: () {
|
||||
final manga = widget.manga!;
|
||||
String url = getMangaAPIUrl(manga.source!).isEmpty
|
||||
? manga.link!
|
||||
: "${getMangaBaseUrl(manga.source!)}${manga.link!}";
|
||||
final source =
|
||||
getSource(widget.manga!.lang!, widget.manga!.source!);
|
||||
String url = source.apiUrl!.isEmpty
|
||||
? widget.manga!.link!
|
||||
: "${source.baseUrl}${widget.manga!.link!}";
|
||||
|
||||
Map<String, String> data = {
|
||||
'url': url,
|
||||
'source': manga.source!,
|
||||
'sourceId': source.id.toString(),
|
||||
'title': manga.name!
|
||||
};
|
||||
context.push("/mangawebview", extra: data);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
import 'package:mangayomi/eval/bridge_class/model.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/services/get_manga_detail.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'update_manga_detail_providers.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<dynamic> updateMangaDetail(UpdateMangaDetailRef ref,
|
||||
{required int? mangaId, required bool isInit}) async {
|
||||
final manga = isar.mangas.getSync(mangaId!);
|
||||
if (manga!.chapters.isNotEmpty && isInit) {
|
||||
return;
|
||||
}
|
||||
final source = getSource(manga.lang!, manga.source!);
|
||||
final mangaS = MangaModel(
|
||||
name: manga.name,
|
||||
link: manga.link,
|
||||
imageUrl: manga.imageUrl,
|
||||
baseUrl: source.baseUrl,
|
||||
apiUrl: source.apiUrl,
|
||||
lang: manga.lang,
|
||||
dateFormat: source.dateFormat,
|
||||
dateFormatLocale: source.dateFormatLocale);
|
||||
final getManga = await ref
|
||||
.watch(getMangaDetailProvider(manga: mangaS, source: source).future);
|
||||
manga
|
||||
..imageUrl =
|
||||
getManga.imageUrl!.isEmpty ? manga.imageUrl ?? "" : getManga.imageUrl
|
||||
..name = getManga.name!.isEmpty
|
||||
? manga.name ?? ""
|
||||
: getManga.name!.trim().trimLeft().trimRight()
|
||||
..genre = getManga.genre!.isEmpty
|
||||
? manga.genre ?? []
|
||||
: getManga.genre!
|
||||
.map((e) => e.toString().trim().trimLeft().trimRight())
|
||||
.toList()
|
||||
..author = getManga.author!.isEmpty
|
||||
? manga.author ?? ""
|
||||
: getManga.author!.trim().trimLeft().trimRight()
|
||||
..status = switch (getManga.status) {
|
||||
0 => Status.ongoing,
|
||||
1 => Status.completed,
|
||||
2 => Status.onHiatus,
|
||||
3 => Status.canceled,
|
||||
4 => Status.publishingFinished,
|
||||
_ => Status.unknown,
|
||||
}
|
||||
..description = getManga.description!.isEmpty
|
||||
? manga.description ?? ""
|
||||
: getManga.description!.trim().trimLeft().trimRight()
|
||||
..link = getManga.link!.isEmpty ? manga.link ?? "" : getManga.link
|
||||
..source = getManga.source!.isEmpty ? manga.source ?? "" : getManga.source
|
||||
..lang = getManga.lang!.isEmpty ? manga.lang ?? "" : getManga.lang
|
||||
..lastUpdate = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.putSync(manga);
|
||||
if (getManga.names!.isNotEmpty &&
|
||||
getManga.names!.length > manga.chapters.length) {
|
||||
int newChapsIndex = getManga.names!.length - manga.chapters.length;
|
||||
manga.lastUpdate = DateTime.now().millisecondsSinceEpoch;
|
||||
for (var i = 0; i < newChapsIndex; i++) {
|
||||
String title = "";
|
||||
String scanlator = "";
|
||||
if (getManga.chaptersChaps != null &&
|
||||
getManga.chaptersVolumes != null) {
|
||||
title = beautifyChapterName(getManga.chaptersVolumes![i],
|
||||
getManga.chaptersChaps![i], getManga.names![i], getManga.lang!);
|
||||
} else {
|
||||
title = getManga.names![i].trim().trimLeft().trimRight();
|
||||
}
|
||||
if (getManga.chaptersScanlators != null) {
|
||||
scanlator = getManga.chaptersScanlators![i]
|
||||
.toString()
|
||||
.replaceAll(']', "")
|
||||
.replaceAll("[", "");
|
||||
}
|
||||
final chapters = Chapter(
|
||||
name: title,
|
||||
url: getManga.urls![i].trim().trimLeft().trimRight(),
|
||||
dateUpload: getManga.chaptersDateUploads![i],
|
||||
scanlator: scanlator,
|
||||
)..manga.value = manga;
|
||||
isar.chapters.putSync(chapters);
|
||||
chapters.manga.saveSync();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
String beautifyChapterName(String vol, String chap, String title, String lang) {
|
||||
String result = "";
|
||||
|
||||
if (vol.trim().trimLeft().trimRight() != "null" && vol.isNotEmpty) {
|
||||
if (chap.trim().trimLeft().trimRight() != "null" && chap.isEmpty) {
|
||||
result += "Volume ${vol.trim().trimLeft().trimRight()} ";
|
||||
} else {
|
||||
result += "Vol. ${vol.trim().trimLeft().trimRight()} ";
|
||||
}
|
||||
}
|
||||
|
||||
if (chap.trim().trimLeft().trimRight() != "null" && chap.isNotEmpty) {
|
||||
if (vol.trim().trimLeft().trimRight() != "null" && vol.isEmpty) {
|
||||
if (lang.trim().trimLeft().trimRight() != "null" && lang == "fr") {
|
||||
result += "Chapitre ${chap.trim().trimLeft().trimRight()}";
|
||||
} else {
|
||||
result += "Chapter ${chap.trim().trimLeft().trimRight()}";
|
||||
}
|
||||
} else {
|
||||
result += "Ch. ${chap.trim().trimLeft().trimRight()} ";
|
||||
}
|
||||
}
|
||||
|
||||
if (title.trim().trimLeft().trimRight() != "null" && title.isNotEmpty) {
|
||||
if (chap.trim().trimLeft().trimRight() != "null" && chap.isEmpty) {
|
||||
result += title.trim().trimLeft().trimRight();
|
||||
} else {
|
||||
result += " : ${title.trim().trimLeft().trimRight()}";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'update_manga_detail_providers.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$updateMangaDetailHash() => r'80479a109b0beb0e0d57d93a32a587bbf41d5bc0';
|
||||
|
||||
/// 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 UpdateMangaDetailRef = AutoDisposeFutureProviderRef<dynamic>;
|
||||
|
||||
/// See also [updateMangaDetail].
|
||||
@ProviderFor(updateMangaDetail)
|
||||
const updateMangaDetailProvider = UpdateMangaDetailFamily();
|
||||
|
||||
/// See also [updateMangaDetail].
|
||||
class UpdateMangaDetailFamily extends Family<AsyncValue<dynamic>> {
|
||||
/// See also [updateMangaDetail].
|
||||
const UpdateMangaDetailFamily();
|
||||
|
||||
/// See also [updateMangaDetail].
|
||||
UpdateMangaDetailProvider call({
|
||||
required int? mangaId,
|
||||
required bool isInit,
|
||||
}) {
|
||||
return UpdateMangaDetailProvider(
|
||||
mangaId: mangaId,
|
||||
isInit: isInit,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
UpdateMangaDetailProvider getProviderOverride(
|
||||
covariant UpdateMangaDetailProvider provider,
|
||||
) {
|
||||
return call(
|
||||
mangaId: provider.mangaId,
|
||||
isInit: provider.isInit,
|
||||
);
|
||||
}
|
||||
|
||||
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'updateMangaDetailProvider';
|
||||
}
|
||||
|
||||
/// See also [updateMangaDetail].
|
||||
class UpdateMangaDetailProvider extends AutoDisposeFutureProvider<dynamic> {
|
||||
/// See also [updateMangaDetail].
|
||||
UpdateMangaDetailProvider({
|
||||
required this.mangaId,
|
||||
required this.isInit,
|
||||
}) : super.internal(
|
||||
(ref) => updateMangaDetail(
|
||||
ref,
|
||||
mangaId: mangaId,
|
||||
isInit: isInit,
|
||||
),
|
||||
from: updateMangaDetailProvider,
|
||||
name: r'updateMangaDetailProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$updateMangaDetailHash,
|
||||
dependencies: UpdateMangaDetailFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
UpdateMangaDetailFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final int? mangaId;
|
||||
final bool isInit;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is UpdateMangaDetailProvider &&
|
||||
other.mangaId == mangaId &&
|
||||
other.isInit == isInit;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, mangaId.hashCode);
|
||||
hash = _SystemHash.combine(hash, isInit.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
|
||||
|
|
@ -17,63 +17,65 @@ class ReadMoreWidgetState extends State<ReadMoreWidget>
|
|||
late bool expanded = widget.text.trim().length < 232;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||
child: ExpandableText(
|
||||
animationDuration: const Duration(milliseconds: 500),
|
||||
onExpandedChanged: (value) {
|
||||
setState(() => expanded = value);
|
||||
widget.onChanged(value);
|
||||
},
|
||||
expandOnTextTap: true,
|
||||
widget.text.trim(),
|
||||
expandText: '',
|
||||
maxLines: 3,
|
||||
expanded: expanded,
|
||||
linkColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
animation: true,
|
||||
collapseOnTextTap: true,
|
||||
prefixText: '',
|
||||
),
|
||||
),
|
||||
if (!expanded)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
child: Container(
|
||||
width: mediaWidth(context, 1),
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Theme.of(context)
|
||||
.scaffoldBackgroundColor
|
||||
.withOpacity(0.2),
|
||||
Theme.of(context).scaffoldBackgroundColor
|
||||
],
|
||||
stops: const [0, .9],
|
||||
return widget.text.isEmpty
|
||||
? const Text("No description")
|
||||
: Column(
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||
child: ExpandableText(
|
||||
animationDuration: const Duration(milliseconds: 500),
|
||||
onExpandedChanged: (value) {
|
||||
setState(() => expanded = value);
|
||||
widget.onChanged(value);
|
||||
},
|
||||
expandOnTextTap: true,
|
||||
widget.text.trim(),
|
||||
expandText: '',
|
||||
maxLines: 3,
|
||||
expanded: expanded,
|
||||
linkColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
animation: true,
|
||||
collapseOnTextTap: true,
|
||||
prefixText: '',
|
||||
),
|
||||
),
|
||||
child: const Icon(Icons.keyboard_arrow_down_sharp),
|
||||
),
|
||||
if (!expanded)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
child: Container(
|
||||
width: mediaWidth(context, 1),
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Theme.of(context)
|
||||
.scaffoldBackgroundColor
|
||||
.withOpacity(0.2),
|
||||
Theme.of(context).scaffoldBackgroundColor
|
||||
],
|
||||
stops: const [0, .9],
|
||||
),
|
||||
),
|
||||
child: const Icon(Icons.keyboard_arrow_down_sharp),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
if (expanded)
|
||||
SizedBox(
|
||||
width: mediaWidth(context, 1),
|
||||
height: 20,
|
||||
child: const Icon(Icons.keyboard_arrow_up_sharp),
|
||||
)
|
||||
],
|
||||
);
|
||||
if (expanded)
|
||||
SizedBox(
|
||||
width: mediaWidth(context, 1),
|
||||
height: 20,
|
||||
child: const Icon(Icons.keyboard_arrow_up_sharp),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,8 +95,9 @@ Future<List<String>> downloadChapter(
|
|||
} else {
|
||||
tasks.add(DownloadTask(
|
||||
taskId: pageUrls[index],
|
||||
headers: ref.watch(headersProvider(source: manga.source!)),
|
||||
url: pageUrls[index],
|
||||
headers: ref.watch(
|
||||
headersProvider(source: manga.source!, lang: manga.lang!)),
|
||||
url: pageUrls[index].trim().trimLeft().trimRight(),
|
||||
filename: "${padIndex(index + 1)}.jpg",
|
||||
baseDirectory:
|
||||
Platform.isAndroid || desktopCustomDownloadLocation
|
||||
|
|
@ -114,8 +115,9 @@ Future<List<String>> downloadChapter(
|
|||
} else {
|
||||
tasks.add(DownloadTask(
|
||||
taskId: pageUrls[index],
|
||||
headers: ref.watch(headersProvider(source: manga.source!)),
|
||||
url: pageUrls[index],
|
||||
headers: ref.watch(
|
||||
headersProvider(source: manga.source!, lang: manga.lang!)),
|
||||
url: pageUrls[index].trim().trimLeft().trimRight(),
|
||||
filename: "${padIndex(index + 1)}.jpg",
|
||||
baseDirectory:
|
||||
Platform.isAndroid || desktopCustomDownloadLocation
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'download_provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$downloadChapterHash() => r'1ddc8d7c1d9b1f3fdea4ba1b4e8252a61597a616';
|
||||
String _$downloadChapterHash() => r'2af3ee0a570e8521336179f60dc1f9f2be919b9b';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -2,25 +2,21 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mangayomi/models/manga_type.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/model.dart';
|
||||
import 'package:mangayomi/services/get_latest_updates_manga.dart';
|
||||
import 'package:mangayomi/services/get_manga_detail.dart';
|
||||
import 'package:mangayomi/services/get_popular_manga.dart';
|
||||
import 'package:mangayomi/services/search_manga.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:mangayomi/utils/colors.dart';
|
||||
import 'package:mangayomi/utils/media_query.dart';
|
||||
import 'package:mangayomi/modules/library/search_text_form_field.dart';
|
||||
import 'package:mangayomi/modules/manga/home/widget/mangas_card_selector.dart';
|
||||
import 'package:mangayomi/modules/widgets/bottom_text_widget.dart';
|
||||
import 'package:mangayomi/modules/widgets/cover_view_widget.dart';
|
||||
import 'package:mangayomi/modules/widgets/gridview_widget.dart';
|
||||
import 'package:mangayomi/modules/widgets/manga_image_card_widget.dart';
|
||||
|
||||
class MangaHomeScreen extends ConsumerStatefulWidget {
|
||||
final MangaType mangaType;
|
||||
const MangaHomeScreen({required this.mangaType, super.key});
|
||||
final Source source;
|
||||
const MangaHomeScreen({required this.source, super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<MangaHomeScreen> createState() => _MangaHomeScreenState();
|
||||
|
|
@ -49,30 +45,26 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
final _textEditingController = TextEditingController();
|
||||
String _query = "";
|
||||
bool _isSearch = false;
|
||||
AsyncValue<List<GetManga?>>? _getManga;
|
||||
AsyncValue<List<MangaModel?>>? _getManga;
|
||||
int _length = 0;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_selectedIndex == 2 && _isSearch && _query.isNotEmpty) {
|
||||
_getManga = ref.watch(searchMangaProvider(
|
||||
source: widget.mangaType.source!,
|
||||
query: _query,
|
||||
lang: widget.mangaType.lang!));
|
||||
_getManga = ref.watch(
|
||||
searchMangaProvider(source: widget.source, query: _query, page: 1));
|
||||
} else if (_selectedIndex == 1 && !_isSearch && _query.isEmpty) {
|
||||
_getManga = ref.watch(getLatestUpdatesMangaProvider(
|
||||
source: widget.mangaType.source!,
|
||||
page: 1,
|
||||
lang: widget.mangaType.lang!));
|
||||
_getManga = ref
|
||||
.watch(getLatestUpdatesMangaProvider(source: widget.source, page: 1));
|
||||
} else if (_selectedIndex == 0 && !_isSearch && _query.isEmpty) {
|
||||
_getManga = ref.watch(getPopularMangaProvider(
|
||||
source: widget.mangaType.source!,
|
||||
page: 1,
|
||||
lang: widget.mangaType.lang!));
|
||||
source: widget.source,
|
||||
page: 1,
|
||||
));
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: _isSearch ? null : Text('${widget.mangaType.source}'),
|
||||
title: _isSearch ? null : Text('${widget.source.name}'),
|
||||
actions: [
|
||||
_isSearch
|
||||
? SeachFormTextField(
|
||||
|
|
@ -80,6 +72,7 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
if (submit.isNotEmpty) {
|
||||
setState(() {
|
||||
_selectedIndex = 2;
|
||||
|
||||
_query = submit;
|
||||
});
|
||||
} else {
|
||||
|
|
@ -87,6 +80,7 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
_selectedIndex = 0;
|
||||
});
|
||||
}
|
||||
_page = 1;
|
||||
},
|
||||
onChanged: (value) {},
|
||||
onSuffixPressed: () {
|
||||
|
|
@ -98,6 +92,7 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
_isSearch = false;
|
||||
_query = "";
|
||||
_selectedIndex = 0;
|
||||
_page = 1;
|
||||
});
|
||||
_textEditingController.clear();
|
||||
},
|
||||
|
|
@ -115,8 +110,8 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
IconButton(
|
||||
onPressed: () {
|
||||
Map<String, String> data = {
|
||||
'url': getMangaBaseUrl(widget.mangaType.source!),
|
||||
'source': widget.mangaType.source!,
|
||||
'url': widget.source.baseUrl!,
|
||||
'sourceId': widget.source.id.toString(),
|
||||
'title': ''
|
||||
};
|
||||
context.push("/mangawebview", extra: data);
|
||||
|
|
@ -147,6 +142,7 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
onPressed: () {
|
||||
setState(() {
|
||||
_selectedIndex = index;
|
||||
_page = 1;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
@ -191,7 +187,7 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
});
|
||||
}
|
||||
|
||||
if (widget.mangaType.isFullData!) {
|
||||
if (widget.source.isFullData!) {
|
||||
Future.delayed(const Duration(seconds: 1))
|
||||
.then((value) {
|
||||
_fullDataLength = _fullDataLength + 20;
|
||||
|
|
@ -207,35 +203,75 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
_page = _page + 1;
|
||||
});
|
||||
}
|
||||
ref
|
||||
.watch(getPopularMangaProvider(
|
||||
source:
|
||||
widget.mangaType.source!,
|
||||
page: _page,
|
||||
lang: widget.mangaType.lang!)
|
||||
.future)
|
||||
.then(
|
||||
(value) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
data.addAll(value);
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
if (_selectedIndex == 0 &&
|
||||
!_isSearch &&
|
||||
_query.isEmpty) {
|
||||
ref
|
||||
.watch(getPopularMangaProvider(
|
||||
source: widget.source,
|
||||
page: _page,
|
||||
).future)
|
||||
.then(
|
||||
(value) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
data.addAll(value);
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
} else if (_selectedIndex == 1 &&
|
||||
!_isSearch &&
|
||||
_query.isEmpty) {
|
||||
ref
|
||||
.watch(
|
||||
getLatestUpdatesMangaProvider(
|
||||
source: widget.source,
|
||||
page: _page,
|
||||
).future)
|
||||
.then(
|
||||
(value) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
data.addAll(value);
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
} else if (_selectedIndex == 2 &&
|
||||
_isSearch &&
|
||||
_query.isNotEmpty) {
|
||||
ref
|
||||
.watch(searchMangaProvider(
|
||||
source: widget.source,
|
||||
query: _query,
|
||||
page: _page,
|
||||
).future)
|
||||
.then(
|
||||
(value) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
data.addAll(value);
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_length = widget.mangaType.isFullData!
|
||||
_length = widget.source.isFullData!
|
||||
? _fullDataLength
|
||||
: data.length;
|
||||
},
|
||||
child: const Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text("Click to load more"),
|
||||
Text("Load more"),
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
|
|
@ -260,7 +296,7 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
});
|
||||
}
|
||||
|
||||
if (widget.mangaType.isFullData!) {
|
||||
if (widget.source.isFullData!) {
|
||||
Future.delayed(const Duration(seconds: 1)).then((value) {
|
||||
_fullDataLength = _fullDataLength + 10;
|
||||
if (mounted) {
|
||||
|
|
@ -275,30 +311,67 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
_page = _page + 1;
|
||||
});
|
||||
}
|
||||
ref
|
||||
.watch(getPopularMangaProvider(
|
||||
source: widget.mangaType.source!,
|
||||
page: _page,
|
||||
lang: widget.mangaType.lang!)
|
||||
.future)
|
||||
.then(
|
||||
(value) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
data.addAll(value);
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
if (_selectedIndex == 0 && !_isSearch && _query.isEmpty) {
|
||||
ref
|
||||
.watch(getPopularMangaProvider(
|
||||
source: widget.source,
|
||||
page: _page,
|
||||
).future)
|
||||
.then(
|
||||
(value) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
data.addAll(value);
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
} else if (_selectedIndex == 1 &&
|
||||
!_isSearch &&
|
||||
_query.isEmpty) {
|
||||
ref
|
||||
.watch(getLatestUpdatesMangaProvider(
|
||||
source: widget.source,
|
||||
page: _page,
|
||||
).future)
|
||||
.then(
|
||||
(value) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
data.addAll(value);
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
} else if (_selectedIndex == 2 &&
|
||||
_isSearch &&
|
||||
_query.isNotEmpty) {
|
||||
ref
|
||||
.watch(searchMangaProvider(
|
||||
source: widget.source,
|
||||
query: _query,
|
||||
page: _page,
|
||||
).future)
|
||||
.then(
|
||||
(value) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
data.addAll(value);
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_length =
|
||||
widget.mangaType.isFullData! ? _fullDataLength : data.length;
|
||||
_length = widget.source.isFullData! ? _fullDataLength : data.length;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
|
|
@ -313,8 +386,7 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
}
|
||||
return MangaHomeImageCard(
|
||||
manga: data[index]!,
|
||||
source: widget.mangaType.source!,
|
||||
lang: widget.mangaType.lang!,
|
||||
source: widget.source,
|
||||
);
|
||||
},
|
||||
)),
|
||||
|
|
@ -331,14 +403,12 @@ class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
|
|||
}
|
||||
|
||||
class MangaHomeImageCard extends ConsumerStatefulWidget {
|
||||
final GetManga manga;
|
||||
final String source;
|
||||
final String lang;
|
||||
final MangaModel manga;
|
||||
final Source source;
|
||||
const MangaHomeImageCard({
|
||||
super.key,
|
||||
required this.manga,
|
||||
required this.source,
|
||||
required this.lang,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -350,28 +420,12 @@ class _MangaHomeImageCardState extends ConsumerState<MangaHomeImageCard>
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final getMangaDetail = ref.watch(getMangaDetailProvider(
|
||||
source: widget.source, manga: widget.manga, lang: widget.lang));
|
||||
|
||||
return getMangaDetail.when(
|
||||
data: (data) {
|
||||
return MangaImageCardWidget(
|
||||
getMangaDetail: data,
|
||||
lang: widget.lang,
|
||||
);
|
||||
},
|
||||
loading: () => CoverViewWidget(onTap: () {}, children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
child: Container(
|
||||
color: Theme.of(context).cardColor,
|
||||
),
|
||||
),
|
||||
BottomTextWidget(
|
||||
text: widget.manga.name!,
|
||||
)
|
||||
]),
|
||||
error: (error, stackTrace) => Center(child: Text(error.toString())),
|
||||
return MangaImageCardWidget(
|
||||
getMangaDetail: widget.manga
|
||||
..lang = widget.source.lang
|
||||
..source = widget.source.name,
|
||||
lang: widget.source.lang!,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,108 +1,25 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/services/search_manga.dart';
|
||||
import 'package:mangayomi/modules/manga/home/manga_home_screen.dart';
|
||||
import 'package:mangayomi/modules/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 SearchResultScreen(
|
||||
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 SearchResultScreen extends ConsumerWidget {
|
||||
final String query;
|
||||
final String source;
|
||||
final String lang;
|
||||
final Source source;
|
||||
final bool viewOnly;
|
||||
const SearchResultScreen({
|
||||
super.key,
|
||||
required this.query,
|
||||
required this.source,
|
||||
required this.lang,
|
||||
this.viewOnly = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final search = ref
|
||||
.watch(searchMangaProvider(source: source, query: query, lang: lang));
|
||||
final search =
|
||||
ref.watch(searchMangaProvider(source: source, query: query, page: 1));
|
||||
return Scaffold(
|
||||
appBar: viewOnly
|
||||
? AppBar(
|
||||
|
|
@ -122,7 +39,6 @@ class SearchResultScreen extends ConsumerWidget {
|
|||
return MangaHomeImageCard(
|
||||
manga: data[index]!,
|
||||
source: source,
|
||||
lang: lang,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,19 +23,19 @@ class ChapterIntervalPageView extends ConsumerWidget {
|
|||
final readerController =
|
||||
ReaderController(chapter: uChapDataPreload.chapter!);
|
||||
|
||||
String text = uChapDataPreload.isPrevPrePage && hasPrevChapter
|
||||
String text = uChapDataPreload.hasPrevPrePage && hasPrevChapter
|
||||
? "Current:"
|
||||
: "Finished:";
|
||||
final noMoreChapter = uChapDataPreload.isNextPrePage && !hasNextChapter ||
|
||||
uChapDataPreload.isPrevPrePage && !hasPrevChapter;
|
||||
final noMoreChapter = uChapDataPreload.hasNextPrePage && !hasNextChapter ||
|
||||
uChapDataPreload.hasPrevPrePage && !hasPrevChapter;
|
||||
String noMore =
|
||||
uChapDataPreload.isNextPrePage && !hasNextChapter ? "Next" : "Previous";
|
||||
uChapDataPreload.hasNextPrePage && !hasNextChapter ? "Next" : "Previous";
|
||||
return SizedBox(
|
||||
height: mediaHeight(context, 0.27),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (uChapDataPreload.isPrevPrePage && hasPrevChapter)
|
||||
if (uChapDataPreload.hasPrevPrePage && hasPrevChapter)
|
||||
Column(
|
||||
children: [
|
||||
const Text(
|
||||
|
|
@ -89,7 +89,7 @@ class ChapterIntervalPageView extends ConsumerWidget {
|
|||
style: const TextStyle(color: Colors.white, fontSize: 14),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (uChapDataPreload.isNextPrePage && hasNextChapter)
|
||||
if (uChapDataPreload.hasNextPrePage && hasNextChapter)
|
||||
Column(
|
||||
children: [
|
||||
Column(
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:mangayomi/utils/headers.dart';
|
|||
import 'package:mangayomi/utils/reg_exp_matcher.dart';
|
||||
|
||||
class ImageViewCenter extends ConsumerWidget {
|
||||
final String lang;
|
||||
final int length;
|
||||
final String url;
|
||||
final int index;
|
||||
|
|
@ -33,6 +34,7 @@ class ImageViewCenter extends ConsumerWidget {
|
|||
required this.onDoubleTap,
|
||||
required this.initGestureConfigHandler,
|
||||
required this.isLocale,
|
||||
required this.lang,
|
||||
this.archiveImage,
|
||||
});
|
||||
|
||||
|
|
@ -59,8 +61,8 @@ class ImageViewCenter extends ConsumerWidget {
|
|||
loadStateChanged: loadStateChanged,
|
||||
)
|
||||
: ExtendedImage.network(
|
||||
url,
|
||||
headers: ref.watch(headersProvider(source: source)),
|
||||
url.trim().trimLeft().trimRight(),
|
||||
headers: ref.watch(headersProvider(source: source, lang: lang)),
|
||||
enableMemoryCache: true,
|
||||
mode: ExtendedImageMode.gesture,
|
||||
cacheMaxAge: const Duration(days: 7),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class ImageViewVertical extends ConsumerWidget {
|
|||
final String source;
|
||||
final String chapter;
|
||||
final Directory path;
|
||||
final String lang;
|
||||
final Uint8List? archiveImage;
|
||||
|
||||
const ImageViewVertical({
|
||||
|
|
@ -29,6 +30,7 @@ class ImageViewVertical extends ConsumerWidget {
|
|||
required this.source,
|
||||
required this.length,
|
||||
required this.isLocale,
|
||||
required this.lang,
|
||||
this.archiveImage,
|
||||
});
|
||||
|
||||
|
|
@ -46,8 +48,7 @@ class ImageViewVertical extends ConsumerWidget {
|
|||
isLocale
|
||||
? archiveImage != null
|
||||
? ExtendedImage.memory(archiveImage!,
|
||||
fit: BoxFit.contain,
|
||||
enableMemoryCache: false,
|
||||
fit: BoxFit.contain, enableMemoryCache: false,
|
||||
loadStateChanged: (ExtendedImageState state) {
|
||||
if (state.extendedImageLoadState == LoadState.loading) {
|
||||
return Container(
|
||||
|
|
@ -70,8 +71,9 @@ class ImageViewVertical extends ConsumerWidget {
|
|||
}
|
||||
return null;
|
||||
})
|
||||
: ExtendedImage.network(url,
|
||||
headers: ref.watch(headersProvider(source: source)),
|
||||
: ExtendedImage.network(url.trim().trimLeft().trimRight(),
|
||||
headers:
|
||||
ref.watch(headersProvider(source: source, lang: lang)),
|
||||
handleLoadingProgress: true,
|
||||
cacheMaxAge: const Duration(days: 7),
|
||||
fit: BoxFit.contain,
|
||||
|
|
|
|||
|
|
@ -273,8 +273,8 @@ class _MangaChapterPageGalleryState
|
|||
}
|
||||
|
||||
void _onPageChanged(int index) {
|
||||
if (!(_uChapDataPreload[index].isNextPrePage ||
|
||||
_uChapDataPreload[index].isPrevPrePage)) {
|
||||
if (!(_uChapDataPreload[index].hasNextPrePage ||
|
||||
_uChapDataPreload[index].hasPrevPrePage)) {
|
||||
_readerController =
|
||||
ReaderController(chapter: _uChapDataPreload[index].chapter!);
|
||||
_chapterUrlModel = _uChapDataPreload[_posIndex ?? 0].chapterUrlModel!;
|
||||
|
|
@ -572,12 +572,13 @@ class _MangaChapterPageGalleryState
|
|||
IconButton(
|
||||
onPressed: () {
|
||||
final manga = chapter.manga.value!;
|
||||
String url = getMangaAPIUrl(manga.source!).isEmpty
|
||||
? manga.link!
|
||||
: "${getMangaBaseUrl(manga.source!)}${manga.link!}";
|
||||
final source = getSource(manga.lang!, manga.source!);
|
||||
String url = source.apiUrl!.isEmpty
|
||||
? chapter.url!
|
||||
: "${source.baseUrl}/${chapter.url!}";
|
||||
Map<String, String> data = {
|
||||
'url': url,
|
||||
'source': manga.source!,
|
||||
'sourceId': source.id.toString(),
|
||||
'title': chapter.name!
|
||||
};
|
||||
context.push("/mangawebview", extra: data);
|
||||
|
|
@ -1063,8 +1064,8 @@ class _MangaChapterPageGalleryState
|
|||
final model = resultMap[_listViewContext];
|
||||
if (model == null) return;
|
||||
_posIndex = model.firstChild?.index ?? 0;
|
||||
if (!(_uChapDataPreload[_posIndex ?? 0].isNextPrePage ||
|
||||
_uChapDataPreload[_posIndex ?? 0].isPrevPrePage)) {
|
||||
if (!(_uChapDataPreload[_posIndex ?? 0].hasNextPrePage ||
|
||||
_uChapDataPreload[_posIndex ?? 0].hasPrevPrePage)) {
|
||||
_readerController = ReaderController(
|
||||
chapter:
|
||||
_uChapDataPreload[_posIndex ?? 0].chapter!);
|
||||
|
|
@ -1100,18 +1101,18 @@ class _MangaChapterPageGalleryState
|
|||
_scrollController.addListener(() {
|
||||
if (_scrollController.position.pixels ==
|
||||
_scrollController.position.maxScrollExtent) {
|
||||
if (_uChapDataPreload[index].isNextPrePage ||
|
||||
_uChapDataPreload[index].isPrevPrePage) {
|
||||
if (_uChapDataPreload[index].hasNextPrePage ||
|
||||
_uChapDataPreload[index].hasPrevPrePage) {
|
||||
bool hasPrevChapter =
|
||||
_readerController.getChapterIndex() + 1 !=
|
||||
_readerController.getChaptersLength();
|
||||
bool hasNextChapter =
|
||||
_readerController.getChapterIndex() != 0;
|
||||
final chapter = _uChapDataPreload[index]
|
||||
.isNextPrePage &&
|
||||
.hasNextPrePage &&
|
||||
hasNextChapter
|
||||
? _readerController.getNextChapter()
|
||||
: _uChapDataPreload[index].isPrevPrePage &&
|
||||
: _uChapDataPreload[index].hasPrevPrePage &&
|
||||
hasPrevChapter
|
||||
? _readerController.getPrevChapter()
|
||||
: null;
|
||||
|
|
@ -1122,7 +1123,7 @@ class _MangaChapterPageGalleryState
|
|||
).future)
|
||||
.then((value) {
|
||||
if (_uChapDataPreload[index]
|
||||
.isNextPrePage) {
|
||||
.hasNextPrePage) {
|
||||
_preloadNextChapter(value, chapter);
|
||||
} else {
|
||||
_preloadPrevChapter(value, chapter);
|
||||
|
|
@ -1132,18 +1133,18 @@ class _MangaChapterPageGalleryState
|
|||
}
|
||||
}
|
||||
});
|
||||
if (_uChapDataPreload[index].isNextPrePage ||
|
||||
_uChapDataPreload[index].isPrevPrePage) {
|
||||
if (_uChapDataPreload[index].hasNextPrePage ||
|
||||
_uChapDataPreload[index].hasPrevPrePage) {
|
||||
bool hasPrevChapter =
|
||||
_readerController.getChapterIndex() + 1 !=
|
||||
_readerController.getChaptersLength();
|
||||
bool hasNextChapter =
|
||||
_readerController.getChapterIndex() != 0;
|
||||
final chapter =
|
||||
_uChapDataPreload[index].isNextPrePage &&
|
||||
_uChapDataPreload[index].hasNextPrePage &&
|
||||
hasNextChapter
|
||||
? _readerController.getNextChapter()
|
||||
: _uChapDataPreload[index].isPrevPrePage &&
|
||||
: _uChapDataPreload[index].hasPrevPrePage &&
|
||||
hasPrevChapter
|
||||
? _readerController.getPrevChapter()
|
||||
: null;
|
||||
|
|
@ -1186,6 +1187,11 @@ class _MangaChapterPageGalleryState
|
|||
cropBorders == true
|
||||
? true
|
||||
: _uChapDataPreload[index].isLocale!,
|
||||
lang: _uChapDataPreload[index]
|
||||
.chapter!
|
||||
.manga
|
||||
.value!
|
||||
.lang!,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
@ -1212,18 +1218,18 @@ class _MangaChapterPageGalleryState
|
|||
: true;
|
||||
},
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (_uChapDataPreload[index].isNextPrePage ||
|
||||
_uChapDataPreload[index].isPrevPrePage) {
|
||||
if (_uChapDataPreload[index].hasNextPrePage ||
|
||||
_uChapDataPreload[index].hasPrevPrePage) {
|
||||
bool hasPrevChapter =
|
||||
_readerController.getChapterIndex() + 1 !=
|
||||
_readerController.getChaptersLength();
|
||||
bool hasNextChapter =
|
||||
_readerController.getChapterIndex() != 0;
|
||||
final chapter =
|
||||
_uChapDataPreload[index].isNextPrePage &&
|
||||
_uChapDataPreload[index].hasNextPrePage &&
|
||||
hasNextChapter
|
||||
? _readerController.getNextChapter()
|
||||
: _uChapDataPreload[index].isPrevPrePage &&
|
||||
: _uChapDataPreload[index].hasPrevPrePage &&
|
||||
hasPrevChapter
|
||||
? _readerController.getPrevChapter()
|
||||
: null;
|
||||
|
|
@ -1393,6 +1399,11 @@ class _MangaChapterPageGalleryState
|
|||
_cropImagesList.isNotEmpty && cropBorders == true
|
||||
? true
|
||||
: _uChapDataPreload[index].isLocale!,
|
||||
lang: _uChapDataPreload[index]
|
||||
.chapter!
|
||||
.manga
|
||||
.value!
|
||||
.lang!,
|
||||
);
|
||||
},
|
||||
itemCount: _uChapDataPreload.length,
|
||||
|
|
@ -1466,8 +1477,8 @@ class UChapDataPreload {
|
|||
final bool? isLocale;
|
||||
final Uint8List? archiveImage;
|
||||
final int? index;
|
||||
final bool isNextPrePage;
|
||||
final bool isPrevPrePage;
|
||||
final bool hasNextPrePage;
|
||||
final bool hasPrevPrePage;
|
||||
final GetChapterUrlModel? chapterUrlModel;
|
||||
UChapDataPreload(
|
||||
this.chapter,
|
||||
|
|
@ -1476,8 +1487,8 @@ class UChapDataPreload {
|
|||
this.isLocale,
|
||||
this.archiveImage,
|
||||
this.index,
|
||||
this.isNextPrePage,
|
||||
this.isPrevPrePage,
|
||||
this.hasNextPrePage,
|
||||
this.hasPrevPrePage,
|
||||
this.chapterUrlModel,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'reader_controller_provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$currentIndexHash() => r'c2b912af925d9efd3e36e7a810914ef11393c1da';
|
||||
String _$currentIndexHash() => r'a5103d732efaeec89b719a14e060ac7aab16c8f4';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@
|
|||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/services/http_service/cloudflare/cloudflare_bypass.dart';
|
||||
import 'package:mangayomi/services/http_service/cloudflare/cookie.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
|
@ -19,12 +17,12 @@ import 'package:path_provider/path_provider.dart';
|
|||
|
||||
class MangaWebView extends ConsumerStatefulWidget {
|
||||
final String url;
|
||||
final String source;
|
||||
final String sourceId;
|
||||
final String title;
|
||||
const MangaWebView({
|
||||
super.key,
|
||||
required this.url,
|
||||
required this.source,
|
||||
required this.sourceId,
|
||||
required this.title,
|
||||
});
|
||||
|
||||
|
|
@ -226,7 +224,7 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
|
|||
onUpdateVisitedHistory:
|
||||
(controller, url, isReload) async {
|
||||
await ref.watch(
|
||||
setCookieProvider(widget.source, url.toString())
|
||||
setCookieProvider(widget.sourceId, url.toString())
|
||||
.future);
|
||||
final canGoback = await controller.canGoBack();
|
||||
final canGoForward = await controller.canGoForward();
|
||||
|
|
@ -239,7 +237,7 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
|
|||
});
|
||||
},
|
||||
initialSettings: InAppWebViewSettings(
|
||||
contentBlockers: adsContentBlockers(),
|
||||
// contentBlockers: adsContentBlockers(),
|
||||
userAgent: isar.settings.getSync(227)!.userAgent!),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: WebUri.uri(Uri.parse(widget.url))),
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/model.dart';
|
||||
import 'package:mangayomi/utils/colors.dart';
|
||||
import 'package:mangayomi/utils/headers.dart';
|
||||
import 'package:mangayomi/modules/widgets/bottom_text_widget.dart';
|
||||
|
|
@ -18,7 +16,7 @@ import 'package:mangayomi/modules/widgets/cover_view_widget.dart';
|
|||
class MangaImageCardWidget extends ConsumerWidget {
|
||||
final String lang;
|
||||
|
||||
final GetManga? getMangaDetail;
|
||||
final MangaModel? getMangaDetail;
|
||||
|
||||
const MangaImageCardWidget({
|
||||
required this.lang,
|
||||
|
|
@ -46,8 +44,9 @@ class MangaImageCardWidget extends ConsumerWidget {
|
|||
as ImageProvider
|
||||
: CachedNetworkImageProvider(
|
||||
getMangaDetail!.imageUrl!,
|
||||
headers: ref.watch(
|
||||
headersProvider(source: getMangaDetail!.source!)),
|
||||
headers: ref.watch(headersProvider(
|
||||
source: getMangaDetail!.source!,
|
||||
lang: getMangaDetail!.lang!)),
|
||||
),
|
||||
onTap: () {
|
||||
pushToMangaReaderDetail(
|
||||
|
|
@ -88,7 +87,7 @@ class MangaImageCardWidget extends ConsumerWidget {
|
|||
}
|
||||
|
||||
void pushToMangaReaderDetail(
|
||||
{GetManga? getManga,
|
||||
{MangaModel? getManga,
|
||||
required String lang,
|
||||
required BuildContext context,
|
||||
int? archiveId,
|
||||
|
|
@ -98,16 +97,15 @@ void pushToMangaReaderDetail(
|
|||
final manga = mangaM ??
|
||||
Manga(
|
||||
imageUrl: getManga!.imageUrl,
|
||||
name: getManga.name,
|
||||
genre: getManga.genre,
|
||||
author: getManga.author,
|
||||
status: getManga.status!,
|
||||
description: getManga.description,
|
||||
link: getManga.url,
|
||||
name: getManga.name!.trim().trimLeft().trimRight(),
|
||||
genre: [],
|
||||
author: "",
|
||||
status: Status.unknown,
|
||||
description: "",
|
||||
link: getManga.link,
|
||||
source: getManga.source,
|
||||
lang: lang,
|
||||
lastUpdate: DateTime.now().millisecondsSinceEpoch);
|
||||
|
||||
lastUpdate: 0);
|
||||
final empty = isar.mangas
|
||||
.filter()
|
||||
.langEqualTo(lang)
|
||||
|
|
@ -117,16 +115,6 @@ void pushToMangaReaderDetail(
|
|||
if (empty) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.putSync(manga);
|
||||
for (var i = 0; i < getManga!.chapters.length; i++) {
|
||||
final chapters = Chapter(
|
||||
name: getManga.chapters[i].name,
|
||||
url: getManga.chapters[i].url,
|
||||
dateUpload: getManga.chapters[i].dateUpload,
|
||||
scanlator: getManga.chapters[i].scanlator,
|
||||
)..manga.value = manga;
|
||||
isar.chapters.putSync(chapters);
|
||||
chapters.manga.saveSync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -181,3 +169,4 @@ void pushToMangaReaderDetail(
|
|||
|
||||
context.push('/manga-reader/detail', extra: mangaId);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga_type.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/browse/sources/sources_filter_screen.dart';
|
||||
import 'package:mangayomi/modules/more/settings/downloads/downloads_screen.dart';
|
||||
import 'package:mangayomi/modules/updates/updates_screen.dart';
|
||||
import 'package:mangayomi/modules/webview/webview.dart';
|
||||
|
|
@ -30,6 +32,7 @@ final routerProvider = Provider<GoRouter>((ref) {
|
|||
final router = AsyncRouterNotifier();
|
||||
|
||||
return GoRouter(
|
||||
observers: [BotToastNavigatorObserver()],
|
||||
initialLocation: '/library',
|
||||
debugLogDiagnostics: false,
|
||||
refreshListenable: router,
|
||||
|
|
@ -92,17 +95,17 @@ class AsyncRouterNotifier extends ChangeNotifier {
|
|||
path: "/mangaHome",
|
||||
name: "mangaHome",
|
||||
builder: (context, state) {
|
||||
final mangaType = state.extra as MangaType?;
|
||||
final source = state.extra as Source?;
|
||||
return MangaHomeScreen(
|
||||
mangaType: mangaType!,
|
||||
source: source!,
|
||||
);
|
||||
},
|
||||
pageBuilder: (context, state) {
|
||||
final mangaType = state.extra as MangaType?;
|
||||
final source = state.extra as Source?;
|
||||
return CustomTransition(
|
||||
key: state.pageKey,
|
||||
child: MangaHomeScreen(
|
||||
mangaType: mangaType!,
|
||||
source: source!,
|
||||
),
|
||||
);
|
||||
}),
|
||||
|
|
@ -202,7 +205,6 @@ class AsyncRouterNotifier extends ChangeNotifier {
|
|||
final data = state.extra as Map<String, dynamic>;
|
||||
return SearchResultScreen(
|
||||
query: data['query']!,
|
||||
lang: data['lang']!,
|
||||
source: data['source']!,
|
||||
viewOnly: data['viewOnly'],
|
||||
);
|
||||
|
|
@ -213,7 +215,6 @@ class AsyncRouterNotifier extends ChangeNotifier {
|
|||
key: state.pageKey,
|
||||
child: SearchResultScreen(
|
||||
query: data['query']!,
|
||||
lang: data['lang']!,
|
||||
source: data['source']!,
|
||||
viewOnly: data['viewOnly'],
|
||||
),
|
||||
|
|
@ -233,6 +234,19 @@ class AsyncRouterNotifier extends ChangeNotifier {
|
|||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/sourceFilter",
|
||||
name: "sourceFilter",
|
||||
builder: (context, state) {
|
||||
return const SourcesFilterScreen();
|
||||
},
|
||||
pageBuilder: (context, state) {
|
||||
return CustomTransition(
|
||||
key: state.pageKey,
|
||||
child: const SourcesFilterScreen(),
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/downloadQueue",
|
||||
name: "downloadQueue",
|
||||
|
|
@ -253,7 +267,7 @@ class AsyncRouterNotifier extends ChangeNotifier {
|
|||
final data = state.extra as Map<String, String>;
|
||||
return MangaWebView(
|
||||
url: data["url"]!,
|
||||
source: data["source"]!,
|
||||
sourceId: data["sourceId"]!,
|
||||
title: data['title']!);
|
||||
},
|
||||
pageBuilder: (context, state) {
|
||||
|
|
@ -262,7 +276,7 @@ class AsyncRouterNotifier extends ChangeNotifier {
|
|||
key: state.pageKey,
|
||||
child: MangaWebView(
|
||||
url: data["url"]!,
|
||||
source: data["source"]!,
|
||||
sourceId: data["sourceId"]!,
|
||||
title: data['title']!,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,27 +1,23 @@
|
|||
// ignore_for_file: depend_o
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:dart_eval/dart_eval_bridge.dart';
|
||||
import 'package:dart_eval/stdlib/core.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/model.dart';
|
||||
import 'package:mangayomi/eval/compiler/compiler.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/archive_reader/providers/archive_reader_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/manga_reader_view.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
import 'package:mangayomi/sources/multisrc/heancms/heancms.dart';
|
||||
import 'package:mangayomi/sources/multisrc/madara/src/madara.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/comick.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/src/mangadex.dart';
|
||||
import 'package:mangayomi/sources/src/en/mangahere/src/mangahere.dart';
|
||||
import 'package:mangayomi/sources/src/fr/japscan/src/japscan.dart';
|
||||
import 'package:mangayomi/sources/src/fr/mangakawaii/src/mangakawaii.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mangathemesia/src/mangathemesia.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mmrcms/src/mmrcms.dart';
|
||||
import 'package:mangayomi/eval/runtime/runtime.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:mangayomi/utils/reg_exp_matcher.dart';
|
||||
import 'package:mangayomi/modules/more/providers/incognito_mode_state_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import '../eval/bridge_class/manga_model.dart';
|
||||
part 'get_chapter_url.g.dart';
|
||||
|
||||
class GetChapterUrlModel {
|
||||
|
|
@ -46,9 +42,7 @@ Future<GetChapterUrlModel> getChapterUrl(
|
|||
List<UChapDataPreload> uChapDataPreloadp = [];
|
||||
Directory? path;
|
||||
List<String> pageUrls = [];
|
||||
final manga = chapter.manga.value!;
|
||||
List<bool> isLocaleList = [];
|
||||
String source = manga.source!.toLowerCase();
|
||||
final settings = isar.settings.getSync(227);
|
||||
List<ChapterPageurls>? chapterPageUrlsList =
|
||||
settings!.chapterPageUrlsList ?? [];
|
||||
|
|
@ -58,7 +52,8 @@ Future<GetChapterUrlModel> getChapterUrl(
|
|||
final storageProvider = StorageProvider();
|
||||
path = await storageProvider.getMangaChapterDirectory(chapter);
|
||||
final mangaDirectory = await storageProvider.getMangaMainDirectory(chapter);
|
||||
|
||||
final source =
|
||||
getSource(chapter.manga.value!.lang!, chapter.manga.value!.source!);
|
||||
List<Uint8List?> archiveImages = [];
|
||||
final isLocalArchive = (chapter.archivePath ?? '').isNotEmpty;
|
||||
if (!chapter.manga.value!.isLocalArchive!) {
|
||||
|
|
@ -66,79 +61,39 @@ Future<GetChapterUrlModel> getChapterUrl(
|
|||
isarPageUrls.first.urls != null &&
|
||||
isarPageUrls.first.urls!.isNotEmpty) {
|
||||
pageUrls = isarPageUrls.first.urls!;
|
||||
}
|
||||
} else {
|
||||
final bytecode = compilerEval(source.sourceCode!);
|
||||
|
||||
/*********/
|
||||
/*comick*/
|
||||
/********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.comick) {
|
||||
pageUrls = await Comick().getChapterUrl(chapter: chapter, ref: ref);
|
||||
}
|
||||
|
||||
/*************/
|
||||
/*mangathemesia*/
|
||||
/**************/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.mangathemesia) {
|
||||
pageUrls =
|
||||
await MangaThemeSia().getChapterUrl(chapter: chapter, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangakawaii*/
|
||||
/***********/
|
||||
|
||||
else if (source == 'mangakawaii') {
|
||||
pageUrls = await MangaKawaii().getChapterUrl(chapter: chapter, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mmrcms*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.mmrcms) {
|
||||
pageUrls = await Mmrcms().getChapterUrl(chapter: chapter, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangahere*/
|
||||
/***********/
|
||||
|
||||
else if (source == 'mangahere') {
|
||||
pageUrls = await Mangahere().getChapterUrl(chapter: chapter, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*japscan*/
|
||||
/***********/
|
||||
|
||||
else if (source == 'japscan') {
|
||||
pageUrls = await Japscan().getChapterUrl(chapter: chapter, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*heancms*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.heancms) {
|
||||
pageUrls = await HeanCms().getChapterUrl(chapter: chapter, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*madara*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.madara) {
|
||||
pageUrls = await Madara().getChapterUrl(chapter: chapter, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangadex*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.mangadex) {
|
||||
pageUrls = await MangaDex().getChapterUrl(chapter: chapter, ref: ref);
|
||||
final runtime = runtimeEval(bytecode);
|
||||
runtime.args = [
|
||||
$MangaModel.wrap(MangaModel(
|
||||
lang: source.lang,
|
||||
link: chapter.url,
|
||||
baseUrl: source.baseUrl,
|
||||
source: source.name,
|
||||
apiUrl: source.apiUrl,
|
||||
sourceId: source.id,
|
||||
))
|
||||
];
|
||||
var result2 = await runtime.executeLib(
|
||||
'package:package:mangayomi/main.dart', 'getChapterUrl');
|
||||
if (result2 is $List) {
|
||||
for (var element in result2.$reified) {
|
||||
if (element is $Value) {
|
||||
pageUrls.add(element.$reified);
|
||||
} else {
|
||||
pageUrls.add(element);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var element in result2) {
|
||||
if (element is $Value) {
|
||||
pageUrls.add(element.$reified);
|
||||
} else {
|
||||
pageUrls.add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'get_chapter_url.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$getChapterUrlHash() => r'6e9e28c2d3775791f9c8544cf0f41bb07b5eaf81';
|
||||
String _$getChapterUrlHash() => r'46fc82b7ddeb8f6f1658dbc942db69f651505aad';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -1,99 +1,59 @@
|
|||
import 'dart:async';
|
||||
import 'package:mangayomi/eval/compiler/compiler.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/sources/multisrc/heancms/heancms.dart';
|
||||
import 'package:mangayomi/sources/multisrc/madara/src/madara.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/comick.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/src/mangadex.dart';
|
||||
import 'package:mangayomi/sources/src/en/mangahere/src/mangahere.dart';
|
||||
import 'package:mangayomi/sources/src/fr/japscan/src/japscan.dart';
|
||||
import 'package:mangayomi/sources/src/fr/mangakawaii/src/mangakawaii.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mangathemesia/src/mangathemesia.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mmrcms/src/mmrcms.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/manga_model.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/model.dart';
|
||||
import 'package:mangayomi/eval/runtime/runtime.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'get_latest_updates_manga.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<List<GetManga?>> getLatestUpdatesManga(
|
||||
Future<List<MangaModel?>> getLatestUpdatesManga(
|
||||
GetLatestUpdatesMangaRef ref, {
|
||||
required String source,
|
||||
required Source source,
|
||||
required int page,
|
||||
required String lang,
|
||||
}) async {
|
||||
List<GetManga?>? latestUpdatesManga;
|
||||
source = source.toLowerCase();
|
||||
List<MangaModel?>? latestUpdatesManga = [];
|
||||
final bytecode = compilerEval(source.sourceCode!);
|
||||
|
||||
/*********/
|
||||
/*comick*/
|
||||
/*******/
|
||||
if (getMangaTypeSource(source) == TypeSource.comick) {
|
||||
latestUpdatesManga = await Comick().getLatestUpdatesManga(
|
||||
source: source, page: page, ref: ref, lang: lang);
|
||||
final runtime = runtimeEval(bytecode);
|
||||
runtime.args = [
|
||||
$MangaModel.wrap(MangaModel(
|
||||
page: page,
|
||||
lang: source.lang,
|
||||
baseUrl: source.baseUrl,
|
||||
apiUrl: source.apiUrl,
|
||||
sourceId: source.id,
|
||||
source: source.name,
|
||||
dateFormat: source.dateFormat,
|
||||
dateFormatLocale: source.dateFormatLocale))
|
||||
];
|
||||
var result2 = await runtime.executeLib(
|
||||
'package:package:mangayomi/main.dart',
|
||||
'getLatestUpdatesManga',
|
||||
);
|
||||
try {
|
||||
if (result2 is $MangaModel) {
|
||||
final value = result2.$reified;
|
||||
List<MangaModel> newManga = [];
|
||||
for (var i = 0; i < value.names!.length; i++) {
|
||||
MangaModel newMangaa = MangaModel(
|
||||
name: value.names![i],
|
||||
link: value.urls![i],
|
||||
imageUrl: value.images![i],
|
||||
baseUrl: value.baseUrl,
|
||||
apiUrl: value.apiUrl,
|
||||
lang: value.lang,
|
||||
sourceId: value.sourceId,
|
||||
dateFormat: value.dateFormat,
|
||||
dateFormatLocale: value.dateFormatLocale);
|
||||
newManga.add(newMangaa);
|
||||
}
|
||||
latestUpdatesManga = newManga;
|
||||
}
|
||||
} catch (_) {
|
||||
throw Exception("");
|
||||
}
|
||||
|
||||
/***************/
|
||||
/*mangathemesia*/
|
||||
/**************/
|
||||
if (getMangaTypeSource(source) == TypeSource.mangathemesia) {
|
||||
latestUpdatesManga = await MangaThemeSia().getLatestUpdatesManga(
|
||||
source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangakawaii*/
|
||||
/***********/
|
||||
if (source == "mangakawaii") {
|
||||
latestUpdatesManga = await MangaKawaii().getLatestUpdatesManga(
|
||||
source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mmrcms*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.mmrcms) {
|
||||
latestUpdatesManga = await Mmrcms().getLatestUpdatesManga(
|
||||
source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangahere*/
|
||||
/***********/
|
||||
else if (source == "mangahere") {
|
||||
latestUpdatesManga = await Mangahere().getLatestUpdatesManga(
|
||||
source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
/***********/
|
||||
/*japscan*/
|
||||
/***********/
|
||||
else if (source == "japscan") {
|
||||
latestUpdatesManga = await Japscan().getLatestUpdatesManga(
|
||||
source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*heancms*/
|
||||
/***********/
|
||||
else if (getMangaTypeSource(source) == TypeSource.heancms) {
|
||||
latestUpdatesManga = await HeanCms().getLatestUpdatesManga(
|
||||
source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*madara*/
|
||||
/***********/
|
||||
else if (getMangaTypeSource(source) == TypeSource.madara) {
|
||||
latestUpdatesManga = await Madara().getLatestUpdatesManga(
|
||||
source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangadex*/
|
||||
/***********/
|
||||
else if (getMangaTypeSource(source) == TypeSource.mangadex) {
|
||||
latestUpdatesManga = await MangaDex().getLatestUpdatesManga(
|
||||
source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
return latestUpdatesManga!;
|
||||
return latestUpdatesManga;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ part of 'get_latest_updates_manga.dart';
|
|||
// **************************************************************************
|
||||
|
||||
String _$getLatestUpdatesMangaHash() =>
|
||||
r'c5ddc8739ee5b7c085cdeede7c55743bfc7f5120';
|
||||
r'6ad1f68ce7a320c661817e5d5ff1bbf8d68b3948';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
@ -31,27 +31,26 @@ class _SystemHash {
|
|||
}
|
||||
|
||||
typedef GetLatestUpdatesMangaRef
|
||||
= AutoDisposeFutureProviderRef<List<GetManga?>>;
|
||||
= AutoDisposeFutureProviderRef<List<MangaModel?>>;
|
||||
|
||||
/// See also [getLatestUpdatesManga].
|
||||
@ProviderFor(getLatestUpdatesManga)
|
||||
const getLatestUpdatesMangaProvider = GetLatestUpdatesMangaFamily();
|
||||
|
||||
/// See also [getLatestUpdatesManga].
|
||||
class GetLatestUpdatesMangaFamily extends Family<AsyncValue<List<GetManga?>>> {
|
||||
class GetLatestUpdatesMangaFamily
|
||||
extends Family<AsyncValue<List<MangaModel?>>> {
|
||||
/// See also [getLatestUpdatesManga].
|
||||
const GetLatestUpdatesMangaFamily();
|
||||
|
||||
/// See also [getLatestUpdatesManga].
|
||||
GetLatestUpdatesMangaProvider call({
|
||||
required String source,
|
||||
required Source source,
|
||||
required int page,
|
||||
required String lang,
|
||||
}) {
|
||||
return GetLatestUpdatesMangaProvider(
|
||||
source: source,
|
||||
page: page,
|
||||
lang: lang,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +61,6 @@ class GetLatestUpdatesMangaFamily extends Family<AsyncValue<List<GetManga?>>> {
|
|||
return call(
|
||||
source: provider.source,
|
||||
page: provider.page,
|
||||
lang: provider.lang,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -83,18 +81,16 @@ class GetLatestUpdatesMangaFamily extends Family<AsyncValue<List<GetManga?>>> {
|
|||
|
||||
/// See also [getLatestUpdatesManga].
|
||||
class GetLatestUpdatesMangaProvider
|
||||
extends AutoDisposeFutureProvider<List<GetManga?>> {
|
||||
extends AutoDisposeFutureProvider<List<MangaModel?>> {
|
||||
/// See also [getLatestUpdatesManga].
|
||||
GetLatestUpdatesMangaProvider({
|
||||
required this.source,
|
||||
required this.page,
|
||||
required this.lang,
|
||||
}) : super.internal(
|
||||
(ref) => getLatestUpdatesManga(
|
||||
ref,
|
||||
source: source,
|
||||
page: page,
|
||||
lang: lang,
|
||||
),
|
||||
from: getLatestUpdatesMangaProvider,
|
||||
name: r'getLatestUpdatesMangaProvider',
|
||||
|
|
@ -107,16 +103,14 @@ class GetLatestUpdatesMangaProvider
|
|||
GetLatestUpdatesMangaFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final String source;
|
||||
final Source source;
|
||||
final int page;
|
||||
final String lang;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is GetLatestUpdatesMangaProvider &&
|
||||
other.source == source &&
|
||||
other.page == page &&
|
||||
other.lang == lang;
|
||||
other.page == page;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -124,7 +118,6 @@ class GetLatestUpdatesMangaProvider
|
|||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, source.hashCode);
|
||||
hash = _SystemHash.combine(hash, page.hashCode);
|
||||
hash = _SystemHash.combine(hash, lang.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,101 +1,37 @@
|
|||
import 'dart:async';
|
||||
import 'package:mangayomi/eval/compiler/compiler.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/sources/multisrc/heancms/heancms.dart';
|
||||
import 'package:mangayomi/sources/multisrc/madara/src/madara.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/comick.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/src/mangadex.dart';
|
||||
import 'package:mangayomi/sources/src/en/mangahere/src/mangahere.dart';
|
||||
import 'package:mangayomi/sources/src/fr/japscan/src/japscan.dart';
|
||||
import 'package:mangayomi/sources/src/fr/mangakawaii/src/mangakawaii.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mangathemesia/src/mangathemesia.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mmrcms/src/mmrcms.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/manga_model.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/model.dart';
|
||||
import 'package:mangayomi/eval/runtime/runtime.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'get_manga_detail.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<GetManga> getMangaDetail(GetMangaDetailRef ref,
|
||||
{required GetManga manga,
|
||||
required String lang,
|
||||
required String source}) async {
|
||||
GetManga? mangadetail;
|
||||
Future<MangaModel> getMangaDetail(
|
||||
GetMangaDetailRef ref, {
|
||||
required MangaModel manga,
|
||||
required Source source,
|
||||
}) async {
|
||||
MangaModel? mangadetail;
|
||||
final bytecode = compilerEval(source.sourceCode!);
|
||||
|
||||
/********/
|
||||
/*comick*/
|
||||
/********/
|
||||
final runtime = runtimeEval(bytecode);
|
||||
runtime.args = [
|
||||
$MangaModel.wrap(manga
|
||||
..source = source.name
|
||||
..lang = source.lang)
|
||||
];
|
||||
|
||||
if (getMangaTypeSource(source.toLowerCase()) == TypeSource.comick) {
|
||||
mangadetail = await Comick()
|
||||
.getMangaDetail(manga: manga, lang: lang, source: source, ref: ref);
|
||||
}
|
||||
/*************/
|
||||
/*mangathemesia*/
|
||||
/**************/
|
||||
|
||||
if (getMangaTypeSource(source.toLowerCase()) == TypeSource.mangathemesia) {
|
||||
mangadetail = await MangaThemeSia()
|
||||
.getMangaDetail(manga: manga, lang: lang, source: source, ref: ref);
|
||||
}
|
||||
/***********/
|
||||
/*mangakawaii*/
|
||||
/***********/
|
||||
else if (source.toLowerCase() == "mangakawaii") {
|
||||
mangadetail = await MangaKawaii()
|
||||
.getMangaDetail(manga: manga, lang: lang, source: source, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mmrcms*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source.toLowerCase()) == TypeSource.mmrcms) {
|
||||
mangadetail = await Mmrcms()
|
||||
.getMangaDetail(manga: manga, lang: lang, source: source, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangahere*/
|
||||
/***********/
|
||||
else if (source.toLowerCase() == "mangahere") {
|
||||
mangadetail = await Mangahere()
|
||||
.getMangaDetail(manga: manga, lang: lang, source: source, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*japscan*/
|
||||
/***********/
|
||||
|
||||
else if (source.toLowerCase() == "japscan") {
|
||||
mangadetail = await Japscan()
|
||||
.getMangaDetail(manga: manga, lang: lang, source: source, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*heancms*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source.toLowerCase()) == TypeSource.heancms) {
|
||||
mangadetail = await HeanCms()
|
||||
.getMangaDetail(manga: manga, lang: lang, source: source, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*madara*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source.toLowerCase()) == TypeSource.madara) {
|
||||
mangadetail = await Madara()
|
||||
.getMangaDetail(manga: manga, lang: lang, source: source, ref: ref);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangadex*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source.toLowerCase()) == TypeSource.mangadex) {
|
||||
mangadetail = await MangaDex()
|
||||
.getMangaDetail(manga: manga, lang: lang, source: source, ref: ref);
|
||||
try {
|
||||
var result = await runtime.executeLib(
|
||||
'package:package:mangayomi/main.dart', 'getMangaDetail');
|
||||
if (result is $MangaModel) {
|
||||
final value = result.$reified;
|
||||
mangadetail = value;
|
||||
}
|
||||
} catch (_) {
|
||||
return manga;
|
||||
}
|
||||
return mangadetail!;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'get_manga_detail.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$getMangaDetailHash() => r'009422e0d88c595e2a8183e2360708ad2e13a955';
|
||||
String _$getMangaDetailHash() => r'e5c9845c4481eb584cbd899dacaaceda8167ae44';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
@ -29,26 +29,24 @@ class _SystemHash {
|
|||
}
|
||||
}
|
||||
|
||||
typedef GetMangaDetailRef = AutoDisposeFutureProviderRef<GetManga>;
|
||||
typedef GetMangaDetailRef = AutoDisposeFutureProviderRef<MangaModel>;
|
||||
|
||||
/// See also [getMangaDetail].
|
||||
@ProviderFor(getMangaDetail)
|
||||
const getMangaDetailProvider = GetMangaDetailFamily();
|
||||
|
||||
/// See also [getMangaDetail].
|
||||
class GetMangaDetailFamily extends Family<AsyncValue<GetManga>> {
|
||||
class GetMangaDetailFamily extends Family<AsyncValue<MangaModel>> {
|
||||
/// See also [getMangaDetail].
|
||||
const GetMangaDetailFamily();
|
||||
|
||||
/// See also [getMangaDetail].
|
||||
GetMangaDetailProvider call({
|
||||
required GetManga manga,
|
||||
required String lang,
|
||||
required String source,
|
||||
required MangaModel manga,
|
||||
required Source source,
|
||||
}) {
|
||||
return GetMangaDetailProvider(
|
||||
manga: manga,
|
||||
lang: lang,
|
||||
source: source,
|
||||
);
|
||||
}
|
||||
|
|
@ -59,7 +57,6 @@ class GetMangaDetailFamily extends Family<AsyncValue<GetManga>> {
|
|||
) {
|
||||
return call(
|
||||
manga: provider.manga,
|
||||
lang: provider.lang,
|
||||
source: provider.source,
|
||||
);
|
||||
}
|
||||
|
|
@ -80,17 +77,15 @@ class GetMangaDetailFamily extends Family<AsyncValue<GetManga>> {
|
|||
}
|
||||
|
||||
/// See also [getMangaDetail].
|
||||
class GetMangaDetailProvider extends AutoDisposeFutureProvider<GetManga> {
|
||||
class GetMangaDetailProvider extends AutoDisposeFutureProvider<MangaModel> {
|
||||
/// See also [getMangaDetail].
|
||||
GetMangaDetailProvider({
|
||||
required this.manga,
|
||||
required this.lang,
|
||||
required this.source,
|
||||
}) : super.internal(
|
||||
(ref) => getMangaDetail(
|
||||
ref,
|
||||
manga: manga,
|
||||
lang: lang,
|
||||
source: source,
|
||||
),
|
||||
from: getMangaDetailProvider,
|
||||
|
|
@ -104,15 +99,13 @@ class GetMangaDetailProvider extends AutoDisposeFutureProvider<GetManga> {
|
|||
GetMangaDetailFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final GetManga manga;
|
||||
final String lang;
|
||||
final String source;
|
||||
final MangaModel manga;
|
||||
final Source source;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is GetMangaDetailProvider &&
|
||||
other.manga == manga &&
|
||||
other.lang == lang &&
|
||||
other.source == source;
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +113,6 @@ class GetMangaDetailProvider extends AutoDisposeFutureProvider<GetManga> {
|
|||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, manga.hashCode);
|
||||
hash = _SystemHash.combine(hash, lang.hashCode);
|
||||
hash = _SystemHash.combine(hash, source.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
|
|
|
|||
|
|
@ -1,94 +1,59 @@
|
|||
import 'dart:async';
|
||||
import 'package:mangayomi/eval/compiler/compiler.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/sources/multisrc/heancms/heancms.dart';
|
||||
import 'package:mangayomi/sources/multisrc/madara/src/madara.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/comick.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/src/mangadex.dart';
|
||||
import 'package:mangayomi/sources/src/en/mangahere/src/mangahere.dart';
|
||||
import 'package:mangayomi/sources/src/fr/japscan/src/japscan.dart';
|
||||
import 'package:mangayomi/sources/src/fr/mangakawaii/src/mangakawaii.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mangathemesia/src/mangathemesia.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mmrcms/src/mmrcms.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/manga_model.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/model.dart';
|
||||
import 'package:mangayomi/eval/runtime/runtime.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'get_popular_manga.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<List<GetManga?>> getPopularManga(GetPopularMangaRef ref,
|
||||
{required String source, required int page, required String lang}) async {
|
||||
List<GetManga?>? popularManga;
|
||||
source = source.toLowerCase();
|
||||
Future<List<MangaModel?>> getPopularManga(
|
||||
GetPopularMangaRef ref, {
|
||||
required Source source,
|
||||
required int page,
|
||||
}) async {
|
||||
List<MangaModel> popularManga = [];
|
||||
final bytecode = compilerEval(source.sourceCode!);
|
||||
|
||||
/*********/
|
||||
/*comick*/
|
||||
/*******/
|
||||
if (getMangaTypeSource(source) == TypeSource.comick) {
|
||||
popularManga = await Comick()
|
||||
.getPopularManga(source: source, page: page, ref: ref, lang: lang);
|
||||
final runtime = runtimeEval(bytecode);
|
||||
runtime.args = [
|
||||
$MangaModel.wrap(MangaModel(
|
||||
page: page,
|
||||
lang: source.lang,
|
||||
baseUrl: source.baseUrl,
|
||||
apiUrl: source.apiUrl,
|
||||
sourceId: source.id,
|
||||
source: source.name,
|
||||
dateFormat: source.dateFormat,
|
||||
dateFormatLocale: source.dateFormatLocale))
|
||||
];
|
||||
var result2 = await runtime.executeLib(
|
||||
'package:package:mangayomi/main.dart',
|
||||
'getPopularManga',
|
||||
);
|
||||
try {
|
||||
if (result2 is $MangaModel) {
|
||||
final value = result2.$reified;
|
||||
List<MangaModel> newManga = [];
|
||||
for (var i = 0; i < value.names!.length; i++) {
|
||||
MangaModel newMangaa = MangaModel(
|
||||
name: value.names![i],
|
||||
link: value.urls![i],
|
||||
imageUrl: value.images![i],
|
||||
baseUrl: value.baseUrl,
|
||||
apiUrl: value.apiUrl,
|
||||
lang: value.lang,
|
||||
status: value.status,
|
||||
dateFormat: value.dateFormat,
|
||||
dateFormatLocale: value.dateFormatLocale);
|
||||
newManga.add(newMangaa);
|
||||
}
|
||||
popularManga = newManga;
|
||||
}
|
||||
} catch (_) {
|
||||
throw Exception("");
|
||||
}
|
||||
|
||||
/***************/
|
||||
/*mangathemesia*/
|
||||
/**************/
|
||||
if (getMangaTypeSource(source) == TypeSource.mangathemesia) {
|
||||
popularManga = await MangaThemeSia()
|
||||
.getPopularManga(source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangakawaii*/
|
||||
/***********/
|
||||
if (source == "mangakawaii") {
|
||||
popularManga = await MangaKawaii()
|
||||
.getPopularManga(source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mmrcms*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.mmrcms) {
|
||||
popularManga = await Mmrcms()
|
||||
.getPopularManga(source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangahere*/
|
||||
/***********/
|
||||
else if (source == "mangahere") {
|
||||
popularManga = await Mangahere()
|
||||
.getPopularManga(source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
/***********/
|
||||
/*japscan*/
|
||||
/***********/
|
||||
else if (source == "japscan") {
|
||||
popularManga = await Japscan()
|
||||
.getPopularManga(source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*heancms*/
|
||||
/***********/
|
||||
else if (getMangaTypeSource(source) == TypeSource.heancms) {
|
||||
popularManga = await HeanCms()
|
||||
.getPopularManga(source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
/***********/
|
||||
/*madara*/
|
||||
/***********/
|
||||
else if (getMangaTypeSource(source) == TypeSource.madara) {
|
||||
popularManga = await Madara()
|
||||
.getPopularManga(source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangadex*/
|
||||
/***********/
|
||||
else if (getMangaTypeSource(source) == TypeSource.mangadex) {
|
||||
popularManga = await MangaDex()
|
||||
.getPopularManga(source: source, page: page, ref: ref, lang: lang);
|
||||
}
|
||||
return popularManga!;
|
||||
return popularManga;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'get_popular_manga.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$getPopularMangaHash() => r'4fe87d57b93957bbc848b9b814dd3c9aa94b89c5';
|
||||
String _$getPopularMangaHash() => r'f0999a803936680db27e22750a620566c25ae69e';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
@ -29,27 +29,25 @@ class _SystemHash {
|
|||
}
|
||||
}
|
||||
|
||||
typedef GetPopularMangaRef = AutoDisposeFutureProviderRef<List<GetManga?>>;
|
||||
typedef GetPopularMangaRef = AutoDisposeFutureProviderRef<List<MangaModel?>>;
|
||||
|
||||
/// See also [getPopularManga].
|
||||
@ProviderFor(getPopularManga)
|
||||
const getPopularMangaProvider = GetPopularMangaFamily();
|
||||
|
||||
/// See also [getPopularManga].
|
||||
class GetPopularMangaFamily extends Family<AsyncValue<List<GetManga?>>> {
|
||||
class GetPopularMangaFamily extends Family<AsyncValue<List<MangaModel?>>> {
|
||||
/// See also [getPopularManga].
|
||||
const GetPopularMangaFamily();
|
||||
|
||||
/// See also [getPopularManga].
|
||||
GetPopularMangaProvider call({
|
||||
required String source,
|
||||
required Source source,
|
||||
required int page,
|
||||
required String lang,
|
||||
}) {
|
||||
return GetPopularMangaProvider(
|
||||
source: source,
|
||||
page: page,
|
||||
lang: lang,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +58,6 @@ class GetPopularMangaFamily extends Family<AsyncValue<List<GetManga?>>> {
|
|||
return call(
|
||||
source: provider.source,
|
||||
page: provider.page,
|
||||
lang: provider.lang,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -81,18 +78,16 @@ class GetPopularMangaFamily extends Family<AsyncValue<List<GetManga?>>> {
|
|||
|
||||
/// See also [getPopularManga].
|
||||
class GetPopularMangaProvider
|
||||
extends AutoDisposeFutureProvider<List<GetManga?>> {
|
||||
extends AutoDisposeFutureProvider<List<MangaModel?>> {
|
||||
/// See also [getPopularManga].
|
||||
GetPopularMangaProvider({
|
||||
required this.source,
|
||||
required this.page,
|
||||
required this.lang,
|
||||
}) : super.internal(
|
||||
(ref) => getPopularManga(
|
||||
ref,
|
||||
source: source,
|
||||
page: page,
|
||||
lang: lang,
|
||||
),
|
||||
from: getPopularMangaProvider,
|
||||
name: r'getPopularMangaProvider',
|
||||
|
|
@ -105,16 +100,14 @@ class GetPopularMangaProvider
|
|||
GetPopularMangaFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final String source;
|
||||
final Source source;
|
||||
final int page;
|
||||
final String lang;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is GetPopularMangaProvider &&
|
||||
other.source == source &&
|
||||
other.page == page &&
|
||||
other.lang == lang;
|
||||
other.page == page;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -122,7 +115,6 @@ class GetPopularMangaProvider
|
|||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, source.hashCode);
|
||||
hash = _SystemHash.combine(hash, page.hashCode);
|
||||
hash = _SystemHash.combine(hash, lang.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,105 +1,19 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:html/dom.dart' as dom;
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/webview/webview.dart';
|
||||
import 'package:mangayomi/services/http_service/cloudflare/cookie.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'cloudflare_bypass.g.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
|
||||
@riverpod
|
||||
Future<dom.Document?> cloudflareBypassDom(CloudflareBypassDomRef ref,
|
||||
Future<String> cloudflareBypass(
|
||||
{required String url,
|
||||
required String source,
|
||||
required bool useUserAgent}) async {
|
||||
final ua = isar.settings.getSync(227)!.userAgent!;
|
||||
bool isOk = false;
|
||||
dom.Document? htmll;
|
||||
if (Platform.isWindows || Platform.isLinux) {
|
||||
String? html;
|
||||
final webview = await WebviewWindow.create(
|
||||
configuration: CreateConfiguration(
|
||||
windowHeight: 500,
|
||||
windowWidth: 500,
|
||||
userDataFolderWindows: await getWebViewPath(),
|
||||
),
|
||||
);
|
||||
webview
|
||||
..setBrightness(Brightness.dark)
|
||||
..setApplicationNameForUserAgent(useUserAgent ? ua : '')
|
||||
..launch(url);
|
||||
|
||||
await Future.delayed(const Duration(seconds: 10));
|
||||
html = await decodeHtml(webview);
|
||||
isOk = true;
|
||||
await Future.doWhile(() async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
if (isOk == true) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
html = await decodeHtml(webview);
|
||||
htmll = dom.Document.html(html!);
|
||||
webview.close();
|
||||
} else {
|
||||
HeadlessInAppWebView? headlessWebView;
|
||||
headlessWebView = HeadlessInAppWebView(
|
||||
onLoadStop: (controller, u) async {
|
||||
String? html;
|
||||
html = await controller.evaluateJavascript(
|
||||
source:
|
||||
"window.document.getElementsByTagName('html')[0].outerHTML;");
|
||||
await Future.doWhile(() async {
|
||||
if (html == null ||
|
||||
html!.contains("Just a moment") ||
|
||||
html!.contains("Un instant…") ||
|
||||
html!.contains("https://challenges.cloudflare.com")) {
|
||||
html = await controller.evaluateJavascript(
|
||||
source:
|
||||
"window.document.getElementsByTagName('html')[0].outerHTML;");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
html = await controller.evaluateJavascript(
|
||||
source:
|
||||
"window.document.getElementsByTagName('html')[0].outerHTML;");
|
||||
htmll = dom.Document.html(html!);
|
||||
isOk = true;
|
||||
headlessWebView!.dispose();
|
||||
},
|
||||
initialSettings: InAppWebViewSettings(
|
||||
userAgent: useUserAgent ? ua : "",
|
||||
),
|
||||
initialUrlRequest: URLRequest(
|
||||
url: WebUri.uri(Uri.parse(url)),
|
||||
),
|
||||
);
|
||||
|
||||
headlessWebView.run();
|
||||
|
||||
await Future.doWhile(() async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
if (isOk == true) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
await ref.watch(setCookieProvider(source, url).future);
|
||||
}
|
||||
return htmll;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<String> cloudflareBypassHtml(CloudflareBypassHtmlRef ref,
|
||||
{required String url,
|
||||
required String source,
|
||||
required bool useUserAgent}) async {
|
||||
required String sourceId,
|
||||
required int method}) async {
|
||||
final ua = isar.settings.getSync(227)!.userAgent!;
|
||||
bool isOk = false;
|
||||
String? html;
|
||||
|
|
@ -113,7 +27,7 @@ Future<String> cloudflareBypassHtml(CloudflareBypassHtmlRef ref,
|
|||
);
|
||||
webview
|
||||
..setBrightness(Brightness.dark)
|
||||
..setApplicationNameForUserAgent(useUserAgent ? ua : '')
|
||||
..setApplicationNameForUserAgent(ua)
|
||||
..launch(url);
|
||||
|
||||
await Future.doWhile(() async {
|
||||
|
|
@ -137,8 +51,7 @@ Future<String> cloudflareBypassHtml(CloudflareBypassHtmlRef ref,
|
|||
await Future.doWhile(() async {
|
||||
if (html == null ||
|
||||
html!.contains("Just a moment") ||
|
||||
html!.contains("Un instant…") ||
|
||||
html!.contains("https://challenges.cloudflare.com")) {
|
||||
html!.contains("challenges.cloudflare.com")) {
|
||||
html = await controller.evaluateJavascript(
|
||||
source:
|
||||
"window.document.getElementsByTagName('html')[0].outerHTML;");
|
||||
|
|
@ -146,7 +59,6 @@ Future<String> cloudflareBypassHtml(CloudflareBypassHtmlRef ref,
|
|||
}
|
||||
return false;
|
||||
});
|
||||
await Future.delayed(const Duration(seconds: 10));
|
||||
html = await controller.evaluateJavascript(
|
||||
source:
|
||||
"window.document.getElementsByTagName('html')[0].outerHTML;");
|
||||
|
|
@ -154,9 +66,17 @@ Future<String> cloudflareBypassHtml(CloudflareBypassHtmlRef ref,
|
|||
headlessWebView!.dispose();
|
||||
},
|
||||
initialSettings: InAppWebViewSettings(
|
||||
userAgent: useUserAgent ? ua : "",
|
||||
userAgent: defaultUserAgent,
|
||||
),
|
||||
initialUrlRequest: URLRequest(
|
||||
headers: headers(sourceId: sourceId),
|
||||
method: method == 0
|
||||
? 'GET'
|
||||
: method == 1
|
||||
? 'POST'
|
||||
: method == 2
|
||||
? 'PUT'
|
||||
: 'DELETE',
|
||||
url: WebUri.uri(Uri.parse(url)),
|
||||
),
|
||||
);
|
||||
|
|
@ -169,46 +89,21 @@ Future<String> cloudflareBypassHtml(CloudflareBypassHtmlRef ref,
|
|||
}
|
||||
return true;
|
||||
});
|
||||
await ref.watch(setCookieProvider(source, url).future);
|
||||
await setCookieB(sourceId, url);
|
||||
}
|
||||
return html!;
|
||||
}
|
||||
|
||||
List<ContentBlocker> adsContentBlockers() {
|
||||
final List<ContentBlocker> contentBlockers = [];
|
||||
// list of Ad URL filters to be used to block ads loading.
|
||||
final adUrlFilters = [
|
||||
".*.doubleclick.net/.*",
|
||||
".*.ads.pubmatic.com/.*",
|
||||
".*.googlesyndication.com/.*",
|
||||
".*.google-analytics.com/.*",
|
||||
".*.adservice.google.*/.*",
|
||||
".*.adbrite.com/.*",
|
||||
".*.exponential.com/.*",
|
||||
".*.quantserve.com/.*",
|
||||
".*.scorecardresearch.com/.*",
|
||||
".*.zedo.com/.*",
|
||||
".*.adsafeprotected.com/.*",
|
||||
".*.teads.tv/.*",
|
||||
".*.outbrain.com/.*"
|
||||
];
|
||||
for (final adUrlFilter in adUrlFilters) {
|
||||
contentBlockers.add(ContentBlocker(
|
||||
trigger: ContentBlockerTrigger(
|
||||
urlFilter: adUrlFilter,
|
||||
),
|
||||
action: ContentBlockerAction(
|
||||
type: ContentBlockerActionType.BLOCK,
|
||||
)));
|
||||
Map<String, String> headers({required String sourceId}) {
|
||||
final source = isar.sources.getSync(int.parse(sourceId))!;
|
||||
if (source.headers!.isEmpty) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// apply the "display: none" style to some HTML elements
|
||||
contentBlockers.add(ContentBlocker(
|
||||
trigger: ContentBlockerTrigger(
|
||||
urlFilter: ".*",
|
||||
),
|
||||
action: ContentBlockerAction(
|
||||
type: ContentBlockerActionType.CSS_DISPLAY_NONE,
|
||||
selector: ".banner, .banners, .ads, .ad, .advert")));
|
||||
return contentBlockers;
|
||||
Map<String, String> newHeaders = {};
|
||||
final headers = jsonDecode(source.headers!) as Map;
|
||||
newHeaders =
|
||||
headers.map((key, value) => MapEntry(key.toString(), value.toString()));
|
||||
|
||||
return newHeaders;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,230 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'cloudflare_bypass.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$cloudflareBypassDomHash() =>
|
||||
r'9329b2e079dda8e1ccdbccd41fe7370df1509953';
|
||||
|
||||
/// 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 CloudflareBypassDomRef = AutoDisposeFutureProviderRef<Document?>;
|
||||
|
||||
/// See also [cloudflareBypassDom].
|
||||
@ProviderFor(cloudflareBypassDom)
|
||||
const cloudflareBypassDomProvider = CloudflareBypassDomFamily();
|
||||
|
||||
/// See also [cloudflareBypassDom].
|
||||
class CloudflareBypassDomFamily extends Family<AsyncValue<Document?>> {
|
||||
/// See also [cloudflareBypassDom].
|
||||
const CloudflareBypassDomFamily();
|
||||
|
||||
/// See also [cloudflareBypassDom].
|
||||
CloudflareBypassDomProvider call({
|
||||
required String url,
|
||||
required String source,
|
||||
required bool useUserAgent,
|
||||
}) {
|
||||
return CloudflareBypassDomProvider(
|
||||
url: url,
|
||||
source: source,
|
||||
useUserAgent: useUserAgent,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
CloudflareBypassDomProvider getProviderOverride(
|
||||
covariant CloudflareBypassDomProvider provider,
|
||||
) {
|
||||
return call(
|
||||
url: provider.url,
|
||||
source: provider.source,
|
||||
useUserAgent: provider.useUserAgent,
|
||||
);
|
||||
}
|
||||
|
||||
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'cloudflareBypassDomProvider';
|
||||
}
|
||||
|
||||
/// See also [cloudflareBypassDom].
|
||||
class CloudflareBypassDomProvider extends AutoDisposeFutureProvider<Document?> {
|
||||
/// See also [cloudflareBypassDom].
|
||||
CloudflareBypassDomProvider({
|
||||
required this.url,
|
||||
required this.source,
|
||||
required this.useUserAgent,
|
||||
}) : super.internal(
|
||||
(ref) => cloudflareBypassDom(
|
||||
ref,
|
||||
url: url,
|
||||
source: source,
|
||||
useUserAgent: useUserAgent,
|
||||
),
|
||||
from: cloudflareBypassDomProvider,
|
||||
name: r'cloudflareBypassDomProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$cloudflareBypassDomHash,
|
||||
dependencies: CloudflareBypassDomFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
CloudflareBypassDomFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final String url;
|
||||
final String source;
|
||||
final bool useUserAgent;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is CloudflareBypassDomProvider &&
|
||||
other.url == url &&
|
||||
other.source == source &&
|
||||
other.useUserAgent == useUserAgent;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, url.hashCode);
|
||||
hash = _SystemHash.combine(hash, source.hashCode);
|
||||
hash = _SystemHash.combine(hash, useUserAgent.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
String _$cloudflareBypassHtmlHash() =>
|
||||
r'fdc35dcbef7c4cb9fcca41d70208c4e82f9eb146';
|
||||
typedef CloudflareBypassHtmlRef = AutoDisposeFutureProviderRef<String>;
|
||||
|
||||
/// See also [cloudflareBypassHtml].
|
||||
@ProviderFor(cloudflareBypassHtml)
|
||||
const cloudflareBypassHtmlProvider = CloudflareBypassHtmlFamily();
|
||||
|
||||
/// See also [cloudflareBypassHtml].
|
||||
class CloudflareBypassHtmlFamily extends Family<AsyncValue<String>> {
|
||||
/// See also [cloudflareBypassHtml].
|
||||
const CloudflareBypassHtmlFamily();
|
||||
|
||||
/// See also [cloudflareBypassHtml].
|
||||
CloudflareBypassHtmlProvider call({
|
||||
required String url,
|
||||
required String source,
|
||||
required bool useUserAgent,
|
||||
}) {
|
||||
return CloudflareBypassHtmlProvider(
|
||||
url: url,
|
||||
source: source,
|
||||
useUserAgent: useUserAgent,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
CloudflareBypassHtmlProvider getProviderOverride(
|
||||
covariant CloudflareBypassHtmlProvider provider,
|
||||
) {
|
||||
return call(
|
||||
url: provider.url,
|
||||
source: provider.source,
|
||||
useUserAgent: provider.useUserAgent,
|
||||
);
|
||||
}
|
||||
|
||||
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'cloudflareBypassHtmlProvider';
|
||||
}
|
||||
|
||||
/// See also [cloudflareBypassHtml].
|
||||
class CloudflareBypassHtmlProvider extends AutoDisposeFutureProvider<String> {
|
||||
/// See also [cloudflareBypassHtml].
|
||||
CloudflareBypassHtmlProvider({
|
||||
required this.url,
|
||||
required this.source,
|
||||
required this.useUserAgent,
|
||||
}) : super.internal(
|
||||
(ref) => cloudflareBypassHtml(
|
||||
ref,
|
||||
url: url,
|
||||
source: source,
|
||||
useUserAgent: useUserAgent,
|
||||
),
|
||||
from: cloudflareBypassHtmlProvider,
|
||||
name: r'cloudflareBypassHtmlProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$cloudflareBypassHtmlHash,
|
||||
dependencies: CloudflareBypassHtmlFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
CloudflareBypassHtmlFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final String url;
|
||||
final String source;
|
||||
final bool useUserAgent;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is CloudflareBypassHtmlProvider &&
|
||||
other.url == url &&
|
||||
other.source == source &&
|
||||
other.useUserAgent == useUserAgent;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, url.hashCode);
|
||||
hash = _SystemHash.combine(hash, source.hashCode);
|
||||
hash = _SystemHash.combine(hash, useUserAgent.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
|
||||
|
|
@ -4,20 +4,28 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|||
part 'cookie.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future setCookie(SetCookieRef ref, String source, String url) async {
|
||||
source = source.toLowerCase();
|
||||
final cookieS = ref.watch(cookieStateProvider(source));
|
||||
|
||||
List<Cookie> cookies = [];
|
||||
Future setCookie(SetCookieRef ref, String sourceId, String url) async {
|
||||
CookieManager cookie = CookieManager.instance();
|
||||
cookies = await cookie.getCookies(url: WebUri.uri(Uri.parse(url.toString())));
|
||||
final cfClearance =
|
||||
|
||||
final cookies =
|
||||
await cookie.getCookies(url: WebUri.uri(Uri.parse(url.toString())));
|
||||
final newCookie =
|
||||
cookies.where((element) => element.name == "cf_clearance").toList();
|
||||
String newCookie = "";
|
||||
if (cfClearance.isNotEmpty && cfClearance.first.name != cookieS) {
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
newCookie = "$newCookie ${cookies[i].name}=${cookies[i].value};";
|
||||
}
|
||||
ref.read(cookieStateProvider(source).notifier).setCookie(newCookie);
|
||||
if (newCookie.isNotEmpty) {
|
||||
ref
|
||||
.read(cookieStateProvider(sourceId).notifier)
|
||||
.setCookie("cf_clearance=${newCookie.first.value};");
|
||||
}
|
||||
}
|
||||
|
||||
Future setCookieB(String sourceId, String url) async {
|
||||
CookieManager cookie = CookieManager.instance();
|
||||
|
||||
final cookies =
|
||||
await cookie.getCookies(url: WebUri.uri(Uri.parse(url.toString())));
|
||||
final newCookie =
|
||||
cookies.where((element) => element.name == "cf_clearance").toList();
|
||||
if (newCookie.isNotEmpty) {
|
||||
setCookieB("cf_clearance=${newCookie.first.value};", sourceId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'cookie.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$setCookieHash() => r'e9519081f85b28e34d09466392e4e253cc51b9af';
|
||||
String _$setCookieHash() => r'3e91a1c44ab0e597cebb55bf50d3a5a29d1eb29b';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
@ -42,11 +42,11 @@ class SetCookieFamily extends Family<AsyncValue<dynamic>> {
|
|||
|
||||
/// See also [setCookie].
|
||||
SetCookieProvider call(
|
||||
String source,
|
||||
String sourceId,
|
||||
String url,
|
||||
) {
|
||||
return SetCookieProvider(
|
||||
source,
|
||||
sourceId,
|
||||
url,
|
||||
);
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ class SetCookieFamily extends Family<AsyncValue<dynamic>> {
|
|||
covariant SetCookieProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.source,
|
||||
provider.sourceId,
|
||||
provider.url,
|
||||
);
|
||||
}
|
||||
|
|
@ -80,12 +80,12 @@ class SetCookieFamily extends Family<AsyncValue<dynamic>> {
|
|||
class SetCookieProvider extends AutoDisposeFutureProvider<dynamic> {
|
||||
/// See also [setCookie].
|
||||
SetCookieProvider(
|
||||
this.source,
|
||||
this.sourceId,
|
||||
this.url,
|
||||
) : super.internal(
|
||||
(ref) => setCookie(
|
||||
ref,
|
||||
source,
|
||||
sourceId,
|
||||
url,
|
||||
),
|
||||
from: setCookieProvider,
|
||||
|
|
@ -98,20 +98,20 @@ class SetCookieProvider extends AutoDisposeFutureProvider<dynamic> {
|
|||
allTransitiveDependencies: SetCookieFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final String source;
|
||||
final String sourceId;
|
||||
final String url;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SetCookieProvider &&
|
||||
other.source == source &&
|
||||
other.sourceId == sourceId &&
|
||||
other.url == url;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, source.hashCode);
|
||||
hash = _SystemHash.combine(hash, sourceId.hashCode);
|
||||
hash = _SystemHash.combine(hash, url.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
|
|
|
|||
|
|
@ -6,14 +6,15 @@ part 'cookie_providers.g.dart';
|
|||
@riverpod
|
||||
class CookieState extends _$CookieState {
|
||||
@override
|
||||
String build(String source) {
|
||||
String build(String idSource) {
|
||||
final cookiesList = isar.settings.getSync(227)!.cookiesList ?? [];
|
||||
final cookieList =
|
||||
cookiesList.where((element) => element.source == source).toList();
|
||||
cookiesList.where((element) => element.idSource == idSource).toList();
|
||||
String cookie = "";
|
||||
if (cookieList.isNotEmpty) {
|
||||
cookie = cookieList.first.cookie!.toString();
|
||||
}
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
|
|
@ -21,15 +22,42 @@ class CookieState extends _$CookieState {
|
|||
final settings = isar.settings.getSync(227);
|
||||
List<Cookie>? cookieList = [];
|
||||
for (var cookie in settings!.cookiesList ?? []) {
|
||||
if (cookie.source != source) {
|
||||
if (cookie.idSource != idSource) {
|
||||
cookieList.add(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
cookieList.add(Cookie()
|
||||
..source = source
|
||||
..idSource = idSource
|
||||
..cookie = newCookie);
|
||||
isar.writeTxnSync(
|
||||
() => isar.settings.putSync(settings..cookiesList = cookieList));
|
||||
}
|
||||
}
|
||||
|
||||
void setCookieB(String newCookie, String idSource) {
|
||||
final settings = isar.settings.getSync(227);
|
||||
List<Cookie>? cookieList = [];
|
||||
for (var cookie in settings!.cookiesList ?? []) {
|
||||
if (cookie.idSource != idSource) {
|
||||
cookieList.add(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
cookieList.add(Cookie()
|
||||
..idSource = idSource
|
||||
..cookie = newCookie);
|
||||
isar.writeTxnSync(
|
||||
() => isar.settings.putSync(settings..cookiesList = cookieList));
|
||||
}
|
||||
|
||||
String getCookie(String idSource) {
|
||||
final cookiesList = isar.settings.getSync(227)!.cookiesList ?? [];
|
||||
final cookieList =
|
||||
cookiesList.where((element) => element.idSource == idSource).toList();
|
||||
String cookie = "";
|
||||
if (cookieList.isNotEmpty) {
|
||||
cookie = cookieList.first.cookie!.toString();
|
||||
}
|
||||
return cookie;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'cookie_providers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$cookieStateHash() => r'10a8c516aead11fc1820ae11549c33bc7b82fc39';
|
||||
String _$cookieStateHash() => r'37608903bb251f586fea7458dd4762160c456868';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
@ -30,10 +30,10 @@ class _SystemHash {
|
|||
}
|
||||
|
||||
abstract class _$CookieState extends BuildlessAutoDisposeNotifier<String> {
|
||||
late final String source;
|
||||
late final String idSource;
|
||||
|
||||
String build(
|
||||
String source,
|
||||
String idSource,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -48,10 +48,10 @@ class CookieStateFamily extends Family<String> {
|
|||
|
||||
/// See also [CookieState].
|
||||
CookieStateProvider call(
|
||||
String source,
|
||||
String idSource,
|
||||
) {
|
||||
return CookieStateProvider(
|
||||
source,
|
||||
idSource,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ class CookieStateFamily extends Family<String> {
|
|||
covariant CookieStateProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.source,
|
||||
provider.idSource,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -84,9 +84,9 @@ class CookieStateProvider
|
|||
extends AutoDisposeNotifierProviderImpl<CookieState, String> {
|
||||
/// See also [CookieState].
|
||||
CookieStateProvider(
|
||||
this.source,
|
||||
this.idSource,
|
||||
) : super.internal(
|
||||
() => CookieState()..source = source,
|
||||
() => CookieState()..idSource = idSource,
|
||||
from: cookieStateProvider,
|
||||
name: r'cookieStateProvider',
|
||||
debugGetCreateSourceHash:
|
||||
|
|
@ -98,17 +98,17 @@ class CookieStateProvider
|
|||
CookieStateFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final String source;
|
||||
final String idSource;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is CookieStateProvider && other.source == source;
|
||||
return other is CookieStateProvider && other.idSource == idSource;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, source.hashCode);
|
||||
hash = _SystemHash.combine(hash, idSource.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
|
|
@ -118,7 +118,7 @@ class CookieStateProvider
|
|||
covariant CookieState notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
source,
|
||||
idSource,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
34
lib/services/http_service/cloudflare/providers/cookies.dart
Normal file
34
lib/services/http_service/cloudflare/providers/cookies.dart
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// import 'dart:developer';
|
||||
// import 'dart:io';
|
||||
// import 'package:cookie_jar/cookie_jar.dart';
|
||||
// import 'package:dio/dio.dart';
|
||||
// import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||
// import 'package:mangayomi/utils/constant.dart';
|
||||
// import 'package:path_provider/path_provider.dart';
|
||||
|
||||
// class SetCookie {
|
||||
// static onSet(List cookiesList, String url) async {
|
||||
// final dio = Dio();
|
||||
// List<Cookie> jarCookies = [];
|
||||
// if (cookiesList.isNotEmpty) {
|
||||
// for (var i in cookiesList) {
|
||||
// if (i.name == 'cf_clearance') {
|
||||
// Cookie jarCookie = Cookie(i.name, i.value);
|
||||
// log(i.value);
|
||||
// jarCookies.add(jarCookie);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// final cookieJar = CookieJar();
|
||||
// await cookieJar.saveFromResponse(Uri.parse(url), jarCookies);
|
||||
|
||||
// dio.interceptors.add(CookieManager(cookieJar));
|
||||
|
||||
// final tt = await dio.get(url,
|
||||
// options: Options(headers: {'User-Agent': defaultUserAgent}));
|
||||
// print(tt.statusCode);
|
||||
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
|
@ -1,52 +1,55 @@
|
|||
import 'package:html/dom.dart';
|
||||
import 'package:mangayomi/services/http_service/cloudflare/cloudflare_bypass.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:mangayomi/utils/headers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'http_service.g.dart';
|
||||
// import 'package:html/dom.dart';
|
||||
// import 'package:mangayomi/services/http_service/cloudflare/cloudflare_bypass.dart';
|
||||
// import 'package:http/http.dart' as http;
|
||||
// import 'package:mangayomi/sources/utils/utils.dart';
|
||||
// import 'package:mangayomi/utils/headers.dart';
|
||||
// import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
// part 'http_service.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<dynamic> httpGet(HttpGetRef ref,
|
||||
{required String url,
|
||||
required String source,
|
||||
required bool resDom,
|
||||
Map<String, String>? headers,
|
||||
bool useUserAgent = false}) async {
|
||||
bool isCloudflaree = isCloudflare(source);
|
||||
if (resDom) {
|
||||
Document? dom;
|
||||
if (isCloudflaree) {
|
||||
dom = await ref.read(cloudflareBypassDomProvider(
|
||||
url: url, source: source, useUserAgent: useUserAgent)
|
||||
.future);
|
||||
} else {
|
||||
dom = await httpResToDom(
|
||||
url: url,
|
||||
headers: headers ?? ref.watch(headersProvider(source: source)));
|
||||
}
|
||||
return dom;
|
||||
} else {
|
||||
String? resHtml;
|
||||
if (isCloudflaree) {
|
||||
resHtml = await ref.read(cloudflareBypassHtmlProvider(
|
||||
url: url, source: source, useUserAgent: useUserAgent)
|
||||
.future);
|
||||
} else {
|
||||
try {
|
||||
final response = await http.get(Uri.parse(url),
|
||||
headers: headers ?? ref.watch(headersProvider(source: source)));
|
||||
resHtml = response.body;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
return resHtml;
|
||||
}
|
||||
}
|
||||
// @riverpod
|
||||
// Future<dynamic> httpGet(HttpGetRef ref,
|
||||
// {required String url,
|
||||
// required String source,
|
||||
// required String lang,
|
||||
// required bool resDom,
|
||||
// Map<String, String>? headers,
|
||||
// bool useUserAgent = false}) async {
|
||||
// bool hasCloudflaree = hasCloudflare(source);
|
||||
// if (resDom) {
|
||||
// Document? dom;
|
||||
// if (hasCloudflaree) {
|
||||
// dom = await ref.read(cloudflareBypassDomProvider(
|
||||
// url: url, source: source, useUserAgent: useUserAgent)
|
||||
// .future);
|
||||
// } else {
|
||||
// dom = await httpResToDom(
|
||||
// url: url,
|
||||
// headers: headers ??
|
||||
// ref.watch(headersProvider(source: source, lang: lang)));
|
||||
// }
|
||||
// return dom;
|
||||
// } else {
|
||||
// String? resHtml;
|
||||
// if (hasCloudflaree) {
|
||||
// resHtml = await ref.read(cloudflareBypassHtmlProvider(
|
||||
// url: url, source: source, useUserAgent: useUserAgent)
|
||||
// .future);
|
||||
// } else {
|
||||
// try {
|
||||
// final response = await http.get(Uri.parse(url),
|
||||
// headers: headers ??
|
||||
// ref.watch(headersProvider(source: source, lang: lang)));
|
||||
// resHtml = response.body;
|
||||
// } catch (e) {
|
||||
// rethrow;
|
||||
// }
|
||||
// }
|
||||
// return resHtml;
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<Document> httpResToDom(
|
||||
{required String url, required Map<String, String>? headers}) async {
|
||||
final response = await http.get(Uri.parse(url), headers: headers);
|
||||
return Document.html(response.body);
|
||||
}
|
||||
// Future<Document> httpResToDom(
|
||||
// {required String url, required Map<String, String>? headers}) async {
|
||||
// final response = await http.get(Uri.parse(url), headers: headers);
|
||||
// return Document.html(response.body);
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,144 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'http_service.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$httpGetHash() => r'115d7fdde9392d32055ddefe661731c37b2b584e';
|
||||
|
||||
/// 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 HttpGetRef = AutoDisposeFutureProviderRef<dynamic>;
|
||||
|
||||
/// See also [httpGet].
|
||||
@ProviderFor(httpGet)
|
||||
const httpGetProvider = HttpGetFamily();
|
||||
|
||||
/// See also [httpGet].
|
||||
class HttpGetFamily extends Family<AsyncValue<dynamic>> {
|
||||
/// See also [httpGet].
|
||||
const HttpGetFamily();
|
||||
|
||||
/// See also [httpGet].
|
||||
HttpGetProvider call({
|
||||
required String url,
|
||||
required String source,
|
||||
required bool resDom,
|
||||
Map<String, String>? headers,
|
||||
bool useUserAgent = false,
|
||||
}) {
|
||||
return HttpGetProvider(
|
||||
url: url,
|
||||
source: source,
|
||||
resDom: resDom,
|
||||
headers: headers,
|
||||
useUserAgent: useUserAgent,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
HttpGetProvider getProviderOverride(
|
||||
covariant HttpGetProvider provider,
|
||||
) {
|
||||
return call(
|
||||
url: provider.url,
|
||||
source: provider.source,
|
||||
resDom: provider.resDom,
|
||||
headers: provider.headers,
|
||||
useUserAgent: provider.useUserAgent,
|
||||
);
|
||||
}
|
||||
|
||||
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'httpGetProvider';
|
||||
}
|
||||
|
||||
/// See also [httpGet].
|
||||
class HttpGetProvider extends AutoDisposeFutureProvider<dynamic> {
|
||||
/// See also [httpGet].
|
||||
HttpGetProvider({
|
||||
required this.url,
|
||||
required this.source,
|
||||
required this.resDom,
|
||||
this.headers,
|
||||
this.useUserAgent = false,
|
||||
}) : super.internal(
|
||||
(ref) => httpGet(
|
||||
ref,
|
||||
url: url,
|
||||
source: source,
|
||||
resDom: resDom,
|
||||
headers: headers,
|
||||
useUserAgent: useUserAgent,
|
||||
),
|
||||
from: httpGetProvider,
|
||||
name: r'httpGetProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$httpGetHash,
|
||||
dependencies: HttpGetFamily._dependencies,
|
||||
allTransitiveDependencies: HttpGetFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final String url;
|
||||
final String source;
|
||||
final bool resDom;
|
||||
final Map<String, String>? headers;
|
||||
final bool useUserAgent;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is HttpGetProvider &&
|
||||
other.url == url &&
|
||||
other.source == source &&
|
||||
other.resDom == resDom &&
|
||||
other.headers == headers &&
|
||||
other.useUserAgent == useUserAgent;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, url.hashCode);
|
||||
hash = _SystemHash.combine(hash, source.hashCode);
|
||||
hash = _SystemHash.combine(hash, resDom.hashCode);
|
||||
hash = _SystemHash.combine(hash, headers.hashCode);
|
||||
hash = _SystemHash.combine(hash, useUserAgent.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
|
||||
|
|
@ -1,107 +1,59 @@
|
|||
import 'package:mangayomi/eval/compiler/compiler.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/sources/multisrc/heancms/heancms.dart';
|
||||
import 'package:mangayomi/sources/multisrc/madara/src/madara.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/comick.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/src/mangadex.dart';
|
||||
import 'package:mangayomi/sources/src/en/mangahere/src/mangahere.dart';
|
||||
import 'package:mangayomi/sources/src/fr/japscan/src/japscan.dart';
|
||||
import 'package:mangayomi/sources/src/fr/mangakawaii/src/mangakawaii.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mangathemesia/src/mangathemesia.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mmrcms/src/mmrcms.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/manga_model.dart';
|
||||
import 'package:mangayomi/eval/bridge_class/model.dart';
|
||||
import 'package:mangayomi/eval/runtime/runtime.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'search_manga.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<List<GetManga?>> searchManga(
|
||||
Future<List<MangaModel?>> searchManga(
|
||||
SearchMangaRef ref, {
|
||||
required String source,
|
||||
required Source source,
|
||||
required String query,
|
||||
required String lang,
|
||||
required int page,
|
||||
}) async {
|
||||
List<GetManga?>? manga;
|
||||
source = source.toLowerCase();
|
||||
List<MangaModel?>? manga = [];
|
||||
final bytecode = compilerEval(source.sourceCode!);
|
||||
|
||||
/********/
|
||||
/*comick*/
|
||||
/********/
|
||||
|
||||
if (getMangaTypeSource(source) == TypeSource.comick) {
|
||||
manga = await Comick()
|
||||
.searchManga(source: source, query: query, ref: ref, lang: lang);
|
||||
final runtime = runtimeEval(bytecode);
|
||||
runtime.args = [
|
||||
$MangaModel.wrap(MangaModel(
|
||||
query: query,
|
||||
lang: source.lang,
|
||||
page: page,
|
||||
baseUrl: source.baseUrl,
|
||||
apiUrl: source.apiUrl,
|
||||
sourceId: source.id,
|
||||
source: source.name,
|
||||
dateFormat: source.dateFormat,
|
||||
dateFormatLocale: source.dateFormatLocale))
|
||||
];
|
||||
var result2 = await runtime.executeLib(
|
||||
'package:package:mangayomi/main.dart',
|
||||
'searchManga',
|
||||
);
|
||||
try {
|
||||
if (result2 is $MangaModel) {
|
||||
final value = result2.$reified;
|
||||
List<MangaModel> newManga = [];
|
||||
for (var i = 0; i < value.names!.length; i++) {
|
||||
MangaModel newMangaa = MangaModel(
|
||||
name: value.names![i],
|
||||
link: value.urls![i],
|
||||
imageUrl: value.images![i],
|
||||
baseUrl: value.baseUrl,
|
||||
apiUrl: value.apiUrl,
|
||||
lang: value.lang,
|
||||
sourceId: value.sourceId,
|
||||
dateFormat: value.dateFormat,
|
||||
dateFormatLocale: value.dateFormatLocale);
|
||||
newManga.add(newMangaa);
|
||||
}
|
||||
manga = newManga;
|
||||
}
|
||||
} catch (_) {
|
||||
throw Exception("");
|
||||
}
|
||||
|
||||
/***************/
|
||||
/*mangathemesia*/
|
||||
/***************/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.mangathemesia) {
|
||||
manga = await MangaThemeSia()
|
||||
.searchManga(source: source, query: query, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangakawaii*/
|
||||
/***********/
|
||||
|
||||
else if (source == "mangakawaii") {
|
||||
manga = await MangaKawaii()
|
||||
.searchManga(source: source, query: query, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mmrcms*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.mmrcms) {
|
||||
manga = await Mmrcms()
|
||||
.searchManga(source: source, query: query, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangahere*/
|
||||
/***********/
|
||||
|
||||
else if (source == "mangahere") {
|
||||
manga = await Mangahere()
|
||||
.searchManga(source: source, query: query, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*japscan*/
|
||||
/***********/
|
||||
|
||||
else if (source == "japscan") {
|
||||
manga = await Japscan()
|
||||
.searchManga(source: source, query: query, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*heancms*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.heancms) {
|
||||
manga = await HeanCms()
|
||||
.searchManga(source: source, query: query, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*madara*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.madara) {
|
||||
manga = await Madara()
|
||||
.searchManga(source: source, query: query, ref: ref, lang: lang);
|
||||
}
|
||||
|
||||
/***********/
|
||||
/*mangadex*/
|
||||
/***********/
|
||||
|
||||
else if (getMangaTypeSource(source) == TypeSource.mangadex) {
|
||||
manga = await MangaDex()
|
||||
.searchManga(source: source, query: query, ref: ref, lang: lang);
|
||||
}
|
||||
return manga!;
|
||||
return manga;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'search_manga.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$searchMangaHash() => r'0448881ce3785ac0c670a87914590008ae19fdd5';
|
||||
String _$searchMangaHash() => r'6e039e81e4764f69dca9a3ed133a76331d291b68';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
@ -29,27 +29,27 @@ class _SystemHash {
|
|||
}
|
||||
}
|
||||
|
||||
typedef SearchMangaRef = AutoDisposeFutureProviderRef<List<GetManga?>>;
|
||||
typedef SearchMangaRef = AutoDisposeFutureProviderRef<List<MangaModel?>>;
|
||||
|
||||
/// See also [searchManga].
|
||||
@ProviderFor(searchManga)
|
||||
const searchMangaProvider = SearchMangaFamily();
|
||||
|
||||
/// See also [searchManga].
|
||||
class SearchMangaFamily extends Family<AsyncValue<List<GetManga?>>> {
|
||||
class SearchMangaFamily extends Family<AsyncValue<List<MangaModel?>>> {
|
||||
/// See also [searchManga].
|
||||
const SearchMangaFamily();
|
||||
|
||||
/// See also [searchManga].
|
||||
SearchMangaProvider call({
|
||||
required String source,
|
||||
required Source source,
|
||||
required String query,
|
||||
required String lang,
|
||||
required int page,
|
||||
}) {
|
||||
return SearchMangaProvider(
|
||||
source: source,
|
||||
query: query,
|
||||
lang: lang,
|
||||
page: page,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ class SearchMangaFamily extends Family<AsyncValue<List<GetManga?>>> {
|
|||
return call(
|
||||
source: provider.source,
|
||||
query: provider.query,
|
||||
lang: provider.lang,
|
||||
page: provider.page,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -80,18 +80,18 @@ class SearchMangaFamily extends Family<AsyncValue<List<GetManga?>>> {
|
|||
}
|
||||
|
||||
/// See also [searchManga].
|
||||
class SearchMangaProvider extends AutoDisposeFutureProvider<List<GetManga?>> {
|
||||
class SearchMangaProvider extends AutoDisposeFutureProvider<List<MangaModel?>> {
|
||||
/// See also [searchManga].
|
||||
SearchMangaProvider({
|
||||
required this.source,
|
||||
required this.query,
|
||||
required this.lang,
|
||||
required this.page,
|
||||
}) : super.internal(
|
||||
(ref) => searchManga(
|
||||
ref,
|
||||
source: source,
|
||||
query: query,
|
||||
lang: lang,
|
||||
page: page,
|
||||
),
|
||||
from: searchMangaProvider,
|
||||
name: r'searchMangaProvider',
|
||||
|
|
@ -104,16 +104,16 @@ class SearchMangaProvider extends AutoDisposeFutureProvider<List<GetManga?>> {
|
|||
SearchMangaFamily._allTransitiveDependencies,
|
||||
);
|
||||
|
||||
final String source;
|
||||
final Source source;
|
||||
final String query;
|
||||
final String lang;
|
||||
final int page;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SearchMangaProvider &&
|
||||
other.source == source &&
|
||||
other.query == query &&
|
||||
other.lang == lang;
|
||||
other.page == page;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -121,7 +121,7 @@ class SearchMangaProvider extends AutoDisposeFutureProvider<List<GetManga?>> {
|
|||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, source.hashCode);
|
||||
hash = _SystemHash.combine(hash, query.hashCode);
|
||||
hash = _SystemHash.combine(hash, lang.hashCode);
|
||||
hash = _SystemHash.combine(hash, page.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,214 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/services/http_service/http_service.dart';
|
||||
import 'package:mangayomi/sources/multisrc/heancms/model/search.dart';
|
||||
import 'package:mangayomi/sources/multisrc/heancms/utils/utils.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
|
||||
class HeanCms extends MangaYomiServices {
|
||||
Map<String, String> _headers(String source) => {
|
||||
'Origin': getMangaBaseUrl(source),
|
||||
'Referer': getMangaAPIUrl(source),
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
@override
|
||||
Future<List<String>> getChapterUrl(
|
||||
{required Chapter chapter,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final chapterId = chapter.url!.split("#").last;
|
||||
var request = http.Request(
|
||||
'GET',
|
||||
Uri.parse(
|
||||
'${getMangaAPIUrl("${chapter.manga.value!.source}")}series/chapter/$chapterId'));
|
||||
request.headers.addAll({
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
});
|
||||
http.StreamedResponse response = await request.send();
|
||||
final res = await response.stream.bytesToString();
|
||||
var page = jsonDecode(res) as Map<String, dynamic>;
|
||||
for (var url in page["content"]["images"]) {
|
||||
if (url.startsWith("http")) {
|
||||
pageUrls.add(url);
|
||||
} else {
|
||||
pageUrls.add("${getMangaAPIUrl("${chapter.manga.value!.source}")}$url");
|
||||
}
|
||||
}
|
||||
return pageUrls;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<GetManga?> getMangaDetail(
|
||||
{required GetManga manga,
|
||||
required String lang,
|
||||
required String source,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
String currentSlug = manga.url!.split('/').last;
|
||||
|
||||
var request = http.Request(
|
||||
'GET',
|
||||
Uri.parse(
|
||||
'${getMangaAPIUrl(source)}series/$currentSlug#${manga.status}'));
|
||||
request.headers.addAll({
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
});
|
||||
http.StreamedResponse response = await request.send();
|
||||
final res = await response.stream.bytesToString();
|
||||
var mangaDetail = jsonDecode(res) as Map<String, dynamic>;
|
||||
|
||||
final d = Data.fromJson(mangaDetail);
|
||||
|
||||
final dom = await httpResToDom(
|
||||
url: '${getMangaBaseUrl(source)}/series/$currentSlug', headers: {});
|
||||
final des = dom
|
||||
.querySelectorAll("div.description-container")
|
||||
.map((e) => e.text.trim())
|
||||
.toList();
|
||||
final genres = dom
|
||||
.querySelectorAll("div.tags-container.pt-3 > span")
|
||||
.map((e) => e.text)
|
||||
.toList();
|
||||
final auth = dom
|
||||
.querySelectorAll("div > p")
|
||||
.where((element) =>
|
||||
element.outerHtml.toLowerCase().contains('autor') ||
|
||||
element.outerHtml.toLowerCase().contains('author'))
|
||||
.map((e) => e.text)
|
||||
.toList();
|
||||
author = auth.first.split(":").last;
|
||||
status = manga.status;
|
||||
genre = source == "OmegaScans" ? [] : genres;
|
||||
description = des.first;
|
||||
for (var chapter in d.chapters!) {
|
||||
chapterUrl
|
||||
.add("/series/$currentSlug/${chapter.chapterSlug}#${chapter.id}");
|
||||
chapterTitle.add(chapter.chapterName!.trim());
|
||||
chapterDate.add(parseDate(chapter.createdAt!, source));
|
||||
}
|
||||
return mangadetailRes(manga: manga, source: source);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga>> getPopularManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
var request = http.Request(
|
||||
'POST', Uri.parse('${getMangaAPIUrl(source)}series/querysearch'));
|
||||
request.body = json.encode({
|
||||
"page": page,
|
||||
"order": "desc",
|
||||
"order_by": "total_views",
|
||||
"series_status": "Ongoing",
|
||||
"series_type": "Comic"
|
||||
});
|
||||
request.headers.addAll(_headers(source));
|
||||
|
||||
http.StreamedResponse response = await request.send();
|
||||
final res = await response.stream.bytesToString();
|
||||
List<Data>? data;
|
||||
if (res.startsWith("{")) {
|
||||
var popularManga = jsonDecode(res) as Map<String, dynamic>;
|
||||
var popularMangaList = HeanCmsSearchModel.fromJson(popularManga);
|
||||
data = popularMangaList.data!;
|
||||
} else {
|
||||
var popularManga = jsonDecode(res) as List;
|
||||
data = popularManga.map((e) => Data.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
for (var a in data) {
|
||||
final status = parseHeanCmsStatus(a.status!);
|
||||
statusList.add(status);
|
||||
image.add(a.thumbnail!.startsWith("https://")
|
||||
? a.thumbnail
|
||||
: "${getMangaAPIUrl(source)}cover/${a.thumbnail}");
|
||||
name.add(a.title);
|
||||
url.add("/series/${a.seriesSlug!.replaceAll(timeStampRegex, '')}");
|
||||
}
|
||||
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> searchManga(
|
||||
{required String source,
|
||||
required String query,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
var request = http.Request(
|
||||
'POST', Uri.parse('${getMangaAPIUrl(source)}series/search'));
|
||||
request.body = json.encode({"term": query.trim()});
|
||||
request.headers.addAll({
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
http.StreamedResponse response = await request.send();
|
||||
final res = await response.stream.bytesToString();
|
||||
List<Data>? data;
|
||||
if (res.startsWith("{")) {
|
||||
var popularManga = jsonDecode(res) as Map<String, dynamic>;
|
||||
var popularMangaList = HeanCmsSearchModel.fromJson(popularManga);
|
||||
data = popularMangaList.data!;
|
||||
} else {
|
||||
var popularManga = jsonDecode(res) as List;
|
||||
data = popularManga.map((e) => Data.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
for (var a in data) {
|
||||
final status = parseHeanCmsStatus(a.status!);
|
||||
statusList.add(status);
|
||||
image.add(a.thumbnail!.startsWith("https://")
|
||||
? a.thumbnail
|
||||
: "${getMangaAPIUrl(source)}cover/${a.thumbnail}");
|
||||
name.add(a.title);
|
||||
url.add("/series/${a.seriesSlug!.replaceAll(timeStampRegex, '')}");
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getLatestUpdatesManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
var request = http.Request(
|
||||
'POST', Uri.parse('${getMangaAPIUrl(source)}series/querysearch'));
|
||||
request.body = json.encode({
|
||||
"page": page,
|
||||
"order": "desc",
|
||||
"order_by": "latest",
|
||||
"series_status": "Ongoing",
|
||||
"series_type": "Comic"
|
||||
});
|
||||
request.headers.addAll(_headers(source));
|
||||
|
||||
http.StreamedResponse response = await request.send();
|
||||
final res = await response.stream.bytesToString();
|
||||
List<Data>? data;
|
||||
if (res.startsWith("{")) {
|
||||
var popularManga = jsonDecode(res) as Map<String, dynamic>;
|
||||
var popularMangaList = HeanCmsSearchModel.fromJson(popularManga);
|
||||
data = popularMangaList.data!;
|
||||
} else {
|
||||
var popularManga = jsonDecode(res) as List;
|
||||
data = popularManga.map((e) => Data.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
for (var a in data) {
|
||||
final status = parseHeanCmsStatus(a.status!);
|
||||
statusList.add(status);
|
||||
image.add(a.thumbnail!.startsWith("https://")
|
||||
? a.thumbnail
|
||||
: "${getMangaAPIUrl(source)}cover/${a.thumbnail}");
|
||||
name.add(a.title);
|
||||
url.add("/series/${a.seriesSlug!.replaceAll(timeStampRegex, '')}");
|
||||
}
|
||||
|
||||
return mangaRes();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
List<Source> get heanCmsSourcesList => _heanCmsSourcesList;
|
||||
List<Source> _heanCmsSourcesList = [
|
||||
Source(
|
||||
sourceName: "YugenMangas",
|
||||
baseUrl: "https://yugenmangas.com",
|
||||
apiUrl: "https://api.yugenmangas.com/",
|
||||
lang: "es",
|
||||
typeSource: TypeSource.heancms,
|
||||
isNsfw: true,
|
||||
logoUrl: '',
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "OmegaScans",
|
||||
baseUrl: "https://omegascans.org",
|
||||
apiUrl: "https://api.omegascans.org/",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.heancms,
|
||||
isNsfw: true,
|
||||
logoUrl: '',
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
|
||||
dateFormatLocale: "en"),
|
||||
// Source(
|
||||
// sourceName: "ReaperScans",
|
||||
// baseUrl: "https://reaperscans.net",
|
||||
// apiUrl: "https://api.reaperscans.net/",
|
||||
// lang: "pt-br",
|
||||
// typeSource: TypeSource.heancms,
|
||||
// isNsfw: true,
|
||||
// logoUrl: 'https://reaperscans.net/images/webicon.png',
|
||||
// dateFormat: "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
|
||||
// dateFormatLocale: "pt-BR",
|
||||
// ),
|
||||
];
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
class HeanCmsSearchModel {
|
||||
Meta? meta;
|
||||
List<Data>? data;
|
||||
|
||||
HeanCmsSearchModel({this.meta, this.data});
|
||||
|
||||
HeanCmsSearchModel.fromJson(Map<String, dynamic> json) {
|
||||
meta = json['meta'] != null ? Meta.fromJson(json['meta']) : null;
|
||||
if (json['data'] != null) {
|
||||
data = <Data>[];
|
||||
json['data'].forEach((v) {
|
||||
data!.add(Data.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Meta {
|
||||
int? total;
|
||||
int? perPage;
|
||||
int? currentPage;
|
||||
int? lastPage;
|
||||
int? firstPage;
|
||||
String? firstPageUrl;
|
||||
String? lastPageUrl;
|
||||
String? nextPageUrl;
|
||||
|
||||
Meta({
|
||||
this.total,
|
||||
this.perPage,
|
||||
this.currentPage,
|
||||
this.lastPage,
|
||||
this.firstPage,
|
||||
this.firstPageUrl,
|
||||
this.lastPageUrl,
|
||||
this.nextPageUrl,
|
||||
});
|
||||
|
||||
Meta.fromJson(Map<String, dynamic> json) {
|
||||
total = json['total'];
|
||||
perPage = json['per_page'];
|
||||
currentPage = json['current_page'];
|
||||
lastPage = json['last_page'];
|
||||
firstPage = json['first_page'];
|
||||
firstPageUrl = json['first_page_url'];
|
||||
lastPageUrl = json['last_page_url'];
|
||||
nextPageUrl = json['next_page_url'];
|
||||
}
|
||||
}
|
||||
|
||||
class Data {
|
||||
String? title;
|
||||
int? id;
|
||||
String? seriesType;
|
||||
String? visibility;
|
||||
String? thumbnail;
|
||||
String? seriesSlug;
|
||||
String? status;
|
||||
String? latest;
|
||||
String? badge;
|
||||
List<Chapters>? chapters;
|
||||
Meta? meta;
|
||||
|
||||
Data(
|
||||
{this.title,
|
||||
this.id,
|
||||
this.seriesType,
|
||||
this.visibility,
|
||||
this.thumbnail,
|
||||
this.seriesSlug,
|
||||
this.status,
|
||||
this.latest,
|
||||
this.badge,
|
||||
this.chapters,
|
||||
this.meta});
|
||||
|
||||
Data.fromJson(Map<String, dynamic> json) {
|
||||
title = json['title'];
|
||||
id = json['id'];
|
||||
seriesType = json['series_type'];
|
||||
visibility = json['visibility'];
|
||||
thumbnail = json['thumbnail'];
|
||||
seriesSlug = json['series_slug'];
|
||||
status = json['status'];
|
||||
latest = json['latest'];
|
||||
badge = json['badge'];
|
||||
if (json['chapters'] != null) {
|
||||
chapters = <Chapters>[];
|
||||
json['chapters'].forEach((v) {
|
||||
chapters!.add(Chapters.fromJson(v));
|
||||
});
|
||||
}
|
||||
meta = json['meta'] != null ? Meta.fromJson(json['meta']) : null;
|
||||
}
|
||||
}
|
||||
|
||||
class Chapters {
|
||||
int? id;
|
||||
String? index;
|
||||
String? chapterName;
|
||||
String? chapterSlug;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? seriesId;
|
||||
|
||||
Chapters(
|
||||
{this.id,
|
||||
this.index,
|
||||
this.chapterName,
|
||||
this.chapterSlug,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.seriesId});
|
||||
|
||||
Chapters.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
index = json['index'];
|
||||
chapterName = json['chapter_name'];
|
||||
chapterSlug = json['chapter_slug'];
|
||||
createdAt = json['created_at'];
|
||||
updatedAt = json['updated_at'];
|
||||
seriesId = json['series_id'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['id'] = id;
|
||||
data['index'] = index;
|
||||
data['chapter_name'] = chapterName;
|
||||
data['chapter_slug'] = chapterSlug;
|
||||
data['created_at'] = createdAt;
|
||||
data['updated_at'] = updatedAt;
|
||||
data['series_id'] = seriesId;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import 'package:mangayomi/models/manga.dart';
|
||||
|
||||
RegExp timeStampRegex = RegExp("-\\d+");
|
||||
|
||||
parseHeanCmsStatus(String status) {
|
||||
return switch (status) {
|
||||
"Ongoing" => Status.ongoing,
|
||||
"Hiatus" => Status.onHiatus,
|
||||
"Dropped" => Status.canceled,
|
||||
"Completed" => Status.completed,
|
||||
"Finished" => Status.completed,
|
||||
_ => Status.unknown,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,594 +0,0 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
const defaultDateFormat = "MMMM dd, yyyy";
|
||||
const defaultDateFormatLocale = "en_US";
|
||||
|
||||
List<Source> get madaraSourcesList => _madaraSourcesList;
|
||||
List<Source> _madaraSourcesList = [
|
||||
Source(
|
||||
sourceName: "FR-Scan",
|
||||
baseUrl: "https://fr-scan.com",
|
||||
lang: "fr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: "fr"),
|
||||
Source(
|
||||
sourceName: "AstralManga",
|
||||
baseUrl: "https://astral-manga.fr",
|
||||
lang: "fr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd/mm/yyyy",
|
||||
dateFormatLocale: "fr"),
|
||||
Source(
|
||||
sourceName: "Akuma no Tenshi",
|
||||
baseUrl: "https://akumanotenshi.com",
|
||||
lang: "tr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd/MM/yyyy",
|
||||
dateFormatLocale: "pt-BR",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Adult Webtoon",
|
||||
baseUrl: "https://adultwebtoon.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
isNsfw: true,
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Adult Webtoon",
|
||||
baseUrl: "https://adultwebtoon.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
isNsfw: true,
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "ArazNovel",
|
||||
baseUrl: "https://www.araznovel.com",
|
||||
lang: "tr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "d MMMM yyyy",
|
||||
dateFormatLocale: "en",
|
||||
),
|
||||
Source(
|
||||
sourceName: "BestManga",
|
||||
baseUrl: "https://bestmanga.club",
|
||||
lang: "ru",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd.MM.yyyy",
|
||||
dateFormatLocale: "ru",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Chibi Manga",
|
||||
baseUrl: "https://www.cmreader.info",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMM dd, yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
// Source(
|
||||
// sourceName: "Clover Manga",
|
||||
// baseUrl: "https://clover-manga.com",
|
||||
// lang: "tr",
|
||||
// typeSource: TypeSource.madara,
|
||||
// logoUrl: '',
|
||||
// dateFormat: defaultDateFormat,
|
||||
// dateFormatLocale: "tr",
|
||||
// ),
|
||||
Source(
|
||||
sourceName: "CookieToon",
|
||||
baseUrl: "https://cookietoon.online",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd/MM/yyyy",
|
||||
dateFormatLocale: "pt-BR",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Drope Scan",
|
||||
baseUrl: "https://dropescan.com",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd/MM/yyyy",
|
||||
dateFormatLocale: "pt-BR",
|
||||
),
|
||||
Source(
|
||||
sourceName: "EvaScans",
|
||||
baseUrl: "https://evascans.com",
|
||||
lang: "tr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMM d, yyy",
|
||||
dateFormatLocale: "tr",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Final Scans",
|
||||
baseUrl: "https://finalscans.com",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
isNsfw: true,
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: "pt-BR",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Glory Manga",
|
||||
baseUrl: "https://glorymanga.com",
|
||||
lang: "tr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd/MM/yyy",
|
||||
dateFormatLocale: "tr",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Hentai Manga",
|
||||
baseUrl: "https://hentaimanga.me",
|
||||
isNsfw: true,
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "HentaiWebtoon",
|
||||
baseUrl: "https://hentaiwebtoon.com",
|
||||
isNsfw: true,
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Ikifeng",
|
||||
baseUrl: "https://ikifeng.com",
|
||||
lang: "es",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
isNsfw: true,
|
||||
dateFormat: "dd/MM/yyyy",
|
||||
dateFormatLocale: "es",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Inmortal Scan",
|
||||
baseUrl: "https://manga.mundodrama.site",
|
||||
lang: "es",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: "es",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Its Your Right Manhua",
|
||||
baseUrl: "https://itsyourightmanhua.com/",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Kami Sama Explorer",
|
||||
baseUrl: "https://leitor.kamisama.com.br",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd 'de' MMMM 'de' yyyy",
|
||||
dateFormatLocale: "pt-BR",
|
||||
),
|
||||
Source(
|
||||
sourceName: "KlikManga",
|
||||
baseUrl: "https://klikmanga.id",
|
||||
lang: "id",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: "id",
|
||||
),
|
||||
Source(
|
||||
sourceName: "KSGroupScans",
|
||||
baseUrl: "https://ksgroupscans.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "LHTranslation",
|
||||
baseUrl: "https://lhtranslation.net",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Lolicon",
|
||||
baseUrl: "https://lolicon.mobi",
|
||||
lang: "en",
|
||||
isNsfw: true,
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Lord Manga",
|
||||
baseUrl: "https://lordmanga.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "MangaVisa",
|
||||
baseUrl: "https://mangavisa.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "MangaClash",
|
||||
baseUrl: "https://mangaclash.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MM/dd/yy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Manga District",
|
||||
baseUrl: "https://mangadistrict.com",
|
||||
lang: "tr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
isNsfw: true,
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Manga-fast.com",
|
||||
baseUrl: "https://manga-fast.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "d MMMM'،' yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Manga Fenix",
|
||||
baseUrl: "https://manga-fenix.com",
|
||||
lang: "es",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: "es",
|
||||
),
|
||||
Source(
|
||||
sourceName: "MangaFreak.online",
|
||||
baseUrl: "https://mangafreak.online",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "d MMMM، yyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "MangaGreat",
|
||||
baseUrl: "https://mangagreat.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Manga Hentai",
|
||||
baseUrl: "https://mangahentai.me",
|
||||
lang: "en",
|
||||
isNsfw: true,
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "MangaMe",
|
||||
baseUrl: "https://mangame.org",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd.MM.yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Manga One Love",
|
||||
baseUrl: "https://mangaonelove.site",
|
||||
lang: "ru",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
isNsfw: true,
|
||||
dateFormat: "dd.MM.yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Manga Read",
|
||||
baseUrl: "https://mangaread.co",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "MangaRolls",
|
||||
baseUrl: "https://mangarolls.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Manga Şehri",
|
||||
baseUrl: "https://mangasehri.com",
|
||||
lang: "tr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd/MM/yyy",
|
||||
dateFormatLocale: "tr",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Mangasushi",
|
||||
baseUrl: "https://mangasushi.org",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Manhwa68",
|
||||
baseUrl: "https://manhwa68.com",
|
||||
lang: "en",
|
||||
isNsfw: true,
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
|
||||
Source(
|
||||
sourceName: "Manhwua.fans",
|
||||
baseUrl: "https://manhwua.fans",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "yyyy'年'M'月'd",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "ManyToon.me",
|
||||
baseUrl: "https://manytoon.me",
|
||||
lang: "en",
|
||||
isNsfw: true,
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
|
||||
Source(
|
||||
sourceName: "Milftoon",
|
||||
baseUrl: "https://milftoon.xxx",
|
||||
lang: "en",
|
||||
isNsfw: true,
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "d MMMM, yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "MurimScan",
|
||||
baseUrl: "https://murimscan.run",
|
||||
lang: "en",
|
||||
isNsfw: true,
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Ninja Scan",
|
||||
baseUrl: "https://ninjascan.xyz",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd 'de' MMMMM 'de' yyyy",
|
||||
dateFormatLocale: "pt-BR",
|
||||
),
|
||||
Source(
|
||||
sourceName: "NovelCrow",
|
||||
baseUrl: "https://novelcrow.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
isNsfw: true,
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Pirulito Rosa",
|
||||
baseUrl: "https://pirulitorosa.site",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
isNsfw: true,
|
||||
dateFormat: "dd/MM/yyy",
|
||||
dateFormatLocale: "pt-BR",
|
||||
),
|
||||
Source(
|
||||
sourceName: "RagnarokScan",
|
||||
baseUrl: "https://ragnarokscan.com",
|
||||
lang: "es",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMMM dd, yyyy",
|
||||
dateFormatLocale: "es",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Ragnarok Scanlation",
|
||||
baseUrl: "https://ragnarokscanlation.com",
|
||||
lang: "es",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: "es",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Rio2 Manga",
|
||||
baseUrl: "https://rio2manga.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: defaultDateFormat,
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "Romantik Manga",
|
||||
baseUrl: "https://rio2manga.com",
|
||||
lang: "tr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: defaultDateFormatLocale,
|
||||
),
|
||||
Source(
|
||||
sourceName: "SamuraiScan",
|
||||
baseUrl: "https://samuraiscan.com",
|
||||
lang: "es",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: "es",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Sdl scans",
|
||||
baseUrl: "https://sdlscans.com",
|
||||
lang: "es",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMMM dd, yyyy",
|
||||
dateFormatLocale: "es",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Shayami",
|
||||
baseUrl: "https://shayami.com",
|
||||
lang: "es",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMM d, yyy",
|
||||
dateFormatLocale: "es",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Taurus Fansub",
|
||||
baseUrl: "https://tatakaescan.com",
|
||||
lang: "es",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd/MM/yyy",
|
||||
dateFormatLocale: "es",
|
||||
),
|
||||
Source(
|
||||
sourceName: "The Sugar",
|
||||
baseUrl: "https://thesugarscan.com",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd/MM/yyyy",
|
||||
dateFormatLocale: "pt-BR",
|
||||
),
|
||||
// Source(
|
||||
// sourceName: "365Manga",
|
||||
// baseUrl: "https://365manga.com",
|
||||
// lang: "en",
|
||||
// typeSource: TypeSource.madara,
|
||||
// logoUrl: '',
|
||||
// dateFormat: defaultDateFormat,
|
||||
// dateFormatLocale: defaultDateFormatLocale,
|
||||
// ),
|
||||
Source(
|
||||
sourceName: "Tortuga Ceviri",
|
||||
baseUrl: "https://tortuga-ceviri.com",
|
||||
lang: "tr",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM d, yyyy",
|
||||
dateFormatLocale: "tr",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Tumangaonline.site",
|
||||
baseUrl: "https://tumangaonline.site",
|
||||
lang: "es",
|
||||
isNsfw: true,
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd MMMMM, yyyy",
|
||||
dateFormatLocale: "es",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Winter Scan",
|
||||
baseUrl: "https://winterscan.com",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd 'de' MMMM 'de' yyyy",
|
||||
dateFormatLocale: "pt-BR",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Yuri Verso",
|
||||
baseUrl: "https://yuri.live",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd/MM/yyyy",
|
||||
dateFormatLocale: "pt-BR",
|
||||
),
|
||||
Source(
|
||||
sourceName: "Zero Scan",
|
||||
baseUrl: "https://zeroscan.com.br",
|
||||
lang: "pt-br",
|
||||
isNsfw: true,
|
||||
typeSource: TypeSource.madara,
|
||||
logoUrl: '',
|
||||
dateFormat: "dd/MM/yyyy",
|
||||
dateFormatLocale: "pt-BR",
|
||||
),
|
||||
// Source(
|
||||
// sourceName: "مانجا ليك",
|
||||
// baseUrl: "https://mangalek.com",
|
||||
// lang: "ar",
|
||||
|
||||
// typeSource: TypeSource.madara,
|
||||
// logoUrl: '',
|
||||
// dateFormat: "MMMM dd, yyyy",
|
||||
// dateFormatLocale: "ar",
|
||||
// ),
|
||||
];
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/services/http_service/http_service.dart';
|
||||
import 'package:mangayomi/sources/multisrc/madara/src/utils.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:mangayomi/utils/reg_exp_matcher.dart';
|
||||
import 'package:mangayomi/utils/xpath_selector.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class Madara extends MangaYomiServices {
|
||||
@override
|
||||
Future<List<String>> getChapterUrl(
|
||||
{required Chapter chapter,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await await ref.watch(httpGetProvider(
|
||||
useUserAgent: true,
|
||||
url: chapter.url!,
|
||||
source: chapter.manga.value!.source!.toLowerCase(),
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
final res = dom!.querySelector(
|
||||
"div.page-break, li.blocks-gallery-item, .reading-content, .text-left img");
|
||||
final imgs = res!
|
||||
.querySelectorAll('img')
|
||||
.map((i) => regSrcMatcher(i.outerHtml).trim().trimLeft().trimRight())
|
||||
.toList();
|
||||
if (imgs.isNotEmpty && imgs.length == 1) {
|
||||
final pagesNumber = dom
|
||||
.querySelector("#single-pager")!
|
||||
.querySelectorAll("option")
|
||||
.map((e) => e.outerHtml)
|
||||
.toList();
|
||||
for (var i = 0; i < pagesNumber.length; i++) {
|
||||
if (i.toString().length == 1) {
|
||||
pageUrls.add(
|
||||
imgs.first.replaceAll("01", '0${int.parse(i.toString()) + 1}'));
|
||||
} else if (i.toString().length == 2) {
|
||||
pageUrls.add(
|
||||
imgs.first.replaceAll("01", '${int.parse(i.toString()) + 1}'));
|
||||
} else if (i.toString().length == 3) {
|
||||
pageUrls.add(
|
||||
imgs.first.replaceAll("01", '${int.parse(i.toString()) + 1}'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pageUrls = imgs;
|
||||
}
|
||||
|
||||
return pageUrls;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<GetManga?> getMangaDetail(
|
||||
{required GetManga manga,
|
||||
required String lang,
|
||||
required String source,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(
|
||||
httpGetProvider(url: manga.url!, source: source, resDom: true)
|
||||
.future) as Document?;
|
||||
author = dom!
|
||||
.querySelectorAll("div.author-content > a")
|
||||
.map((e) => e.text)
|
||||
.toList()
|
||||
.join(', ');
|
||||
description = dom
|
||||
.querySelectorAll(
|
||||
"div.description-summary div.summary__content, div.summary_content div.post-content_item > h5 + div, div.summary_content div.manga-excerpt, div.sinopsis div.contenedor, .description-summary > p")
|
||||
.map((e) => e.text)
|
||||
.toList()
|
||||
.first;
|
||||
status = madaraStatusParser(dom
|
||||
.querySelectorAll("div.summary-content")
|
||||
.map((e) => e.text.trim().trimLeft().trimRight())
|
||||
.toList()
|
||||
.last);
|
||||
|
||||
manga.imageUrl = dom
|
||||
.querySelectorAll("div.summary_image img")
|
||||
.map((e) => regSrcMatcher(e.outerHtml))
|
||||
.toList()
|
||||
.first;
|
||||
genre = dom
|
||||
.querySelectorAll("div.genres-content a")
|
||||
.map((e) => e.text)
|
||||
.toList();
|
||||
final mangaId = dom
|
||||
.querySelectorAll("div[id^=manga-chapters-holder]")
|
||||
.map((e) => e.attributes['data-id'])
|
||||
.first;
|
||||
Response? mangaResponse;
|
||||
final headers = {
|
||||
"Referer": "${getMangaBaseUrl(source)}/",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
};
|
||||
mangaResponse = await http.post(
|
||||
Uri.parse(
|
||||
"${getMangaBaseUrl(source)}/wp-admin/admin-ajax.php?action=manga_get_chapters&manga=$mangaId"),
|
||||
headers: headers);
|
||||
|
||||
if (mangaResponse.statusCode == 400) {
|
||||
mangaResponse = await http.post(Uri.parse("${manga.url}ajax/chapters"),
|
||||
headers: headers);
|
||||
}
|
||||
|
||||
final xpath = xpathSelector(mangaResponse.body);
|
||||
for (var url in xpath.query("//li/a/@href").attrs) {
|
||||
chapterUrl.add(url!);
|
||||
}
|
||||
for (var title in xpath.query("//li/a/text()").attrs) {
|
||||
chapterTitle.add(title!.trim().trimLeft().trimRight());
|
||||
}
|
||||
final dateF = xpath.query("//li/span/i/text()").attrs;
|
||||
|
||||
if (dateF.length == chapterUrl.length) {
|
||||
for (var date in dateF) {
|
||||
chapterDate.add(parseChapterDate(date!, source).toString());
|
||||
}
|
||||
} else if (dateF.length < chapterUrl.length) {
|
||||
final length = chapterUrl.length - dateF.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
chapterDate.add(DateTime.now().millisecondsSinceEpoch.toString());
|
||||
}
|
||||
for (var date in dateF) {
|
||||
chapterDate.add(parseChapterDate(date!, source).toString());
|
||||
}
|
||||
}
|
||||
return mangadetailRes(manga: manga, source: source);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getPopularManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
String? html;
|
||||
html = await ref.watch(httpGetProvider(
|
||||
url: '${getMangaBaseUrl(source)}/manga/page/$page/?m_orderby=views',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
if (xpathSelector(html!)
|
||||
.query('//*[@id^="manga-item"]/a/@title')
|
||||
.attrs
|
||||
.isEmpty) {
|
||||
html = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaBaseUrl(source)}/manga/page/$page/?m_orderby=trending',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
if (xpathSelector(html!)
|
||||
.query('//*[@id^="manga-item"]/a/@title')
|
||||
.attrs
|
||||
.isEmpty) {
|
||||
html = await ref.watch(httpGetProvider(
|
||||
url: getMangaBaseUrl(source), source: source, resDom: false)
|
||||
.future) as String?;
|
||||
}
|
||||
}
|
||||
final xpath = xpathSelector(html!);
|
||||
name = xpath.query('//*[@id^="manga-item"]/a/@title').attrs;
|
||||
url = xpath.query('//*[@class^="post-title"]/h3/a/@href').attrs;
|
||||
image = xpath.query('//*[@id^="manga-item"]/a/img/@data-src=').attrs;
|
||||
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> searchManga(
|
||||
{required String source,
|
||||
required String query,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final html = await ref.watch(httpGetProvider(
|
||||
url: '${getMangaBaseUrl(source)}/?s=$query&post_type=wp-manga',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
final xpath = xpathSelector(html!);
|
||||
name = xpath.query('//*[@class^="post-title"]/h3/a/text()').attrs;
|
||||
url = xpath.query('//*[@class^="post-title"]/h3/a/@href').attrs;
|
||||
image = name;
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getLatestUpdatesManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
String? html;
|
||||
html = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaBaseUrl(source)}/manga/page/$page/?m_orderby=latest',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
final xpath = xpathSelector(html!);
|
||||
name = xpath.query('//*[@id^="manga-item"]/a/@title').attrs;
|
||||
url = xpath.query('//*[@class^="post-title"]/h3/a/@href').attrs;
|
||||
image = xpath.query('//*[@id^="manga-item"]/a/img/@data-src=').attrs;
|
||||
return mangaRes();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
|
||||
class WordSet {
|
||||
final List<String> words;
|
||||
|
||||
WordSet(this.words);
|
||||
|
||||
bool anyWordIn(String dateString) {
|
||||
return words
|
||||
.any((word) => dateString.toLowerCase().contains(word.toLowerCase()));
|
||||
}
|
||||
|
||||
bool startsWith(String dateString) {
|
||||
return words
|
||||
.any((word) => dateString.toLowerCase().startsWith(word.toLowerCase()));
|
||||
}
|
||||
|
||||
bool endsWith(String dateString) {
|
||||
return words
|
||||
.any((word) => dateString.toLowerCase().endsWith(word.toLowerCase()));
|
||||
}
|
||||
}
|
||||
|
||||
int parseChapterDate(String? date, String source) {
|
||||
date ??= '';
|
||||
|
||||
int parseRelativeDate(String date) {
|
||||
final number = int.tryParse(RegExp(r"(\d+)").firstMatch(date)!.group(0)!);
|
||||
if (number == null) return 0;
|
||||
final cal = DateTime.now();
|
||||
|
||||
if (WordSet([
|
||||
"hari",
|
||||
"gün",
|
||||
"jour",
|
||||
"día",
|
||||
"dia",
|
||||
"day",
|
||||
"วัน",
|
||||
"ngày",
|
||||
"giorni",
|
||||
"أيام",
|
||||
"天"
|
||||
]).anyWordIn(date)) {
|
||||
return cal.subtract(Duration(days: number)).millisecondsSinceEpoch;
|
||||
} else if (WordSet([
|
||||
"jam",
|
||||
"saat",
|
||||
"heure",
|
||||
"hora",
|
||||
"hour",
|
||||
"ชั่วโมง",
|
||||
"giờ",
|
||||
"ore",
|
||||
"ساعة",
|
||||
"小时"
|
||||
]).anyWordIn(date)) {
|
||||
return cal.subtract(Duration(hours: number)).millisecondsSinceEpoch;
|
||||
} else if (WordSet(
|
||||
["menit", "dakika", "min", "minute", "minuto", "นาที", "دقائق"])
|
||||
.anyWordIn(date)) {
|
||||
return cal.subtract(Duration(minutes: number)).millisecondsSinceEpoch;
|
||||
} else if (WordSet(["detik", "segundo", "second", "วินาที"])
|
||||
.anyWordIn(date)) {
|
||||
return cal.subtract(Duration(seconds: number)).millisecondsSinceEpoch;
|
||||
} else if (WordSet(["week", "semana"]).anyWordIn(date)) {
|
||||
return cal.subtract(Duration(days: number * 7)).millisecondsSinceEpoch;
|
||||
} else if (WordSet(["month", "mes"]).anyWordIn(date)) {
|
||||
return cal.subtract(Duration(days: number * 30)).millisecondsSinceEpoch;
|
||||
} else if (WordSet(["year", "año"]).anyWordIn(date)) {
|
||||
return cal.subtract(Duration(days: number * 365)).millisecondsSinceEpoch;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (WordSet(["yesterday", "يوم واحد"]).startsWith(date)) {
|
||||
DateTime cal = DateTime.now().subtract(const Duration(days: 1));
|
||||
cal = DateTime(cal.year, cal.month, cal.day);
|
||||
return cal.millisecondsSinceEpoch;
|
||||
} else if (WordSet(["today"]).startsWith(date)) {
|
||||
DateTime cal = DateTime.now();
|
||||
cal = DateTime(cal.year, cal.month, cal.day);
|
||||
return cal.millisecondsSinceEpoch;
|
||||
} else if (WordSet(["يومين"]).startsWith(date)) {
|
||||
DateTime cal = DateTime.now().subtract(const Duration(days: 2));
|
||||
cal = DateTime(cal.year, cal.month, cal.day);
|
||||
return cal.millisecondsSinceEpoch;
|
||||
} else if (WordSet(["ago", "atrás", "önce", "قبل"]).endsWith(date)) {
|
||||
return parseRelativeDate(date);
|
||||
} else if (WordSet(["hace"]).startsWith(date)) {
|
||||
return parseRelativeDate(date);
|
||||
} else if (date.contains(RegExp(r"\d(st|nd|rd|th)"))) {
|
||||
final cleanedDate = date
|
||||
.split(" ")
|
||||
.map((it) => it.contains(RegExp(r"\d\D\D"))
|
||||
? it.replaceAll(RegExp(r"\D"), "")
|
||||
: it)
|
||||
.join(" ");
|
||||
return DateFormat(getFormatDate(source), getFormatDateLocale(source))
|
||||
.parse(cleanedDate)
|
||||
.millisecondsSinceEpoch;
|
||||
} else {
|
||||
return DateFormat(getFormatDate(source), getFormatDateLocale(source))
|
||||
.parse(date)
|
||||
.millisecondsSinceEpoch;
|
||||
}
|
||||
}
|
||||
|
||||
List<String> completedStatusList = [
|
||||
"Completed",
|
||||
"Completo",
|
||||
"Completado",
|
||||
"Concluído",
|
||||
"Concluido",
|
||||
"Finalizado",
|
||||
"Terminé",
|
||||
"Hoàn Thành",
|
||||
"مكتملة",
|
||||
"مكتمل",
|
||||
"已完结",
|
||||
];
|
||||
|
||||
List<String> ongoingStatusList = [
|
||||
"OnGoing",
|
||||
"Продолжается",
|
||||
"Updating",
|
||||
"Em Lançamento",
|
||||
"Em lançamento",
|
||||
"Em andamento",
|
||||
"Em Andamento",
|
||||
"En cours",
|
||||
"Ativo",
|
||||
"Lançando",
|
||||
"Đang Tiến Hành",
|
||||
"Devam Ediyor",
|
||||
"Devam ediyor",
|
||||
"In Corso",
|
||||
"In Arrivo",
|
||||
"مستمرة",
|
||||
"مستمر",
|
||||
"En Curso",
|
||||
"En curso",
|
||||
"Emision",
|
||||
"En marcha",
|
||||
"Publicandose",
|
||||
"En emision",
|
||||
"连载中",
|
||||
];
|
||||
|
||||
List<String> hiatusStatusList = [
|
||||
"On Hold",
|
||||
"Pausado",
|
||||
"En espera",
|
||||
];
|
||||
|
||||
List<String> canceledStatusList = [
|
||||
"Canceled",
|
||||
"Cancelado",
|
||||
];
|
||||
|
||||
Status madaraStatusParser(String status) {
|
||||
return canceledStatusList.contains(status)
|
||||
? Status.canceled
|
||||
: completedStatusList.contains(status)
|
||||
? Status.completed
|
||||
: ongoingStatusList.contains(status)
|
||||
? Status.ongoing
|
||||
: hiatusStatusList.contains(status)
|
||||
? Status.onHiatus
|
||||
: Status.unknown;
|
||||
}
|
||||
|
|
@ -1,316 +0,0 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
List<Source> get mangathemesiaSourcesList => _mangathemesiaSourcesList;
|
||||
List<Source> _mangathemesiaSourcesList = [
|
||||
Source(
|
||||
sourceName: "KomikLab",
|
||||
baseUrl: "https://komiklab.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "AnimatedGlitchedScans",
|
||||
baseUrl: "https://anigliscans.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "ArenaScans",
|
||||
baseUrl: "https://arenascans.net",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'https://arenascans.net/wp-content/uploads/2023/02/arena-logo-1.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "AzureScans",
|
||||
baseUrl: "https://azuremanga.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
|
||||
// Source(
|
||||
// sourceName: "Boosei",
|
||||
// baseUrl: "https://boosei.net",
|
||||
// lang: "id",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl:
|
||||
// 'https://cdn.boosei.com/wp-content/uploads/2021/11/Logo-Darkmode.png.webp',
|
||||
// dateFormat: "MMMM dd, yyyy",
|
||||
// dateFormatLocale: "id"),
|
||||
Source(
|
||||
sourceName: "Clayrer",
|
||||
baseUrl: "https://clayrer.net",
|
||||
lang: "es",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'https://clayrer.net/wp-content/uploads/2022/12/cropped-BackgroundEraser_20221205_145151639-1.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "es"),
|
||||
Source(
|
||||
sourceName: "CosmicScans",
|
||||
baseUrl: "https://cosmicscans.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "DiskusScan",
|
||||
baseUrl: "https://diskusscan.com",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'https://diskusscan.com/wp-content/uploads/2022/12/imagem_2022-12-26_222007506.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "pt"),
|
||||
Source(
|
||||
sourceName: "DuniaKomik",
|
||||
baseUrl: "https://duniakomik.id",
|
||||
lang: "id",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "id"),
|
||||
// Source(
|
||||
// sourceName: "FlameScans",
|
||||
// baseUrl: "https://flamescans.fr",
|
||||
// lang: "fr",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl: ''),
|
||||
// Source(
|
||||
// sourceName: "GremoryMangas",
|
||||
// baseUrl: "https://gremorymangas.com",
|
||||
// lang: "es",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl: 'https://gremorymangas.com/wp-content/uploads/2022/09/6941.png'),
|
||||
Source(
|
||||
sourceName: "InfernalVoidScans",
|
||||
baseUrl: "https://void-scans.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: '',
|
||||
isCloudflare: true,
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
|
||||
// Source(
|
||||
// sourceName: "Kiryuu",
|
||||
// baseUrl: "https://kiryuu.id",
|
||||
// lang: "id",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl: '',
|
||||
// dateFormat: "MMMM dd, yyyy",
|
||||
// dateFormatLocale: "id"),
|
||||
// Source(
|
||||
// sourceName: "KomikIndo",
|
||||
// baseUrl: "https://komikindo.co",
|
||||
// lang: "id",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl: '',
|
||||
// dateFormat: "MMM dd, yyyy",
|
||||
// dateFormatLocale: "id"),
|
||||
Source(
|
||||
sourceName: "KomikMama",
|
||||
baseUrl: "https://komikmama.co",
|
||||
lang: "id",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "id"),
|
||||
Source(
|
||||
sourceName: "Komiku",
|
||||
baseUrl: "https://komiku.com",
|
||||
lang: "id",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: 'https://komiku.com/wp-content/uploads/2022/03/logooo.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "id"),
|
||||
Source(
|
||||
sourceName: "KumaScans (Kuma Translation)",
|
||||
baseUrl: "https://kumascans.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'http://kumascans.com/wp-content/uploads/2021/01/web-rawkuma-copy.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
// Source(
|
||||
// sourceName: "MangaKita",
|
||||
// baseUrl: "https://mangakita.net",
|
||||
// lang: "id",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl: '',
|
||||
// dateFormat: "MMMM dd, yyyy",
|
||||
// dateFormatLocale: "en"),
|
||||
// Source(
|
||||
// sourceName: "MangaTale",
|
||||
// baseUrl: "https://mangatale.co",
|
||||
// lang: "id",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl:
|
||||
// 'http://mangatale.co/wp-content/uploads/2022/07/Group-of-23-Objects.png',
|
||||
// dateFormat: "MMMM dd, yyyy",
|
||||
// dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "MangaYaro",
|
||||
baseUrl: "https://mangayaro.net",
|
||||
lang: "id",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'https://cache.mangayaro.net/wp-content/uploads/2021/07/20210723_193720.png.webp',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "MangKomik",
|
||||
baseUrl: "https://mangkomik.net",
|
||||
lang: "id",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'https://cdn.mangkomik.net/wp-content/uploads/2023/02/image_2023-02-04_175453454-e1675527068905.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "id"),
|
||||
Source(
|
||||
sourceName: "MangásChan",
|
||||
baseUrl: "https://mangaschan.com",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: 'https://mangaschan.com/wp-content/uploads/Logo-web.webp',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "pt-br"),
|
||||
// Source(
|
||||
// sourceName: "ManhwaFreak",
|
||||
// baseUrl: "https://manhwafreak.com",
|
||||
// lang: "en",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl:
|
||||
// 'https://manhwafreak.com/wp-content/uploads/2022/09/New-Theme-Logo-2.png',
|
||||
// dateFormat: "MMMM dd, yyyy",
|
||||
// dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "ManhwaList",
|
||||
baseUrl: "https://manhwalist.in",
|
||||
lang: "id",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
// Source(
|
||||
// sourceName: "MasterKomik",
|
||||
// baseUrl: "https://masterkomik.com",
|
||||
// lang: "id",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl: 'https://masterkomik.com/wp-content/uploads/2020/12/New-MK.png',
|
||||
// dateFormat: "MMMM dd, yyyy",
|
||||
// dateFormatLocale: "id"),
|
||||
// Source(
|
||||
// sourceName: "Nekomik",
|
||||
// baseUrl: "https://nekomik.com",
|
||||
// lang: "id",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl: '',
|
||||
// dateFormat: "MMMM dd, yyyy",
|
||||
// dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "PhantomScans",
|
||||
baseUrl: "https://phantomscans.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: '',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
// Source(
|
||||
// sourceName: "PhoenixFansub",
|
||||
// baseUrl: "https://phoenixfansub.com",
|
||||
// lang: "es",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl: '',
|
||||
// dateFormat: "MMMM dd, yyyy",
|
||||
// dateFormatLocale: "es"),
|
||||
Source(
|
||||
sourceName: "PiScans",
|
||||
baseUrl: "https://piscans.in",
|
||||
lang: "id",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'https://piscans.in/wp-content/uploads/2022/11/WM-Websitewq1.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
// Source(
|
||||
// sourceName: "Rawkuma",
|
||||
// baseUrl: "https://rawkuma.com/",
|
||||
// lang: "ja",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl: 'http://rawkuma.com/wp-content/uploads/2021/12/Rawkuma-Logo.png',
|
||||
// dateFormat: "MMMM dd, yyyy",
|
||||
// dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "Readkomik",
|
||||
baseUrl: "https://readkomik.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'https://readkomik.com/wp-content/uploads/2021/01/PicsArt_01-23-03.51.01.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "SuryaScans",
|
||||
baseUrl: "https://suryascans.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'https://suryascans.com/wp-content/uploads/2022/09/new-text-surya-logo1x.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "SushiScan",
|
||||
baseUrl: "https://sushiscan.net",
|
||||
lang: "fr",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl: 'https://sushiscan.net/wp-content/uploads/SushiScanNewLogo.png',
|
||||
isCloudflare: true,
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "fr"),
|
||||
Source(
|
||||
sourceName: "TsundokuTraduções",
|
||||
baseUrl: "https://tsundoku.com.br",
|
||||
lang: "pt-br",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'https://tsundoku.com.br/wp-content/uploads/2022/01/TsunBranca.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "pt-br"),
|
||||
Source(
|
||||
sourceName: "TukangKomik",
|
||||
baseUrl: "https://tukangkomik.id",
|
||||
lang: "id",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'https://tukangkomik.id/wp-content/uploads/2022/03/tukangkomik-logo-1.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
// Source(
|
||||
// sourceName: "WestManga",
|
||||
// baseUrl: "https://westmanga.info",
|
||||
// lang: "id",
|
||||
// typeSource: TypeSource.mangathemesia,
|
||||
// logoUrl: 'https://westmanga.info/wp-content/uploads/2021/01/LOGOxx-1.png',
|
||||
// dateFormat: "MMMM dd, yyyy",
|
||||
// dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: "xCaliBRScans",
|
||||
baseUrl: "https://xcalibrscans.com",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.mangathemesia,
|
||||
logoUrl:
|
||||
'https://xcalibrscans.com/wp-content/uploads/2021/06/xcalibr-dark-v3.png',
|
||||
dateFormat: "MMMM dd, yyyy",
|
||||
dateFormatLocale: "en"),
|
||||
];
|
||||
|
|
@ -1,343 +0,0 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/services/http_service/http_service.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
|
||||
class MangaThemeSia extends MangaYomiServices {
|
||||
@override
|
||||
Future<GetManga?> getMangaDetail(
|
||||
{required GetManga manga,
|
||||
required String lang,
|
||||
required String source,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url: manga.url!, source: source, resDom: true, useUserAgent: true)
|
||||
.future) as Document?;
|
||||
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 = mangathemesiaStatusParser(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 = mangathemesiaStatusParser(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 = mangathemesiaStatusParser(resHtml
|
||||
.querySelectorAll('.tsinfo .imptdt')
|
||||
.map((e) => e.text.replaceAll('Status', '').trim())
|
||||
.toList()
|
||||
.first);
|
||||
} else {
|
||||
status = Status.unknown;
|
||||
}
|
||||
|
||||
//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(parseDate(ok, source));
|
||||
}
|
||||
}
|
||||
}
|
||||
return mangadetailRes(manga: manga, source: source);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getPopularManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
source = source.toLowerCase();
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
useUserAgent: true,
|
||||
url:
|
||||
'${getMangaBaseUrl(source)}/manga/?title=&page=$page&order=popular',
|
||||
source: source,
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
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();
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> searchManga(
|
||||
{required String source,
|
||||
required String query,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url: '${getMangaBaseUrl(source)}/?s=${query.trim()}',
|
||||
source: source,
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
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();
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getChapterUrl(
|
||||
{required Chapter chapter,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
useUserAgent: true,
|
||||
url: chapter.url!,
|
||||
source: "mangathemesia",
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
if (dom!.querySelectorAll('#readerarea').isNotEmpty) {
|
||||
final ta =
|
||||
dom.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 (dom.querySelectorAll(' #select-paged ').isNotEmpty) {
|
||||
final ee = dom
|
||||
.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.isNotEmpty && urls.length == 1) {
|
||||
for (var i = 0; i < urlss.length; i++) {
|
||||
if (urlss[i]!.length == 1) {
|
||||
pageUrls.add(
|
||||
urls.first!.replaceAll("001", '00${int.parse(urlss[i]!) + 1}'));
|
||||
} else if (urlss[i]!.length == 2) {
|
||||
pageUrls.add(
|
||||
urls.first!.replaceAll("001", '0${int.parse(urlss[i]!) + 1}'));
|
||||
} else if (urlss[i]!.length == 3) {
|
||||
pageUrls.add(
|
||||
urls.first!.replaceAll("001", '${int.parse(urlss[i]!) + 1}'));
|
||||
}
|
||||
}
|
||||
} else if (urls.length > 1 && urls.isNotEmpty) {
|
||||
for (var tt in urls) {
|
||||
pageUrls.add(tt!);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pageUrls;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getLatestUpdatesManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
source = source.toLowerCase();
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
useUserAgent: true,
|
||||
url:
|
||||
'${getMangaBaseUrl(source)}/manga/?title=&page=$page&order=update',
|
||||
source: source,
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
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();
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
}
|
||||
|
||||
mangathemesiaStatusParser(String status) {
|
||||
return (switch (status) {
|
||||
"ongoing" => Status.ongoing,
|
||||
"publishing" => Status.ongoing,
|
||||
"hiatus" => Status.onHiatus,
|
||||
"completed" => Status.completed,
|
||||
_ => Status.unknown,
|
||||
});
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
List<Source> get mmrcmsSourcesList => _mmrcmsSourcesList;
|
||||
List<Source> _mmrcmsSourcesList = [
|
||||
// Source(
|
||||
// sourceName: "Fallen Angels",
|
||||
// baseUrl: "https://manga.fascans.com",
|
||||
// lang: "en",
|
||||
// typeSource: TypeSource.mmrcms,
|
||||
// logoUrl: '',
|
||||
// dateFormat: "d MMM. yyyy",
|
||||
// dateFormatLocale: "en_US"),
|
||||
Source(
|
||||
sourceName: "Scan FR",
|
||||
baseUrl: "https://www.scan-fr.org",
|
||||
lang: "fr",
|
||||
typeSource: TypeSource.mmrcms,
|
||||
logoUrl: '',
|
||||
dateFormat: "d MMM. yyyy",
|
||||
dateFormatLocale: "en_US"),
|
||||
Source(
|
||||
sourceName: "Scan VF",
|
||||
baseUrl: "https://www.scan-vf.net",
|
||||
lang: "fr",
|
||||
typeSource: TypeSource.mmrcms,
|
||||
logoUrl: '',
|
||||
dateFormat: "d MMM. yyyy",
|
||||
dateFormatLocale: "en_US"), //
|
||||
Source(
|
||||
sourceName: "Komikid",
|
||||
baseUrl: "https://www.komikid.com",
|
||||
lang: "id",
|
||||
typeSource: TypeSource.mmrcms,
|
||||
logoUrl: '',
|
||||
dateFormat: "d MMM. yyyy",
|
||||
dateFormatLocale: "en_US"),
|
||||
// Source(
|
||||
// sourceName: "MangaHanta",
|
||||
// baseUrl: "http://mangahanta.com",
|
||||
// lang: "tr",
|
||||
// typeSource: TypeSource.mmrcms,
|
||||
// logoUrl: '',
|
||||
// dateFormat: "d MMM. yyyy",
|
||||
// dateFormatLocale: "en_US"),
|
||||
Source(
|
||||
sourceName: "MangaID",
|
||||
baseUrl: "https://mangaid.click",
|
||||
lang: "id",
|
||||
typeSource: TypeSource.mmrcms,
|
||||
logoUrl: '',
|
||||
dateFormat: "d MMM. yyyy",
|
||||
dateFormatLocale: "en_US"),
|
||||
Source(
|
||||
sourceName: "Jpmangas",
|
||||
baseUrl: "https://jpmangas.cc",
|
||||
lang: "fr",
|
||||
typeSource: TypeSource.mmrcms,
|
||||
logoUrl: '',
|
||||
dateFormat: "d MMM. yyyy",
|
||||
dateFormatLocale: "en_US"),
|
||||
];
|
||||
|
|
@ -1,222 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/services/http_service/http_service.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mmrcms/src/utils.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
import 'package:mangayomi/utils/reg_exp_matcher.dart';
|
||||
|
||||
class Mmrcms extends MangaYomiServices {
|
||||
@override
|
||||
Future<GetManga?> getMangaDetail(
|
||||
{required GetManga manga,
|
||||
required String lang,
|
||||
required String source,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(
|
||||
httpGetProvider(url: manga.url!, source: source, resDom: true)
|
||||
.future) as Document?;
|
||||
description = dom!
|
||||
.querySelectorAll('.row .well p')
|
||||
.map((e) => e.text.trim())
|
||||
.toList()
|
||||
.first;
|
||||
status = mmrcmsStatusParser(dom
|
||||
.querySelectorAll('.row .dl-horizontal dt')
|
||||
.where((e) =>
|
||||
e.innerHtml.toString().toLowerCase().contains("status") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("statut") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("estado") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("durum"))
|
||||
.map((e) => e.nextElementSibling!.text.trim())
|
||||
.toList()
|
||||
.first);
|
||||
if (dom.querySelectorAll(".row .dl-horizontal dt").isNotEmpty) {
|
||||
author = dom
|
||||
.querySelectorAll('.row .dl-horizontal dt')
|
||||
.where((e) =>
|
||||
e.innerHtml.toString().toLowerCase().contains("auteur(s)") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("author(s)") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("autor(es)") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("yazar(lar)") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("mangaka(lar)") ||
|
||||
e.innerHtml
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.contains("pengarang/penulis") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("autor") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("penulis"))
|
||||
.map((e) => e.nextElementSibling!.text
|
||||
.trim()
|
||||
.replaceAll(RegExp(r"\s+\b|\b\s"), ""))
|
||||
.toList()
|
||||
.first;
|
||||
final genr = dom
|
||||
.querySelectorAll('.row .dl-horizontal dt')
|
||||
.where((e) =>
|
||||
e.innerHtml.toString().toLowerCase().contains("categories") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("categorías") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("catégories") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("kategoriler") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("categorias") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("kategorie") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("kategori") ||
|
||||
e.innerHtml.toString().toLowerCase().contains("tagi"))
|
||||
.map((e) => e.nextElementSibling!.text.trim())
|
||||
.toList();
|
||||
if (genr.isNotEmpty) {
|
||||
genre = genr.first.replaceAll(RegExp(r"\s+\b|\b\s"), "").split(',');
|
||||
}
|
||||
}
|
||||
final rrr = dom.querySelectorAll(".row [class^=img-responsive]");
|
||||
final data = rrr.map((e) => e.outerHtml).toList();
|
||||
if (source.toLowerCase() == 'jpmangas' ||
|
||||
source.toLowerCase() == 'fr scan') {
|
||||
manga.imageUrl = regSrcMatcher(data.first).replaceAll('//', 'https://');
|
||||
} else {
|
||||
manga.imageUrl = regSrcMatcher(data.first);
|
||||
}
|
||||
|
||||
final ttt = dom
|
||||
.querySelectorAll("ul[class^=chapters] > li:not(.btn), table.table tr");
|
||||
if (ttt.isNotEmpty) {
|
||||
final data = ttt
|
||||
.map((e) => e.querySelector("[class^=chapter-title-rtl]")!)
|
||||
.toList();
|
||||
var name = data;
|
||||
for (var iaa in name) {
|
||||
chapterTitle.add(iaa.getElementsByTagName("a").first.text);
|
||||
chapterUrl
|
||||
.add(regHrefMatcher(iaa.getElementsByTagName("a").first.outerHtml));
|
||||
}
|
||||
final date = ttt
|
||||
.map((e) => e
|
||||
.getElementsByClassName("date-chapter-title-rtl")
|
||||
.map((e) => e.text.trim())
|
||||
.first)
|
||||
.toList();
|
||||
|
||||
for (var da in date) {
|
||||
chapterDate.add(parseDate(da, source));
|
||||
}
|
||||
}
|
||||
return mangadetailRes(manga: manga, source: source);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getPopularManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaBaseUrl(source)}/filterList?page=$page&sortBy=views&asc=false',
|
||||
source: source,
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
final urlElement = dom!.getElementsByClassName('chart-title');
|
||||
for (var e in urlElement) {
|
||||
RegExp exp = RegExp(r'href="([^"]+)"');
|
||||
Iterable<Match> matches = exp.allMatches(e.outerHtml);
|
||||
String? firstMatch = matches.first.group(1);
|
||||
url.add(firstMatch);
|
||||
name.add(e.text);
|
||||
}
|
||||
final imgElement = dom.getElementsByTagName('img');
|
||||
for (var e in imgElement) {
|
||||
RegExp exp = RegExp(r'src="([^"]+)"');
|
||||
Iterable<Match> matches = exp.allMatches(e.outerHtml);
|
||||
String? firstMatch = matches.first.group(1);
|
||||
image.add(firstMatch);
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> searchManga(
|
||||
{required String source,
|
||||
required String query,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url: '${getMangaBaseUrl(source)}/search?query=${query.trim()}',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
final rep = jsonDecode(response!);
|
||||
for (var ok in rep['suggestions']) {
|
||||
if (source == 'Read Comics Online') {
|
||||
url.add('${getMangaBaseUrl(source)}/comic/${ok['data']}');
|
||||
} else if (source == 'Scan VF') {
|
||||
url.add('${getMangaBaseUrl(source)}/${ok['data']}');
|
||||
} else {
|
||||
url.add('${getMangaBaseUrl(source)}/manga/${ok['data']}');
|
||||
}
|
||||
name.add(ok["value"]);
|
||||
image.add('');
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getChapterUrl(
|
||||
{required Chapter chapter,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await await ref.watch(httpGetProvider(
|
||||
useUserAgent: true,
|
||||
url: chapter.url!,
|
||||
source: chapter.manga.value!.source!.toLowerCase(),
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
if (dom!.querySelectorAll('#all > .img-responsive').isNotEmpty) {
|
||||
pageUrls = dom.querySelectorAll('#all > .img-responsive').map((e) {
|
||||
final RegExp regexx = RegExp(r'data-src="([^"]+)"');
|
||||
if (chapter.manga.value!.source!.toLowerCase() == 'fr scan' ||
|
||||
chapter.manga.value!.source!.toLowerCase() == 'jpmangas') {
|
||||
return regexx
|
||||
.allMatches(e.outerHtml)
|
||||
.first
|
||||
.group(1)!
|
||||
.replaceAll('//', 'https://')
|
||||
.replaceAll(RegExp(r"\s+\b|\b\s"), "")
|
||||
.replaceAll("https:https://", "https://");
|
||||
}
|
||||
return regexx
|
||||
.allMatches(e.outerHtml)
|
||||
.first
|
||||
.group(1)!
|
||||
.replaceAll(RegExp(r"\s+\b|\b\s"), "")
|
||||
.replaceAll("https:https://", "https://");
|
||||
}).toList();
|
||||
}
|
||||
return pageUrls;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getLatestUpdatesManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url: '${getMangaBaseUrl(source)}/latest-release?page=$page',
|
||||
source: source,
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
final urlElement = dom!.querySelectorAll("div.mangalist div.manga-item");
|
||||
|
||||
for (var e in urlElement) {
|
||||
RegExp exp = RegExp(r'href="([^"]+)"');
|
||||
Iterable<Match> matches = exp.allMatches(e.querySelector("a")!.outerHtml);
|
||||
String? firstMatch = matches.first.group(1);
|
||||
url.add(firstMatch);
|
||||
name.add(e.querySelector("a")!.text);
|
||||
image.add(
|
||||
"${getMangaBaseUrl(source)}/uploads/manga/${firstMatch!.split('/').last}/cover/cover_250x350.jpg");
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
import 'package:mangayomi/models/manga.dart';
|
||||
|
||||
Status mmrcmsStatusParser(String status) {
|
||||
return completedStatusList.contains(status)
|
||||
? Status.completed
|
||||
: ongoingStatusList.contains(status)
|
||||
? Status.ongoing
|
||||
: Status.unknown;
|
||||
}
|
||||
|
||||
List<String> completedStatusList = [
|
||||
"complete",
|
||||
"مكتملة",
|
||||
"complet",
|
||||
"completo",
|
||||
"zakończone",
|
||||
"concluído"
|
||||
];
|
||||
List<String> ongoingStatusList = [
|
||||
"ongoing",
|
||||
"مستمرة",
|
||||
"en cours",
|
||||
"em lançamento",
|
||||
"prace w toku",
|
||||
"ativo",
|
||||
"em andamento"
|
||||
];
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
|
||||
abstract class MangaYomiServices {
|
||||
List<String?> url = [];
|
||||
List<String?> name = [];
|
||||
List<String?> image = [];
|
||||
List<String> genre = [];
|
||||
String? author = "";
|
||||
Status? status;
|
||||
List<Status> statusList = [];
|
||||
List<String> chapterTitle = [];
|
||||
List<String> chapterUrl = [];
|
||||
List<String> chapterDate = [];
|
||||
String? description = "";
|
||||
List<Chapter> chapters = [];
|
||||
List<String> scanlators = [];
|
||||
List<String> pageUrls = [];
|
||||
List<GetManga> mangaList = [];
|
||||
List<GetManga> mangaRes() {
|
||||
for (var i = 0; i < name.length; i++) {
|
||||
mangaList.add(GetManga(
|
||||
genre: genre,
|
||||
author: author,
|
||||
status: statusList.isEmpty ? Status.unknown : statusList[i],
|
||||
chapters: chapters,
|
||||
imageUrl: image[i],
|
||||
description: description,
|
||||
url: url[i],
|
||||
name: name[i],
|
||||
source: ""));
|
||||
}
|
||||
return mangaList;
|
||||
}
|
||||
|
||||
GetManga mangadetailRes({required GetManga manga, required String source}) {
|
||||
if (chapterDate.isNotEmpty &&
|
||||
chapterTitle.isNotEmpty &&
|
||||
chapterUrl.isNotEmpty) {
|
||||
for (var i = 0; i < chapterUrl.length; i++) {
|
||||
chapters.add(Chapter(
|
||||
name: chapterTitle[i],
|
||||
url: chapterUrl[i],
|
||||
dateUpload: chapterDate[i],
|
||||
isBookmarked: false,
|
||||
scanlator: scanlators.isEmpty ? "" : scanlators[i],
|
||||
isRead: false,
|
||||
lastPageRead: '',
|
||||
));
|
||||
}
|
||||
}
|
||||
return GetManga(
|
||||
status: status,
|
||||
genre: genre,
|
||||
author: author,
|
||||
description: description,
|
||||
name: manga.name,
|
||||
url: manga.url,
|
||||
source: source,
|
||||
imageUrl: manga.imageUrl,
|
||||
chapters: chapters,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<GetManga?>> getPopularManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref});
|
||||
Future<List<GetManga?>> getLatestUpdatesManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref});
|
||||
Future<GetManga?> getMangaDetail(
|
||||
{required GetManga manga,
|
||||
required String lang,
|
||||
required String source,
|
||||
required AutoDisposeFutureProviderRef ref});
|
||||
Future<List<String>?> getChapterUrl(
|
||||
{required Chapter chapter, required AutoDisposeFutureProviderRef ref});
|
||||
Future<List<GetManga?>> searchManga(
|
||||
{required String source,
|
||||
required String query,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref});
|
||||
}
|
||||
|
||||
class GetManga {
|
||||
List<String> genre = [];
|
||||
List<Chapter> chapters = [];
|
||||
String? author;
|
||||
Status? status;
|
||||
String? source;
|
||||
String? url;
|
||||
String? name;
|
||||
String? imageUrl;
|
||||
String? description;
|
||||
GetManga({
|
||||
required this.genre,
|
||||
required this.author,
|
||||
required this.status,
|
||||
required this.chapters,
|
||||
required this.imageUrl,
|
||||
required this.description,
|
||||
required this.url,
|
||||
required this.name,
|
||||
required this.source,
|
||||
});
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/sources/multisrc/heancms/heancms_source_list.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/comick_source_list.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/mangadex_source_list.dart';
|
||||
import 'package:mangayomi/sources/src/en/mangahere/mangahere_source.dart';
|
||||
import 'package:mangayomi/sources/src/fr/japscan/japscan_source.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mangathemesia/mangathemesia_source_list.dart';
|
||||
import 'package:mangayomi/sources/multisrc/mmrcms/mmrcms_source_list.dart';
|
||||
import 'package:mangayomi/sources/multisrc/madara/madara_source_list.dart';
|
||||
// import 'package:mangayomi/sources/src/fr/mangakawaii/mangakawaii_source.dart';
|
||||
List<Source> get sourcesList => _sourcesList;
|
||||
List<Source> _sourcesList = [
|
||||
mangahereSource,
|
||||
// mangakawaiiSource,
|
||||
...mangathemesiaSourcesList,
|
||||
...comickSourcesList,
|
||||
...mmrcmsSourcesList,
|
||||
japscanSource,
|
||||
...heanCmsSourcesList,
|
||||
...madaraSourcesList,
|
||||
...mangaDexSourcesList
|
||||
];
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
const logoUrl =
|
||||
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75';
|
||||
const apiUrl = 'https://api.comick.fun';
|
||||
const baseUrl = 'https://comick.app';
|
||||
const isNsfw = true;
|
||||
List<Source> get comickSourcesList => _comickSourcesList;
|
||||
List<Source> _comickSourcesList = [
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'en',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'ar',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'pt',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'pt-br',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'it',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'ru',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'es',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'es-419',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'id',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'hi',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'de',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'ja',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'tr',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'pl',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'zh',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'zh-hk',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
Source(
|
||||
sourceName: 'Comick',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'fr',
|
||||
typeSource: TypeSource.comick,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: "en"),
|
||||
];
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/model/chapter_page_comick.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/model/manga_chapter_detail.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/model/manga_detail_comick.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/model/popular_manga_comick.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/model/search_manga_cimick.dart';
|
||||
import 'package:mangayomi/services/http_service/http_service.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/src/all/comick/src/utils/utils.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
|
||||
class Comick extends MangaYomiServices {
|
||||
@override
|
||||
Future<List<GetManga?>> getPopularManga(
|
||||
{required String source,
|
||||
required String lang,
|
||||
required int page,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
source = source.toLowerCase();
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}/v1.0/search?sort=follow&page=$page&tachiyomi=true',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
var popularManga = jsonDecode(response!) as List;
|
||||
var popularMangaList =
|
||||
popularManga.map((e) => PopularMangaModelComick.fromJson(e)).toList();
|
||||
|
||||
for (var popular in popularMangaList) {
|
||||
url.add("/comic/${popular.hid}#");
|
||||
name.add(popular.title);
|
||||
image.add(popular.coverUrl);
|
||||
}
|
||||
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<GetManga?> getMangaDetail(
|
||||
{required GetManga manga,
|
||||
required String lang,
|
||||
required String source,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}${manga.url!.replaceAll("#", '')}?tachiyomi=true',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
var mangaDetail = jsonDecode(response!) as Map<String, dynamic>;
|
||||
var mangaDetailLMap = MangaDetailModelComick.fromJson(mangaDetail);
|
||||
RegExp regExp = RegExp(r'name:\s*(.*?),');
|
||||
String authorr =
|
||||
regExp.firstMatch(mangaDetailLMap.authors![0].toString())?.group(1) ??
|
||||
'';
|
||||
status = parseStatut(mangaDetailLMap.comic!.status!);
|
||||
author = authorr;
|
||||
RegExp regExp1 = RegExp(r'name:\s*(.*?)}');
|
||||
|
||||
for (var ok in mangaDetailLMap.genres!) {
|
||||
genre.add(regExp1.firstMatch(ok.toString())!.group(1)!);
|
||||
}
|
||||
|
||||
description = mangaDetailLMap.comic!.desc;
|
||||
final request = await paginatedChapterListRequest(
|
||||
ref, manga.url!.replaceAll("#", ''), 1, source, lang);
|
||||
final chapterListResponse =
|
||||
MangaChapterModelComick.fromJson(json.decode(request));
|
||||
final mangaUrl = request.substring(request.indexOf(getMangaAPIUrl(source)) +
|
||||
getMangaAPIUrl(source).length);
|
||||
var resultSize = chapterListResponse.chapters!.length;
|
||||
var page = 2;
|
||||
|
||||
while (chapterListResponse.total! > resultSize) {
|
||||
final newResponse = await paginatedChapterListRequest(
|
||||
ref, manga.url!.replaceAll("#", ''), page, source, lang);
|
||||
final newChapterListResponse =
|
||||
MangaChapterModelComick.fromJson(json.decode(newResponse));
|
||||
chapterListResponse.chapters!.addAll(newChapterListResponse.chapters!);
|
||||
resultSize += newChapterListResponse.chapters!.length;
|
||||
page += 1;
|
||||
}
|
||||
|
||||
for (var chapter in chapterListResponse.chapters!) {
|
||||
scanlators.add(chapter.groupName!.isNotEmpty
|
||||
? chapter.groupName!.join()
|
||||
: "Unknown");
|
||||
chapterUrl.add("$mangaUrl/${chapter.hid}-chapter-${chapter.chap}-$lang");
|
||||
chapterDate.add(parseDate(chapter.createdAt!, source));
|
||||
chapterTitle.add(beautifyChapterName(
|
||||
chapter.vol ?? "", chapter.chap ?? "", chapter.title ?? "", lang));
|
||||
}
|
||||
|
||||
return mangadetailRes(manga: manga, source: source);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> searchManga(
|
||||
{required String source,
|
||||
required String query,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}/v1.0/search?q=${query.trim()}&tachiyomi=true&limit=50&page=1',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
var search = jsonDecode(response!) as List;
|
||||
var searchList =
|
||||
search.map((e) => MangaSearchModelComick.fromJson(e)).toList();
|
||||
|
||||
for (var search in searchList) {
|
||||
url.add("/comic/${search.hid}#");
|
||||
name.add(search.title);
|
||||
image.add(search.coverUrl);
|
||||
}
|
||||
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getChapterUrl(
|
||||
{required Chapter chapter,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
String mangaHid = chapter.url!.split('/').last.split('-').first;
|
||||
String source = chapter.manga.value!.source!;
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url: '${getMangaAPIUrl(source)}/chapter/$mangaHid?tachiyomi=true',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
var data = jsonDecode(response!) as Map<String, dynamic>;
|
||||
var page = ChapterPageComick.fromJson(data);
|
||||
|
||||
for (var url in page.chapter!.images!) {
|
||||
pageUrls.add(url.url!);
|
||||
}
|
||||
|
||||
return pageUrls;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getLatestUpdatesManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
source = source.toLowerCase();
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}/v1.0/search?sort=uploaded&page=$page&tachiyomi=true',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
var popularManga = jsonDecode(response!) as List;
|
||||
var popularMangaList =
|
||||
popularManga.map((e) => PopularMangaModelComick.fromJson(e)).toList();
|
||||
|
||||
for (var popular in popularMangaList) {
|
||||
url.add("/comic/${popular.hid}#");
|
||||
name.add(popular.title);
|
||||
image.add(popular.coverUrl);
|
||||
}
|
||||
|
||||
return mangaRes();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
class ChapterPageComick {
|
||||
ChapterC? chapter;
|
||||
bool? matureContent;
|
||||
List<Chapters>? chapters;
|
||||
String? canonical;
|
||||
String? seoTitle;
|
||||
String? seoDescription;
|
||||
String? chapTitle;
|
||||
bool? checkVol2Chap1;
|
||||
|
||||
ChapterPageComick(
|
||||
{this.chapter,
|
||||
this.matureContent,
|
||||
this.chapters,
|
||||
this.canonical,
|
||||
this.seoTitle,
|
||||
this.seoDescription,
|
||||
this.chapTitle,
|
||||
this.checkVol2Chap1});
|
||||
|
||||
ChapterPageComick.fromJson(Map<String, dynamic> json) {
|
||||
chapter =
|
||||
json['chapter'] != null ? ChapterC.fromJson(json['chapter']) : null;
|
||||
|
||||
matureContent = json['matureContent'];
|
||||
if (json['chapters'] != null) {
|
||||
chapters = <Chapters>[];
|
||||
json['chapters'].forEach((v) {
|
||||
chapters!.add(Chapters.fromJson(v));
|
||||
});
|
||||
}
|
||||
|
||||
canonical = json['canonical'];
|
||||
seoTitle = json['seoTitle'];
|
||||
seoDescription = json['seoDescription'];
|
||||
chapTitle = json['chapTitle'];
|
||||
checkVol2Chap1 = json['checkVol2Chap1'];
|
||||
}
|
||||
}
|
||||
|
||||
class ChapterC {
|
||||
int? id;
|
||||
String? chap;
|
||||
String? title;
|
||||
String? server;
|
||||
String? hid;
|
||||
String? hash;
|
||||
List<String>? groupName;
|
||||
String? createdAt;
|
||||
String? mdid;
|
||||
int? commentCount;
|
||||
int? upCount;
|
||||
int? downCount;
|
||||
String? status;
|
||||
String? lang;
|
||||
List<Images>? images;
|
||||
|
||||
ChapterC(
|
||||
{this.id,
|
||||
this.chap,
|
||||
this.title,
|
||||
this.server,
|
||||
this.hid,
|
||||
this.hash,
|
||||
this.groupName,
|
||||
this.createdAt,
|
||||
this.mdid,
|
||||
this.commentCount,
|
||||
this.upCount,
|
||||
this.downCount,
|
||||
this.status,
|
||||
this.lang,
|
||||
this.images});
|
||||
|
||||
ChapterC.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
chap = json['chap'];
|
||||
|
||||
title = json['title'];
|
||||
server = json['server'];
|
||||
hid = json['hid'];
|
||||
hash = json['hash'];
|
||||
|
||||
createdAt = json['created_at'];
|
||||
mdid = json['mdid'];
|
||||
commentCount = json['comment_count'];
|
||||
upCount = json['up_count'];
|
||||
downCount = json['down_count'];
|
||||
status = json['status'];
|
||||
lang = json['lang'];
|
||||
|
||||
if (json['images'] != null) {
|
||||
images = <Images>[];
|
||||
json['images'].forEach((v) {
|
||||
images!.add(Images.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Images {
|
||||
String? url;
|
||||
int? w;
|
||||
int? h;
|
||||
|
||||
Images({this.url, this.w, this.h});
|
||||
|
||||
Images.fromJson(Map<String, dynamic> json) {
|
||||
url = json['url'];
|
||||
w = json['w'];
|
||||
h = json['h'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['url'] = url;
|
||||
data['w'] = w;
|
||||
data['h'] = h;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Chapters {
|
||||
String? chap;
|
||||
String? vol;
|
||||
String? hid;
|
||||
String? lang;
|
||||
int? id;
|
||||
String? title;
|
||||
|
||||
Chapters({this.chap, this.vol, this.hid, this.lang, this.id, this.title});
|
||||
|
||||
Chapters.fromJson(Map<String, dynamic> json) {
|
||||
chap = json['chap'];
|
||||
vol = json['vol'];
|
||||
hid = json['hid'];
|
||||
lang = json['lang'];
|
||||
id = json['id'];
|
||||
title = json['title'];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
class MangaChapterModelComick {
|
||||
List<Chapters>? chapters;
|
||||
int? total;
|
||||
|
||||
MangaChapterModelComick({this.chapters, this.total});
|
||||
|
||||
MangaChapterModelComick.fromJson(Map<String, dynamic> json) {
|
||||
if (json['chapters'] != null) {
|
||||
chapters = <Chapters>[];
|
||||
json['chapters'].forEach((v) {
|
||||
chapters!.add(Chapters.fromJson(v));
|
||||
});
|
||||
}
|
||||
total = json['total'];
|
||||
}
|
||||
}
|
||||
|
||||
class Chapters {
|
||||
int? id;
|
||||
String? chap;
|
||||
String? title;
|
||||
String? vol;
|
||||
String? slug;
|
||||
String? lang;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? upCount;
|
||||
int? downCount;
|
||||
List<dynamic>? groupName;
|
||||
String? hid;
|
||||
List<MdGroups>? mdGroups;
|
||||
|
||||
Chapters(
|
||||
{this.id,
|
||||
this.chap,
|
||||
this.title,
|
||||
this.vol,
|
||||
this.slug,
|
||||
this.lang,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.upCount,
|
||||
this.downCount,
|
||||
this.groupName,
|
||||
this.hid,
|
||||
this.mdGroups});
|
||||
|
||||
Chapters.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
chap = json['chap'];
|
||||
title = json['title'];
|
||||
vol = json['vol'];
|
||||
slug = json['slug'];
|
||||
lang = json['lang'];
|
||||
createdAt = json['created_at'];
|
||||
// updatedAt = json['updated_at'];
|
||||
// upCount = json['up_count'];
|
||||
// downCount = json['down_count'];
|
||||
groupName = json['group_name'] ?? [];
|
||||
hid = json['hid'];
|
||||
// if (json['md_groups'] != null) {
|
||||
// mdGroups = <MdGroups>[];
|
||||
// json['md_groups'].forEach((v) {
|
||||
// mdGroups!.add(MdGroups.fromJson(v));
|
||||
// });
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
class MdGroups {
|
||||
String? slug;
|
||||
String? title;
|
||||
|
||||
MdGroups({this.slug, this.title});
|
||||
|
||||
MdGroups.fromJson(Map<String, dynamic> json) {
|
||||
slug = json['slug'];
|
||||
title = json['title'];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
class MangaDetailModelComick {
|
||||
FirstChap? firstChap;
|
||||
Comic? comic;
|
||||
List? artists;
|
||||
List? authors;
|
||||
List<String>? langList;
|
||||
|
||||
List? genres;
|
||||
bool? matureContent;
|
||||
bool? checkVol2Chap1;
|
||||
|
||||
MangaDetailModelComick(
|
||||
{this.firstChap,
|
||||
this.comic,
|
||||
this.artists,
|
||||
this.authors,
|
||||
this.langList,
|
||||
this.genres,
|
||||
this.matureContent,
|
||||
this.checkVol2Chap1});
|
||||
|
||||
MangaDetailModelComick.fromJson(Map<String, dynamic> json) {
|
||||
firstChap = json['firstChap'] != null
|
||||
? FirstChap.fromJson(json['firstChap'])
|
||||
: null;
|
||||
comic = json['comic'] != null ? Comic.fromJson(json['comic']) : null;
|
||||
if (json['artists'] != null) {
|
||||
artists = json['artists'];
|
||||
}
|
||||
if (json['authors'] != null) {
|
||||
authors = json['authors'];
|
||||
}
|
||||
|
||||
if (json['genres'] != null) {
|
||||
genres = json['genres'];
|
||||
}
|
||||
matureContent = json['matureContent'];
|
||||
checkVol2Chap1 = json['checkVol2Chap1'];
|
||||
}
|
||||
}
|
||||
|
||||
class FirstChap {
|
||||
String? chap;
|
||||
String? hid;
|
||||
String? lang;
|
||||
List<String>? groupName;
|
||||
|
||||
FirstChap({
|
||||
this.chap,
|
||||
this.hid,
|
||||
this.lang,
|
||||
this.groupName,
|
||||
});
|
||||
|
||||
FirstChap.fromJson(Map<String, dynamic> json) {
|
||||
chap = json['chap'];
|
||||
hid = json['hid'];
|
||||
lang = json['lang'];
|
||||
}
|
||||
}
|
||||
|
||||
class Comic {
|
||||
int? id;
|
||||
String? hid;
|
||||
String? title;
|
||||
String? country;
|
||||
int? status;
|
||||
|
||||
double? lastChapter;
|
||||
int? chapterCount;
|
||||
int? demographic;
|
||||
bool? hentai;
|
||||
int? userFollowCount;
|
||||
int? followRank;
|
||||
int? commentCount;
|
||||
int? followCount;
|
||||
String? desc;
|
||||
String? parsed;
|
||||
String? slug;
|
||||
int? year;
|
||||
String? bayesianRating;
|
||||
int? ratingCount;
|
||||
String? contentRating;
|
||||
bool? translationCompleted;
|
||||
bool? chapterNumbersResetOnNewVolumeManual;
|
||||
|
||||
String? iso6391;
|
||||
String? langName;
|
||||
String? langNative;
|
||||
String? coverUrl;
|
||||
|
||||
Comic(
|
||||
{this.id,
|
||||
this.hid,
|
||||
this.title,
|
||||
this.country,
|
||||
this.status,
|
||||
this.lastChapter,
|
||||
this.chapterCount,
|
||||
this.demographic,
|
||||
this.hentai,
|
||||
this.userFollowCount,
|
||||
this.followRank,
|
||||
this.commentCount,
|
||||
this.followCount,
|
||||
this.desc,
|
||||
this.parsed,
|
||||
this.slug,
|
||||
this.year,
|
||||
this.bayesianRating,
|
||||
this.ratingCount,
|
||||
this.contentRating,
|
||||
this.translationCompleted,
|
||||
this.chapterNumbersResetOnNewVolumeManual,
|
||||
this.iso6391,
|
||||
this.langName,
|
||||
this.langNative,
|
||||
this.coverUrl});
|
||||
|
||||
Comic.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
hid = json['hid'];
|
||||
title = json['title'];
|
||||
country = json['country'];
|
||||
status = json['status'];
|
||||
|
||||
// lastChapter = double.parse(json['last_chapter'].toString());
|
||||
chapterCount = json['chapter_count'];
|
||||
demographic = json['demographic'];
|
||||
hentai = json['hentai'];
|
||||
userFollowCount = json['user_follow_count'];
|
||||
followRank = json['follow_rank'];
|
||||
commentCount = json['comment_count'];
|
||||
followCount = json['follow_count'];
|
||||
desc = json['desc'];
|
||||
// parsed = json['parsed'];
|
||||
slug = json['slug'];
|
||||
// year = json['year'];
|
||||
// bayesianRating = json['bayesian_rating'];
|
||||
// ratingCount = json['rating_count'];
|
||||
// contentRating = json['content_rating'];
|
||||
// translationCompleted = json['translation_completed'];
|
||||
// chapterNumbersResetOnNewVolumeManual =
|
||||
// json['chapter_numbers_reset_on_new_volume_manual'];
|
||||
|
||||
// iso6391 = json['iso639_1'];
|
||||
// langName = json['lang_name'];
|
||||
// langNative = json['lang_native'];
|
||||
coverUrl = json['cover_url'];
|
||||
}
|
||||
}
|
||||
|
||||
class Artists {
|
||||
String? name;
|
||||
String? slug;
|
||||
|
||||
Artists({this.name, this.slug});
|
||||
|
||||
Artists.fromJson(Map<String, dynamic> json) {
|
||||
name = json['name'];
|
||||
slug = json['slug'];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
class PopularMangaModelComick {
|
||||
int? id;
|
||||
String? hid;
|
||||
String? slug;
|
||||
String? title;
|
||||
String? rating;
|
||||
String? bayesianRating;
|
||||
int? ratingCount;
|
||||
int? followCount;
|
||||
String? desc;
|
||||
double? lastChapter;
|
||||
bool? translationCompleted;
|
||||
int? viewCount;
|
||||
String? contentRating;
|
||||
int? demographic;
|
||||
List<int>? genres;
|
||||
int? userFollowCount;
|
||||
int? year;
|
||||
List<MdTitles>? mdTitles;
|
||||
List<MdCovers>? mdCovers;
|
||||
MuComics? muComics;
|
||||
String? coverUrl;
|
||||
|
||||
PopularMangaModelComick(
|
||||
{this.id,
|
||||
this.hid,
|
||||
this.slug,
|
||||
this.title,
|
||||
this.rating,
|
||||
this.bayesianRating,
|
||||
this.ratingCount,
|
||||
this.followCount,
|
||||
this.desc,
|
||||
this.lastChapter,
|
||||
this.translationCompleted,
|
||||
this.viewCount,
|
||||
this.contentRating,
|
||||
this.demographic,
|
||||
this.genres,
|
||||
this.userFollowCount,
|
||||
this.year,
|
||||
this.mdTitles,
|
||||
this.mdCovers,
|
||||
this.muComics,
|
||||
this.coverUrl});
|
||||
|
||||
PopularMangaModelComick.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
hid = json['hid'];
|
||||
slug = json['slug'];
|
||||
title = json['title'];
|
||||
rating = json['rating'];
|
||||
bayesianRating = json['bayesian_rating'];
|
||||
ratingCount = json['rating_count'];
|
||||
followCount = json['follow_count'];
|
||||
desc = json['desc'];
|
||||
lastChapter = double.parse(json['last_chapter'].toString());
|
||||
translationCompleted = json['translation_completed'];
|
||||
viewCount = json['view_count'];
|
||||
contentRating = json['content_rating'];
|
||||
demographic = json['demographic'];
|
||||
genres = json['genres'].cast<int>();
|
||||
userFollowCount = json['user_follow_count'];
|
||||
year = json['year'];
|
||||
if (json['md_titles'] != null) {
|
||||
mdTitles = <MdTitles>[];
|
||||
json['md_titles'].forEach((v) {
|
||||
mdTitles!.add(MdTitles.fromJson(v));
|
||||
});
|
||||
}
|
||||
if (json['md_covers'] != null) {
|
||||
mdCovers = <MdCovers>[];
|
||||
json['md_covers'].forEach((v) {
|
||||
mdCovers!.add(MdCovers.fromJson(v));
|
||||
});
|
||||
}
|
||||
muComics =
|
||||
json['mu_comics'] != null ? MuComics.fromJson(json['mu_comics']) : null;
|
||||
coverUrl = json['cover_url'];
|
||||
}
|
||||
}
|
||||
|
||||
class MdTitles {
|
||||
String? title;
|
||||
|
||||
MdTitles({this.title});
|
||||
|
||||
MdTitles.fromJson(Map<String, dynamic> json) {
|
||||
title = json['title'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['title'] = title;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class MdCovers {
|
||||
int? w;
|
||||
int? h;
|
||||
String? b2key;
|
||||
|
||||
MdCovers({this.w, this.h, this.b2key});
|
||||
|
||||
MdCovers.fromJson(Map<String, dynamic> json) {
|
||||
w = json['w'];
|
||||
h = json['h'];
|
||||
b2key = json['b2key'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['w'] = w;
|
||||
data['h'] = h;
|
||||
data['b2key'] = b2key;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class MuComics {
|
||||
int? year;
|
||||
|
||||
MuComics({this.year});
|
||||
|
||||
MuComics.fromJson(Map<String, dynamic> json) {
|
||||
year = json['year'];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
class MangaSearchModelComick {
|
||||
String? title;
|
||||
String? hid;
|
||||
int? id;
|
||||
String? slug;
|
||||
String? rating;
|
||||
int? ratingCount;
|
||||
int? followCount;
|
||||
int? userFollowCount;
|
||||
String? contentRating;
|
||||
int? demographic;
|
||||
List<MdCovers>? mdCovers;
|
||||
String? highlight;
|
||||
String? coverUrl;
|
||||
|
||||
MangaSearchModelComick(
|
||||
{this.title,
|
||||
this.hid,
|
||||
this.id,
|
||||
this.slug,
|
||||
this.rating,
|
||||
this.ratingCount,
|
||||
this.followCount,
|
||||
this.userFollowCount,
|
||||
this.contentRating,
|
||||
this.demographic,
|
||||
this.mdCovers,
|
||||
this.highlight,
|
||||
this.coverUrl});
|
||||
|
||||
MangaSearchModelComick.fromJson(Map<String, dynamic> json) {
|
||||
title = json['title'];
|
||||
|
||||
slug = json['slug'];
|
||||
hid = json['hid'];
|
||||
|
||||
coverUrl = json['cover_url'];
|
||||
}
|
||||
}
|
||||
|
||||
class MdCovers {
|
||||
String? vol;
|
||||
int? w;
|
||||
int? h;
|
||||
String? b2key;
|
||||
|
||||
MdCovers({this.vol, this.w, this.h, this.b2key});
|
||||
|
||||
MdCovers.fromJson(Map<String, dynamic> json) {
|
||||
vol = json['vol'];
|
||||
w = json['w'];
|
||||
h = json['h'];
|
||||
b2key = json['b2key'];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/services/http_service/http_service.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
|
||||
parseStatut(int i) {
|
||||
return switch (i) {
|
||||
1 => Status.ongoing,
|
||||
2 => Status.completed,
|
||||
3 => Status.canceled,
|
||||
4 => Status.onHiatus,
|
||||
_ => Status.unknown,
|
||||
};
|
||||
}
|
||||
|
||||
beautifyChapterName(String? vol, String? chap, String? title, String? lang) {
|
||||
return "${vol!.isNotEmpty ? chap!.isEmpty ? "Volume $vol " : "Vol. $vol " : ""}${chap!.isNotEmpty ? vol.isEmpty ? lang == "fr" ? "Chapitre $chap" : "Chapter $chap" : "Ch. $chap " : ""}${title!.isNotEmpty ? chap.isEmpty ? title : " : $title" : ""}";
|
||||
}
|
||||
|
||||
Future<String> paginatedChapterListRequest(AutoDisposeFutureProviderRef ref,
|
||||
String mangaUrl, int page, String source, String lang) async {
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
"${getMangaAPIUrl(source)}$mangaUrl/chapters?${lang != "all" ? 'lang=$lang' : ''}&tachiyomi=true&page=$page",
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
return response!;
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
const logoUrl = '';
|
||||
const apiUrl = 'https://api.mangadex.org';
|
||||
const baseUrl = 'https://mangadex.org';
|
||||
const isNsfw = true;
|
||||
List<Source> get mangaDexSourcesList => _mangaDexSourcesList;
|
||||
List<Source> _mangaDexSourcesList = [
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'en',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'ar',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'pt',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'pt-br',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'it',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'ru',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'es',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'es-419',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'id',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'hi',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'de',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'ja',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'tr',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'pl',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'zh',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'zh-hk',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
Source(
|
||||
sourceName: 'MangaDex',
|
||||
apiUrl: apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
lang: 'fr',
|
||||
typeSource: TypeSource.mangadex,
|
||||
logoUrl: logoUrl,
|
||||
dateFormat: "yyyy-MM-dd'T'HH:mm:ss+SSS",
|
||||
isNsfw: isNsfw,
|
||||
dateFormatLocale: 'en_Us'),
|
||||
];
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
|
||||
class Aggregate {
|
||||
String? result;
|
||||
Map<String, AggregateVolumes>? volumes;
|
||||
|
||||
Aggregate({this.result, this.volumes});
|
||||
|
||||
Aggregate.fromJson(Map<String, dynamic> json) {
|
||||
result = json['result'];
|
||||
if (json['volumes'] is List<dynamic>) {
|
||||
volumes = (json['volumes'] as List<dynamic>).asMap().map((key, value) =>
|
||||
MapEntry(key.toString(),
|
||||
AggregateVolumes.fromJson(value as Map<String, dynamic>)));
|
||||
} else {
|
||||
volumes = (json['volumes'] as Map<String, dynamic>?)?.map((key, value) =>
|
||||
MapEntry(
|
||||
key, AggregateVolumes.fromJson(value as Map<String, dynamic>)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AggregateVolumes {
|
||||
String? volume;
|
||||
String? count;
|
||||
Map<String, AggregateChapter>? chapters;
|
||||
|
||||
AggregateVolumes({this.volume, this.chapters, this.count});
|
||||
AggregateVolumes.fromJson(Map<String, dynamic> json) {
|
||||
volume = json['volume'];
|
||||
count = json['count'].toString();
|
||||
if (json['chapters'] is List<dynamic>) {
|
||||
chapters = (json['chapters'] as List<dynamic>).asMap().map((key, value) =>
|
||||
MapEntry(key.toString(),
|
||||
AggregateChapter.fromJson(value as Map<String, dynamic>)));
|
||||
} else {
|
||||
chapters = (json['chapters'] as Map<String, dynamic>?)?.map(
|
||||
(key, value) => MapEntry(
|
||||
key, AggregateChapter.fromJson(value as Map<String, dynamic>)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AggregateChapter {
|
||||
String? chapter;
|
||||
String? count;
|
||||
|
||||
AggregateChapter({this.chapter, this.count});
|
||||
AggregateChapter.fromJson(Map<String, dynamic> json) {
|
||||
chapter = json['chapter'];
|
||||
count = json['count'].toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
class ChapterMDX {
|
||||
String? result;
|
||||
String? response;
|
||||
List<Data>? data;
|
||||
int? limit;
|
||||
int? offset;
|
||||
int? total;
|
||||
|
||||
ChapterMDX(
|
||||
{this.result,
|
||||
this.response,
|
||||
this.data,
|
||||
this.limit,
|
||||
this.offset,
|
||||
this.total});
|
||||
|
||||
ChapterMDX.fromJson(Map<String, dynamic> json) {
|
||||
result = json['result'];
|
||||
response = json['response'];
|
||||
if (json['data'] != null) {
|
||||
data = <Data>[];
|
||||
json['data'].forEach((v) {
|
||||
data!.add(Data.fromJson(v));
|
||||
});
|
||||
}
|
||||
limit = json['limit'];
|
||||
offset = json['offset'];
|
||||
total = json['total'];
|
||||
}
|
||||
}
|
||||
|
||||
class Data {
|
||||
String? id;
|
||||
String? type;
|
||||
Attributes? attributes;
|
||||
List<Relationships>? relationships;
|
||||
|
||||
Data({this.id, this.type, this.attributes, this.relationships});
|
||||
|
||||
Data.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
type = json['type'];
|
||||
attributes = json['attributes'] != null
|
||||
? Attributes.fromJson(json['attributes'])
|
||||
: null;
|
||||
if (json['relationships'] != null) {
|
||||
relationships = <Relationships>[];
|
||||
json['relationships'].forEach((v) {
|
||||
relationships!.add(Relationships.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Attributes {
|
||||
String? volume;
|
||||
String? chapter;
|
||||
String? title;
|
||||
String? translatedLanguage;
|
||||
|
||||
String? publishAt;
|
||||
String? readableAt;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? pages;
|
||||
int? version;
|
||||
|
||||
Attributes(
|
||||
{this.volume,
|
||||
this.chapter,
|
||||
this.title,
|
||||
this.translatedLanguage,
|
||||
this.publishAt,
|
||||
this.readableAt,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.pages,
|
||||
this.version});
|
||||
|
||||
Attributes.fromJson(Map<String, dynamic> json) {
|
||||
volume = json['volume'];
|
||||
chapter = json['chapter'];
|
||||
title = json['title'];
|
||||
translatedLanguage = json['translatedLanguage'];
|
||||
|
||||
publishAt = json['publishAt'];
|
||||
readableAt = json['readableAt'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
pages = json['pages'];
|
||||
version = json['version'];
|
||||
}
|
||||
}
|
||||
|
||||
class Relationships {
|
||||
String? id;
|
||||
String? type;
|
||||
RAttributes? attributes;
|
||||
Relationships({this.id, this.type, this.attributes});
|
||||
|
||||
Relationships.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
type = json['type'];
|
||||
// log(json['attributes'].toString());
|
||||
attributes = json['attributes'] != null
|
||||
? RAttributes.fromJson(json['attributes'])
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
class RAttributes {
|
||||
String? name;
|
||||
String? username;
|
||||
RAttributes({
|
||||
this.name,
|
||||
this.username,
|
||||
});
|
||||
|
||||
RAttributes.fromJson(Map<String, dynamic> json) {
|
||||
name = json['name'];
|
||||
username = json['username'];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
class CoverAA {
|
||||
String? result;
|
||||
String? response;
|
||||
List<CoverData>? data;
|
||||
int? limit;
|
||||
int? offset;
|
||||
int? total;
|
||||
|
||||
CoverAA(
|
||||
{this.result,
|
||||
this.response,
|
||||
this.data,
|
||||
this.limit,
|
||||
this.offset,
|
||||
this.total});
|
||||
|
||||
CoverAA.fromJson(Map<String, dynamic> json) {
|
||||
result = json['result'];
|
||||
response = json['response'];
|
||||
if (json['data'] != null) {
|
||||
data = <CoverData>[];
|
||||
json['data'].forEach((v) {
|
||||
data!.add(CoverData.fromJson(v));
|
||||
});
|
||||
}
|
||||
limit = json['limit'];
|
||||
offset = json['offset'];
|
||||
total = json['total'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['result'] = result;
|
||||
data['response'] = response;
|
||||
if (this.data != null) {
|
||||
data['data'] = this.data!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
data['limit'] = limit;
|
||||
data['offset'] = offset;
|
||||
data['total'] = total;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class CoverData {
|
||||
String? id;
|
||||
String? type;
|
||||
Attributes? attributes;
|
||||
List<Relationships>? relationships;
|
||||
|
||||
CoverData({this.id, this.type, this.attributes, this.relationships});
|
||||
|
||||
CoverData.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
type = json['type'];
|
||||
attributes = json['attributes'] != null
|
||||
? Attributes.fromJson(json['attributes'])
|
||||
: null;
|
||||
if (json['relationships'] != null) {
|
||||
relationships = <Relationships>[];
|
||||
json['relationships'].forEach((v) {
|
||||
relationships!.add(Relationships.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['id'] = id;
|
||||
data['type'] = type;
|
||||
if (attributes != null) {
|
||||
data['attributes'] = attributes!.toJson();
|
||||
}
|
||||
if (relationships != null) {
|
||||
data['relationships'] =
|
||||
relationships!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Attributes {
|
||||
String? description;
|
||||
String? volume;
|
||||
String? fileName;
|
||||
String? locale;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? version;
|
||||
|
||||
Attributes(
|
||||
{this.description,
|
||||
this.volume,
|
||||
this.fileName,
|
||||
this.locale,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.version});
|
||||
|
||||
Attributes.fromJson(Map<String, dynamic> json) {
|
||||
description = json['description'];
|
||||
volume = json['volume'];
|
||||
fileName = json['fileName'];
|
||||
locale = json['locale'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
version = json['version'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['description'] = description;
|
||||
data['volume'] = volume;
|
||||
data['fileName'] = fileName;
|
||||
data['locale'] = locale;
|
||||
data['createdAt'] = createdAt;
|
||||
data['updatedAt'] = updatedAt;
|
||||
data['version'] = version;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Relationships {
|
||||
String? id;
|
||||
String? type;
|
||||
|
||||
Relationships({this.id, this.type});
|
||||
|
||||
Relationships.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
type = json['type'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['id'] = id;
|
||||
data['type'] = type;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
class GetChapterUrl {
|
||||
String? result;
|
||||
String? baseUrl;
|
||||
Chapter? chapter;
|
||||
|
||||
GetChapterUrl({this.result, this.baseUrl, this.chapter});
|
||||
|
||||
GetChapterUrl.fromJson(Map<String, dynamic> json) {
|
||||
result = json['result'];
|
||||
baseUrl = json['baseUrl'];
|
||||
chapter =
|
||||
json['chapter'] != null ? Chapter.fromJson(json['chapter']) : null;
|
||||
}
|
||||
}
|
||||
|
||||
class Chapter {
|
||||
String? hash;
|
||||
List<String>? data;
|
||||
List<String>? dataSaver;
|
||||
|
||||
Chapter({this.hash, this.data, this.dataSaver});
|
||||
|
||||
Chapter.fromJson(Map<String, dynamic> json) {
|
||||
hash = json['hash'];
|
||||
data = json['data'].cast<String>();
|
||||
dataSaver = json['dataSaver'].cast<String>();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,330 +0,0 @@
|
|||
class MangaDexDto {
|
||||
String? result;
|
||||
String? response;
|
||||
List<Data>? data;
|
||||
int? limit;
|
||||
int? offset;
|
||||
int? total;
|
||||
|
||||
MangaDexDto(
|
||||
{this.result,
|
||||
this.response,
|
||||
this.data,
|
||||
this.limit,
|
||||
this.offset,
|
||||
this.total});
|
||||
|
||||
MangaDexDto.fromJson(Map<String, dynamic> json) {
|
||||
result = json['result'];
|
||||
response = json['response'];
|
||||
if (json['data'] != null) {
|
||||
data = <Data>[];
|
||||
json['data'].forEach((v) {
|
||||
data!.add(Data.fromJson(v));
|
||||
});
|
||||
}
|
||||
limit = json['limit'];
|
||||
offset = json['offset'];
|
||||
total = json['total'];
|
||||
}
|
||||
}
|
||||
|
||||
class Data {
|
||||
String? id;
|
||||
String? type;
|
||||
Attributes? attributes;
|
||||
List<Relationships>? relationships;
|
||||
|
||||
Data({this.id, this.type, this.attributes, this.relationships});
|
||||
|
||||
Data.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
type = json['type'];
|
||||
attributes = json['attributes'] != null
|
||||
? Attributes.fromJson(json['attributes'])
|
||||
: null;
|
||||
if (json['relationships'] != null) {
|
||||
relationships = <Relationships>[];
|
||||
json['relationships'].forEach((v) {
|
||||
relationships!.add(Relationships.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Attributes {
|
||||
Map<String, dynamic>? title;
|
||||
List<dynamic>? altTitles;
|
||||
Description? description;
|
||||
bool? isLocked;
|
||||
Links? links;
|
||||
String? originalLanguage;
|
||||
String? lastVolume;
|
||||
String? lastChapter;
|
||||
String? publicationDemographic;
|
||||
String? status;
|
||||
int? year;
|
||||
String? contentRating;
|
||||
List<Tags>? tags;
|
||||
String? state;
|
||||
bool? chapterNumbersResetOnNewVolume;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? version;
|
||||
List<String>? availableTranslatedLanguages;
|
||||
String? latestUploadedChapter;
|
||||
|
||||
Attributes(
|
||||
{this.title,
|
||||
this.altTitles,
|
||||
this.description,
|
||||
this.isLocked,
|
||||
this.links,
|
||||
this.originalLanguage,
|
||||
this.lastVolume,
|
||||
this.lastChapter,
|
||||
this.publicationDemographic,
|
||||
this.status,
|
||||
this.year,
|
||||
this.contentRating,
|
||||
this.tags,
|
||||
this.state,
|
||||
this.chapterNumbersResetOnNewVolume,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.version,
|
||||
this.availableTranslatedLanguages,
|
||||
this.latestUploadedChapter});
|
||||
|
||||
Attributes.fromJson(Map<String, dynamic> json) {
|
||||
title = json['title'] as Map<String, dynamic>;
|
||||
if (json['altTitles'] != null) {
|
||||
altTitles = json['altTitles'];
|
||||
}
|
||||
description = json['description'] != null
|
||||
? Description.fromJson(json['description'])
|
||||
: null;
|
||||
isLocked = json['isLocked'];
|
||||
links = json['links'] != null ? Links.fromJson(json['links']) : null;
|
||||
originalLanguage = json['originalLanguage'];
|
||||
lastVolume = json['lastVolume'];
|
||||
lastChapter = json['lastChapter'];
|
||||
publicationDemographic = json['publicationDemographic'];
|
||||
status = json['status'];
|
||||
year = json['year'];
|
||||
contentRating = json['contentRating'];
|
||||
if (json['tags'] != null) {
|
||||
tags = <Tags>[];
|
||||
json['tags'].forEach((v) {
|
||||
tags!.add(Tags.fromJson(v));
|
||||
});
|
||||
}
|
||||
state = json['state'];
|
||||
chapterNumbersResetOnNewVolume = json['chapterNumbersResetOnNewVolume'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
version = json['version'];
|
||||
availableTranslatedLanguages =
|
||||
json['availableTranslatedLanguages'].cast<String>();
|
||||
latestUploadedChapter = json['latestUploadedChapter'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Description {
|
||||
String? en;
|
||||
String? fr;
|
||||
String? pt;
|
||||
String? ptBr;
|
||||
String? zhHk;
|
||||
String? ru;
|
||||
String? es;
|
||||
String? ja;
|
||||
String? tr;
|
||||
String? uk;
|
||||
String? esLa;
|
||||
String? ko;
|
||||
String? de;
|
||||
String? id;
|
||||
String? it;
|
||||
String? pl;
|
||||
String? th;
|
||||
String? zh;
|
||||
String? cs;
|
||||
|
||||
Description(
|
||||
{this.en,
|
||||
this.fr,
|
||||
this.pt,
|
||||
this.ptBr,
|
||||
this.zhHk,
|
||||
this.ru,
|
||||
this.es,
|
||||
this.ja,
|
||||
this.tr,
|
||||
this.uk,
|
||||
this.esLa,
|
||||
this.ko,
|
||||
this.de,
|
||||
this.id,
|
||||
this.it,
|
||||
this.pl,
|
||||
this.th,
|
||||
this.zh,
|
||||
this.cs});
|
||||
|
||||
Description.fromJson(Map<String, dynamic> json) {
|
||||
en = json['en'];
|
||||
fr = json['fr'];
|
||||
pt = json['pt'];
|
||||
ptBr = json['pt-br'];
|
||||
zhHk = json['zh-hk'];
|
||||
ru = json['ru'];
|
||||
es = json['es'];
|
||||
ja = json['ja'];
|
||||
tr = json['tr'];
|
||||
uk = json['uk'];
|
||||
esLa = json['es-la'];
|
||||
ko = json['ko'];
|
||||
de = json['de'];
|
||||
id = json['id'];
|
||||
it = json['it'];
|
||||
pl = json['pl'];
|
||||
th = json['th'];
|
||||
zh = json['zh'];
|
||||
cs = json['cs'];
|
||||
}
|
||||
}
|
||||
|
||||
class Links {
|
||||
String? al;
|
||||
String? mu;
|
||||
String? amz;
|
||||
String? mal;
|
||||
String? engtl;
|
||||
String? kt;
|
||||
String? cdj;
|
||||
String? ebj;
|
||||
String? raw;
|
||||
String? ap;
|
||||
String? bw;
|
||||
String? nu;
|
||||
|
||||
Links(
|
||||
{this.al,
|
||||
this.mu,
|
||||
this.amz,
|
||||
this.mal,
|
||||
this.engtl,
|
||||
this.kt,
|
||||
this.cdj,
|
||||
this.ebj,
|
||||
this.raw,
|
||||
this.ap,
|
||||
this.bw,
|
||||
this.nu});
|
||||
|
||||
Links.fromJson(Map<String, dynamic> json) {
|
||||
al = json['al'];
|
||||
mu = json['mu'];
|
||||
amz = json['amz'];
|
||||
mal = json['mal'];
|
||||
engtl = json['engtl'];
|
||||
kt = json['kt'];
|
||||
cdj = json['cdj'];
|
||||
ebj = json['ebj'];
|
||||
raw = json['raw'];
|
||||
ap = json['ap'];
|
||||
bw = json['bw'];
|
||||
nu = json['nu'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['al'] = al;
|
||||
data['mu'] = mu;
|
||||
data['amz'] = amz;
|
||||
data['mal'] = mal;
|
||||
data['engtl'] = engtl;
|
||||
data['kt'] = kt;
|
||||
data['cdj'] = cdj;
|
||||
data['ebj'] = ebj;
|
||||
data['raw'] = raw;
|
||||
data['ap'] = ap;
|
||||
data['bw'] = bw;
|
||||
data['nu'] = nu;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Tags {
|
||||
TagAttributes? attributes;
|
||||
|
||||
Tags({
|
||||
this.attributes,
|
||||
});
|
||||
|
||||
Tags.fromJson(Map<String, dynamic> json) {
|
||||
attributes = json['attributes'] != null
|
||||
? TagAttributes.fromJson(json['attributes'])
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
class TagAttributes {
|
||||
String? group;
|
||||
|
||||
TagAttributes({this.group});
|
||||
|
||||
TagAttributes.fromJson(Map<String, dynamic> json) {
|
||||
group = json['group'];
|
||||
}
|
||||
}
|
||||
|
||||
class Relationships {
|
||||
String? id;
|
||||
String? type;
|
||||
RAttributes? attributes;
|
||||
String? related;
|
||||
|
||||
Relationships({this.id, this.type, this.attributes, this.related});
|
||||
|
||||
Relationships.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
type = json['type'];
|
||||
attributes = json['attributes'] != null
|
||||
? RAttributes.fromJson(json['attributes'])
|
||||
: null;
|
||||
related = json['related'];
|
||||
}
|
||||
}
|
||||
|
||||
class RAttributes {
|
||||
String? description;
|
||||
String? volume;
|
||||
String? fileName;
|
||||
String? locale;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? version;
|
||||
|
||||
RAttributes(
|
||||
{this.description,
|
||||
this.volume,
|
||||
this.fileName,
|
||||
this.locale,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.version});
|
||||
|
||||
RAttributes.fromJson(Map<String, dynamic> json) {
|
||||
description = json['description'];
|
||||
volume = json['volume'];
|
||||
fileName = json['fileName'];
|
||||
locale = json['locale'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
version = json['version'];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
class MDXDetail {
|
||||
String? result;
|
||||
String? response;
|
||||
Dataa? data;
|
||||
|
||||
MDXDetail({this.result, this.response, this.data});
|
||||
|
||||
MDXDetail.fromJson(Map<String, dynamic> json) {
|
||||
result = json['result'];
|
||||
response = json['response'];
|
||||
data = json['data'] != null ? Dataa.fromJson(json['data']) : null;
|
||||
}
|
||||
}
|
||||
|
||||
class Dataa {
|
||||
String? id;
|
||||
String? type;
|
||||
Attributes? attributes;
|
||||
List<Relationships>? relationships;
|
||||
|
||||
Dataa({this.id, this.type, this.attributes, this.relationships});
|
||||
|
||||
Dataa.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
type = json['type'];
|
||||
attributes = json['attributes'] != null
|
||||
? Attributes.fromJson(json['attributes'])
|
||||
: null;
|
||||
if (json['relationships'] != null) {
|
||||
relationships = <Relationships>[];
|
||||
json['relationships'].forEach((v) {
|
||||
relationships!.add(Relationships.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Attributes {
|
||||
Title? title;
|
||||
|
||||
Map<String, dynamic>? description;
|
||||
bool? isLocked;
|
||||
String? originalLanguage;
|
||||
String? lastVolume;
|
||||
String? lastChapter;
|
||||
String? publicationDemographic;
|
||||
String? status;
|
||||
int? year;
|
||||
String? contentRating;
|
||||
List<Tags>? tags;
|
||||
String? state;
|
||||
bool? chapterNumbersResetOnNewVolume;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? version;
|
||||
List<String>? availableTranslatedLanguages;
|
||||
String? latestUploadedChapter;
|
||||
|
||||
Attributes(
|
||||
{this.title,
|
||||
this.description,
|
||||
this.isLocked,
|
||||
this.originalLanguage,
|
||||
this.lastVolume,
|
||||
this.lastChapter,
|
||||
this.publicationDemographic,
|
||||
this.status,
|
||||
this.year,
|
||||
this.contentRating,
|
||||
this.tags,
|
||||
this.state,
|
||||
this.chapterNumbersResetOnNewVolume,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.version,
|
||||
this.availableTranslatedLanguages,
|
||||
this.latestUploadedChapter});
|
||||
|
||||
Attributes.fromJson(Map<String, dynamic> json) {
|
||||
title = json['title'] != null ? Title.fromJson(json['title']) : null;
|
||||
description = json['description'];
|
||||
isLocked = json['isLocked'];
|
||||
|
||||
originalLanguage = json['originalLanguage'];
|
||||
lastVolume = json['lastVolume'];
|
||||
lastChapter = json['lastChapter'];
|
||||
publicationDemographic = json['publicationDemographic'];
|
||||
status = json['status'];
|
||||
year = json['year'];
|
||||
contentRating = json['contentRating'];
|
||||
if (json['tags'] != null) {
|
||||
tags = <Tags>[];
|
||||
json['tags'].forEach((v) {
|
||||
tags!.add(Tags.fromJson(v));
|
||||
});
|
||||
}
|
||||
state = json['state'];
|
||||
chapterNumbersResetOnNewVolume = json['chapterNumbersResetOnNewVolume'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
version = json['version'];
|
||||
availableTranslatedLanguages =
|
||||
json['availableTranslatedLanguages'].cast<String>();
|
||||
latestUploadedChapter = json['latestUploadedChapter'];
|
||||
}
|
||||
}
|
||||
|
||||
class Title {
|
||||
String? en;
|
||||
|
||||
Title({this.en});
|
||||
|
||||
Title.fromJson(Map<String, dynamic> json) {
|
||||
en = json['en'];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Tags {
|
||||
String? id;
|
||||
String? type;
|
||||
TAttributes? attributes;
|
||||
|
||||
Tags({this.id, this.type, this.attributes});
|
||||
|
||||
Tags.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
type = json['type'];
|
||||
attributes = json['attributes'] != null
|
||||
? TAttributes.fromJson(json['attributes'])
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
class TAttributes {
|
||||
Title? name;
|
||||
Map<String, dynamic>? description;
|
||||
String? group;
|
||||
int? version;
|
||||
|
||||
TAttributes({this.name, this.description, this.group, this.version});
|
||||
|
||||
TAttributes.fromJson(Map<String, dynamic> json) {
|
||||
name = json['name'] != null ? Title.fromJson(json['name']) : null;
|
||||
description = json['description'];
|
||||
group = json['group'];
|
||||
version = json['version'];
|
||||
}
|
||||
}
|
||||
|
||||
class Relationships {
|
||||
String? id;
|
||||
String? type;
|
||||
RAttributes? attributes;
|
||||
String? related;
|
||||
|
||||
Relationships({this.id, this.type, this.attributes, this.related});
|
||||
|
||||
Relationships.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
type = json['type'];
|
||||
attributes = json['attributes'] != null
|
||||
? RAttributes.fromJson(json['attributes'])
|
||||
: null;
|
||||
related = json['related'];
|
||||
}
|
||||
}
|
||||
|
||||
class RAttributes {
|
||||
String? name;
|
||||
Title? biography;
|
||||
String? twitter;
|
||||
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? version;
|
||||
String? description;
|
||||
String? volume;
|
||||
String? fileName;
|
||||
String? locale;
|
||||
|
||||
RAttributes(
|
||||
{this.name,
|
||||
this.biography,
|
||||
this.twitter,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.version,
|
||||
this.description,
|
||||
this.volume,
|
||||
this.fileName,
|
||||
this.locale});
|
||||
|
||||
RAttributes.fromJson(Map<String, dynamic> json) {
|
||||
name = json['name'];
|
||||
|
||||
biography =
|
||||
json['biography'] != null ? Title.fromJson(json['biography']) : null;
|
||||
twitter = json['twitter'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
version = json['version'];
|
||||
description = json['description'];
|
||||
volume = json['volume'];
|
||||
fileName = json['fileName'];
|
||||
locale = json['locale'];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,323 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/services/http_service/http_service.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/model/chapter.dart' as mdx;
|
||||
import 'package:mangayomi/sources/src/all/mangadex/model/cover.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/model/aggregate.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/model/get_chapter_url.dart'
|
||||
as get_chap_url;
|
||||
import 'package:mangayomi/sources/src/all/mangadex/model/manga.dart'
|
||||
as manga_dx;
|
||||
import 'package:mangayomi/sources/src/all/mangadex/model/mdx_detail.dart'
|
||||
as mdx_detail;
|
||||
import 'package:mangayomi/sources/src/all/mangadex/src/utils/utils.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
|
||||
class MangaDex extends MangaYomiServices {
|
||||
@override
|
||||
Future<List<String>> getChapterUrl(
|
||||
{required Chapter chapter,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
String chapterId = chapter.url!.split('/').last;
|
||||
String source = chapter.manga.value!.source!;
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url: '${getMangaAPIUrl(source)}/at-home/server/$chapterId',
|
||||
source: source,
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
final result = jsonDecode(response!) as Map<String, dynamic>;
|
||||
final atHome = get_chap_url.GetChapterUrl.fromJson(result);
|
||||
final host = atHome.baseUrl;
|
||||
final hash = atHome.chapter!.hash!;
|
||||
List<String> pageSuffix = [];
|
||||
for (var data in atHome.chapter!.data!) {
|
||||
pageSuffix.add("/data/$hash/$data");
|
||||
}
|
||||
for (var url in pageSuffix) {
|
||||
pageUrls.add("$host$url");
|
||||
}
|
||||
|
||||
return pageUrls;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getLatestUpdatesManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}/chapter?limit=20&offset=${(20 * (page - 1))}&translatedLanguage[]=$lang&includeFutureUpdates=0${getMDXContentRating()}&order[publishAt]=desc&includeFuturePublishAt=0&includeEmptyPages=0',
|
||||
source: source,
|
||||
resDom: false,
|
||||
).future) as String?;
|
||||
final result = jsonDecode(response!) as Map<String, dynamic>;
|
||||
final chapterList = mdx.ChapterMDX.fromJson(result);
|
||||
List<String> mangaIds = [];
|
||||
for (var element in chapterList.data!) {
|
||||
for (var el in element.relationships!) {
|
||||
mangaIds.add(el.id!);
|
||||
}
|
||||
}
|
||||
mangaIds = mangaIds.toSet().toList();
|
||||
String manga = "";
|
||||
for (var id in mangaIds) {
|
||||
manga = "$manga&ids[]=$id";
|
||||
}
|
||||
|
||||
final mangaResponse = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}/manga?includes[]=cover_art&limit=${mangaIds.length}${getMDXContentRating()}$manga',
|
||||
source: source,
|
||||
resDom: false,
|
||||
).future) as String?;
|
||||
final res = jsonDecode(mangaResponse!) as Map<String, dynamic>;
|
||||
final resultt = manga_dx.MangaDexDto.fromJson(res);
|
||||
|
||||
final firstVolumeCovers =
|
||||
await fetchFirstVolumeCovers(resultt.data, source, ref);
|
||||
for (var da in resultt.data!) {
|
||||
url.add("/manga/${da.id}");
|
||||
name.add(findTitle(da.attributes!.altTitles, da.attributes!.title, lang));
|
||||
image.add(
|
||||
"https://uploads.mangadex.org/covers/${da.id}/${getFileName(da.id!, resultt.data, firstVolumeCovers)}");
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<GetManga?> getMangaDetail(
|
||||
{required GetManga manga,
|
||||
required String lang,
|
||||
required String source,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}${manga.url}?includes[]=cover_art&includes[]=author&includes[]=artist',
|
||||
source: source,
|
||||
resDom: false,
|
||||
).future) as String?;
|
||||
final res = jsonDecode(response!) as Map<String, dynamic>;
|
||||
final result = mdx_detail.MDXDetail.fromJson(res);
|
||||
final aggregate = await fetchSimpleChapterList(result, lang, source, ref);
|
||||
|
||||
List<String> authors = [];
|
||||
for (var element in result.data!.relationships!) {
|
||||
if (element.attributes != null) {
|
||||
authors.add(element.attributes!.name ?? "");
|
||||
}
|
||||
}
|
||||
final attr = result.data!.attributes!;
|
||||
author = authors.join(", ");
|
||||
Map<String, dynamic>? descrip = attr.description;
|
||||
description = descrip![lang] ?? descrip["en"] ?? "";
|
||||
for (var element in attr.tags!) {
|
||||
if (element.attributes != null) {
|
||||
genre.add(element.attributes!.name!.en!);
|
||||
}
|
||||
}
|
||||
genre = [
|
||||
...genre,
|
||||
attr.publicationDemographic ?? "",
|
||||
attr.contentRating != null && attr.contentRating!.toLowerCase() != "safe"
|
||||
? "Content rating: ${attr.contentRating}"
|
||||
: ""
|
||||
].where((element) => element.isNotEmpty).toList();
|
||||
|
||||
status = getPublicationStatus(aggregate, attr);
|
||||
String mangaId = manga.url!.split('/').last;
|
||||
final paginatedChapterList =
|
||||
await paginatedChapterListRequest(mangaId, 0, ref, source, lang);
|
||||
final rres = jsonDecode(paginatedChapterList) as Map<String, dynamic>;
|
||||
final chapterListResponse = mdx.ChapterMDX.fromJson(rres);
|
||||
final chapterListResults = chapterListResponse.data;
|
||||
var limit = chapterListResponse.limit!;
|
||||
var offset = chapterListResponse.offset!;
|
||||
var hasMoreResults = (limit + offset) < chapterListResponse.total!;
|
||||
while (hasMoreResults) {
|
||||
offset += limit;
|
||||
var newRequest =
|
||||
await paginatedChapterListRequest(mangaId, offset, ref, source, lang);
|
||||
final rres = jsonDecode(newRequest) as Map<String, dynamic>;
|
||||
var newChapterList = mdx.ChapterMDX.fromJson(rres);
|
||||
|
||||
chapterListResults!.addAll(newChapterList.data!);
|
||||
hasMoreResults = (limit + offset) < newChapterList.total!;
|
||||
}
|
||||
|
||||
for (var chapterData in chapterListResults!) {
|
||||
List<String> scan = [];
|
||||
var groups = chapterData.relationships!
|
||||
.where((element) => element.id != legacyNoGroupId)
|
||||
.toList();
|
||||
for (var element in groups) {
|
||||
scan.add(
|
||||
element.attributes != null ? element.attributes!.name ?? "" : "");
|
||||
}
|
||||
final scann = scan.join(" ").trim();
|
||||
if (scann.isEmpty) {
|
||||
scan = [];
|
||||
for (var element in groups) {
|
||||
scan.add(element.attributes != null
|
||||
? element.attributes!.username != null
|
||||
? "Uploaded by ${element.attributes!.username}"
|
||||
: ""
|
||||
: "");
|
||||
}
|
||||
}
|
||||
if (scann.isEmpty) {
|
||||
scan = [];
|
||||
scan.add("No Group");
|
||||
}
|
||||
|
||||
List<String> chapName = [];
|
||||
var chapAttr = chapterData.attributes!;
|
||||
if (chapAttr.volume != null && chapAttr.volume!.isNotEmpty) {
|
||||
chapName.add("Vol.${chapAttr.volume}");
|
||||
}
|
||||
if (chapAttr.chapter != null && chapAttr.chapter!.isNotEmpty) {
|
||||
chapName.add("Ch.${chapAttr.chapter}");
|
||||
}
|
||||
if (chapAttr.title != null && chapAttr.title!.isNotEmpty) {
|
||||
if (chapName.isNotEmpty) {
|
||||
chapName.add("-");
|
||||
}
|
||||
chapName.add(chapAttr.title!);
|
||||
}
|
||||
if (chapName.isEmpty) {
|
||||
chapName.add("Oneshot");
|
||||
}
|
||||
scanlators.add(scan.join(" ").trim());
|
||||
chapterUrl.add("/chapter/${chapterData.id}");
|
||||
chapterTitle.add(chapName.join(" "));
|
||||
chapterDate.add(parseDate(chapAttr.publishAt!, source));
|
||||
}
|
||||
|
||||
return mangadetailRes(manga: manga, source: source);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getPopularManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}/manga?limit=20&offset=${(20 * (page - 1))}&availableTranslatedLanguage[]=$lang&includes[]=cover_art${getMDXContentRating()}&order[followedCount]=desc',
|
||||
source: source,
|
||||
resDom: false,
|
||||
).future) as String?;
|
||||
final res = jsonDecode(response!) as Map<String, dynamic>;
|
||||
final result = manga_dx.MangaDexDto.fromJson(res);
|
||||
|
||||
final firstVolumeCovers =
|
||||
await fetchFirstVolumeCovers(result.data, source, ref);
|
||||
for (var da in result.data!) {
|
||||
url.add("/manga/${da.id}");
|
||||
name.add(findTitle(da.attributes!.altTitles, da.attributes!.title, lang));
|
||||
image.add(
|
||||
"https://uploads.mangadex.org/covers/${da.id}/${getFileName(da.id!, result.data, firstVolumeCovers)}");
|
||||
}
|
||||
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> searchManga(
|
||||
{required String source,
|
||||
required String query,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}/manga?includes[]=cover_art&offset=0&limit=20&title=${query.toLowerCase().trim()}${getMDXContentRating()}&order[followedCount]=desc&availableTranslatedLanguage[]=$lang',
|
||||
source: source,
|
||||
resDom: false,
|
||||
).future) as String?;
|
||||
final res = jsonDecode(response!) as Map<String, dynamic>;
|
||||
final resultt = manga_dx.MangaDexDto.fromJson(res);
|
||||
final firstVolumeCovers =
|
||||
await fetchFirstVolumeCovers(resultt.data, source, ref);
|
||||
for (var da in resultt.data!) {
|
||||
url.add("/manga/${da.id}");
|
||||
name.add(findTitle(da.attributes!.altTitles, da.attributes!.title, lang));
|
||||
image.add(
|
||||
"https://uploads.mangadex.org/covers/${da.id}/${getFileName(da.id!, resultt.data, firstVolumeCovers)}");
|
||||
}
|
||||
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
Future<List<CoverData>?> fetchFirstVolumeCovers(List<manga_dx.Data>? data,
|
||||
String source, AutoDisposeFutureProviderRef ref) async {
|
||||
List<String> mangaIds = [];
|
||||
List<String> locales = [];
|
||||
for (var element in data!) {
|
||||
final attributes = element.attributes;
|
||||
if (attributes!.originalLanguage != null &&
|
||||
attributes.originalLanguage!.isNotEmpty) {
|
||||
mangaIds.add(element.id!);
|
||||
locales.add(attributes.originalLanguage!);
|
||||
}
|
||||
}
|
||||
|
||||
int limit = (mangaIds.length * locales.length);
|
||||
if (limit > 100) {
|
||||
limit = 100;
|
||||
}
|
||||
String manga = "";
|
||||
for (var id in mangaIds) {
|
||||
manga = "$manga&manga[]=$id";
|
||||
}
|
||||
String locale = "";
|
||||
for (var loc in locales.toSet()) {
|
||||
locale = "$locale&locales[]=$loc";
|
||||
}
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}/cover?order[volume]=asc&limit=$limit&offset=0$manga$locale',
|
||||
source: source,
|
||||
resDom: false,
|
||||
).future) as String?;
|
||||
|
||||
final res = jsonDecode(response!) as Map<String, dynamic>;
|
||||
|
||||
final result = CoverAA.fromJson(res).data;
|
||||
result!.sort(
|
||||
(a, b) => a.relationships!.firstOrNull!.id!
|
||||
.compareTo(b.relationships!.firstOrNull!.id!),
|
||||
);
|
||||
|
||||
return result.where((element) {
|
||||
return mangaIds.contains(element.relationships!.first.id);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future<Aggregate>? fetchSimpleChapterList(mdx_detail.MDXDetail? data,
|
||||
String lang, String source, AutoDisposeFutureProviderRef ref) async {
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}/manga/${data!.data!.id}/aggregate?translatedLanguage[]=$lang',
|
||||
source: source,
|
||||
resDom: false,
|
||||
).future) as String?;
|
||||
final res = jsonDecode(response!) as Map<String, dynamic>;
|
||||
|
||||
return Aggregate.fromJson(res);
|
||||
}
|
||||
|
||||
Future<String> paginatedChapterListRequest(String mangaId, int offset,
|
||||
AutoDisposeFutureProviderRef ref, String source, String lang) async {
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaAPIUrl(source)}/manga/$mangaId/feed?limit=500&offset=$offset&includes[]=user&includes[]=scanlation_group&order[volume]=desc&order[chapter]=desc&translatedLanguage[]=$lang&includeFuturePublishAt=0&includeEmptyPages=0&contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic',
|
||||
source: source,
|
||||
resDom: false,
|
||||
).future) as String?;
|
||||
return response!;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/model/aggregate.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/model/cover.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/model/manga.dart';
|
||||
import 'package:mangayomi/sources/src/all/mangadex/model/mdx_detail.dart'
|
||||
as mdx_detail;
|
||||
|
||||
String getMDXContentRating() {
|
||||
List<String> contentRating = [
|
||||
"suggestive",
|
||||
"safe",
|
||||
"erotica",
|
||||
"pornographic"
|
||||
];
|
||||
String ctnRating = "";
|
||||
for (var rating in contentRating) {
|
||||
ctnRating = "$ctnRating&contentRating[]=$rating";
|
||||
}
|
||||
return ctnRating;
|
||||
}
|
||||
|
||||
findTitle(
|
||||
List<dynamic>? altTitles, Map<String, dynamic>? enTitle, String lang) {
|
||||
final altTitle = altTitles!
|
||||
.where((element) =>
|
||||
element[lang].toString().isNotEmpty ||
|
||||
element["en"].toString().isNotEmpty)
|
||||
.toList();
|
||||
String? altTitless = altTitle.isNotEmpty
|
||||
? altTitle.firstOrNull[lang] ?? altTitle.firstOrNull["en"]
|
||||
: null;
|
||||
return enTitle!.values.firstOrNull ?? altTitless ?? "";
|
||||
}
|
||||
|
||||
getFileName(String id, List<Data>? data, List<CoverData>? firstVolumeCovers) {
|
||||
final mangaMap =
|
||||
data!.asMap().map((key, value) => MapEntry(value.id, value.attributes));
|
||||
final firstVolumeCoversF = firstVolumeCovers!
|
||||
.where((element) =>
|
||||
element.attributes!.locale == mangaMap[id]!.originalLanguage!)
|
||||
.where((element) =>
|
||||
element.attributes!.fileName != null &&
|
||||
element.attributes!.fileName!.isNotEmpty)
|
||||
.toList()
|
||||
.asMap()
|
||||
.map((key, value) =>
|
||||
MapEntry(value.relationships!.first.id, value.attributes!.fileName));
|
||||
|
||||
final datas = data
|
||||
.where((element) => element.id == id)
|
||||
.toList()
|
||||
.firstOrNull!
|
||||
.relationships!
|
||||
.firstOrNull!
|
||||
.attributes;
|
||||
return firstVolumeCoversF.isNotEmpty
|
||||
? firstVolumeCoversF[id]
|
||||
: datas != null
|
||||
? datas.fileName
|
||||
: "";
|
||||
}
|
||||
|
||||
Status getPublicationStatus(Aggregate? aggregate, mdx_detail.Attributes attr) {
|
||||
List<String> chaptersList = [];
|
||||
for (var element in aggregate!.volumes!.values) {
|
||||
for (var elem in element.chapters!.values) {
|
||||
chaptersList.add(elem.chapter!);
|
||||
}
|
||||
}
|
||||
var tempStatus = switch (attr.status) {
|
||||
"ongoing" => Status.ongoing,
|
||||
"cancelled" => Status.canceled,
|
||||
"completed" => Status.publishingFinished,
|
||||
"hiatus" => Status.onHiatus,
|
||||
_ => Status.unknown,
|
||||
};
|
||||
var publishedOrCancelled =
|
||||
tempStatus == Status.publishingFinished || tempStatus == Status.canceled;
|
||||
var isOneShot = attr.tags!.any((element) => element.id == tagOneShotUuid) &&
|
||||
attr.tags!.where((element) => (element.id == tagAnthologyUuid)).isEmpty;
|
||||
return chaptersList.contains(attr.lastChapter) && publishedOrCancelled
|
||||
? Status.completed
|
||||
: isOneShot
|
||||
? Status.completed
|
||||
: tempStatus;
|
||||
}
|
||||
|
||||
const tagAnthologyUuid = "51d83883-4103-437c-b4b1-731cb73d786c";
|
||||
const tagOneShotUuid = "0234a31e-a729-4e28-9d6a-3f87c4966b9e";
|
||||
const legacyNoGroupId = "00e03853-1b96-4f41-9542-c71b8692033b";
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
Source get mangahereSource => _mangahereSource;
|
||||
|
||||
Source _mangahereSource = Source(
|
||||
sourceName: "MangaHere",
|
||||
baseUrl: "http://www.mangahere.cc",
|
||||
lang: "en",
|
||||
typeSource: TypeSource.single,
|
||||
logoUrl: 'http://static.mangahere.cc/v20210106/mangahere/images/logo.png',
|
||||
dateFormat: "MMM dd,yyyy",
|
||||
dateFormatLocale: "en",
|
||||
);
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
import 'package:flutter_js/flutter_js.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/services/http_service/http_service.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:html/dom.dart' as dom;
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
|
||||
class Mangahere extends MangaYomiServices {
|
||||
@override
|
||||
Future<GetManga?> getMangaDetail(
|
||||
{required GetManga manga,
|
||||
required String lang,
|
||||
required String source,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url: "${getMangaBaseUrl(source)}/${manga.url}",
|
||||
source: source,
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
if (dom!
|
||||
.querySelectorAll(
|
||||
' body > div > div > div.detail-info-right > p.detail-info-right-title > span.detail-info-right-title-tip')
|
||||
.isNotEmpty) {
|
||||
final tt = dom
|
||||
.querySelectorAll(
|
||||
' body > div > div > div.detail-info-right > p.detail-info-right-title > span.detail-info-right-title-tip')
|
||||
.map((e) => e.text.trim())
|
||||
.toList();
|
||||
|
||||
status = switch (tt[0].toLowerCase()) {
|
||||
"ongoing" => Status.ongoing,
|
||||
"completed" => Status.completed,
|
||||
_ => Status.unknown,
|
||||
};
|
||||
} else {
|
||||
status = Status.unknown;
|
||||
}
|
||||
if (dom
|
||||
.querySelectorAll(
|
||||
' body > div > div > div.detail-info-right > p.detail-info-right-say > a')
|
||||
.isNotEmpty) {
|
||||
final tt = dom
|
||||
.querySelectorAll(
|
||||
' body > div > div > div.detail-info-right > p.detail-info-right-say > a')
|
||||
.map((e) => e.text.trim())
|
||||
.toList();
|
||||
|
||||
author = tt[0];
|
||||
} else {
|
||||
author = "";
|
||||
}
|
||||
|
||||
if (dom
|
||||
.querySelectorAll(
|
||||
'body > div > div > div.detail-info-right > p.detail-info-right-content')
|
||||
.isNotEmpty) {
|
||||
final tt = dom
|
||||
.querySelectorAll(
|
||||
'body > div > div > div.detail-info-right > p.detail-info-right-content')
|
||||
.map((e) => e.text.trim())
|
||||
.toList();
|
||||
|
||||
description = tt.first;
|
||||
}
|
||||
|
||||
if (dom.querySelectorAll('ul > li > a').isNotEmpty) {
|
||||
final udl = dom
|
||||
.querySelectorAll('ul > li > a ')
|
||||
.where((e) => e.attributes.containsKey('href'))
|
||||
.map((e) => e.attributes['href'])
|
||||
.toList();
|
||||
|
||||
for (var ok in udl) {
|
||||
chapterUrl.add(ok!);
|
||||
}
|
||||
}
|
||||
if (dom.querySelectorAll('ul > li > a > div > p.title3').isNotEmpty) {
|
||||
final tt = dom
|
||||
.querySelectorAll('ul > li > a > div > p.title3')
|
||||
.map((e) => e.text.trim())
|
||||
.toList();
|
||||
for (var ok in tt) {
|
||||
chapterTitle.add(ok);
|
||||
}
|
||||
}
|
||||
if (dom.querySelectorAll('ul > li > a > div > p.title2').isNotEmpty) {
|
||||
final tt = dom
|
||||
.querySelectorAll('ul > li > a > div > p.title2')
|
||||
.map((e) => e.text.trim())
|
||||
.toList();
|
||||
for (var ok in tt) {
|
||||
chapterDate.add(parseMangaHereChapterDate(ok, source).toString());
|
||||
}
|
||||
}
|
||||
if (dom
|
||||
.querySelectorAll(
|
||||
' body > div > div > div.detail-info-right > p.detail-info-right-tag-list > a')
|
||||
.isNotEmpty) {
|
||||
final tt = dom
|
||||
.querySelectorAll(
|
||||
' body > div > div > div.detail-info-right > p.detail-info-right-tag-list > a')
|
||||
.map((e) => e.text.trim())
|
||||
.toList();
|
||||
|
||||
for (var ok in tt) {
|
||||
genre.add(ok);
|
||||
}
|
||||
}
|
||||
return mangadetailRes(manga: manga, source: source);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getPopularManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url: '${getMangaBaseUrl(source)}/directory/$page.htm',
|
||||
source: source,
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
if (dom!.querySelectorAll('.manga-list-1-list li').isNotEmpty) {
|
||||
url = dom
|
||||
.querySelectorAll('.manga-list-1-list li > a ')
|
||||
.where((e) => e.attributes.containsKey('href'))
|
||||
.map((e) => e.attributes['href'])
|
||||
.toList();
|
||||
|
||||
image = dom
|
||||
.querySelectorAll('.manga-list-1-list 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('.manga-list-1-list li > a ')
|
||||
.where((e) => e.attributes.containsKey('title'))
|
||||
.map((e) => e.attributes['title'])
|
||||
.toList();
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> searchManga(
|
||||
{required String source,
|
||||
required String query,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
'${getMangaBaseUrl(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',
|
||||
source: source,
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
|
||||
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 mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getChapterUrl(
|
||||
{required Chapter chapter,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
JavascriptRuntime? flutterJs;
|
||||
flutterJs = getJavascriptRuntime();
|
||||
extractSecretKey(String response, JavascriptRuntime? flutterJs) {
|
||||
var secretKeyScriptLocation =
|
||||
response.indexOf("eval(function(p,a,c,k,e,d)");
|
||||
var secretKeyScriptEndLocation =
|
||||
response.indexOf("</script>", secretKeyScriptLocation);
|
||||
var secretKeyScript = response
|
||||
.substring(secretKeyScriptLocation, secretKeyScriptEndLocation)
|
||||
.replaceAll("eval", "");
|
||||
var secretKeyDeobfuscatedScript =
|
||||
flutterJs!.evaluate(secretKeyScript).toString();
|
||||
var secretKeyStartLoc = secretKeyDeobfuscatedScript.indexOf("'");
|
||||
var secretKeyEndLoc = secretKeyDeobfuscatedScript.indexOf(";");
|
||||
|
||||
var secretKeyResultScript = secretKeyDeobfuscatedScript.substring(
|
||||
secretKeyStartLoc, secretKeyEndLoc);
|
||||
|
||||
return secretKeyResultScript;
|
||||
}
|
||||
|
||||
var link =
|
||||
"${getMangaBaseUrl(chapter.manga.value!.source!)}${chapter.url!}";
|
||||
final response = await ref.watch(
|
||||
httpGetProvider(url: link, source: "mangahere", resDom: false)
|
||||
.future) as String?;
|
||||
|
||||
dom.Document htmll = dom.Document.html(response!);
|
||||
int? pagesNumber = -1;
|
||||
if (htmll.querySelectorAll('body > div > div > span > a:').isNotEmpty) {
|
||||
final ta = htmll
|
||||
.querySelectorAll('body > div > div > span > a:')
|
||||
.map((e) => e.text.trim())
|
||||
.toList();
|
||||
ta.removeLast();
|
||||
pagesNumber = int.parse(ta.last);
|
||||
}
|
||||
if (pagesNumber == -1) {
|
||||
final script = htmll
|
||||
.getElementsByTagName("script")
|
||||
.firstWhere((e) => e.innerHtml.contains(
|
||||
"function(p,a,c,k,e,d)",
|
||||
))
|
||||
.innerHtml
|
||||
.replaceAll("eval", "");
|
||||
|
||||
String deobfuscatedScript = flutterJs.evaluate(script).toString();
|
||||
List<String> urlss = deobfuscatedScript
|
||||
.substring(
|
||||
deobfuscatedScript.indexOf("newImgs=['") + "newImgs=['".length,
|
||||
deobfuscatedScript.indexOf("'];"))
|
||||
.split("','");
|
||||
for (var tt in urlss) {
|
||||
pageUrls.add("https:$tt");
|
||||
}
|
||||
flutterJs.dispose();
|
||||
} else {
|
||||
var secretKey = extractSecretKey(response, flutterJs);
|
||||
|
||||
var chapterIdStartLoc = response.indexOf("chapterid");
|
||||
var chapterId = response
|
||||
.substring(
|
||||
chapterIdStartLoc + 11, response.indexOf(";", chapterIdStartLoc))
|
||||
.trim();
|
||||
|
||||
var pageBase = link.substring(0, link.lastIndexOf("/"));
|
||||
|
||||
for (int i = 1; i <= pagesNumber; i++) {
|
||||
var pageLink =
|
||||
"$pageBase/chapterfun.ashx?cid=$chapterId&page=$i&key=$secretKey";
|
||||
var responseText = "";
|
||||
|
||||
for (int tr = 1; tr <= 3; tr++) {
|
||||
var response = await http.get(Uri.parse(pageLink), headers: {
|
||||
"Referer": link,
|
||||
"Accept": "*/*",
|
||||
"Accept-Language": "en-US,en;q=0.9",
|
||||
"Connection": "keep-alive",
|
||||
"Host": "www.mangahere.cc",
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
});
|
||||
responseText = response.body;
|
||||
if (responseText.isNotEmpty) {
|
||||
break;
|
||||
} else {
|
||||
secretKey = "";
|
||||
}
|
||||
}
|
||||
|
||||
var deobfuscatedScript =
|
||||
flutterJs.evaluate(responseText.replaceAll("eval", "")).toString();
|
||||
|
||||
var baseLinkStartPos = deobfuscatedScript.indexOf("pix=") + 5;
|
||||
var baseLinkEndPos =
|
||||
deobfuscatedScript.indexOf(";", baseLinkStartPos) - 1;
|
||||
var baseLink =
|
||||
deobfuscatedScript.substring(baseLinkStartPos, baseLinkEndPos);
|
||||
|
||||
var imageLinkStartPos = deobfuscatedScript.indexOf("pvalue=") + 9;
|
||||
var imageLinkEndPos =
|
||||
deobfuscatedScript.indexOf("\"", imageLinkStartPos);
|
||||
var imageLink =
|
||||
deobfuscatedScript.substring(imageLinkStartPos, imageLinkEndPos);
|
||||
pageUrls.add("https:$baseLink$imageLink");
|
||||
}
|
||||
|
||||
flutterJs.dispose();
|
||||
}
|
||||
return pageUrls;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getLatestUpdatesManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url: '${getMangaBaseUrl(source)}/directory/$page.htm?latest',
|
||||
source: source,
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
if (dom!.querySelectorAll('.manga-list-1-list li').isNotEmpty) {
|
||||
url = dom
|
||||
.querySelectorAll('.manga-list-1-list li > a ')
|
||||
.where((e) => e.attributes.containsKey('href'))
|
||||
.map((e) => e.attributes['href'])
|
||||
.toList();
|
||||
|
||||
image = dom
|
||||
.querySelectorAll('.manga-list-1-list 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('.manga-list-1-list li > a ')
|
||||
.where((e) => e.attributes.containsKey('title'))
|
||||
.map((e) => e.attributes['title'])
|
||||
.toList();
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
}
|
||||
|
||||
int parseMangaHereChapterDate(String date, String source) {
|
||||
if (date.contains('Today') || date.contains(' ago')) {
|
||||
final now = DateTime.now();
|
||||
final today = DateTime(now.year, now.month, now.day);
|
||||
return today.millisecondsSinceEpoch;
|
||||
} else if (date.contains('Yesterday')) {
|
||||
final now = DateTime.now();
|
||||
final yesterday = DateTime(now.year, now.month, now.day - 1);
|
||||
return yesterday.millisecondsSinceEpoch;
|
||||
} else {
|
||||
try {
|
||||
final dateFormat =
|
||||
DateFormat(getFormatDate(source), getFormatDateLocale(source));
|
||||
final parsedDate = dateFormat.parse(date);
|
||||
return parsedDate.millisecondsSinceEpoch;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
Source get japscanSource => _japscanSource;
|
||||
|
||||
Source _japscanSource = Source(
|
||||
sourceName: "Japscan",
|
||||
baseUrl: "https://japscan.lol",
|
||||
lang: "fr",
|
||||
typeSource: TypeSource.single,
|
||||
logoUrl: '',
|
||||
isCloudflare: true,
|
||||
dateFormat: "d MMM yyyy",
|
||||
dateFormatLocale: "en_US");
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
// ignore_for_file: depend_on_referenced_packages
|
||||
|
||||
import 'dart:convert';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:html/dom.dart' as dom;
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/services/http_service/cloudflare/cloudflare_bypass.dart';
|
||||
import 'package:mangayomi/services/http_service/http_service.dart';
|
||||
import 'package:mangayomi/sources/service.dart';
|
||||
import 'package:mangayomi/sources/utils/utils.dart';
|
||||
|
||||
class Japscan extends MangaYomiServices {
|
||||
@override
|
||||
Future<GetManga?> getMangaDetail(
|
||||
{required GetManga manga,
|
||||
required String lang,
|
||||
required String source,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url: manga.url!,
|
||||
source: source,
|
||||
resDom: true,
|
||||
).future) as Document?;
|
||||
if (dom!.querySelectorAll('.col-7 > p').isNotEmpty) {
|
||||
final images =
|
||||
dom.querySelectorAll('.col-5 ').map((e) => e.outerHtml).toList();
|
||||
RegExp exp = RegExp(r'src="([^"]+)"');
|
||||
|
||||
String? srcValue = exp.firstMatch(images[0])?.group(1);
|
||||
manga.imageUrl = '${getMangaBaseUrl(source)}$srcValue';
|
||||
|
||||
if (dom.querySelectorAll('.col-7 > p').isNotEmpty) {
|
||||
final stat = dom
|
||||
.querySelectorAll('.col-7 > p')
|
||||
.where((element) => element.innerHtml.contains('Statut:'))
|
||||
.map((e) => e.text)
|
||||
.toList();
|
||||
|
||||
if (stat.isNotEmpty) {
|
||||
String sta = stat[0].replaceAll('Statut:', '').trim();
|
||||
status = switch (sta) {
|
||||
"En Cours" => Status.ongoing,
|
||||
"Terminé" => Status.completed,
|
||||
_ => Status.unknown,
|
||||
};
|
||||
} else {
|
||||
status = Status.unknown;
|
||||
}
|
||||
|
||||
final auth = dom
|
||||
.querySelectorAll('.col-7 > p')
|
||||
.where((element) => element.innerHtml.contains('Auteur(s):'))
|
||||
.map((e) => e.text)
|
||||
.toList();
|
||||
if (auth.isNotEmpty) {
|
||||
author = auth[0].replaceAll('Auteur(s):', '').trim();
|
||||
}
|
||||
} else {
|
||||
author = "";
|
||||
status = Status.unknown;
|
||||
}
|
||||
|
||||
final genres = dom
|
||||
.querySelectorAll('.col-7 > p')
|
||||
.where((element) => element.innerHtml.contains('Genre(s):'))
|
||||
.map((e) => e.text.replaceAll('Genre(s):', '').trim())
|
||||
.toList();
|
||||
if (genres.isNotEmpty) {
|
||||
for (var ok in genres[0].split(',')) {
|
||||
genre.add(ok);
|
||||
}
|
||||
}
|
||||
|
||||
final synop = dom
|
||||
.querySelectorAll('p.list-group-item ')
|
||||
.map((e) => e.text.trim())
|
||||
.toList();
|
||||
if (synop.isNotEmpty) {
|
||||
description = synop[0];
|
||||
}
|
||||
}
|
||||
|
||||
final urls =
|
||||
dom.querySelectorAll('.col-8 ').map((e) => e.outerHtml).toList();
|
||||
|
||||
for (var ok in urls) {
|
||||
RegExp exp = RegExp(r'href="([^"]+)"');
|
||||
|
||||
String? srcValue = exp.firstMatch(ok)?.group(1);
|
||||
chapterUrl.add('${getMangaBaseUrl(source)}$srcValue');
|
||||
}
|
||||
|
||||
final chapterTitlee =
|
||||
dom.querySelectorAll('.col-8').map((e) => e.text.trim()).toList();
|
||||
|
||||
if (chapterTitlee.isNotEmpty) {
|
||||
for (var ok in chapterTitlee) {
|
||||
chapterTitle.add(ok);
|
||||
}
|
||||
}
|
||||
|
||||
final chapterDatee =
|
||||
dom.querySelectorAll('.col-4').map((e) => e.text.trim()).toList();
|
||||
if (chapterDatee.isNotEmpty) {
|
||||
for (var ok in chapterDatee) {
|
||||
chapterDate.add(parseDate(ok, source));
|
||||
}
|
||||
}
|
||||
return mangadetailRes(manga: manga, source: source);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getPopularManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url: "${getMangaBaseUrl(source)}/", source: source, resDom: true)
|
||||
.future) as Document?;
|
||||
if (dom!.querySelectorAll('#top_mangas_week > ul > li ').isNotEmpty) {
|
||||
final urls = dom
|
||||
.querySelectorAll('#top_mangas_week > ul > li > a')
|
||||
.where((e) => e.attributes['href'].toString().contains('manga'))
|
||||
.map((e) => e.attributes['href'])
|
||||
.toList();
|
||||
print(urls);
|
||||
for (var ok in urls) {
|
||||
url.add("${getMangaBaseUrl(source)}$ok");
|
||||
}
|
||||
name = dom
|
||||
.querySelectorAll(
|
||||
'#top_mangas_week > ul > li > a.text-dark.font-weight-bold')
|
||||
.map((e) => e.innerHtml)
|
||||
.toList();
|
||||
for (var i = 0; i < name.length; i++) {
|
||||
image.add("");
|
||||
}
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> searchManga(
|
||||
{required String source,
|
||||
required String query,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url:
|
||||
"https://www.google.com/search?q=${query.toLowerCase()}+japscan",
|
||||
source: source,
|
||||
resDom: true)
|
||||
.future) as Document?;
|
||||
|
||||
if (dom!.querySelectorAll("div > div > div > div > div > a").isNotEmpty) {
|
||||
final urls = dom
|
||||
.querySelectorAll("div > div > div > div > div > a")
|
||||
.where((e) => e.attributes.containsKey('href'))
|
||||
.where((element) =>
|
||||
element.text.toLowerCase().contains("https://www.japscan."))
|
||||
.map((e) => e.attributes['href']
|
||||
.toString()
|
||||
.replaceAll('lecture-en-ligne', 'manga')
|
||||
.split("/"))
|
||||
.toList();
|
||||
List<String?> tt = [];
|
||||
List<String?> ta = [];
|
||||
for (var ok in urls) {
|
||||
tt.add("${ok[0]}//${ok[2]}/${ok[3]}/${ok[4]}/");
|
||||
ta.add(ok[4]
|
||||
.replaceAll('-', " ")
|
||||
.toString()
|
||||
.split(' ')
|
||||
.map((word) =>
|
||||
word.substring(0, 1).toUpperCase() + word.substring(1))
|
||||
.join(' '));
|
||||
}
|
||||
name = ta.toSet().toList();
|
||||
url = tt.toSet().toList();
|
||||
for (var i = 0; i < name.length; i++) {
|
||||
image.add("");
|
||||
}
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getChapterUrl(
|
||||
{required Chapter chapter,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final response = await ref.watch(httpGetProvider(
|
||||
useUserAgent: true,
|
||||
url: chapter.url!,
|
||||
source: "japscan",
|
||||
resDom: false)
|
||||
.future) as String?;
|
||||
RegExp regex = RegExp(r'<script src="/zjs/(.*?)"');
|
||||
Match? match = regex.firstMatch(response!);
|
||||
String zjsurl = match!.group(1)!;
|
||||
baseUrl = response;
|
||||
zjsUrl = "${getMangaBaseUrl(chapter.manga.value!.source!)}/zjs/$zjsurl";
|
||||
zjs(ref);
|
||||
await Future.doWhile(() async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
if (isOk == true) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return pageUrls;
|
||||
}
|
||||
|
||||
bool isOk = false;
|
||||
String? baseUrl;
|
||||
String? zjsUrl;
|
||||
zjs(AutoDisposeFutureProviderRef ref) async {
|
||||
final html = await ref.watch(cloudflareBypassHtmlProvider(
|
||||
url: zjsUrl!, source: "japscan", useUserAgent: true)
|
||||
.future);
|
||||
dom.Document htmll = dom.Document.html(baseUrl!);
|
||||
List<String?> stringLookupTables =
|
||||
RegExp(r"'([\dA-Z]{62})'", caseSensitive: false)
|
||||
.allMatches(html)
|
||||
.map((match) => match.group(1))
|
||||
.toList();
|
||||
if (stringLookupTables.length != 2) {
|
||||
throw Exception(
|
||||
"Attendait 2 chaînes de recherche dans ZJS, a trouvé ${stringLookupTables.length}");
|
||||
}
|
||||
final scrambledData =
|
||||
htmll.getElementById("data")!.attributes['data-data']!;
|
||||
|
||||
for (var i = 0; i <= 1; i++) {
|
||||
final otherIndex = i == 0 ? 1 : 0;
|
||||
|
||||
final lookupTable = Map.fromIterables(stringLookupTables[i]!.split(''),
|
||||
stringLookupTables[otherIndex]!.split(''));
|
||||
try {
|
||||
final unscrambledData = scrambledData
|
||||
.split('')
|
||||
.map((char) => lookupTable[char] ?? char)
|
||||
.join();
|
||||
|
||||
final decoded = utf8.decode(base64.decode(unscrambledData));
|
||||
final data = jsonDecode(decoded);
|
||||
for (var url in data["imagesLink"]) {
|
||||
pageUrls.add(url);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
isOk = true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GetManga?>> getLatestUpdatesManga(
|
||||
{required String source,
|
||||
required int page,
|
||||
required String lang,
|
||||
required AutoDisposeFutureProviderRef ref}) async {
|
||||
final dom = await ref.watch(httpGetProvider(
|
||||
url: "${getMangaBaseUrl(source)}/", source: source, resDom: true)
|
||||
.future) as Document?;
|
||||
if (dom!
|
||||
.querySelectorAll(
|
||||
'#chapters h3.text-truncate, #chapters_list h3.text-truncate')
|
||||
.isNotEmpty) {
|
||||
final urls = dom
|
||||
.querySelectorAll(
|
||||
'#chapters h3.text-truncate, #chapters_list h3.text-truncate > a')
|
||||
.where((e) => e.attributes['href'].toString().contains('manga'))
|
||||
.map((e) => e.attributes['href'])
|
||||
.toList();
|
||||
for (var ok in urls) {
|
||||
url.add("${getMangaBaseUrl(source)}$ok");
|
||||
}
|
||||
|
||||
name = dom
|
||||
.querySelectorAll(
|
||||
'#chapters h3.text-truncate, #chapters_list h3.text-truncate > a.text-dark.font-weight-bold')
|
||||
.map((e) => e.innerHtml)
|
||||
.toList();
|
||||
for (var i = 0; i < name.length; i++) {
|
||||
image.add("");
|
||||
}
|
||||
}
|
||||
return mangaRes();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
Source get mangakawaiiSource => _mangakawaiiSource;
|
||||
Source _mangakawaiiSource = Source(
|
||||
sourceName: "MangaKawaii",
|
||||
baseUrl: "https://www.mangakawaii.io",
|
||||
lang: "fr",
|
||||
typeSource: TypeSource.single,
|
||||
logoUrl: 'https://www.mangakawaii.io/assets/img/logo.png',
|
||||
dateFormat: "dd.MM.yyyy",
|
||||
isCloudflare: true,
|
||||
dateFormatLocale: "en_US");
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue