# -*- coding: utf-8 -*- ''' Torrenter v2 plugin for XBMC/Kodi Copyright (C) 2012-2015 Vadim Skorba v1 - DiMartino v2 https://forums.tvaddons.ag/addon-releases/29224-torrenter-v2.html This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ''' import urllib2 import re import socket import datetime import time import sys import os import json import urllib import xbmcplugin import xbmcgui import xbmc import xbmcaddon import xbmcvfs import Localization try: from sqlite3 import dbapi2 as sqlite except: from pysqlite2 import dbapi2 as sqlite __settings__ = xbmcaddon.Addon(id='plugin.video.torrenter') __language__ = __settings__.getLocalizedString ROOT = __settings__.getAddonInfo('path') # .decode('utf-8').encode(sys.getfilesystemencoding()) userStorageDirectory = xbmc.translatePath(__settings__.getSetting("storage")) torrentFilesDirectory = 'torrents' USERAGENT = "Mozilla/5.0 (Windows NT 6.1; rv:5.0) Gecko/20100101 Firefox/5.0" __addonpath__ = __settings__.getAddonInfo('path') icon = os.path.join(__addonpath__, 'icon.png') __version__ = __settings__.getAddonInfo('version') __plugin__ = __settings__.getAddonInfo('name') + " v." + __version__ def clearStorage(userStorageDirectory, force = False): userStorageDirectory = decode(userStorageDirectory) #log('[clearStorage]: storage '+str(userStorageDirectory) + os.sep) min_storage_size = __settings__.getSetting("min_storage_size") storage_size = getDirectorySizeInGB(userStorageDirectory.encode('utf-8')) if storage_size >= min_storage_size or force: if xbmcvfs.exists(userStorageDirectory + os.sep) or os.path.exists(userStorageDirectory): log('[clearStorage]: storage exists') import shutil temp = userStorageDirectory.rstrip('Torrenter').rstrip('/\\') torrents_temp, saved_temp, i = None, None, '' while not torrents_temp or os.path.exists(torrents_temp) or os.path.exists(saved_temp): torrents_temp = os.path.join(temp, 'torrents' + str(i)) + os.sep saved_temp = os.path.join(temp, 'Saved Files' + str(i)) + os.sep if i=='': i=0 else: i += 1 torrents = os.path.join(userStorageDirectory, 'torrents') saved = os.path.join(userStorageDirectory, 'Saved Files') torrents_bool, saved_bool = False, False if os.path.exists(torrents): shutil.move(torrents, torrents_temp) torrents_bool = True if os.path.exists(saved): shutil.move(saved, saved_temp) saved_bool = True shutil.rmtree(userStorageDirectory, ignore_errors=True) #log(str(xbmcvfs.listdir(userStorageDirectory))) xbmcvfs.mkdir(userStorageDirectory) if torrents_bool: shutil.move(torrents_temp, torrents) if saved_bool: shutil.move(saved_temp, saved) showMessage(Localization.localize('Storage'), Localization.localize('Storage has been cleared')) else: showMessage(Localization.localize('Storage'), Localization.localize('Does not exists')) log('[clearStorage]: fail storage '+userStorageDirectory + os.sep) try: DownloadDB().clear() except Exception, e: log('[clearStorage]: DownloadDB().clear() failed. '+str(e)) def sortcomma(dict, json): for x in dict: y = dict[x].split(',') dict[x] = '' for i in y: if i not in json: dict[x] = dict[x] + ',' + i if len(dict[x]) > 0: dict[x] = dict[x][1:len(dict[x])] return dict def md5(string): try: from hashlib import md5 except ImportError: from md5 import md5 hasher = hashlib.md5() try: hasher.update(string) except: hasher.update(string.encode('utf-8', 'ignore')) return hasher.hexdigest() def log(msg): try: xbmc.log("### [%s]: %s" % (__plugin__,msg,), level=xbmc.LOGNOTICE ) except UnicodeEncodeError: xbmc.log("### [%s]: %s" % (__plugin__,msg.encode("utf-8", "ignore"),), level=xbmc.LOGNOTICE ) except: xbmc.log("### [%s]: %s" % (__plugin__,'ERROR LOG',), level=xbmc.LOGNOTICE ) def debug(msg, forced=False): if getSettingAsBool('debug') and forced: level=xbmc.LOGNOTICE else: level=xbmc.LOGDEBUG try: xbmc.log("### [%s]: %s" % (__plugin__,msg,), level=level ) except UnicodeEncodeError: xbmc.log("### [%s]: %s" % (__plugin__,msg.encode("utf-8", "ignore"),), level=level ) except: xbmc.log("### [%s]: %s" % (__plugin__,'ERROR DEBUG',), level=level ) def showMessage(heading, message, times=10000, forced=False): if forced or not getSettingAsBool('disable_notifications'): xbmc.executebuiltin('XBMC.Notification("%s", "%s", %s, "%s")' % ( heading.replace('"', "'"), message.replace('"', "'"), times, icon)) debug(str((heading.replace('"', "'"), message.replace('"', "'"), times, icon))) def magnet_alert(): showMessage('Ace Stream', Localization.localize('Does not support magnet links!')) def dates_diff(fdate, ldate): try: if ldate == 'today': x = datetime.datetime.now() else: x = datetime.datetime.strptime(ldate, '%d.%m.%Y') y = datetime.datetime.strptime(fdate, '%d.%m.%Y') z = x - y except TypeError: if ldate == 'today': x = datetime.datetime.now() else: x = datetime.datetime(*(time.strptime(ldate, '%d.%m.%Y')[0:6])) y = datetime.datetime(*(time.strptime(fdate, '%d.%m.%Y')[0:6])) z = x - y return str(z.days) def today_str(): try: x = datetime.datetime.now().strftime('%d.%m.%Y') except TypeError: x = datetime.datetime(*(time.strptime(datetime.datetime.now().strftime('%d.%m.%Y'), '%d.%m.%Y')[0:6])) x = datetime.datetime.strftime(x, '%d.%m.%Y') return str(x) def get_params(): param = [] paramstring = sys.argv[2] if len(paramstring) >= 2: params = sys.argv[2] cleanedparams = params.replace('?', '') if (params[len(params) - 1] == '/'): params = params[0:len(params) - 2] pairsofparams = cleanedparams.split('&') param = {} for i in range(len(pairsofparams)): splitparams = {} splitparams = pairsofparams[i].split('=') if (len(splitparams)) == 2: param[splitparams[0]] = splitparams[1] return param def get_apps(paramstring=None): if not paramstring: paramstring = sys.argv[2] if len(paramstring) >= 2: try: apps = json.loads(urllib.unquote_plus(paramstring)) except: cleanapps = str(paramstring).replace('?', '', 1) apps = json.loads(urllib.unquote_plus(cleanapps)) return apps def int_xx(intxx): if intxx and intxx != 'None': return '%02d' % (int(intxx)) else: return '00' def StripName(name, list, replace=' '): lname = name.lower().split(' ') name = '' for n in lname: if n not in list: name += ' ' + n return name.strip() def tempdir(): dirname = xbmc.translatePath('special://temp') for subdir in ('xbmcup', 'plugin.video.torrenter'): dirname = os.path.join(dirname, subdir) if not os.path.exists(dirname): os.mkdir(dirname) return dirname def makeapp(s): return urllib.quote_plus(json.dumps(s)) def get_url(cookie, url): headers = {'User-Agent': 'XBMC', 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': cookie} try: conn = urllib2.urlopen(urllib2.Request(url, urllib.urlencode({}), headers)) array = conn.read() # debug('[get_url]: arr"'+str(array)+'"') if array == '': # debug('[get_url][2]: arr=""') array = True return array except urllib2.HTTPError as e: # debug('[get_url]: HTTPError, e.code='+str(e.code)) if e.code == 401: debug('[get_url]: Denied! Wrong login or api is broken!') return elif e.code in [503]: debug('[get_url]: Denied, HTTP Error, e.code=' + str(e.code)) return else: showMessage('HTTP Error', str(e.code)) debug('[get_url]: HTTP Error, e.code=' + str(e.code)) xbmc.sleep(2000) return except: return False def post_url_json(cookie, url, post): headers = {'User-Agent': 'XBMC', 'Connection': 'keep-alive', 'Cookie': cookie} conn = urllib2.urlopen(urllib2.Request(url, post, headers)) array = conn.read() conn.close() return array def creat_db(dbfilename): db = sqlite.connect(dbfilename) cur = db.cursor() cur.execute('pragma auto_vacuum=1') cur.execute( 'create table sources(addtime integer, filename varchar(32) PRIMARY KEY, showId integer, seasonId integer, episodeId integer, id integer, stype varchar(32))') cur.execute('create table cache(addtime integer, url varchar(32))') cur.execute('create table scan(addtime integer, filename varchar(32) PRIMARY KEY)') cur.execute('create table watched(addtime integer, rating integer, id varchar(32) PRIMARY KEY)') db.commit() cur.close() db.close() def invert_bool(var): if bool(var): var = False else: var = True return var def getSettingAsBool(setting): return __settings__.getSetting(setting).lower() == "true" def calculate(full): consts = [(100, 20), (300, 15), (500, 10), (2000, 7), (4000, 5)] max_const = 150 repl_const = 0 for size, const in consts: if (size * 1024 * 1024) > full: repl_const = int(float(const) / 100 * (full / (1024 * 1024))) # MB of first break if repl_const == 0: repl_const = max_const return repl_const def getDirList(path, newl=None): l = [] try: if not newl: dirs, newl = xbmcvfs.listdir(path) except: try: if not newl: dirs, newl = xbmcvfs.listdir(path.decode('utf-8').encode('cp1251')) except: showMessage(__language__(30206), __language__(30280), forced=True) return l for fl in newl: match = re.match('.avi|.mp4|.mkV|.flv|.mov|.vob|.wmv|.ogm|.asx|.mpg|mpeg|.avc|.vp3|.fli|.flc|.m4v', fl[int(len(fl)) - 4:len(fl)], re.I) if match: l.append(fl) return l def cutFileNames(l): from difflib import Differ d = Differ() text = sortext(l) newl = [] for li in l: newl.append(cutStr(li[0:len(li) - 1 - len(li.split('.')[-1])])) l = newl text1 = cutStr(text[0][0:len(text[0]) - 1 - len(text[0].split('.')[-1])]) text2 = cutStr(text[1][0:len(text[1]) - 1 - len(text[1].split('.')[-1])]) sep_file = " " result = list(d.compare(text1.split(sep_file), text2.split(sep_file))) debug('[cutFileNames] ' + unicode(result)) start = '' end = '' for res in result: if str(res).startswith('-') or str(res).startswith('+') or str(res).startswith('.?'): break start = start + str(res).strip() + sep_file result.reverse() for res in result: if str(res).startswith('-') or str(res).startswith('+') or str(res).startswith('?'): break end = sep_file + str(res).strip() + end newl = l l = [] debug('[cutFileNames] [start] ' + start) debug('[cutFileNames] [end] ' + end) for fl in newl: if cutStr(fl[0:len(start)]) == cutStr(start): fl = fl[len(start):] if cutStr(fl[len(fl) - len(end):]) == cutStr(end): fl = fl[0:len(fl) - len(end)] try: isinstance(int(fl.split(sep_file)[0]), int) fl = fl.split(sep_file)[0] except: pass l.append(fl) debug('[cutFileNames] [sorted l] ' + unicode(sorted(l, key=lambda x: x))) return l def cutStr(s): return s.replace('.', ' ').replace('_', ' ').replace('[', ' ').replace(']', ' ').lower().strip() def sortext(filelist): result = {} for name in filelist: ext = name.split('.')[-1] try: result[ext] = result[ext] + 1 except: result[ext] = 1 lol = result.iteritems() lol = sorted(lol, key=lambda x: x[1]) debug('[sortext]: lol:' + str(lol)) popext = lol[-1][0] result, i = [], 0 for name in filelist: if name.split('.')[-1] == popext: result.append(name) i = i + 1 result = sweetpair(result) debug('[sortext]: result:' + str(result)) return result def cutFolder(contentList, tdir=None): dirList, contentListNew = [], [] if len(contentList) > 1: common_folder = contentList[0][0] debug('[cutFolder]: common_folder '+common_folder) if '\\' in common_folder: common_folder = common_folder.split('\\')[0] elif '/' in common_folder: common_folder = common_folder.split('/')[0] common = True for item in contentList: if common_folder not in item[0]: common = False break for item in contentList: dir = None if common: item[0] = item[0][len(common_folder) + 1:] #debug('[cutFolder]: item[0] '+item[0]) if '\\' in item[0]: dir = item[0].split('\\')[0] elif '/' in item[0]: dir = item[0].split('/')[0] elif not tdir: contentListNew.append(item) if tdir and ensure_str(dir) == ensure_str(tdir): tupleContent = list(item) tupleContent[0] = item[0][len(dir) + 1:] contentListNew.append(list(tupleContent)) if not tdir and dir and dir not in dirList: dirList.append(dir) debug('[cutFolder]: dirList, contentListNew '+str(dirList)+str(contentListNew)) return dirList, contentListNew else: debug('[cutFolder]: dirList, contentList '+str(dirList)+str(contentList)) return dirList, contentList def sweetpair(l): from difflib import SequenceMatcher s = SequenceMatcher() ratio = [] for i in range(0, len(l)): ratio.append(0) for i in range(0, len(l)): for p in range(0, len(l)): s.set_seqs(l[i], l[p]) ratio[i] = ratio[i] + s.quick_ratio() id1, id2 = 0, 0 for i in range(0, len(l)): if ratio[id1] <= ratio[i] and i != id2 or id2 == id1 and ratio[id1] == ratio[i]: id2 = id1 id1 = i # debug('1 - %d %d' % (id1, id2)) elif (ratio[id2] <= ratio[i] or id1 == id2) and i != id1: id2 = i # debug('2 - %d %d' % (id1, id2)) debug('[sweetpair]: id1 ' + l[id1] + ':' + str(ratio[id1])) debug('[sweetpair]: id2 ' + l[id2] + ':' + str(ratio[id2])) return [l[id1], l[id2]] def FileNamesPrepare(filename): my_season = None my_episode = None try: if int(filename): my_episode = int(filename) debug('[FileNamesPrepare] ' + str([my_season, my_episode, filename])) return [my_season, my_episode, filename] except: pass urls = ['s(\d+)e(\d+)', '(\d+)[x|-](\d+)', 'E(\d+)', 'Ep(\d+)', '\((\d+)\)'] for file in urls: match = re.compile(file, re.DOTALL | re.I | re.IGNORECASE).findall(filename) if match: try: my_episode = int(match[1]) my_season = int(match[0]) except: try: my_episode = int(match[0]) except: try: my_episode = int(match[0][1]) my_season = int(match[0][0]) except: try: my_episode = int(match[0][0]) except: break if my_season and my_season > 100: my_season = None if my_episode and my_episode > 365: my_episode = None debug('[FileNamesPrepare] ' + str([my_season, my_episode, filename])) return [my_season, my_episode, filename] def filename2match(filename, no_date=False): results = {'label': filename} urls = ['(.+|.?)s(\d+)e(\d+)', '(.+|.?)s(\d+)\.e(\d+)', '(.+|.?) [\[|\(](\d+)[x|-](\d+)[\]|\)]', '(.+|.?) (\d+)[x|-](\d+)'] # same in service for file in urls: match = re.compile(file, re.I | re.IGNORECASE).findall(filename) # print str(results) if match: results['showtitle'], results['season'], results['episode'] = match[0] results['showtitle'] = results['showtitle'].replace('.', ' ').replace('_', ' ').strip().replace( 'The Daily Show', 'The Daily Show With Jon Stewart') results['season'], results['episode'] = int(results['season']), int(results['episode']) # debug('[filename2match] '+str(results)) return results if no_date: return urls = ['(.+)(\d{4})\.(\d{2,4})\.(\d{2,4})', '(.+)(\d{4}) (\d{2}) (\d{2})'] # same in service for file in urls: match = re.compile(file, re.I | re.IGNORECASE).findall(filename) if match: results['showtitle'] = match[0][0].replace('.', ' ').strip().replace('The Daily Show', 'The Daily Show With Jon Stewart') results['date'] = '%s.%s.%s' % (match[0][3], match[0][2], match[0][1]) debug('[filename2match] ' + str(results)) return results def TextBB(string, action=None, color=None): if action == 'b': string = '[B]' + string + '[/B]' return string def jstr(s): if not s: s = 'null' elif not unicode(s).isnumeric(): s = '"%s"' % (s) return str(s) def view_style(func): styles = {} num_skin = 0 view_style = int(__settings__.getSetting("skin_optimization")) if view_style in [3, 2, 6]: styles['searchOption'] = styles['History'] = styles['List'] = 'info' styles['drawContent'] = styles['drawtrackerList'] = styles['drawcontentList'] = 'info' styles['sectionMenu'] = styles['Seasons'] = 'list' styles['uTorrentBrowser'] = styles['torrentPlayer'] = styles['openTorrent'] = 'wide' styles['showFilesList'] = styles['DownloadStatus'] = 'wide' elif view_style in [1, 4, 5, 7]: styles['searchOption'] = 'info' styles['drawContent'] = styles['torrentPlayer'] = styles['openTorrent'] = styles['drawtrackerList'] = 'info' styles['uTorrentBrowser'] = styles['History'] = styles['DownloadStatus'] = 'wide' styles['showFilesList'] = styles['sectionMenu'] = 'wide' styles['List'] = styles['drawcontentList'] = 'info3' if view_style in [1, 7]: styles['uTorrentBrowser'] = styles['torrentPlayer'] = 'wide' styles['openTorrent'] = styles['History'] = styles['DownloadStatus'] = 'wide' styles['sectionMenu'] = 'icons' elif view_style == 5: styles['uTorrentBrowser'] = styles['torrentPlayer'] = 'wide' styles['openTorrent'] = styles['History'] = styles['DownloadStatus'] = 'wide' styles['drawtrackerList'] = styles['drawContent'] = styles['List'] = styles['sectionMenu'] = 'list' styles['searchOption'] = 'info' if view_style == 8: styles['sectionMenu'] = 'thumbnails' #меню styles['List'] = 'biglist' styles['Seasons'] = 'biglist' styles['uTorrentBrowser'] = 'biglist' styles['torrentPlayer'] = 'biglist' styles['openTorrent'] = 'biglist' styles['History'] = 'biglist' #история поиска styles['DownloadStatus'] = 'biglist' #статус загрузки styles['drawtrackerList'] = 'biglist' styles['drawContent'] = 'list' #списки медиа styles['drawcontentList'] = 'extrainfo' #списки медиа - лист styles['searchOption'] = 'biglist' styles['showFilesList'] = 'biglist' if view_style in [1, 3, 4, 5]: num_skin = 0 elif view_style == 2: num_skin = 1 elif view_style == 6: num_skin = 2 elif view_style == 7: num_skin = 3 if view_style == 8: num_skin = 4 style = styles.get(func) log('[view_style]: lock '+str(style)+' for '+str(func)) lockView(style, num_skin) def lockView(viewId='info', num_skin=0): xbmcplugin.setContent(int(sys.argv[1]), 'movies') skinOptimizations = ( {'list': 50, 'info': 50, 'wide': 51, 'icons': 500, 'info3': 515, }, # Confluence {'list': 50, 'info': 51, 'wide': 52, 'icons': 53, }, # Transperency! {'list': 55, 'info': 55, 'wide': 55, 'icons': 55, 'info3': 55, }, # Aeon Nox {'list': 50, 'info': 54, 'wide': 55, 'icons': 54, 'info3': 500, }, # Estuary {'list': 50, 'bigwide': 51, 'biglist': 52, 'poster': 53, 'banner': 54, 'wall': 55, 'mediainfo': 56, 'extrainfo': 57, "cards":58, "bannerwall":59, 'thumbnails': 500, 'postersquare': 503, 'wallsquare': 505, }, # Arctic: Zephyr ) try: if viewId == 'wide' and num_skin == 3: xbmcplugin.setContent(int(sys.argv[1]), 'files') xbmc.executebuiltin("Container.SetViewMode(%s)" % str(skinOptimizations[num_skin][viewId])) except: return ''' Estuary View_50_List View_51_Poster View_52_IconWall View_53_Shift View_54_InfoWall View_55_WideList View_500_SmallThumb View_501_Banner View_502_FanArt ''' ''' PosterWrapView2_Fanart MediaListView3 MediaListView2 MediaListView4 WideIconView MusicVideoInfoListView AddonInfoListView1 AddonInfoThumbView1 LiveTVView1 ''' def torrent_dir(): from resources.utorrent.net import Download socket.setdefaulttimeout(3) list = Download().list() ret = 0 if list and len(list) > 0: dirs = ["Keyboard"] for dl in list: if dl['dir'] not in dirs: dirs.append(dl['dir']) basename = os.path.dirname(dl['dir']) if basename not in dirs: dirs.append(basename) else: dirs.remove(basename) dirs.insert(1, basename) dialog = xbmcgui.Dialog() ret = dialog.select(Localization.localize('Manual Torrent-client Path Edit'), dirs) else: ret = 0 if ret == 0: KB = xbmc.Keyboard() KB.setHeading(Localization.localize('Manual Torrent-client Path Edit')) KB.setDefault(__settings__.getSetting("torrent_dir")) KB.doModal() if (KB.isConfirmed()): __settings__.setSetting("torrent_dir", KB.getText()) elif ret > 0: __settings__.setSetting("torrent_dir", dirs[ret]) def saveCheckPoint(): __settings__.setSetting("checkpoint", str(sys.argv[2])) def gotoCheckPoint(): xbmc.executebuiltin( 'XBMC.ActivateWindow(Videos,plugin://plugin.video.myshows/%s)' % (__settings__.getSetting("checkpoint"))) def smbtopath(path): x = path.split('@') if len(x) > 1: path = x[1] else: path = path.replace('smb://', '') debug('[smbtopath]:' + '\\\\' + path.replace('/', '\\')) return '\\\\' + path.replace('/', '\\') def PrepareFilename(filename): badsymb = [':', '"', '\\', '/', '\'', '!', ['&', 'and'], '*', '?', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] for b in badsymb: if not isinstance(b, list): filename = filename.replace(b, ' ') else: filename = filename.replace(b[0], b[1]) return filename.rstrip('. ') def PrepareSearch(filename): titles = [filename] rstr = '. -' badsymb = [':', '"', r'\\', '/', r'\'', '!', '&', '*', '_', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] for b in badsymb: filename = filename.replace(b, ' ') filename = re.sub("\([^)]*\)([^(]*)", "\\1", filename) filename = re.sub("\[[^\]]*\]([^\[]*)", "\\1", filename) filename = filename.strip().rstrip(rstr) if titles[0] != filename and filename != '': titles.insert(0, filename) title_array = [(u'(.+?)(Cезон|cезон|Сезон|сезон|Season|season|СЕЗОН|SEASON)', titles[0], 0), (u'(.+?)[sS](\d{1,2})', titles[0].replace('.', ' '), 0), ] for regex, title, i in title_array: recomp = re.compile(regex) match = recomp.findall(title) if match: titles.insert(0, match[0][i].rstrip(rstr)) return titles def isRemoteTorr(): localhost = ['127.0.0.1', '0.0.0.0', 'localhost'] if __settings__.getSetting("torrent") == '0': if __settings__.getSetting("torrent_utorrent_host") not in localhost: debug('[isRemoteTorr]: uTorrent is Remote!') return True elif __settings__.getSetting("torrent") == '1': if __settings__.getSetting("torrent_transmission_host") not in localhost: debug('[isRemoteTorr]: Transmission is Remote!') return True def changeDBTitle(showId): from utilities import xbmcJsonRequest shows = xbmcJsonRequest( {'jsonrpc': '2.0', 'method': 'VideoLibrary.GetTVShows', 'params': {'properties': ['title']}, 'id': 0}) if not shows: debug('[changeDBTitle]: XBMC JSON Result was empty.') return if 'tvshows' in shows: shows = shows['tvshows'] debug("[changeDBTitle]: XBMC JSON Result: '%s'" % str(shows)) else: debug("[changeDBTitle]: Key 'tvshows' not found") return if len(shows) > 0: newtitle = id2title(showId, None, True)[0].decode('utf-8', 'ignore') dialog = xbmcgui.Dialog() dialog_items, dialog_ids = [__language__(30205)], [-1] shows = sorted(shows, key=lambda x: x['tvshowid'], reverse=True) for show in shows: dialog_ids.append(show['tvshowid']) dialog_items.append(show['title']) ret = dialog.select(newtitle, dialog_items) if ret > 0: ok = dialog.yesno(__language__(30322), __language__(30534), __language__(30535) % (dialog_items[ret], newtitle)) if ok: result = xbmcJsonRequest({'jsonrpc': '2.0', 'method': 'VideoLibrary.SetTVShowDetails', 'params': {'tvshowid': int(dialog_ids[ret]), 'title': unicode(newtitle)}, 'id': 1}) if result in [newtitle, 'OK']: showMessage(__language__(30208), __language__(30536) % (newtitle), forced=True) else: debug("[changeDBTitle]: XBMC JSON Result: '%s'" % str(result)) return class TimeOut(): def __init__(self): self.scan = CacheDB('web_timeout') self.gone_online = CacheDB('go_online') self.get = self.scan.get() self.online = 30 self.offline = 1 def go_offline(self, manual=False): gone_online = self.gone_online.get() if gone_online: gone_online = int(gone_online) else: gone_online = 0 if not manual: if gone_online and gone_online + self.online >= int(round(time.time())): debug('[TimeOut]: too soon to go back offline! %d s' % ( (gone_online + self.online * 4) - int(round(time.time())))) return if self.timeout() == self.online: debug('[TimeOut]: Gone offline! %d s' % ((gone_online + self.online * 4) - int(round(time.time())))) showMessage(__language__(30520), __language__(30545) % (self.offline)) if self.get: self.scan.delete() self.scan.add() def go_online(self): if self.get: self.scan.delete() debug('[TimeOut]: Gone online!') showMessage(__language__(30521), __language__(30545) % (self.online)) if self.gone_online.get(): self.gone_online.delete() self.gone_online.add() def timeout(self): if self.get and int(time.time()) - self.get < refresh_period * 3600: to = self.offline else: to = self.online # debug('[TimeOut]: '+str(to)) return to class ListDB: def __init__(self, version=1.0): self.dbname = 'list' + '.db3' dirname = xbmc.translatePath('special://temp') self.dbfilename = os.path.join(dirname, 'xbmcup', __settings__.getAddonInfo('id').replace('plugin://', '').replace('/', ''), self.dbname) self.version = version if not xbmcvfs.exists(self.dbfilename): self.creat_db() def creat_db(self): self._connect() self.cur.execute('pragma auto_vacuum=1') self.cur.execute('create table db_ver(version real)') # self.cur.execute('create table list(addtime integer PRIMARY KEY, title varchar(32), originaltitle varchar(32)' # ', year integer, category varchar(32), subcategory varchar(32))') self.cur.execute('create table list(addtime integer PRIMARY KEY, info varchar(32))') self.cur.execute('insert into db_ver(version) values(?)', (self.version,)) self.db.commit() self._close() def get(self, addtime): self._connect() self.cur.execute('select info from list where addtime="' + addtime + '"') x = self.cur.fetchone() self._close() return x[0] if x else None def get_all(self): self._connect() # self.cur.execute('select addtime,title,originaltitle,year,category,subcategory from list') self.cur.execute('select addtime,info from list') x = self.cur.fetchall() self._close() return x if x else None def add(self, url): self._connect() self.cur.execute('insert into list(addtime,info)' ' values(?,?)', (int(time.time()), url)) self.db.commit() self._close() def delete(self, addtime): self._connect() self.cur.execute('delete from list where addtime="' + addtime + '"') self.db.commit() self._close() def _connect(self): self.db = sqlite.connect(self.dbfilename) self.cur = self.db.cursor() def _close(self): self.cur.close() self.db.close() class HistoryDB: def __init__(self, version=1.1): self.name = 'history.db3' self.version = version def get_all(self): self._connect() self.cur.execute('select addtime,string,fav from history order by addtime DESC') x = self.cur.fetchall() self._close() return x if x else None def get(self, url): self._connect() self.cur.execute('select string from history where string="' + url + '"') x = self.cur.fetchone() self._close() return x[0] if x else None def get_providers(self, addtime): self._connect() self.cur.execute('select providers from history where addtime="' + addtime + '"') x = self.cur.fetchone() self._close() return x[0].split(',') if x and x[0] != '' else None def set_providers(self, addtime, providers): self._connect() if isinstance(providers, dict): temp = [] for i in providers.keys(): if providers.get(i): temp.append(i) providers = temp str_p = ','.join(providers) self.cur.execute('UPDATE history SET providers = "' + str_p + '" where addtime=' + addtime) self.db.commit() self._close() def change_providers(self, addtime, searcher): #self._connect() providers = self.get_providers(addtime) keys = Searchers().dic().keys() if providers and len(providers) > 0: if searcher in providers: providers.remove(searcher) else: providers.append(searcher) for i in providers: if i not in keys: providers.remove(i) self.set_providers(addtime, providers) #self.db.commit() #self._close() def add(self, url): self._connect() self.cur.execute('select fav from history where string="' + url + '"') x = self.cur.fetchone() if x: x = int(x[0]) fav = True if x else False if not fav: self.cur.execute('delete from history where string="' + decode(url) + '"') self.cur.execute('insert into history(addtime,string,fav,providers)' ' values(?,?,?,?)', (int(time.time()), decode(url), 0, "")) self.db.commit() self._close() def update(self, addtime, fav): self._connect() self.cur.execute('UPDATE history SET fav = ' + str(fav) + ' where addtime=' + addtime) self.db.commit() self._close() def fav(self, addtime): self.update(addtime, fav=1) def unfav(self, addtime): self.update(addtime, fav=0) def delete(self, addtime): self._connect() self.cur.execute('delete from history where addtime="' + addtime + '"') self.db.commit() self._close() def clear(self): self._connect() self.cur.execute('delete from history where fav=0') self.db.commit() self._close() def _connect(self): dirname = xbmc.translatePath('special://temp') for subdir in ('xbmcup', 'plugin.video.torrenter'): dirname = os.path.join(dirname, subdir) if not xbmcvfs.exists(dirname): xbmcvfs.mkdir(dirname) self.filename = os.path.join(dirname, self.name) first = False if not xbmcvfs.exists(self.filename): first = True self.db = sqlite.connect(self.filename, check_same_thread=False) if not first: self.cur = self.db.cursor() try: self.cur.execute('select version from db_ver') row = self.cur.fetchone() if not row or float(row[0]) != self.version: self.cur.execute('drop table history') self.cur.execute('drop table if exists db_ver') first = True self.db.commit() self.cur.close() except: self.cur.execute('drop table history') first = True self.db.commit() self.cur.close() if first: cur = self.db.cursor() cur.execute('pragma auto_vacuum=1') cur.execute('create table db_ver(version real)') cur.execute( 'create table history(addtime integer PRIMARY KEY, string varchar(32), providers varchar(32), fav integer)') cur.execute('insert into db_ver(version) values(?)', (self.version,)) self.db.commit() cur.close() self.cur = self.db.cursor() def _close(self): self.cur.close() self.db.close() class WatchedHistoryDB: def __init__(self, version=1.3): self.name = 'watched_history.db3' self.version = version self.history_bool = __settings__.getSetting('history') == 'true' def get_all(self): self._connect() self.cur.execute('select addtime,filename,foldername,path,url,seek,length,ind,size from history order by addtime DESC') x = self.cur.fetchall() self._close() return x if x else None def get(self, get, by, equal): self._connect() self.cur.execute('select '+get+' from history where '+by+'="' + equal + '"') x = self.cur.fetchone() self._close() return x if x else None def getbypathind(self, path, ind): self._connect() self.cur.execute('select seek from history where path="' + path + '" AND ind=' + ind) x = self.cur.fetchone() self._close() return x if x else None def add(self, filename, path, foldername = None, seek = 0, length = 1, ind = 0, size = 0): try: watchedPercent = int((float(seek) / float(length if length else 1)) * 100) except: watchedPercent = 0 max_history_add = int(__settings__.getSetting('max_history_add')) if self.history_bool and watchedPercent <= max_history_add: self._connect() url = __settings__.getSetting("lastTorrentUrl") #path = __settings__.getSetting("lastTorrent") if not foldername: foldername = '' self.cur.execute('delete from history where filename="' + decode(filename) + '"') self.cur.execute('insert into history(addtime,filename,foldername,path,url,seek,length,ind,size)' ' values(?,?,?,?,?,?,?,?,?)', (int(time.time()), decode(filename), decode(foldername), decode(path), decode(url), str(int(seek)), str(int(length)), str(ind), str(size))) self.db.commit() self._close() def update(self, what, to, by, equal): self._connect() self.cur.execute('UPDATE history SET '+what+' = ' + to + ' where '+by+'=' + equal) self.db.commit() self._close() def delete(self, addtime): self._connect() self.cur.execute('delete from history where addtime="' + addtime + '"') self.db.commit() self._close() def clear(self): self._connect() self.cur.execute('delete from history') self.db.commit() self._close() def _connect(self): dirname = xbmc.translatePath('special://temp') for subdir in ('xbmcup', 'plugin.video.torrenter'): dirname = os.path.join(dirname, subdir) if not xbmcvfs.exists(dirname): xbmcvfs.mkdir(dirname) self.filename = os.path.join(dirname, self.name) first = False if not xbmcvfs.exists(self.filename): first = True self.db = sqlite.connect(self.filename, check_same_thread=False) if not first: self.cur = self.db.cursor() try: self.cur.execute('select version from db_ver') row = self.cur.fetchone() if not row or float(row[0]) != self.version: self.cur.execute('drop table history') self.cur.execute('drop table if exists db_ver') first = True self.db.commit() self.cur.close() except: self.cur.execute('drop table history') first = True self.db.commit() self.cur.close() if first: cur = self.db.cursor() cur.execute('pragma auto_vacuum=1') cur.execute('create table db_ver(version real)') cur.execute( 'create table history(addtime integer PRIMARY KEY, filename varchar(32), foldername varchar(32), path varchar(32), url varchar(32), seek integer, length integer, ind integer, size integer)') cur.execute('insert into db_ver(version) values(?)', (self.version,)) self.db.commit() cur.close() self.cur = self.db.cursor() def _close(self): self.cur.close() self.db.close() class Searchers(): def __init__(self): pass def getBoolSetting(self, setting): return __settings__.getSetting(setting).lower() == "true" def setBoolSetting(self, setting, bool=True): __settings__.setSetting(setting, "true" if bool else "false") def list(self, only=None): searchersDict = {} if only!='external': searchers_dir = os.path.join(ROOT, 'resources', 'searchers') searchers_dirList=xbmcvfs.listdir(searchers_dir)[1] for searcherFile in searchers_dirList: if re.match('^(\w+)\.py$', searcherFile): name=searcherFile.replace('.py', '') searchersDict[name]={'name':name, 'path':searchers_dir, 'searcher':os.path.join(searchers_dir,name+'.py'), 'type':'local'} if only!='local': addons_dir = os.path.join(xbmc.translatePath('special://home'),'addons') addons_dirsList = xbmcvfs.listdir(addons_dir)[0] for searcherDir in addons_dirsList: #if len(searchersDict)>1: break if re.match('^torrenter\.searcher\.(\w+)$', searcherDir): name=searcherDir.replace('torrenter.searcher.', '') path=os.path.join(addons_dir, searcherDir) searchersDict[name]={'name':name, 'path':path, 'searcher':os.path.join(path,name+'.py'), 'type':'external'} return searchersDict def dic(self, providers=[]): dic = {} for searcher in self.list(): if not providers: dic[searcher] = self.old(searcher) else: dic[searcher] = searcher in providers return dic def old(self, searcher): if not self.getBoolSetting('old_' + searcher): self.setBoolSetting('old_' + searcher) self.setBoolSetting(searcher) return self.getBoolSetting(searcher) def get_active(self): get_active = [] for searcher in self.list().keys(): if self.old(searcher): get_active.append(searcher + '.py') log('Active Searchers: ' + str(get_active)) return get_active def searchWithSearcher(self, keyword, searcher): import traceback filesList = [] slist = self.list() if slist[searcher]['path'] not in sys.path: sys.path.insert(0, slist[searcher]['path']) log('Added %s in sys.path' % (slist[searcher]['path'])) try: searcherObject = getattr(__import__(searcher), searcher) filesList = searcherObject().search(keyword) del searcherObject except Exception, e: log('Unable to use searcher: ' + searcher + ' at ' + __plugin__ + ' searchWithSearcher(). Exception: ' + str(e)) log(traceback.format_exc()) return filesList def downloadWithSearcher(self, url, searcher): slist = Searchers().list() if slist[searcher]['path'] not in sys.path: sys.path.insert(0, slist[searcher]['path']) log('Added %s in sys.path' % (slist[searcher]['path'])) try: searcherObject = getattr(__import__(searcher), searcher)() url = searcherObject.getTorrentFile(url) except Exception, e: log('Unable to use searcher: ' + searcher + ' at ' + __plugin__ + ' downloadWithSearcher(). Exception: ' + str(e)) return url def checkExist(self, searcher): if searcher not in self.list(): yes=xbmcgui.Dialog().yesno('< %s >' % Localization.localize('Torrenter Tracker Install'), Localization.localize('Torrenter didn\'t find %s searcher' % searcher), Localization.localize('Would you like to install %s from "MyShows.me Kodi Repo" in Programs section?') % searcher,) if yes: xbmc.executebuiltin('Dialog.Close(all,true)') xbmc.executebuiltin('XBMC.ActivateWindow(Addonbrowser,addons://search/%s)' % ('Torrenter Searcher %s' % searcher)) def activeExternal(self): slist = [] for searcher in self.list('external').keys(): if self.old(searcher): slist.append(searcher) return slist def search(url, searchersList, isApi=None): from threading import Thread try: from Queue import Queue, Empty except ImportError: from queue import Queue, Empty num_threads=__settings__.getSetting('num_threads') if num_threads and int(num_threads)>0: num_threads = int(num_threads) else: num_threads = 3 queue = Queue() result = {} iterator, filesList, left_searchers = 0, [], [] timeout_multi=int(sys.modules["__main__"].__settings__.getSetting("timeout")) wait_time=10+(10*timeout_multi) left_searchers.extend(searchersList) if not isApi: progressBar = xbmcgui.DialogProgress() progressBar.create(Localization.localize('Please Wait'), Localization.localize('Materials are loading now.')) class CleanExit: pass def search_one(i, q): while True: try: if not isApi and progressBar.iscanceled(): progressBar.update(0) progressBar.close() return iterator=100*int(len(searchersList)-len(left_searchers))/len(searchersList) if not isApi: progressBar.update(int(iterator), join_list(left_searchers, replace='.py')) searcherFile = q.get_nowait() if searcherFile == CleanExit: return searcher=searcherFile.replace('.py','') log("Thread %s: Searching at %s" % (i, searcher)) result[searcherFile]=Searchers().searchWithSearcher(url, searcher) left_searchers.remove(searcherFile) q.task_done() except Empty: pass workers=[] for i in range(num_threads): worker = Thread(target=search_one, args=(i, queue)) worker.setDaemon(True) worker.start() workers.append(worker) for searcherFile in searchersList: queue.put(searcherFile) log("Main Thread Waiting") queue.join() for i in range(num_threads): queue.put(CleanExit) log("Main Thread Waiting for Threads") for w in workers: w.join() log("Done") if not isApi: progressBar.update(0) progressBar.close() for k in result.keys(): if result.get(k): filesList.extend(result[k]) return filesList def get_filesList(query, searchersList, addtime = None): if __settings__.getSetting('history')=='true': HistoryDB().add(query) filesList=search(query, searchersList) if __settings__.getSetting('sort_search')=='true': __settings__.setSetting('sort_search','1') if int(__settings__.getSetting('sort_search'))==0: filesList = sorted(filesList, key=lambda x: x[0], reverse=True) elif int(__settings__.getSetting('sort_search'))==2: filesList = sorted(filesList, key=lambda x: x[4], reverse=False) debug('get_filesList filesList: '+str(filesList)) return filesList def get_searchersList(addtime = None): searchersList = [] if addtime: providers=HistoryDB().get_providers(addtime) if providers: for searcher in providers: searchersList.append(searcher) if not addtime or not searchersList: searchersList = Searchers().get_active() debug('get_searchersList: '+str(searchersList)) return searchersList def get_contentList(url): import Downloader url = urllib.unquote_plus(url) debug('0' + __settings__.getSetting("lastTorrent")) __settings__.setSetting("lastTorrentUrl", url) classMatch = re.search('(\w+)::(.+)', url) if classMatch: searcher = classMatch.group(1) url = Searchers().downloadWithSearcher(classMatch.group(2), searcher) __settings__.setSetting("lastTorrent", url) torrent = Downloader.Torrent(userStorageDirectory, url, torrentFilesDirectory=torrentFilesDirectory) debug('1'+__settings__.getSetting("lastTorrent")) filename = torrent.saveTorrent(url) __settings__.setSetting("lastTorrent", filename) debug('2'+__settings__.getSetting("lastTorrent")) append_filesize = __settings__.getSetting("append_filesize") == 'true' contentList = [] for filedict in torrent.getContentList(): fileTitle = filedict.get('title') size = filedict.get('size') if size: if append_filesize: fileTitle += ' [%d MB]' % (size / 1024 / 1024) contentList.append([unescape(fileTitle), str(filedict.get('ind')), size]) # contentList = sorted(contentList, key=lambda x: x[0]) debug('get_contentList contentList: ' + str(contentList)) return contentList, filename def join_list(l, char=', ', replace=''): string='' for i in l: string+=i.replace(replace,'')+char return string.rstrip(' ,') class Contenters(): def __init__(self): pass def first_time(self, scrapperDB_ver, language='ru'): from resources.scrapers.scrapers import Scrapers if language not in ['en','ru','he']: language = 'ru' searcher = 'metadata' redl = False scrapperDB_ver = scrapperDB_ver[language] if scrapperDB_ver != __settings__.getSetting('scrapperDB_ver' + language) and self.getBoolSetting(searcher): __settings__.setSetting('scrapperDB_ver' + language, scrapperDB_ver) ok = xbmcgui.Dialog().yesno('< %s >' % Localization.localize('Content Lists'), Localization.localize('Your preloaded databases are outdated!'), Localization.localize('Do you want to download new ones right now?')) if ok: dirname = xbmc.translatePath('special://temp') dirname = os.path.join(dirname, 'xbmcup', 'plugin.video.torrenter') scrapers = {'tvdb': 'TheTVDB.com', 'tmdb': 'TheMovieDB.org', 'kinopoisk': 'KinoPoisk.ru'} for i in scrapers.keys(): xbmcvfs.delete(os.path.join(dirname, i + '.' + language + '.db')) showMessage(Localization.localize('Reset All Cache DBs'), Localization.localize('Deleted!')) redl = True else: xbmcgui.Dialog().ok('< %s >' % Localization.localize('Content Lists'), Localization.localize( 'You can always restart this by deleting DBs via Context Menu'), ) if not self.getBoolSetting('oldc_' + searcher + language): self.setBoolSetting('oldc_' + searcher + language, True) __settings__.setSetting('scrapperDB_ver' + language, scrapperDB_ver) ok = xbmcgui.Dialog().yesno('< %s >' % Localization.localize('Content Lists'), Localization.localize('Do you want to search and cache full metadata + arts?'), Localization.localize( 'This vastly decreases load speed, but you will be asked to download premade bases!')) if ok: self.setBoolSetting(searcher, True) redl = True else: self.setBoolSetting(searcher, False) if redl: self.Scraper = Scrapers() scrapers = {'tvdb': 'TheTVDB.com', 'tmdb': 'TheMovieDB.org', 'kinopoisk': 'KinoPoisk.ru'} for scraper in scrapers.keys(): if scraper != 'kinopoisk' or language == 'ru': self.Scraper.scraper(scraper, {'label': 'Мстители', 'search': [u'Мстители', u'The Avengers'], 'year': 2012}, language) def getBoolSetting(self, setting): return __settings__.getSetting(setting).lower() == "true" def setBoolSetting(self, setting, bool=True): __settings__.setSetting(setting, "true" if bool else "false") def list(self): searchersList = [] dirList = os.listdir(ROOT + os.sep + 'resources' + os.sep + 'contenters') for searcherFile in dirList: if re.match('^(\w+)\.py$', searcherFile) and searcherFile != '__init__.py': searchersList.append(searcherFile.replace('.py', '')) return searchersList def dic(self): dic = {} for searcher in self.list(): dic[searcher] = self.old(searcher) return dic def get_activedic(self): dic = {} # for searcher in self.list(): # if self.old(searcher): dic[searcher]=(searcher,) for searcher in self.list(): dic[searcher] = (searcher,) return dic def old(self, searcher): if not self.getBoolSetting('oldc_' + searcher): self.setBoolSetting('oldc_' + searcher) self.setBoolSetting(searcher) return self.getBoolSetting(searcher) def get_active(self): get_active = [] for searcher in self.list(): if self.old(searcher): get_active.append(searcher) return get_active def countSeasons(jdata): seasons, epdict = [], {} for id in jdata['episodes']: seasonNumber = jdata['episodes'][id]['seasonNumber'] if seasonNumber not in seasons: seasons.append(seasonNumber) if jdata['episodes'][id]['episodeNumber']: if str(jdata['episodes'][id]['seasonNumber']) not in epdict: epdict[str(jdata['episodes'][id]['seasonNumber'])] = str(jdata['episodes'][id]['id']) else: epdict[str(jdata['episodes'][id]['seasonNumber'])] = epdict[str( jdata['episodes'][id]['seasonNumber'])] + ',' + str(jdata['episodes'][id]['id']) seasons.sort() return seasons, epdict def fetchData(url, referer=None): request = urllib2.Request(url) if referer != None: request.add_header('Referer', referer) request.add_header('User-Agent', USERAGENT) if __settings__.getSetting("auth"): authString = '; ' + __settings__.getSetting("auth") else: authString = '' request.add_header('Cookie', authString) try: connection = urllib2.urlopen(request) result = connection.read() connection.close() return (result) except (urllib2.HTTPError, urllib2.URLError) as e: log(" fetchData(" + url + ") exception: " + str(e)) return def file_decode(filename): pass try: filename = filename.decode('utf-8') # ,'ignore') except: pass return filename def file_encode(filename): if sys.getfilesystemencoding() == 'mbcs' and isAsciiString(filename): filename = filename.decode('cp1251').encode('utf-8') return filename def isAsciiString(mediaName): for index, char in enumerate(mediaName): if ord(char) >= 128: return False return True def getParameters(parameterString): commands = {} splitCommands = parameterString[parameterString.find('?') + 1:].split('&') for command in splitCommands: if (len(command) > 0): splitCommand = command.split('=') if (len(splitCommand) > 1): name = splitCommand[0] value = splitCommand[1] commands[name] = value return commands def isSubtitle(filename, filename2): filename_if = filename[:len(filename) - len(filename.split('.')[-1]) - 1] filename_if = filename_if.split('/')[-1].split('\\')[-1] filename_if2 = filename2.split('/')[-1].split('\\')[-1][:len(filename_if)] # debug('Compare ' + filename_if.lower() + ' and ' + filename_if2.lower() + ' and ' + filename2.lower().split('.')[-1]) ext = ['ass', 'mpsub', 'rum', 'sbt', 'sbv', 'srt', 'ssa', 'sub', 'sup', 'w32'] if filename2.lower().split('.')[-1] in ext and \ filename_if.lower() == filename_if2.lower(): return True return False def delete_russian(ok=False, action='delete'): i = 0 if not ok: ok = xbmcgui.Dialog().yesno('< %s >' % Localization.localize('International Check - First Run'), 'Delete Russian stuff?', Localization.localize('Delete Russian stuff?')) if ok: fileList = { 'contenters': ['CXZ.py', 'FastTorrent.py', 'KinoPoisk.py', 'RiperAM.py'], 'searchers': ['NNMClubRu.py', 'OpenSharing.py', 'RiperAM.py', 'RuTorOrg.py', 'RuTrackerOrg.py', 'TFileME.py'] } for path in fileList.keys(): for filename in fileList[path]: if action == 'delete': filepath = os.path.join(ROOT, 'resources', path, filename) if xbmcvfs.exists(filepath): newfilepath = os.path.join(ROOT, 'resources', path, 'unused', filename) xbmcvfs.copy(filepath, newfilepath) xbmcvfs.delete(filepath) elif action == 'return': filepath = os.path.join(ROOT, 'resources', path, 'unused', filename) if xbmcvfs.exists(filepath): newfilepath = os.path.join(ROOT, 'resources', path, filename) xbmcvfs.copy(filepath, newfilepath) xbmcvfs.delete(filepath) i = i + 1 if action == 'return': return i return True else: return False class DownloadDB: def __init__(self, version=1.41): self.name = 'download.db3' self.version = version def get_all(self): self._connect() self._execute( 'select addtime, title, path, type, jsoninfo, status, torrent, ind, lastupdate, storage from downloads order by addtime DESC') x = self.cur.fetchall() self._close() return x if x else None def get(self, title): self._connect() self._execute( 'select addtime, title, path, type, jsoninfo, status, torrent, ind, lastupdate, storage from downloads where title="' + decode( title) + '"') x = self.cur.fetchone() self._close() return x if x else None def get_byaddtime(self, addtime): self._connect() self._execute( 'select addtime, title, path, type, jsoninfo, status, torrent, ind, lastupdate, storage from downloads where addtime="' + str( addtime) + '"') x = self.cur.fetchone() self._close() return x if x else None def _execute(self, sql): try: self.cur.execute(sql) except Exception, e: if str(e)=='no such table: downloads': cur = self.db.cursor() cur.execute('pragma auto_vacuum=1') cur.execute( 'create table downloads(addtime integer PRIMARY KEY, title varchar(32), path varchar(32), type varchar(32), jsoninfo varchar(32), status varchar(32), torrent varchar(32), ind integer, lastupdate integer, storage varchar(32))') self.db.commit() cur.close() self.cur = self.db.cursor() else: self._close() debug('[DownloadDB]: DELETE ' + str(self.filename)) xbmcvfs.delete(self.filename) self._connect() self.cur.execute(sql) def get_status(self, title): self._connect() self._execute('select status from downloads where title="' + decode(title) + '"') x = self.cur.fetchone() self._close() return x[0] if x else None def add(self, title, path, type, info, status, torrent, ind, storage): if not self.get(title): self._connect() self.cur.execute( 'insert into downloads(addtime, title, path, type, jsoninfo, status, torrent, ind, lastupdate, storage)' ' values(?,?,?,?,?,?,?,?,?,?)', ( int(time.time()), decode(title), decode(path), type, json.dumps(info), status, decode(torrent), ind, int(time.time()), decode(storage))) self.db.commit() self._close() return True else: return False def update(self, title, info={}): try: title = title.decode('utf-8') except: pass self._connect() self._execute( 'UPDATE downloads SET jsoninfo = "' + urllib.quote_plus(json.dumps(info)) + '", lastupdate=' + str( int(time.time())) + ' where title="' + title + '"') self.db.commit() self._close() def update_status(self, addtime, status): self._connect() self._execute('UPDATE downloads SET status = "' + status + '" where addtime="' + str(addtime) + '"') self.db.commit() self._close() def delete(self, addtime): self._connect() self._execute('delete from downloads where addtime="' + str(addtime) + '"') self.db.commit() self._close() def clear(self): self._connect() self._execute('delete from downloads') self.db.commit() self._close() def _connect(self): dirname = xbmc.translatePath('special://temp') for subdir in ('xbmcup', 'plugin.video.torrenter'): dirname = os.path.join(dirname, subdir) if not xbmcvfs.exists(dirname): xbmcvfs.mkdir(dirname) self.filename = os.path.join(dirname, self.name) first = False if not xbmcvfs.exists(self.filename): first = True self.db = sqlite.connect(self.filename, check_same_thread=False) if not first: self.cur = self.db.cursor() try: self.cur.execute('select version from db_ver') row = self.cur.fetchone() if not row or float(row[0]) != self.version: self.cur.execute('drop table downloads') self.cur.execute('drop table if exists db_ver') first = True self.db.commit() self.cur.close() except: try: self.cur.execute('drop table downloads') first = True self.db.commit() self.cur.close() except: self.cur.close() self.db.close() os.remove(self.filename) first = True self.db = sqlite.connect(self.filename, check_same_thread=False) if first: cur = self.db.cursor() cur.execute('pragma auto_vacuum=1') cur.execute('create table db_ver(version real)') cur.execute( 'create table downloads(addtime integer PRIMARY KEY, title varchar(32), path varchar(32), type varchar(32), jsoninfo varchar(32), status varchar(32), torrent varchar(32), ind integer, lastupdate integer, storage varchar(32))') cur.execute('insert into db_ver(version) values(?)', (self.version,)) self.db.commit() cur.close() self.cur = self.db.cursor() def _close(self): self.cur.close() self.db.close() def decode(string, ret=None): try: string = string.decode('utf-8') return string except: if ret: return ret else: return string def unquote(string, ret=None): try: return urllib.unquote_plus(string) except: if ret: return ret else: return string def itemScrap(item, kwarg): # debug('[itemTVDB]:meta '+str(kwarg)) if 'title' in kwarg and kwarg['title']: item.setLabel(kwarg['title']) if 'label' in kwarg and kwarg['label']: item.setLabel2(kwarg['label']) if 'icon' in kwarg and kwarg['icon']: item.setIconImage(kwarg['icon']) if 'thumbnail' in kwarg and kwarg['thumbnail']: item.setThumbnailImage(kwarg['thumbnail']) if 'properties' in kwarg and kwarg['properties']: for key, value in kwarg['properties'].iteritems(): item.setProperty(key, str(value)) if 'info' in kwarg and kwarg['properties']: item.setInfo(type='Video', infoLabels=kwarg['info']) return item def get_ids_video(contentList): ids_video = [] allowed_video_ext = ['avi', 'mp4', 'mkv', 'flv', 'mov', 'vob', 'wmv', 'ogm', 'asx', 'mpg', 'mpeg', 'avc', 'vp3', 'fli', 'flc', 'm4v', 'iso'] allowed_music_ext = ['mp3', 'flac', 'wma', 'ogg', 'm4a', 'aac', 'm4p', 'rm', 'ra'] for extlist in [allowed_video_ext, allowed_music_ext]: for item in contentList: title = item[0] identifier = item[1] try: ext = title.split('.')[-1] if ext.lower() in extlist: ids_video.append(str(identifier)) except: pass if len(ids_video) > 1: break # print debug('[get_ids_video]:'+str(ids_video)) return ids_video def first_run_230(delete_russian): if not __settings__.getSetting('first_run_230')=='True': __settings__.setSetting('first_run_230','True') if not delete_russian: yes=xbmcgui.Dialog().yesno('< %s >' % Localization.localize('Torrenter Update 2.3.0'), Localization.localize('Would you like to install %s from "MyShows.me Kodi Repo" in Programs section?') % 'RuTrackerOrg', Localization.localize('Open installation window?')) if yes: xbmc.executebuiltin('Dialog.Close(all,true)') xbmc.executebuiltin('XBMC.ActivateWindow(Addonbrowser,addons://search/%s)' % ('Torrenter Searcher')) def first_run_231(): if not __settings__.getSetting('first_run_231')=='True': __settings__.setSetting('first_run_231','True') ok = xbmcgui.Dialog().ok('< %s >' % Localization.localize('Torrenter Update 2.3.1'), Localization.localize('We added Android ARM full support to Torrenter v2!'), Localization.localize('I deleted pre-installed ones, install them in Search Control Window!')) yes=xbmcgui.Dialog().yesno('< %s >' % Localization.localize('Torrenter Update 2.3.1'), Localization.localize('You have no installed or active searchers! More info in Search Control Window!'), Localization.localize('Would you like to install %s from "MyShows.me Kodi Repo" in Programs section?') % '',) if yes: xbmc.executebuiltin('Dialog.Close(all,true)') xbmc.executebuiltin('XBMC.ActivateWindow(Addonbrowser,addons://search/%s)' % ('Torrenter Searcher')) def first_run_250(): if __settings__.getSetting('torrent_player')=='3': __settings__.setSetting('first_run_250','True') if not __settings__.getSetting('first_run_250')=='True': __settings__.setSetting('first_run_250','True') yes=xbmcgui.Dialog().yesno('< %s >' % (Localization.localize('Torrenter Update ') + '2.5.0'), Localization.localize('New player to Torrenter v2 - pyrrent2http! Advantages of Torrent2HTTP ' 'but with python-libtorrent library instead of libtorrent-go!'), Localization.localize('Would you like to try it?'),) if yes: __settings__.openSettings() #__settings__.setSetting('torrent_player','2') #ok = xbmcgui.Dialog().ok('< %s >' % (Localization.localize('Torrenter Update ') + '2.4.2'), # Localization.localize('Torrent2HTTP enabled! Can be changed in Settings.')) def first_run_260(): if not __settings__.getSetting('first_run_260') == 'True': yes=xbmcgui.Dialog().yesno('< %s >' % (Localization.localize('Torrenter Update ') + '2.6.0'), Localization.localize('Torrenter Search Window')+' ' +Localization.localize('is recommended for Kodi 17 users and now out of beta.') +Localization.localize('You can disable it usage in Settings.'), Localization.localize('Would you like to try it?'),) if yes: import searchwindow searchwindow.main() def estuary(): if __settings__.getSetting('skin_optimization') not in ['7', '0'] and \ __settings__.getSetting('ask17_skin_optimization') != 'True': yes = xbmcgui.Dialog().yesno('< %s >' % (Localization.localize('Torrenter Update ') + '2.6.0'), Localization.localize('Torrenter has a better view style for Kodi 17 default skin.'), Localization.localize('Would you like to try it?'), ) if yes: __settings__.setSetting('skin_optimization', '7') __settings__.setSetting('ask_skin_optimization', 'True') def seeking_warning(seek): if __settings__.getSetting('torrent_player')!='1': seek_point = '%02d:%02d:%02d' % ((seek / (60*60)), (seek / 60) % 60, seek % 60) yes=xbmcgui.Dialog().yesno('< %s >' % (Localization.localize('Seeking')), Localization.localize('Would you like to resume from %s?') % seek_point,) if yes: log('[seeking_warning]: yes, seek = '+str(seek)) return seek else: log('[seeking_warning]: no, seek = '+str(0)) return 0 else: if not __settings__.getSetting('seeking_warning')=='True': __settings__.setSetting('seeking_warning','True') yes=xbmcgui.Dialog().yesno('< %s >' % (Localization.localize('Seeking')), Localization.localize('Seeking is working only with player Torrent2HTTP.'), Localization.localize('Would you like to try it?')) if yes: __settings__.openSettings() #__settings__.setSetting('torrent_player','2') #ok = xbmcgui.Dialog().ok('< %s >' % (Localization.localize('Seeking')), # Localization.localize('Torrent2HTTP enabled! Can be changed in Settings.')) return seek def noActiveSerachers(): yes=xbmcgui.Dialog().yesno('< %s >' % Localization.localize('Torrenter v2'), Localization.localize('You have no installed or active searchers! More info in Search Control Window!'), Localization.localize('Would you like to install %s from "MyShows.me Kodi Repo" in Programs section?') % '',) if yes: xbmc.executebuiltin('Dialog.Close(all,true)') xbmc.executebuiltin('XBMC.ActivateWindow(Addonbrowser,addons://search/%s)' % ('Torrenter Searcher')) def windows_check(): import platform """ Checks if the current platform is Windows :returns: True or False :rtype: bool """ return platform.system() in ('Windows', 'Microsoft') def vista_check(): import platform """ Checks if the current platform is Windows Vista :returns: True or False :rtype: bool """ return platform.release() == "Vista" def is_writable(path): if not xbmcvfs.exists(path+os.sep): xbmcvfs.mkdirs(path) try: open(os.path.join(file_decode(path), 'temp'), 'w') except: return False else: os.remove(os.path.join(file_decode(path), 'temp')) return True def unescape(string): htmlCodes = ( ('&', '&'), ('<', '<'), ('>', '>'), ('"', '"'), ("'", '''), ) for (symbol, code) in htmlCodes: string = re.sub(code, symbol, string) return string def stripHtml(string): stripPairs = ( ('

', '\n'), ('

  • ', '\n'), ('
    ', '\n'), ('<.+?>', ' '), ('', ' '), (' ', ' '), ('«', '"'), ('»', '"'), ) for (html, replacement) in stripPairs: string = re.sub(html, replacement, string) return string def chooseFile(filelist): myshows_items, myshows_files, contentList, myshows_sizes = [], [], [], {} for filedict in filelist: fileTitle = '' if filedict.get('size'): myshows_sizes[str(filedict.get('ind'))]='[%d MB] ' % (filedict.get('size') / 1024 / 1024) title = filedict.get('title') fileTitle = fileTitle + '[%s]%s' % (title[len(title) - 3:], title) contentList.append((unescape(fileTitle), str(filedict.get('ind')))) contentList = sorted(contentList, key=lambda x: x[0]) EXTS=['avi','mp4','mkv','flv','mov','vob','wmv','ogm','asx','mpg','mpeg','avc','vp3','fli','flc','m4v','iso','mp3'] for title, identifier in contentList: try: if title.split('.')[-1].lower() in EXTS: myshows_items.append(title) myshows_files.append(identifier) except: pass if len(myshows_items) > 1: if len(myshows_sizes)==0: myshows_items = cutFileNames(myshows_items) else: myshows_cut = cutFileNames(myshows_items) myshows_items=[] x=-1 for i in myshows_files: x=x+1 fileTitle=myshows_sizes[str(i)]+myshows_cut[x] myshows_items.append(fileTitle) log('[chooseFile]: myshows_items '+str(myshows_items)) if len(myshows_items) == 1: ret = 0 else: ret = xbmcgui.Dialog().select(Localization.localize('Search results:'), myshows_items) if ret > -1: return myshows_files[ret] def check_network_advancedsettings(): path=xbmc.translatePath('special://profile/advancedsettings.xml') updated=False #path='''C:\\Users\\Admin\\AppData\\Roaming\\Kodi\\userdata\\advancedsettings.xml''' settings={'buffermode':2, 'curlclienttimeout':30, 'cachemembuffersize':252420, 'readbufferfactor':5.0} add, update = {}, {} if not os.path.exists(path): updated=True file_cont=''' 2 30 252420 5 ''' else: with open(path) as f: file_cont=f.read() log('[check_network_advancedsettings]: old file_cont '+str(file_cont)) if not updated and not re.search('.+?', file_cont, re.DOTALL): updated=True file_cont=file_cont.replace('', ''' 2 30 252420 5 ''') elif not updated: for key in settings.keys(): search=re.search('<'+key+'>(.+?)', file_cont, re.DOTALL) if not search: add[key]=settings[key] else: present_value=search.group(1) if key == 'buffermode' and int(present_value)==3: update[key]=settings[key] elif key in ['curlclienttimeout', 'cachemembuffersize'] and int(present_value)0 or len(update)>0: updated=True for key in add.keys(): file_cont=file_cont.replace('','\r\n <'+key+'>'+str(add[key])+'') for key in update.keys(): file_cont=re.sub(r'<'+key+'>.+?', '<'+key+'>'+str(update[key])+'', file_cont) if updated: dialog=xbmcgui.Dialog() ok=dialog.yesno(Localization.localize('Upgrade advancedsettings.xml'), Localization.localize('We would like to set some advanced settings for you!'), Localization.localize('Do it!')) if ok: log('[check_network_advancedsettings]: new file_cont '+str(file_cont)) f=open(path, mode='w') f.write(file_cont) f.close() dialog.ok(Localization.localize('Upgrade advancedsettings.xml'), Localization.localize('Please, restart Kodi now!')) log('Restart Kodi') else: log('UPDATE advancedsettings.xml disabled by user!') def get_download_dir(): import tempfile platform = get_platform() dialog=xbmcgui.Dialog() dialog.ok(Localization.localize('Torrenter'), Localization.localize('Please specify storage folder in Settings!')) # try: if not platform['system']=='android': download_dir = tempfile.gettempdir() else: download_dir = tempdir() except: download_dir = tempdir() return download_dir def check_download_dir(): if len(__settings__.getSetting("storage"))==0: dialog=xbmcgui.Dialog() dialog.ok(Localization.localize('Torrenter'), Localization.localize('Please specify storage folder in Settings!')) __settings__.openSettings() def ensure_str(string, encoding='utf-8'): if isinstance(string, unicode): string = string.encode(encoding) if not isinstance(string, str): string = str(string) return string def file_url(torrentFile): import urlparse torrentFile = ensure_str(torrentFile) if not re.match("^file\:.+$", torrentFile): torrentFile = urlparse.urljoin('file:', urllib.pathname2url(torrentFile)) return torrentFile def dump(obj): for attr in dir(obj): try: log("'%s':'%s'," % (attr, getattr(obj, attr))) except: pass def getDirectorySizeInBytes(directory): dir_size = 0 for (path, dirs, files) in os.walk(directory): for file in files: filename = os.path.join(path, file) try: dir_size += os.path.getsize(filename) except: pass return dir_size def getDirectorySizeInGB(directory): dir_size = int(getDirectorySizeInBytes(directory)/1024/1024/1024) return dir_size def foldername(path): if '\\' in path: foldername = path.split('\\')[0] else: foldername = '' return foldername def uri2path(uri): import urlparse if uri[1] == ':' and sys.platform.startswith('win'): uri = 'file:///' + uri fileUri = urlparse.urlparse(uri) if fileUri.scheme == 'file': uriPath = fileUri.path if uriPath != '' and sys.platform.startswith('win') and (os.path.sep == uriPath[0] or uriPath[0] == '/'): uriPath = uriPath[1:] absPath = os.path.abspath(urllib.unquote(uriPath)) return localize_path(absPath) def localize_path(path): import chardet if not isinstance(path, unicode): try: path = path.decode(chardet.detect(path).get('encoding') or 'utf-8') except: pass if not sys.platform.startswith('win'): path = encode_msg(path) return path def encode_msg(msg): try: msg = isinstance(msg, unicode) and msg.encode( (sys.getfilesystemencoding() not in ('ascii', 'ANSI_X3.4-1968')) and sys.getfilesystemencoding() or 'utf-8') or msg except: import traceback log(traceback.format_exc()) msg = ensure_str(msg) return msg def decode_str(string, encoding='utf-8'): if not isinstance(string, unicode): string = string.decode(encoding) return string def get_platform(): ret = { "arch": sys.maxsize > 2 ** 32 and "x64" or "x86", } if xbmc.getCondVisibility("system.platform.android"): ret["os"] = "android" if "arm" in os.uname()[4] or "aarch64" in os.uname()[4]: ret["arch"] = "arm" elif xbmc.getCondVisibility("system.platform.linux"): ret["os"] = "linux" uname=os.uname()[4] if "arm" in uname: if "armv7" in uname: ret["arch"] = "armv7" elif "armv6" in uname: ret["arch"] = "armv6" else: ret["arch"] = "arm" elif "mips" in uname: if sys.maxunicode > 65536: ret["arch"] = 'mipsel_ucs4' else: ret["arch"] = 'mipsel_ucs2' elif xbmc.getCondVisibility("system.platform.windows"): ret["os"] = "windows" elif xbmc.getCondVisibility("system.platform.osx"): ret["os"] = "darwin" elif xbmc.getCondVisibility("system.platform.ios"): ret["os"] = "ios" ret["arch"] = "arm" return ret def getTorrentClientIcon(): client = __settings__.getSetting("torrent") if client == '1': return 'transmission.png' elif client == '2': return 'vuze.png' elif client == '3': return 'deluge.png' elif client == '4': return 'qbittorrent.png' else: return 'torrent-client.png' def get_item(): #some plugin.video.quasar magic item = xbmcgui.ListItem( path='', label=xbmc.getInfoLabel("ListItem.Label"), label2=xbmc.getInfoLabel("ListItem.label2"), thumbnailImage=xbmc.getInfoLabel("ListItem.Art(thumb)")) _infoLabels = { "Title": xbmc.getInfoLabel("ListItem.Title"), "OriginalTitle": xbmc.getInfoLabel("ListItem.OriginalTitle"), "TVShowTitle": xbmc.getInfoLabel("ListItem.TVShowTitle"), "Season": xbmc.getInfoLabel("ListItem.Season"), "Episode": xbmc.getInfoLabel("ListItem.Episode"), "Premiered": xbmc.getInfoLabel("ListItem.Premiered"), "Plot": xbmc.getInfoLabel("ListItem.Plot"), # "Date": xbmc.getInfoLabel("ListItem.Date"), "VideoCodec": xbmc.getInfoLabel("ListItem.VideoCodec"), "VideoResolution": xbmc.getInfoLabel("ListItem.VideoResolution"), "VideoAspect": xbmc.getInfoLabel("ListItem.VideoAspect"), "DBID": xbmc.getInfoLabel("ListItem.DBID"), "DBTYPE": xbmc.getInfoLabel("ListItem.DBTYPE"), "Writer": xbmc.getInfoLabel("ListItem.Writer"), "Director": xbmc.getInfoLabel("ListItem.Director"), "Rating": xbmc.getInfoLabel("ListItem.Rating"), "Votes": xbmc.getInfoLabel("ListItem.Votes"), "IMDBNumber": xbmc.getInfoLabel("ListItem.IMDBNumber"), } infoLabels = {} for key, value in _infoLabels.iteritems(): if value: infoLabels[key] = value poster = xbmc.getInfoLabel("ListItem.Art(poster)") if not poster: poster = xbmc.getInfoLabel("ListItem.Art(tvshow.poster)") item.setArt({ "poster": poster, "banner": xbmc.getInfoLabel("ListItem.Art(banner)"), "fanart": xbmc.getInfoLabel("ListItem.Art(fanart)") }) item.setInfo(type='Video', infoLabels=infoLabels) return item def loadsw_onstop(): if __settings__.getSetting('loadsw_onstop') == 'true': import searchwindow params = {'mode': 'load'} searchwindow.main(params) def watched_seek(filename, ind): db = WatchedHistoryDB() seek = db.getbypathind(filename, ind) log('[watched_seek] seek - '+str(seek)) if seek: seek = seek[0] seek = int(seek) if int(seek) > 3 * 60 else 0 if seek > 0: seek_text = '%02d:%02d:%02d' % ((seek / (60 * 60)), (seek / 60) % 60, seek % 60) dialog_items = [Localization.localize('Play (from %s)') % seek_text, Localization.localize('Play (from start)')] ret = xbmcgui.Dialog().select(Localization.localize('Play (with seek)'), dialog_items) if ret == 0: return str(seek) return '0'