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>