Skip to content

Commit 147c3f0

Browse files
committed
[service.subtitles.opensubtitles-com] 1.0.7
1 parent c319727 commit 147c3f0

File tree

6 files changed

+126
-7
lines changed

6 files changed

+126
-7
lines changed

service.subtitles.opensubtitles-com/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ Search and download subtitles for movies and TV-Series from OpenSubtitles.com. S
44

55
REST API implementation based on tomburke25 [python-opensubtitles-rest-api](https://github.com/tomburke25/python-opensubtitles-rest-api)
66

7+
v1.0.7 (2025-08-26)
8+
- added IMDB and TMDB collection on files for more accurate search to the API
9+
10+
v1.0.6 (2024-11-29)
11+
- fixed issue with RAR archives (thanks ninjacarr)
12+
- handles default chinese language to zh-cn (thanks ninjacarr)
13+
714
v1.0.5 (2024-07-30)
815
- fixed issue with portuguese file names
916
- added AI translated filter (thanks Kate6)

service.subtitles.opensubtitles-com/addon.xml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
22
<addon id="service.subtitles.opensubtitles-com"
33
name="OpenSubtitles.com"
4-
version="1.0.5"
4+
version="1.0.7"
55
provider-name="amet, opensubtitles, juokelis, opensubtitlesdev">
66
<requires>
77
<import addon="xbmc.python" version="3.0.0"/>
@@ -52,6 +52,14 @@
5252
<description lang="zh_CN">多语种的电影及剧集字幕,每日更新千余条翻译好的字幕。免费下载,提供API接口,已拥有上百万的用户。</description>
5353
<disclaimer lang="en_GB">Users need to provide OpenSubtitles.com username and password in add-on configuration. This is our new extension, old opensubtitles.org will not work on this, but the account can be easily imported on opensubtitles.com.</disclaimer>
5454
<news>
55+
56+
v1.0.7 (2025-08-26)
57+
- added IMDB and TMDB collection on files for more accurate search to the API
58+
59+
v1.0.6 (2024-11-29)
60+
- fixed issue with RAR archives (thanks ninjacarr)
61+
- handles default chinese language to zh-cn (thanks ninjacarr)
62+
5563
v1.0.5 (2024-07-30)
5664
- fixed issue with portuguese file names
5765
- added AI translated filter (thanks Kate6)

service.subtitles.opensubtitles-com/changelog.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
v1.0.6 (2024-11-29)
2+
- fixed issue with RAR archives (thanks ninjacarr)
3+
- handles default chinese language to zh-cn (thanks ninjacarr)
4+
15
v1.0.5 (2024-07-30)
26
- fixed issue with portuguese file names
37
- added AI translated filter (thanks Kate6)

service.subtitles.opensubtitles-com/resources/lib/data_collector.py

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

22
from urllib.parse import unquote
33
from difflib import SequenceMatcher
4+
import json
5+
import xml.etree.ElementTree as ET
46

57
import xbmc
68
import xbmcaddon
@@ -29,21 +31,111 @@ def get_media_data():
2931
"season_number": str(xbmc.getInfoLabel("VideoPlayer.Season")),
3032
"episode_number": str(xbmc.getInfoLabel("VideoPlayer.Episode")),
3133
"tv_show_title": normalize_string(xbmc.getInfoLabel("VideoPlayer.TVshowtitle")),
32-
"original_title": normalize_string(xbmc.getInfoLabel("VideoPlayer.OriginalTitle"))}
34+
"original_title": normalize_string(xbmc.getInfoLabel("VideoPlayer.OriginalTitle")),
35+
"parent_tmdb_id": None,
36+
"parent_imdb_id": None,
37+
"imdb_id": None,
38+
"tmdb_id": None}
3339

3440

3541

3642

3743
if item["tv_show_title"]:
44+
item["tvshowid"] = xbmc.getInfoLabel("VideoPlayer.TvShowDBID")
3845
item["query"] = item["tv_show_title"]
3946
item["year"] = None # Kodi gives episode year, OS searches by series year. Without year safer.
40-
item["imdb_id"] = None # Kodi gives strange id. Without id safer.
47+
# Reset movie-specific IDs for TV shows
4148
# TODO if no season and episode numbers use guessit
49+
50+
# Extract TMDB and IMDB IDs for TV shows to improve search results
51+
if len(item["tvshowid"]) != 0:
52+
try:
53+
TVShowDetails = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id":"1", "method": "VideoLibrary.GetTVShowDetails", "params":{"tvshowid":'+item["tvshowid"]+', "properties": ["episodeguide", "imdbnumber"]} }')
54+
TVShowDetails_dict = json.loads(TVShowDetails)
55+
if "result" in TVShowDetails_dict and "tvshowdetails" in TVShowDetails_dict["result"]:
56+
tvshow_details = TVShowDetails_dict["result"]["tvshowdetails"]
57+
58+
# Extract parent IMDB ID from imdbnumber field
59+
if "imdbnumber" in tvshow_details and tvshow_details["imdbnumber"]:
60+
imdb_raw = str(tvshow_details["imdbnumber"])
61+
# Extract numeric part from IMDB ID (remove 'tt' prefix if present)
62+
if imdb_raw.startswith('tt'):
63+
imdb_number = imdb_raw[2:]
64+
else:
65+
imdb_number = imdb_raw
66+
# Validate it's numeric and reasonable length (IMDB IDs are typically 6-8 digits)
67+
if imdb_number.isdigit() and 6 <= len(imdb_number) <= 8:
68+
item["parent_imdb_id"] = int(imdb_number)
69+
log(__name__, f"Found parent IMDB ID for TV show: {item['parent_imdb_id']}")
70+
71+
# Extract parent TMDB ID from episodeguide
72+
if "episodeguide" in tvshow_details and tvshow_details["episodeguide"]:
73+
episodeguideXML = tvshow_details["episodeguide"]
74+
episodeguide = ET.fromstring(episodeguideXML)
75+
if episodeguide.text:
76+
episodeguideJSON = json.loads(episodeguide.text)
77+
if "tmdb" in episodeguideJSON and episodeguideJSON["tmdb"]:
78+
tmdb_id = int(episodeguideJSON["tmdb"])
79+
if tmdb_id > 0:
80+
item["parent_tmdb_id"] = tmdb_id
81+
log(__name__, f"Found parent TMDB ID for TV show: {item['parent_tmdb_id']}")
82+
except (json.JSONDecodeError, ET.ParseError, ValueError, KeyError) as e:
83+
log(__name__, f"Failed to extract TV show IDs: {e}")
84+
item["parent_tmdb_id"] = None
85+
item["parent_imdb_id"] = None
4286

4387
elif item["original_title"]:
4488
item["query"] = item["original_title"]
45-
46-
89+
90+
# For movies, try to extract IMDB and TMDB IDs
91+
try:
92+
# Get IMDB ID from VideoPlayer
93+
imdb_raw = xbmc.getInfoLabel("VideoPlayer.IMDBNumber")
94+
if imdb_raw:
95+
# Extract numeric part from IMDB ID (remove 'tt' prefix if present)
96+
if imdb_raw.startswith('tt'):
97+
imdb_number = imdb_raw[2:]
98+
else:
99+
imdb_number = imdb_raw
100+
# Validate it's numeric and reasonable length (IMDB IDs are typically 6-8 digits)
101+
if imdb_number.isdigit() and 6 <= len(imdb_number) <= 8:
102+
item["imdb_id"] = int(imdb_number)
103+
log(__name__, f"Found IMDB ID for movie: {item['imdb_id']}")
104+
105+
# Try to get TMDB ID (might be available in some library setups)
106+
# This is less common but worth trying
107+
tmdb_raw = xbmc.getInfoLabel("VideoPlayer.DBID")
108+
if tmdb_raw and tmdb_raw.isdigit():
109+
tmdb_id = int(tmdb_raw)
110+
if tmdb_id > 0:
111+
item["tmdb_id"] = tmdb_id
112+
log(__name__, f"Found TMDB ID for movie: {item['tmdb_id']}")
113+
except (ValueError, KeyError) as e:
114+
log(__name__, f"Failed to extract movie IDs: {e}")
115+
116+
117+
# Clean up and apply fallback logic for IDs
118+
# Remove zero or invalid IDs
119+
if item.get("parent_tmdb_id") == 0:
120+
item["parent_tmdb_id"] = None
121+
if item.get("parent_imdb_id") == 0:
122+
item["parent_imdb_id"] = None
123+
if item.get("tmdb_id") == 0:
124+
item["tmdb_id"] = None
125+
if item.get("imdb_id") == 0:
126+
item["imdb_id"] = None
127+
128+
# Apply fallback strategy: prefer one ID type to avoid conflicts
129+
# For TV shows: prefer parent_tmdb_id over parent_imdb_id
130+
if item.get("parent_tmdb_id") and item.get("parent_imdb_id"):
131+
log(__name__, f"Both parent TMDB and IMDB IDs found, preferring TMDB ID: {item['parent_tmdb_id']}")
132+
item["parent_imdb_id"] = None
133+
134+
# For movies: prefer tmdb_id over imdb_id
135+
if item.get("tmdb_id") and item.get("imdb_id"):
136+
log(__name__, f"Both TMDB and IMDB IDs found for movie, preferring TMDB ID: {item['tmdb_id']}")
137+
item["imdb_id"] = None
138+
47139
if not item["query"]:
48140
log(__name__, "query still blank, fallback to title")
49141
item["query"] = normalize_string(xbmc.getInfoLabel("VideoPlayer.Title")) # no original title, get just Title
@@ -53,6 +145,10 @@ def get_media_data():
53145
item["season_number"] = "0" #
54146
item["episode_number"] = item["episode_number"][-1:]
55147

148+
# Remove tvshowid since it's only used internally and not needed by API
149+
if "tvshowid" in item:
150+
del item["tvshowid"]
151+
56152
return item
57153

58154

@@ -134,6 +230,7 @@ def convert_language(language, reverse=False):
134230
"English": "en",
135231
"Portuguese (Brazil)": "pt-br",
136232
"Portuguese": "pt-pt",
233+
"Chinese": "zh-cn",
137234
"Chinese (simplified)": "zh-cn",
138235
"Chinese (traditional)": "zh-tw"}
139236

service.subtitles.opensubtitles-com/resources/lib/file_operations.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import xbmcvfs, xbmc
66

7+
from urllib.parse import unquote
78
from resources.lib.utilities import log
89

910

@@ -53,7 +54,9 @@ def hash_file(file_path, rar):
5354
log(__name__, f"Processing file: {file_path} - Is RAR: {rar}")
5455

5556
if rar:
56-
return hash_rar(file_path)
57+
# The rar VFS uses the following scheme: rar://urlencoded_rar_path/archive_content
58+
# file_path is thus urlencoded at this point and must be unquoted
59+
return hash_rar(unquote(file_path))
5760

5861
log(__name__, "Hash Standard file")
5962
long_long_format = "q" # long long

service.subtitles.opensubtitles-com/resources/lib/os/provider.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def __init__(self, api_key, username, password):
7373
logging(f"Username: {self.username}, Password: {self.password}")
7474

7575

76-
self.request_headers = {"Api-Key": self.api_key, "User-Agent": "Opensubtitles.com Kodi plugin v1.0.5" ,"Content-Type": CONTENT_TYPE, "Accept": CONTENT_TYPE}
76+
self.request_headers = {"Api-Key": self.api_key, "User-Agent": "Opensubtitles.com Kodi plugin v1.0.7" ,"Content-Type": CONTENT_TYPE, "Accept": CONTENT_TYPE}
7777

7878
self.session = Session()
7979
self.session.headers = self.request_headers

0 commit comments

Comments
 (0)