- Introduced a new Flutter project for the cxyz Android manga/comic reader, including essential files such as pubspec.yaml, AndroidManifest.xml, and Gradle configuration. - Added .gitignore files to exclude unnecessary files and directories for both the main project and Android-specific files. - Implemented initial README.md with project description, features, setup instructions, and API details for ComicLand, MangaRead, and MangaTown. - Configured analysis options and dependencies in pubspec.yaml to support Flutter development. - Established a pnpm workspace and lockfile for dependency management. - Included various Android resources such as icons, styles, and launch backgrounds for a complete application setup.
177 lines
3.7 KiB
Dart
177 lines
3.7 KiB
Dart
// Shared models and source-adapter contract (port of cxyz `sources.js`).
|
||
|
||
enum SourceId {
|
||
comicland,
|
||
mangaread,
|
||
mangatown;
|
||
|
||
String get id => name;
|
||
|
||
String get label {
|
||
switch (this) {
|
||
case SourceId.comicland:
|
||
return 'ComicLand';
|
||
case SourceId.mangaread:
|
||
return 'MangaRead';
|
||
case SourceId.mangatown:
|
||
return 'MangaTown';
|
||
}
|
||
}
|
||
|
||
/// Bundled favicon asset (not fetched at runtime).
|
||
String get logoAsset => 'assets/sources/$name.png';
|
||
|
||
static SourceId? tryParse(String raw) {
|
||
final key = raw.trim().toLowerCase();
|
||
for (final id in SourceId.values) {
|
||
if (id.name == key) return id;
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// Browse menus a source may expose on its Catalog home.
|
||
enum CatalogMenuKind {
|
||
popular,
|
||
latest,
|
||
official,
|
||
uncensored,
|
||
recommended,
|
||
search,
|
||
hotToday,
|
||
hotWeek,
|
||
hotMonth,
|
||
directory;
|
||
|
||
String get pathSegment => name;
|
||
}
|
||
|
||
/// Optional directory filters (MangaTown). Segment `0` means All.
|
||
class CatalogBrowseParams {
|
||
const CatalogBrowseParams({
|
||
this.demographic = '0',
|
||
this.genre = '0',
|
||
this.status = '0',
|
||
this.initial = '0',
|
||
});
|
||
|
||
final String demographic;
|
||
final String genre;
|
||
final String status;
|
||
final String initial;
|
||
|
||
CatalogBrowseParams copyWith({
|
||
String? demographic,
|
||
String? genre,
|
||
String? status,
|
||
String? initial,
|
||
}) {
|
||
return CatalogBrowseParams(
|
||
demographic: demographic ?? this.demographic,
|
||
genre: genre ?? this.genre,
|
||
status: status ?? this.status,
|
||
initial: initial ?? this.initial,
|
||
);
|
||
}
|
||
|
||
@override
|
||
bool operator ==(Object other) {
|
||
return other is CatalogBrowseParams &&
|
||
other.demographic == demographic &&
|
||
other.genre == genre &&
|
||
other.status == status &&
|
||
other.initial == initial;
|
||
}
|
||
|
||
@override
|
||
int get hashCode => Object.hash(demographic, genre, status, initial);
|
||
}
|
||
|
||
class CatalogMenu {
|
||
const CatalogMenu({required this.kind, required this.title});
|
||
|
||
final CatalogMenuKind kind;
|
||
final String title;
|
||
}
|
||
|
||
class CatalogComic {
|
||
const CatalogComic({
|
||
required this.source,
|
||
required this.slug,
|
||
required this.title,
|
||
required this.listingUrl,
|
||
this.coverUrl,
|
||
this.description,
|
||
this.rating,
|
||
});
|
||
|
||
final SourceId source;
|
||
final String slug;
|
||
final String title;
|
||
final String listingUrl;
|
||
final String? coverUrl;
|
||
final String? description;
|
||
final double? rating;
|
||
}
|
||
|
||
class ChapterLink {
|
||
const ChapterLink({
|
||
required this.url,
|
||
required this.title,
|
||
required this.chapterKey,
|
||
this.chapterNumber,
|
||
});
|
||
|
||
final String url;
|
||
final String title;
|
||
final String chapterKey;
|
||
final String? chapterNumber;
|
||
}
|
||
|
||
class SeriesDetail {
|
||
const SeriesDetail({
|
||
required this.source,
|
||
required this.listingUrl,
|
||
required this.slug,
|
||
required this.title,
|
||
required this.chapters,
|
||
this.coverUrl,
|
||
this.description,
|
||
});
|
||
|
||
final SourceId source;
|
||
final String listingUrl;
|
||
final String slug;
|
||
final String title;
|
||
final String? coverUrl;
|
||
final String? description;
|
||
final List<ChapterLink> chapters;
|
||
}
|
||
|
||
/// Site adapter: per-source catalog menus + series + page URLs.
|
||
abstract class ComicSource {
|
||
SourceId get id;
|
||
|
||
String get displayName;
|
||
|
||
String get blurb;
|
||
|
||
/// Menus on this source’s Catalog home. Omit unsupported feeds (e.g. no search).
|
||
List<CatalogMenu> get catalogMenus;
|
||
|
||
/// Headers for page/CDN image requests.
|
||
Map<String, String> get imageHeaders;
|
||
|
||
Future<List<CatalogComic>> loadCatalog(
|
||
CatalogMenuKind kind, {
|
||
String query = '',
|
||
int offset = 0,
|
||
int limit = 30,
|
||
CatalogBrowseParams? browse,
|
||
});
|
||
|
||
Future<SeriesDetail> fetchSeries(String listingUrl);
|
||
|
||
Future<List<String>> fetchPageUrls(String chapterUrl);
|
||
}
|