From bdbf6b363c67992a82ea54fdd54474eaad39a3d8 Mon Sep 17 00:00:00 2001
From: dec05eba <dec05eba@protonmail.com>
Date: Sun, 12 Apr 2020 17:39:31 +0200
Subject: Add support for mangadex

---
 README.md                |   2 +-
 plugins/lhtranslation.py |   2 +-
 plugins/mangadex.py      | 155 +++++++++++++++++++++++++++++++++++++++++++++++
 plugins/manganelo.py     |   2 +-
 plugins/mangaplus.py     |   2 +-
 plugins/mangawindow.py   |   2 +-
 6 files changed, 160 insertions(+), 5 deletions(-)
 create mode 100755 plugins/mangadex.py

diff --git a/README.md b/README.md
index 85022ba..23da4d4 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 # AutoMedia
-Automatically track new releases of media and download them. Currently works with rss for torrent sites (`nyaa.si`) and a for these manga sites: `manganelo.com`, `mangakakalot.com`, `lhtranslation.net`, `mangawindow.net` and `mangaplus.shueisha.co.jp`.
+Automatically track new releases of media and download them. Currently works with rss for torrent sites (`nyaa.si`) and a for these manga sites: `manganelo.com`, `mangakakalot.com`, `lhtranslation.net`, `mangawindow.net`, `mangaplus.shueisha.co.jp` and `mangadex.org`.
 A notification is shown on the screen when a download finishes (if notify-send is installed).
 ## Usage
 Run automedia with `sync` option and keep it running to track media. You can then use `add` option to add new media to track.
diff --git a/plugins/lhtranslation.py b/plugins/lhtranslation.py
index f23806d..c86401e 100755
--- a/plugins/lhtranslation.py
+++ b/plugins/lhtranslation.py
@@ -69,7 +69,7 @@ def list_chapters(url, chapter_list_input):
 def download_chapter(url, download_dir):
     response = requests.get(url)
     if response.status_code != 200:
-        print("Failed to list chapters, server responded with status code %d" % response.status_code)
+        print("Failed to list chapter images, server responded with status code %d" % response.status_code)
         exit(2)
 
     in_progress_filepath = os.path.join(download_dir, ".in_progress")
diff --git a/plugins/mangadex.py b/plugins/mangadex.py
new file mode 100755
index 0000000..a1e004b
--- /dev/null
+++ b/plugins/mangadex.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python3
+
+import os
+import time
+import sys
+import requests
+import json
+import re
+
+from lxml import etree
+
+def usage():
+    print("mangadex.py command")
+    print("commands:")
+    print("  download")
+    print("  list")
+    exit(1)
+
+def usage_list():
+    print("mangadex.py list <url>")
+    exit(1)
+
+def usage_download():
+    print("mangadex.py download <url> <download_dir>")
+    print("examples:")
+    print("  mangadex.py download \"https://mangadex.org/title/7139/one-punch-man\" /home/adam/Manga/MangaName")
+    print("")
+    print("Note: The manga directory has to exist.")
+    exit(1)
+
+if len(sys.argv) < 2:
+    usage()
+
+def download_file(url, save_path):
+    with requests.get(url, stream=True) as response:
+        response.raise_for_status()
+        with open(save_path, "wb") as file:
+            for chunk in response.iter_content(chunk_size=8192):
+                if chunk:
+                    file.write(chunk)
+
+def title_url_extract_manga_id(url):
+    result = re.search("mangadex.org/title/([0-9]+)/", url)
+    if result and len(result.groups()) > 0:
+        return result.groups()[0]
+
+# TODO: Support pagination. Currently only the n latest chapters are listed,
+# but going through all pages might be too slow for large manga like naruto
+def list_chapters(url, chapter_list_input):
+    manga_id = title_url_extract_manga_id(url)
+    if not manga_id:
+        print("Failed to extract manga id from url: %s. Note: url is expected to be in this format: mangadex.org/title/<number>/..." % url)
+        exit(2)
+
+    response = requests.get(url)
+    if response.status_code != 200:
+        print("Failed to list chapters, server responded with status code %d" % response.status_code)
+        exit(2)
+
+    seen_titles = set()
+    for item in chapter_list_input:
+        title = item.get("title")
+        if title and len(title) > 0:
+            seen_titles.add(title.lower().replace(" ", ""))
+
+    seen_urls = set()
+    for item in chapter_list_input:
+        url = item.get("url")
+        if url and len(url) > 0:
+            seen_urls.add(url)
+
+    lang = "1" # english
+
+    tree = etree.HTML(response.text)
+    chapters = []
+    for element in tree.xpath("//div[@data-manga-id='%s']" % manga_id):
+        chapter_lang = element.attrib.get("data-lang")
+        if chapter_lang != lang:
+            continue
+        chapter_id = element.attrib.get("data-id")
+        chapter_url = "https://mangadex.org/chapter/%s" % chapter_id
+        chapter_title = element.attrib.get("data-title") # optional
+        chapter_number = element.attrib.get("data-chapter")
+        chapter_name = "Ch. %s" % chapter_number
+        if chapter_title:
+            chapter_name += " - %s" % chapter_title
+        if chapter_title.lower().replace(" ", "") in seen_titles or chapter_url in seen_urls:
+            break
+        chapters.append({ "name": chapter_name, "url": chapter_url })
+    print(json.dumps(chapters))
+
+def chapter_url_extract_manga_id(url):
+    result = re.search("mangadex.org/chapter/([0-9]+)", url)
+    if result and len(result.groups()) > 0:
+        return result.groups()[0]
+
+def download_chapter(url, download_dir):
+    manga_id = chapter_url_extract_manga_id(url)
+    if not manga_id:
+        print("Failed to extract manga id from url: %s. Note: url is expected to be in this format: mangadex.org/chapter/<number>" % url)
+        exit(2)
+
+    url = "https://mangadex.org/api/?id=%s&server=null&type=chapter" % manga_id
+
+    response = requests.get(url)
+    if response.status_code != 200:
+        print("Failed to list chapter images, server responded with status code %d" % response.status_code)
+        exit(2)
+
+    in_progress_filepath = os.path.join(download_dir, ".in_progress")
+    with open(in_progress_filepath, "w") as file:
+        file.write(url)
+
+    img_number = 1
+    json_response = response.json()
+    status = json_response["status"]
+    if status != "OK":
+        print("Expected server response OK, got %s" % status)
+        exit(2)
+
+    chapter_hash = json_response["hash"]
+    for image_name in json_response["page_array"]:
+        image_url = "https://mangadex.org/data/%s/%s" % (chapter_hash, image_name)
+        ext = image_url[image_url.rfind("."):]
+        image_name = str(img_number) + ext
+        image_path = os.path.join(download_dir, image_name)
+        print("Downloading {} to {}".format(image_url, image_path))
+        download_file(image_url, image_path)
+        img_number += 1
+
+    with open(os.path.join(download_dir, ".finished"), "w") as file:
+        file.write("1")
+
+    os.remove(in_progress_filepath)
+
+command = sys.argv[1]
+if command == "list":
+    if len(sys.argv) < 3:
+        usage_list()
+    
+    url = sys.argv[2]
+    chapter_list_input = sys.stdin.read()
+    if len(chapter_list_input) == 0:
+        chapter_list_input = []
+    else:
+        chapter_list_input = json.loads(chapter_list_input)
+    list_chapters(url, chapter_list_input)
+elif command == "download":
+    if len(sys.argv) < 4:
+        usage_download()
+    url = sys.argv[2]
+    download_dir = sys.argv[3]
+    download_chapter(url, download_dir)
+else:
+    usage()
diff --git a/plugins/manganelo.py b/plugins/manganelo.py
index 57ad6f5..352bb23 100755
--- a/plugins/manganelo.py
+++ b/plugins/manganelo.py
@@ -72,7 +72,7 @@ def list_chapters(url, chapter_list_input):
 def download_chapter(url, download_dir):
     response = requests.get(url)
     if response.status_code != 200:
-        print("Failed to list chapters, server responded with status code %d" % response.status_code)
+        print("Failed to list chapter images, server responded with status code %d" % response.status_code)
         exit(2)
 
     in_progress_filepath = os.path.join(download_dir, ".in_progress")
diff --git a/plugins/mangaplus.py b/plugins/mangaplus.py
index 75ba118..3658770 100755
--- a/plugins/mangaplus.py
+++ b/plugins/mangaplus.py
@@ -262,7 +262,7 @@ def download_chapter(url, download_dir):
 
     response = requests.get(url, headers=headers)
     if response.status_code != 200:
-        print("Failed to list chapters, server responded with status code %d" % response.status_code)
+        print("Failed to list chapter images, server responded with status code %d" % response.status_code)
         exit(2)
 
     in_progress_filepath = os.path.join(download_dir, ".in_progress")
diff --git a/plugins/mangawindow.py b/plugins/mangawindow.py
index d55e68f..10f0f88 100755
--- a/plugins/mangawindow.py
+++ b/plugins/mangawindow.py
@@ -74,7 +74,7 @@ def list_chapters(url, chapter_list_input):
 def download_chapter(url, download_dir):
     response = requests.get(url)
     if response.status_code != 200:
-        print("Failed to list chapters, server responded with status code %d" % response.status_code)
+        print("Failed to list chapter images, server responded with status code %d" % response.status_code)
         exit(2)
 
     in_progress_filepath = os.path.join(download_dir, ".in_progress")
-- 
cgit v1.2.3-70-g09d2