-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathweb.py
173 lines (135 loc) · 5.53 KB
/
web.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
from PIL import Image
from io import BytesIO
import base64
from common_fetch_strategy import FetchData, ResponseType, Strategy
from models import Series
from settings import TOKEN, TV_DB_API
from utils import createElement
from datetime import datetime
from pyweb import pydom
from pyscript import when
async def search_series(query: str, fetch_strategy: Strategy) -> list[Series]:
url = f"{TV_DB_API}/search?query={query}&type=series"
headers = {"Authorization": f"Bearer {TOKEN}", "accept": "application/json"}
data = FetchData(url, headers, fetch_strategy)
content = await data.fetch()
if not content["data"]:
return []
return [
Series(
id=s.get("id", ""),
name=s.get("name", ""),
status=s.get("status", ""),
tvdb_id=s.get("tvdb_id", ""),
thumbnail=s.get("thumbnail", ""),
)
for s in content["data"]
]
def reverse_date(date: str) -> str:
return "-".join(date.split("-")[::-1])
def delta_days(date: str) -> int:
return (datetime.now() - datetime.strptime(date, "%d-%m-%Y")).days
async def render_search_series(e):
from pyodide_fetch_strategy import PyodideStrategy
pyodide_strategy = PyodideStrategy()
query = pydom['#search-input'][0].value
if not query:
return
series: list[Series] = await search_series(query, fetch_strategy=pyodide_strategy)
series_table_body = pydom["#series"][0]
if len(series_table_body.children) > 0:
series_table_body.html = ""
continuing, upcoming, ended = 0, 0, 0
for s in series:
if s.status == "Continuing":
continuing += 1
elif s.status == "Ended":
ended += 1
elif s.status == "Upcoming":
upcoming += 1
pydom["#total-results-count"][0].html = f"{len(series)}"
pydom["#total-continuing-count"][0].html = f"{continuing}"
pydom["#total-upcoming-count"][0].html = f"{upcoming}"
pydom["#total-ended-count"][0].html = f"{ended}"
pydom["#total-results-count"][0].parent._js.classList.remove("d-none")
pydom["#spinner"][0]._js.classList.remove("d-none")
for s in series:
new_row = createElement("tr")
title_cell = createElement("th", f"{s.name}", scope="row")
new_row.append(title_cell)
pyodide_strategy.response_type = ResponseType.BYTES
thumb = await generate_thumbnail(s.thumbnail, fetch_strategy=pyodide_strategy)
cover_cell = createElement(
"td",
inner_html=f'<img src="{thumb}" class="img-fluid" />'
if thumb
else "No thumbnail available",
)
new_row.append(cover_cell)
status_cell = createElement("td", text_content=f"{s.status}")
new_row.append(status_cell)
pyodide_strategy.response_type = ResponseType.JSON
aired_data = await get_aired_data(s, fetch_strategy=pyodide_strategy)
next_aired = aired_data["next_aired"]
eye_icon = "<i class='bi bi-eye-fill'></i>"
if next_aired:
reversed_next_aired = reverse_date(next_aired)
dd = delta_days(reversed_next_aired)
if dd < 0:
cell_html = f"{eye_icon} in {abs(dd)} days ({reversed_next_aired})"
else:
cell_html = f"{reversed_next_aired}"
next_episode_cell = createElement(
"td",
inner_html=cell_html,
)
next_episode_cell._js.classList.add("text-success")
else:
text_content = f"No upcoming episodes {'yet' if s.status in ['Continuing', 'Upcoming'] else ''}"
next_episode_cell = createElement("td", text_content=text_content)
new_row.append(next_episode_cell)
last_aired = aired_data["last_aired"]
last_episode_cell = createElement(
"td", text_content=f"{reverse_date(last_aired) if last_aired else 'N/A'}"
)
new_row.append(last_episode_cell)
if s.status == "Continuing":
new_row._js.classList.add("table-success")
elif s.status == "Ended":
new_row._js.classList.add("table-danger")
elif s.status == "Upcoming":
new_row._js.classList.add("table-info")
series_table_body.append(new_row)
pydom["#spinner"][0]._js.classList.add("d-none")
async def get_aired_data(s: Series, fetch_strategy: Strategy) -> dict[str, str]:
url = f"{TV_DB_API}/series/{s.tvdb_id}"
headers = {"Authorization": f"Bearer {TOKEN}", "accept": "application/json"}
data = FetchData(url, headers, strategy=fetch_strategy)
content = await data.fetch()
next_aired = content["data"]["nextAired"]
last_aired = content["data"]["lastAired"]
return {
"next_aired": next_aired if next_aired else None,
"last_aired": last_aired if last_aired else None,
}
async def generate_thumbnail(
thumbnail_url: str, fetch_strategy: Strategy
) -> str | None:
try:
data = FetchData(thumbnail_url, {}, strategy=fetch_strategy)
content = await data.fetch()
image = Image.open(BytesIO(content))
image.thumbnail((100, 100))
buffered = BytesIO()
image.save(buffered, format="JPEG")
return (
f"data:image/jpeg;base64,{base64.b64encode(buffered.getvalue()).decode()}"
)
except Exception:
from pyscript import window
window.console.log("Error generating thumbnail")
return None
@when('keypress', '#search-input')
async def keyhandle(evt):
if evt.key == "Enter":
await render_search_series(evt)