Skip to content

Commit 71ad5b9

Browse files
committed
Add timeout to NUS downloads, handle with custom exception
This means that downloads won't run forever if your internet dies. If a connection error occurs during download (like a timeout or anything else), all NUS functions now return a NUSConnectionError
1 parent 3743587 commit 71ad5b9

1 file changed

Lines changed: 31 additions & 18 deletions

File tree

src/libWiiPy/title/nus.py

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
_nus_endpoint = ["http://nus.cdn.shop.wii.com/ccs/download/", "http://ccs.cdn.wup.shop.nintendo.net/ccs/download/"]
1515

1616

17+
class NUSConnectionError(Exception):
18+
pass
19+
20+
1721
class DownloadCallback(Protocol):
1822
"""
1923
The format of a callable passed to a NUS download function.
@@ -126,13 +130,13 @@ def download_tmd(title_id: str, title_version: int | None = None, wiiu_endpoint:
126130
tmd_url += "." + str(title_version)
127131
# Make the request.
128132
try:
129-
response = requests.get(url=tmd_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True)
133+
response = requests.get(url=tmd_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True, timeout=5)
130134
except requests.exceptions.ConnectionError:
131135
if endpoint_override:
132136
raise ValueError("A connection could not be made to the NUS endpoint. Please make sure that your endpoint "
133137
"override is valid.")
134138
else:
135-
raise Exception("A connection could not be made to the NUS endpoint. The NUS may be unavailable.")
139+
raise NUSConnectionError("A connection could not be made to the NUS endpoint. The NUS may be unavailable.")
136140
# Handle a 404 if the TID/version doesn't exist.
137141
if response.status_code == 404:
138142
raise ValueError("The requested Title ID or TMD version does not exist. Please check the Title ID and Title"
@@ -144,9 +148,12 @@ def download_tmd(title_id: str, title_version: int | None = None, wiiu_endpoint:
144148
progress(0, total_size)
145149
# Stream the TMD's data in chunks so that we can post updates to the callback function (assuming one was supplied).
146150
raw_tmd = b""
147-
for chunk in response.iter_content(512):
148-
raw_tmd += chunk
149-
progress(len(raw_tmd), total_size)
151+
try:
152+
for chunk in response.iter_content(512):
153+
raw_tmd += chunk
154+
progress(len(raw_tmd), total_size)
155+
except requests.exceptions.ConnectionError:
156+
raise NUSConnectionError("A connection could not be made to the NUS endpoint. The NUS may be unavailable.")
150157
# Use a TMD object to load the data and then return only the actual TMD.
151158
tmd_temp = TMD()
152159
tmd_temp.load(raw_tmd)
@@ -194,13 +201,13 @@ def download_ticket(title_id: str, wiiu_endpoint: bool = False, endpoint_overrid
194201
ticket_url = endpoint_url + title_id + "/cetk"
195202
# Make the request.
196203
try:
197-
response = requests.get(url=ticket_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True)
204+
response = requests.get(url=ticket_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True, timeout=5)
198205
except requests.exceptions.ConnectionError:
199206
if endpoint_override:
200207
raise ValueError("A connection could not be made to the NUS endpoint. Please make sure that your endpoint "
201208
"override is valid.")
202209
else:
203-
raise Exception("A connection could not be made to the NUS endpoint. The NUS may be unavailable.")
210+
raise NUSConnectionError("A connection could not be made to the NUS endpoint. The NUS may be unavailable.")
204211
if response.status_code == 404:
205212
raise ValueError("The requested Title ID does not exist, or refers to a non-free title. Tickets can only"
206213
" be downloaded for titles that are free on the NUS.")
@@ -211,9 +218,12 @@ def download_ticket(title_id: str, wiiu_endpoint: bool = False, endpoint_overrid
211218
progress(0, total_size)
212219
# Stream the Ticket's data just like with the TMD.
213220
cetk = b""
214-
for chunk in response.iter_content(chunk_size=1024):
215-
cetk += chunk
216-
progress(len(cetk), total_size)
221+
try:
222+
for chunk in response.iter_content(chunk_size=1024):
223+
cetk += chunk
224+
progress(len(cetk), total_size)
225+
except requests.exceptions.ConnectionError:
226+
raise NUSConnectionError("A connection could not be made to the NUS endpoint. The NUS may be unavailable.")
217227
# Use a Ticket object to load only the Ticket data from cetk and return it.
218228
ticket_temp = Ticket()
219229
ticket_temp.load(cetk)
@@ -249,14 +259,14 @@ def download_cert_chain(wiiu_endpoint: bool = False, endpoint_override: str | No
249259
tmd_url = endpoint_url + "0000000100000002/tmd.513"
250260
cetk_url = endpoint_url + "0000000100000002/cetk"
251261
try:
252-
tmd = requests.get(url=tmd_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True).content
253-
cetk = requests.get(url=cetk_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True).content
262+
tmd = requests.get(url=tmd_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True, timeout=5).content
263+
cetk = requests.get(url=cetk_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True, timeout=5).content
254264
except requests.exceptions.ConnectionError:
255265
if endpoint_override:
256266
raise ValueError("A connection could not be made to the NUS endpoint. Please make sure that your endpoint "
257267
"override is valid.")
258268
else:
259-
raise Exception("A connection could not be made to the NUS endpoint. The NUS may be unavailable.")
269+
raise NUSConnectionError("A connection could not be made to the NUS endpoint. The NUS may be unavailable.")
260270
# Assemble the certificate chain.
261271
cert_chain = b''
262272
# Certificate Authority data.
@@ -312,13 +322,13 @@ def download_content(title_id: str, content_id: int, wiiu_endpoint: bool = False
312322
content_url = f"{endpoint_url}{title_id}/{content_id:08X}"
313323
# Make the request.
314324
try:
315-
response = requests.get(url=content_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True)
325+
response = requests.get(url=content_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True, timeout=5)
316326
except requests.exceptions.ConnectionError:
317327
if endpoint_override:
318328
raise ValueError("A connection could not be made to the NUS endpoint. Please make sure that your endpoint "
319329
"override is valid.")
320330
else:
321-
raise Exception("A connection could not be made to the NUS endpoint. The NUS may be unavailable.")
331+
raise NUSConnectionError("A connection could not be made to the NUS endpoint. The NUS may be unavailable.")
322332
if response.status_code == 404:
323333
raise ValueError(f"The requested Title ID does not exist, or an invalid Content ID is present in the"
324334
f" content records provided.\n Failed while downloading Content ID: {content_id:08X}")
@@ -329,9 +339,12 @@ def download_content(title_id: str, content_id: int, wiiu_endpoint: bool = False
329339
progress(0, total_size)
330340
# Stream the content just like the TMD/Ticket.
331341
content = b""
332-
for chunk in response.iter_content(chunk_size=1024):
333-
content += chunk
334-
progress(len(content), total_size)
342+
try:
343+
for chunk in response.iter_content(chunk_size=1024):
344+
content += chunk
345+
progress(len(content), total_size)
346+
except requests.exceptions.ConnectionError:
347+
raise NUSConnectionError("A connection could not be made to the NUS endpoint. The NUS may be unavailable.")
335348
return content
336349

337350

0 commit comments

Comments
 (0)