├── .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 | --------------------------------------------------------------------------------