From 49871c1d6eedfee8b8d689a334b9a1320320e327 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Sun, 11 May 2025 13:49:05 +0200 Subject: [PATCH] add latest updates --- src/app.py | 131 +++++++++++++++++++++++++++--------- src/static/style.css | 66 ++++++++++++++++-- src/templates/index.html | 46 +++---------- src/templates/requests.html | 11 ++- 4 files changed, 176 insertions(+), 78 deletions(-) diff --git a/src/app.py b/src/app.py index 51efd62..d58b301 100644 --- a/src/app.py +++ b/src/app.py @@ -1,11 +1,17 @@ from quart import Quart, render_template, request, jsonify import httpx -from anilistapi.queries.manga import REQUESTS_QUERY, TAGS_QUERY, GENRES_QUERY +from anilistapi.queries.manga import ( + REQUESTS_QUERY, + TAGS_QUERY, + GENRES_QUERY, + REQUESTED_QUERY, +) from anilistapi.schemas.manga import Manga from komconfig import KomConfig from komcache import KomCache from komgapi import komgapi as KOMGAPI from typing import Any, Dict, List +from limit import limit app = Quart(__name__) @@ -13,9 +19,6 @@ cache = KomCache() cache.create_table( "CREATE TABLE IF NOT EXISTS manga_requests (id INTEGER PRIMARY KEY, manga_id INTEGER, grabbed BOOLEAN DEFAULT 0)" ) -cache.create_table( - "CREATE TABLE IF NOT EXISTS manga_titles (id INTEGER PRIMARY KEY, anilist_id INTEGER DEFAULT 0, komga_title UNIQUE)" -) settings = KomConfig() @@ -25,35 +28,20 @@ komga = KOMGAPI( password=settings.komga.password, ) komga_series = komga.seriesList() -# store the entries in the database table manga_titles, komga_series is a list of strings of the series names -for series in komga_series: - # check if the series is already in the database - existing_series = cache.fetch_one( - query="SELECT komga_title FROM manga_titles WHERE komga_title = ?", - args=(series,), - ) - if existing_series: - # series already exists, skip - continue - else: - cache.insert( - # insert into if not in database - query="INSERT OR IGNORE INTO manga_titles (komga_title) VALUES (?)", - args=(series,), - ) -komga_series = [series.lower() for series in komga_series] -# Update type annotations for fetch_data -async def fetch_data(data: Dict[str, Any]) -> List[Dict[str, Any]]: +@limit(90, 60) +async def fetch_data( + data: Dict[str, Any], query: str = REQUESTS_QUERY +) -> List[Dict[str, Any]]: async with httpx.AsyncClient() as client: try: - variables: Dict[str, Any] = {"search": data["query"]} - if len(data["genres"]) > 0: + variables: Dict[str, Any] = {"search": data["query"], "type": "MANGA"} + if data.get("genres"): variables["genres"] = data["genres"] - if len(data["tags"]) > 0: + if data.get("tags"): variables["tags"] = data["tags"] - print(data["query"], variables) + # print(data["query"], variables) response = await client.post( "https://graphql.anilist.co", json={ @@ -62,6 +50,7 @@ async def fetch_data(data: Dict[str, Any]) -> List[Dict[str, Any]]: }, ) response.raise_for_status() + data = response.json() results: List[Dict[str, Any]] = [] @@ -74,9 +63,7 @@ async def fetch_data(data: Dict[str, Any]) -> List[Dict[str, Any]]: query="SELECT manga_id, grabbed FROM manga_requests WHERE manga_id = ?", args=(manga.id,), ) - komga_request = False - if requested: - komga_request = True + komga_request = bool(requested) results.append( { @@ -109,6 +96,73 @@ async def fetch_data(data: Dict[str, Any]) -> List[Dict[str, Any]]: return [] +async def fetch_requested_data(data: list[int]) -> List[Dict[str, Any]]: + requested_data: list[dict[str, Any]] = [] + for manga_id in data: + async with httpx.AsyncClient() as client: + try: + variables: Dict[str, Any] = { + "search": manga_id, + "type": "MANGA", + } # TODO: replace the type once the UI has been updated + # print(data, variables) + response = await client.post( + "https://graphql.anilist.co", + json={ + "query": REQUESTED_QUERY, + "variables": variables, + }, + ) + response.raise_for_status() + data = response.json() + + # print(data) + entry = data.get("data", {}).get("Media", {}) + if entry: + manga = Manga(**entry) + + in_komga = komga.getSeries( + manga.title.english + if manga.title.english + else manga.title.romaji + ) + requested = cache.fetch_one( + query="SELECT manga_id, grabbed FROM manga_requests WHERE manga_id = ?", + args=(manga.id,), + ) + komga_request = bool(requested) + + requested_data.append( + { + "id": manga.id, + "title": manga.title.english + if manga.title.english + else manga.title.romaji, + "image": manga.coverImage.get("large") + if manga.coverImage + else "https://demofree.sirv.com/nope-not-here.jpg", + "status": manga.status, + "type": manga.format, + "genres": manga.genres or [], + "tags": [tag.name for tag in (manga.tags or [])], + "description": manga.description.replace("
", "\n") + if manga.description + else "No description available", + "isAdult": manga.isAdult, + "in_komga": in_komga, + "requested": komga_request, + } + ) + + except httpx.RequestError as e: + print(f"An error occurred while requesting data: {e}") + requested_data.append({"id": manga_id}) + except Exception as e: + print(f"Unexpected error: {e}") + requested_data.append({"id": manga_id}) + return requested_data + + @app.route("/api/genres") async def get_genres(): async with httpx.AsyncClient() as client: @@ -147,6 +201,7 @@ async def get_tags(): @app.route("/search", methods=["POST"]) async def search(): data = await request.get_json() + print(data) results = await fetch_data(data) if not results: return jsonify({"error": "No results found"}), 404 @@ -172,5 +227,21 @@ async def log_request(): return jsonify({"status": "failed"}), 400 +@app.route("/requests", methods=["GET"]) +async def requests_page(): + requests = ( + cache.fetch_all(query="SELECT manga_id, grabbed FROM manga_requests") or [] + ) + entries: List[Dict[str, Any]] = [] + req_ids = [req[0] for req in requests] + if req_ids: + entries = await fetch_requested_data(req_ids) + else: + entries = [] + print(entries) + + return await render_template("requests.html", requests=entries) + + if __name__ == "__main__": app.run(debug=True, host="0.0.0.0", port=5001) diff --git a/src/static/style.css b/src/static/style.css index 7ce6d90..cc986aa 100644 --- a/src/static/style.css +++ b/src/static/style.css @@ -1,8 +1,9 @@ +/* other styles are in magic.css */ /* ========== GRID LAYOUT ========== */ .results { display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 20px; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 1em; margin-top: 20px; } @@ -20,11 +21,16 @@ border: 1px solid #ccc; background: #fafafa; text-align: center; - transition: box-shadow 0.2s; + transition: box-shadow 0.2s transform 0.2s; + border-radius: 10px; } .card:hover { box-shadow: 0 0 10px #aaa; + transform: scale(1.1); + /* pop out by 10% */ + z-index: 2; + /* ensure it appears above others */ } .card img { @@ -39,6 +45,32 @@ justify-content: space-around; } +.actions .info { + background-color: #007bff; + color: white; + border: none; + padding: 0.5em 1em; + cursor: pointer; + border-radius: 5px; + margin-right: 0.5em; +} + +.actions .info:hover { + background-color: #0056b3; +} + +.actions .request { + background-color: #28a745; + color: white; + border: none; + padding: 0.5em 1em; + cursor: pointer; + border-radius: 5px; +} + +.actions .request:hover { + background-color: #1e7e34; +} /* ========== MODAL ========== */ .modal { position: fixed; @@ -84,12 +116,15 @@ /* ========== BLUR CONTROL VIA BODY CLASS ========== */ body.nsfw-disabled .image-container.nsfw img { - filter: blur(8px); + filter: grayscale(100%) blur(5px) !important; + /* Ensure the blur is applied */ + transition: filter 0.3s ease; + /* Add smooth transition */ } body.nsfw-disabled .image-container.nsfw:hover img { - filter: none; - /* Remove blur on hover, set as important */ + filter: none !important; + /* Remove blur on hover */ } @@ -145,10 +180,23 @@ body.nsfw-disabled .image-container.nsfw:hover img { .reset { /* Implement design here */ + background-color: #f44336; + /* round the edges of the button */ + border-radius: 9px; + + } + + .selectors { + display: flex; + gap: 10px; + margin-bottom: 8px; + margin-top: 5px; + flex-wrap: wrap; } .info { /* Implement design here */ + background-color: #4CAF50; } .request { @@ -183,4 +231,10 @@ body.nsfw-disabled .image-container.nsfw:hover img { .card.komga .request { pointer-events: none; opacity: 0.5; +} +.card.komga:hover, +.card.requested:hover { + transform: none !important; + box-shadow: none !important; + z-index: auto !important; } \ No newline at end of file diff --git a/src/templates/index.html b/src/templates/index.html index b850101..6b66161 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -4,7 +4,6 @@ Anime Search and Request Page - @@ -45,17 +44,15 @@ - + - {% if results %} - +
+ {% if results %} {% for result in results %} -
- +
Cover @@ -74,38 +71,11 @@
{% endfor %} + {% endif %}
- {% endif %} - {% if results %} -
- {% for result in results %} - -
- -
- - Cover - {% if result.isAdult %} -
18+
- {% endif %} -
- -

{{ result.title }}

-
- - - {% if not result.in_komga %} - - {% endif %} -
-
- {% endfor %} -
- {% endif %} - {% endif %} {% if requests %}