Maybe fix? please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please please
|
@ -1,32 +0,0 @@
|
|||
**PLEASE READ THIS**
|
||||
|
||||
I acknowledge that:
|
||||
|
||||
- I have updated to the latest version of the app (stable is v0.14.7)
|
||||
- I have updated all extensions
|
||||
- If this is an issue with the app itself, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi
|
||||
- I have searched the existing issues for duplicates
|
||||
- For source requests, I have checked the list of existing extensions including the multi-source spreadsheet: https://tachiyomi.org/extensions/
|
||||
|
||||
**DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT**
|
||||
|
||||
---
|
||||
|
||||
## Device information
|
||||
* Tachiyomi version: ?
|
||||
* Android version: ?
|
||||
* Device: ?
|
||||
|
||||
## Source information
|
||||
* Source name: ?
|
||||
* Source extension version: ?
|
||||
|
||||
## Steps to reproduce
|
||||
1. First step
|
||||
2. Second step
|
||||
|
||||
## Issue/Request
|
||||
?
|
||||
|
||||
## Other details
|
||||
Additional details and attachments.
|
|
@ -10,7 +10,7 @@ body:
|
|||
description: |
|
||||
You can find the extension name and version in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
Example: "Mangahere 1.3.18"
|
||||
Example: "Not Real Scans 1.4.1"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
@ -59,11 +59,11 @@ body:
|
|||
- type: input
|
||||
id: tachiyomi-version
|
||||
attributes:
|
||||
label: Tachiyomi version
|
||||
label: Mihon/Tachiyomi version
|
||||
description: |
|
||||
You can find your Tachiyomi version in **More → About**.
|
||||
You can find your Mihon/Tachiyomi version in **More → About**.
|
||||
placeholder: |
|
||||
Example: "0.14.7"
|
||||
Example: "0.16.3"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
@ -95,7 +95,7 @@ body:
|
|||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.14.7](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||
- label: I have updated the app to version **[0.15.3](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||
required: true
|
||||
- label: I have updated all installed extensions.
|
||||
required: true
|
||||
|
|
|
@ -45,14 +45,10 @@ body:
|
|||
label: Acknowledgements
|
||||
description: Your issue will be closed if you haven't done these steps.
|
||||
options:
|
||||
- label: I have checked that the extension does not already exist on the [website extensions list](https://tachiyomi.org/extensions/) or the app.
|
||||
required: true
|
||||
- label: I have checked that the extension is not on [the removed sources list](https://github.com/tachiyomiorg/tachiyomi-extensions/blob/master/REMOVED_SOURCES.md).
|
||||
- label: I have checked that the extension does not already exist by searching the [extension listing](https://keiyoushi.github.io/extensions/) and verified it does not appear there.
|
||||
required: true
|
||||
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open or closed issue.
|
||||
required: true
|
||||
- label: I have checked that the extension does not already exist by searching the [GitHub repository](https://github.com/tachiyomiorg/tachiyomi-extensions/) and verified it does not appear in the code base.
|
||||
required:
|
||||
- label: I have written a meaningful title with the source name.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
|
|
|
@ -10,7 +10,7 @@ body:
|
|||
description: |
|
||||
You can find the extension name and version in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
Example: "NotRealScans 1.3.1"
|
||||
Example: "NotRealScans 1.4.1"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
@ -47,7 +47,7 @@ body:
|
|||
options:
|
||||
- label: I have updated all installed extensions.
|
||||
required: true
|
||||
- label: I have opened WebView and checked that the source URL is not updated yet.
|
||||
- label: I have opened WebView and checked that the source URL has not changed yet.
|
||||
required: true
|
||||
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open or closed issue.
|
||||
required: true
|
||||
|
|
|
@ -16,7 +16,7 @@ body:
|
|||
description: |
|
||||
You can find the extension name in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
Example: "NotRealScans"
|
||||
Example: "Not Real Scans"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ body:
|
|||
description: |
|
||||
You can find the extension name in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
Example: "Mangahere"
|
||||
Example: "Not Real Scans"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
@ -51,9 +51,9 @@ body:
|
|||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: If this is an issue with the app itself, I should be opening an issue in the [app repository](https://github.com/tachiyomiorg/tachiyomi/issues/new/choose).
|
||||
- label: If this is an issue with the app itself, I should be opening an issue in the [app repository](https://github.com/mihonapp/mihon/issues/new/choose).
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.14.7](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||
- label: I have updated the app to version **[0.15.3](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
|
|
|
@ -31,9 +31,9 @@ body:
|
|||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: If this is an issue with the app itself, I should be opening an issue in the [app repository](https://github.com/tachiyomiorg/tachiyomi/issues/new/choose).
|
||||
- label: If this is an issue with the app itself, I should be opening an issue in the [app repository](https://github.com/mihonapp/mihon/issues/new/choose).
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.14.7](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||
- label: I have updated the app to version **[0.15.3](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||
required: true
|
||||
- label: I have updated all installed extensions.
|
||||
required: true
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
name: 🗑 Source removal request
|
||||
description: Scanlators can request their site to be removed
|
||||
labels: [Meta request]
|
||||
body:
|
||||
|
||||
- type: input
|
||||
id: link
|
||||
attributes:
|
||||
label: Source link
|
||||
placeholder: |
|
||||
Example: "https://notrealscans.org"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: other-details
|
||||
attributes:
|
||||
label: Other details
|
||||
placeholder: |
|
||||
Additional details and attachments.
|
||||
|
||||
- type: checkboxes
|
||||
id: requirements
|
||||
attributes:
|
||||
label: Requirements
|
||||
description: Your request will be denied if you don't meet these requirements.
|
||||
options:
|
||||
- label: I've added a `TXT` DNS record for my domain with the value `tachiyomi-verification`
|
||||
required: true
|
||||
- label: Site only hosts content scanlated by the group and not stolen from other scanlators or official releases (i.e., not an aggregator site)
|
||||
required: true
|
||||
- label: Site is not infested with user-hostile features (e.g., invasive or malicious ads)
|
||||
required: true
|
|
@ -1,11 +1,11 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: ⚠️ Application issue
|
||||
url: https://github.com/tachiyomiorg/tachiyomi/issues/new/choose
|
||||
about: Issues and requests about the app itself should be opened in the tachiyomi repository instead
|
||||
- name: 📦 Tachiyomi extensions
|
||||
url: https://tachiyomi.org/extensions
|
||||
url: https://github.com/mihonapp/mihon/issues/new/choose
|
||||
about: Issues and requests about the app itself should be opened in Mihon's repository instead
|
||||
- name: 📦 Extension list
|
||||
url: https://keiyoushi.github.io/extensions
|
||||
about: List of all available extensions with download links
|
||||
- name: 🖥️ Tachiyomi website
|
||||
url: https://tachiyomi.org/help/
|
||||
- name: 🖥️ Mihon website
|
||||
url: https://mihon.app/docs/guides/troubleshooting/
|
||||
about: Guides, troubleshooting, and answers to common questions
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[
|
||||
"ko.newtoki",
|
||||
"ko.wolfdotcom"
|
||||
]
|
Before Width: | Height: | Size: 1.1 KiB |
|
@ -1,4 +1,3 @@
|
|||
import html
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
@ -11,10 +10,8 @@ VERSION_CODE_REGEX = re.compile(r"versionCode='([^']+)'")
|
|||
VERSION_NAME_REGEX = re.compile(r"versionName='([^']+)'")
|
||||
IS_NSFW_REGEX = re.compile(r"'tachiyomi.extension.nsfw' value='([^']+)'")
|
||||
APPLICATION_LABEL_REGEX = re.compile(r"^application-label:'([^']+)'", re.MULTILINE)
|
||||
APPLICATION_ICON_320_REGEX = re.compile(
|
||||
r"^application-icon-320:'([^']+)'", re.MULTILINE
|
||||
)
|
||||
LANGUAGE_REGEX = re.compile(r"tachiyomi-([^\.]+)")
|
||||
APPLICATION_ICON_320_REGEX = re.compile(r"^application-icon-320:'([^']+)'", re.MULTILINE)
|
||||
LANGUAGE_REGEX = re.compile(r"tachiyomi-([^.]+)")
|
||||
|
||||
*_, ANDROID_BUILD_TOOLS = (Path(os.environ["ANDROID_HOME"]) / "build-tools").iterdir()
|
||||
REPO_DIR = Path("repo")
|
||||
|
@ -26,7 +23,6 @@ REPO_ICON_DIR.mkdir(parents=True, exist_ok=True)
|
|||
with open("output.json", encoding="utf-8") as f:
|
||||
inspector_data = json.load(f)
|
||||
|
||||
index_data = []
|
||||
index_min_data = []
|
||||
|
||||
for apk in REPO_APK_DIR.iterdir():
|
||||
|
@ -41,7 +37,7 @@ for apk in REPO_APK_DIR.iterdir():
|
|||
).decode()
|
||||
|
||||
package_info = next(x for x in badging.splitlines() if x.startswith("package: "))
|
||||
package_name = PACKAGE_NAME_REGEX.search(package_info).group(1)
|
||||
package_name = PACKAGE_NAME_REGEX.search(package_info).group(1)
|
||||
application_icon = APPLICATION_ICON_320_REGEX.search(badging).group(1)
|
||||
|
||||
with ZipFile(apk) as z, z.open(application_icon) as i, (
|
||||
|
@ -87,31 +83,6 @@ for apk in REPO_APK_DIR.iterdir():
|
|||
)
|
||||
|
||||
index_min_data.append(min_data)
|
||||
index_data.append(
|
||||
{
|
||||
**common_data,
|
||||
"hasReadme": 0,
|
||||
"hasChangelog": 0,
|
||||
"sources": sources,
|
||||
}
|
||||
)
|
||||
|
||||
index_data.sort(key=lambda x: x["pkg"])
|
||||
index_min_data.sort(key=lambda x: x["pkg"])
|
||||
|
||||
with (REPO_DIR / "index.json").open("w", encoding="utf-8") as f:
|
||||
index_data_str = json.dumps(index_data, ensure_ascii=False, indent=2)
|
||||
|
||||
print(index_data_str)
|
||||
f.write(index_data_str)
|
||||
|
||||
with (REPO_DIR / "index.min.json").open("w", encoding="utf-8") as f:
|
||||
json.dump(index_min_data, f, ensure_ascii=False, separators=(",", ":"))
|
||||
|
||||
with (REPO_DIR / "index.html").open("w", encoding="utf-8") as f:
|
||||
f.write('<!DOCTYPE html>\n<html>\n<head>\n<meta charset="UTF-8">\n<title>apks</title>\n</head>\n<body>\n<pre>\n')
|
||||
for entry in index_data:
|
||||
apk_escaped = 'apk/' + html.escape(entry["apk"])
|
||||
name_escaped = html.escape(entry["name"])
|
||||
f.write(f'<a href="{apk_escaped}">{name_escaped}</a>\n')
|
||||
f.write('</pre>\n</body>\n</html>\n')
|
||||
with REPO_DIR.joinpath("index.min.json").open("w", encoding="utf-8") as index_file:
|
||||
json.dump(index_min_data, index_file, ensure_ascii=False, separators=(",", ":"))
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
import itertools
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import NoReturn
|
||||
|
||||
EXTENSION_REGEX = re.compile(r"^src/(?P<lang>\w+)/(?P<extension>\w+)")
|
||||
MULTISRC_LIB_REGEX = re.compile(r"^lib-multisrc/(?P<multisrc>\w+)")
|
||||
LIB_REGEX = re.compile(r"^lib/(?P<lib>\w+)")
|
||||
MODULE_REGEX = re.compile(r"^:src:(?P<lang>\w+):(?P<extension>\w+)$")
|
||||
CORE_FILES_REGEX = re.compile(
|
||||
r"^(buildSrc/|core/|gradle/|build\.gradle\.kts|common\.gradle|gradle\.properties|settings\.gradle\.kts)"
|
||||
)
|
||||
|
||||
def run_command(command: str) -> str:
|
||||
result = subprocess.run(command, capture_output=True, text=True, shell=True)
|
||||
if result.returncode != 0:
|
||||
print(result.stderr.strip())
|
||||
sys.exit(result.returncode)
|
||||
return result.stdout.strip()
|
||||
|
||||
def get_module_list(ref: str) -> tuple[list[str], list[str]]:
|
||||
changed_files = run_command(f"git diff --name-only {ref}").splitlines()
|
||||
|
||||
modules = set()
|
||||
libs = set()
|
||||
deleted = set()
|
||||
core_files_changed = False
|
||||
|
||||
for file in map(lambda x: Path(x).as_posix(), changed_files):
|
||||
if CORE_FILES_REGEX.search(file):
|
||||
core_files_changed = True
|
||||
elif match := EXTENSION_REGEX.search(file):
|
||||
lang = match.group("lang")
|
||||
extension = match.group("extension")
|
||||
if Path("src", lang, extension).is_dir():
|
||||
modules.add(f':src:{lang}:{extension}')
|
||||
deleted.add(f"{lang}.{extension}")
|
||||
elif match := MULTISRC_LIB_REGEX.search(file):
|
||||
multisrc = match.group("multisrc")
|
||||
if Path("lib-multisrc", multisrc).is_dir():
|
||||
libs.add(f":lib-multisrc:{multisrc}:printDependentExtensions")
|
||||
elif match := LIB_REGEX.search(file):
|
||||
lib = match.group("lib")
|
||||
if Path("lib", lib).is_dir():
|
||||
libs.add(f":lib:{lib}:printDependentExtensions")
|
||||
|
||||
def is_extension_module(module: str) -> bool:
|
||||
if not (match := MODULE_REGEX.search(module)):
|
||||
return False
|
||||
lang = match.group("lang")
|
||||
extension = match.group("extension")
|
||||
deleted.add(f"{lang}.{extension}")
|
||||
return True
|
||||
|
||||
if len(libs) != 0 and not core_files_changed:
|
||||
modules.update([
|
||||
module for module in
|
||||
run_command("./gradlew -q " + " ".join(libs)).splitlines()
|
||||
if is_extension_module(module)
|
||||
])
|
||||
|
||||
if os.getenv("IS_PR_CHECK") != "true" and not core_files_changed:
|
||||
with Path(".github/always_build.json").open() as always_build_file:
|
||||
always_build = json.load(always_build_file)
|
||||
for extension in always_build:
|
||||
modules.add(":src:" + extension.replace(".", ":"))
|
||||
deleted.add(extension)
|
||||
|
||||
if core_files_changed:
|
||||
(all_modules, all_deleted) = get_all_modules()
|
||||
|
||||
modules.update(all_modules)
|
||||
deleted.update(all_deleted)
|
||||
|
||||
return list(modules), list(deleted)
|
||||
|
||||
def get_all_modules() -> tuple[list[str], list[str]]:
|
||||
modules = []
|
||||
deleted = []
|
||||
for lang in Path("src").iterdir():
|
||||
for extension in lang.iterdir():
|
||||
modules.append(f":src:{lang.name}:{extension.name}")
|
||||
deleted.append(f"{lang.name}.{extension.name}")
|
||||
return modules, deleted
|
||||
|
||||
def main() -> NoReturn:
|
||||
_, ref, build_type = sys.argv
|
||||
modules, deleted = get_module_list(ref)
|
||||
|
||||
chunked = {
|
||||
"chunk": [
|
||||
{"number": i + 1, "modules": modules}
|
||||
for i, modules in
|
||||
enumerate(itertools.batched(
|
||||
map(lambda x: f"{x}:assemble{build_type}", modules),
|
||||
int(os.getenv("CI_CHUNK_SIZE", 65))
|
||||
))
|
||||
]
|
||||
}
|
||||
|
||||
print(f"Module chunks to build:\n{json.dumps(chunked, indent=2)}\n\nModule to delete:\n{json.dumps(deleted, indent=2)}")
|
||||
|
||||
if os.getenv("CI") == "true":
|
||||
with open(os.getenv("GITHUB_OUTPUT"), 'a') as out_file:
|
||||
out_file.write(f"matrix={json.dumps(chunked)}\n")
|
||||
out_file.write(f"delete={json.dumps(deleted)}\n")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,50 @@
|
|||
import html
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
REMOTE_REPO: Path = Path.cwd()
|
||||
LOCAL_REPO: Path = REMOTE_REPO.parent.joinpath("main/repo")
|
||||
|
||||
to_delete: list[str] = json.loads(sys.argv[1])
|
||||
|
||||
for module in to_delete:
|
||||
apk_name = f"tachiyomi-{module}-v*.*.*.apk"
|
||||
icon_name = f"eu.kanade.tachiyomi.extension.{module}.png"
|
||||
for file in REMOTE_REPO.joinpath("apk").glob(apk_name):
|
||||
print(file.name)
|
||||
file.unlink(missing_ok=True)
|
||||
for file in REMOTE_REPO.joinpath("icon").glob(icon_name):
|
||||
print(file.name)
|
||||
file.unlink(missing_ok=True)
|
||||
|
||||
shutil.copytree(src=LOCAL_REPO.joinpath("apk"), dst=REMOTE_REPO.joinpath("apk"), dirs_exist_ok = True)
|
||||
shutil.copytree(src=LOCAL_REPO.joinpath("icon"), dst=REMOTE_REPO.joinpath("icon"), dirs_exist_ok = True)
|
||||
|
||||
with REMOTE_REPO.joinpath("index.min.json").open() as remote_index_file:
|
||||
remote_index = json.load(remote_index_file)
|
||||
|
||||
with LOCAL_REPO.joinpath("index.min.json").open() as local_index_file:
|
||||
local_index = json.load(local_index_file)
|
||||
|
||||
index = [
|
||||
item for item in remote_index
|
||||
if not any([item["pkg"].endswith(f".{module}") for module in to_delete])
|
||||
]
|
||||
index.extend(local_index)
|
||||
index.sort(key=lambda x: x["pkg"])
|
||||
|
||||
with REMOTE_REPO.joinpath("index.json").open("w", encoding="utf-8") as index_file:
|
||||
json.dump(index, index_file, ensure_ascii=False, indent=2)
|
||||
|
||||
with REMOTE_REPO.joinpath("index.min.json").open("w", encoding="utf-8") as index_min_file:
|
||||
json.dump(index, index_min_file, ensure_ascii=False, separators=(",", ":"))
|
||||
|
||||
with REMOTE_REPO.joinpath("index.html").open("w", encoding="utf-8") as index_html_file:
|
||||
index_html_file.write('<!DOCTYPE html>\n<html>\n<head>\n<meta charset="UTF-8">\n<title>apks</title>\n</head>\n<body>\n<pre>\n')
|
||||
for entry in index:
|
||||
apk_escaped = 'apk/' + html.escape(entry["apk"])
|
||||
name_escaped = html.escape(entry["name"])
|
||||
index_html_file.write(f'<a href="{apk_escaped}">{name_escaped}</a>\n')
|
||||
index_html_file.write('</pre>\n</body>\n</html>\n')
|
10
.github/scripts/move-apks.py → .github/scripts/move-built-apks.py
vendored
Executable file → Normal file
|
@ -3,14 +3,10 @@ import shutil
|
|||
|
||||
REPO_APK_DIR = Path("repo/apk")
|
||||
|
||||
try:
|
||||
shutil.rmtree(REPO_APK_DIR)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
shutil.rmtree(REPO_APK_DIR, ignore_errors=True)
|
||||
REPO_APK_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for apk in (Path.home() / "apk-artifacts").glob("**/*.apk"):
|
||||
for apk in Path.home().joinpath("apk-artifacts").glob("**/*.apk"):
|
||||
apk_name = apk.name.replace("-release.apk", ".apk")
|
||||
|
||||
shutil.move(apk, REPO_APK_DIR / apk_name)
|
||||
shutil.move(apk, REPO_APK_DIR.joinpath(apk_name))
|
|
@ -0,0 +1,71 @@
|
|||
name: PR check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**'
|
||||
- '!**.md'
|
||||
- '!.github/**'
|
||||
- '.github/workflows/build_pull_request.yml'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CI_CHUNK_SIZE: 65
|
||||
IS_PR_CHECK: true
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
name: Prepare job
|
||||
runs-on: 'ubuntu-24.04'
|
||||
outputs:
|
||||
matrix: ${{ steps.generate-matrices.outputs.matrix }}
|
||||
delete: ${{ steps.generate-matrices.outputs.delete }}
|
||||
steps:
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # v4.3.0
|
||||
with:
|
||||
cache-read-only: true
|
||||
|
||||
- id: generate-matrices
|
||||
name: Generate build matrices
|
||||
run: |
|
||||
git fetch origin main
|
||||
python ./.github/scripts/generate-build-matrices.py origin/main Debug
|
||||
|
||||
build:
|
||||
name: Build extensions (${{ matrix.chunk.number }})
|
||||
needs: prepare
|
||||
runs-on: 'ubuntu-24.04'
|
||||
if: ${{ toJson(fromJson(needs.prepare.outputs.matrix).chunk) != '[]' }}
|
||||
strategy:
|
||||
matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}
|
||||
steps:
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # v4.3.0
|
||||
with:
|
||||
cache-read-only: true
|
||||
|
||||
- name: Build extensions (${{ matrix.chunk.number }})
|
||||
run: |
|
||||
./gradlew $(echo '${{ toJson(matrix.chunk.modules) }}' | jq -r 'join(" ")')
|
|
@ -24,7 +24,7 @@ jobs:
|
|||
CI_MODULE_GEN: true
|
||||
steps:
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
build_individual:
|
||||
name: Build individual modules
|
||||
|
@ -32,10 +32,10 @@ jobs:
|
|||
runs-on: arch
|
||||
steps:
|
||||
- name: Checkout master branch
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
|
@ -45,7 +45,7 @@ jobs:
|
|||
echo ${{ secrets.SIGNING_KEY }} | base64 -d > signingkey.jks
|
||||
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2
|
||||
uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # v4.3.0
|
||||
|
||||
- name: Build extensions
|
||||
env:
|
||||
|
@ -56,7 +56,7 @@ jobs:
|
|||
|
||||
|
||||
- name: Upload APKs
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
||||
with:
|
||||
name: "individual-apks"
|
||||
path: "**/*.apk"
|
||||
|
@ -72,18 +72,18 @@ jobs:
|
|||
runs-on: arch
|
||||
steps:
|
||||
- name: Download APK artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
path: ~/apk-artifacts
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
|
||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
|
||||
- name: Checkout master branch
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: master
|
||||
path: master
|
||||
|
@ -91,14 +91,14 @@ jobs:
|
|||
- name: Create repo artifacts
|
||||
run: |
|
||||
cd master
|
||||
python ./.github/scripts/move-apks.py
|
||||
python ./.github/scripts/move-built-apks.py
|
||||
INSPECTOR_LINK="$(curl -s "https://api.github.com/repos/keiyoushi/extensions-inspector/releases/latest" | jq -r '.assets[0].browser_download_url')"
|
||||
curl -L "$INSPECTOR_LINK" -o ./Inspector.jar
|
||||
java -jar ./Inspector.jar "repo/apk" "output.json" "tmp"
|
||||
python ./.github/scripts/create-repo.py
|
||||
|
||||
- name: Checkout repo branch
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: repo
|
||||
path: repo
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Sync repo to the Codeberg mirror
|
||||
name: Mirror Sync
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
workflow_dispatch: # Manual dispatch
|
||||
schedule:
|
||||
- cron: "0 */8 * * *"
|
||||
|
||||
jobs:
|
||||
codeberg:
|
||||
if: "github.repository == 'keiyoushi/extensions-source'"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: pixta-dev/repository-mirroring-action@674e65a7d483ca28dafaacba0d07351bdcc8bd75 # v1.1.1
|
||||
with:
|
||||
target_repo_url: "git@codeberg.org:keiyoushi/extensions-source.git"
|
||||
ssh_private_key: ${{ secrets.CODEBERG_SSH }}
|
|
@ -0,0 +1,71 @@
|
|||
name: Issue moderator
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [ opened, edited, reopened ]
|
||||
issue_comment:
|
||||
types: [ created ]
|
||||
|
||||
jobs:
|
||||
autoclose:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Moderate issues
|
||||
uses: keiyoushi/issue-moderator-action@a017be83547db6e107431ce7575f53c1dfa3296a
|
||||
with:
|
||||
repo-token: ${{ secrets.ISSUE_MODERATOR_PAT }}
|
||||
duplicate-label: Duplicate
|
||||
|
||||
blurbs: |
|
||||
[
|
||||
{
|
||||
"keywords": ["cf", "cloudflare"],
|
||||
"message": "Refer to the **Solving Cloudflare issues** section at https://keiyoushi.github.io/docs/guides/troubleshooting#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection."
|
||||
},
|
||||
{
|
||||
"keywords": ["uninstall"],
|
||||
"message": "Uninstall the extension before updating. If that does not work, uninstall it from your device's settings by navigating to your device's Settings -> Apps and uninstall it from there by looking for `Tachiyomi: <extension name>`.\n\nThis is usually caused your previous extension having a different signature from the updated extension, making Android refuse to update it."
|
||||
}
|
||||
]
|
||||
|
||||
duplicate-check-enabled: true
|
||||
duplicate-check-labels: |
|
||||
["Source request", "Domain changed"]
|
||||
|
||||
existing-check-enabled: true
|
||||
existing-check-labels: |
|
||||
["Source request", "Domain changed"]
|
||||
existing-check-repo-url: https://raw.githubusercontent.com/keiyoushi/extensions/repo/index.min.json
|
||||
|
||||
auto-close-rules: |
|
||||
[
|
||||
{
|
||||
"type": "both",
|
||||
"regex": ".*(?:fail(?:ed|ure|s)?|can\\s*(?:no|')?t|(?:not|un).*able|(?<!n[o']?t )blocked by|error) (?:to )?(?:get past|by ?pass|penetrate)?.*cl[oa]ud ?fl?[ai]re.*",
|
||||
"ignoreCase": true,
|
||||
"labels": ["Cloudflare protected"],
|
||||
"message": "Refer to the **Solving Cloudflare issues** section at https://keiyoushi.github.io/docs/guides/troubleshooting#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection."
|
||||
},
|
||||
{
|
||||
"type": "both",
|
||||
"regex": "remanga\\.org",
|
||||
"ignoreCase": true,
|
||||
"labels": ["invalid"],
|
||||
"message": "ReManga (Russian) will not be added back as it has been removed [due to legal reasons](https://github.com/github/dmca)."
|
||||
},
|
||||
{
|
||||
"type": "both",
|
||||
"regex": "(kumanga\\.com)",
|
||||
"ignoreCase": true,
|
||||
"labels": ["invalid"],
|
||||
"message": "{match} will not be added back as it is too difficult to maintain."
|
||||
},
|
||||
{
|
||||
"type": "both",
|
||||
"regex": "(centralnovel\\.com)",
|
||||
"ignoreCase": true,
|
||||
"labels": ["invalid"],
|
||||
"message": "Novels aren't supported"
|
||||
}
|
||||
]
|
||||
auto-close-ignore-label: do-not-autoclose
|
|
@ -0,0 +1,20 @@
|
|||
name: Lock threads
|
||||
|
||||
on:
|
||||
# Daily
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
# Manual trigger
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: '2'
|
||||
pr-inactive-days: '2'
|
20
README.md
|
@ -1,13 +1,24 @@
|
|||
### Please give the repo a :star:
|
||||
|
||||
| Build | Support Server |
|
||||
|-------|---------|
|
||||
| Build | Support Server |
|
||||
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [](https://github.com/keiyoushi/extensions-source/actions/workflows/build_push.yml) | [](https://discord.gg/3FbCpdKbdY) |
|
||||
|
||||
# Usage
|
||||
|
||||
[Getting started](https://keiyoushi.github.io/docs/guides/getting-started#adding-the-extension-repo)
|
||||
|
||||
# Requests
|
||||
|
||||
To request a new source or bug fix, [create an issue](https://github.com/keiyoushi/extensions-source/issues/new/choose).
|
||||
|
||||
Please note that creating an issue does not mean that the source will be added or fixed in a timely
|
||||
fashion, because the work is volunteer-based. Some sources may also be impossible to do or prohibitively
|
||||
difficult to maintain.
|
||||
|
||||
If you would like to see a request fulfilled and have the necessary skills to do so, consider contributing!
|
||||
Issues are up-for-grabs for any developer if there is no assigned user already.
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions are welcome!
|
||||
|
@ -32,5 +43,8 @@ Check out the repo's [issue backlog](https://github.com/keiyoushi/extensions-sou
|
|||
|
||||
## Disclaimer
|
||||
|
||||
This project is not affiliated with Tachiyomi. Don't ask for help about these extensions at the official support means of Tachiyomi. All credits to the codebase goes to the original contributors.
|
||||
This project does not have any affiliation with the content providers available.
|
||||
|
||||
This project is not affiliated with Mihon/Tachiyomi. Don't ask for help about these extensions at the
|
||||
official support means of Mihon/Tachiyomi. All credits to the codebase goes to the original contributors.
|
||||
|
||||
|
|
|
@ -1,6 +1,36 @@
|
|||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.plugins.ExtensionAware
|
||||
import org.gradle.kotlin.dsl.extra
|
||||
|
||||
var ExtensionAware.baseVersionCode: Int
|
||||
get() = extra.get("baseVersionCode") as Int
|
||||
set(value) = extra.set("baseVersionCode", value)
|
||||
|
||||
fun Project.getDependents(): Set<Project> {
|
||||
val dependentProjects = mutableSetOf<Project>()
|
||||
|
||||
rootProject.allprojects.forEach { project ->
|
||||
project.configurations.forEach { configuration ->
|
||||
configuration.dependencies.forEach { dependency ->
|
||||
if (dependency is ProjectDependency && dependency.path == path) {
|
||||
dependentProjects.add(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dependentProjects
|
||||
}
|
||||
|
||||
fun Project.printDependentExtensions() {
|
||||
getDependents().forEach { project ->
|
||||
if (project.path.startsWith(":src:")) {
|
||||
println(project.path)
|
||||
} else if (project.path.startsWith(":lib-multisrc:")) {
|
||||
project.getDependents().forEach { println(it.path) }
|
||||
} else if (project.path.startsWith(":lib:")) {
|
||||
project.printDependentExtensions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,3 +21,9 @@ android {
|
|||
dependencies {
|
||||
compileOnly(versionCatalogs.named("libs").findBundle("common").get())
|
||||
}
|
||||
|
||||
tasks.register("printDependentExtensions") {
|
||||
doLast {
|
||||
project.printDependentExtensions()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
/**
|
||||
* Add or remove modules to load as needed for local development here.
|
||||
*/
|
||||
loadAllIndividualExtensions()
|
||||
// loadIndividualExtension("all", "mangadex")
|
||||
|
||||
/**
|
||||
* ===================================== COMMON CONFIGURATION ======================================
|
||||
*/
|
||||
include(":core")
|
||||
include(":utils")
|
||||
|
||||
|
@ -7,11 +16,9 @@ File(rootDir, "lib").eachDir { include("lib:${it.name}") }
|
|||
// Load all modules under /lib-multisrc
|
||||
File(rootDir, "lib-multisrc").eachDir { include("lib-multisrc:${it.name}") }
|
||||
|
||||
|
||||
|
||||
loadAllIndividualExtensions()
|
||||
|
||||
|
||||
/**
|
||||
* ======================================== HELPER FUNCTION ========================================
|
||||
*/
|
||||
fun loadAllIndividualExtensions() {
|
||||
File(rootDir, "src").eachDir { dir ->
|
||||
dir.eachDir { subdir ->
|
||||
|
@ -23,18 +30,6 @@ fun loadIndividualExtension(lang: String, name: String) {
|
|||
include("src:${lang}:${name}")
|
||||
}
|
||||
|
||||
fun File.getChunk(chunk: Int, chunkSize: Int): List<File>? {
|
||||
return listFiles()
|
||||
// Lang folder
|
||||
?.filter { it.isDirectory }
|
||||
// Extension subfolders
|
||||
?.mapNotNull { dir -> dir.listFiles()?.filter { it.isDirectory } }
|
||||
?.flatten()
|
||||
?.sortedBy { it.name }
|
||||
?.chunked(chunkSize)
|
||||
?.get(chunk)
|
||||
}
|
||||
|
||||
fun File.eachDir(block: (File) -> Unit) {
|
||||
val files = listFiles() ?: return
|
||||
for (file in files) {
|
||||
|
@ -42,4 +37,4 @@ fun File.eachDir(block: (File) -> Unit) {
|
|||
block(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 56 KiB |
|
@ -1,7 +0,0 @@
|
|||
ext {
|
||||
extName = 'RE Manga'
|
||||
extClass = '.REManga'
|
||||
extVersionCode = 2
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 25 KiB |
|
@ -1,258 +0,0 @@
|
|||
package eu.kanade.tachiyomi.extension.ar.remanga
|
||||
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class REManga : ParsedHttpSource() {
|
||||
|
||||
override val name = "RE Manga"
|
||||
|
||||
override val baseUrl = "https://re-manga.com"
|
||||
|
||||
override val lang = "ar"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
// Popular
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request =
|
||||
GET("$baseUrl/manga-list/?title=&order=popular&status=&type=")
|
||||
|
||||
override fun popularMangaSelector() = "article.animpost"
|
||||
|
||||
override fun popularMangaFromElement(element: Element): SManga =
|
||||
SManga.create().apply {
|
||||
setUrlWithoutDomain(element.select("a").attr("abs:href"))
|
||||
element.select("img").let {
|
||||
thumbnail_url = it.attr("abs:src")
|
||||
title = it.attr("title")
|
||||
}
|
||||
}
|
||||
|
||||
override fun popularMangaNextPageSelector(): String? = null
|
||||
|
||||
// Latest
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request =
|
||||
GET("$baseUrl/manga-list/?title=&order=update&status=&type=")
|
||||
|
||||
override fun latestUpdatesSelector() = popularMangaSelector()
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
|
||||
override fun latestUpdatesNextPageSelector(): String? = popularMangaNextPageSelector()
|
||||
|
||||
// Search
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/manga-list/?".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("title", query)
|
||||
filters.forEach { filter ->
|
||||
when (filter) {
|
||||
is SortFilter -> url.addQueryParameter("order", filter.toUriPart())
|
||||
|
||||
is StatusFilter -> url.addQueryParameter("status", filter.toUriPart())
|
||||
|
||||
is TypeFilter -> url.addQueryParameter("type", filter.toUriPart())
|
||||
|
||||
is GenreFilter -> {
|
||||
filter.state
|
||||
.filter { it.state != Filter.TriState.STATE_IGNORE }
|
||||
.forEach { url.addQueryParameter("genre[]", it.id) }
|
||||
}
|
||||
|
||||
is YearFilter -> {
|
||||
filter.state
|
||||
.filter { it.state != Filter.TriState.STATE_IGNORE }
|
||||
.forEach { url.addQueryParameter("years[]", it.id) }
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
return GET(url.build(), headers)
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = popularMangaSelector()
|
||||
|
||||
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
|
||||
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
// Details
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga {
|
||||
return SManga.create().apply {
|
||||
document.select("div.infox").first()!!.let { info ->
|
||||
title = info.select("h1").text()
|
||||
}
|
||||
description = document.select("div.desc > div > p").text()
|
||||
genre = document.select("div.spe > span:contains(نوع), div.genre-info > a").joinToString { it.text() }
|
||||
document.select("div.spe > span:contains(الحالة)").first()?.text()?.also { statusText ->
|
||||
when {
|
||||
statusText.contains("مستمر", true) -> status = SManga.ONGOING
|
||||
else -> status = SManga.COMPLETED
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chapters
|
||||
|
||||
override fun chapterListSelector() = ".lsteps li"
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter {
|
||||
return SChapter.create().apply {
|
||||
setUrlWithoutDomain(element.select("a").first()!!.attr("abs:href"))
|
||||
|
||||
val chNum = element.select(".eps > a").first()!!.text()
|
||||
val chTitle = element.select(".lchx > a").first()!!.text()
|
||||
|
||||
name = when {
|
||||
chTitle.startsWith("الفصل ") -> chTitle
|
||||
else -> "الفصل $chNum - $chTitle"
|
||||
}
|
||||
|
||||
element.select(".date").first()?.text()?.let { date ->
|
||||
date_upload = DATE_FORMATTER.parse(date)?.time ?: 0L
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pages
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
return document.select("div.reader-area img").mapIndexed { i, img ->
|
||||
Page(i, "", img.attr("abs:src"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
|
||||
|
||||
// Filters
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
SortFilter(getSortFilters()),
|
||||
StatusFilter(getStatusFilters()),
|
||||
TypeFilter(getTypeFilter()),
|
||||
Filter.Separator(),
|
||||
Filter.Header("exclusion not available for This source"),
|
||||
GenreFilter(getGenreFilters()),
|
||||
YearFilter(getYearFilters()),
|
||||
)
|
||||
|
||||
private class SortFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("Sort by", vals)
|
||||
|
||||
private class TypeFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("Type", vals)
|
||||
|
||||
private class StatusFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("Status", vals)
|
||||
|
||||
class Genre(name: String, val id: String = name) : Filter.TriState(name)
|
||||
private class GenreFilter(genres: List<Genre>) : Filter.Group<Genre>("Genre", genres)
|
||||
|
||||
class Year(name: String, val id: String = name) : Filter.TriState(name)
|
||||
private class YearFilter(years: List<Year>) : Filter.Group<Year>("Year", years)
|
||||
|
||||
private fun getSortFilters(): Array<Pair<String?, String>> = arrayOf(
|
||||
Pair("title", "A-Z"),
|
||||
Pair("titlereverse", "Z-A"),
|
||||
Pair("update", "Latest Update"),
|
||||
Pair("latest", "Latest Added"),
|
||||
Pair("popular", "Popular"),
|
||||
)
|
||||
|
||||
private fun getStatusFilters(): Array<Pair<String?, String>> = arrayOf(
|
||||
Pair("", "All"),
|
||||
Pair("Publishing", "مستمر"),
|
||||
Pair("Finished", "تاريخ انتهي"),
|
||||
)
|
||||
|
||||
private fun getTypeFilter(): Array<Pair<String?, String>> = arrayOf(
|
||||
Pair("", "All"),
|
||||
Pair("Manga", "Manga"),
|
||||
Pair("Manhwa", "Manhwa"),
|
||||
Pair("Manhua", "Manhua"),
|
||||
)
|
||||
|
||||
private fun getGenreFilters(): List<Genre> = listOf(
|
||||
Genre("Action", "action"),
|
||||
Genre("Adventure", "adventure"),
|
||||
Genre("Comedy", "comedy"),
|
||||
Genre("Dementia", "dementia"),
|
||||
Genre("Demons", "demons"),
|
||||
Genre("Drama", "drama"),
|
||||
Genre("Ecchi", "ecchi"),
|
||||
Genre("Fantasy", "fantasy"),
|
||||
Genre("Harem", "harem"),
|
||||
Genre("Historical", "historical"),
|
||||
Genre("Horror", "horror"),
|
||||
Genre("Josei", "josei"),
|
||||
Genre("Magic", "magic"),
|
||||
Genre("Martial Arts", "martial-arts"),
|
||||
Genre("Military", "military"),
|
||||
Genre("Mystery", "mystery"),
|
||||
Genre("Parody", "parody"),
|
||||
Genre("Psychological", "psychological"),
|
||||
Genre("Romance", "romance"),
|
||||
Genre("Samurai", "samurai"),
|
||||
Genre("School", "school"),
|
||||
Genre("Sci-Fi", "sci-fi"),
|
||||
Genre("Seinen", "seinen"),
|
||||
Genre("Shounen", "shounen"),
|
||||
Genre("Slice of Life", "slice-of-life"),
|
||||
Genre("Sports", "sports"),
|
||||
Genre("Super Power", "super-power"),
|
||||
Genre("Supernatural", "supernatural"),
|
||||
Genre("Vampire", "vampire"),
|
||||
)
|
||||
|
||||
private fun getYearFilters(): List<Year> = listOf(
|
||||
Year("1970", "1970"),
|
||||
Year("1986", "1986"),
|
||||
Year("1989", "1989"),
|
||||
Year("1995", "1995"),
|
||||
Year("1997", "1997"),
|
||||
Year("1998", "1998"),
|
||||
Year("1999", "1999"),
|
||||
Year("2000", "2000"),
|
||||
Year("2002", "2002"),
|
||||
Year("2003", "2003"),
|
||||
Year("2004", "2004"),
|
||||
Year("2005", "2005"),
|
||||
Year("2006", "2006"),
|
||||
Year("2007", "2007"),
|
||||
Year("2008", "2008"),
|
||||
Year("2009", "2009"),
|
||||
Year("2010", "2010"),
|
||||
Year("2011", "2011"),
|
||||
Year("2012", "2012"),
|
||||
Year("2013", "2013"),
|
||||
Year("2014", "2014"),
|
||||
Year("2016", "2016"),
|
||||
Year("2017", "2017"),
|
||||
Year("2018", "2018"),
|
||||
Year("2019", "2019"),
|
||||
Year("2020", "2020"),
|
||||
)
|
||||
|
||||
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String?, String>>) :
|
||||
Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray()) {
|
||||
fun toUriPart() = vals[state].first
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DATE_FORMATTER by lazy {
|
||||
SimpleDateFormat("MMM d, yyy", Locale("ar"))
|
||||
}
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 236 KiB |
|
@ -55,12 +55,14 @@ class SushiScan :
|
|||
.set("Referer", "$baseUrl$mangaUrlDirectory")
|
||||
|
||||
override val altNamePrefix = "Nom alternatif : "
|
||||
override val seriesAuthorSelector = ".imptdt:contains(Auteur) i, .fmed b:contains(Auteur)+span"
|
||||
override val seriesStatusSelector = ".imptdt:contains(Statut) i"
|
||||
override val seriesAuthorSelector = ".infotable tr:contains(Auteur) td:last-child"
|
||||
override val seriesStatusSelector = ".infotable tr:contains(Statut) td:last-child"
|
||||
override fun String?.parseStatus(): Int = when {
|
||||
this == null -> SManga.UNKNOWN
|
||||
this.contains("En Cours", ignoreCase = true) -> SManga.ONGOING
|
||||
this.contains("Terminé", ignoreCase = true) -> SManga.COMPLETED
|
||||
this.contains("Abandonné", ignoreCase = true) -> SManga.CANCELLED
|
||||
this.contains("En Pause", ignoreCase = true) -> SManga.ON_HIATUS
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application>
|
||||
<activity
|
||||
android:name=".ru.remanga.RemangaActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<!-- RemangaActivity sites can be added here. -->
|
||||
<data
|
||||
android:host="remanga.org"
|
||||
android:pathPattern="/manga/..*"
|
||||
android:scheme="https" />
|
||||
<data
|
||||
android:host="xn--80aaig9ahr.xn--c1avg"
|
||||
android:pathPattern="/manga/..*"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
|
@ -1,11 +0,0 @@
|
|||
ext {
|
||||
extName = 'Remanga'
|
||||
extClass = '.Remanga'
|
||||
extVersionCode = 86
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation project(':lib:dataimage')
|
||||
}
|
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 20 KiB |
|
@ -1,40 +0,0 @@
|
|||
package eu.kanade.tachiyomi.extension.ru.remanga
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import kotlin.system.exitProcess
|
||||
/**
|
||||
* Springboard that accepts https://remanga.org/manga/xxx intents and redirects them to
|
||||
* the main tachiyomi process. The idea is to not install the intent filter unless
|
||||
* you have this extension installed, but still let the main tachiyomi app control
|
||||
* things.
|
||||
*/
|
||||
class RemangaActivity : Activity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val pathSegments = intent?.data?.pathSegments
|
||||
if (pathSegments != null && pathSegments.size > 1) {
|
||||
val titleid = pathSegments[1]
|
||||
val mainIntent = Intent().apply {
|
||||
action = "eu.kanade.tachiyomi.SEARCH"
|
||||
putExtra("query", "${Remanga.PREFIX_SLUG_SEARCH}$titleid")
|
||||
putExtra("filter", packageName)
|
||||
}
|
||||
|
||||
try {
|
||||
startActivity(mainIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e("RemangaActivity", e.toString())
|
||||
}
|
||||
} else {
|
||||
Log.e("RemangaActivity", "could not parse uri from intent $intent")
|
||||
}
|
||||
|
||||
finish()
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
package eu.kanade.tachiyomi.extension.ru.remanga.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class TagsDto(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BranchesDto(
|
||||
val id: Long,
|
||||
val count_chapters: Int,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ImgDto(
|
||||
val high: String? = null,
|
||||
val mid: String? = null,
|
||||
val low: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LibraryDto(
|
||||
val id: Long,
|
||||
val en_name: String,
|
||||
val rus_name: String,
|
||||
val dir: String,
|
||||
val img: ImgDto,
|
||||
val bookmark_type: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MyLibraryDto(
|
||||
val title: LibraryDto,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class StatusDto(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangaDetDto(
|
||||
val id: Long,
|
||||
val en_name: String,
|
||||
val rus_name: String,
|
||||
val another_name: String,
|
||||
val dir: String,
|
||||
val description: String?,
|
||||
val issue_year: Int?,
|
||||
val img: ImgDto,
|
||||
val type: TagsDto,
|
||||
val genres: List<TagsDto>,
|
||||
val categories: List<TagsDto>,
|
||||
val branches: List<BranchesDto>,
|
||||
val status: StatusDto,
|
||||
val avg_rating: String,
|
||||
val count_rating: Int,
|
||||
val age_limit: Int,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PropsDto(
|
||||
val total_pages: Int? = 0,
|
||||
val page: Int,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PageWrapperDto<T>(
|
||||
val content: List<T>,
|
||||
val props: PropsDto,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SeriesWrapperDto<T>(
|
||||
val content: T,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PublisherDto(
|
||||
val name: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BookDto(
|
||||
val id: Long,
|
||||
val tome: Int,
|
||||
val chapter: String,
|
||||
val name: String,
|
||||
val upload_date: String,
|
||||
val is_paid: Boolean,
|
||||
val is_bought: Boolean?,
|
||||
val publishers: List<PublisherDto>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ExWrapperDto<T>(
|
||||
val data: T,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ExBookDto(
|
||||
val id: Long,
|
||||
val tome: Int,
|
||||
val chapter: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ExLibraryDto(
|
||||
val id: Long,
|
||||
val dir: String,
|
||||
val name: String = "Без названия",
|
||||
val img: String?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PagesDto(
|
||||
val link: String,
|
||||
val height: Int,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PageDto(
|
||||
val pages: List<PagesDto>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ChunksPageDto(
|
||||
val pages: List<List<PagesDto>>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class UserDto(
|
||||
val id: Long,
|
||||
)
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Iqiyi'
|
||||
extClass = '.Iqiyi'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -27,7 +27,7 @@ class Iqiyi : ParsedHttpSource() {
|
|||
|
||||
// Popular
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/category/全部_0_9_$page/", headers)
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/category/全部_-1_-1_9_$page/", headers)
|
||||
override fun popularMangaNextPageSelector(): String = "div.mod-page > a.a1:contains(下一页)"
|
||||
override fun popularMangaSelector(): String = "ul.cartoon-hot-ul > li.cartoon-hot-list"
|
||||
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
|
||||
|
@ -38,7 +38,7 @@ class Iqiyi : ParsedHttpSource() {
|
|||
|
||||
// Latest
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/category/全部_0_4_$page/", headers)
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/category/全部_-1_-1_4_$page/", headers)
|
||||
override fun latestUpdatesNextPageSelector(): String = popularMangaNextPageSelector()
|
||||
override fun latestUpdatesSelector() = popularMangaSelector()
|
||||
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
|
||||
|
|