diff --git a/.editorconfig b/.editorconfig index 7f2b16d0..a352878e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,8 +6,8 @@ end_of_line = lf indent_style = tab max_line_length = 120 tab_width = 4 -insert_final_newline = false -disabled_rules=no-wildcard-imports,no-unused-imports +insert_final_newline = true +disabled_rules = no-wildcard-imports, no-unused-imports [{*.kt,*.kts}] ij_kotlin_allow_trailing_comma = true diff --git a/.github/workflows/test-parsers.yml b/.github/workflows/test-parsers.yml index 4479e758..817a3941 100644 --- a/.github/workflows/test-parsers.yml +++ b/.github/workflows/test-parsers.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: pull_request: paths: - - 'src/main/kotlin/org/koitharu/kotatsu/parsers/site/*' + - 'src/main/kotlin/org/koitharu/kotatsu/parsers/site/**' permissions: contents: read @@ -24,4 +24,4 @@ jobs: - uses: actions/upload-artifact@v3 with: name: Report - path: build/test-results-html/TEST-org.koitharu.kotatsu.parsers.MangaParserTest.htm \ No newline at end of file + path: build/test-results-html/TEST-org.koitharu.kotatsu.parsers.MangaParserTest.htm diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..4ba46f33 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,91 @@ +# Contributing + +The following is guide for creating a Kotatsu parsers. Thanks for taking the time to contribute! + +## Prerequisites + +Before you start, please note that the ability to use following technologies is **required**. + +- Basic [Android development](https://developer.android.com/) +- [Kotlin](https://kotlinlang.org/) +- Web scraping ([JSoup](https://jsoup.org/)) or JSON API + +### Tools + +- [Android Studio](https://developer.android.com/studio) +- [IntelliJ IDEA](https://www.jetbrains.com/idea/) (Community edition is enough) +- Android device (or emulator) + +Kotatsu parsers is not a part of Android application, but you can easily develop and test it directly inside an Android +application project and relocate it to the library project when done. + +### Before you start + +First, take a look at `kotatsu-parsers` project structure. Each parser is a single class that +extends `MangaParser` class and have a `MangaSourceParser` annotation. +Also pay attention on extensions in `util` package. For example, extensions from `Jsoup` file +should be used instead of existing JSoup functions because they have better nullability support +and improved error messages. + +## Writing your parser + +So, you want to create a parser, that will provide access to manga from a website. +First, you should explore a website for API availability. +If it does not contain any documentation about +API, [explore network requests](https://firefox-source-docs.mozilla.org/devtools-user/): +some websites use ajax. + +- [Example](https://github.com/KotatsuApp/kotatsu-parsers/blob/master/src/main/kotlin/org/koitharu/kotatsu/parsers/site/DesuMeParser.kt) + of Json API usage. +- [Example](https://github.com/KotatsuApp/kotatsu-parsers/blob/master/src/main/kotlin/org/koitharu/kotatsu/parsers/site/AnibelParser.kt) + of GraphQL API usage +- [Example](https://github.com/KotatsuApp/kotatsu-parsers/blob/master/src/main/kotlin/org/koitharu/kotatsu/parsers/site/MangaTownParser.kt) + of pure HTML parsing. + +If website is based on some engine it is rationally to use common base class for this one (for example, Madara wordress +theme +and the `MadaraParser` class) + +### Parser class skeleton + +Parser class must have exactly one primary constructor parameter of type `MangaLoaderContext` and have an +`MangaSourceParser` annotation that provides internal name, title and language of a manga source. + +All functions in `MangaParser` class are documented. Pay attention to some peculiarities: + +- Never hardcode domain. Specify default domain in `configKeyDomain` field and obtain an actual one using `getDomain()`. +- All ids must be unique and domain-independent. Use `generateUid` functions with relative url or some internal id which + is unique across the manga source. +- `sortOrders` set should not be empty. If your source is not support sorting, specify one most relevance value. +- If you cannot obtain direct links to pages images inside `getPages` method, it is ok to use an intermediate url + as `Page.url` and fetch a direct link at `getPageUrl` function. +- You can use _asserts_ to check some optional fields. For example. `Manga.author` field is not required, but if your + source provide such information, add `assert(it != null)`. This will not have any effect on production but help to + find issues during unit testing. +- If your source website (or it's api) uses pages for pagination instead of offset you should extend `PagedMangaParser` + instead of `MangaParser`. +- Your parser may also implement the `Interceptor` interface for additional manipulation of all network requests and/or + responses, including image loading. + +## Development process + +During the development it is recommended (but not necessary) to write it directly +in the Kotatsu android application project. You can use `core.parser.DummyParser` class as a sandbox. `Dummy` manga +source is available in debug Kotatsu build. + +Once parser is ready you can relocate your code into `kotatsu-parsers` library project in a `site` package and create a +Pull Request. + +### Testing + +It is recommended to run unit tests before submitting a PR. + +- Temporary modify the `MangaSources` annotation class: specify your parser(s) name(s) and change mode + to `EnumSource.Mode.INCLUDE` +- Run the `MangaParserTest` (`gradlew :test --tests "org.koitharu.kotatsu.parsers.MangaParserTest"`) +- Optionally, you can run the `generateTestsReport` gradle task to get a pretty readable html report from test results. + +## Help + +If you need a help or have some questions, ask a community in our [Telegram chat](https://t.me/kotatsuapp) +or [Discord server](https://discord.gg/NNJ5RgVBC5). diff --git a/README.md b/README.md index 504c9d0a..4612c1a9 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Kotatsu parsers -Library that provides manga sources. +This library provides manga sources. -[](https://jitpack.io/#KotatsuApp/kotatsu-parsers)   [](https://discord.gg/NNJ5RgVBC5) +[](https://jitpack.io/#KotatsuApp/kotatsu-parsers)   [](https://t.me/kotatsuapp) [](https://discord.gg/NNJ5RgVBC5) -### Usage +## Usage -1. Add it in your root build.gradle at the end of repositories: +1. Add it to your root build.gradle at the end of repositories: ```groovy allprojects { @@ -35,7 +35,7 @@ Library that provides manga sources. } ``` - See for versions at [JitPack](https://jitpack.io/#KotatsuApp/kotatsu-parsers) + Versions are available on [JitPack](https://jitpack.io/#KotatsuApp/kotatsu-parsers) 3. Usage in code @@ -44,8 +44,18 @@ Library that provides manga sources. ``` `mangaLoaderContext` is an implementation of the `MangaLoaderContext` class. - See [Android](https://github.com/KotatsuApp/Kotatsu/blob/devel/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt) + See examples + of [Android](https://github.com/KotatsuApp/Kotatsu/blob/devel/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt) and [Non-Android](https://github.com/KotatsuApp/kotatsu-dl/blob/master/src/main/kotlin/org/koitharu/kotatsu_dl/env/MangaLoaderContextImpl.kt) - implementation examples. + implementation. - Note that the `MangaSource.LOCAL` and `MangaSource.DUMMY` parsers cannot be instantiated. \ No newline at end of file + Note that the `MangaSource.LOCAL` and `MangaSource.DUMMY` parsers cannot be instantiated. + +## Contribution + +See [CONTRIBUTING.md](./CONTRIBUTING.md) for the guidelines. + +## DMCA disclaimer + +The developers of this application have no affiliation with the content available in the app. It is collected from +sources freely available through any web browser. diff --git a/build.gradle b/build.gradle index 9cae532e..cbf87048 100644 --- a/build.gradle +++ b/build.gradle @@ -57,7 +57,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'com.squareup.okio:okio:3.2.0' - api 'org.jsoup:jsoup:1.15.2' + api 'org.jsoup:jsoup:1.15.3' implementation 'org.json:json:20220320' implementation 'androidx.collection:collection-ktx:1.2.0' @@ -70,4 +70,5 @@ dependencies { testImplementation 'io.webfolder:quickjs:1.1.0' } +//noinspection ConfigurationAvoidance task generateTestsReport(type: ReportGenerateTask) \ No newline at end of file diff --git a/buildSrc/src/main/resources/report.html b/buildSrc/src/main/resources/report.html index 0a6c8dbf..b18a0dad 100644 --- a/buildSrc/src/main/resources/report.html +++ b/buildSrc/src/main/resources/report.html @@ -2,13 +2,15 @@
- +| Source | - {% for test in tests %} -{{ test }} | - {% endfor %} -||
|---|---|---|---|
| {{ name }} | - {% for test in tests %} - {% set case = cases[test] %} - {% if case.failure == null %} -- - | - {% else %} - {% if case.failure.type == 'java.lang.AssertionError' %} -- - | - {% else %} -- - | - {% endif %} - -