Add cxyz Android project structure and initial configuration
- 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.
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
// 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);
|
||||
}
|
||||
Reference in New Issue
Block a user