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:
2026-08-29 17:50:31 +07:00
parent 6e7f1ced35
commit a7c6058540
105 changed files with 16721 additions and 275 deletions
+176
View File
@@ -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);
}