Repos / pytaku / 4128c53b39
commit 4128c53b39c5319109e471843bc62bf98de55610
Author: Bùi Thành Nhân <hi@imnhan.com>
Date:   Sun Aug 2 12:18:01 2020 +0700

    persist title & chapter, got prev/next working
    
    TODO: expiry

diff --git a/.gitignore b/.gitignore
index 68e2f20..fd80dcd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,6 @@
 __pycache__
 *.pyc
 *.json
-*.sqlite3
+*.sqlite3*
 *.egg-info
 /dist/
diff --git a/src/mangoapi/__init__.py b/src/mangoapi/__init__.py
index e83784e..c41cf81 100644
--- a/src/mangoapi/__init__.py
+++ b/src/mangoapi/__init__.py
@@ -14,9 +14,9 @@ def _parse_chapter_number(string):
     count = len(nums)
     assert count == 1 or count == 2
     result = {"number": string}
-    result["major"] = int(nums[0])
+    result["num_major"] = int(nums[0])
     if count == 2:
-        result["minor"] = int(nums[1])
+        result["num_minor"] = int(nums[1])
     return result
 
 
@@ -35,13 +35,10 @@ def get_title(title_id):
     md_json = md_resp.json()
     assert md_json["status"] == "OK"
 
-    cover_url = md_json["manga"]["cover_url"]
-    cover = "https://mangadex.org" + cover_url[: cover_url.rfind("?")]
-
     title = {
+        "id": title_id,
         "name": md_json["manga"]["title"],
         "alt_names": md_json["manga"]["alt_names"],
-        "cover": cover,
         "descriptions": md_json["manga"]["description"].split("\r\n\r\n"),
         "chapters": [
             {
@@ -52,7 +49,7 @@ def get_title(title_id):
                 **_parse_chapter_number(chap["chapter"]),
             }
             for chap_id, chap in md_json["chapter"].items()
-            if chap["lang_code"] == "gb"
+            if chap["lang_code"] == "gb" and chap["group_name"] != "MangaPlus"
         ],
     }
     return title
diff --git a/src/pytaku/database/migrations/latest_schema.sql b/src/pytaku/database/migrations/latest_schema.sql
index 8755f7d..eac8301 100644
--- a/src/pytaku/database/migrations/latest_schema.sql
+++ b/src/pytaku/database/migrations/latest_schema.sql
@@ -3,6 +3,7 @@
 
 CREATE TABLE title (
     id text,
+    name text,
     site text,
     chapters text,
     alt_names text,
@@ -13,13 +14,14 @@ CREATE TABLE title (
 CREATE TABLE chapter (
     id text,
     title_id text,
+    site text,
     num_major integer,
     num_minor integer,
     name text,
     pages text,
     groups text,
 
-    foreign key (title_id) references title (id),
-    unique(id, title_id),
+    foreign key (title_id, site) references title (id, site),
+    unique(id, title_id, site),
     unique(num_major, num_minor, title_id)
 );
diff --git a/src/pytaku/database/migrations/m0001.sql b/src/pytaku/database/migrations/m0001.sql
index 045fa19..c8d0fbd 100644
--- a/src/pytaku/database/migrations/m0001.sql
+++ b/src/pytaku/database/migrations/m0001.sql
@@ -1,5 +1,6 @@
 create table title (
     id text,
+    name text,
     site text,
     chapters text,
     alt_names text,
@@ -11,13 +12,14 @@ create table title (
 create table chapter (
     id text,
     title_id text,
+    site text,
     num_major integer,
     num_minor integer,
     name text,
     pages text,
     groups text,
 
-    foreign key (title_id) references title (id),
-    unique(id, title_id),
+    foreign key (title_id, site) references title (id, site),
+    unique(id, title_id, site),
     unique(num_major, num_minor, title_id)
 );
diff --git a/src/pytaku/main.py b/src/pytaku/main.py
index 8eb9d23..0a23275 100644
--- a/src/pytaku/main.py
+++ b/src/pytaku/main.py
@@ -7,6 +7,13 @@
 from mangoapi import get_chapter, get_title, search_title
 
 from . import mangadex
+from .persistence import (
+    get_prev_next_chapters,
+    load_chapter,
+    load_title,
+    save_chapter,
+    save_title,
+)
 
 app = Flask(__name__)
 
@@ -16,18 +23,39 @@ def home_view():
     return render_template("home.html")
 
 
-@app.route("/title/mangadex/<int:title_id>")
+@app.route("/title/mangadex/<title_id>")
 def title_view(title_id):
-    title = get_title(title_id)
-    return render_template("title.html", id=title_id, **title)
-
-
-@app.route("/chapter/mangadex/<int:chapter_id>")
+    title = load_title(title_id)
+    if not title:
+        print("Getting title", title_id)
+        title = get_title(title_id)
+        print("Saving title", title_id, "to db")
+        save_title(title)
+    else:
+        print("Loading title", title_id, "from db")
+    return render_template("title.html", **title)
+
+
+@app.route("/chapter/mangadex/<chapter_id>")
 def chapter_view(chapter_id):
-    chapter = get_chapter(chapter_id)
+    chapter = load_chapter(chapter_id)
+    if not chapter:
+        print("Getting chapter", chapter_id)
+        chapter = get_chapter(chapter_id)
+        save_chapter(chapter)
+    else:
+        print("Loading chapter", chapter_id, "from db")
+
     chapter["pages"] = [
         url_for("proxy_view", b64_url=_encode_proxy_url(p)) for p in chapter["pages"]
     ]
+
+    # YIIIIKES
+    title = load_title(chapter["title_id"])
+    prev_chapter, next_chapter = get_prev_next_chapters(title, chapter)
+    chapter["prev_chapter"] = prev_chapter
+    chapter["next_chapter"] = next_chapter
+
     return render_template("chapter.html", **chapter)
 
 
@@ -46,8 +74,7 @@ def proxy_view(b64_url):
     """Fine I'll do it"""
     url = _decode_proxy_url(b64_url)
     if not _is_manga_img_url(url):
-        return "Nope"
-    print("Proxying", url)
+        return "Nope", 400
     md_resp = requests.get(url)
     resp = make_response(md_resp.content, md_resp.status_code)
     resp.headers.extend(**md_resp.headers)
diff --git a/src/pytaku/persistence.py b/src/pytaku/persistence.py
new file mode 100644
index 0000000..7bdc074
--- /dev/null
+++ b/src/pytaku/persistence.py
@@ -0,0 +1,147 @@
+import json
+
+from .database.common import get_conn
+
+
+def save_title(title):
+    conn = get_conn()
+    conn.cursor().execute(
+        """
+    INSERT INTO title (
+        id,
+        name,
+        site,
+        chapters,
+        alt_names,
+        descriptions
+    ) VALUES (
+        :id,
+        :name,
+        :site,
+        :chapters,
+        :alt_names,
+        :descriptions
+    );
+    """,
+        {
+            "id": title["id"],
+            "name": title["name"],
+            "site": "mangadex",
+            "chapters": json.dumps(title["chapters"]),
+            "alt_names": json.dumps(title["alt_names"]),
+            "descriptions": json.dumps(title["descriptions"]),
+        },
+    )
+
+
+def load_title(title_id):
+    conn = get_conn()
+    result = list(
+        conn.cursor().execute(
+            """
+    SELECT id, name, site, chapters, alt_names, descriptions
+    FROM title
+    WHERE id = ?;
+    """,
+            (title_id,),
+        )
+    )
+    if not result:
+        return None
+    elif len(result) > 1:
+        raise Exception(f"Found multiple results for title_id {title_id}!")
+    else:
+        title = result[0]
+        return {
+            "id": title[0],
+            "name": title[1],
+            "site": title[2],
+            "chapters": json.loads(title[3]),
+            "alt_names": json.loads(title[4]),
+            "descriptions": json.loads(title[5]),
+        }
+
+
+def save_chapter(chapter):
+    conn = get_conn()
+    conn.cursor().execute(
+        """
+    INSERT INTO chapter (
+        id,
+        title_id,
+        site,
+        num_major,
+        num_minor,
+        name,
+        pages,
+        groups
+    ) VALUES (
+        :id,
+        :title_id,
+        :site,
+        :num_major,
+        :num_minor,
+        :name,
+        :pages,
+        :groups
+    );
+    """,
+        {
+            "id": chapter["id"],
+            "title_id": chapter["title_id"],
+            "site": "mangadex",
+            "num_major": chapter["num_major"],
+            "num_minor": chapter.get("num_minor", None),
+            "name": chapter["name"],
+            "pages": json.dumps(chapter["pages"]),
+            "groups": json.dumps(chapter["groups"]),
+        },
+    )
+
+
+def load_chapter(chapter_id):
+    conn = get_conn()
+    result = list(
+        conn.cursor().execute(
+            """
+    SELECT id, title_id, num_major, num_minor, name, pages, groups
+    FROM chapter
+    WHERE id = ?;
+    """,
+            (chapter_id,),
+        )
+    )
+    if not result:
+        return None
+    elif len(result) > 1:
+        raise Exception(f"Found multiple results for chapter_id {chapter_id}!")
+    else:
+        chapter = result[0]
+        return {
+            "id": chapter[0],
+            "title_id": chapter[1],
+            "num_major": chapter[2],
+            "num_minor": chapter[3],
+            "name": chapter[4],
+            "pages": json.loads(chapter[5]),
+            "groups": json.loads(chapter[6]),
+        }
+
+
+def get_prev_next_chapters(title, chapter):
+    """
+    Maybe consider writing SQL query instead?
+    """
+    chapters = title["chapters"]
+    chapter_id = chapter["id"]
+
+    prev_chapter = None
+    next_chapter = None
+    for i, chap in enumerate(chapters):
+        if chap["id"] == chapter_id:
+            if i - 1 >= 0:
+                next_chapter = chapters[i - 1]
+            if i + 1 < len(chapters):
+                prev_chapter = chapters[i + 1]
+
+    return prev_chapter, next_chapter
diff --git a/src/pytaku/static/base.css b/src/pytaku/static/base.css
index c694ac3..e366545 100644
--- a/src/pytaku/static/base.css
+++ b/src/pytaku/static/base.css
@@ -85,6 +85,15 @@ .button.blue {
   background-color: var(--btn-blue);
   border-bottom: 4px solid var(--btn-blue-bottom);
 }
+.button.disabled,
+.button.disabled:hover,
+.button.disabled:active {
+  background-color: #aaa;
+  border-bottom: 4px solid #aaa;
+  filter: none;
+  cursor: default;
+  opacity: 0.5;
+}
 .button > img {
   height: 1rem;
 }
diff --git a/src/pytaku/static/icons/arrow-up-right.svg b/src/pytaku/static/icons/arrow-up-right.svg
new file mode 100644
index 0000000..b61b220
--- /dev/null
+++ b/src/pytaku/static/icons/arrow-up-right.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-up-right"><line x1="7" y1="17" x2="17" y2="7"></line><polyline points="7 7 17 7 17 17"></polyline></svg>
\ No newline at end of file
diff --git a/src/pytaku/static/icons/list.svg b/src/pytaku/static/icons/list.svg
new file mode 100644
index 0000000..1c2ecba
--- /dev/null
+++ b/src/pytaku/static/icons/list.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-list"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>
\ No newline at end of file
diff --git a/src/pytaku/templates/base.html b/src/pytaku/templates/base.html
index 94e7a47..022fefc 100644
--- a/src/pytaku/templates/base.html
+++ b/src/pytaku/templates/base.html
@@ -1,9 +1,11 @@
 {# vim: ft=htmldjango
 #}
 
-{% macro ibutton(href='', left_icon='', right_icon='', text='', color='red') -%}
+{% macro ibutton(href='', left_icon='', right_icon='', text='', color='red', disabled=False) -%}
 {% set element = 'a' if href else 'button' %}
-<{{ element }} class="{{ color }} button" {% if href %}href="{{ href }}"{% endif %}>
+<{{ element }} class="{{ color }} button {% if disabled %}disabled{% endif %}"
+        {% if href %}href="{{ href }}"{% endif %}
+        {% if disabled %}disabled{% endif %} >
   {% if left_icon %}
   <img src="{{ url_for('static', filename='icons/' + left_icon + '.svg')}}" alt="{{ text }} icon" />
   {% endif %}
diff --git a/src/pytaku/templates/chapter.html b/src/pytaku/templates/chapter.html
index aa11159..3df7cc2 100644
--- a/src/pytaku/templates/chapter.html
+++ b/src/pytaku/templates/chapter.html
@@ -1,7 +1,9 @@
 {% extends 'base.html' %}
 
 {% block title %}
-Ch.{{ number }} - {{ name }}
+Ch. {{ num_major }}
+{% if num_minor %}.{{ num_minor }}{% endif %}
+{% if name %} - {{ name }}{% endif %}
 {% endblock %}
 
 {% block head %}
@@ -45,14 +47,24 @@
 
 {% block content %}
 
-<h1>Ch.{{ number }}: {{ name }}</h1>
+<h1>{{ self.title() }}</h1>
 
 {# Put buttons in block to reuse later in this same template #}
 {% block buttons %}
 <div class="buttons">
-{{ ibutton(href='#TODO', left_icon='chevrons-left', text='Previous') }}
-{{ ibutton(left_icon='eye', text='Reading', color='green') }}
-{{ ibutton(href='#TODO', right_icon='chevrons-right', text='Next') }}
+  {% if prev_chapter %}
+  {{ ibutton(href=url_for('chapter_view', chapter_id=prev_chapter['id']), left_icon='chevrons-left', text='Prev') }}
+  {% else %}
+  {{ ibutton(left_icon='chevrons-left', text='Prev', disabled=True) }}
+  {% endif %}
+
+  {{ ibutton(href=url_for('title_view', title_id=title_id), left_icon='list', text='Chapter list', color='blue') }}
+
+  {% if next_chapter %}
+  {{ ibutton(href=url_for('chapter_view', chapter_id=next_chapter['id']), right_icon='chevrons-right', text='Next') }}
+  {% else %}
+  {{ ibutton(right_icon='chevrons-right', text='Next', disabled=True) }}
+  {% endif %}
 </div>
 {% endblock %}
 
diff --git a/src/pytaku/templates/title.html b/src/pytaku/templates/title.html
index 700f6e2..ad63a95 100644
--- a/src/pytaku/templates/title.html
+++ b/src/pytaku/templates/title.html
@@ -15,13 +15,12 @@
 
 {% block content %}
 
+<div>
 <h1>{{ name }}</h1>
+{{ ibutton(href='https://mangadex.org/manga/' + id, right_icon='arrow-up-right', text='Source site', color='blue') }}
+</div>
 
-{{ ibutton(left_icon='bookmark', text='Bookmark', color='blue') }}
-
-<a href="https://mangadex.org/manga/{{ id }}">Original link</a>
-
-<img class="cover" src="{{ cover }}" alt="cover" />
+<img class="cover" src="https://mangadex.org/images/manga/{{ id }}.jpg" alt="cover" />
 
 <table>
   <tr>
@@ -35,7 +34,6 @@ <h1>{{ name }}</h1>
         Chapter {{ chapter['number'] }}
         {% if chapter['volume'] %}Volume {{ chapter['volume'] }} {% endif %}
         {% if chapter['name'] %}- {{ chapter['name'] }} {% endif %}
-
       </a>
     </td>
     <td>{{ ', '.join(chapter['groups']) }}</td>