diff --git a/.idea/misc.xml b/.idea/misc.xml index 5ca5e8e..2c3cc30 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,4 @@ - - - + + \ No newline at end of file diff --git a/.idea/plugin.video.torrenter.iml b/.idea/plugin.video.torrenter.iml index a34a857..ca32dfa 100644 --- a/.idea/plugin.video.torrenter.iml +++ b/.idea/plugin.video.torrenter.iml @@ -2,8 +2,7 @@ - + - - + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 8e0ab87..dd8ecf9 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,12 +1,20 @@ - + - - - - + + + + + + + + + + + + @@ -24,7 +32,7 @@ - + @@ -33,61 +41,258 @@ - + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -96,33 +301,118 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -131,545 +421,423 @@ - - + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -678,61 +846,62 @@ - - + + - - - - - - - - - - - - - - - - - - - - - + + - - + + + + + + + + + + + + + + - - + + - - - - - + + + - - + + - - + + + + + + + + + + + + @@ -749,6 +918,53 @@ + + + navi_save + last_link + self.last_right_button + self.last_top_button + navi_restore + traceback + self.filesList + self.navi_back + torrent_moveup + last_listing_item + self.navi['addtime'] + self.button_keyboard + back + route + if self.listing.size(): + navi_route + rutor- + __main__ + url_after + log( + debug + torrentPlayer + json + onPlayBackStopped + play + xbmc.P + loadsw_onstop + setSetting + ettings__.setSetting('loadsw_onstop' + self.close + + + pyxbmct.Button( + button_history + button_right1 + self.right_press1 + self.progressBar + action + __settings__ + get + self.navi['filesList'] + self.route + debug( + + @@ -760,57 +976,57 @@ @@ -820,16 +1036,7 @@ - - - - - - - - - - + @@ -845,6 +1052,7 @@ + @@ -852,89 +1060,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - + + + + @@ -1113,350 +1266,350 @@ @@ -1470,15 +1623,15 @@ - + - + - + + - - - + + @@ -1487,13 +1640,31 @@ - + - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - + - - + + - + - - + + + + + + + + + + + + - - + + - + - - + + - + + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - + + - + - - - + + - + - - + + - + - - + + - + - - + + - - - - - - - - - - - - - - - + - + - + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/AceStream.py b/AceStream.py index 8d7137f..4c91f2b 100644 --- a/AceStream.py +++ b/AceStream.py @@ -25,10 +25,10 @@ import hashlib import re import base64 from StringIO import StringIO -import gzip +import zlib from functions import file_decode, file_encode -from functions import magnet_alert, log, debug +from functions import magnet_alert, log, loadsw_onstop import xbmcvfs @@ -81,6 +81,7 @@ class AceStream: def __exit__(self): self.TSplayer.end() + loadsw_onstop() # Reload Search Window def play_url_ind(self, ind, label, icon): self.TSplayer.play_url_ind(int(ind), label, str(icon), '') @@ -93,19 +94,12 @@ class AceStream: torrentFile = self.storageDirectory + os.sep + self.torrentFilesDirectory + os.sep + self.md5( torrentUrl) + '.torrent' try: - if not re.match("^http\:.+$", torrentUrl): - content = xbmcvfs.File(file_decode(torrentUrl), "rb").read() + if not re.match("^[htps]+?://.+$|^://.+$", torrentUrl): + log('xbmcvfs.File for %s' % torrentUrl) + content = xbmcvfs.File(torrentUrl, "rb").read() else: - request = urllib2.Request(torrentUrl) - request.add_header('Referer', torrentUrl) - request.add_header('Accept-encoding', 'gzip') - result = urllib2.urlopen(request) - if result.info().get('Content-Encoding') == 'gzip': - buf = StringIO(result.read()) - f = gzip.GzipFile(fileobj=buf) - content = f.read() - else: - content = result.read() + log('request for %s' % torrentUrl) + content = self.makeRequest(torrentUrl) localFile = xbmcvfs.File(torrentFile, "w+b") localFile.write(content) @@ -119,6 +113,29 @@ class AceStream: self.torrentFileInfo = self.TSplayer.load_torrent(base64.b64encode(content), 'RAW') return self.torrentFile + def makeRequest(self, torrentUrl): + torrentUrl = re.sub('^://', 'http://', torrentUrl) + x = re.search("://(.+?)/|://(.+?)$", torrentUrl) + if x: + baseurl = x.group(1) if x.group(1) else x.group(2) + else: + baseurl ='' + + headers = [('User-Agent', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 YaBrowser/14.10.2062.12061 Safari/537.36'), + ('Referer', 'http://%s/' % baseurl), ('Accept-encoding', 'gzip'), ] + + opener = urllib2.build_opener() + opener.addheaders = headers + result = opener.open(torrentUrl) + if result.info().get('Content-Encoding') == 'gzip': + buf = StringIO(result.read()) + decomp = zlib.decompressobj(16 + zlib.MAX_WBITS) + content = decomp.decompress(buf.getvalue()) + else: + content = result.read() + return content + def magnetToTorrent(self, magnet): try: from SkorbaLoader import SkorbaLoader diff --git a/Anteoloader.py b/Anteoloader.py index 4eae5fa..6b50e07 100644 --- a/Anteoloader.py +++ b/Anteoloader.py @@ -30,7 +30,7 @@ import xbmcgui import xbmcvfs import xbmcaddon import Localization -from functions import file_encode, isSubtitle, DownloadDB, log, debug, is_writable, unquote, file_url +from functions import localize_path, isSubtitle, loadsw_onstop, is_writable, file_url import os @@ -134,7 +134,7 @@ class AnteoLoader: dht_routers = ["router.bittorrent.com:6881", "router.utorrent.com:6881"] user_agent = 'uTorrent/2200(24683)' - self.engine = Engine(uri=file_url(self.torrentFile), download_path=self.storageDirectory, + self.engine = Engine(uri=file_url(localize_path(self.torrentFile)), download_path=self.storageDirectory, connections_limit=connections_limit, encryption=encryption, keep_complete=keep_complete, keep_incomplete=keep_incomplete, dht_routers=dht_routers, use_random_port=use_random_port, listen_port=listen_port, @@ -200,19 +200,12 @@ class AnteoLoader: if not xbmcvfs.exists(self.torrentFilesPath): xbmcvfs.mkdirs(self.torrentFilesPath) torrentFile = os.path.join(self.torrentFilesPath, self.md5(torrentUrl) + '.torrent') try: - if not re.match("^http\:.+$", torrentUrl): + if not re.match("^[htps]+?://.+$|^://.+$", torrentUrl): + log('xbmcvfs.File for %s' % torrentUrl) content = xbmcvfs.File(torrentUrl, "rb").read() else: - request = urllib2.Request(torrentUrl) - request.add_header('Referer', torrentUrl) - request.add_header('Accept-encoding', 'gzip') - result = urllib2.urlopen(request) - if result.info().get('Content-Encoding') == 'gzip': - buf = StringIO(result.read()) - decomp = zlib.decompressobj(16 + zlib.MAX_WBITS) - content = decomp.decompress(buf.getvalue()) - else: - content = result.read() + log('request for %s' % torrentUrl) + content = self.makeRequest(torrentUrl) localFile = xbmcvfs.File(torrentFile, "w+b") localFile.write(content) @@ -229,6 +222,29 @@ class AnteoLoader: self.torrentFile = torrentFile return self.torrentFile + def makeRequest(self, torrentUrl): + torrentUrl = re.sub('^://', 'http://', torrentUrl) + x = re.search("://(.+?)/|://(.+?)$", torrentUrl) + if x: + baseurl = x.group(1) if x.group(1) else x.group(2) + else: + baseurl ='' + + headers = [('User-Agent', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 YaBrowser/14.10.2062.12061 Safari/537.36'), + ('Referer', 'http://%s/' % baseurl), ('Accept-encoding', 'gzip'), ] + + opener = urllib2.build_opener() + opener.addheaders = headers + result = opener.open(torrentUrl) + if result.info().get('Content-Encoding') == 'gzip': + buf = StringIO(result.read()) + decomp = zlib.decompressobj(16 + zlib.MAX_WBITS) + content = decomp.decompress(buf.getvalue()) + else: + content = result.read() + return content + def md5(self, string): hasher = hashlib.md5() try: @@ -286,9 +302,12 @@ class AnteoPlayer(xbmc.Player): if self.buffer(): log('[AnteoPlayer]: ************************************* GOING LOOP') if self.setup_play(): + WatchedHistoryDB().add(self.basename, self.torrentUrl, + foldername(self.getContentList()[self.contentId]['title']), + self.watchedTime, self.totalTime, self.contentId, self.fullSize) self.setup_subs() self.loop() - WatchedHistoryDB().add(self.basename, foldername(self.getContentList()[self.contentId]['title']), self.watchedTime, self.totalTime, self.contentId, self.fullSize) + WatchedHistoryDB().add(self.basename, self.torrentUrl, foldername(self.getContentList()[self.contentId]['title']), self.watchedTime, self.totalTime, self.contentId, self.fullSize) else: log('[AnteoPlayer]: ************************************* break') break @@ -304,6 +323,8 @@ class AnteoPlayer(xbmc.Player): continue log('[AnteoPlayer]: ************************************* NO! break') + showMessage(self.localize('Information'), + self.localize('Stopping the torrent2http process...')) break xbmc.Player().stop() @@ -311,14 +332,11 @@ class AnteoPlayer(xbmc.Player): if '1' != self.__settings__.getSetting("keep_files") and 'Saved Files' not in self.userStorageDirectory: xbmc.sleep(1000) clearStorage(self.userStorageDirectory) - else: - #if self.seeding_status: - #showMessage(self.localize('Information'), - # self.localize('Torrent is seeding. To stop it use Download Status.'), forced=True) - #else: - #if self.seeding: self.db_delete() - showMessage(self.localize('Information'), - self.localize('Torrent downloading is stopped.'), forced=True) + + showMessage(self.localize('Information'), + self.localize('torrent2http process stopped.')) + + loadsw_onstop() # Reload Search Window def init(self): self.next_contentId = False @@ -332,16 +350,6 @@ class AnteoPlayer(xbmc.Player): self.torrentUrl = self.torrentUrl def setup_engine(self): - #uri=None, binaries_path=None, platform=None, download_path=".", - #bind_host='127.0.0.1', bind_port=5001, connections_limit=None, download_kbps=None, upload_kbps=None, - #enable_dht=True, enable_lsd=True, enable_natpmp=True, enable_upnp=True, enable_scrape=False, - #log_stats=False, encryption=Encryption.ENABLED, keep_complete=False, keep_incomplete=False, - #keep_files=False, log_files_progress=False, log_overall_progress=False, log_pieces_progress=False, - #listen_port=6881, use_random_port=False, max_idle_timeout=None, no_sparse=False, resume_file=None, - #user_agent=None, startup_timeout=5, state_file=None, enable_utp=True, enable_tcp=True, - #debug_alerts=False, logger=None, torrent_connect_boost=50, connection_speed=50, - #peer_connect_timeout=15, request_timeout=20, min_reconnect_time=60, max_failcount=3, - #dht_routers=None, trackers=None) encryption = Encryption.ENABLED if self.__settings__.getSetting('encryption') == 'true' else Encryption.DISABLED upload_limit = int(self.__settings__.getSetting("upload_limit"))*1024/8 if self.__settings__.getSetting( @@ -382,16 +390,10 @@ class AnteoPlayer(xbmc.Player): keep_files=keep_files, user_agent=user_agent, resume_file=resume_file, enable_dht=enable_dht) def buffer(self): - #self.pre_buffer_bytes = 30*1024*1024 #30 MB ready = False progressBar = xbmcgui.DialogProgress() progressBar.create('[%sPlayer v%s] ' % (author, __version__) + self.localize('Please Wait'), self.localize('Seeds searching.')) - #if self.subs_dl: - # subs = self.torrent.getSubsIds(os.path.basename(self.torrent.getFilePath(self.contentId))) - # if len(subs) > 0: - # for ind, title in subs: - # self.torrent.continueSession(ind) while not xbmc.abortRequested and not ready: xbmc.sleep(500) @@ -503,6 +505,10 @@ class AnteoPlayer(xbmc.Player): listitem.setThumbnailImage(urllib.unquote_plus(thumbnail)) self.display_name = label + if self.get('listitem'): + listitem = self.get('listitem') + listitem.setPath(url) + player = xbmc.Player() player.play(url, listitem) @@ -547,10 +553,10 @@ class AnteoPlayer(xbmc.Player): status = self.engine.status() file_status = self.engine.file_status(self.contentId) self.watchedTime = xbmc.Player().getTime() - self.totalTime = xbmc.Player().getTotalTime() if self.iterator == 100 and debug_counter < 100: debug_counter += 1 else: + self.totalTime = xbmc.Player().getTotalTime() self.print_debug(status) debug_counter=0 @@ -564,12 +570,6 @@ class AnteoPlayer(xbmc.Player): log('[loop]: xbmc.Player().pause()') xbmc.sleep(1000) - #if not self.seeding_run and self.iterator == 100 and self.seeding: - #self.seeding_run = True - #self.seed(self.contentId) - #self.seeding_status = True - # xbmc.sleep(7000) - def onPlayBackStarted(self): for f in self.on_playback_started: f() @@ -600,10 +600,10 @@ class AnteoPlayer(xbmc.Player): def _get_status_lines(self, s, f): return [ - self.display_name, - "%.2f%% %s" % (f.progress * 100, self.localize(STATE_STRS[s.state]).decode('utf-8')), - "D:%.2f%s U:%.2f%s S:%d P:%d" % (s.download_rate, self.localize('kb/s').decode('utf-8'), - s.upload_rate, self.localize('kb/s').decode('utf-8'), + ensure_str(self.display_name), + "%.2f%% %s" % (f.progress * 100, self.localize(STATE_STRS[s.state])), + "D:%.2f%s U:%.2f%s S:%d P:%d" % (s.download_rate, self.localize('kb/s'), + s.upload_rate, self.localize('kb/s'), s.num_seeds, s.num_peers) ] diff --git a/Content.py b/Content.py index 8322bce..073b15d 100644 --- a/Content.py +++ b/Content.py @@ -26,6 +26,7 @@ import re from StringIO import StringIO import gzip import HTMLParser +import ssl from datetime import date import Localization @@ -87,6 +88,7 @@ class Content: 'horror': ('Horror',), 'romance': ('Romance',), 'thriller': ('Thriller',), + 'sci_fi': ('Sci-Fi',), } } @@ -131,7 +133,8 @@ class Content: else: get = self.category_dict[category][subcategory] - if category == 'search': get = (get[0], get[1] % urllib.quote_plus(subcategory.encode('utf-8'))) + if category == 'search' and subcategory != True: + get = (get[0], get[1] % urllib.quote_plus(subcategory.encode('utf-8'))) property = self.get_property(category, subcategory) @@ -172,14 +175,20 @@ class Content: except: pass if has_property: - if category == 'search': property['page'] = property['page'] % urllib.quote_plus( - subcategory.encode('utf-8')) + if category == 'search' and subcategory != True: + property['page'] = property['page'] % urllib.quote_plus(subcategory.encode('utf-8')) return property def makeRequest(self, url, data={}, headers=[]): self.cookieJar = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookieJar)) + # python ssl Context support - PEP 0466 + if hasattr(ssl, '_create_unverified_context'): + ssl_context = ssl._create_unverified_context() + opener.add_handler(urllib2.HTTPSHandler(context=ssl_context)) + else: + opener.add_handler(urllib2.HTTPSHandler()) opener.addheaders = headers if 0 < len(data): encodedData = urllib.urlencode(data) @@ -211,6 +220,7 @@ class Content: ('>', '>'), ('"', '"'), ("'", '''), + ("'", '''), (' ', ' ',), ('"', '«', ), ('"', '»', ), @@ -218,6 +228,7 @@ class Content: ('e', 'é',), ('e', 'è',), ('&', '&',), + ('&', '&',), ('u', 'ù',), ('u', 'ú',), ('o', 'ô',), diff --git a/Core.py b/Core.py index d667c03..82302d1 100644 --- a/Core.py +++ b/Core.py @@ -30,14 +30,16 @@ class Core: __plugin__ = sys.modules["__main__"].__plugin__ __settings__ = sys.modules["__main__"].__settings__ ROOT = sys.modules["__main__"].__root__ - userStorageDirectory = localize_path(__settings__.getSetting("storage"))#file_encode(__settings__.getSetting("storage")) + userStorageDirectory = localize_path(xbmc.translatePath(__settings__.getSetting("storage")))#file_encode(__settings__.getSetting("storage")) torrentFilesDirectory = 'torrents' debug = __settings__.getSetting('debug') == 'true' torrent_player = __settings__.getSetting("torrent_player") history_bool = __settings__.getSetting('history') == 'true' open_option = int(__settings__.getSetting('open_option')) - language = {0: 'en', 1: 'ru', 2: 'uk', 3: 'he'}.get(int(__settings__.getSetting("language"))) + language = {0: 'en', 1: 'ru', 2: 'uk', 3: 'he', 4: 'hu'}.get(int(__settings__.getSetting("language"))) scrapperDB_ver = {'en':'1.1', 'ru':'1.3', 'he':'1.3'} + torrent_info_style = int(__settings__.getSetting('torrent_info_style')) + searchwindowmode = int(__settings__.getSetting('searchwindowmode')) log('SYS ARGV: ' + str(sys.argv)) @@ -52,7 +54,11 @@ class Core: def sectionMenu(self): if self.__settings__.getSetting('plugin_name') != self.__plugin__: #Every update run - first_run_250() + first_run_260() + if self.version_check(): + estuary() + + self.__settings__.setSetting('first_run_260', 'True') self.__settings__.setSetting('plugin_name', self.__plugin__) #check_network_advancedsettings() check_download_dir() @@ -64,49 +70,100 @@ class Core: contextMenu = [(self.localize('Search Control Window'), 'xbmc.RunScript(%s,)' % os.path.join(ROOT, 'controlcenter.py'))] - if self.history_bool: - HistorycontextMenu=[] + #Search Window + if self.searchwindowmode < 3: + self.drawItem('< %s >' % self.localize('Search Window'), 'searchWindow', + image=self.ROOT + '/icons/kodi.png', isFolder=False) + + #History + if self.history_bool and self.searchwindowmode > 0: + HistorycontextMenu = [] HistorycontextMenu.extend(contextMenu) HistorycontextMenu.append( - (self.localize('Clear %s') % self.localize('Search History'), ListString % ('History', 'clear', 'addtime', ''))) - self.drawItem('< %s >' % self.localize('Search History'), 'History', - image=self.ROOT + '/icons/history2.png', contextMenu=HistorycontextMenu, replaceMenu=False) - self.drawItem('< %s >' % self.localize('Search'), 'search', image=self.ROOT + '/icons/search.png', ) + (self.localize('Clear %s') % self.localize('Search History'), + ListString % ('History', 'clear', 'addtime', ''))) + if self.searchwindowmode == 1: + self.drawItem('< %s >' % self.localize('Search History'), 'swHistory', + image=self.ROOT + '/icons/history2.png', contextMenu=HistorycontextMenu, replaceMenu=False) + else: + self.drawItem('< %s >' % self.localize('Search History'), 'History', + image=self.ROOT + '/icons/history2.png', contextMenu=HistorycontextMenu, replaceMenu=False) + + #Search + if self.searchwindowmode == 1: + self.drawItem('< %s >' % self.localize('Search'), 'swsearch', image=self.ROOT + '/icons/search.png',) + elif self.searchwindowmode > 1: + self.drawItem('< %s >' % self.localize('Search'), 'search', image=self.ROOT + '/icons/search.png', ) + + #Media CLcontextMenu=[] CLcontextMenu.extend(contextMenu) CLcontextMenu.append((self.localize('Reset All Cache DBs'), ListString % ('full_download', '', 'url', json.dumps({'action': 'delete'})))) self.drawItem('< %s >' % self.localize('Content Lists'), 'openContent', image=self.ROOT + '/icons/media.png', contextMenu=CLcontextMenu, replaceMenu=False) + + #DL Status DLScontextMenu=[(self.localize('Start All'), ListString % ('DownloadStatus', 'startall', 'addtime', '')), (self.localize('Stop All'), ListString % ('DownloadStatus', 'stopall', 'addtime', '')),] DLScontextMenu.append( (self.localize('Clear %s') % self.localize('Download Status'), ListString % ('DownloadStatus', 'clear', 'addtime', ''))) DLScontextMenu.extend(contextMenu) - self.drawItem('< %s >' % self.localize('Download Status'), 'DownloadStatus', image=self.ROOT + '/icons/download.png', - contextMenu=DLScontextMenu, replaceMenu=False) - self.drawItem('< %s >' % self.localize('Torrent-client Browser'), 'uTorrentBrowser', - image=self.ROOT + '/icons/' + self.getTorrentClientIcon()) - if self.history_bool: + + if self.searchwindowmode == 1: + self.drawItem('< %s >' % self.localize('Download Status'), 'swDownloadStatus', + image=self.ROOT + '/icons/download.png', + contextMenu=DLScontextMenu, replaceMenu=False) + elif self.searchwindowmode > 1: + self.drawItem('< %s >' % self.localize('Download Status'), 'DownloadStatus', + image=self.ROOT + '/icons/download.png', + contextMenu=DLScontextMenu, replaceMenu=False) + + #Torrent-client + if self.searchwindowmode == 1: + self.drawItem('< %s >' % self.localize('Torrent-client Browser'), 'swuTorrentBrowser', + image=self.ROOT + '/icons/' + getTorrentClientIcon()) + elif self.searchwindowmode > 1: + self.drawItem('< %s >' % self.localize('Torrent-client Browser'), 'uTorrentBrowser', + image=self.ROOT + '/icons/' + getTorrentClientIcon()) + + #Watched + if self.history_bool and self.searchwindowmode > 0: WatchedHistorycontextMenu=[] WatchedHistorycontextMenu.extend(contextMenu) WatchedHistorycontextMenu.append( (self.localize('Clear %s') % self.localize('Watched History'), ListString % ('WatchedHistory', 'clear', 'addtime', ''))) - self.drawItem('< %s >' % self.localize('Watched History'), 'WatchedHistory', - image=self.ROOT + '/icons/watched.png', contextMenu=WatchedHistorycontextMenu, replaceMenu=False) + if self.searchwindowmode == 1: + self.drawItem('< %s >' % self.localize('Watched History'), 'swWatchedHistory', + image=self.ROOT + '/icons/watched.png', contextMenu=WatchedHistorycontextMenu, + replaceMenu=False) + else: + self.drawItem('< %s >' % self.localize('Watched History'), 'WatchedHistory', + image=self.ROOT + '/icons/watched.png', contextMenu=WatchedHistorycontextMenu, + replaceMenu=False) + + #Torr player self.drawItem('< %s >' % self.localize('.torrent Player'), 'torrentPlayer', - image=self.ROOT + '/icons/torrentPlayer.png') + image=self.ROOT + '/icons/torrentPlayer.png', isFolder = False) + + #Search Control Window self.drawItem('< %s >' % self.localize('Search Control Window'), 'controlCenter', image=self.ROOT + '/icons/settings.png', isFolder=False) + + #Magnet player self.drawItem('< %s >' % self.localize('Magnet-link Player'), 'magentPlayer', - image=self.ROOT + '/icons/magnet.png') + image=self.ROOT + '/icons/magnet.png', isFolder = False) + + #Debug if self.debug: self.drawItem('full_download', 'full_download', image=self.ROOT + '/icons/magnet.png') self.drawItem('test', 'test', image=self.ROOT + '/icons/magnet.png', isFolder=False) + #Clear storage if '0' != self.__settings__.getSetting("keep_files"): self.drawItem('< %s >' % self.localize('Clear Storage'), 'clearStorage', isFolder=True, image=self.ROOT + '/icons/clear.png') + view_style('sectionMenu') xbmcplugin.endOfDirectory(handle=int(sys.argv[1]), succeeded=True) @@ -239,54 +296,42 @@ class Core: lockView('wide') def test(self, params={}): - #from Anteoloader import AnteoPlayer - #from python_libtorrent import get_libtorrent - #self.lt=get_libtorrent() - #self.torrentFile='D:\\test.torrent' - #self.session = self.lt.session() - #e=self.lt.bdecode(xbmcvfs.File(self.torrentFile,'rb').read()) - #self.torrentFileInfo = self.lt.torrent_info(e) - #torrent_info={'ti': self.torrentFileInfo, - # 'save_path': self.userStorageDirectory, - # 'flags': 0x300, - # #'storage_mode': self.lt.storage_mode_t(1), - # 'paused': False, - # #'auto_managed': False, - # #'duplicate_is_error': True - # } - #self.torrentHandle = self.session.add_torrent(torrent_info) - #log(self.torrentHandle.torrent_file()) - #self.session.remove_torrent(self.torrentHandle) + pass + import searchwindow + params = {'mode': 'file_browser', 'path':'D:\\', 'tdir':'D:\\FRAPS\\'} + searchwindow.main(params) - #params['url']='0' - #if not xbmcvfs.exists(torrentUrl): - # action = xbmcgui.Dialog() - # torrentUrl = action.browse(1, self.localize('Choose .torrent in video library'), 'video', '.torrent') - #if torrentUrl and xbmcvfs.exists(torrentUrl): - # if 0 != len(torrentUrl): - # self.Downloader = Downloader.Torrent(self.userStorageDirectory, torrentUrl) - # else: - # log(self.__plugin__ + " Unexpected access to method Anteoloader() without torrent content") - #if self.Downloader: - # x=self.Downloader.getContentList() - # print str(x) - # xbmc.sleep(1000) - # self.Downloader.__exit__() - #self.Player = AnteoPlayer(userStorageDirectory=self.userStorageDirectory, torrentUrl=torrentUrl, params=params) + def swHistory(self, params={}): + import searchwindow + params = {'mode': 'history'} + searchwindow.main(params) - #xbmcgui.Dialog().ok('Dam Son!','Now send this shit to DiMartino') - from resources.proxy import antizapret - filename = os.path.join(tempdir(),"antizapret.pac_config") - import shelve - from contextlib import contextmanager, closing - with closing(shelve.open(filename, writeback=True)) as d: - import time - log(str(d)) - log(str(time.time())) - log(str((time.time() - d["created_at"]))) - ttl = 24*3600 - if ttl > 0 and (time.time() - d["created_at"]) > ttl: - log('xxx') + def swDownloadStatus(self, params={}): + import searchwindow + params = {'mode': 'downloadstatus'} + searchwindow.main(params) + + def swuTorrentBrowser(self, params={}): + import searchwindow + params = {'mode': 'browser'} + searchwindow.main(params) + + def swWatchedHistory(self, params={}): + import searchwindow + params = {'mode': 'watched'} + searchwindow.main(params) + + def swsearch(self, params={}): + if len(Searchers().get_active())<1: + noActiveSerachers() + return + keyboard = xbmc.Keyboard('', self.localize('Search Phrase')) + keyboard.doModal() + params["query"] = keyboard.getText() + if keyboard.isConfirmed(): + params["mode"] = 'search' + import searchwindow + searchwindow.main(params) def DownloadStatus(self, params={}): db = DownloadDB() @@ -419,8 +464,6 @@ class Core: self.drawItem(title, 'DownloadStatus', link, image=img, contextMenu=contextMenu, replaceMenu=False) view_style('DownloadStatus') xbmcplugin.endOfDirectory(handle=int(sys.argv[1]), succeeded=True) - #xbmc.sleep(30000) - #xbmc.executebuiltin('Container.Refresh') return def History(self, params={}): @@ -521,7 +564,7 @@ class Core: self.__settings__.setSetting("lastTorrent", path) xbmc.executebuiltin( 'XBMC.ActivateWindow(%s)' % 'Videos,plugin://plugin.video.torrenter/?action=%s&url=%s' - % ('torrentPlayer', path)) + % ('torrentPlayer', path.encode('utf-8'))) if action2 == 'playnoseek' or action2 == 'playwithseek': filename, path, url, seek, length, ind = db.get('filename, path, url, seek, length, ind', 'addtime', str(addtime)) @@ -551,7 +594,7 @@ class Core: #for favbool, bbstring in favlist: for addtime, filename, foldername, path, url, seek, length, ind, size in items: seek = int(seek) if int(seek) > 3*60 else 0 - watchedPercent = int((float(seek) / float(length)) * 100) + watchedPercent = int((float(seek) / float(length if length else 1)) * 100) duration = '%02d:%02d:%02d' % ((length / (60*60)), (length / 60) % 60, length % 60) title = '[%d%%][%s] %s [%d MB]' %\ (watchedPercent, duration, filename.encode('utf-8'), int(size)) @@ -725,6 +768,19 @@ class Core: else: if provider: self.Content = self.contenterObject[provider] + if category == 'search' and provider and subcategory == True: + keyboard = xbmc.Keyboard('', self.localize('Search Phrase') + ':') + keyboard.doModal() + query = keyboard.getText() + if not query: + return + elif keyboard.isConfirmed(): + subcategory = query + if subcategory: + apps['subcategory'] = subcategory.decode('utf-8') + else: + return + if not self.Content.isTracker(): self.draw(apps, mode='content') else: @@ -740,6 +796,7 @@ class Core: page = apps.get('page') if apps.get('page') else 1 sort = apps.get('sort') if apps.get('sort') else 0 apps_property={'page':page, 'sort':sort} + log('draw: '+str((category, subcategory))) property = self.Content.get_property(category, subcategory) contentList = self.Content.get_contentList(category, subcategory, apps_property) if property and property.get('page'): @@ -895,7 +952,7 @@ class Core: progressBar.update(iterator, dialogText, title, scrapers[scraper]) meta = self.Scraper.scraper(scraper, {'label': title, 'search': search, 'year': year}, self.language) - #print 'meta:'+str(meta) + log('meta:'+str(meta)) if self.language == 'ru': if not meta.get('info').get('title') or \ not meta.get('properties').get('fanart_image') or not meta.get('icon'): @@ -991,7 +1048,7 @@ class Core: title = title.encode('utf-8', 'ignore') except: continue - label = info.get('label').encode('utf-8', 'ignore') + log(str(info)) if info.get('link'): if isinstance(info.get('link'), tuple): @@ -1112,7 +1169,7 @@ class Core: images = {'icon':image, 'thumb':image} images = {'icon': image, 'thumb': image, 'poster': image, 'banner': image, - 'fanart': image, 'landscape': image, + #'fanart': image, 'landscape': image, #'clearart': image, 'clearlogo': image, } listitem.setArt(images) @@ -1148,14 +1205,14 @@ class Core: contextMenu = [(self.localize('Search Control Window'), 'xbmc.RunScript(%s,)' % os.path.join(ROOT, 'controlcenter.py'))] replaceMenu = False - if contextMenu: - listitem.addContextMenuItems(contextMenu, replaceItems=replaceMenu) if isFolder: listitem.setProperty("Folder", "true") listitem.setInfo(type='Video', infoLabels=info) else: listitem.setInfo(type='Video', infoLabels=info) listitem.setArt({'thumb': image}) + if contextMenu: + listitem.addContextMenuItems(contextMenu, replaceItems=replaceMenu) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=listitem, isFolder=isFolder) def getParameters(self, parameterString): @@ -1233,7 +1290,7 @@ class Core: filename = os.path.join(folder, filename) xbmc.executebuiltin('xbmc.PlayMedia("' + filename.encode('utf-8') + '")') elif tdir and action == 'copy': - path=os.path.join(folder, tdir) + path = os.path.join(localize_path(folder), localize_path(tdir)) dirs, files=xbmcvfs.listdir(path) if len(dirs) > 0: dirs.insert(0, self.localize('./ (Root folder)')) @@ -1248,9 +1305,10 @@ class Core: path=os.path.join(path, dirs[ret]) dirs, files=xbmcvfs.listdir(path) for file in files: - if not xbmcvfs.exists(os.path.join(path,file)): - xbmcvfs.delete(os.path.join(path,file)) - xbmcvfs.copy(os.path.join(path,file),os.path.join(folder,file)) + file = localize_path(file) + if not xbmcvfs.exists(os.path.join(path, file)): + xbmcvfs.delete(os.path.join(path, file)) + xbmcvfs.copy(os.path.join(path, file),os.path.join(folder, file)) i=i+1 showMessage(self.localize('Torrent-client Browser'), self.localize('Copied %d files!') % i, forced=True) return @@ -1365,7 +1423,6 @@ class Core: params["url"] = urllib.quote_plus(unescape(urllib.unquote_plus(query))) else: params["url"] = urllib.quote_plus(unescape(urllib.unquote_plus(defaultKeyword))) - #print str(params) self.torrentPlayer(params) def torrentPlayer(self, params={}): @@ -1376,13 +1433,22 @@ class Core: if not url: action = xbmcgui.Dialog() url = action.browse(1, self.localize('Choose .torrent in video library'), 'video', '.torrent') + torrent = Downloader.Torrent(self.userStorageDirectory, torrentFilesDirectory=self.torrentFilesDirectory) + self.__settings__.setSetting("lastTorrent", torrent.saveTorrent(url)) + self.__settings__.setSetting("lastTorrentUrl", url) if url: + xbmc.executebuiltin("Dialog.Close(all,true)") xbmc.executebuiltin( 'XBMC.ActivateWindow(%s)' % 'Videos,plugin://plugin.video.torrenter/?action=%s&url=%s' % ('torrentPlayer', url)) return if url: - self.openTorrent(params) + if self.searchwindowmode > 1: + self.openTorrent(params) + else: + import searchwindow + params = {'mode': 'open_torrent', 'link': url} + searchwindow.main(params) def userStorage(self, params): save=False @@ -1409,7 +1475,14 @@ class Core: self.userStorageDirectory=dirname def playTorrent(self, params={}): - torrentUrl = self.__settings__.getSetting("lastTorrent") + if params.get('filename'): + torrentUrl = unquote(params.get('filename')) + else: + torrentUrl = self.__settings__.getSetting("lastTorrent") + #xbmc.executebuiltin('Action(Stop)') + if self.torrent_player != '1' and params.get('external') == '1' and not params.get('seek'): + params['seek'] = watched_seek(torrentUrl, params['url']) + self.userStorage(params) if self.torrent_player == '0': from Player import TorrentPlayer @@ -1434,9 +1507,9 @@ class Core: #xbmc.executebuiltin('xbmc.RunPlugin("plugin://plugin.video.yatp/?action=play&torrent=%s&file_index=%s")' % (urllib.quote_plus(torrentUrl), params['url'])) elif self.torrent_player == '1': __ASsettings__ = xbmcaddon.Addon(id='script.module.torrent.ts') - folder=__ASsettings__.getSetting("folder") + folder=__ASsettings__.getSetting("path") save=__ASsettings__.getSetting("save") - __ASsettings__.setSetting("folder", self.__settings__.getSetting("storage")) + __ASsettings__.setSetting("path", xbmc.translatePath(self.__settings__.getSetting("storage"))) __ASsettings__.setSetting("save", self.__settings__.getSetting("keep_files")) xbmc.sleep(1000) torrent = Downloader.Torrent(self.userStorageDirectory, torrentUrl, self.torrentFilesDirectory) @@ -1447,7 +1520,7 @@ class Core: label = unquote(get("label"), os.path.basename(path)) torrent.play_url_ind(int(ind), label, icon) torrent.__exit__() - __ASsettings__.setSetting("folder", folder) + __ASsettings__.setSetting("path", folder) __ASsettings__.setSetting("save", save) def saveUrlTorrent(self, url): @@ -1477,14 +1550,29 @@ class Core: get = params.get xbmc.executebuiltin('xbmc.Playlist.Clear') url = unquote(get("url"), None) - fileIndex = unquote(get("index"), None) + url2 = unquote(get("url2"), None) + index = unquote(get("index"), None) if url: self.__settings__.setSetting("lastTorrentUrl", url) + classMatch = re.search('(\w+)::(.+)', url) + if classMatch: + searcher = classMatch.group(1) + url = Searchers().downloadWithSearcher(classMatch.group(2), searcher) torrent = Downloader.Torrent(self.userStorageDirectory, torrentFilesDirectory=self.torrentFilesDirectory) - self.__settings__.setSetting("lastTorrent", torrent.saveTorrent(url)) - if fileIndex==None: fileIndex = chooseFile(torrent.getContentList()) - if fileIndex: - xbmc.executebuiltin('xbmc.RunPlugin("plugin://plugin.video.torrenter/?action=playTorrent&url='+fileIndex+'")') + filename = torrent.saveTorrent(url) + self.__settings__.setSetting("lastTorrent", filename) + if index == None: index = chooseFile(torrent.getContentList()) + if index: + #params = {'url': index, 'filename': filename} + #if url2: params['url2'] = url2 + #self.playTorrent(params) + + url = 'plugin://plugin.video.torrenter/?action=playTorrent' + url += '&url=%s' % (str(index)) + if url2: url += '&url2=%s' % (url2) + if filename: url += '&filename=%s' % (urllib.quote_plus(ensure_str(filename))) + + xbmc.executebuiltin('xbmc.RunPlugin("%s")' % (url)) def openTorrent(self, params={}): get = params.get @@ -1503,7 +1591,8 @@ class Core: torrent = Downloader.Torrent(self.userStorageDirectory, torrentFilesDirectory=self.torrentFilesDirectory) if not torrent: torrent = Downloader.Torrent(self.userStorageDirectory, torrentFilesDirectory=self.torrentFilesDirectory) - self.__settings__.setSetting("lastTorrent", torrent.saveTorrent(url)) + filename = torrent.saveTorrent(url) + self.__settings__.setSetting("lastTorrent", filename) append_filesize = self.__settings__.getSetting("append_filesize") == 'true' hasSize = False @@ -1539,9 +1628,11 @@ class Core: 'XBMC.RunPlugin(%s)' % ('%s?action=%s&ind=%s') % ( sys.argv[0], 'downloadLibtorrent', str(identifier))), ] - link = {'url': identifier, 'thumbnail': thumbnail, 'save_folder':save_folder} + link = {'url': identifier, 'thumbnail': thumbnail, 'save_folder':save_folder, + 'filename':ensure_str(filename)} self.drawItem(title, 'playTorrent', link, image=thumbnail, isFolder=False, - action2=ids_video.rstrip(','), contextMenu=contextMenu, replaceMenu=False, fileSize=filesize) + action2=ids_video.rstrip(','), contextMenu=contextMenu, + replaceMenu=False, fileSize=filesize) view_style('openTorrent') p_handle = int(sys.argv[1]) try: @@ -1598,6 +1689,10 @@ class Core: xbmc.executebuiltin( 'xbmc.RunScript(%s,)' % os.path.join(ROOT, 'controlcenter.py')) + def searchWindow(self, params={}): + import searchwindow + searchwindow.main(params) + def showFilesList(self, filesList, params={}): get = params.get thumbnail = unquote(get("thumbnail"),'') @@ -1615,8 +1710,8 @@ class Core: (self.localize('Download via Libtorrent'), 'XBMC.RunPlugin(%s)' % ('%s?action=%s&url=%s') % ( sys.argv[0], 'downloadLibtorrent', urllib.quote_plus(link))), - (self.localize('Open (no return)'), - 'XBMC.ActivateWindow(Videos,%s)' % ('%s?action=%s%s') % ( + (self.localize('Open'), + 'XBMC.Container.Update(%s)' % ('%s?action=%s%s') % ( sys.argv[0], 'openTorrent', link_url)), ] title = self.titleMake(seeds, leechers, size, title) @@ -1676,8 +1771,8 @@ class Core: (self.localize('Add to %s') % return_name, 'XBMC.RunPlugin(%s)' % (back_url+'&stringdata=' + urllib.quote_plus( json.dumps(sdata)))), - (self.localize('Open (no return)'), - 'XBMC.ActivateWindow(Videos,%s)' % ('%s?action=%s%s') % ( + (self.localize('Open'), + 'XBMC.Container.Update(%s)' % ('%s?action=%s%s') % ( sys.argv[0], 'openTorrent', link_url)), (self.localize('Return to %s') % return_name, 'XBMC.ActivateWindow(%s)' % ('Videos,%s' % return_url)), @@ -1693,8 +1788,28 @@ class Core: xbmcplugin.endOfDirectory(handle=int(sys.argv[1]), succeeded=True) def context(self, params={}): - xbmc.executebuiltin("Action(ContextMenu)") - return + if not self.version_check(): + xbmc.executebuiltin("Action(ContextMenu)") + sys.exit() + else: + fixed = xbmcgui.Dialog().contextmenu(list=[(self.localize('Open')), + (self.localize('Download via Libtorrent')), + (self.localize('Download via T-client'))]) + if fixed == 0: + xbmc.executebuiltin('XBMC.Container.Update(%s)' % + ('%s?action=%s&url=%s') % + (sys.argv[0], 'openTorrent', params['url'])) + elif fixed == 1: + xbmc.executebuiltin('XBMC.RunPlugin(%s)' % + ('%s?action=%s&url=%s') % + (sys.argv[0], 'downloadLibtorrent', params['url'])) + elif fixed == 2: + xbmc.executebuiltin('XBMC.RunPlugin(%s)' % + ('%s?action=%s&url=%s') % + (sys.argv[0], 'downloadFilesList', params['url'])) + + def version_check(self): + return False if int(xbmc.getInfoLabel( "System.BuildVersion" )[:2]) < 17 else True def downloadFilesList(self, params={}): from resources.utorrent.net import Download @@ -1765,11 +1880,19 @@ class Core: f = open(url, 'rb') torrent = f.read() f.close() + try: + from python_libtorrent import get_libtorrent + libtorrent = get_libtorrent() + info = libtorrent.torrent_info(libtorrent.bdecode(torrent)) + name = info.name() + except: + log('get_libtorrent import error, name = None') + name = None success = Download().add(torrent, dirname) if success: showMessage(self.localize('Torrent-client Browser'), self.localize('Added!'), forced=True) if ind: - id = self.chooseHASH(Download().list())[0] + id = self.chooseHASH(Download().list(), name)[0] Download().setprio(id, ind) def downloadLibtorrent(self, params={}): @@ -1803,7 +1926,6 @@ class Core: int(self.__settings__.getSetting("download_limit")) * 1000000 / 8) #MBits/second torrent.downloadProcess(ind, encryption) showMessage(self.localize('Download Status'), self.localize('Added!')) - xbmcplugin.endOfDirectory(handle=int(sys.argv[1]), succeeded=True) def titleMake(self, seeds, leechers, size, title): @@ -1815,13 +1937,21 @@ class Core: clAliceblue = '[COLOR FFF0F8FF]%s[/COLOR]' clRed = '[COLOR FFFF0000]%s[/COLOR]' - title = title.replace('720p', '[B]720p[/B]') - title = clWhite % title + chr(10) - second = '[I](%s) [S/L: %d/%d] [/I]' % (size, seeds, leechers) + chr(10) - space = '' - for i in range(0, 180 - len(second)): - space += ' ' - title += space + second + title = title.replace('720p', '[B]720p[/B]').replace('1080p', '[B]1080p[/B]') + + if self.torrent_info_style == 0: + title = clWhite % title + second = '[I](%s) [S/L: %d/%d] [/I]' % (size, seeds, leechers) + title += ' ' + second + elif self.torrent_info_style == 1: + title = clWhite % title + second = '[I](%s) [S/L: %d/%d] [/I]' % (size, seeds, leechers) + title = second + ' ' + title + elif self.torrent_info_style == 2: + title = clWhite % title + second = '[I](%s) [S/L: %d/%d] [/I]' % (size, seeds, leechers) + title += '\r\n' + clDimgray % second + return title def search(self, params={}): @@ -1852,7 +1982,7 @@ class Core: else: self.openSection(params) - def chooseHASH(self, list): + def chooseHASH(self, list, name = None): dialog_items, dialog_items_clean = [], [] dialog_files = [] dat = list @@ -1862,14 +1992,29 @@ class Core: for data in dat: dialog_files.append((data['id'], data['dir'].encode('utf-8'))) dialog_items.append('[' + str(data['progress']) + '%] ' + data['name']) - if len(dialog_items) > 1: - ret = xbmcgui.Dialog().select(self.localize('Choose in torrent-client:'), dialog_items) - if ret > -1 and ret < len(dialog_files): - hash = dialog_files[ret] + dialog_items_clean.append(data['name']) + + log('[chooseHASH]: name %s ' % str(name)) + for data in dat: + # Debug('[chooseHASH]: '+str((data['name'], data['id'], data['dir'].encode('utf-8')))) + dialog_files.append((data['id'], data['dir'].encode('utf-8'))) + dialog_items.append('[' + str(data['progress']) + '%] ' + data['name']) + dialog_items_clean.append(data['name']) + + if name: + if decode_str(name) in dialog_items_clean: + return dialog_files[dialog_items_clean.index(decode_str(name))] + elif name in dialog_items_clean: + return dialog_files[dialog_items_clean.index(name)] + else: + if len(dialog_items) > 1: + ret = xbmcgui.Dialog().select(self.localize('Choose in torrent-client:'), dialog_items) + if ret > -1 and ret < len(dialog_files): + hash = dialog_files[ret] + return hash + elif len(dialog_items) == 1: + hash = dialog_files[0] return hash - elif len(dialog_items) == 1: - hash = dialog_files[0] - return hash def localize(self, string): try: @@ -1881,19 +2026,6 @@ class Core: i=delete_russian(ok=True, action='return') showMessage(self.localize('Return Russian stuff'),self.localize('%d files have been returned')%i) - def getTorrentClientIcon(self): - client = self.__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 callback(self, params={}): get = params.get diff --git a/Inposloader.py b/Inposloader.py index bd5f2eb..89b86dd 100644 --- a/Inposloader.py +++ b/Inposloader.py @@ -28,8 +28,9 @@ import xbmc import xbmcgui import xbmcvfs import xbmcaddon +import xbmcplugin import Localization -from functions import encode_msg, isSubtitle, is_writable, file_url +from functions import loadsw_onstop, isSubtitle, is_writable, file_url, localize_path import os @@ -166,22 +167,14 @@ class InposLoader: return self.torrentFile else: if not xbmcvfs.exists(self.torrentFilesPath): xbmcvfs.mkdirs(self.torrentFilesPath) - torrentFile = os.path.join(self.torrentFilesPath, self.md5(torrentUrl) + '.torrent') + torrentFile = localize_path(os.path.join(self.torrentFilesPath, self.md5(torrentUrl) + '.torrent')) try: - if not re.match("^http\:.+$", torrentUrl): + if not re.match("^[htps]+?://.+$|^://.+$", torrentUrl): + log('xbmcvfs.File for %s' % torrentUrl) content = xbmcvfs.File(torrentUrl, "rb").read() else: - request = urllib2.Request(torrentUrl) - request.add_header('Referer', torrentUrl) - request.add_header('Accept-encoding', 'gzip') - result = urllib2.urlopen(request) - if result.info().get('Content-Encoding') == 'gzip': - buf = StringIO(result.read()) - decomp = zlib.decompressobj(16 + zlib.MAX_WBITS) - content = decomp.decompress(buf.getvalue()) - else: - content = result.read() - + log('request for %s' % torrentUrl) + content = self.makeRequest(torrentUrl) localFile = xbmcvfs.File(torrentFile, "w+b") localFile.write(content) localFile.close() @@ -197,6 +190,29 @@ class InposLoader: self.torrentFile = torrentFile return self.torrentFile + def makeRequest(self, torrentUrl): + torrentUrl = re.sub('^://', 'http://', torrentUrl) + x = re.search("://(.+?)/|://(.+?)$", torrentUrl) + if x: + baseurl = x.group(1) if x.group(1) else x.group(2) + else: + baseurl ='' + + headers = [('User-Agent', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 YaBrowser/14.10.2062.12061 Safari/537.36'), + ('Referer', 'http://%s/' % baseurl), ('Accept-encoding', 'gzip'), ] + + opener = urllib2.build_opener() + opener.addheaders = headers + result = opener.open(torrentUrl) + if result.info().get('Content-Encoding') == 'gzip': + buf = StringIO(result.read()) + decomp = zlib.decompressobj(16 + zlib.MAX_WBITS) + content = decomp.decompress(buf.getvalue()) + else: + content = result.read() + return content + def md5(self, string): hasher = hashlib.md5() try: @@ -257,9 +273,12 @@ class InposPlayer(xbmc.Player): while True: log('['+author+'Player]: ************************************* GOING LOOP') if self.setup_play(): + WatchedHistoryDB().add(self.basename, self.torrentUrl, + foldername(self.getContentList()[self.contentId]['title']), + self.watchedTime, self.totalTime, self.contentId, self.fullSize) self.setup_subs() self.loop() - WatchedHistoryDB().add(self.basename, foldername(self.getContentList()[self.contentId]['title']), self.watchedTime, self.totalTime, self.contentId, self.fullSize) + WatchedHistoryDB().add(self.basename, self.torrentUrl, foldername(self.getContentList()[self.contentId]['title']), self.watchedTime, self.totalTime, self.contentId, self.fullSize) else: log('['+author+'Player]: ************************************* break') break @@ -295,7 +314,9 @@ class InposPlayer(xbmc.Player): #else: #if self.seeding: self.db_delete() showMessage(self.localize('Information'), - self.localize('Torrent downloading is stopped.'), forced=True) + self.localize('Torrent downloading is stopped.')) + + loadsw_onstop() # Reload Search Window def init(self): self.next_contentId = False @@ -483,6 +504,10 @@ class InposPlayer(xbmc.Player): self.display_name = label log(self.display_name) + if self.get('listitem'): + listitem = self.get('listitem') + listitem.setPath(url) + player = xbmc.Player() player.play(url, listitem) @@ -541,7 +566,7 @@ class InposPlayer(xbmc.Player): self.iterator = int(file_status.progress * 100) - if pause and self.__settings__.getSetting("pause_onplay") == 'true': + if pause and (self.__settings__.getSetting("pause_onplay") == 'true') and (self.getTime() > 0): pause = False xbmc.Player().pause() xbmc.sleep(1000) @@ -550,7 +575,7 @@ class InposPlayer(xbmc.Player): int) and self.next_contentId != False: self.engine.activate_file(self.next_contentId) showMessage(self.localize('Torrent Downloading'), - self.localize('Starting download next episode!'), forced=True) + self.localize('Starting download next episode!')) log('[loop]: next_contentId '+str(self.next_contentId)+str(isinstance(self.next_contentId, int))) file_status = self.engine.file_status(self.next_contentId) self.basename = self.display_name = os.path.basename(file_status.name) @@ -587,7 +612,7 @@ class InposPlayer(xbmc.Player): def _get_status_lines(self, s, f): return [ - self.display_name.encode('utf-8'), + ensure_str(self.display_name), "%.2f%% %s" % (f.progress * 100, self.localize(STATE_STRS[s.state])), "D:%.2f%s U:%.2f%s S:%d P:%d" % (s.download_rate, self.localize('kb/s'), s.upload_rate, self.localize('kb/s'), diff --git a/Localization.py b/Localization.py index ff2fd79..26b81dc 100644 --- a/Localization.py +++ b/Localization.py @@ -23,7 +23,7 @@ try: __settings__ = xbmcaddon.Addon(id='plugin.video.torrenter') - language = ('en', 'ru', 'uk','he')[int(__settings__.getSetting("language"))] + language = ('en', 'ru', 'uk','he', 'es')[int(__settings__.getSetting("language"))] except: language = 'ru' @@ -31,7 +31,266 @@ except: def localize(text): dictionary = { - 'he': { + 'es': { + 'Seeds searching.': 'Búsqueda de fuentes', + 'Please Wait': 'Espere', + 'Information': 'Información', + 'Torrent downloading is stopped.': 'Se detuvo la descarga del torrent', + 'Search': 'Buscar', + 'Seeds': 'Semillas', + 'Peers': 'Pares', + 'Materials are loading now.': 'Se están cargando los materiales', + 'Search Phrase': 'Términos de búsqueda', + 'Magnet-link is converting': 'Se está convirtiendo el enlace magnet', + 'Error': 'Error', + 'Your library out of date and can\'t save magnet-links.': 'Su colección está anticuada y no pueden guardarse enlaces magnet.', + 'Bookmarks': 'Favoritos', + 'Logout': 'Cerrar sesión', + 'Login': 'Iniciar sesión', + 'Recent Materials': 'Materiales recientes ', + 'Register': 'Registrar', + 'Bookmark': 'Favorito', + 'Item successfully added to Bookmarks': 'El elemento se ha añadido a "Favoritos"', + 'Item successfully removed from Bookmarks': 'El elemento se ha eliminado de "Favoritos"', + 'Bookmark not added': 'No se ha añadido a "Favoritos"', + 'Bookmark not removed': 'No se ha eliminado de "Favoritos"', + 'Add To Bookmarks': 'Añadir a "Favoritos"', + 'Remove From Bookmarks': 'Eliminar de "Favoritos"', + 'Auth': 'Validación', + 'Already logged in': 'Ya se había iniciado sesión', + 'Input Email (for password recovery):': 'Introduzca correo electrónico (para recuperar contraseña):', + 'Input Email:': 'Introduzca correo electrónico:', + 'Input Password (6+ symbols):': 'Introduzca contraseña (más de 6 caracteres):', + 'Input Password:': 'Introduzca contraseña', + 'Login successfull': 'Se ha iniciado sesión', + 'Login failed': 'Falló el inicio de sesión', + 'User not logged in': 'El usuario no ha iniciado sesión', + 'User successfully logged out': 'El usuario ha cerrado sesión', + 'Preloaded: ': 'Carga previa: ', + 'Do you want to STOP torrent downloading and seeding?': '¿Desea detener la descarga y dejar de compartir el torrent?', + 'Torrent Downloading': 'Descarga de torrent', + 'Auth expired, please relogin': 'Expiró la identificación. Vuelva a iniciar sesión', + 'Storage': 'Almacén', + 'Storage has been cleared': 'El almacén se ha vaciado', + 'Clear Storage': 'Vaciar almacén', + 'Popular': 'Popular', + 'Views': 'Vistas', + 'Uploading': 'Subiendo', + 'Download': 'Descargar', + 'Input symbols from CAPTCHA image:': 'Introduzca los caracteres que aparecen en la imagen:', + 'Please, rate watched video:': 'Valore el vídeo que ha visto:', + 'Bad': 'Malo', + 'So-So': 'Regular', + 'Good': 'Bueno', + 'Ratings': 'Valoraciones', + 'Rating': 'Valoración', + 'Retry': 'Reintentar', + '%ds has left': 'Ha quedado %ds', + 'File failed to play! Do you want to RETRY and buffer more?': 'Falló la reproducción del archivo. ¿Desea volver a intentarlo y aumentar el tamaño de búfer?', + 'High Priority Files': 'Archivos de prioridad alta', + 'Skip All Files': 'Omitir todos los archivos', + 'Start': 'Iniciar', + 'Stop': 'Detener', + 'Play':'Reproducir', + 'High Priority': 'Prioridad alta', + 'Skip File': 'Omitir archivo', + 'Remove': 'Eliminar', + 'Remove with files': 'Eliminar con los archivos', + 'Play File': 'Reproducir archivo', + 'Start All Files': 'Iniciar todos los archivos', + 'Stop All Files': 'Detener todos los archivos', + 'Torrent-client Browser': 'Explorador de cliente de BitTorrent', + 'Remote Torrent-client': 'Cliente de BitTorrent remoto', + 'You didn\'t set up replacement path in setting.': 'No se ha establecido una ruta alternativa en "Ajustes".', + 'For example /media/dl_torr/ to smb://SERVER/dl_torr/. Setup now?': 'Por ejemplo, /media/dl_torr/ a smb://SERVER/dl_torr/. ¿Desea establecerla ahora?', + 'Manual Torrent-client Path Edit': 'Edición manual de la ruta del cliente de BitTorrent', + 'Choose .torrent in video library': 'Seleccione archivo .torrent en la colección de vídeos', + '.torrent Player': 'Gestor de archivo .torrent', + 'Choose directory:': 'Seleccionar directorio:', + 'Starting download next episode!': 'Iniciando descarga de episodio siguiente', + 'Choose in torrent-client:': 'Elija cliente torrent', + 'Search Control Window': 'Control de búsquedas', + 'Magnet-link (magnet:...)': 'Enlace magnet (magnet:...)', + 'Not a magnet-link!': 'No es un enlace magnet', + 'Magnet-link Player': 'Gestor de enlace magnet', + 'UNKNOWN STATUS': 'ESTADO DESCONOCIDO', + 'Checking preloaded files...': 'Comprobando archivos precargados...', + 'Waiting for website response...': 'Esperando respuesta del sitio web...', + 'Search and cache information for:': 'Buscar y almacenar datos para:', + 'Open Torrent': 'Abrir torrent', + 'Torrent list is empty.': 'La lista de torrents está en blanco', + 'Content Lists': 'Listas de contenido', + 'Canceled by User': 'Cancelado por el usuario', + 'Do you want to search and cache full metadata + arts?': '¿Desea buscar y almacenar los metadatos y fan-arts completos?', + 'This vastly decreases load speed, but you will be asked to download premade bases!': 'Esto reduce considerablemente la velocidad de carga. Pero se le solicitará descargar desde cero', + 'Do you want to preload full metadata?': '¿Desea precargar los metadatos completos?', + 'It is highly recommended!': 'Altamente recomendado', + 'TV Shows': 'Series de televisión', + 'Cartoons': 'Dibujos animados', + 'Anime': 'Anime', + 'Most Recent': 'Estrenos', + 'Top 250 Movies': 'Las 250 mejores películas', + 'Top All Time': 'Las mejores de todos los tiempos', + 'by Genre': 'Por género', + 'by Year': 'Por año', + 'Action': 'Acción', + 'Adventure': 'Aventuras', + 'Animation': 'Animación', + 'Biography': 'Biografías', + 'Comedy': 'Comedia', + 'Crime': 'Policiacas', + 'Documentary': 'Documentales', + 'Drama': 'Drama', + 'Family': 'Todos los públicos', + 'Fantasy': 'Fantásticas', + 'Film-Noir': 'Cine negro', + 'History': 'Historia', + 'Horror': 'Terror', + 'Music': 'Música', + 'Musical': 'Musicales', + 'Mystery': 'Misterio', + 'Romance': 'Románticas', + 'Sci-Fi': 'Ciencia ficción', + 'Short': 'Cortos', + 'Sport': 'Deportes', + 'Thriller': 'Suspense', + 'War': 'Bélicas', + 'Western': 'Películas del Oeste', + '[B]by Site[/B]': '[B]Por sitio[/B]', + 'Cartoons Series': 'Series de dibujos animados', + 'Cartoons Short': 'Cortos de dibujos animados', + 'Male': 'Hombre', + 'Female': 'Mujer', + 'Russia & USSR': 'Rusia & URSS', + 'Next Page': 'Página siguiente', + 'Previous Page': 'Página anterior', + 'Russian Movies': 'Películas rusas', + 'israeli Movies': 'Películas israelíes', + 'hebdub movies': 'Películas dobladas al hebreo', + 'Movies': 'Películas', + 'High Resolution Movies': 'Películas en alta resolución', + '3D Movies': 'Películas en 3D', + 'Movies [Bluray]': 'Películas en formato Blu-ray', + 'Anime Film': 'Películas Anime', + 'Anime Series': 'Series Anime', + 'Can\'t download torrent, probably no seeds available.': 'No se puede descargar el torrent, probablemente no hay fuentes disponibles.', + 'Personal List': 'Lista personal', + 'Add to %s': 'Añadir a %s', + 'Delete from %s': 'Eliminar de %s', + 'Added!': 'Añadido', + 'Deleted!': 'Eliminado', + 'Search History': 'Historial de búsquedas', + ' History ':' Historial ', + 'Torrent History':'Historial de archivos torrent', + 'Watched History':'Historial de vistos', + 'Favourites': 'Favoritos', + 'Favourites SH': 'Favoritos SH', + 'Clear %s': 'Vaciar %s', + 'Clear!': 'Vacío', + 'kb/s': 'kbps', + 'Queued': 'Situado en cola', + 'Checking': 'Comprobando', + 'Downloading metadata': 'Descargando metadatos', + 'Downloading': 'Descargando', + 'Finished': 'Finalizado', + 'Seeding': 'Compartiendo', + 'Allocating': 'Reservando espacio', + 'Allocating file & Checking resume': 'Reservando espacio y comprobando reanudación', + 'For Kids': 'Para niños', + 'Adult': 'Adultos', + 'Does not support magnet links!': 'No compatible con enlaces magnet', + 'Reset All Cache DBs': 'Reiniciar todas las bases de datos de la caché', + '[B]Search[/B]': '[B]Buscar[/B]', + 'You can always restart this by deleting DBs via Context Menu': 'Siempre se puede reiniciar esto eliminando las bases de datos a través del menú contextual', + 'Your preloaded databases are outdated!': 'Las bases de datos precargadas son obsoletas', + 'Do you want to download new ones right now?': '¿Desea descargar versiones actualizadas?', + 'Individual Tracker Options':'Opciones individuales del rastreador', + 'Downloading and copy subtitles. Please wait.':'Descargando y copiando subtítulos. Espere.', + 'International Check - First Run':'Comprobación internacional - Primera ejecución.', + 'Delete Russian stuff?':'¿Desea eliminar las cosas de Rusia?', + 'Save to path':'Ruta para guardar', + 'Return Russian stuff':'Devolver las cosas de Rusia', + '%d files have been returned':'Han vuelto %d archivos', + 'Download via T-client':'Descargar a través de cliente BitTorrent', + 'Download via Libtorrent':'Descargar a través de Libtorrent', + 'Download Status':'Estado de la descarga', + 'Download has not finished yet':'La descarga no ha finalizado todavía', + 'Stopped and Deleted!':'Detenido y eliminado', + 'Unpaused!':'Despausado', + 'Paused!':'Pausado', + 'Stopped!':'Detenido', + 'Started!':'Iniciado', + 'Delete and Stop':'Eliminar y detener', + 'Unpause':'Despausar', + 'Pause':'Pausar', + 'Delete':'Eliminar', + 'Open':'Abrir', + 'Torrent is seeding. To stop it use Download Status.':'El torrent se está compartiendo. Para detenerlo utilice "Estado de la descarga".', + 'Start All':'Iniciar todo', + 'Started All!':'Se ha iniciado todo', + 'Stopped All!':'Se ha detenido todo', + 'Stop All':'Detener todo', + 'Keyboard':'Teclado', + 'Copy Files in Root':'Copiar archivos en directorio personal de root (/root)', + 'Copied %d files!':'Se han copiado %d archivos', + 'Add to MyShows.ru':'Añadir a MyShows.ru', + 'Return to MyShows.ru':'Volver a MyShows.ru', + 'Search results:':'Resultados de la búsqueda', + 'by Seeders':'Por fuentes', + 'by Date':'Por fecha', + 'Sort':'Ordenar', + 'Close':'Cerrar', + 'Views:':'Vistas:', + 'Rating:':'Valoración:', + 'Information not found!':'No se han encontrado datos', + 'Choose searcher':'Elegir buscador', + 'python-libtorrent Not Found':'No se ha encontrado python-libtorrent', + 'Windows has static compiled python-libtorrent included.':'Windows incluye una versión de python-libtorrent compilada estáticamente', + 'You should install "script.module.libtorrent" from "MyShows.me Kodi Repo"':'Se debe instalar "script.module.libtorrrent" desde el repositorio "MyShows.me"', + 'Linux x64 has not static compiled python-libtorrent included.':'Linux x64 no incluye una versión de python-libtorrent compilada estáticamente', + 'You should install it by "sudo apt-get install python-libtorrent"':'Se debe instalar con "sudo apt-get install python-libtorrent"', + 'Linux has static compiled python-libtorrent included but it didn\'t work.':'Linux incluye una version de python-libtorrent compilada estáticamente, pero no ha funcionado', + 'As far as I know you can compile python-libtorrent for ARMv6-7.':'Por lo que yo sé, python-libtorrent se puede compilar para ARMv6-7.', + 'You should search for "OneEvil\'s OpenELEC libtorrent" or use Ace Stream.':'Se debe buscar "OpenELEC libtorrent de OneEvil" o utilizar Ace Stream', + 'Please use install Ace Stream APK and choose it in Settings.':'Instale APK de Ace Stream y selecciónelo en "Ajustes"', + 'It is possible to compile python-libtorrent for Android, but I don\'t know how.':'Es posible compilar python-libtorrent para Android, pero no sé como hacerlo.', + 'It is possible to compile python-libtorrent for OS X.':'Es posible compilar python-libtorrent para Mac OS X.', + 'But you would have to do it by yourself, there is some info on github.com.':'Pero debe hacerlo por sí mismo. Hay alguna información en github.com.', + 'It is NOT possible to compile python-libtorrent for iOS.':'No es posible compilar python-libtorrent para iOS', + 'But you can use torrent-client control functions.':'Pero se pueden utilizar las funciones de control del cliente de BitTorrent', + 'I added custom searchers to Torrenter v2!':'Se han añadido buscadores personalizados a Torrenter v2', + 'Now you can use your login on trackers or write and install your own searcher!':'Ahora puede identificarse en rastreadores o crear e instalar su propio buscador', + 'Would you like to install %s from "MyShows.me Kodi Repo" in Programs section?':'¿Le gustaría instalar %s desde el repositorio "MyShows.me", sección Programas?', + 'Open installation window?':'¿Abrir ventana de instalación?', + 'Android Support':'Soporte de Android', + 'Android has no temprorary folder':'Android no tiene carpeta temporal', + 'Please specify storage folder in Settings!':'Especifique la carpeta de almacenamiento en "Ajustes"', + 'You have no installed or active searchers! More info in Search Control Window!':'No se han instalado o activado buscadores. Tiene más información en Control de búsquedas', + 'Please contact DiMartino on kodi.tv forum. We compiled python-libtorrent for Android,':'Póngase en contacto con DiMartino en el foro de kodi.tv. Hemos compilado python-libtorren para Android,', + 'but we need your help with some Torrent is seeding. To stop it use Download Status.s on different processors.':'pero necesitamos su ayuda con algún torrent que se comparte. Para detenerlo utilice Estado de la descarga. En diferentes procesadores.', + 'We added Android ARM full support to Torrenter v2!':'Hemos añadido compatibilidad completa para Android ARM a Torrenter v2', + 'I deleted pre-installed ones, install them in Search Control Window!':'Se eliminaron los que estaban preinstalados; instálelos desde Control de búsquedas', + 'Torrenter didn\'t find %s searcher':'Torrenter no ha encontrado el buscador %s', + 'Torrenter Tracker Install':'Instalación de rastreador de Torrenter', + 'Ask to save':'Preguntar para guardar', + 'Would you like to save this file?':'¿Le gustaría guardar este archivo?', + 'Your storage path is not writable or not local! Please change it in settings!':'La ruta del almacén está protegida contra escritura o no es local. Cámbiela en "Ajustes"', + 'Upgrade advancedsettings.xml':'Actualizar archivo advancedsettings.xml', + 'We would like to set some advanced settings for you!':'Nos gustaría efectuar algunos ajustes avanzados por usted', + 'Do it!':'Hazlo', + 'Please, restart Kodi now!':'Reinicie Kodi', + './ (Root folder)':'./ (Carpeta de root)', + 'Opening torrent file':'Abriendo archivo torrent', + 'New player to Torrenter v2 - Torrent2HTTP! It should be faster, stable and better with Android, also seeking works in it.':'Nuevo reproductor para Torrenter v2 - Torrent2HTTP. Debe ser más rápido, estable y mejor en Android; además, con él funcionan los intercambios.', + 'Would you like to try it?':'¿Le gustaría probarlo?', + 'Torrent2HTTP enabled! Can be changed in Settings.':'Se ha activado Torrent2HTTP. Puede cambiarse en "Ajustes".', + 'Seeking':'Compartiendo', + 'Would you like to resume from %s?':'¿Le gustaría reanudar desde %s?', + 'Seeking is working only with player Torrent2HTTP.':'Compartir funciona solamente con el reproductor Torrent2HTTP.', + 'Play (from %s)':'Reproducir desde %s', + 'Play (from start)':'Reproducir desde el inicio', + }, + 'he': { 'Seeds searching.': 'חיפוש זורעים', 'Please Wait': 'המתן', 'Information': 'מידע', @@ -224,7 +483,7 @@ def localize(text): 'Unpause':'אל תפסיק', 'Pause':'הפסק', 'Delete':'מחק', - 'Open (no return)':'פתח', + 'Open':'פתח', 'Torrent is seeding. To stop it use Download Status.':'Torrent is seeding. To stop it use Download Status.', 'Start All':'התחל הכל', 'Started All!':'מיין הכל', @@ -290,7 +549,280 @@ def localize(text): 'Play (from %s)':'%s נגן מ', 'Play (from start)':'נגן מהתחלה', }, + 'hu': { + 'Seeds searching.': 'Seederek keresése.', + 'Please Wait': 'Kérlek várj', + 'Information': 'Információ', + 'Torrent downloading is stopped.': 'A torrent letöltése befejeződött.', + 'Search': 'Keresés', + 'Seeds': 'Seederek', + 'Peers': 'Kapcsolatok', + 'Materials are loading now.': 'A tartalmak most töltődnek.', + 'Search Phrase': 'Keresés kifejezésre', + 'Magnet-link is converting': 'Magnet-link konvertálása', + 'Error': 'Hiba', + 'Your library out of date and can\'t save magnet-links.': 'A könyvtár elavult, nem lehet lementeni a magnet-linket.', + 'Bookmarks': 'Könyvjelzők', + 'Logout': 'Kijelentkezés', + 'Login': 'Bejelentkezés', + 'Recent Materials': 'Jelenlegi tartalmak', + 'Register': 'Regisztráció', + 'Bookmark': 'Könyvjelző', + 'Item successfully added to Bookmarks': 'Az elem sikeresen hozzáadva a Könyvjelzőkhöz', + 'Item successfully removed from Bookmarks': 'Az elem sikeresen törölve a Könyvjelzőkből', + 'Bookmark not added': 'A könyvjelző nem lett hozzáadva', + 'Bookmark not removed': 'A könyvjelző nem lett törölve', + 'Add To Bookmarks': 'Hozzáadás a Könyvjelzőkhöz', + 'Remove From Bookmarks': 'Eltávolítás a Könyvjelzőkből', + 'Auth': 'Hitelesítés', + 'Already logged in': 'Már be vagy jelentkezve', + 'Input Email (for password recovery):': 'E-mail bevitel (jelszó helyreállításához):', + 'Input Email:': 'E-mail bevitel:', + 'Input Password (6+ symbols):': 'Jelszó bevitel (6+ karakter):', + 'Input Password:': 'Jelszó bevitel:', + 'Login successfull': 'Bejelentkezés sikeres', + 'Login failed': 'Bejelentkezés sikertelen', + 'User not logged in': 'A felhasználó nincs bejelentkezve', + 'User successfully logged out': 'A felhasználó sikeresen kijelentkezett', + 'Preloaded: ': 'Előtöltés: ', + 'Do you want to STOP torrent downloading and seeding?': 'Be akarod fejezni a torrent letöltését és seedelését?', + 'Torrent Downloading': 'Torrent letöltése', + 'Auth expired, please relogin': 'Hitelesítés lejárt, kérlek jelentkezz be újra', + 'Storage': 'Tárolóhely', + 'Storage has been cleared': 'A tárolóhely megtisztítva', + 'Clear Storage': 'Tárolóhely Megtisztítása', + 'Popular': 'Népszerű', + 'Views': 'Nézetek', + 'Uploading': 'Feltöltés', + 'Download': 'Letöltés', + 'Input symbols from CAPTCHA image:': 'Szimbólumok bevitele a CAPTCHA képről:', + 'Please, rate watched video:': 'Kérlek, értékeld a megnézett videót:', + 'Bad': 'Rossz', + 'So-So': 'Elmegy', + 'Good': 'Jó', + 'Ratings': 'Értékelések', + 'Rating': 'Értékelés', + 'Retry': 'Újra', + '%ds has left': '%ds van hátra', + 'File failed to play! Do you want to RETRY and buffer more?': 'A fájl lejátszása hibába ütközött. Újra akarod indítani és többet bufferelni?', + 'High Priority Files': 'Magas prioritású fájlok', + 'Skip All Files': 'Összes fájl kihagyása', + 'Start': 'Indít', + 'Stop': 'Megállít', + 'Play':'Lejátszás', + 'High Priority': 'Magas prioritás', + 'Skip File': 'Fájl kihagyása', + 'Remove': 'Eltávolítás', + 'Remove with files': 'Eltávolítás a fájlokkal együtt', + 'Play File': 'Fájl lejátszása', + 'Start All Files': 'Összes fájl elindítása', + 'Stop All Files': 'Összes fájl megállítása', + 'Torrent-client Browser': 'Torrentkliens Böngésző', + 'Remote Torrent-client': 'Távoli torrentkliens', + 'You didn\'t set up replacement path in setting.': 'Nem állítottál be helyettesítő elérési utat a beállításokban.', + 'For example /media/dl_torr/ to smb://SERVER/dl_torr/. Setup now?': 'Például /media/dl_torr/ a smb://SERVER/dl_torr/ - hez. Beállítod most?', + 'Manual Torrent-client Path Edit': 'Kézi torrentkliens elérési út módosítás', + 'Choose .torrent in video library': '.torrent kiválasztása a videó könyvtárban', + '.torrent Player': '.torrent Lejátszó', + 'Choose directory:': 'Könyvtár kiválasztása:', + 'Starting download next episode!': 'Elindul a következő epizód letöltése!', + 'Choose in torrent-client:': 'Kiválasztás a torrentkliensben:', + 'Search Control Window': 'Keresést Kezelő Ablak', + 'Magnet-link (magnet:...)': 'Magnet-link (magnet:...)', + 'Not a magnet-link!': 'Ez nem magnet-link!', + 'Magnet-link Player': 'Magnet-link Lejátszó', + 'UNKNOWN STATUS': 'ISMERETLEN STÁTUSZ', + 'Checking preloaded files...': 'Előtöltött fájlok ellenőrzése...', + 'Waiting for website response...': 'Várakozás a weboldal válaszára...', + 'Search and cache information for:': 'Információ keresése és gyorsítótárazása:', + 'Open Torrent': 'Torrent megnyitása', + 'Torrent list is empty.': 'A torrent lista üres.', + 'Content Lists': 'Tartalmak Listája', + 'Canceled by User': 'Leállítva felhasználó által', + 'Do you want to search and cache full metadata + arts?': 'Szeretnéd lekérni és gyorsítótárazni a metaadatokat és a képeket?', + 'This vastly decreases load speed, but you will be asked to download premade bases!': 'Ez jelentősen csökkenti a betöltési sebességet, megkérünk, hogy töltsd le az előre elkészített adatbázisokat!', + 'Do you want to preload full metadata?': 'Előtöltöd a metaadatokat?', + 'It is highly recommended!': 'Ez erősen ajánlott!', + 'TV Shows': 'Sorozatok', + 'Cartoons': 'Rajzfilmek', + 'Anime': 'Anime', + 'Most Recent': 'Legújabb', + 'Top 250 Movies': 'Top 250 Filmek', + 'Top All Time': 'Top Mindenkori', + 'by Genre': 'Műfaj alapján', + 'by Year': 'Évszám alapján', + 'Action': 'Akció', + 'Adventure': 'Kaland', + 'Animation': 'Animációs', + 'Biography': 'Életrajzi', + 'Comedy': 'Vígjáték', + 'Crime': 'Bűnügyi', + 'Documentary': 'Dokumentum', + 'Drama': 'Dráma', + 'Family': 'Családi', + 'Fantasy': 'Fantasy', + 'Film-Noir': 'Film-Noir', + 'History': 'Történelmi', + 'Horror': 'Horror', + 'Music': 'Zenei', + 'Musical': 'Musical', + 'Mystery': 'Misztikus', + 'Romance': 'Romantikus', + 'Sci-Fi': 'Sci-Fi', + 'Short': 'Rövidfilm', + 'Sport': 'Sport', + 'Thriller': 'Thriller', + 'War': 'Háborús', + 'Western': 'Western', + '[B]by Site[/B]': '[B]Weboldal alapján[/B]', + 'Cartoons Series': 'Rajzfilmsorozatok', + 'Cartoons Short': 'Rövid rajzfilmek', + 'Male': 'Férfi', + 'Female': 'Női', + 'Russia & USSR': 'Oroszország & Szovjetunió', + 'Next Page': 'Következő oldal', + 'Previous Page': 'Előző oldal', + 'Russian Movies': 'Orosz filmek', + 'israeli Movies': 'Izraeli fimek', + 'hebdub movies': 'hebdub filmek', + 'Movies': 'Filmek', + 'High Resolution Movies': 'HD filmek', + '3D Movies': '3D filmek', + 'Movies [Bluray]': 'Filmek [Bluray]', + 'Anime Film': 'Anime filmek', + 'Anime Series': 'Anime sorozatok', + 'Can\'t download torrent, probably no seeds available.': 'Nem lehet letölteni a torrentet, valószínűleg nincsenek elérhető seederek.', + 'Personal List': 'Saját lista', + 'Add to %s': 'Hozzáadás ehhez %s', + 'Delete from %s': 'Eltávolítás innen %s', + 'Added!': 'Hozzáadva!', + 'Deleted!': 'Eltávolítva!', + 'Search History': 'Keresési Előzmények', + ' History ':' Előzmények ', + 'Torrent History':'Torrent Előzmények', + 'Watched History':'Megtekintett Előzmények', + 'Favourites': 'Kedvencek', + 'Favourites SH': 'Kedvencek SH', + 'Clear %s': 'Tisztítás %s', + 'Clear!': 'Tisztítás!', + 'kb/s': 'kb/s', + 'Queued': 'Sorba állítva', + 'Checking': 'Ellenőrzés', + 'Downloading metadata': 'Metaadatok letöltése', + 'Downloading': 'Letöltés', + 'Finished': 'Befejezve', + 'Seeding': 'Seedelés', + 'Allocating': 'Összeállítás', + 'Allocating file & Checking resume': 'Fájl összeállítása és folytatás ellenőrzése', + 'For Kids': 'Gyerekek számára', + 'Adult': 'Felnőtt', + 'Does not support magnet links!': 'Nem támogatja a magnet-linkeket!', + 'Reset All Cache DBs': 'Összes gyorsítótár adatbázis visszaállítása', + '[B]Search[/B]': '[B]Keresés[/B]', + 'You can always restart this by deleting DBs via Context Menu': 'Mindig visszaállíthatja az adatbázis törlésével a helyi menün keresztül', + 'Your preloaded databases are outdated!': 'Az előtöltött adatbázisok elavultak!', + 'Do you want to download new ones right now?': 'Szeretnéd letölteni az újakat most?', + 'Individual Tracker Options':'Egyéni tracker beállítások', + 'Downloading and copy subtitles. Please wait.':'Feliratok letöltése és másolása. Kérlek, várj.', + 'International Check - First Run':'International ellenőrzés - első futtatás', + 'Delete Russian stuff?':'Orosz tartalom törlése?', + 'Save to path':'Mentés a mappába', + 'Return Russian stuff':'Orosz tartalom visszaállítása', + '%d files have been returned':'%d fájlok visszaállítva', + 'Download via T-client':'Letöltés T-kliensen keresztül', + 'Download via Libtorrent':'Letöltés Libtorrenten keresztül', + 'Download Status':'Letöltési Állapot', + 'Download has not finished yet':'A letöltés még nem fejeződött be', + 'Stopped and Deleted!':'Leállítva és eltávolítva!', + 'Unpaused!':'Újra elindítva!', + 'Paused!':'Szünetelve!', + 'Stopped!':'Megállítva!', + 'Started!':'Elindítva!', + 'Delete and Stop':'Megállít és eltávolít', + 'Unpause':'Újra elindít', + 'Pause':'Szüneteltet', + 'Delete':'Eltávolít', + 'Open':'Megnyitás', + 'Torrent is seeding. To stop it use Download Status.':'Torrent seedelés alatt. Ahhoz, hogy megállítsd használd a Letöltési Állapotot.', + 'Start All':'Összes elindítása', + 'Started All!':'Összes elindítva!', + 'Stopped All!':'Összes megállítva!', + 'Stop All':'Összes megállítása', + 'Keyboard':'Billentyűzet', + 'Copy Files in Root':'Fájlok másolása a Rendszerkönyvtárba', + 'Copied %d files!':'%d fájl átmásolva!', + 'Add to MyShows.ru':'Hozzáadás MyShows.ru-hoz', + 'Return to MyShows.ru':'Visszatérés a MyShows.ru-hoz', + 'Search results:':'Keresési eredmények:', + 'by Seeders':'Seederek alapján', + 'by Date':'Dátum alapján', + 'Sort':'Rendezés', + 'Close':'Bezár', + 'Views:':'Nézetek:', + 'Rating:':'Értékelés:', + 'Information not found!':'Nem található információ!', + 'Choose searcher':'Válassz keresőt', + 'python-libtorrent Not Found':'python-libtorrent nem található', + 'Windows has static compiled python-libtorrent included.':'A Windows statikusan fordított python-libtorrentet tartalmaz.', + 'You should install "script.module.libtorrent" from "MyShows.me Kodi Repo"':'Telepítsd a "script.module.libtorrent"-et a "MyShows.me Kodi Repo"-ból', + 'Linux x64 has not static compiled python-libtorrent included.':'A Linux x64 statikusan fordított python-libtorrentet nem tartalmaz.', + 'You should install it by "sudo apt-get install python-libtorrent"':'Telepítsd ezzel a paranccsal "sudo apt-get install python-libtorrent"', + 'Linux has static compiled python-libtorrent included but it didn\'t work.':'A Linux statikusan fordított python-libtorrentet tartalmaz de nem működik.', + 'As far as I know you can compile python-libtorrent for ARMv6-7.':'Ahogy én tudom, le lehet fordítani a python-libtorrentet ARMv6-7-ra', + 'You should search for "OneEvil\'s OpenELEC libtorrent" or use Ace Stream.':'Keress rá "OneEvil\'s OpenELEC libtorrent"-re vagy használj Ace Streamet', + 'Please use install Ace Stream APK and choose it in Settings.':'Kérlek használj Ace Stream APK-t és válaszd ki a beállításokban.', + 'It is possible to compile python-libtorrent for Android, but I don\'t know how.':'Lehetséges, hogy lefordítható a python-libtorrent Android-ra, de nem tudom hogyan.', + 'It is possible to compile python-libtorrent for OS X.':'Lehetséges, hogy lefordítható a python-libtorrent OS X-re.', + 'But you would have to do it by yourself, there is some info on github.com.':'De ezt magadnak kell megcsinálnod, van néhány infó erről a github.com-on.', + 'It is NOT possible to compile python-libtorrent for iOS.':'iOS-re nem lehet lefordítani a python-libtorrentet.', + 'But you can use torrent-client control functions.':'De a torrentkliens kezelőt funkciót tudod használni.', + 'I added custom searchers to Torrenter v2!':'Hozzáadtam egyéni keresőket a Torrenter v2-höz!', + 'Now you can use your login on trackers or write and install your own searcher!':'Most már be tudsz lépni a trackerekre, vagy megírhatod és feltelepítheted a saját keresődet!', + 'Would you like to install %s from "MyShows.me Kodi Repo" in Programs section?':'Szeretnéd telepíteni %s a "MyShows.me Kodi Repo"-ból?', + 'Open installation window?':'Telepítő ablak megnyitása?', + 'Android Support':'Android támogatás', + 'Android has no temprorary folder':'Az Android nem rendelkezik ideiglenes mappával', + 'Please specify storage folder in Settings!':'Kérlek add meg a tárolóhely mappáját a beállításokban!', + 'You have no installed or active searchers! More info in Search Control Window!':'Nincsenek telepített vagy aktív keresők! Több információ a Keresést Kezelő Abalakban.', + 'Please contact DiMartino on kodi.tv forum. We compiled python-libtorrent for Android,':'Kérünk lépj kapcsolatba DiMartino-val a kodi.tv fórumon. Lefordítottuk a python-libtorrentet Androidhoz,', + 'but we need your help with some tests on different processors.':'de szükségünk lesz még néhány tesztre különböző processzorokon.', + 'We added Android ARM full support to Torrenter v2!':'A Torrenter v2 számara teljesen támogatottá tettük az Android ARM-t!', + 'I deleted pre-installed ones, install them in Search Control Window!':'Töröltem az összes előre telepítettet, telepítsd őket a Keresést Kezelő Ablakban!', + 'Torrenter didn\'t find %s searcher':'A Torrenter nem találja a %s keresőt', + 'Torrenter Tracker Install':'Torrent tracker telepítés', + 'Ask to save':'Mentés kérése', + 'Would you like to save this file?':'El szeretnéd menteni ez a fájlt?', + 'Your storage path is not writable or not local! Please change it in settings!':'A tárolóhely elérési útja nem írható vagy nem elérhető! Kérlek változtatsd meg a beállításokban!', + 'Upgrade advancedsettings.xml':'advancedsettings.xml frissítése', + 'We would like to set some advanced settings for you!':'Be szeretnénk állítani néhány haladó beállítást!', + 'Do it!':'Csináld!', + 'Please, restart Kodi now!':'Kérlek, indítsd újra a Kodit, most.', + './ (Root folder)':'./ (Rendszerkönyvtár)', + 'Opening torrent file':'Torrent fájl megnyitása', + 'New player to Torrenter v2 - Torrent2HTTP! It should be faster, stable and better with Android, also seeking works in it.':'Új lejátszó a Torrenter v2-höz - Torrent2HTTP: Gyorsabb, stabilabb és jobb Androiddal, valamint a keresés is működik vele.', + 'Would you like to try it?':'Ki szeretnéd próbálni?', + 'Torrent2HTTP enabled! Can be changed in Settings.':'Torrent2HTTP engedélyezve! Ezen a beállításokban tudsz változtatni.', + 'Seeking':'Keresés', + 'Would you like to resume from %s?':'Szeretnéd folytatni innen: %s?', + 'Seeking is working only with player Torrent2HTTP.':'A keresés csak a Torrent2HTTP lejátszóval működik.', + 'Play (from %s)':'Lejátszás (innen %s)', + 'Play (from start)':'Lejátszás (az elejétől)', + }, 'ru': { + 'Torrenter has a better view style for Kodi 17 default skin.':'У Torrenter есть оптимизированный вид под новый скин Kodi 17.', + 'is recommended for Kodi 17 users and now out of beta.': 'рекомендовано пользователям Kodi 17 и вышло из беты.', + 'You can disable it usage in Settings.':'Его можно отключить в настройках плагина.', + 'Move Up': 'Вверх', + 'Torrenter Search Window': 'Окно Поиска Torrenter', + 'Cancel': 'Отмена', + 'Clear History': 'Очистить Историю', + 'Play (with seek)': 'Играть (перемотка)', + 'Mass Control':'Массовое Управление', + 'Info':'Инфо', + 'Delete torrent with files?':'Вы уверены, что хотите удалить торрент с файлами?', + 'Fav. / Unfav.':'Доб./удал. Избранное', + 'Search Window': 'Окно поиска', + 'Context menu': 'Контекстное меню', 'Seeds searching.': 'Идёт поиск сидов.', 'Please Wait': 'Подождите', 'Information': 'Информация', @@ -376,7 +908,7 @@ def localize(text): 'Checking preloaded files...': 'Проверка файлов...', 'Waiting for website response...': 'Ожидание ответа сайта...', 'Search and cache information for:': 'Поиск и кэширование информации для:', - 'Open Torrent': 'Открыть Список файлов', + 'Open Torrent': 'Открыть Торрент', 'Torrent list is empty.': 'Список раздач пуст.', 'Content Lists': 'Списки Медиа', 'Canceled by User': 'Отменено пользователем', @@ -483,7 +1015,7 @@ def localize(text): 'Unpause':'Возобновить', 'Pause':'Пауза', 'Delete':'Удалить', - 'Open (no return)':'Открыть (без возврата)', + 'Open':'Открыть', 'Torrent is seeding. To stop it use Download Status.':'Сидирование. Для остановки используйте Статус Загрузки.', 'Start All':'Запустить Все', 'Started All!':'Все Запущены!', @@ -491,6 +1023,7 @@ def localize(text): 'Stop All':'Остановить Все', 'Keyboard':'Клавиатура', 'Copy Files in Root':'Скопировать файлы в Корень', + 'Copy in Root': 'Скопировать в Корень', 'Copied %d files!':'Скопировано %d файлов!', 'Return to %s':'Вернуться в %s', 'Search results:':'Результаты поиска:', @@ -741,7 +1274,7 @@ def localize(text): 'Unpause':'Відновити', 'Pause':'Пауза', 'Delete':'Видалити', - 'Open (no return)':'Відкрити (без повернення)', + 'Open':'Відкрити', 'Torrent is seeding. To stop it use Download Status.':'Сідування. Для зупинки використовуйте Статус завантаження.', 'Start All':'Запустити все', 'Started All!':'Все запущене!', diff --git a/Player.py b/Player.py index 4376657..78909ba 100644 --- a/Player.py +++ b/Player.py @@ -30,7 +30,7 @@ import Downloader import xbmcgui import xbmcvfs import Localization -from functions import calculate, showMessage, clearStorage, WatchedHistoryDB, DownloadDB, get_ids_video, log, debug, foldername +from functions import calculate, showMessage, clearStorage, WatchedHistoryDB, DownloadDB, get_ids_video, log, debug, foldername, ensure_str, loadsw_onstop ROOT = sys.modules["__main__"].__root__ RESOURCES_PATH = os.path.join(ROOT, 'resources') @@ -166,8 +166,12 @@ class TorrentPlayer(xbmc.Player): debug('************************************* GOING LOOP') self.torrent.startSession() self.torrent.continueSession(self.contentId) + WatchedHistoryDB().add(self.basename, self.torrentUrl, + foldername(self.torrent.getContentList()[self.contentId]['title']), + self.watchedTime, self.totalTime, self.contentId, + self.fullSize / 1024 / 1024) self.loop() - WatchedHistoryDB().add(self.basename, foldername(self.torrent.getContentList()[self.contentId]['title']), self.watchedTime, self.totalTime, self.contentId, self.fullSize / 1024 / 1024) + WatchedHistoryDB().add(self.basename, self.torrentUrl, foldername(self.torrent.getContentList()[self.contentId]['title']), self.watchedTime, self.totalTime, self.contentId, self.fullSize / 1024 / 1024) else: break debug('************************************* GO NEXT?') @@ -193,11 +197,13 @@ class TorrentPlayer(xbmc.Player): else: if self.seeding_status: showMessage(self.localize('Information'), - self.localize('Torrent is seeding. To stop it use Download Status.'), forced=True) + self.localize('Torrent is seeding. To stop it use Download Status.')) else: if self.seeding: self.db_delete() showMessage(self.localize('Information'), - self.localize('Torrent downloading is stopped.'), forced=True) + self.localize('Torrent downloading is stopped.')) + + loadsw_onstop() # Reload Search Window def init(self): self.next_dl = True if self.__settings__.getSetting('next_dl') == 'true' and self.ids_video else False @@ -338,12 +344,12 @@ class TorrentPlayer(xbmc.Player): response = json.loads(request) xbmc.sleep(1000) + if self.get('listitem'): + listitem = self.get('listitem') + listitem.setPath(path) + if response: xbmc.Player().play(path, listitem) - #playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) - #playlist.clear() - #playlist.add(path, listitem) - #xbmc.Player().play(playlist) xbmc.sleep(2000) # very important, do not edit this, podavan i = 0 @@ -367,7 +373,7 @@ class TorrentPlayer(xbmc.Player): if len(subs) > 0: self.torrent.startSession() showMessage(self.localize('Information'), - self.localize('Downloading and copy subtitles. Please wait.'), forced=True) + self.localize('Downloading and copy subtitles. Please wait.')) for ind, title in subs: self.torrent.continueSession(ind) while iterator < 100: @@ -455,7 +461,7 @@ class TorrentPlayer(xbmc.Player): if self.iterator == 100 and self.next_dl and not self.next_dling and isinstance(self.next_contentId, int) and self.next_contentId != False: showMessage(self.localize('Torrent Downloading'), - self.localize('Starting download next episode!'), forced=True) + self.localize('Starting download next episode!')) self.torrent.stopSession() # xbmc.sleep(1000) path = self.torrent.getFilePath(self.next_contentId) @@ -465,10 +471,10 @@ class TorrentPlayer(xbmc.Player): def _get_status_lines(self, s): return [ - self.display_name+'; '+self.torrent.get_debug_info('dht_state'), - "%.2f%% %s; %s" % (s.progress * 100, self.localize(STATE_STRS[s.state]).decode('utf-8'), self.torrent.get_debug_info('trackers_sum')), - "D:%.2f%s U:%.2f%s S:%d P:%d" % (s.download_rate / 1024, self.localize('kb/s').decode('utf-8'), - s.upload_rate / 1024, self.localize('kb/s').decode('utf-8'), + ensure_str(self.display_name), + "%.2f%% %s" % (s.progress * 100, self.localize(STATE_STRS[s.state])), + "D:%.2f%s U:%.2f%s S:%d P:%d" % (s.download_rate / 1024, self.localize('kb/s'), + s.upload_rate / 1024, self.localize('kb/s'), s.num_seeds, s.num_peers) ] diff --git a/SearcherABC.py b/SearcherABC.py index 35f1b3d..9b49903 100644 --- a/SearcherABC.py +++ b/SearcherABC.py @@ -35,7 +35,9 @@ import xbmc import Localization from functions import log, debug, showMessage +import ssl +#ssl._create_default_https_context = ssl._create_unverified_context class SearcherABC: searchIcon = '/icons/video.png' sourceWeight = 1 @@ -103,19 +105,25 @@ class SearcherABC: def makeRequest(self, url, data={}, headers={}): self.load_cookie() - opener = None + opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookieJar)) if self.proxy == 1: try: from resources.proxy import antizapret - opener = urllib2.build_opener(antizapret.AntizapretProxyHandler(), urllib2.HTTPCookieProcessor(self.cookieJar)) + opener.add_handler(antizapret.AntizapretProxyHandler()) config = antizapret.config() self.debug('[antizapret]: '+str(config["domains"])) self.debug('[antizapret]: '+str(config["server"])) except: showMessage('AntiZapret', Localization.localize('Error')) self.debug('[antizapret]: OFF!') - if not opener: - opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookieJar)) + # python ssl Context support - PEP 0466 + if 'https:' in url: + ssl_context = ssl.create_default_context() + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + log('urllib2.HTTPSHandler(context=ssl_context)') + opener.add_handler(urllib2.HTTPSHandler(context=ssl_context)) + opener.addheaders = headers if 0 < len(data): encodedData = urllib.urlencode(data) @@ -134,13 +142,14 @@ class SearcherABC: self.log('[makeRequest]: HTTP Error, e.code=' + str(e.code)) return #self.cookieJar.extract_cookies(response, urllib2) + #self.log(response.info().get('Set-Cookie')) if response.info().get('Content-Encoding') == 'gzip': buf = StringIO(response.read()) decomp = zlib.decompressobj(16 + zlib.MAX_WBITS) - response = decomp.decompress(buf.getvalue()) + text = decomp.decompress(buf.getvalue()) else: - response = response.read() - return response + text = response.read() + return text def askCaptcha(self, url): temp_dir = self.tempdir() diff --git a/SkorbaLoader.py b/SkorbaLoader.py index 30b02c4..cd316db 100644 --- a/SkorbaLoader.py +++ b/SkorbaLoader.py @@ -32,7 +32,7 @@ import xbmcgui import xbmcvfs import Localization from functions import isSubtitle, DownloadDB, log, debug, is_writable,\ - vista_check, windows_check, localize_path + vista_check, windows_check, localize_path, decode_str class SkorbaLoader: magnetLink = None @@ -54,7 +54,7 @@ class SkorbaLoader: if not is_writable(self.storageDirectory): xbmcgui.Dialog().ok(Localization.localize('Torrenter v2'), Localization.localize('Your storage path is not writable or not local! Please change it in settings!'), - Localization.localize(self.storageDirectory)) + self.storageDirectory) sys.exit(1) @@ -93,24 +93,14 @@ class SkorbaLoader: else: if not xbmcvfs.exists(self.torrentFilesPath): xbmcvfs.mkdirs(self.torrentFilesPath) - torrentFile = self.torrentFilesPath + self.md5( - torrentUrl) + '.torrent' + torrentFile = localize_path(os.path.join(self.torrentFilesPath, self.md5(torrentUrl) + '.torrent')) try: - if not re.match("^http\:.+$", torrentUrl): - contentFile = xbmcvfs.File(torrentUrl, "rb") - content = contentFile.read() - contentFile.close() + if not re.match("^[htps]+?://.+$|^://.+$", torrentUrl): + log('xbmcvfs.File for %s' % torrentUrl) + content = xbmcvfs.File(torrentUrl, "rb").read() else: - request = urllib2.Request(torrentUrl) - request.add_header('Referer', torrentUrl) - request.add_header('Accept-encoding', 'gzip') - result = urllib2.urlopen(request) - if result.info().get('Content-Encoding') == 'gzip': - buf = StringIO(result.read()) - decomp = zlib.decompressobj(16 + zlib.MAX_WBITS) - content = decomp.decompress(buf.getvalue()) - else: - content = result.read() + log('request for %s' % torrentUrl) + content = self.makeRequest(torrentUrl) localFile = xbmcvfs.File(torrentFile, "w+b") localFile.write(content) @@ -128,7 +118,7 @@ class SkorbaLoader: return if not xbmcvfs.exists(self.torrentFilesPath): xbmcvfs.mkdirs(self.torrentFilesPath) - newFile = self.torrentFilesPath + self.md5(torrentUrl) + '.torrent' + newFile = localize_path(self.torrentFilesPath + self.md5(torrentUrl) + '.torrent') if newFile != torrentFile: if xbmcvfs.exists(newFile): xbmcvfs.delete(newFile) @@ -145,7 +135,30 @@ class SkorbaLoader: self.torrentFileInfo = self.lt.torrent_info(e) self.torrentFile = torrentFile return self.torrentFile - + + def makeRequest(self, torrentUrl): + torrentUrl = re.sub('^://', 'http://', torrentUrl) + x = re.search("://(.+?)/|://(.+?)$", torrentUrl) + if x: + baseurl = x.group(1) if x.group(1) else x.group(2) + else: + baseurl ='' + + headers = [('User-Agent', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 YaBrowser/14.10.2062.12061 Safari/537.36'), + ('Referer', 'http://%s/' % baseurl), ('Accept-encoding', 'gzip'), ] + + opener = urllib2.build_opener() + opener.addheaders = headers + result = opener.open(torrentUrl) + if result.info().get('Content-Encoding') == 'gzip': + buf = StringIO(result.read()) + decomp = zlib.decompressobj(16 + zlib.MAX_WBITS) + content = decomp.decompress(buf.getvalue()) + else: + content = result.read() + return content + def getMagnetInfo(self): magnetSettings = { 'url': self.magnetLink, @@ -157,14 +170,11 @@ class SkorbaLoader: } progressBar = xbmcgui.DialogProgress() progressBar.create(Localization.localize('Please Wait'), Localization.localize('Magnet-link is converting')) - #try: self.torrentHandle = self.session.add_torrent(magnetSettings) - #except: - # self.torrentHandle = self.lt.add_magnet_uri(self.session, self.magnetLink, magnetSettings) iterator = 0 + if self.enable_dht: self.torrentHandle.force_dht_announce() while iterator < 100: xbmc.sleep(500) - if self.enable_dht: self.torrentHandle.force_dht_announce() progressBar.update(iterator, Localization.localize('Please Wait'), Localization.localize('Magnet-link is converting')+'.' * (iterator % 4), ' ') iterator += 1 if progressBar.iscanceled(): @@ -237,8 +247,9 @@ class SkorbaLoader: def getContentList(self): filelist = [] + #from functions import decode_str for contentId, contentFile in enumerate(self.torrentFileInfo.files()): - stringdata = {"title": localize_path(contentFile.path), "size": contentFile.size, "ind": int(contentId), + stringdata = {"title": contentFile.path, "size": contentFile.size, "ind": int(contentId), 'offset': contentFile.offset} filelist.append(stringdata) return filelist @@ -307,13 +318,13 @@ class SkorbaLoader: else: for i in range(self.torrentFileInfo.num_pieces()): self.torrentHandle.piece_priority(i, 6) + del db thread.start_new_thread(self.downloadLoop, (title,)) def downloadLoop(self, title): db = DownloadDB() status = 'downloading' while db.get(title) and status != 'stopped': - xbmc.sleep(3000) status = db.get_status(title) if not self.paused: if status == 'pause': @@ -332,7 +343,9 @@ class SkorbaLoader: iterator = int(s.progress * 100) info['progress'] = iterator db.update(title, info) - self.debug() + #self.debug() + xbmc.sleep(3000) + log('out of downloadLoop') self.session.remove_torrent(self.torrentHandle) return diff --git a/addon.xml b/addon.xml index 3510c38..9de2fff 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - - + + @@ -8,6 +8,7 @@ + video @@ -23,10 +24,14 @@ Так же плагин может добавлять, проигрывать и управлять скачками в торрент клиентах (uTorrent, Transmisson, Deluge и Vuse, qBittorrent) или средставми python-libtorrent. - GNU GPLv3 http://www.gnu.org/licenses/ - https://forums.tvaddons.ag/addon-releases/29224-torrenter-v2.html - http://xbmc.ru/forum/showthread.php?t=6837 - skype:kyonkodura - https://github.com/DiMartinoXBMC/plugin.video.torrenter + GNU GPLv3 http://www.gnu.org/licenses/ + Complemento que nos permite ver vídeos que se distribuyen en redes BitTorrent sin necesidad de descargarlos previamente por completo. + + También gestiona torrents y nos deja elegir entre su propio cliente interno y otros externos (μTorrent, Transmission, Deluge, Vuze, qBittorrent), el cliente BitTorrent con el que realizar las descargas. + + https://forums.tvaddons.ag/addon-releases/29224-torrenter-v2.html + http://xbmc.ru/forum/showthread.php?t=6837 + skype:kyonkodura + https://github.com/DiMartinoXBMC/plugin.video.torrenter diff --git a/changelog.txt b/changelog.txt index 09674ff..777a953 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,13 +1,40 @@ English changelog at http://bit.ly/1MfSVUP +[B]Version 2.6.2[/B] +[+] История Просмотров: Перемотка при внешнем вызове + +[B]Version 2.6.1[/B] +[+] Окно Поиска: Использование окна в сторонних плагинах + +[B]Version 2.6.0[/B] +[+] Окно Поиска: Полноценный релиз +[+] Настройки: Добавлена возможность отключения уведомлений +[+] История Просмотров: При аварийном выходе данные остаются + +[B]Version 2.5.6[/B] +[+] Списки Медиа: Добавлен RuTorOrg + +[B]Version 2.5.5[/B] +[+] Control Center: Исправлена работа выбора трекеров для определенного запроса +[+] Торрент-клиент: При скачивании одного файла торрент-клиентом теперь автоматически выбирается раздача +[+] .torrent Player: Исправлена работа (спасибо kolosovski) +[+] Окно Поиска: Добавлено Окно Поиска в режиме тестирования + +[B]Version 2.5.4[/B] +[+] Исправлено "открыть без возврата". Спасибо viorel-m. + +[B]Version 2.5.3[/B] +[+] Венгерский язык +[+] Совместимость с Коди 17 + [B]Version 2.5.2[/B] -[+] Упорядочивание по разверу файла. +[+] Упорядочивание по размеру файла [B]Version 2.5.1[/B] -[+] Исправлена работа с кодировками. +[+] Исправлена работа с кодировками [B]Version 2.5.0[/B] -[+] НОВЫЙ проигрыватель pyrrent2http от inpos! Аналог torrent2http написаный на python, а не на GO. +[+] НОВЫЙ проигрыватель pyrrent2http от inpos! Аналог torrent2http написаный на python, а не на GO [+] Проигрыватель: Ускорена повторная работа с торрентом - resume data (спасибо srg70 и RussakHH) [B]Version 2.4.6[/B] diff --git a/controlcenter.py b/controlcenter.py index 7371021..7bd7476 100644 --- a/controlcenter.py +++ b/controlcenter.py @@ -24,7 +24,7 @@ import xbmcaddon import xbmc import xbmcgui from functions import getParameters, HistoryDB, Searchers, log -from resources.pyxbmct.addonwindow import * +import pyxbmct __settings__ = xbmcaddon.Addon(id='plugin.video.torrenter') __language__ = __settings__.getLocalizedString @@ -39,7 +39,7 @@ if len(sys.argv) > 1: else: params = {} -class ControlCenter(AddonDialogWindow): +class ControlCenter(pyxbmct.AddonDialogWindow): def __init__(self, title, addtime=None): super(ControlCenter, self).__init__(title) @@ -57,18 +57,17 @@ class ControlCenter(AddonDialogWindow): if not providers: self.db.set_providers(addtime, self.dic) else: - for searcher in self.keys: + for searcher in self.dic.keys(): self.dic[searcher] = False for searcher in providers: try: - if searcher in self.keys: + if searcher in self.dic.keys(): self.dic[searcher] = True except: - pass + log('self.dic[searcher] except') self.keys = self.dic.keys() self.placed, self.button_columns, self.last_column_row = self.place() - #print str((self.placed, self.button_columns, self.last_column_row)) else: self.button_columns=0 @@ -78,7 +77,7 @@ class ControlCenter(AddonDialogWindow): self.set_active_controls() self.set_navigation() # Connect a key action (Backspace) to close the window. - self.connect(ACTION_NAV_BACK, self.close) + self.connect(pyxbmct.ACTION_NAV_BACK, self.close) def place(self): placed = {} @@ -90,7 +89,6 @@ class ControlCenter(AddonDialogWindow): else: i += 1 placed[item] = (j, i) - #print item+str((j, i)) return placed, j, i def set_info_controls(self): @@ -103,7 +101,7 @@ class ControlCenter(AddonDialogWindow): self.radiobutton_top, self.radiobutton_bottom = [None, None, None], [None, None, None] for searcher in self.keys: place = self.placed[searcher] - self.radiobutton[searcher] = RadioButton(searcher) + self.radiobutton[searcher] = pyxbmct.RadioButton(searcher) self.placeControl(self.radiobutton[searcher], place[0], place[1]) self.radiobutton[searcher].setSelected(self.dic[searcher]) self.connect(self.radiobutton[searcher], self.radio_update) @@ -112,32 +110,32 @@ class ControlCenter(AddonDialogWindow): self.radiobutton_bottom[place[1]] = self.radiobutton[searcher] # Button - self.button_install = Button(__language__(30415)) + self.button_install = pyxbmct.Button(__language__(30415)) self.placeControl(self.button_install, 2 + self.button_columns, 0) self.connect(self.button_install, self.installSearcher) # Button - self.button_openserchset = Button(__language__(30416)) + self.button_openserchset = pyxbmct.Button(__language__(30416)) self.placeControl(self.button_openserchset, 2 + self.button_columns, 1) self.connect(self.button_openserchset, self.openSearcherSettings) # Button - self.button_clearstor = Button(__language__(30417)) + self.button_clearstor = pyxbmct.Button(__language__(30417)) self.placeControl(self.button_clearstor, 2 + self.button_columns, 2) self.connect(self.button_clearstor, self.clearStorage) # Button - self.button_openset = Button(__language__(30413)) + self.button_openset = pyxbmct.Button(__language__(30413)) self.placeControl(self.button_openset, 3 + self.button_columns, 0) self.connect(self.button_openset, self.openSettings) # Button - self.button_utorrent = Button(__language__(30414)) + self.button_utorrent = pyxbmct.Button(__language__(30414)) self.placeControl(self.button_utorrent, 3 + self.button_columns, 1) self.connect(self.button_utorrent, self.openUtorrent) # Button - self.button_close = Button(__language__(30412)) + self.button_close = pyxbmct.Button(__language__(30412)) self.placeControl(self.button_close, 3 + self.button_columns, 2) self.connect(self.button_close, self.close) @@ -171,8 +169,6 @@ class ControlCenter(AddonDialogWindow): ser = placed_keys[placed_values.index((place[0], place[1] - 1))] self.radiobutton[searcher].controlLeft(self.radiobutton[ser]) - #print str((self.button_columns, self.last_column_row)) - #print searcher if self.more_two_searcher: if place == (self.button_columns, self.last_column_row) and self.last_column_row < 2: ser = placed_keys[placed_values.index((place[0] - 1, place[1] + 1))] @@ -247,7 +243,10 @@ class ControlCenter(AddonDialogWindow): def openSearcherSettings(self): slist=Searchers().activeExternal() if len(slist)>0: - ret = xbmcgui.Dialog().select(__language__(30418), slist) + if len(slist) == 1: + ret = 0 + else: + ret = xbmcgui.Dialog().select(__language__(30418), slist) if ret > -1 and ret < len(slist): sid = slist[ret] Searcher=xbmcaddon.Addon(id='torrenter.searcher.'+sid) diff --git a/functions.py b/functions.py index d514917..9d93a70 100644 --- a/functions.py +++ b/functions.py @@ -43,7 +43,8 @@ except: __settings__ = xbmcaddon.Addon(id='plugin.video.torrenter') __language__ = __settings__.getLocalizedString ROOT = __settings__.getAddonInfo('path') # .decode('utf-8').encode(sys.getfilesystemencoding()) -userStorageDirectory = __settings__.getSetting("storage") +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') @@ -92,10 +93,10 @@ def clearStorage(userStorageDirectory, force = False): if saved_bool: shutil.move(saved_temp, saved) - showMessage(Localization.localize('Storage'), Localization.localize('Storage has been cleared'), forced=True) + showMessage(Localization.localize('Storage'), Localization.localize('Storage has been cleared')) else: - showMessage(Localization.localize('Storage'), Localization.localize('Does not exists'), forced=True) + showMessage(Localization.localize('Storage'), Localization.localize('Does not exists')) log('[clearStorage]: fail storage '+userStorageDirectory + os.sep) try: @@ -151,8 +152,9 @@ def debug(msg, forced=False): def showMessage(heading, message, times=10000, forced=False): - xbmc.executebuiltin('XBMC.Notification("%s", "%s", %s, "%s")' % ( - heading.replace('"', "'"), message.replace('"', "'"), times, icon)) + 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))) @@ -575,32 +577,52 @@ def view_style(func): styles['sectionMenu'] = styles['Seasons'] = 'list' styles['uTorrentBrowser'] = styles['torrentPlayer'] = styles['openTorrent'] = 'wide' styles['showFilesList'] = styles['DownloadStatus'] = 'wide' - elif view_style in [1, 4, 5]: + 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 == 1: + 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'] = 'icons' + 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) - # debug('[view_style]: lock '+str(style)) + log('[view_style]: lock '+str(style)+' for '+str(func)) lockView(style, num_skin) @@ -610,23 +632,38 @@ def lockView(viewId='info', num_skin=0): {'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 ''' - PosterWrapView2_Fanart - MediaListView3 - MediaListView2 - MediaListView4 - WideIconView - MusicVideoInfoListView - AddonInfoListView1 - AddonInfoThumbView1 - LiveTVView1 - ''' def torrent_dir(): @@ -899,7 +936,6 @@ class HistoryDB: self.cur.execute('select providers from history where addtime="' + addtime + '"') x = self.cur.fetchone() self._close() - # print 'get_providers: '+str(x[0].split(',') if x and x[0]!='' else None) return x[0].split(',') if x and x[0] != '' else None def set_providers(self, addtime, providers): @@ -916,7 +952,7 @@ class HistoryDB: self._close() def change_providers(self, addtime, searcher): - self._connect() + #self._connect() providers = self.get_providers(addtime) keys = Searchers().dic().keys() if providers and len(providers) > 0: @@ -928,8 +964,8 @@ class HistoryDB: if i not in keys: providers.remove(i) self.set_providers(addtime, providers) - self.db.commit() - self._close() + #self.db.commit() + #self._close() def add(self, url): self._connect() @@ -1035,13 +1071,23 @@ class WatchedHistoryDB: self._close() return x if x else None - def add(self, filename, foldername = None, seek = 0, length = 1, ind = 0, size = 0): - watchedPercent = int((float(seek) / float(length)) * 100) + 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") + #path = __settings__.getSetting("lastTorrent") if not foldername: foldername = '' self.cur.execute('delete from history where filename="' + decode(filename) + '"') @@ -1289,9 +1335,76 @@ def search(url, searchersList, isApi=None): progressBar.close() for k in result.keys(): - filesList.extend(result[k]) + 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='' @@ -1306,6 +1419,8 @@ class Contenters(): 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] @@ -1358,7 +1473,7 @@ class Contenters(): searchersList = [] dirList = os.listdir(ROOT + os.sep + 'resources' + os.sep + 'contenters') for searcherFile in dirList: - if re.match('^(\w+)\.py$', searcherFile): + if re.match('^(\w+)\.py$', searcherFile) and searcherFile != '__init__.py': searchersList.append(searcherFile.replace('.py', '')) return searchersList @@ -1770,6 +1885,29 @@ def first_run_250(): #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) @@ -1910,7 +2048,7 @@ def check_network_advancedsettings(): file_cont=''' 2 - 30 + 30 252420 5 @@ -1927,7 +2065,7 @@ def check_network_advancedsettings(): ''' 2 - 30 + 30 252420 5 @@ -2063,13 +2201,19 @@ def localize_path(path): def encode_msg(msg): try: - msg = isinstance(msg, unicode) and msg.encode('utf-8') or msg + 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", @@ -2101,4 +2245,84 @@ def get_platform(): ret["os"] = "ios" ret["arch"] = "arm" - return ret \ No newline at end of file + 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' \ No newline at end of file diff --git a/icons/ContentPanel.png b/icons/ContentPanel.png new file mode 100644 index 0000000..e8c13eb Binary files /dev/null and b/icons/ContentPanel.png differ diff --git a/icons/kodi.png b/icons/kodi.png new file mode 100644 index 0000000..46a33e1 Binary files /dev/null and b/icons/kodi.png differ diff --git a/icons/searchwindow/button.psd b/icons/searchwindow/button.psd new file mode 100644 index 0000000..2965a22 Binary files /dev/null and b/icons/searchwindow/button.psd differ diff --git a/icons/searchwindow/fcontrolcenter.png b/icons/searchwindow/fcontrolcenter.png new file mode 100644 index 0000000..9b579ae Binary files /dev/null and b/icons/searchwindow/fcontrolcenter.png differ diff --git a/icons/searchwindow/fdeluge.png b/icons/searchwindow/fdeluge.png new file mode 100644 index 0000000..51b7cd8 Binary files /dev/null and b/icons/searchwindow/fdeluge.png differ diff --git a/icons/searchwindow/fdownloadstatus.png b/icons/searchwindow/fdownloadstatus.png new file mode 100644 index 0000000..7c09306 Binary files /dev/null and b/icons/searchwindow/fdownloadstatus.png differ diff --git a/icons/searchwindow/fhistory.png b/icons/searchwindow/fhistory.png new file mode 100644 index 0000000..951e241 Binary files /dev/null and b/icons/searchwindow/fhistory.png differ diff --git a/icons/searchwindow/fkeyboard.png b/icons/searchwindow/fkeyboard.png new file mode 100644 index 0000000..8d993d1 Binary files /dev/null and b/icons/searchwindow/fkeyboard.png differ diff --git a/icons/searchwindow/fqbittorrent.png b/icons/searchwindow/fqbittorrent.png new file mode 100644 index 0000000..c8dd9e1 Binary files /dev/null and b/icons/searchwindow/fqbittorrent.png differ diff --git a/icons/searchwindow/fsearch.png b/icons/searchwindow/fsearch.png new file mode 100644 index 0000000..8e786c2 Binary files /dev/null and b/icons/searchwindow/fsearch.png differ diff --git a/icons/searchwindow/ftorrent-client.png b/icons/searchwindow/ftorrent-client.png new file mode 100644 index 0000000..da2b9ab Binary files /dev/null and b/icons/searchwindow/ftorrent-client.png differ diff --git a/icons/searchwindow/ftransmission.png b/icons/searchwindow/ftransmission.png new file mode 100644 index 0000000..0fc7721 Binary files /dev/null and b/icons/searchwindow/ftransmission.png differ diff --git a/icons/searchwindow/fvuze.png b/icons/searchwindow/fvuze.png new file mode 100644 index 0000000..9762c0a Binary files /dev/null and b/icons/searchwindow/fvuze.png differ diff --git a/icons/searchwindow/fwatched.png b/icons/searchwindow/fwatched.png new file mode 100644 index 0000000..07917e1 Binary files /dev/null and b/icons/searchwindow/fwatched.png differ diff --git a/icons/searchwindow/nfcontrolcenter.png b/icons/searchwindow/nfcontrolcenter.png new file mode 100644 index 0000000..b7187f3 Binary files /dev/null and b/icons/searchwindow/nfcontrolcenter.png differ diff --git a/icons/searchwindow/nfdeluge.png b/icons/searchwindow/nfdeluge.png new file mode 100644 index 0000000..4e21b9a Binary files /dev/null and b/icons/searchwindow/nfdeluge.png differ diff --git a/icons/searchwindow/nfdownloadstatus.png b/icons/searchwindow/nfdownloadstatus.png new file mode 100644 index 0000000..664a1f2 Binary files /dev/null and b/icons/searchwindow/nfdownloadstatus.png differ diff --git a/icons/searchwindow/nfhistory.png b/icons/searchwindow/nfhistory.png new file mode 100644 index 0000000..0562056 Binary files /dev/null and b/icons/searchwindow/nfhistory.png differ diff --git a/icons/searchwindow/nfkeyboard.png b/icons/searchwindow/nfkeyboard.png new file mode 100644 index 0000000..a9a8750 Binary files /dev/null and b/icons/searchwindow/nfkeyboard.png differ diff --git a/icons/searchwindow/nfqbittorrent.png b/icons/searchwindow/nfqbittorrent.png new file mode 100644 index 0000000..e316251 Binary files /dev/null and b/icons/searchwindow/nfqbittorrent.png differ diff --git a/icons/searchwindow/nfsearch.png b/icons/searchwindow/nfsearch.png new file mode 100644 index 0000000..46284d6 Binary files /dev/null and b/icons/searchwindow/nfsearch.png differ diff --git a/icons/searchwindow/nftorrent-client.png b/icons/searchwindow/nftorrent-client.png new file mode 100644 index 0000000..c472822 Binary files /dev/null and b/icons/searchwindow/nftorrent-client.png differ diff --git a/icons/searchwindow/nftransmission.png b/icons/searchwindow/nftransmission.png new file mode 100644 index 0000000..070eb97 Binary files /dev/null and b/icons/searchwindow/nftransmission.png differ diff --git a/icons/searchwindow/nfvuze.png b/icons/searchwindow/nfvuze.png new file mode 100644 index 0000000..8afc504 Binary files /dev/null and b/icons/searchwindow/nfvuze.png differ diff --git a/icons/searchwindow/nfwatched.png b/icons/searchwindow/nfwatched.png new file mode 100644 index 0000000..cb64791 Binary files /dev/null and b/icons/searchwindow/nfwatched.png differ diff --git a/resources/contenters/IMDB.py b/resources/contenters/IMDB.py index 334648e..bb61442 100644 --- a/resources/contenters/IMDB.py +++ b/resources/contenters/IMDB.py @@ -99,27 +99,28 @@ class IMDB(Content.Content): return True def get_contentList(self, category, subcategory=None, apps_property=None): + self.debug = self.log contentList = [] url = self.get_url(category, subcategory, apps_property) response = self.makeRequest(url, headers=self.headers) if None != response and 0 < len(response): - #print response + self.debug(response) if category in ['top']: contentList = self.topmode(response) elif category == 'search': contentList = self.searchmode(response) else: #if category in ['genre']: contentList = self.genremode(response) - #print str(contentList) + self.debug(str(contentList)) return contentList def searchmode(self, response): contentList = [] pars = HTMLParser.HTMLParser() Soup = BeautifulSoup(response) - result = Soup.findAll('tr', {'class': ['findResult odd', 'findResult even']}) + result = Soup.findAll('tr', {'class': 'lister-item mode-advanced'}) num = 250 for tr in result: #main @@ -147,7 +148,7 @@ class IMDB(Content.Content): int(int(self.sourceWeight) * (251 - int(num))), originaltitle, title, int(year), img, info, )) - #print result + self.debug(str(result)) return contentList def genremode(self, response): @@ -181,7 +182,7 @@ class IMDB(Content.Content): int(int(self.sourceWeight) * (251 - int(num))), originaltitle, title, int(year), img, info, )) - #print result + self.debug(str(result)) return contentList def biggerImg(self, img): diff --git a/resources/contenters/KickAssSo.py b/resources/contenters/KickAssSo.py index b5aee44..b384404 100644 --- a/resources/contenters/KickAssSo.py +++ b/resources/contenters/KickAssSo.py @@ -48,7 +48,7 @@ class KickAssSo(Content.Content): } - baseurl = "http://kat.cr" + baseurl = "http://kat.am" headers = [('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124' + \ ' YaBrowser/14.10.2062.12061 Safari/537.36'), @@ -82,25 +82,25 @@ class KickAssSo(Content.Content): return False def get_contentList(self, category, subcategory=None, apps_property=None): + self.debug = self.log contentList = [] url = self.get_url(category, subcategory, apps_property) response = self.makeRequest(url, headers=self.headers) if None != response and 0 < len(response): - # print response + self.debug(response) if category: contentList = self.mode(response) - # print str(contentList) + self.debug(str(contentList)) return contentList def mode(self, response): contentList = [] - # print str(result) num = 51 good_forums = ['TV', 'Anime', 'Movies'] regex = '''''' - regex_tr = r'''title="Download torrent file" href="(.+?)" class=".+?">(.+?).+? in .+?">(.+?).+?(.+?).+?(\d+ .+?).+?(\d+?).+?(\d+?)''' + regex_tr = r'''(.+?).+? in .+?">(.+?).+?(.+?).+?(\d+ .+?).+?(\d+?).+?(\d+?)''' for tr in re.compile(regex, re.DOTALL).findall(response): result=re.compile(regex_tr, re.DOTALL).findall(tr) if result: @@ -129,6 +129,7 @@ class KickAssSo(Content.Content): return contentList def get_info(self, url): + self.debug = self.log movieInfo = {} color = '[COLOR blue]%s:[/COLOR] %s\r\n' response = self.makeRequest(url, headers=self.headers) @@ -143,7 +144,7 @@ class KickAssSo(Content.Content): 'kinopoisk': ''} try: img = result.find('a', {'class': 'movieCover'}).find('img').get('src') - movieInfo['poster'] = 'http:' + img + movieInfo['poster'] = img if img.startswith('http:') else 'http:' + img except: pass try: @@ -190,7 +191,5 @@ class KickAssSo(Content.Content): if i == 'IMDb link': movieInfo['kinopoisk'] = 'http://imdb.snick.ru/ratefor/02/tt%s.png' % info.get(i) - - # print str(info) - + self.debug(str(movieInfo)) return movieInfo diff --git a/resources/contenters/KinoPoisk.py b/resources/contenters/KinoPoisk.py index d8f040b..a46cd66 100644 --- a/resources/contenters/KinoPoisk.py +++ b/resources/contenters/KinoPoisk.py @@ -104,22 +104,23 @@ class KinoPoisk(Content.Content): return True def get_contentList(self, category, subcategory=None, apps_property=None): + #self.debug=self.log socket.setdefaulttimeout(15) contentList = [] url = self.get_url(category, subcategory, apps_property) - #print url + self.debug('get_contentList: url = '+url) response = self.makeRequest(url, headers=self.headers) if None != response and 0 < len(response): - #print response + self.debug(str(response)) if category in ['hot']: contentList = self.popmode(response) elif url.startswith(self.baseurl + '/s/type/film/list/'): contentList = self.infomode(response) else: contentList = self.topmode(response) - #print str(contentList) + self.debug('get_contentList: contentList = '+str(contentList)) return contentList def stripTtl(self, title): @@ -132,12 +133,12 @@ class KinoPoisk(Content.Content): contentList = [] Soup = BeautifulSoup(response) result = Soup.find('div', 'stat').findAll('div', 'el') - #print str(result) + self.debug('popmode: '+str(result)) for tr in result: #main a = tr.findAll('a') num = a[0].text - #print num + info = {} year = 0 img = '' @@ -152,12 +153,14 @@ class KinoPoisk(Content.Content): img = self.id2img(id[0]) try: title, year = re.compile('(.+?) \((\d\d\d\d)\)', re.DOTALL).findall(a[1].text)[0] + #self.log('popmode 1'+str((title, year))) except: pass if not year: try: title, year = re.compile('(.+?) \(.*(\d\d\d\d)').findall(a[1].text)[0] info['tvshowtitle'] = title + #self.log('popmode 2' + str((title, year))) except: pass title = self.stripHtml(self.stripTtl(title)) @@ -176,7 +179,7 @@ class KinoPoisk(Content.Content): contentList = [] Soup = BeautifulSoup(response) result = Soup.find('table', {'cellpadding': '3'}).findAll('tr')[2:] - #print str(result) + self.debug('topmode: ' + str(result)) for tr in result: #main td = tr.findAll('td') @@ -198,8 +201,12 @@ class KinoPoisk(Content.Content): year = re.compile('(.+) \((\d\d\d\d)\)').findall(a_all.text) if not year: try: - title, year = re.compile('(.+) \(.*(\d\d\d\d)').findall(a_all.text)[0] - info['tvshowtitle'] = title + match = re.search(r"(.+) \((\d\d\d\d) –|(.+) \(.*(\d\d\d\d)", a_all.text, + re.IGNORECASE | re.MULTILINE) + if match: + title = match.group(1) + year = match.group(2) + info['tvshowtitle'] = title except: title = a_all.text else: diff --git a/resources/contenters/RuTorOrg.py b/resources/contenters/RuTorOrg.py new file mode 100644 index 0000000..e5b58b5 --- /dev/null +++ b/resources/contenters/RuTorOrg.py @@ -0,0 +1,249 @@ +# -*- coding: utf-8 -*- +''' + Torrenter plugin for XBMC + Copyright (C) 2012 Vadim Skorba + vadim.skorba@gmail.com + + 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 re, sys + +import Content +from BeautifulSoup import BeautifulSoup + +#http://anti-tor.org/browse/0/1/0/0 date movie +#http://anti-tor.org/browse/1/1/0/0 page 2 +#http://anti-tor.org/browse/0/1/0/2 seed movie +# page/cat/?/sort + + +class RuTorOrg(Content.Content): + category_dict = { + 'movies': ('Movies', '/browse/0/1/0', + {'page': '/browse/%d/1/0', 'increase': 1, 'second_page': 1, + 'sort': [{'name': 'by Seeders', 'url_after': '/2'}, + {'name': 'by Date', 'url_after': '/0'}]}), + 'rus_movies': ('Russian Movies', '/browse/0/5/0', + {'page': '/browse/%d/5/0', 'increase': 1, 'second_page': 1, + 'sort': [{'name': 'by Seeders', 'url_after': '/2'}, + {'name': 'by Date', 'url_after': '/0'}]}), + 'tvshows': ('TV Shows', '/browse/0/4/0', + {'page': '/browse/%d/4/0', 'increase': 1, 'second_page': 1, + 'sort': [{'name': 'by Seeders', 'url_after': '/2'}, + {'name': 'by Date', 'url_after': '/0'}]}), + 'science': ('Научно - популярные фильмы', '/browse/0/12/0', + {'page': '/browse/%d/12/0', 'increase': 1, 'second_page': 1, + 'sort': [{'name': 'by Seeders', 'url_after': '/2'}, + {'name': 'by Date', 'url_after': '/0'}]}), + 'cartoons': ('Cartoons', '/browse/0/7/0', + {'page': '/browse/%d/7/0', 'increase': 1, 'second_page': 1, + 'sort': [{'name': 'by Seeders', 'url_after': '/2'}, + {'name': 'by Date', 'url_after': '/0'}]}), + 'anime': ('Anime', '/browse/0/10/0', + {'page': '/browse/%d/10/0', 'increase': 1, 'second_page': 1, + 'sort': [{'name': 'by Seeders', 'url_after': '/2'}, + {'name': 'by Date', 'url_after': '/0'}]}), + 'sport': ('Спорт и Здоровье', '/browse/0/13/0', + {'page': '/browse/%d/13/0', 'increase': 1, 'second_page': 1, + 'sort': [{'name': 'by Seeders', 'url_after': '/2'}, + {'name': 'by Date', 'url_after': '/0'}]}), + 'tele': ('Телевизор', '/browse/0/6/0', + {'page': '/browse/%d/6/0', 'increase': 1, 'second_page': 1, + 'sort': [{'name': 'by Seeders', 'url_after': '/2'}, + {'name': 'by Date', 'url_after': '/0'}]}), + 'humor': ('Юмор', '/browse/0/15/0', + {'page': '/browse/%d/15/0', 'increase': 1, 'second_page': 1, + 'sort': [{'name': 'by Seeders', 'url_after': '/2'}, + {'name': 'by Date', 'url_after': '/0'}]}), + } + + + #'hot': ('Most Recent',), + + + baseurl = "anti-tor.org" + + ''' + Weight of source with this searcher provided. + Will be multiplied on default weight. + Default weight is seeds number + ''' + sourceWeight = 1 + + def isTracker(self): + return True + + def isSearcher(self): + return False + + def isScrappable(self): + return False + + def isInfoLink(self): + return True + + def isPages(self): + return True + + def isSort(self): + return True + + def isSearchOption(self): + return False + + def get_contentList(self, category, subcategory=None, apps_property=None): + self.debug = self.log + contentList = [] + url = 'http://%s' % self.get_url(category, subcategory, apps_property) + self.debug(url) + + self.headers = [('User-Agent', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 YaBrowser/14.10.2062.12061 Safari/537.36'), + ('Referer', 'http://%s' % self.baseurl), ('Accept-encoding', 'gzip'), + ('Cookie', str(sys.modules["__main__"].__settings__.getSetting("rutor-auth")))] + + response = self.makeRequest(url, headers=self.headers) + + if None != response and 0 < len(response): + #self.debug(response) + cookie = re.compile("document.cookie='(.+?)';").findall(response) + if cookie and str(cookie[0]) != str(sys.modules["__main__"].__settings__.getSetting("rutor-auth")): + cookie = cookie[0] + self.log('ok found new cookie: ' + str(cookie)) + sys.modules["__main__"].__settings__.setSetting("rutor-auth", cookie) + headers = [('User-Agent', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 YaBrowser/14.10.2062.12061 Safari/537.36'), + ('Referer', 'http://%s' % self.baseurl), ('Accept-encoding', 'gzip'), + ('Cookie', str(sys.modules["__main__"].__settings__.getSetting("rutor-auth")))] + response = self.makeRequest(url, headers=headers) + self.debug(response) + + if category: + contentList = self.mode(response.decode('utf-8')) + self.debug(str(contentList)) + return contentList + + def mode(self, response): + contentList = [] + num = 101 + + + regex = '''.+?.+?(.+?).+?(\d*?\..+? .+?).+? (\d+).+?alt="L".+?> (\d+)' + for tr in re.compile(regex, re.DOTALL).findall(response): + result=re.compile(regex_tr, re.DOTALL).findall(tr) + if result: + self.debug(tr + ' -> ' + str(result[0])) + (date, link, infolink, title, size, seeds, leechers)=result[0] + # main + info = {} + num = num - 1 + original_title = None + year = 0 + img = '' + + # info + title = self.unescape(self.stripHtml(title.strip())) + info['label'] = info['title'] = title + + if link[0] == '/': link = 'http://%s%s' % (self.baseurl, link) + info['link'] = link + + #if infolink[0] == '/': infolink = 'http://%s%s' % (self.baseurl, infolink) + #info['infolink'] = infolink + + size = size.replace(' ', ' ') + date = date.replace(' ', ' ') + + info['plot'] = info['title'] + '\r\n[I](%s) [S/L: %s/%s] [/I]\r\n%s: %s' % ( + size, seeds, leechers, self.localize('Date'), date) + + #regex_title = '(.+?) / (.+?) \((\d\d\d\d)\)' + #regex_result = re.compile(regex_title, re.DOTALL).findall(title) + #if regex_result: + # title, original_title, year = regex_result[0] + # info['title'] = title + + contentList.append(( + int(int(self.sourceWeight) * (int(num))), + original_title, title, int(year), img, info, + )) + + return contentList + + def get_info(self, url): + self.debug = self.log + movieInfo = {} + color = '[COLOR blue]%s:[/COLOR] %s\r\n' + response = self.makeRequest(url, headers=self.headers) + + if None != response and 0 < len(response): + Soup = BeautifulSoup(response) + result = Soup.find('div', 'torrentMediaInfo') + if not result: + return None + li = result.findAll('li') + info, movieInfo = {'Cast': ''}, {'desc': '', 'poster': '', 'title': '', 'views': '0', 'rating': '50', + 'kinopoisk': ''} + try: + img = result.find('a', {'class': 'movieCover'}).find('img').get('src') + movieInfo['poster'] = img if img.startswith('http:') else 'http:' + img + except: + pass + try: + movie = re.compile('View all (.+?) episodes').match(str(result)) + if movie: + info['Movie'] = movie.group(1) + except: + pass + for i in li: + name = i.find('strong').text + if name: + info[name.rstrip(':')] = i.text.replace(name, '', 1) + plot = result.find('div', {'id': 'summary'}) + if plot: + cut = plot.find('strong').text + info['plot'] = plot.text.replace(cut, '', 1).replace('report summary', '') + # print str(result) + cast = re.compile('(.+?)').findall(str(result)) + if cast: + for actor in cast: + info['Cast'] += actor + ", " + if 'Genres' in info: + info['Genres'] = info['Genres'].replace(', ', ',').replace(',', ', ') + for key in info.keys(): + if not 'Movie' in info and info[key] == 'addto bookmarks': + movieInfo['title'] = self.unescape(key) + info['TV Show'] = self.unescape(key) + if not 'plot' in info and 'Summary' in key: + info['plot'] = info[key] + + for i in ['Movie', 'TV Show', 'Release date', 'Original run', 'Episode', 'Air date', 'Genres', 'Language', + 'Director', 'Writers', 'Cast', 'Original run', 'IMDb rating', 'AniDB rating']: + if info.get(i) and info.get(i) not in ['']: + movieInfo['desc'] += color % (i, info.get(i)) + if i == 'Movie': + movieInfo['title'] = info.get(i) + + for i in ['plot', 'IMDb link', 'RottenTomatoes']: + if info.get(i) and info.get(i) not in ['']: + if i == 'plot': + movieInfo['desc'] += '\r\n[COLOR blue]Plot:[/COLOR]\r\n' + self.unescape(info.get(i)) + if i == 'RottenTomatoes': + movieInfo['rating'] = str(info.get(i).split('%')[0]) + if i == 'IMDb link': + movieInfo['kinopoisk'] = 'http://imdb.snick.ru/ratefor/02/tt%s.png' % info.get(i) + + self.debug(str(movieInfo)) + return movieInfo diff --git a/resources/contenters/ThePirateBaySe.py b/resources/contenters/ThePirateBaySe.py index 2cf0d48..3294f7f 100644 --- a/resources/contenters/ThePirateBaySe.py +++ b/resources/contenters/ThePirateBaySe.py @@ -23,6 +23,7 @@ import Content class ThePirateBaySe(Content.Content): + # debug = log category_dict = { 'tvshows': ('TV Shows', '/browse/205', {'page': '/browse/208/%d', 'increase': 1, 'second_page': 1, 'sort': [{'name': 'by Seeders', 'url_after': '/0/7/0'}, @@ -45,11 +46,11 @@ class ThePirateBaySe(Content.Content): 'heb_movies': ('סרטים מדובבים', '/search/Hebrew-dubbed/0/7/0'), } - baseurl = "thepiratebay.rs" + baseurl = "http://thepiratebay.ae" headers = [('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124' + \ ' YaBrowser/14.10.2062.12061 Safari/537.36'), - ('Referer', 'http://kickass.so/'), ('Accept-Encoding', 'gzip')] + ('Referer', 'http://thepiratebay.ae/'), ('Accept-Encoding', 'gzip')] ''' Weight of source with this searcher provided. Will be multiplied on default weight. @@ -82,7 +83,8 @@ class ThePirateBaySe(Content.Content): contentList = [] url = self.get_url(category, subcategory, apps_property) - response = self.open2(url) + import requests + response = requests.get(url).text if None != response and 0 < len(response): # print response @@ -91,40 +93,35 @@ class ThePirateBaySe(Content.Content): # print str(contentList) return contentList - def open2(self, url=''): - import httplib - conn = httplib.HTTPConnection(self.baseurl) - conn.request("GET", url.replace(self.baseurl,'')) - r1 = conn.getresponse() - status = str(r1.status) + " " + r1.reason - content = r1.read() - #print str(status) - #print str(content) - return content - def mode(self, response): contentList = [] - # print str(result) + self.debug = self.log num = 31 - result = re.compile( - r'''
.+?">(.+?).+?Uploaded (.+?), Size (.+?), .+?.+?(\d+?).+?(\d+?)''', - re.DOTALL).findall(response) - for title, link, date, size, seeds, leechers in result: - info = {} - num = num - 1 - original_title = None - year = 0 - img = '' - size = size.replace(' ', ' ') - date = self.stripHtml(date.replace(' ', ' ')) + self.debug(response) + regex = '''.+?''' + regex_tr = r'
.+?">(.+?).+?Uploaded (.+?), Size (.+?), .+?.+?(\d+?).+?(\d+?)' + for tr in re.compile(regex, re.DOTALL).findall(response): + result = re.compile(regex_tr, re.DOTALL).findall(tr) + self.debug(tr + ' -> ' + str(result)) + if result: + (title, link, date, size, seeds, leechers) = result[0] - # info + info = {} + num = num - 1 + original_title = None + year = 0 + img = '' + size = size.replace(' ', ' ') + date = self.stripHtml(date.replace(' ', ' ')) - info['label'] = info['title'] = self.unescape(title) - info['link'] = link - info['plot'] = info['title'] + '\r\n[I](%s) [S/L: %s/%s] [/I]\r\n%s' % (size, seeds, leechers, date) - contentList.append(( - int(int(self.sourceWeight) * (int(num))), - original_title, title, int(year), img, info, - )) + # info + + info['label'] = info['title'] = self.unescape(title) + info['link'] = link + self.log(info['link']) + info['plot'] = info['title'] + '\r\n[I](%s) [S/L: %s/%s] [/I]\r\n%s' % (size, seeds, leechers, date) + contentList.append(( + int(int(self.sourceWeight) * (int(num))), + original_title, title, int(year), img, info, + )) return contentList diff --git a/resources/pyxbmct/__init__.py b/resources/contenters/__init__.py similarity index 100% rename from resources/pyxbmct/__init__.py rename to resources/contenters/__init__.py diff --git a/resources/contenters/CXZ.py b/resources/contenters/unused/CXZ.py similarity index 100% rename from resources/contenters/CXZ.py rename to resources/contenters/unused/CXZ.py diff --git a/resources/contenters/EZTV.py b/resources/contenters/unused/EZTV.py similarity index 98% rename from resources/contenters/EZTV.py rename to resources/contenters/unused/EZTV.py index 8cf2e13..5396b8c 100644 --- a/resources/contenters/EZTV.py +++ b/resources/contenters/unused/EZTV.py @@ -28,7 +28,7 @@ class EZTV(Content.Content): 'hot': ('Most Recent', '/', {'page': '/page_%d', 'increase': 1, 'second_page': 1}), } - baseurl = "https://eztv.ch" + baseurl = "https://eztv.ag" headers = [('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124' + \ ' YaBrowser/14.10.2062.12061 Safari/537.36'), diff --git a/resources/skins/__init__.py b/resources/contenters/unused/__init__.py similarity index 100% rename from resources/skins/__init__.py rename to resources/contenters/unused/__init__.py diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml index 8897896..cac403a 100644 --- a/resources/language/English/strings.xml +++ b/resources/language/English/strings.xml @@ -68,11 +68,24 @@ pyrrent2http (python-libtorrent via http) Append size to file name Enable DHT + Estuary (by DiMartino) + Стиль информации о раздаче + In the end + In the beginning + Second string + Arctic: Zephyr (by xbmc00) Interface P2P Network Advanced Torrent-client Hentai (fine-tuning) + Search Window Mode + Enabled, hide old style + Enabled, replace old style + Enabled, optional + Disabled + Search Window Transparent Background + Disable Notifications Save path Call dialog Default diff --git a/resources/language/Hungarian/strings.xml b/resources/language/Hungarian/strings.xml new file mode 100644 index 0000000..0eb3145 --- /dev/null +++ b/resources/language/Hungarian/strings.xml @@ -0,0 +1,97 @@ + + + Kezelőfelület nyelve + Mappák nézetének lezárása + Ki + Fájlok mentése mappába (nem FAT32) + Magnet-linkek használata + Letöltött fájlok megtartása + Letöltött fájlok seedben tartása + Feltöltési sebességkorlát MBits/sec (0 - korlátlan) + Letöltési sebességkorlát MBits/sec (0 - korlátlan) + Csak rendszerszintű libtorrent használata + A következő epizód előtöltése és lejátszása + Metaadatok letöltése a Tartalmak Listájába + Hibakeresés (Fejlesztői Mód) + Confluence (by slng) + Transperency (by slng) + Confluence (by DiMartino) + Confluence (by RussakHH) + Keresési Előzmények engedélyezése + python-libtorrent + Ace Stream + P2P Lejátszó + Egyéb beállítások a programban - AceStream Client + Keresés időkorlátja + Rövid (10s) + Normál (20s) + Hosszú (30s) + Feliratok előtöltése az összes mappából + Seedben tartás megtekintés után + Kérdezzen rá a tárolóhely váltására lejátszás előtt + Orosz tartalmak törlése + Orosz tartalmak visszaállítása + Torrentre kattintás művelete + Torrent fájl megnyitása + Helyi menü megnyitása + Letöltés torrentkliensen keresztül + Letöltés python-libtorrenten keresztül + Keresések rendezése seed alapján + Egyéni keresési lehetőség kifejezésre + Titkosítás + Keresési szálak száma + Törölje a fájlokat + Mentse a fájlokat + Kérdezzen rá + Torrent2HTTP (libtorrent via http) + Proxy automatikus feloldása + Ki + Anti-zapret + Immunicity + Max. kapcsolatok (0 - korlátlan) + Véletlen port használata + Port a bejövő kapcsolatokhoz + Előtöltendő adat lejátszás előtt (MB) + Következő epizód automatikus lejátszása (vagy kérdezzen) + Eszköz konfiguráció + Átlagos/Jó PC + Átlag alatti PC/router + Tárolóhely legkisebb mérete automatikus tisztítás miatt (GB) + Videó szüneteltetése elindítás után + Keresési eredmények rendezése + Seed alapján + Ne rendezze + Név alapján + Ne adja Megtek. Előzm.-hez ennél kevesebb lejátszásnál (%) + Confluence (by safonov_ivan) + Aeon Nox (by joyrider) + pyrrent2http (python-libtorrent via http) + Méret mellékelése a fájl nevéhez + DHT engedélyezése + Kezelőfelület + P2P Hálózat + Haladó + Torrentkliens + Hentai (finomhangolás) + Mentési útvonal + Párbeszédablak + Alapértelmezett + Elérési út + Alkönyvtár készítése a leolvasó számára + Gazdagép + Port + URL + Felhasználónév + Jelszó + Torrentkliens + URL (csak No SSL) + Elérési út helyettesítése (csak távoli) + Bezár + Beállítások megnyitása + Torrentkliens Böngésző + Trackerek telepítése + Tracker beállítások + Tárolóhely tisztítása + Kereső választása + Nem rendelkezel külső keresővel. Kérlek, telepíts egyet. + diff --git a/resources/language/Russian/strings.xml b/resources/language/Russian/strings.xml index fb67ec9..0d0603d 100644 --- a/resources/language/Russian/strings.xml +++ b/resources/language/Russian/strings.xml @@ -68,11 +68,24 @@ pyrrent2http (python-libtorrent по http) Добавлять размер к имени файла Включить DHT + Estuary (от DiMartino) + Стиль информации о раздаче + В конце + В начале + Второй строкой + Arctic: Zephyr (от xbmc00) Интерфейс P2P Сеть Дополнительные Торрент-клиент Hentai (тонкая настр.) + Режим работы Окна Поиска + Включен, убрать старый вид + Включен, заменить старый вид + Включен, как опция + Отключен + Прозрачность Окна Поиска + Отключить Уведомления Директория для сохранения файлов Вызывать диалог Задать по умолчанию diff --git a/resources/language/spanish/strings.xml b/resources/language/spanish/strings.xml new file mode 100644 index 0000000..b91427a --- /dev/null +++ b/resources/language/spanish/strings.xml @@ -0,0 +1,110 @@ + + + Idioma de la interfaz + Modo vista de carpetas + No fijar + Carpeta en la que guardar archivos (no FAT32) + Utilizar enlaces magnet + Almacenaje de archivos descargados + Seguir compartiendo archivos descargados + Máx. vel. de subida en Mbit/s (0 = ilimitada) + Máx. vel. de descarga en Mbit/s (0 = ilimitada) + Utilizar solo libtorrent + Lanzar descarga de episodio siguiente + Descargar metadatos para listas de contenido + Depuración (Modo desarrollador) + Confluence (de slng) + Transperency (de slng) + Confluence (de DiMartino) + Confluence (de RussakHH) + Habilitar el historial de búsquedas + python-libtorrent + Ace Stream + Reproductor P2P + Restablecer ajustes en Add-ons de programas -- AceStream client + Duración de las búsquedas + Breve (10 s) + Normal (20 s) + Larga (30 s) + Predescargar los subtítulos de todas las carpetas + Seguir compartiendo después de ver el vídeo + Preguntar para cambiar de almacén antes de la descarga + Eliminar material ruso + Restituir material ruso + Efecto de clic sobre torrent + Abrir archivo torrent + Abrir menú contextual + Descargar vía cliente BitTorrent + Descargar vía python-libtorrent + Ordenar resultados por fuentes + Términos para búsqueda personalizada + Cifrado + Número de hilos de búsqueda + Eliminar archivos + Guardar archivos + Preguntar para guardar + Torrent2HTTP (libtorrent vía HTTP) + Desbloqueo automático de proxy + Ninguno + Anti-zapret (antirestricción) + Inmunidad + Núm. máx. de conexiones (0 = ilimitadas) + Utilizar puertos aleatorios + Puerto para conexiones entrantes + Tamaño de precarga para empezar a reproducir (MB) + Autorreproducir episodio siguiente (o preguntar) + Configuración de dispositivo + Promedio/Buen PC + Por debajo de la media, PC/router + Tamaño mínimo de almacenaje para autoborrado (GB) + Diferir inicio del reproductor + Ordenar resultados de la búsqueda + Por fuentes + No ordenar + Por nombre + Añadir al historial de vistos después del (%) + Confluence (de safonov_ivan) + Aeon Nox (de joyrider) + pyrrent2http (python-libtorrent vía HTTP) + Añadir tamaño a nombre de archivo + Activar protocolo DHT + Estuary (de DiMartino) + Modo presentación de datos + Al final + Al principio + En segunda línea + Arctic: Zephyr (de xbmc00) + Interfaz + Red P2P + Avanzado + Cliente de BitTorrent + Hentai (reajustes) + Modo ventana de búsquedas + Activado ocultar estilo anterior + Activado remplazar estilo anterior + Activado opcional + Desactivado + Fondo transparente de ventana de búsquedas + Desactivar notificaciones + Ruta para guardar + Preguntar + Por defecto + Ruta + Crear subdirectorio para scrapper + Host + Puerto + URL + Usuario + Contraseña + Cliente de BitTorrent + URL (no SSL) + Sustitución de ruta (Solo remoto) + Cerrar + Abrir "Ajustes" + Navegador de cliente BitTorrent + Instalar rastreadores + Ajustes de rastreadores + Vaciar almacén + Seleccionar buscador + No hay buscador externo. Instale uno primero. + diff --git a/resources/proxy/antizapret.py b/resources/proxy/antizapret.py index 0decbe2..eee5f43 100644 --- a/resources/proxy/antizapret.py +++ b/resources/proxy/antizapret.py @@ -1,66 +1,72 @@ # -*- coding: utf-8 -*- -import os, re, fnmatch, threading, urllib2 +import os, re, fnmatch, threading, urllib2, time, shelve, anydbm from contextlib import contextmanager, closing from functions import log, debug, tempdir -LOCKS = {} PAC_URL = "http://antizapret.prostovpn.org/proxy.pac" CACHE_DIR = tempdir() USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36" +CONFIG_LOCK = threading.Lock() if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR) -CACHE = 24 * 3600 # 24 hour caching - -#@contextmanager -def shelf(filename, ttl=0): - import shelve - filename = os.path.join(CACHE_DIR, filename) - with LOCKS.get(filename, threading.RLock()): -# with closing(shelve.open(filename, writeback=True)) as d: - d = shelve.open(filename, writeback=True) - try: - import time - if not dict(d): - d.update({ - "created_at": time.time(), - "data": {}, - }) - elif ttl > 0 and (time.time() - d["created_at"]) > ttl: - d["created_at"] = time.time() - d["data"] = {} - return d - except: - d.close() - raise - -_config = {} +CACHE_LIFETIME = 24 * 3600 # 24 hour caching def config(): - global _config - if not _config: -# with shelf("antizapret.pac_config", ttl=CACHE) as pac_config: - d = shelf("antizapret.pac_config2", ttl=CACHE) - pac_config = d['data'] - if not pac_config: - log("[antizapret]: Fetching Antizapret PAC file on %s" %PAC_URL) - try: - pac_data = urllib2.urlopen(PAC_URL).read() - except: - pac_data = "" + shelf = None + try: + CONFIG_LOCK.acquire() + filename = os.path.join(CACHE_DIR, "antizapret.pac_config2") + try: + shelf = shelve.open(filename) + except anydbm.error: + os.remove(filename) + shelf = shelve.open(filename) - r = re.search(r"\"PROXY (.*); DIRECT", pac_data) - if r: - pac_config["server"] = r.group(1) - pac_config["domains"] = map(lambda x: x.replace(r"\Z(?ms)", "").replace("\\", ""), map(fnmatch.translate, re.findall(r"\"(.*?)\",", pac_data))) - else: - pac_config["server"] = None - pac_config["domains"] = [] - d.close() - _config = pac_config - return _config + created_at = 0 + data = {} + + if 'created_at' in shelf: + created_at = shelf['created_at'] + + if 'data' in shelf: + data = shelf['data'] + + if((time.time() - created_at) <= CACHE_LIFETIME + and 'domains' in data + and len(data['domains']) > 0): + return data + + log("[antizapret]: Fetching Antizapret PAC file on %s" %PAC_URL) + try: + pac_data = urllib2.urlopen(PAC_URL).read() + except: + pac_data = "" + + r = re.search(r"\"PROXY (.*); DIRECT", pac_data) + if r: + data["server"] = r.group(1) + data["domains"] = map(lambda x: x.replace(r"\Z(?ms)", "").replace("\\", ""), map(fnmatch.translate, re.findall(r"\"(.*?)\",", pac_data))) + else: + data["server"] = None + data["domains"] = [] + + shelf.clear() + shelf.update({ + "created_at": time.time(), + "data": data, + }) + return data + except Exception as ex: + debug("[antizapret]: " + str(ex)) + raise + finally: + if shelf: + shelf.close() + if CONFIG_LOCK.locked(): + CONFIG_LOCK.release() class AntizapretProxyHandler(urllib2.ProxyHandler, object): def __init__(self): @@ -73,7 +79,8 @@ class AntizapretProxyHandler(urllib2.ProxyHandler, object): def proxy_open(self, req, proxy, type): import socket - if socket.gethostbyname(req.get_host().split(":")[0]) in self.config["domains"]: + hostname = req.get_host().split(":")[0] + if socket.gethostbyname(hostname) in self.config["domains"] or hostname in self.config["domains"]: debug("[antizapret]: Pass request through proxy " + self.config["server"]) return urllib2.ProxyHandler.proxy_open(self, req, self.config["server"], type) @@ -104,5 +111,4 @@ def url_get(url, params={}, headers={}, post = None): return data except urllib2.HTTPError as e: log("[antizapret]: HTTP Error(%s): %s" % (e.errno, e.strerror)) - return None - + return None \ No newline at end of file diff --git a/resources/pyxbmct/addonwindow.py b/resources/pyxbmct/addonwindow.py deleted file mode 100644 index c4825e9..0000000 --- a/resources/pyxbmct/addonwindow.py +++ /dev/null @@ -1,786 +0,0 @@ -# -*- coding: utf-8 -*- -# This is a "local" version of PyXBMCt to be used in standalone addons. -# -# PyXBMCt is a mini-framework for creating XBMC Python addons with arbitrary UI -# made of controls - decendants of xbmcgui.Control class. -# The framework uses image textures from XBMC Confluence skin. -# -# Licence: GPL v.3 http://www.gnu.org/licenses/gpl.html -# -## @package addonwindow -# PyXBMCt framework module - -import os - -import xbmc -import xbmcgui - - -# _addon = xbmcaddon.Addon() -_images = os.path.join(os.path.dirname(__file__), 'textures', 'default') - - -# Text alighnment constants. Mixed variants are obtained by bit OR (|) -ALIGN_LEFT = 0 -ALIGN_RIGHT = 1 -ALIGN_CENTER_X = 2 -ALIGN_CENTER_Y = 4 -ALIGN_CENTER = 6 -ALIGN_TRUNCATED = 8 -ALIGN_JUSTIFY = 10 - -# XBMC key action codes. -# More codes at https://github.com/xbmc/xbmc/blob/master/xbmc/guilib/Key.h -## ESC action -ACTION_PREVIOUS_MENU = 10 -## Backspace action -ACTION_NAV_BACK = 92 -## Left arrow key -ACTION_MOVE_LEFT = 1 -## Right arrow key -ACTION_MOVE_RIGHT = 2 -## Up arrow key -ACTION_MOVE_UP = 3 -## Down arrow key -ACTION_MOVE_DOWN = 4 -## Mouse wheel up -ACTION_MOUSE_WHEEL_UP = 104 -## Mouse wheel down -ACTION_MOUSE_WHEEL_DOWN = 105 -## Mouse drag -ACTION_MOUSE_DRAG = 106 -## Mouse move -ACTION_MOUSE_MOVE = 107 - - -def _set_textures(textures={}, kwargs={}): - """Set texture arguments for controls.""" - for texture in textures.keys(): - try: - kwargs[texture] - except KeyError: - kwargs[texture] = textures[texture] - - -class AddonWindowError(Exception): - """Custom exception.""" - pass - - -class Label(xbmcgui.ControlLabel): - """ControlLabel class. - - Parameters: - label: string or unicode - text string. - font: string - font used for label text. (e.g. 'font13') - textColor: hexstring - color of enabled label's label. (e.g. '0xFFFFFFFF') - disabledColor: hexstring - color of disabled label's label. (e.g. '0xFFFF3300') - alignment: integer - alignment of label - *Note, see xbfont.h - hasPath: bool - True=stores a path / False=no path. - angle: integer - angle of control. (+ rotates CCW, - rotates CW)" - - Note: - After you create the control, you need to add it to the window with placeControl(). - - Example: - self.label = Label('Status', angle=45) - """ - - def __new__(cls, *args, **kwargs): - return super(Label, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs) - - -class FadeLabel(xbmcgui.ControlFadeLabel): - """Control that scrolls label text. - - Parameters: - font: string - font used for label text. (e.g. 'font13') - textColor: hexstring - color of fadelabel's labels. (e.g. '0xFFFFFFFF') - _alignment: integer - alignment of label - *Note, see xbfont.h - - Note: - After you create the control, you need to add it to the window with placeControl(). - - Example: - self.fadelabel = FadeLabel(textColor='0xFFFFFFFF') - """ - - def __new__(cls, *args, **kwargs): - return super(FadeLabel, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs) - - -class TextBox(xbmcgui.ControlTextBox): - """ControlTextBox class. - - Parameters: - font: string - font used for text. (e.g. 'font13') - textColor: hexstring - color of textbox's text. (e.g. '0xFFFFFFFF') - - Note: - After you create the control, you need to add it to the window with placeControl(). - - Example: - self.textbox = TextBox(textColor='0xFFFFFFFF') - """ - - def __new__(cls, *args, **kwargs): - return super(TextBox, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs) - - -class Image(xbmcgui.ControlImage): - """ControlImage class. - - Parameters: - filename: string - image filename. - colorKey: hexString - (example, '0xFFFF3300') - aspectRatio: integer - (values 0 = stretch (default), 1 = scale up (crops), 2 = scale down (black bars) - colorDiffuse: hexString - (example, '0xC0FF0000' (red tint)). - - Note: - After you create the control, you need to add it to the window with placeControl(). - - Example: - self.image = Image('d:\images\picture.jpg', aspectRatio=2) - """ - - def __new__(cls, *args, **kwargs): - return super(Image, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs) - - -class Button(xbmcgui.ControlButton): - """ControlButton class. - - Parameters: - label: string or unicode - text string. - focusTexture: string - filename for focus texture. - noFocusTexture: string - filename for no focus texture. - textOffsetX: integer - x offset of label. - textOffsetY: integer - y offset of label. - alignment: integer - alignment of label - *Note, see xbfont.h - font: string - font used for label text. (e.g. 'font13') - textColor: hexstring - color of enabled button's label. (e.g. '0xFFFFFFFF') - disabledColor: hexstring - color of disabled button's label. (e.g. '0xFFFF3300') - angle: integer - angle of control. (+ rotates CCW, - rotates CW) - shadowColor: hexstring - color of button's label's shadow. (e.g. '0xFF000000') - focusedColor: hexstring - color of focused button's label. (e.g. '0xFF00FFFF') - - Note: - After you create the control, you need to add it to the window with placeControl(). - - Example: - self.button = Button('Status', font='font14') - """ - - def __new__(cls, *args, **kwargs): - textures = {'focusTexture': os.path.join(_images, 'Button', 'KeyboardKey.png'), - 'noFocusTexture': os.path.join(_images, 'Button', 'KeyboardKeyNF.png')} - _set_textures(textures, kwargs) - try: - kwargs['alignment'] - except KeyError: - kwargs['alignment'] = ALIGN_CENTER - return super(Button, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs) - - -class RadioButton(xbmcgui.ControlRadioButton): - """ControlRadioButton class. - - Parameters: - label: string or unicode - text string. - focusTexture: string - filename for focus texture. - noFocusTexture: string - filename for no focus texture. - textOffsetX: integer - x offset of label. - textOffsetY: integer - y offset of label. - _alignment: integer - alignment of label - *Note, see xbfont.h - font: string - font used for label text. (e.g. 'font13') - textColor: hexstring - color of enabled radio button's label. (e.g. '0xFFFFFFFF') - disabledColor: hexstring - color of disabled radio button's label. (e.g. '0xFFFF3300') - angle: integer - angle of control. (+ rotates CCW, - rotates CW) - shadowColor: hexstring - color of radio button's label's shadow. (e.g. '0xFF000000') - focusedColor: hexstring - color of focused radio button's label. (e.g. '0xFF00FFFF') - focusOnTexture: string - filename for radio focused/checked texture. - noFocusOnTexture: string - filename for radio not focused/checked texture. - focusOffTexture: string - filename for radio focused/unchecked texture. - noFocusOffTexture: string - filename for radio not focused/unchecked texture. - Note: To customize RadioButton all 4 abovementioned textures need to be provided. - - Note: - After you create the control, you need to add it to the window with placeControl(). - - Example: - self.radiobutton = RadioButton('Status', font='font14') - """ - - def __new__(cls, *args, **kwargs): - if int(xbmc.getInfoLabel('System.BuildVersion')[:2]) >= 13: - textures = {'focusTexture': os.path.join(_images, 'RadioButton', 'MenuItemFO.png'), - 'noFocusTexture': os.path.join(_images, 'RadioButton', 'MenuItemNF.png'), - 'focusOnTexture': os.path.join(_images, 'RadioButton', 'radiobutton-focus.png'), - 'noFocusOnTexture': os.path.join(_images, 'RadioButton', 'radiobutton-focus.png'), - 'focusOffTexture': os.path.join(_images, 'RadioButton', 'radiobutton-nofocus.png'), - 'noFocusOffTexture': os.path.join(_images, 'RadioButton', 'radiobutton-nofocus.png')} - else: # This is for compatibility with Frodo and earlier versions. - textures = {'focusTexture': os.path.join(_images, 'RadioButton', 'MenuItemFO.png'), - 'noFocusTexture': os.path.join(_images, 'RadioButton', 'MenuItemNF.png'), - 'TextureRadioFocus': os.path.join(_images, 'RadioButton', 'radiobutton-focus.png'), - 'TextureRadioNoFocus': os.path.join(_images, 'RadioButton', 'radiobutton-nofocus.png')} - _set_textures(textures, kwargs) - return super(RadioButton, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs) - - -class Edit(xbmcgui.ControlEdit): - """ - ControlEdit class. - - Edit(label[, font, textColor, disabledColor, alignment, focusTexture, noFocusTexture]) - - Parameters: - label : string or unicode - text string. - font : [opt] string - font used for label text. (e.g. 'font13') - textColor : [opt] hexstring - color of enabled label's label. (e.g. '0xFFFFFFFF') - disabledColor : [opt] hexstring - color of disabled label's label. (e.g. '0xFFFF3300') - _alignment : [opt] integer - alignment of label - *Note, see xbfont.h - focusTexture : [opt] string - filename for focus texture. - noFocusTexture : [opt] string - filename for no focus texture. - isPassword : [opt] bool - if true, mask text value. - - *Note, You can use the above as keywords for arguments and skip certain optional arguments. - Once you use a keyword, all following arguments require the keyword. - After you create the control, you need to add it to the window with palceControl(). - - example: - - self.edit = Edit('Status') - """ - - def __new__(cls, *args, **kwargs): - textures = {'focusTexture': os.path.join(_images, 'Edit', 'button-focus.png'), - 'noFocusTexture': os.path.join(_images, 'Edit', 'black-back2.png')} - _set_textures(textures, kwargs) - return super(Edit, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs) - - -class List(xbmcgui.ControlList): - """ControlList class. - - Parameters: - font: string - font used for items label. (e.g. 'font13') - textColor: hexstring - color of items label. (e.g. '0xFFFFFFFF') - buttonTexture: string - filename for no focus texture. - buttonFocusTexture: string - filename for focus texture. - selectedColor: integer - x offset of label. - _imageWidth: integer - width of items icon or thumbnail. - _imageHeight: integer - height of items icon or thumbnail. - _itemTextXOffset: integer - x offset of items label. - _itemTextYOffset: integer - y offset of items label. - _itemHeight: integer - height of items. - _space: integer - space between items. - _alignmentY: integer - Y-axis alignment of items label - *Note, see xbfont.h - - Note: - After you create the control, you need to add it to the window with placeControl(). - - Example: - self.cList = List('font14', space=5) - """ - - def __new__(cls, *args, **kwargs): - textures = {'buttonTexture': os.path.join(_images, 'List', 'MenuItemNF.png'), - 'buttonFocusTexture': os.path.join(_images, 'List', 'MenuItemFO.png')} - _set_textures(textures, kwargs) - return super(List, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs) - - -class Slider(xbmcgui.ControlSlider): - """ControlSlider class. - - Parameters: - textureback: string - image filename. - texture: string - image filename. - texturefocus: string - image filename. - - Note: - After you create the control, you need to add it to the window with placeControl(). - - Example: - self.slider = Slider() - """ - - def __new__(cls, *args, **kwargs): - textures = {'textureback': os.path.join(_images, 'Slider', 'osd_slider_bg.png'), - 'texture': os.path.join(_images, 'Slider', 'osd_slider_nibNF.png'), - 'texturefocus': os.path.join(_images, 'Slider', 'osd_slider_nib.png')} - _set_textures(textures, kwargs) - return super(Slider, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs) - - -class _AbstractWindow(object): - """ - Top-level control window. - - The control windows serves as a parent widget for other XBMC UI controls - much like Tkinter.Tk or PyQt QWidget class. - This is an abstract class which is not supposed to be instantiated directly - and will raise exeptions. - - This class is a basic "skeleton" for a control window. - """ - - def __init__(self): - """Constructor method.""" - self.actions_connected = [] - self.controls_connected = [] - - def setGeometry(self, width_, height_, rows_, columns_, pos_x=-1, pos_y=-1): - """ - Set width, height, Grid layout, and coordinates (optional) for a new control window. - - Parameters: - width_, height_: widgh and height of the created window. - rows_, columns_: rows and colums of the Grid layout to place controls on. - pos_x, pos_y (optional): coordinates of the top left corner of the window. - If pos_x and pos_y are not privided, the window will be placed - at the center of the screen. - Example: - self.setGeometry(400, 500, 5, 4) - """ - self.width = width_ - self.height = height_ - self.rows = rows_ - self.columns = columns_ - if pos_x > 0 and pos_y > 0: - self.x = pos_x - self.y = pos_y - else: - self.x = 640 - self.width / 2 - self.y = 360 - self.height / 2 - self.setGrid() - - def setGrid(self): - """ - Set window grid layout of rows * columns. - This is a helper method not to be called directly. - """ - self.grid_x = self.x - self.grid_y = self.y - self.tile_width = self.width / self.columns - self.tile_height = self.height / self.rows - - def placeControl(self, control, row, column, rowspan=1, columnspan=1, pad_x=5, pad_y=5): - """ - Place a control within the window grid layout. - - pad_x, pad_y: horisontal and vertical padding for control's - size and aspect adjustments. Negative values can be used - to make a control overlap with grid cells next to it, if necessary. - Raises AddonWindowError if a grid has not yet been set. - Example: - self.placeControl(self.label, 0, 1) - """ - try: - control_x = (self.grid_x + self.tile_width * column) + pad_x - control_y = (self.grid_y + self.tile_height * row) + pad_y - control_width = self.tile_width * columnspan - 2 * pad_x - control_height = self.tile_height * rowspan - 2 * pad_y - except AttributeError: - raise AddonWindowError('Window geometry is not defined! Call setGeometry first.') - control.setPosition(control_x, control_y) - control.setWidth(control_width) - control.setHeight(control_height) - self.addControl(control) - self.setAnimation(control) - - def getX(self): - """Get X coordinate of the top-left corner of the window.""" - try: - return self.x - except AttributeError: - raise AddonWindowError('Window geometry is not defined! Call setGeometry first.') - - def getY(self): - """Get Y coordinate of the top-left corner of the window.""" - try: - return self.y - except AttributeError: - raise AddonWindowError('Window geometry is not defined! Call setGeometry first.') - - def getWindowWidth(self): - """Get window width.""" - try: - return self.width - except AttributeError: - raise AddonWindowError('Window geometry is not defined! Call setGeometry first.') - - def getWindowHeight(self): - """Get window height.""" - try: - return self.height - except AttributeError: - raise AddonWindowError('Window geometry is not defined! Call setGeometry first.') - - def getRows(self): - """ - Get grid rows count. - Raises AddonWindowError if a grid has not yet been set. - """ - try: - return self.rows - except AttributeError: - raise AddonWindowError('Grid layot is not set! Call setGeometry first.') - - def getColumns(self): - """ - Get grid columns count. - Raises AddonWindowError if a grid has not yet been set. - """ - try: - return self.columns - except AttributeError: - raise AddonWindowError('Grid layout is not set! Call setGeometry first.') - - def connect(self, event, function): - """ - Connect an event to a function. - - An event can be an inctance of a Control object or an integer key action code. - Several basic key action codes are provided by PyXBMCT. More action codes can be found at - https://github.com/xbmc/xbmc/blob/master/xbmc/guilib/Key.h - - You can connect the following Controls: Button, RadioButton and List. Other Controls do not - generate any control events when activated so their connections won't work. - To catch Slider events you need to connect the following key actions: - ACTION_MOVE_LEFT, ACTION_MOVE_RIGHT and ACTION_MOUSE_DRAG, and do a check - whether the Slider is focused. - - "function" parameter is a function or a method to be executed. Note that you must provide - a function object [without brackets ()], not a function call! - lambda can be used as a function to call another function or method with parameters. - - Examples: - self.connect(self.exit_button, self.close) - or - self.connect(ACTION_NAV_BACK, self.close) - """ - try: - self.disconnect(event) - except AddonWindowError: - if type(event) == int: - self.actions_connected.append([event, function]) - else: - self.controls_connected.append([event, function]) - - def connectEventList(self, events, function): - """ - Connect a list of controls/action codes to a function. - See connect docstring for more info. - """ - [self.connect(event, function) for event in events] - - def disconnect(self, event): - """ - Disconnect an event from a function. - - An event can be an inctance of a Control object or an integer key action code - which has previously been connected to a function or a method. - Raises AddonWindowError if an event is not connected to any function. - - Examples: - self.disconnect(self.exit_button) - or - self.disconnect(ACTION_NAV_BACK) - """ - if type(event) == int: - event_list = self.actions_connected - else: - event_list = self.controls_connected - for index in range(len(event_list)): - if event == event_list[index][0]: - event_list.pop(index) - break - else: - raise AddonWindowError('The action or control %s is not connected!' % event) - - def disconnectEventList(self, events): - """ - Disconnect a list of controls/action codes from functions. - See disconnect docstring for more info. - Raises AddonWindowError if at least one event in the list - is not connected to any function. - """ - [self.disconnect(event) for event in events] - - def executeConnected(self, event, connected_list): - """ - Execute a connected event (an action or a control). - This is a helper method not to be called directly. - """ - for item in connected_list: - if event == item[0]: - item[1]() - break - - def setAnimation(self, control): - """ - This method is called to set animation properties for all controls - added to the current addon window instance - both built-in controls - (window background, title bar etc.) and controls added with placeControl(). - It receives a control instance as the 2nd positional argument (besides self). - By default the method does nothing, i.e. no animation is set for controls. - To add animation you need to re-implement this menthod in your child class. - - E.g: - def setAnimation(self, control): - control.setAnimations([('WindowOpen', 'effect=fade start=0 end=100 time=1000',), - ('WindowClose', 'effect=fade start=100 end=0 time=1000',)]) - """ - pass - - -class _AddonWindow(_AbstractWindow): - """ - Top-level control window. - - The control windows serves as a parent widget for other XBMC UI controls - much like Tkinter.Tk or PyQt QWidget class. - This is an abstract class which is not supposed to be instantiated directly - and will raise exeptions. It is designed to be implemented in a grand-child class - with the second inheritance from xbmcgui.Window or xbmcgui.WindowDialog - in a direct child class. - - This class provides a control window with a background and a header - similar to top-level widgets of desktop UI frameworks. - """ - - def __init__(self, title=''): - """Constructor method.""" - super(_AddonWindow, self).__init__() - self.setFrame(title) - - def setFrame(self, title): - """ - Define paths to images for window background and title background textures, - and set control position adjustment constants used in setGrid. - This is a helper method not to be called directly. - """ - # Window background image - self.background_img = os.path.join(_images, 'AddonWindow', 'ContentPanel.png') - # Background for a window header - self.title_background_img = os.path.join(_images, 'AddonWindow', 'dialogheader.png') - # Horisontal adjustment for a header background if the main background has transparent edges. - self.X_MARGIN = 5 - # Vertical adjustment for a header background if the main background has transparent edges - self.Y_MARGIN = 5 - # Header position adjustment if the main backround has visible borders. - self.Y_SHIFT = 4 - # The height of a window header (for the title background and the title label). - self.HEADER_HEIGHT = 35 - self.background = xbmcgui.ControlImage(-10, -10, 1, 1, self.background_img) - self.addControl(self.background) - self.setAnimation(self.background) - self.title_background = xbmcgui.ControlImage(-10, -10, 1, 1, self.title_background_img) - self.addControl(self.title_background) - self.setAnimation(self.title_background) - self.title_bar = xbmcgui.ControlLabel(-10, -10, 1, 1, title, alignment=ALIGN_CENTER, textColor='0xFFFFA500', - font='font13_title') - self.addControl(self.title_bar) - self.setAnimation(self.title_bar) - self.window_close_button = xbmcgui.ControlButton(-100, -100, 60, 30, '', - focusTexture=os.path.join(_images, 'AddonWindow', - 'DialogCloseButton-focus.png'), - noFocusTexture=os.path.join(_images, 'AddonWindow', - 'DialogCloseButton.png')) - self.addControl(self.window_close_button) - self.setAnimation(self.window_close_button) - - def setGeometry(self, width_, height_, rows_, columns_, pos_x=-1, pos_y=-1, padding=5): - """ - Set width, height, Grid layout, and coordinates (optional) for a new control window. - - Parameters: - width_, height_: widgh and height of the created window. - rows_, columns_: rows and colums of the Grid layout to place controls on. - pos_x, pos_y (optional): coordinates of the top left corner of the window. - If pos_x and pos_y are not privided, the window will be placed - at the center of the screen. - padding (optional): padding between outer edges of the window and - controls placed on it. - Example: - self.setGeometry(400, 500, 5, 4) - """ - self.win_padding = padding - super(_AddonWindow, self).setGeometry(width_, height_, rows_, columns_, pos_x, pos_y) - self.background.setPosition(self.x, self.y) - self.background.setWidth(self.width) - self.background.setHeight(self.height) - self.title_background.setPosition(self.x + self.X_MARGIN, self.y + self.Y_MARGIN + self.Y_SHIFT) - self.title_background.setWidth(self.width - 2 * self.X_MARGIN) - self.title_background.setHeight(self.HEADER_HEIGHT) - self.title_bar.setPosition(self.x + self.X_MARGIN, self.y + self.Y_MARGIN + self.Y_SHIFT) - self.title_bar.setWidth(self.width - 2 * self.X_MARGIN) - self.title_bar.setHeight(self.HEADER_HEIGHT) - self.window_close_button.setPosition(self.x + self.width - 70, self.y + self.Y_MARGIN + self.Y_SHIFT) - - def setGrid(self): - """ - Set window grid layout of rows * columns. - This is a helper method not to be called directly. - """ - self.grid_x = self.x + self.X_MARGIN + self.win_padding - self.grid_y = self.y + self.Y_MARGIN + self.Y_SHIFT + self.HEADER_HEIGHT + self.win_padding - self.tile_width = (self.width - 2 * (self.X_MARGIN + self.win_padding)) / self.columns - self.tile_height = ( - self.height - self.HEADER_HEIGHT - self.Y_SHIFT - 2 * ( - self.Y_MARGIN + self.win_padding)) / self.rows - - def setWindowTitle(self, title=''): - """ - Set window title. - This method must be called AFTER (!!!) setGeometry(), - otherwise there is some werid bug with all skin text labels set to the 'title' text. - Example: - self.setWindowTitle('My Cool Addon') - """ - self.title_bar.setLabel(title) - - def getWindowTitle(self): - """Get window title.""" - return self.title_bar.getLabel() - - -class _FullWindow(xbmcgui.Window): - """An abstract class to define window event processing.""" - - def onAction(self, action): - """ - Catch button actions. - Note that, despite being compared to an integer, - action is an instance of xbmcgui.Action class. - """ - if action == ACTION_PREVIOUS_MENU: - self.close() - else: - self.executeConnected(action, self.actions_connected) - - def onControl(self, control): - """ - Catch activated controls. - Control is an instance of xbmcgui.Control class. - """ - if control == self.window_close_button: - self.close() - else: - self.executeConnected(control, self.controls_connected) - - -class _DialogWindow(xbmcgui.WindowDialog): - """An abstract class to define window event processing.""" - - def onAction(self, action): - """ - Catch button actions. - Note that, despite being compared to an integer, - action is an instance of xbmcgui.Action class. - """ - if action == ACTION_PREVIOUS_MENU: - self.close() - else: - self.executeConnected(action, self.actions_connected) - - def onControl(self, control): - """ - Catch activated controls. - Control is an instance of xbmcgui.Control class. - """ - if control == self.window_close_button: - self.close() - else: - self.executeConnected(control, self.controls_connected) - - -class BlankFullWindow(_FullWindow, _AbstractWindow): - """ - Addon UI container with a solid background. - This is a blank window with a black background and without any elements whatsoever. - The decoration and layout are completely up to an addon developer. - The window controls can hide under video or music visualization. - Window ID can be passed on class instantiation an agrument - but __init__ must have the 2nd fake argument, e.g: - - def __init__(self, *args) - - Minimal example: - - addon = MyAddon('My Cool Addon') - addon.setGeometry(400, 300, 4, 3) - addon.doModal() - """ - pass - - -class BlankDialogWindow(_DialogWindow, _AbstractWindow): - """ - Addon UI container with a transparent background. - This is a blank window with a transparent background and without any elements whatsoever. - The decoration and layout are completely up to an addon developer. - The window controls are always displayed over video or music visualization. - Minimal example: - - addon = MyAddon('My Cool Addon') - addon.setGeometry(400, 300, 4, 3) - addon.doModal() - """ - pass - - -class AddonFullWindow(_FullWindow, _AddonWindow): - """ - Addon UI container with a solid background. - Control window is displayed on top of the main background image - self.main_bg. - Video and music visualization are displayed unhindered. - Window ID can be passed on class instantiation as the 2nd positional agrument - but __init__ must have the 3rd fake argument, e.g: - - def __init__(self, title='', *args) - - Minimal example: - - addon = MyAddon('My Cool Addon') - addon.setGeometry(400, 300, 4, 3) - addon.doModal() - """ - - def __new__(cls, title='', *args, **kwargs): - return super(AddonFullWindow, cls).__new__(cls, *args, **kwargs) - - def setFrame(self, title): - """ - Set the image for for the fullscreen background. - """ - # Image for the fullscreen background. - self.main_bg_img = os.path.join(_images, 'AddonWindow', 'SKINDEFAULT.jpg') - # Fullscreen background image control. - self.main_bg = xbmcgui.ControlImage(1, 1, 1280, 720, self.main_bg_img) - self.addControl(self.main_bg) - super(AddonFullWindow, self).setFrame(title) - - def setBackground(self, image=''): - """ - Set the main bacground to an image file. - image: path to an image file as str. - Example: - self.setBackground('d:\images\bacground.png') - """ - self.main_bg.setImage(image) - - -class AddonDialogWindow(_DialogWindow, _AddonWindow): - """ - Addon UI container with a transparent background. - Control window is displayed on top of XBMC UI, - including video an music visualization! - Minimal example: - - addon = MyAddon('My Cool Addon') - addon.setGeometry(400, 300, 4, 3) - addon.doModal() - """ - pass diff --git a/resources/pyxbmct/textures/default/AddonWindow/ContentPanel.png b/resources/pyxbmct/textures/default/AddonWindow/ContentPanel.png deleted file mode 100644 index eb022d6..0000000 Binary files a/resources/pyxbmct/textures/default/AddonWindow/ContentPanel.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/AddonWindow/DialogCloseButton-focus.png b/resources/pyxbmct/textures/default/AddonWindow/DialogCloseButton-focus.png deleted file mode 100644 index fdde65c..0000000 Binary files a/resources/pyxbmct/textures/default/AddonWindow/DialogCloseButton-focus.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/AddonWindow/DialogCloseButton.png b/resources/pyxbmct/textures/default/AddonWindow/DialogCloseButton.png deleted file mode 100644 index 7f5d105..0000000 Binary files a/resources/pyxbmct/textures/default/AddonWindow/DialogCloseButton.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/AddonWindow/SKINDEFAULT.jpg b/resources/pyxbmct/textures/default/AddonWindow/SKINDEFAULT.jpg deleted file mode 100644 index 3d5b572..0000000 Binary files a/resources/pyxbmct/textures/default/AddonWindow/SKINDEFAULT.jpg and /dev/null differ diff --git a/resources/pyxbmct/textures/default/AddonWindow/dialogheader.png b/resources/pyxbmct/textures/default/AddonWindow/dialogheader.png deleted file mode 100644 index af7ba85..0000000 Binary files a/resources/pyxbmct/textures/default/AddonWindow/dialogheader.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/Button/KeyboardKey.png b/resources/pyxbmct/textures/default/Button/KeyboardKey.png deleted file mode 100644 index 5c8b2bf..0000000 Binary files a/resources/pyxbmct/textures/default/Button/KeyboardKey.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/Button/KeyboardKeyNF.png b/resources/pyxbmct/textures/default/Button/KeyboardKeyNF.png deleted file mode 100644 index 077777f..0000000 Binary files a/resources/pyxbmct/textures/default/Button/KeyboardKeyNF.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/Edit/black-back2.png b/resources/pyxbmct/textures/default/Edit/black-back2.png deleted file mode 100644 index 399e2d0..0000000 Binary files a/resources/pyxbmct/textures/default/Edit/black-back2.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/Edit/button-focus.png b/resources/pyxbmct/textures/default/Edit/button-focus.png deleted file mode 100644 index 831dd45..0000000 Binary files a/resources/pyxbmct/textures/default/Edit/button-focus.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/List/MenuItemFO.png b/resources/pyxbmct/textures/default/List/MenuItemFO.png deleted file mode 100644 index 14230c0..0000000 Binary files a/resources/pyxbmct/textures/default/List/MenuItemFO.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/List/MenuItemNF.png b/resources/pyxbmct/textures/default/List/MenuItemNF.png deleted file mode 100644 index ecbf84f..0000000 Binary files a/resources/pyxbmct/textures/default/List/MenuItemNF.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/RadioButton/MenuItemFO.png b/resources/pyxbmct/textures/default/RadioButton/MenuItemFO.png deleted file mode 100644 index 14230c0..0000000 Binary files a/resources/pyxbmct/textures/default/RadioButton/MenuItemFO.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/RadioButton/MenuItemNF.png b/resources/pyxbmct/textures/default/RadioButton/MenuItemNF.png deleted file mode 100644 index ecbf84f..0000000 Binary files a/resources/pyxbmct/textures/default/RadioButton/MenuItemNF.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/RadioButton/radiobutton-focus.png b/resources/pyxbmct/textures/default/RadioButton/radiobutton-focus.png deleted file mode 100644 index bcb4cb5..0000000 Binary files a/resources/pyxbmct/textures/default/RadioButton/radiobutton-focus.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/RadioButton/radiobutton-nofocus.png b/resources/pyxbmct/textures/default/RadioButton/radiobutton-nofocus.png deleted file mode 100644 index 282396d..0000000 Binary files a/resources/pyxbmct/textures/default/RadioButton/radiobutton-nofocus.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/Slider/osd_slider_bg.png b/resources/pyxbmct/textures/default/Slider/osd_slider_bg.png deleted file mode 100644 index 4b5160b..0000000 Binary files a/resources/pyxbmct/textures/default/Slider/osd_slider_bg.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/Slider/osd_slider_bg_2.png b/resources/pyxbmct/textures/default/Slider/osd_slider_bg_2.png deleted file mode 100644 index 37dc249..0000000 Binary files a/resources/pyxbmct/textures/default/Slider/osd_slider_bg_2.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/Slider/osd_slider_nib.png b/resources/pyxbmct/textures/default/Slider/osd_slider_nib.png deleted file mode 100644 index 4e57eb1..0000000 Binary files a/resources/pyxbmct/textures/default/Slider/osd_slider_nib.png and /dev/null differ diff --git a/resources/pyxbmct/textures/default/Slider/osd_slider_nibNF.png b/resources/pyxbmct/textures/default/Slider/osd_slider_nibNF.png deleted file mode 100644 index 638d4f8..0000000 Binary files a/resources/pyxbmct/textures/default/Slider/osd_slider_nibNF.png and /dev/null differ diff --git a/resources/settings.xml b/resources/settings.xml index 43c2b1d..b435dd3 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -1,11 +1,12 @@  - + - + + @@ -61,15 +62,18 @@ - - - - - - - - - - + + + + + + + + + + + + + diff --git a/resources/skins/Default/720p/movieinfo.xml b/resources/skins/Default/720p/movieinfo.xml deleted file mode 100644 index 27494b1..0000000 --- a/resources/skins/Default/720p/movieinfo.xml +++ /dev/null @@ -1,218 +0,0 @@ - - - 1 - 240 - 20 - - dialogeffect - - - WindowOpen - WindowClose - - background image - 0 - 0 - 800 - 680 - ConfluenceDialogBack.png - - - Dialog Header image - 40 - 16 - 720 - 40 - Confluencedialogheader.png - - - header label - 40 - 20 - 720 - 30 - font13_title - - center - center - selected - black - - - - Close Window button - 710 - 15 - 64 - 32 - - - - PreviousMenu - ConfluenceDialogCloseButton-focus.png - ConfluenceDialogCloseButton.png - 10 - 10 - 10 - 10 - system.getbool(input.enablemouse) - - - - 760 - 100 - 25 - 495 - ScrollBarV.png - ScrollBarV_bar.png - ScrollBarV_bar_focus.png - ScrollBarNib.png - ScrollBarNib.png - 30 - 131 - true - IntegerGreaterThan(Container(32).NumPages,1) - vertical - - - - Dialog Header image - 33 - 85 - 104 - 149 - - - - - rating - 34 - 240 - 102 - 20 - true - left - center - false - - false - font10 - white - black - false - - - - rating - 34 - 260 - 102 - 20 - true - left - center - false - - false - font10 - white - black - false - - - - kinopoisk - 34 - 292 - 102 - 38 - - - - - filepath - 180 - 70 - 550 - 60 - 470 - left - top - font18 - Нет описания - center - center - white - black - true - - - 615 - 50 - - - close - 350 - 0 - 320 - 40 - center - center - font12_title - - 33 - 60 - 30 - 30 - - - - play - 20 - -50 - 320 - 40 - center - center - font12_title - - 60 - 30 - 33 - 33 - - - - libtorrent - 350 - -50 - 320 - 40 - center - center - font12_title - - 131 - 60 - 22 - 22 - - - - tclient - 20 - 0 - 320 - 40 - center - center - font12_title - - 22 - 22 - 131 - 131 - - - - - diff --git a/resources/skins/Default/720p/reviews.xml b/resources/skins/Default/720p/reviews.xml deleted file mode 100644 index ccc51dc..0000000 --- a/resources/skins/Default/720p/reviews.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - 1 - 240 - 20 - - dialogeffect - - - WindowOpen - WindowClose - - background image - 0 - 0 - 800 - 680 - DialogBack.png - - - Dialog Header image - 40 - 16 - 720 - 40 - dialogheader.png - - - header label - 40 - 20 - 720 - 30 - font13_title - - center - center - selected - black - - - - Close Window button - 710 - 15 - 64 - 32 - - - - PreviousMenu - DialogCloseButton-focus.png - DialogCloseButton.png - 10 - 10 - 10 - 10 - system.getbool(input.enablemouse) - - - - 760 - 100 - 25 - 495 - ScrollBarV.png - ScrollBarV_bar.png - ScrollBarV_bar_focus.png - ScrollBarNib.png - ScrollBarNib.png - 22 - 22 - true - IntegerGreaterThan(Container(32).NumPages,1) - vertical - - - - Dialog Header image - 33 - 85 - 104 - 149 - - - - - - filepath - 60 - 70 - 690 - 60 - 530 - left - top - font12 - Нет отзывов - center - center - white - black - true - - - 615 - 50 - - - close - 20 - 0 - 650 - 40 - center - center - font12_title - - 60 - 60 - 60 - 60 - - - - - - diff --git a/resources/skins/Default/media/ConfluenceDialogBack.png b/resources/skins/Default/media/ConfluenceDialogBack.png deleted file mode 100644 index 812801a..0000000 Binary files a/resources/skins/Default/media/ConfluenceDialogBack.png and /dev/null differ diff --git a/resources/skins/Default/media/ConfluenceDialogCloseButton-focus.png b/resources/skins/Default/media/ConfluenceDialogCloseButton-focus.png deleted file mode 100644 index fdde65c..0000000 Binary files a/resources/skins/Default/media/ConfluenceDialogCloseButton-focus.png and /dev/null differ diff --git a/resources/skins/Default/media/ConfluenceDialogCloseButton.png b/resources/skins/Default/media/ConfluenceDialogCloseButton.png deleted file mode 100644 index 7f5d105..0000000 Binary files a/resources/skins/Default/media/ConfluenceDialogCloseButton.png and /dev/null differ diff --git a/resources/skins/Default/media/Confluencedialogheader.png b/resources/skins/Default/media/Confluencedialogheader.png deleted file mode 100644 index af7ba85..0000000 Binary files a/resources/skins/Default/media/Confluencedialogheader.png and /dev/null differ diff --git a/resources/skins/DialogReviews.py b/resources/skins/DialogReviews.py deleted file mode 100644 index 959f46d..0000000 --- a/resources/skins/DialogReviews.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- - -import re -import htmlentitydefs - -import xbmcgui - -pattern = re.compile("&(\w+?);") - -def html_entity_decode_char(m, defs=htmlentitydefs.entitydefs): - try: - return defs[m.group(1)] - except KeyError: - return m.group(0) - -def html_entity_decode(string): - return pattern.sub(html_entity_decode_char, string) - -KEY_BUTTON_BACK = 275 -KEY_KEYBOARD_ESC = 61467 -ACTION_PREVIOUS_MENU = 10 -ACTION_NAV_BACK = 92 -class DialogReviews(xbmcgui.WindowXMLDialog): - def onInit(self): - print "DialogReviews(): Window Initialized" - self.reviews_box = self.getControl(32) - self.reviews_box.setText(self.get_reviews()) - - self.setFocus(self.getControl(22)) - - def onAction(self, action): - buttonCode = action.getButtonCode() - if (action == ACTION_NAV_BACK or action == ACTION_PREVIOUS_MENU): - self.close() - if (buttonCode == KEY_BUTTON_BACK or buttonCode == KEY_KEYBOARD_ESC): - self.close() - - def onClick(self, controlID): - if (controlID == 2 or controlID == 22): - self.close() - - - def onFocus(self, controlID): - #print "onFocus(): control %i" % controlID - pass - - - def doModal(self, movieHtml): - self.movieHtml = movieHtml - xbmcgui.WindowXMLDialog.doModal(self) - - - def get_reviews(self): - reviews_texts = re.compile('
([^<]+)
',re.S).findall(self.movieHtml) - reviews_autors = re.compile('
',re.S).findall(self.movieHtml) - reviews_dates = re.compile('
([^<]+)
',re.S).findall(self.movieHtml) - texts = '' - i = 0 - for text in reviews_texts: - texts = texts+"\n[B][COLOR purple]"+reviews_autors[i]+"[/COLOR][/B] [I]"+reviews_dates[i]+"[/I]\n" - texts = texts+html_entity_decode(text)+"\n" - i = i + 1 - return texts diff --git a/resources/skins/DialogXml.py b/resources/skins/DialogXml.py deleted file mode 100644 index ad29c21..0000000 --- a/resources/skins/DialogXml.py +++ /dev/null @@ -1,83 +0,0 @@ -# -*- coding: utf-8 -*- -import sys - -import xbmcgui -import Localization -import xbmc - -KEY_BUTTON_BACK = 275 -KEY_KEYBOARD_ESC = 61467 -ACTION_PREVIOUS_MENU = 10 -ACTION_NAV_BACK = 92 - - -class DialogXml(xbmcgui.WindowXMLDialog): - def onInit(self): - print "onInit(): Window Initialized" - localize = Localization.localize - color = '[COLOR %s]%s[/COLOR]' - self.movie_label = self.getControl(32) - self.movie_label.setText(self.movieInfo['desc']) - - if self.movieInfo.get('views'): - self.view_label = self.getControl(34) - self.view_label.setLabel(color % ('blue', localize('Views:')) + self.movieInfo['views']) - - self.view_label = self.getControl(35) - self.ratingcolor = 'green' - self.ratingint = int(self.movieInfo['rating']) - if (self.ratingint < 70): - self.ratingcolor = 'red' - self.view_label.setLabel( - color % ('blue', localize('Rating:')) + color % (self.ratingcolor, self.movieInfo['rating'])) - - self.movie_label = self.getControl(1) - self.movie_label.setLabel(self.movieInfo['title']) - - self.movie_label = self.getControl(32) - self.movie_label.setText(self.movieInfo['desc']) - - self.poster = self.getControl(31) - self.poster.setImage(self.movieInfo['poster']) - - self.poster = self.getControl(36) - self.poster.setImage(self.movieInfo['kinopoisk']) - self.getControl(22).setLabel(localize('Close')) - self.getControl(33).setLabel(localize('Download via T-client')) - self.getControl(30).setLabel(localize('Download via Libtorrent')) - self.getControl(131).setLabel(localize('Play')) - - self.setFocus(self.getControl(22)) - - def onAction(self, action): - buttonCode = action.getButtonCode() - if (action == ACTION_NAV_BACK or action == ACTION_PREVIOUS_MENU): - self.close() - if (buttonCode == KEY_BUTTON_BACK or buttonCode == KEY_KEYBOARD_ESC): - self.close() - - def onClick(self, controlID): - if (controlID == 2 or controlID == 22): - self.close() - if (controlID == 30): - self.RunPlugin('downloadLibtorrent') - if (controlID == 33): - self.RunPlugin('downloadFilesList') - if (controlID == 131): - self.RunPlugin('openTorrent&external=1') - - def RunPlugin(self, action): - if self.link: - exec_str = 'XBMC.RunPlugin(%s)' % \ - ('%s?action=%s&url=%s') % \ - (sys.argv[0], action, self.link) - xbmc.executebuiltin(exec_str) - - def onFocus(self, controlID): - # print "onFocus(): control %i" % controlID - pass - - def doModal(self, movieInfo, url): - self.movieInfo = movieInfo - self.link = url - xbmcgui.WindowXMLDialog.doModal(self) diff --git a/searchwindow.py b/searchwindow.py new file mode 100644 index 0000000..e6403fd --- /dev/null +++ b/searchwindow.py @@ -0,0 +1,1371 @@ +# -*- 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 pyxbmct.addonwindow as pyxbmct +import xbmcaddon +import xbmcgui +import xbmcvfs +import xbmc + +from functions import * + +__settings__ = xbmcaddon.Addon(id='plugin.video.torrenter') +__language__ = __settings__.getLocalizedString +__version__ = __settings__.getAddonInfo('version') +__plugin__ = __settings__.getAddonInfo('name') + " v." + __version__ +__root__ = __settings__.getAddonInfo('path') + +log('SYS ARGV: ' + str(sys.argv)) + +# https://github.com/xbmc/xbmc/blob/8d4a5bba55638dfd0bdc5e7de34f3e5293f99933/xbmc/input/Key.h +ACTION_STOP = 13 +ACTION_PLAYER_PLAY = 79 +ACTION_MOUSE_RIGHT_CLICK = 101 +ACTION_CONTEXT_MENU = 117 +ACTION_SHOW_OSD = 24 + + +class SearchWindow(pyxbmct.AddonDialogWindow): + __settings__ = sys.modules["__main__"].__settings__ + right_buttons_count = 7 + right_label_count = 7 + last_right_buttons_count = 0 + last_top_button = None + last_right_button = None + last_listing_mode = None + route = {} + count = 0 + navi_right_menu = [] + navi_top_menu = [] + + icon = __root__ + '/icons/searchwindow/%s.png' + icon_tc = __root__ + '/icons/searchwindow/%s' + getTorrentClientIcon() + + def __init__(self, params = None): + log('SearchWindow init params: '+str(params)) + super(SearchWindow, self).__init__(self.localize('Torrenter Search Window')) + __settings__.setSetting('loadsw_onstop', 'false') + self.setGeometry(1280, 720, 9, 16) + self.set_navi() + self.set_controls() + self.set_focus() + self.connect_controls() + if params and params.get('mode'): + if params.get('mode') == 'load': + self.navi_load() + elif params.get('mode') == 'search': + self.search(params) + elif params.get('mode') == 'externalsearch': + self.externalsearch(params) + elif params.get('mode') == 'history': + self.history() + elif params.get('mode') == 'downloadstatus': + self.downloadstatus() + elif params.get('mode') == 'browser': + self.browser() + elif params.get('mode') == 'watched': + self.watched() + elif params.get('mode') == 'open_torrent': + self.open_torrent(params) + elif params.get('mode') == 'file_browser': + self.file_browser(params) + else: + self.navi_load() + + def set_navi(self): + self.navi = { + 'last_top_button': 4, + 'last_right_button': 1, + 'contentList': [], + 'searchersList': [], + 'filesList': [], + 'last_addtime': None, + 'last_query': None, + 'last_link': None, + 'last_filename': None, + 'route': [{'mode': 'close', 'params': {}, 'last_listing_item': 0}] + } + + def set_controls(self): + if not __settings__.getSetting('sw_transparent_back') == 'true': + self.background.setImage('%s/icons/%s.png' % (__root__, 'ContentPanel')) + + # Top menu + self.button_downloadstatus = pyxbmct.Button("", + focusTexture=self.icon % 'fdownloadstatus', + noFocusTexture=self.icon % 'nfdownloadstatus') + self.placeControl(self.button_downloadstatus, 0, 1, 1, 1) + + self.button_browser = pyxbmct.Button("", + focusTexture=self.icon_tc % 'f', + noFocusTexture=self.icon_tc % 'nf') + self.placeControl(self.button_browser, 0, 2, 1, 1) + self.button_controlcenter = pyxbmct.Button("", focusTexture=self.icon % 'fcontrolcenter', + noFocusTexture=self.icon % 'nfcontrolcenter') + self.placeControl(self.button_controlcenter, 0, 3, 1, 1) + self.button_filter = pyxbmct.Button("", focusTexture=self.icon % 'fkeyboard', + noFocusTexture=self.icon % 'nfkeyboard') + self.placeControl(self.button_filter, 0, 4, 1, 1) + self.input_search = pyxbmct.Edit("", _alignment=pyxbmct.ALIGN_CENTER_X | pyxbmct.ALIGN_CENTER_Y) + self.placeControl(self.input_search, 0, 5, 1, 6) + self.button_search = pyxbmct.Button("", focusTexture=self.icon % 'fsearch', + noFocusTexture=self.icon % 'nfsearch') + self.placeControl(self.button_search, 0, 11, 1, 1) + self.button_history = pyxbmct.Button("", focusTexture=self.icon % 'fhistory', + noFocusTexture=self.icon % 'nfhistory') + self.placeControl(self.button_history, 0, 12, 1, 1) + self.button_watched = pyxbmct.Button("", focusTexture=self.icon % 'fwatched', + noFocusTexture=self.icon % 'nfwatched') + self.placeControl(self.button_watched, 0, 13, 1, 1) + + # Main + self.listing = pyxbmct.List(_imageWidth=60, _imageHeight=60, _itemTextXOffset=1, + _itemTextYOffset=0, _itemHeight=60, _space=0, _alignmentY=4) + self.placeControl(self.listing, 1, 0, 8, 14) + + self.navi_top_menu = [self.button_downloadstatus, self.button_browser, self.button_controlcenter, + self.input_search, self.button_search, self.button_history, self.button_watched] + + # Right menu + self.right_menu() + + def connect_controls(self): + self.connect(self.listing, self.right_press1) + self.connect(self.button_history, self.history) + self.connect(self.button_search, self.search) + self.connect(self.button_controlcenter, self.controlcenter) + self.connect(self.button_browser, self.browser) + self.connect(self.button_downloadstatus, self.downloadstatus) + self.connect(self.button_watched, self.watched) + self.connect(self.button_filter, self.filter) + + self.connect(pyxbmct.ACTION_NAV_BACK, self.navi_back) + self.connect(pyxbmct.ACTION_PREVIOUS_MENU, self.navi_back) + self.connect(xbmcgui.ACTION_BACKSPACE, self.navi_back) + self.connect(xbmcgui.KEY_BUTTON_BACK, self.navi_back) + + self.connect(ACTION_MOUSE_RIGHT_CLICK, self.context) + self.connect(ACTION_CONTEXT_MENU, self.context) + self.connect(ACTION_SHOW_OSD, self.context) + + self.connect(pyxbmct.ACTION_MOVE_LEFT, self.navi_update) + self.connect(pyxbmct.ACTION_MOVE_RIGHT, self.navi_update) + self.connect(pyxbmct.ACTION_MOVE_UP, self.navi_update) + self.connect(pyxbmct.ACTION_MOVE_DOWN, self.navi_update) + + def set_navigation(self): + # Top menu + self.button_browser.setNavigation(self.window_close_button, self.listing, self.button_downloadstatus, + self.button_controlcenter) + self.button_controlcenter.setNavigation(self.window_close_button, self.listing, self.button_browser, self.button_filter) + self.button_filter.setNavigation(self.window_close_button, self.listing, self.button_controlcenter, self.input_search) + self.input_search.setNavigation(self.window_close_button, self.listing, self.button_filter, self.button_search) + self.button_search.setNavigation(self.window_close_button, self.listing, self.input_search, self.button_history) + self.button_history.setNavigation(self.window_close_button, self.listing, self.button_search, self.button_watched) + self.update_navigation() + + def update_navigation(self): + self.last_top_button = self.navi_top_menu[self.navi['last_top_button'] - 1] + if self.navi['last_right_button'] > self.right_label_count: + self.navi['last_right_button'] = self.right_label_count + self.last_right_button = self.navi_right_menu[self.navi['last_right_button'] - 1] + + # Top menu + self.button_downloadstatus.setNavigation(self.window_close_button, self.listing, self.last_right_button, + self.button_browser) + self.button_watched.setNavigation(self.window_close_button, self.listing, self.button_history, self.last_right_button) + self.window_close_button.setNavigation(self.listing, self.last_top_button, self.button_watched, + self.button_downloadstatus) + # Main + self.listing.setNavigation(self.last_top_button, self.input_search, self.button_downloadstatus, + self.last_right_button) + + def setAnimation(self, control): + # Set fade animation for all add-on window controls + control.setAnimations([('WindowOpen', 'effect=fade start=0 end=100 time=500',), + ('WindowClose', 'effect=fade start=100 end=0 time=500',)]) + + def navi_back(self): + debug('navi_back init') + self.navi['route'].pop(-1) + self.navi_restore() + + def navi_route_reset(self): + debug('navi_route_reset init') + self.navi['route'] = [self.navi['route'][0]] + + def navi_route_pop(self): + debug('navi_route_pop init') + self.navi['route'].pop(-1) + + def navi_restore(self): + debug('navi_restore init') + self.route = self.navi['route'].pop(-1) + action = getattr(self, self.route['mode']) + try: + if self.route['params']: + action(self.route['params']) + else: + action() + + self.set_focus(self.route['mode']) + + debug('self.route[last_listing_item]: ' + str(self.route['last_listing_item'])) + if self.route['last_listing_item'] > 0: + self.listing.selectItem(self.route['last_listing_item']) + except: + import traceback + debug('navi_restore ERROR '+traceback.format_exc()) + self.set_navi() + self.history() + + def navi_load(self): + debug('navi_load init') + __tmppath__ = os.path.join(xbmc.translatePath('special://temp'), 'xbmcup', 'plugin.video.torrenter') + if not xbmcvfs.exists(__tmppath__): + xbmcvfs.mkdirs(__tmppath__) + navi_file = os.path.join(__tmppath__, 'navi.txt') + if not xbmcvfs.exists(navi_file): + self.set_navi() + self.navi['route'].append({"last_listing_item": 0, "params": {}, "mode": "history"}) + with open(navi_file, 'w') as f: f.write(json.dumps(self.navi)) + + with open(navi_file, 'r') as read: navi = read.read() + + try: + debug('navi_load navi: '+str(navi)) + log('navi_load navi: ' + str(navi['route'])) + except: + log('navi_load load error') + + if navi and len(navi) > 0: + self.navi = json.loads(navi) + self.navi_restore() + + def navi_save(self, mode = None): + debug('navi_save init') + + self.set_focus(mode) + + navi = json.dumps(self.navi) + + __tmppath__ = os.path.join(xbmc.translatePath('special://temp'), 'xbmcup', 'plugin.video.torrenter') + if not xbmcvfs.exists(__tmppath__): + xbmcvfs.mkdirs(__tmppath__) + navi_file = os.path.join(__tmppath__, 'navi.txt') + write = xbmcvfs.File(navi_file, 'w') + write.write(navi) + write.close() + + def navi_update(self): + debug('navi_update init') + try: + focused_control = self.getFocus() + except: + focused_control = None + debug('start navi_update' + str(focused_control)) + debug(str(self.navi['route'])) + + if focused_control == self.listing: + item_index = self.listing.getSelectedPosition() + self.navi['route'][-1]['last_listing_item'] = item_index + debug('self.listing getSelectedPosition ' + str(item_index)) + + item = self.listing.getSelectedItem() + params = json.loads(item.getfilename()) + mode = params.get('mode') + debug('navi_update:' + str(mode)) + if self.last_listing_mode != mode: + self.last_listing_mode = mode + debug('set_menulist navi_update:' + str(mode)) + self.set_menulist(mode) + self.update_navigation() + + elif focused_control in self.navi_top_menu: + self.navi['last_top_button'] = self.navi_top_menu.index(focused_control) + 1 + self.update_navigation() + + elif focused_control in self.navi_right_menu: + self.navi['last_right_button'] = self.navi_right_menu.index(focused_control) + 1 + self.update_navigation() + + def navi_route(self, mode, params = {}, right_menu = None): + debug('navi_route init') + try: + focused_control = self.getFocus() + except: + focused_control = None + + if focused_control in self.navi_top_menu: + debug('focused_control in self.navi[\'top_menu\']') + self.navi_route_reset() + + debug('***** self.navi[\'route\'].append *****' + str(mode) + str(params)) + + self.navi['route'].append({'mode': mode, + 'params': params, + 'last_listing_item': 0}) + + self.right_menu(mode if not right_menu else right_menu) + self.listing.reset() + + def set_focus(self, mode = None): + if not self.listing.size(): + if mode and hasattr(self, "button_" + mode): + self.setFocus(getattr(self, "button_" + mode)) + else: + self.setFocus(self.input_search) + else: + self.setFocus(self.listing) + + def search(self, params = {}): + log('search init params: ' + str(params)) + self.navi_route('search', params) + + get = params.get + addtime = get('addtime') + query = get('query') + self.route = self.navi['route'][-1] + + if query: + self.input_search.setText(query) + else: + if self.input_search.getText() not in ['', None]: + query = self.input_search.getText() + elif self.navi['last_query'] not in ['', None]: + query = self.navi['last_query'] + self.input_search.setText(self.navi['last_query']) + + log('Search query: ' + str(query)) + + if not addtime and query == self.navi['last_query']: + addtime = self.navi['last_addtime'] + + searchersList = get_searchersList(addtime) + + # cache + self.route['params']['query'] = query + if (query != self.navi['last_query'] or self.navi['searchersList'] != searchersList) and len(query) > 0: + self.navi['filesList'] = get_filesList(query, searchersList, addtime) + self.route['params']['addtime'] = addtime + self.navi['last_addtime'] = addtime + self.navi['searchersList'] = searchersList + self.navi['last_query'] = query + elif len(query) == 0: + self.navi['filesList'] = [] + + if self.navi['filesList']: + for (order, seeds, leechers, size, title, link, image) in self.navi['filesList']: + title = titleMake(seeds, leechers, size, title) + self.drawItem(title, {'mode': 'search_item', 'filename': link}, image) + self.setFocus(self.listing) + + def externalsearch(self, params={}): + log('search init params: ' + str(params)) + + if hasattr(self, 'params'): + params = self.params + + self.params = params + get = params.get + query = unquote(get('query'),'') + external = unquote(params.get("external"), 'torrenterall') + back_url = unquote(get("back_url"),'') + self.return_name = unquote(get("return_name"),'') + sdata = unquote(get("sdata"),'{}') + + self.reconnect(self.button_search, self.externalsearch) + self.navi_route('externalsearch', params) + + try: + sdata = json.loads(sdata) + except: + sdata = json.loads(urllib.unquote_plus(sdata)) + + + if self.input_search.getText() not in ['', None]: + query = self.input_search.getText() + else: + self.input_search.setText(query) + + #contextMenu = [ + # (self.localize('Add to %s') % return_name, + # 'XBMC.RunPlugin(%s)' % (back_url + '&stringdata=' + urllib.quote_plus( + # json.dumps(sdata)))), + + + # url = 'plugin://plugin.video.torrenter/?action=searchWindow&mode=externalsearch&query=%s' \ + # '&sdata=%s&external=%s&back_url=%s&return_name=%s' % \ + # (urllib.quote_plus(query), urllib.quote_plus(json.dumps(sdata)), + # self.externals[self.stype], urllib.quote_plus(back_self.url), + # urllib.quote_plus(return_name)) + + log('Search query: ' + str(query)) + + searchersList = [] + + if not external or external == 'torrenterall': + searchersList = get_searchersList() + elif external == 'torrenterone': + slist = Searchers().list().keys() + ret = xbmcgui.Dialog().select(self.localize('Choose searcher')+':', slist) + if ret > -1 and ret < len(slist): + external = slist[ret] + searchersList.append(external) + else: + searchersList.append(external) + + if len(query) > 0: + self.navi['filesList'] = get_filesList(query, searchersList) + else: + self.navi['filesList'] = [] + + if self.navi['filesList']: + for (order, seeds, leechers, size, title, link, image) in self.navi['filesList']: + title = titleMake(seeds, leechers, size, title) + sdata['filename'] = link + stringdata = json.dumps(sdata) + self.drawItem(title, {'mode': 'externalsearch_item', 'filename': link, + 'stringdata': stringdata, 'back_url': back_url}, image) + self.setFocus(self.listing) + + def history(self, params = {}): + self.navi_route('history', params) + + db = HistoryDB() + items = db.get_all() + favlist = [(1, '[B]%s[/B]'), (0, '%s')] + last_listing_item = 0 + last_addtime_fav = False + if items: + for favbool, bbstring in favlist: + for addtime, string, fav in items: + if favbool == int(fav): + title = string.encode('utf-8') + + if int(fav) == 1: + img = __root__ + '/icons/fav.png' + if str(self.navi['last_addtime']) == str(addtime): + last_addtime_fav = True + if not last_addtime_fav: + last_listing_item += 1 + else: + img = __root__ + '/icons/unfav.png' + + link = {'mode': 'history_item', 'query': title, 'addtime': str(addtime), + 'fav': str(fav)} + self.drawItem(bbstring % title, link, img) + self.route['last_listing_item'] = last_listing_item + self.navi_save('history') + + def history_action(self, action, addtime, fav): + db = HistoryDB() + + if action == 'delete': + db.delete(addtime) + showMessage(self.localize('Search History'), self.localize('Deleted!')) + + if action == 'fav' and fav == '0': + db.fav(addtime) + showMessage(self.localize('Favourites'), self.localize('Added!')) + elif action == 'fav': + db.unfav(addtime) + showMessage(self.localize('Favourites'), self.localize('Deleted!')) + + self.navi_restore() + + def watched(self, params = {}): + self.navi_route('watched', params) + + db = WatchedHistoryDB() + + items = db.get_all() + log('[WatchedHistory]: items - '+str(items)) + if items: + for addtime, filename, foldername, path, url, seek, length, ind, size in items: + seek = int(seek) if int(seek) > 3*60 else 0 + watchedPercent = int((float(seek) / float(length if length else 1)) * 100) + duration = '%02d:%02d:%02d' % ((length / (60*60)), (length / 60) % 60, length % 60) + title = '[%d%%][%s] %s [%d MB]' %\ + (watchedPercent, duration, filename.encode('utf-8'), int(size)) + clDimgray = '[COLOR FF696969]%s[/COLOR]' + clWhite = '[COLOR FFFFFFFF]%s[/COLOR]' + + title = clWhite % title + chr(10) + clDimgray % '(%s)' % foldername.encode('utf-8') + + if watchedPercent >= 85: + img = __root__ + '/icons/stop-icon.png' + else: + img = __root__ + '/icons/pause-icon.png' + + + link = {'mode': 'watched_item', 'addtime': str(addtime)} + self.drawItem(title, link, image=img) + self.navi_save('watched') + + def watched_action(self, action, addtime): + db = WatchedHistoryDB() + + if action == 'delete': + db.delete(addtime) + showMessage(self.localize('Watched History'), self.localize('Deleted!')) + self.navi_restore() + + if action == 'open': + filename, foldername, path, url, seek, length, ind = db.get('filename, foldername, path, url, seek, length, ind', 'addtime', str(addtime)) + params = {'link': path.encode('utf-8')} + self.open_torrent(params) + + if action == 'playnoseek' or action == 'playwithseek': + filename, path, url, seek, length, ind = db.get('filename, path, url, seek, length, ind', 'addtime', str(addtime)) + + if action == 'playwithseek': + seek = int(seek) + else: + seek = 0 + + if os.path.exists(path): + __settings__.setSetting("lastTorrent", path) + else: + from Downloader import Downloader + torrent = Downloader.Torrent(self.userStorageDirectory, torrentFilesDirectory=self.torrentFilesDirectory) + __settings__.setSetting("lastTorrent", torrent.saveTorrent(url)) + xbmc.executebuiltin('xbmc.RunPlugin("plugin://plugin.video.torrenter/?action=playTorrent&url='+str(ind)+'&seek='+str(seek)+'")') + __settings__.setSetting('loadsw_onstop', 'true') + self.close() + + if action == 'clear': + db.clear() + showMessage(self.localize('Watched History'), self.localize('Clear!')) + self.navi_restore() + + def browser(self, params = {}): + from resources.utorrent.net import Download + menu, dirs = [], [] + + get = params.get + hash = get('hash') + tdir = get('tdir') + + DownloadList = Download().list() + if DownloadList == False: + showMessage(self.localize('Error'), self.localize('No connection! Check settings!'), forced=True) + return + + if not hash: + self.navi_route('browser') + for data in DownloadList: + status = " " + img = '' + if data['status'] in ('seed_pending', 'stopped'): + status = TextBB(' [||] ', 'b') + elif data['status'] in ('seeding', 'downloading'): + status = TextBB(' [>] ', 'b') + if data['status'] == 'seed_pending': + img = os.path.join(__root__, 'icons', 'pause-icon.png') + elif data['status'] == 'stopped': + img = os.path.join(__root__, 'icons', 'stop-icon.png') + elif data['status'] == 'seeding': + img = os.path.join(__root__, 'icons', 'upload-icon.png') + elif data['status'] == 'downloading': + img = os.path.join(__root__, 'icons', 'download-icon.png') + + title = '[%s%%]%s%s [%s]' % (str(data['progress']), status, data['name'], str(data['ratio'])) + menu.append( + {"title": title, "image": img, "argv": {'mode': 'browser_item', 'hash': str(data['id'])}}) + elif not tdir: + self.navi_route('browser', params, 'browser_subfolder') + self.drawItem('..', {'mode': 'browser_moveup'}, image = 'DefaultFolderBack.png', isFolder = True) + dllist = sorted(Download().listfiles(hash), key=lambda x: x[0]) + for name, percent, ind, size in dllist: + if '/' not in name: + title = '[%s%%][%s]%s' % (str(percent), str(size), name) + menu.append({"title": title, "image": '', + "argv": {'mode': 'browser_file', 'hash': hash, 'ind': str(ind)}}) + else: + newtdir = name.split('/')[0] + if newtdir not in dirs: dirs.append(newtdir) + elif tdir: + self.navi_route('browser', params, 'browser_subfolder') + self.drawItem('..', {'mode': 'browser_moveup'}, isFolder=True) + dllist = sorted(Download().listfiles(hash), key=lambda x: x[0]) + for name, percent, ind, size in dllist: + + if name[:len(tdir)] == tdir: + name = name[len(tdir) + 1:] + if '/' not in name: + title = '[%s%%][%s]%s' % (str(percent), str(size), name) + menu.append({"title": title, "image": '', + "argv": {'mode': 'browser_file', 'hash': hash, 'ind': str(ind)}}) + else: + newtdir = tdir+'/'+name.split('/')[0] + if newtdir not in dirs: dirs.append(newtdir) + + for tdir in dirs: + params = {'mode': 'browser_subfolder', 'hash': hash, 'tdir': tdir} + title = tdir.split('/')[-1] if '/' in tdir else tdir + self.drawItem(title, params, isFolder = True) + + for i in menu: + params = i['argv'] + img = i['image'] + popup = [] + if not hash: + folder = True + else: + folder = False + + self.drawItem(i['title'], params, image = img, isFolder = folder) + + self.navi_save('browser') + + def browser_action(self, hash, action, tdir = None, ind = None): + from resources.utorrent.net import Download + menu = [] + + DownloadList = Download().list() + if DownloadList == False: + showMessage(self.localize('Error'), self.localize('No connection! Check settings!'), forced=True) + return False + + if (ind or ind == 0) and action in ('0', '3'): + Download().setprio_simple(hash, action, ind) + elif action in ['play', 'copy']: + p, dllist, i, folder, filename = DownloadList, Download().listfiles(hash), 0, None, None + for data in p: + if data['id'] == hash: + folder = data['dir'] + break + if isRemoteTorr(): + torrent_dir = __settings__.getSetting("torrent_dir") + torrent_replacement = __settings__.getSetting("torrent_replacement") + empty = [None, ''] + if torrent_dir in empty or torrent_replacement in empty: + if xbmcgui.Dialog().yesno( + self.localize('Remote Torrent-client'), + self.localize('You didn\'t set up replacement path in setting.'), + self.localize('For example /media/dl_torr/ to smb://SERVER/dl_torr/. Setup now?')): + if torrent_dir in empty: + torrent_dir() + __settings__.openSettings() + return + + folder = folder.replace(torrent_dir, torrent_replacement) + if (ind or ind == 0) and action == 'play': + for data in dllist: + if data[2] == int(ind): + filename = data[0] + break + filename = os.path.join(folder, filename) + self.file_play(filename) + elif tdir and action == 'copy': + path = os.path.join(folder, tdir) + dirs, files = xbmcvfs.listdir(path) + if len(dirs) > 0: + dirs.insert(0, self.localize('./ (Root folder)')) + for dd in dirs: + dd = file_decode(dd) + dds = xbmcvfs.listdir(os.path.join(path, dd))[0] + if len(dds) > 0: + for d in dds: + dirs.append(dd + os.sep + d) + ret = xbmcgui.Dialog().select(self.localize('Choose directory:'), dirs) + if ret > 0: + path = os.path.join(path, dirs[ret]) + dirs, files = xbmcvfs.listdir(path) + for file in files: + if not xbmcvfs.exists(os.path.join(path, file)): + xbmcvfs.delete(os.path.join(path, file)) + xbmcvfs.copy(os.path.join(path, file), os.path.join(folder, file)) + i = i + 1 + showMessage(self.localize('Torrent-client Browser'), self.localize('Copied %d files!') % i, forced=True) + return True + elif not tdir and action not in ('0', '3'): + if action == 'removedata': + ok = xbmcgui.Dialog().yesno(self.localize('Torrent-client Browser'), + self.localize('Delete torrent with files?')) + if not ok: sys.exit(1) + Download().action_simple(action, hash) + elif action in ('0', '3'): + dllist = sorted(Download().listfiles(hash), key=lambda x: x[0]) + for name, percent, ind, size in dllist: + if tdir: + if '/' in name and tdir in name: + menu.append((hash, action, str(ind))) + else: + menu.append((hash, action, str(ind))) + Download().setprio_simple_multi(menu) + return True + return True + + def downloadstatus(self, params = {}): + self.navi_route('downloadstatus', params) + + db = DownloadDB() + items = db.get_all() + + if items: + for addtime, title, path, type, info, status, torrent, ind, lastupdate, storage in items: + jsoninfo = json.loads(urllib.unquote_plus(info)) + + if status != 'stopped' and int(lastupdate) < int(time.time()) - 10: + status = 'stopped' + db.update_status(addtime, status) + + progress = int(jsoninfo.get('progress')) + if status == 'pause': + status_sign = '[||]' + img = os.path.join(__root__, 'icons', 'pause-icon.png') + elif status == 'stopped': + status_sign = '[X]' + img = os.path.join(__root__, 'icons', 'stop-icon.png') + else: + status_sign = '[>]' + if progress == 100: + img = os.path.join(__root__, 'icons', 'upload-icon.png') + else: + img = os.path.join(__root__, 'icons', 'download-icon.png') + + title = '[%d%%]%s %s' % (progress, status_sign, title) + if jsoninfo.get('seeds') != None and jsoninfo.get('peers') != None and \ + jsoninfo.get('download') != None and jsoninfo.get('upload') != None: + d, u = float(jsoninfo['download']) / 1000000, float(jsoninfo['upload']) / 1000000 + s, p = str(jsoninfo['seeds']), str(jsoninfo['peers']) + second = '[D/U %.2f/%.2f (MB/s)][S/L %s/%s]' % (d, u, s, p) + title = dlstat_titleMake('[B]%s[/B]' % title if type == 'folder' else title, second) + + params = {'addtime': addtime, 'type': type, 'path': path, + 'status': status, 'progress': progress, 'storage': storage} + params['mode'] = 'downloadstatus_subfolder' if type == 'folder' else 'downloadstatus_file' + + self.drawItem(title, params, image=img, isFolder=type == 'folder') + + # def drawItem(self, title, params, image = None, isFolder = False): + + self.navi_save('downloadstatus') + + def downloadstatus_action(self, action, addtime, path, type, progress, storage): + + db = DownloadDB() + + if action == 'play': + if type == 'file' and progress > 30 or progress == 100: + self.file_browser(type, path, path) + else: + showMessage(self.localize('Download Status'), self.localize('Download has not finished yet')) + + elif action == 'delete': + db.delete(addtime) + showMessage(self.localize('Download Status'), self.localize('Stopped and Deleted!')) + + elif action == 'pause': + db.update_status(addtime, 'pause') + showMessage(self.localize('Download Status'), self.localize('Paused!')) + + elif action == 'stop': + db.update_status(addtime, 'stopped') + showMessage(self.localize('Download Status'), self.localize('Stopped!')) + + elif action == 'start': + start = db.get_byaddtime(addtime) + if start[5] == 'pause': + db.update_status(addtime, 'downloading') + showMessage(self.localize('Download Status'), self.localize('Unpaused!')) + else: + torrent, ind = start[6], start[7] + + del db + + import SkorbaLoader + __settings__.setSetting("lastTorrent", torrent.encode('utf-8')) + torrent = SkorbaLoader.SkorbaLoader(storage.encode('utf-8'), torrent) + encryption = __settings__.getSetting('encryption') == 'true' + torrent.downloadProcess(ind, encryption) + showMessage(self.localize('Download Status'), self.localize('Started!')) + xbmc.sleep(1000) + + elif action == 'masscontrol': + dialog_items = [self.localize('Start All'), self.localize('Stop All'), + self.localize('Clear %s') % self.localize('Download Status'), self.localize('Cancel')] + ret = xbmcgui.Dialog().select(self.localize('Mass Control'), dialog_items) + if ret == 0: + items = db.get_all() + del db + if items: + import SkorbaLoader + for addtime, title, path, type, info, status, torrent, ind, lastupdate, storage in items: + __settings__.setSetting("lastTorrent", torrent.encode('utf-8')) + torrent = SkorbaLoader.SkorbaLoader(storage.encode('utf-8'), torrent) + encryption = __settings__.getSetting('encryption') == 'true' + torrent.downloadProcess(ind, encryption) + xbmc.sleep(1000) + + xbmc.sleep(2000) + showMessage(self.localize('Download Status'), self.localize('Started All!')) + elif ret == 1: + items = db.get_all() + if items: + for addtime, title, path, type, info, status, torrent, ind, lastupdate, storage in items: + db.update_status(addtime, 'stopped') + xbmc.sleep(1000) + showMessage(self.localize('Download Status'), self.localize('Stopped All!')) + elif ret == 2: + db.clear() + showMessage(self.localize('Download Status'), self.localize('Clear!')) + + xbmc.sleep(1000) + self.downloadstatus() + + def file_browser(self, params): + self.navi_route('file_browser', params) + + get = params.get + mode = get('mode') + path = get('path') + tdir = get('tdir') + + path = encode_msg(path) + tdir = encode_msg(tdir) + + if mode == 'moveup' and tdir == os.path.dirname(path): + self.downloadstatus() + elif mode == 'file': + swPlayer().play(localize_path(tdir)) + else: + self.drawItem('..', {'mode': 'moveup', 'path': path, + 'tdir': os.path.dirname(tdir)}, image = 'DefaultFolderBack.png', isFolder=True) + + dirs, files = xbmcvfs.listdir(tdir + os.sep) + if len(dirs) > 0: + for dir in dirs: + link = {'mode': 'subfolder', 'path': path, 'type': 'folder', + 'tdir': os.path.join(tdir, dir)} + self.drawItem(dir, link, isFolder=True) + for file in files: + link = {'mode': 'file', 'path': path, 'type': 'file', + 'tdir': os.path.join(tdir, file)} + self.drawItem(file, link, isFolder=False) + + self.navi_save('file_browser') + + def file_play(self, file): + self.close() + swPlayer().play(item = file) + + def open_torrent(self, params): + self.navi_route('open_torrent', params) + + get = params.get + link = get('link') + tdir = get('tdir') + + # cache + if link != self.navi['last_link']: + self.navi['contentList'], filename = get_contentList(link) + else: + filename = self.navi['last_filename'] + self.navi['last_link'] = link + self.navi['last_filename'] = filename + + dirList, contentListNew = cutFolder(self.navi['contentList'], tdir) + + self.drawItem('..', {'mode': 'torrent_moveup', 'filename': link}, + image = 'DefaultFolderBack.png', isFolder=True) + + dirList = sorted(dirList, key=lambda x: x[0], reverse=False) + for title in dirList: + self.drawItem(title, {'mode': 'torrent_subfolder', 'tdir': title, 'filename': link}, isFolder=True) + + ids_video_result = get_ids_video(contentListNew) + ids_video = '' + + if len(ids_video_result) > 0: + for identifier in ids_video_result: + ids_video = ids_video + str(identifier) + ',' + + contentListNew = sorted(contentListNew, key=lambda x: x[0], reverse=False) + for title, identifier, filesize in contentListNew: + params = {'mode': 'torrent_play', 'index': identifier, 'url2': ids_video.rstrip(','), 'url': link, + 'filename': filename} + self.drawItem(title, params) + + self.navi_save('open_torrent') + + def get_menulist(self, mode): + + label_list = ["Empty", "Empty", "Empty", "Empty", "Empty", "Empty", "Empty"] + + if mode in ['search', 'search_item', 'torrent_play', 'open_torrent']: + label_list = [self.localize('Open'), + self.localize('Download via T-client'), + self.localize('Download via Libtorrent'), + self.localize('Info'),] + if mode in ['externalsearch', 'externalsearch_item']: + label_list = [self.localize('Add to %s') % self.return_name, + self.localize('Open'), + self.localize('Download via T-client'), + self.localize('Download via Libtorrent'), + self.localize('Info'),] + elif mode in ['torrent_subfolder', 'file_browser', 'subfolder']: + label_list = [self.localize('Open'),] + elif mode in ['torrent_moveup', 'browser_moveup']: + label_list = [self.localize('Move Up'),] + elif mode in ['file']: + label_list = [self.localize('Play'), ] + elif mode in ['history', 'history_item']: + label_list = [self.localize('Open'), + self.localize('Edit'), + self.localize('Individual Tracker Options'), + self.localize('Fav. / Unfav.'), + self.localize('Delete')] + elif mode in ['browser', 'browser_item']: + label_list = [self.localize('Open'), self.localize('Start'), self.localize('Stop'), + self.localize('Remove'), self.localize('High Priority'), + self.localize('Skip All Files'), self.localize('Remove with files')] + elif mode in ['browser_file']: + label_list = [self.localize('Play File'), + self.localize('High Priority'), self.localize('Skip File')] + elif mode in ['browser_subfolder']: + label_list = [self.localize('Open'), + self.localize('High Priority'), + self.localize('Skip All Files'), + self.localize('Copy in Root'), ] + elif mode in ['downloadstatus', 'downloadstatus_subfolder']: + label_list = [self.localize('Open'), self.localize('Start'), self.localize('Pause'), + self.localize('Stop'), self.localize('Delete'), self.localize('Mass Control'),] + elif mode in ['downloadstatus_file']: + label_list = [self.localize('Play'), self.localize('Start'), self.localize('Pause'), + self.localize('Stop'), self.localize('Delete'), self.localize('Mass Control'),] + elif mode in ['watched', 'watched_item']: + label_list = [self.localize('Open Torrent'), self.localize('Play (from start)'), + self.localize('Play (with seek)'), self.localize('Delete'), self.localize('Clear History'), ] + self.right_label_count = len(label_list) + return label_list + + def context(self): + try: + focused_control = self.getFocus() + except: + focused_control = None + if focused_control == self.listing: + item = self.listing.getSelectedItem() + params = json.loads(item.getfilename()) + mode = params.get('mode') + filename = params.get('filename') + label_list = self.get_menulist(mode) + + if not self.version_check(): + ret = xbmcgui.Dialog().select(self.localize('Context menu'), label_list) + else: + ret = xbmcgui.Dialog().contextmenu(list=[(x) for x in label_list]) + + if ret > -1 and ret < len(label_list): + getattr(self, "right_press" + str(ret + 1))() + elif focused_control == self.input_search: + self.input_search.setText('') + + def right_menu(self, mode='place'): + if not mode == 'place': + self.last_right_buttons_count = self.right_buttons_count + remove_list = [getattr(self, "button_right" + str(index)) for index + in range(1, self.last_right_buttons_count + 1)] + self.disconnectEventList(remove_list) + self.removeControls(remove_list) + + label_list = self.get_menulist(mode) + self.navi_right_menu = [] + + self.right_buttons_count = len(label_list) + button_num_list = range(1, self.right_buttons_count + 1) + + for index in button_num_list: + setattr(self, "button_right" + str(index), pyxbmct.Button(label_list[index - 1])) + button = getattr(self, "button_right" + str(index)) + self.connect(button, getattr(self, "right_press" + str(index))) + self.placeControl(button, index, 14, 1, 2) + + # Navigation + self.navi['last_right_button'] = 1 + for index in button_num_list: + button = getattr(self, "button_right" + str(index)) + self.navi_right_menu.append(button) + + if self.right_buttons_count == 1: + button.setNavigation(self.button_controlcenter, + self.button_right1, self.listing, self.input_search) + else: + if index == button_num_list[0]: + button.setNavigation(getattr(self, "button_right" + str(self.right_buttons_count)), + self.button_right2, self.listing, self.input_search) + elif index == button_num_list[-1]: + button.setNavigation(getattr(self, "button_right" + str(index - 1)), self.button_right1, + self.listing, + self.input_search) + else: + button.setNavigation(getattr(self, "button_right" + str(index - 1)), + getattr(self, "button_right" + str(index + 1)), + self.listing, + self.input_search) + + self.set_menulist(mode) + self.set_navigation() + + def set_menulist(self, mode): + self.count += 1 + label_list = self.get_menulist(mode) + debug('set_menulist; ' + str(label_list)) + + button_num_list = range(1, self.right_label_count + 1) + debug('set_menulist button_num_list: ' + str(button_num_list)) + + for index in button_num_list: + button = getattr(self, "button_right" + str(index)) + self.setlabel(button, (label_list[index - 1])) + button.setEnabled(True) + + if self.right_buttons_count > self.right_label_count: + disable_button_num_list = range(self.right_label_count + 1, self.right_buttons_count + 1) + debug('set_menulist disable_button_num_list: ' + str(disable_button_num_list)) + for index in disable_button_num_list: + button = getattr(self, "button_right" + str(index)) + button.setLabel(' ') + button.setEnabled(False) + + def setlabel(self, button, label): + label = label.decode('utf-8') + + debug('setlabel: ' + label + ' ' + str(len(label))) + + if len(label) > 10: + spaces = label.count(' ') + debug('setlabel spaces=' + str(spaces)) + if spaces == 0: + words = [label[:10], label[10:]] + label = '%s-\r\n%s' % (words[0], words[1]) + elif spaces == 1: + words = label.split(' ') + label = '%s\r\n%s' % (words[0], words[1]) + elif spaces == 2: + words = label.split(' ') + if len(words[0]) <= len(words[2]): + words[0] = words[0] + ' ' + words[1] + words[1] = words[2] + else: + words[1] = words[1] + ' ' + words[2] + label = '%s\r\n%s' % (words[0], words[1]) + + button.setLabel(label) + + def right_press1(self): + self.right_press(1) + + def right_press2(self): + self.right_press(2) + + def right_press3(self): + self.right_press(3) + + def right_press4(self): + self.right_press(4) + + def right_press5(self): + self.right_press(5) + + def right_press6(self): + self.right_press(6) + + def right_press7(self): + self.right_press(7) + + def right_press(self, index): + item = self.listing.getSelectedItem() + params = json.loads(item.getfilename()) + log('right_press %d params %s' % (index, str(params))) + mode = params.get('mode') + filename = params.get('filename') + hash = params.get('hash') + ind = params.get('ind') + tdir = params.get('tdir') + action = None + + if mode in ['search_item', 'torrent_subfolder', 'externalsearch', 'externalsearch_item']: + if mode in ['externalsearch', 'externalsearch_item']: + index = index - 1 + + if index == 0: + url = params.get('back_url') + '&stringdata=' + urllib.quote_plus(params.get('stringdata')) + xbmc.executebuiltin('xbmc.RunPlugin("%s")' % (url)) + elif index == 1: + params = {'link': filename, 'tdir': tdir} + self.open_torrent(params) + elif index == 2: + action = 'downloadFilesList' + link = {'url': filename} + url = self.form_link(action, link) + xbmc.executebuiltin('xbmc.RunPlugin("%s")' % (url)) + elif index == 3: + action = 'downloadLibtorrent' + link = {'url': filename} + url = self.form_link(action, link) + xbmc.executebuiltin('xbmc.RunPlugin("%s")' % (url)) + elif index == 4: #search_item + cleanlabel = re.sub('\[[^\]]*\]', '', item.getLabel()) + ttl, yr = xbmc.getCleanMovieTitle(cleanlabel) + infoW = InfoWindow(ttl, yr) + infoW.doModal() + del infoW + elif mode in ['torrent_moveup', 'browser_moveup', 'moveup']: + self.navi_back() + elif mode == 'torrent_play': + if index == 1: + if filename and xbmcvfs.exists(filename): + params['url'] = ensure_str(filename) + url = self.form_link('playSTRM', params) + xbmc.executebuiltin('xbmc.RunPlugin("%s")' % (url)) + __settings__.setSetting('loadsw_onstop', 'true') + self.close() + elif index == 2: + action = 'downloadFilesList' + link = {'ind': str(params.get('url'))} + url = self.form_link(action, link) + xbmc.executebuiltin('xbmc.RunPlugin("%s")' % (url)) + elif index == 3: + action = 'downloadLibtorrent' + link = {'ind': str(params.get('url'))} + url = self.form_link(action, link) + xbmc.executebuiltin('xbmc.RunPlugin("%s")' % (url)) + elif mode == 'history_item': + addtime = params.get('addtime') + fav = params.get('fav') + query = params.get('query') + if index == 1: + self.input_search.setText(query) + self.search({'addtime': addtime}) + elif index == 2: + self.input_search.setText(query) + self.setFocus(self.input_search) + elif index == 3: + params['title'] = params.get('query') + self.controlCenter(params) + else: + if index == 4: action = 'fav' + elif index == 5: action = 'delete' + self.history_action(action, addtime, fav) + elif mode in ['browser_item', 'browser_subfolder']: + if index == 1: + self.browser(params) + elif index in [2, 3, 4] and mode =='browser_subfolder': + if index == 2: action = '3' + elif index == 3: action = '0' + elif index == 4: action = 'copy' + + self.browser_action(hash, action, tdir=tdir, ind=ind) + else: + if index == 2: action = 'start' + elif index == 3: action = 'stop' + elif index == 4: action = 'remove' + elif index == 5: action = '3' + elif index == 6: action = '0' + elif index == 7: action = 'removedata' + + if self.browser_action(hash, action): + self.navi_restore() + elif mode == 'browser_file': + if index == 1: action = 'play' + elif index == 2: action = '3' + elif index == 3: action = '0' + + self.browser_action(hash, action, tdir = tdir, ind = ind) + elif mode in ['downloadstatus', 'downloadstatus_subfolder', 'downloadstatus_file']: + if index == 1: action = 'play' + elif index == 2: action = 'start' + elif index == 3: action = 'pause' + elif index == 4: action = 'stop' + elif index == 5: action = 'delete' + elif index == 6: action = 'masscontrol' + self.downloadstatus_action(action, params.get('addtime'), params.get('path'), + params.get('type'), params.get('progress'), params.get('storage')) + elif mode in ['subfolder', 'file']: + self.file_browser(params) + elif mode == 'watched_item': + if index == 1: action = 'open' + elif index == 2: action = 'playnoseek' + elif index == 3: action = 'playwithseek' + elif index == 4: action = 'delete' + elif index == 5: action = 'clear' + self.watched_action(action, params.get('addtime')) + + def localize(self, string): + try: + return Localization.localize(string) + except: + return string + + def drawItem(self, title, params, image=None, isFolder=False): + if isinstance(params, str): + params = {'mode': params} + + if not image and isFolder: + image = 'DefaultFolder.png' + elif not image: + image = 'DefaultVideo.png' + listitem = xbmcgui.ListItem(title, '', image, image, json.dumps(params)) + self.listing.addItem(listitem) + + def form_link(self, action, link): + if isinstance(link, dict): + link_url = '' + for key in link.keys(): + if link.get(key) and key != 'mode': + link_url = '%s&%s=%s' % (link_url, key, urllib.quote_plus(ensure_str(link.get(key)))) + url = '%s?action=%s' % ('plugin://plugin.video.torrenter/', action) + link_url + else: + url = '%s?action=%s&url=%s' % ('plugin://plugin.video.torrenter/', action, urllib.quote_plus(link)) + + return url + + def controlcenter(self, params={}): + import controlcenter + controlcenter.main() + + def reconnect(self, event, callable): + self.disconnect(event) + self.connect(event, callable) + + def version_check(self): + return False if int(xbmc.getInfoLabel("System.BuildVersion")[:2]) < 17 else True + + def filter(self): + list = self.listing + self.listing.setPageControlVisible(True) + size = self.listing.size() + if size > 0: + for index in range(0, size): + listitem = self.listing.getListItem(index) + +class InfoWindow(pyxbmct.AddonDialogWindow): + def __init__(self, title="", year=""): + super(InfoWindow, self).__init__(title) + self.title = title + self.year = year + self.setGeometry(600, 600, 3, 3) + self.set_controls() + self.connect_controls() + # self.set_navigation() + + def set_controls(self): + # pyxbmct.AddonWindow().setImage(__root__ + '/resources/skins/Default/media/ConfluenceDialogBack.png') + # self.placeControl(self.background, 0, 0, rowspan=3, columnspan=2) + self.listing = pyxbmct.List(_imageWidth=30, _imageHeight=30, _itemTextXOffset=1, + _itemTextYOffset=0, _itemHeight=30, _space=0, _alignmentY=0) + self.placeControl(self.listing, 0, 1, 2, 2) + self.logoimg = pyxbmct.Image('', aspectRatio=0) + self.placeControl(self.logoimg, 0, 0, rowspan=2) + self.plot = pyxbmct.TextBox() + self.placeControl(self.plot, 2, 0, 1, columnspan=3) + self.plot.autoScroll(1000, 1000, 1000) + # self.button_search = pyxbmct.Button("Search") + # self.placeControl(self.button_search, 0, 5, 1, 2) + + def connect_controls(self): + from resources.scrapers.scrapers import Scrapers + self.Scraper = Scrapers() + meta = self.Scraper.scraper('tmdb', {'label': 'tmdb', 'search': self.title, 'year': ''}, 'en') + meta = meta.get('info') + + """ + meta results for xXx + {'info': {'count': 7451, 'plot': u'Xander Cage is your standard adrenaline junkie with no fear and a lousy attitude. When the US Government "recruits" him to go on a mission, he\'s not exactly thrilled. His mission: to gather information on an organization that may just be planning the destruction of the world, led by the nihilistic Yorgi.', 'votes': u'809', 'code': u'tt0295701', 'rating': 5.7000000000000002, 'title': u'xXx', 'tagline': u'A New Breed Of Secret Agent.', 'director': u'Rob Cohen', 'premiered': u'2002-08-09', 'originaltitle': u'xXx', 'cast': [u'Vin Diesel', u'Asia Argento', u'Marton Csokas', u'Samuel L. Jackson', u'Michael Roof', u'Petr J\xe1kl Jr.', u'Richy M\xfcller', u'Joe Bucaro III', u'Eve', u'Leila Arcieri', u'William Hope', u'Ted Maynard', u'Martin Hub'], 'castandrole': [u'Vin Diesel|Xander Cage', u'Asia Argento|Yelena', u'Marton Csokas|Yorgi', u'Samuel L. Jackson|Agent Gibbons', u'Michael Roof|Agent Toby Lee Shavers', u'Petr J\xe1kl Jr.|Kolya', u'Richy M\xfcller|Milan Sova', u'Joe Bucaro III|Virg', u'Eve|J.J.', u'Leila Arcieri|Jordan King', u'William Hope|Agent Roger Donnan', u'Ted Maynard|James Tannick', u'Martin Hub|Ivan Podrov'], 'studio': u'Columbia Pictures, Original Film, Revolution Studios', 'year': 2002, 'genre': u'Action', 'runtime': u'124'}, 'thumbnail': u'http://image.tmdb.org/t/p/original/fPHNTG1OXFBQ6aEVO7Lv8tSgfrY.jpg', 'label': 'tmdb', 'properties': {'fanart_image': u'http://image.tmdb.org/t/p/original/oNQIcuvJssiK93TjrXVtbERaKE1.jpg'}, 'icon': u'http://image.tmdb.org/t/p/original/fPHNTG1OXFBQ6aEVO7Lv8tSgfrY.jpg'} + """ + self.connect(pyxbmct.ACTION_NAV_BACK, self.close) + self.connect(pyxbmct.ACTION_PREVIOUS_MENU, self.close) + self.listing.addItem("Title: %s" % meta.get('title')) + self.listing.addItem("genre: %s" % meta.get('genre')) + self.listing.addItem("rating: %s" % meta.get('rating')) + self.listing.addItem("year: %s" % meta.get('year')) + self.listing.addItem("runtime: %sm" % meta.get('runtime')) + if meta.get('thumbnail'): + self.logoimg.setImage(meta.get('thumbnail')) + self.plot.setText(meta.get('plot')) + +def log(msg): + try: + xbmc.log("#SW# [%s]: %s" % (__plugin__, msg,), level=xbmc.LOGNOTICE) + except UnicodeEncodeError: + xbmc.log("#SW# [%s]: %s" % (__plugin__, msg.encode("utf-8", "ignore"),), level=xbmc.LOGNOTICE) + except: + xbmc.log("#SW# [%s]: %s" % (__plugin__, 'ERROR LOG',), level=xbmc.LOGNOTICE) + +def titleMake(seeds, leechers, size, title): + # AARRGGBB + clGreen = '[COLOR FF008000]%s[/COLOR]' + clDodgerblue = '[COLOR FF1E90FF]%s[/COLOR]' + clDimgray = '[COLOR FF999999]%s[/COLOR]' + clWhite = '[COLOR FFFFFFFF]%s[/COLOR]' + clAliceblue = '[COLOR FFF0F8FF]%s[/COLOR]' + clRed = '[COLOR FFFF0000]%s[/COLOR]' + + title = title.replace('720p', '[B]720p[/B]').replace('1080p', '[B]1080p[/B]') + title = clWhite % title + second = '[I](%s) [S/L: %d/%d] [/I]' % (size, seeds, leechers) + title += '\r\n' + clDimgray % second + return title + +def dlstat_titleMake(title, second): + # AARRGGBB + clDimgray = '[COLOR FF999999]%s[/COLOR]' + clWhite = '[COLOR FFFFFFFF]%s[/COLOR]' + title = clWhite % title + title += '\r\n' + clDimgray % second + return title + +def main(params = None): + dialog = SearchWindow(params) + dialog.doModal() + del dialog # You need to delete your instance when it is no longer needed + # because underlying xbmcgui classes are not grabage-collected. + +class swPlayer(xbmc.Player): + def play(self, item): + xbmc.Player().play(item = item) + i = 0 + while not self.isPlaying() and i < 100: + i += 1 + xbmc.sleep(500) + log('swPlayer not started '+str(i)) + + if i > 99: + return False + else: + while not xbmc.abortRequested and self.isPlaying(): + xbmc.sleep(500) + log('swPlayer playing') + + params = {'mode': 'load'} + main(params) + +if __name__ == '__main__': + try: + main() + except Exception, e: + import xbmc + import traceback + + map(xbmc.log, traceback.format_exc().split("\n")) + raise