Skip to content

Commit f92ab54

Browse files
authored
fixed bugs (#2181)
- fixed scenes with no studio crashing - more robust filename handling with normalizing and encoding, so all special characters should work - more robust handling of missing fields overall
1 parent 3c1f701 commit f92ab54

File tree

1 file changed

+51
-30
lines changed

1 file changed

+51
-30
lines changed

scrapers/ShokoAPI/ShokoAPI.py

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
from urllib.request import Request, urlopen
2-
import sys
31
import os
2+
import sys
43
import json
54
import re
6-
import urllib.error
7-
import urllib.request
5+
import unicodedata # For Unicode normalization
6+
from urllib.parse import quote # For URL encoding
7+
from urllib.request import Request, urlopen # For HTTP requests
8+
import urllib.error # For handling URL-related errors
89

910
# to import from a parent directory we need to add that directory to the system path
1011
csd = os.path.dirname(
@@ -35,6 +36,7 @@
3536
"You need to download the folder 'py_common' from the community repo! (CommunityScrapers/tree/master/scrapers/py_common)",
3637
file=sys.stderr)
3738
sys.exit()
39+
3840
import config
3941

4042
SHOKO_API_KEY = '' #leave empty it gets your Shoko api key with your shoko server username and password
@@ -43,7 +45,6 @@
4345
SHOKO_PASS = config.SHOKO.get("pass", "")
4446

4547

46-
4748
def validate_user_inputs() -> bool:
4849
shoko = bool(re.search(r"^(http|https)://.+:\d+$", SHOKO_URL))
4950
if shoko is False:
@@ -61,8 +62,10 @@ def get_filename(scene_id: str) -> str:
6162
pattern = "(^.+)([\\\\]|[/])"
6263
replace = ""
6364
filename = re.sub(pattern, replace, str(path))
64-
log.debug(f"encoded filename: {filename}")
65-
return filename
65+
normalized_filename = unicodedata.normalize('NFC', filename)
66+
encoded_filename = quote(normalized_filename, safe='')
67+
log.debug(f"encoded filename: {encoded_filename}")
68+
return encoded_filename
6669

6770

6871
def find_scene_id(scene_id: str) -> (str, str):
@@ -85,15 +88,17 @@ def lookup_scene(scene_id: str, epnumber: str, apikey: str, date: str) -> dict:
8588
res['date'] = date
8689
res['tags'] = [{"name": i} for i in tags]
8790

88-
# Convert the string to a ScrapedStudio instance
89-
studio: ScrapedStudio = {
90-
"name": studio, # only the name will get imported
91-
"image": f"{SHOKO_URL}/api/v3/Image/AniDB/Staff/{studio_id}"
92-
# image will not be imported because of a bug in Stash right now
93-
# this will work as soon as that is fixed
94-
}
91+
if studio is None or studio_id is None:
92+
log.info("No studio information found. Skipping studio.")
93+
else:
94+
# Convert the string to a ScrapedStudio instance
95+
studio: ScrapedStudio = {
96+
"name": studio, # Map the string to the required `name` field
97+
"image": f"{SHOKO_URL}/api/v3/Image/AniDB/Staff/{studio_id}"
98+
}
9599

96-
res['studio'] = studio
100+
res['studio'] = studio
101+
97102
log.debug("sceneinfo from Shoko: " + str(res))
98103
return res
99104

@@ -149,26 +154,42 @@ def get_series(apikey: str, scene_id: str):
149154
headers["apikey"] = apikey
150155
request = Request(SHOKO_URL + '/api/serie/fromep?id=' + scene_id, headers=headers)
151156

152-
response_body = urlopen(request).read()
153-
json_object = json.loads(response_body.decode('utf-8'))
157+
try:
158+
response_body = urlopen(request).read()
159+
json_object = json.loads(response_body.decode('utf-8'))
160+
except Exception as e:
161+
log.error(f"Failed to fetch series details: {e}")
162+
return None, None, None, None, None, None
163+
154164
log.debug("got series:\t" + str(json_object))
155-
title = json_object['name']
156-
details = json_object['summary']
157-
local_sizes = json_object['local_sizes']['Episodes']
165+
title = json_object.get('name', None)
166+
details = json_object.get('summary', None)
167+
local_sizes = json_object.get('local_sizes', {}).get('Episodes', 0)
158168
log.debug("number of episodes " + str(local_sizes))
159169
cover = SHOKO_URL + json_object['art']['thumb'][0]['url']
160-
tags = json_object['tags']
161-
162-
series_id = json_object['id']
163-
response = requests.get(SHOKO_URL + '/api/v3/Series/%s/Cast?roleType=Studio' % series_id, headers=headers)
170+
tags = json_object.get('tags', [])
164171

165-
# Parse the JSON response
166-
json_response = response.json()
172+
series_id = json_object.get('id')
173+
174+
try:
175+
response = requests.get(
176+
f'{SHOKO_URL}/api/v3/Series/{series_id}/Cast?roleType=Studio',
177+
headers=headers
178+
)
179+
response.raise_for_status()
180+
json_response = response.json()
181+
except requests.RequestException as e:
182+
log.error(f"Failed to fetch studio data: {e}")
183+
json_response = []
184+
185+
# Handle cases where there are no studios
186+
if json_response:
187+
studio = json_response[0].get('Staff', {}).get('Name', None)
188+
studio_id = json_response[0].get('Staff', {}).get('ID', None)
189+
else:
190+
studio = None
191+
studio_id = None
167192

168-
# Extract the 'Name' field
169-
studio = json_response[0]['Staff']['Name']
170-
studio_id = json_response[0]['Staff']['ID']
171-
172193
return title, details, cover, tags, studio, studio_id #, characters
173194

174195

0 commit comments

Comments
 (0)