├── .gitignore
├── resources
├── lib
│ ├── __init__.py
│ └── OSUtilities.py
├── settings.xml
└── language
│ ├── Chinese (Simple)
│ └── strings.po
│ └── English
│ └── strings.po
├── icon.png
├── logo.png
├── README.md
├── addon.xml
├── changelog.txt
└── service.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | *.pyc
3 | *.pyo
--------------------------------------------------------------------------------
/resources/lib/__init__.py:
--------------------------------------------------------------------------------
1 | # Dummy file to make this directory a package.
2 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amet/service.subtitles.opensubtitles/HEAD/icon.png
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amet/service.subtitles.opensubtitles/HEAD/logo.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is no longer being maintained
2 | -----------------------------------
3 |
4 | Use https://github.com/opensubtitles/service.subtitles.opensubtitles to report any issues you might have
5 |
--------------------------------------------------------------------------------
/resources/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/resources/language/Chinese (Simple)/strings.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 |
4 | #Chinese Simplified
5 |
6 | msgctxt "#32001"
7 | msgid "Error searching for subtitles"
8 | msgstr "字幕搜索出错"
9 |
10 | msgctxt "#32002"
11 | msgid "Username"
12 | msgstr "用户名"
13 |
14 | msgctxt "#32003"
15 | msgid "Password"
16 | msgstr "密码"
17 |
18 | msgctxt "#32004"
19 | msgid "Login Details"
20 | msgstr "登录详细信息"
--------------------------------------------------------------------------------
/resources/language/English/strings.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: XBMC Main Translation Project (Frodo)\n"
4 | "Report-Msgid-Bugs-To: http://trac.xbmc.org/\n"
5 | "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
6 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
7 | "Last-Translator: XBMC Translation Team\n"
8 | "Language-Team: English (http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/en/)\n"
9 | "MIME-Version: 1.0\n"
10 | "Content-Type: text/plain; charset=UTF-8\n"
11 | "Content-Transfer-Encoding: 8bit\n"
12 | "Language: en\n"
13 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
14 |
15 | #English
16 |
17 | msgctxt "#32001"
18 | msgid "Error searching for subtitles"
19 | msgstr ""
20 |
21 | msgctxt "#32002"
22 | msgid "Username"
23 | msgstr ""
24 |
25 | msgctxt "#32003"
26 | msgid "Password"
27 | msgstr ""
28 |
29 | msgctxt "#32004"
30 | msgid "Login Details"
31 | msgstr ""
32 |
33 | msgctxt "#32005"
34 | msgid "check username/pass or register at opensubtitles.org"
35 | msgstr ""
--------------------------------------------------------------------------------
/addon.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | version="5.0.15"
6 |
7 |
8 |
9 |
11 |
12 | OpenSubtitles
13 | Search and download subtitles from opensubtitles.org
14 | 从 opensubtitles.org 搜索并下载字幕
15 |
16 | all
17 | GNU GENERAL PUBLIC LICENSE. Version 2, June 1991
18 | http://forum.xbmc.org/showthread.php?tid=183114
19 |
20 |
21 | https://github.com/amet/service.subtitles.opensubtitles
22 |
23 |
24 |
--------------------------------------------------------------------------------
/changelog.txt:
--------------------------------------------------------------------------------
1 | 5.0.15
2 | - disable anonymous login, users need to register on opensubtitles.org and login in addon settings.
3 |
4 | 5.0.14
5 | - Fix for Portuguese (Brazil) broken by 42f6ec9, thx host505
6 |
7 | 5.0.13
8 | - Fix for Greek subtitles, thx host505
9 |
10 | 5.0.12
11 | - compare season and episode and display only matching results
12 |
13 | 5.0.11
14 | - fix: search issues
15 | - cosmetics
16 | - add slash or backslash at the end of path (fix xbmcvfs.exists in Helix), thx Ondrej Bima
17 |
18 | 5.0.10
19 | - fix: Don't unquote(urldecode) file_original_path as it breaks http file hashing, thx arnova
20 |
21 | 5.0.9
22 | - fix hash large rars, Beam
23 | - Support for preferred language sorting and fetch using IMDBID, Glenn Jennehed
24 | - fix: hack to work around issue where Brazilian is not found as language in XBMC
25 |
26 | 5.0.8
27 | - fix: extension is needed for downloaded files
28 |
29 | 5.0.7
30 | - fix: Do not use unsafe file names, thx Cesar Canassa
31 |
32 | 5.0.6
33 | - clean temp folder
34 | - add login details to addon settings
35 |
36 | 5.0.5
37 | - [fix] ascii UNICODE.decode
38 | - [fix] manual search string unquoted
39 | - cosmetics and code simplification
40 |
41 | 5.0.4
42 | - manual search button support
43 |
44 | 5.0.3
45 | - fix Portuguese (Brazil) and Greek
46 |
47 | 5.0.2
48 | - icon.png and added logo.png for skin to use in window
49 |
50 | 5.0.1
51 | - let skin control flag filetype
52 |
53 | 5.0.0
54 | - move the service out of XBMC Subtitles
--------------------------------------------------------------------------------
/resources/lib/OSUtilities.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import os
4 | import sys
5 | import xbmc
6 | import struct
7 | import urllib
8 | import xbmcvfs
9 | import xmlrpclib
10 | import xbmcaddon
11 | import unicodedata
12 |
13 | __addon__ = xbmcaddon.Addon()
14 | __version__ = __addon__.getAddonInfo('version') # Module version
15 | __scriptname__ = "XBMC Subtitles Login"
16 |
17 | BASE_URL_XMLRPC = u"http://api.opensubtitles.org/xml-rpc"
18 |
19 | class OSDBServer:
20 | def __init__( self, *args, **kwargs ):
21 | self.server = xmlrpclib.Server( BASE_URL_XMLRPC, verbose=0 )
22 | login = self.server.LogIn(__addon__.getSetting( "OSuser" ), __addon__.getSetting( "OSpass" ), "en", "%s_v%s" %(__scriptname__.replace(" ","_"),__version__))
23 | if (login["status"] == "200 OK"):
24 | self.osdb_token = login[ "token" ]
25 |
26 | def searchsubtitles( self, item):
27 | if ( self.osdb_token ) :
28 | searchlist = []
29 | if item['mansearch']:
30 | searchlist = [{'sublanguageid':",".join(item['3let_language']),
31 | 'query' :urllib.unquote(item['mansearchstr'])
32 | }]
33 | search = self.server.SearchSubtitles( self.osdb_token, searchlist )
34 | if search["data"]:
35 | return search["data"]
36 | else:
37 | return None
38 |
39 | if len(item['tvshow']) > 0:
40 | OS_search_string = ("%s S%.2dE%.2d" % (item['tvshow'],
41 | int(item['season']),
42 | int(item['episode']),)
43 | ).replace(" ","+")
44 | else:
45 | if str(item['year']) == "":
46 | item['title'], item['year'] = xbmc.getCleanMovieTitle( item['title'] )
47 |
48 | OS_search_string = item['title'].replace(" ","+")
49 |
50 | log( __name__ , "Search String [ %s ]" % (OS_search_string,))
51 |
52 | if not item['temp']:
53 | try:
54 | size, hash = hashFile(item['file_original_path'], item['rar'])
55 | log( __name__ ,"OpenSubtitles module hash [%s] and size [%s]" % (hash, size,))
56 | searchlist.append({'sublanguageid' :",".join(item['3let_language']),
57 | 'moviehash' :hash,
58 | 'moviebytesize':str(size)
59 | })
60 | except:
61 | pass
62 |
63 | imdb = str(xbmc.Player().getVideoInfoTag().getIMDBNumber().replace('tt',''))
64 |
65 | if ((not item['tvshow']) and imdb != ""):
66 | searchlist.append({'sublanguageid' :",".join(item['3let_language']),
67 | 'imdbid' :imdb
68 | })
69 |
70 | searchlist.append({'sublanguageid':",".join(item['3let_language']),
71 | 'query' :OS_search_string
72 | })
73 |
74 | else:
75 | searchlist = [{'sublanguageid':",".join(item['3let_language']),
76 | 'query' :OS_search_string
77 | }]
78 |
79 | search = self.server.SearchSubtitles( self.osdb_token, searchlist )
80 | if search["data"]:
81 | return search["data"]
82 |
83 |
84 | def download(self, ID, dest):
85 | try:
86 | import zlib, base64
87 | down_id=[ID,]
88 | result = self.server.DownloadSubtitles(self.osdb_token, down_id)
89 | if result["data"]:
90 | local_file = open(dest, "w" + "b")
91 | d = zlib.decompressobj(16+zlib.MAX_WBITS)
92 | data = d.decompress(base64.b64decode(result["data"][0]["data"]))
93 | local_file.write(data)
94 | local_file.close()
95 | log( __name__,"Download Using XMLRPC")
96 | return True
97 | return False
98 | except:
99 | return False
100 |
101 | def log(module, msg):
102 | xbmc.log((u"### [%s] - %s" % (module,msg,)).encode('utf-8'),level=xbmc.LOGDEBUG )
103 |
104 | def hashFile(file_path, rar):
105 | if rar:
106 | return OpensubtitlesHashRar(file_path)
107 |
108 | log( __name__,"Hash Standard file")
109 | longlongformat = 'q' # long long
110 | bytesize = struct.calcsize(longlongformat)
111 | f = xbmcvfs.File(file_path)
112 |
113 | filesize = f.size()
114 | hash = filesize
115 |
116 | if filesize < 65536 * 2:
117 | return "SizeError"
118 |
119 | buffer = f.read(65536)
120 | f.seek(max(0,filesize-65536),0)
121 | buffer += f.read(65536)
122 | f.close()
123 | for x in range((65536/bytesize)*2):
124 | size = x*bytesize
125 | (l_value,)= struct.unpack(longlongformat, buffer[size:size+bytesize])
126 | hash += l_value
127 | hash = hash & 0xFFFFFFFFFFFFFFFF
128 |
129 | returnHash = "%016x" % hash
130 | return filesize,returnHash
131 |
132 |
133 | def OpensubtitlesHashRar(firsrarfile):
134 | log( __name__,"Hash Rar file")
135 | f = xbmcvfs.File(firsrarfile)
136 | a=f.read(4)
137 | if a!='Rar!':
138 | raise Exception('ERROR: This is not rar file.')
139 | seek=0
140 | for i in range(4):
141 | f.seek(max(0,seek),0)
142 | a=f.read(100)
143 | type,flag,size=struct.unpack( '=2:
114 | params=paramstring
115 | cleanedparams=params.replace('?','')
116 | if (params[len(params)-1]=='/'):
117 | params=params[0:len(params)-2]
118 | pairsofparams=cleanedparams.split('&')
119 | param={}
120 | for i in range(len(pairsofparams)):
121 | splitparams={}
122 | splitparams=pairsofparams[i].split('=')
123 | if (len(splitparams))==2:
124 | param[splitparams[0]]=splitparams[1]
125 |
126 | return param
127 |
128 | params = get_params()
129 |
130 | if params['action'] == 'search' or params['action'] == 'manualsearch':
131 | log( __name__, "action '%s' called" % params['action'])
132 | item = {}
133 | item['temp'] = False
134 | item['rar'] = False
135 | item['mansearch'] = False
136 | item['year'] = xbmc.getInfoLabel("VideoPlayer.Year") # Year
137 | item['season'] = str(xbmc.getInfoLabel("VideoPlayer.Season")) # Season
138 | item['episode'] = str(xbmc.getInfoLabel("VideoPlayer.Episode")) # Episode
139 | item['tvshow'] = normalizeString(xbmc.getInfoLabel("VideoPlayer.TVshowtitle")) # Show
140 | item['title'] = normalizeString(xbmc.getInfoLabel("VideoPlayer.OriginalTitle"))# try to get original title
141 | item['file_original_path'] = xbmc.Player().getPlayingFile().decode('utf-8') # Full path of a playing file
142 | item['3let_language'] = [] #['scc','eng']
143 | PreferredSub = params.get('preferredlanguage')
144 |
145 | if 'searchstring' in params:
146 | item['mansearch'] = True
147 | item['mansearchstr'] = params['searchstring']
148 |
149 | for lang in urllib.unquote(params['languages']).decode('utf-8').split(","):
150 | if lang == "Portuguese (Brazil)":
151 | lan = "pob"
152 | elif lang == "Greek":
153 | lan = "ell"
154 | else:
155 | lan = xbmc.convertLanguage(lang,xbmc.ISO_639_2)
156 |
157 | item['3let_language'].append(lan)
158 |
159 | if item['title'] == "":
160 | log( __name__, "VideoPlayer.OriginalTitle not found")
161 | item['title'] = normalizeString(xbmc.getInfoLabel("VideoPlayer.Title")) # no original title, get just Title
162 |
163 | if item['episode'].lower().find("s") > -1: # Check if season is "Special"
164 | item['season'] = "0" #
165 | item['episode'] = item['episode'][-1:]
166 |
167 | if ( item['file_original_path'].find("http") > -1 ):
168 | item['temp'] = True
169 |
170 | elif ( item['file_original_path'].find("rar://") > -1 ):
171 | item['rar'] = True
172 | item['file_original_path'] = os.path.dirname(item['file_original_path'][6:])
173 |
174 | elif ( item['file_original_path'].find("stack://") > -1 ):
175 | stackPath = item['file_original_path'].split(" , ")
176 | item['file_original_path'] = stackPath[0][8:]
177 |
178 | Search(item)
179 |
180 | elif params['action'] == 'download':
181 | subs = Download(params["ID"], params["link"],params["format"])
182 | for sub in subs:
183 | listitem = xbmcgui.ListItem(label=sub)
184 | xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=sub,listitem=listitem,isFolder=False)
185 |
186 |
187 | xbmcplugin.endOfDirectory(int(sys.argv[1]))
188 |
--------------------------------------------------------------------------------