diff --git a/core/autoplay.py b/core/autoplay.py index a35cb592..5ace4018 100644 --- a/core/autoplay.py +++ b/core/autoplay.py @@ -27,7 +27,7 @@ def start(itemlist, item): :return: try to auto-reproduce, in case of failure it returns the itemlist that it received in the beginning ''' - if item.global_search or item.from_action: # from_action means that's a special function calling this (ex: add to videolibrary) + if item.global_search or item.from_action or item.contentAction: # from_action means that's a special function calling this (ex: add to videolibrary) return itemlist logger.debug() diff --git a/lib/torrentool/__init__.py b/lib/torrentool/__init__.py new file mode 100644 index 00000000..f7f572ba --- /dev/null +++ b/lib/torrentool/__init__.py @@ -0,0 +1 @@ +VERSION = (1, 0, 2) \ No newline at end of file diff --git a/lib/torrentool/api.py b/lib/torrentool/api.py new file mode 100644 index 00000000..baac4b3b --- /dev/null +++ b/lib/torrentool/api.py @@ -0,0 +1,7 @@ +""" +Exposes commonly used classes and functions. + +""" +from .bencode import Bencode +from .torrent import Torrent +from .utils import upload_to_cache_server, get_open_trackers_from_local, get_open_trackers_from_remote diff --git a/lib/torrentool/bencode.py b/lib/torrentool/bencode.py new file mode 100644 index 00000000..6d431d13 --- /dev/null +++ b/lib/torrentool/bencode.py @@ -0,0 +1,204 @@ +from collections import OrderedDict +from operator import itemgetter +from codecs import encode +from sys import version_info + +from .exceptions import BencodeDecodingError, BencodeEncodingError + + +PY3 = version_info >= (3, 0) + +if PY3: + str_type = str + byte_types = (bytes, bytearray) + chr_ = chr + int_types = int +else: + str_type = basestring + byte_types = bytes + chr_ = lambda ch: ch + int_types = (int, long) + + +class Bencode(object): + """Exposes utilities for bencoding.""" + + @classmethod + def encode(cls, value): + """Encodes a value into bencoded bytes. + + :param value: Python object to be encoded (str, int, list, dict). + :param str val_encoding: Encoding used by strings in a given object. + :rtype: bytes + """ + val_encoding = 'utf-8' + + def encode_str(v): + try: + v_enc = encode(v, val_encoding) + + except UnicodeDecodeError: + if PY3: + raise + else: + # Suppose bytestring + v_enc = v + + prefix = encode('%s:' % len(v_enc), val_encoding) + return prefix + v_enc + + def encode_(val): + if isinstance(val, str_type): + result = encode_str(val) + + elif isinstance(val, int_types): + result = encode(('i%se' % val), val_encoding) + + elif isinstance(val, (list, set, tuple)): + result = encode('l', val_encoding) + for item in val: + result += encode_(item) + result += encode('e', val_encoding) + + elif isinstance(val, dict): + result = encode('d', val_encoding) + + # Dictionaries are expected to be sorted by key. + for k, v in OrderedDict(sorted(val.items(), key=itemgetter(0))).items(): + result += (encode_str(k) + encode_(v)) + + result += encode('e', val_encoding) + + elif isinstance(val, byte_types): + result = encode('%s:' % len(val), val_encoding) + result += val + + else: + raise BencodeEncodingError('Unable to encode `%s` %s' % (type(val), val)) + + return result + + return encode_(value) + + @classmethod + def decode(cls, encoded): + """Decodes bencoded data introduced as bytes. + + Returns decoded structure(s). + + :param bytes encoded: + """ + def create_dict(items): + # Let's guarantee that dictionaries are sorted. + k_v_pair = zip(*[iter(items)] * 2) + return OrderedDict(sorted(k_v_pair, key=itemgetter(0))) + + def create_list(items): + return list(items) + + stack_items = [] + stack_containers = [] + + def compress_stack(): + target_container = stack_containers.pop() + subitems = [] + + while True: + subitem = stack_items.pop() + subitems.append(subitem) + if subitem is target_container: + break + + container_creator = subitems.pop() + container = container_creator(reversed(subitems)) + stack_items.append(container) + + def parse_forward(till_char, sequence): + number = '' + char_sub_idx = 0 + + for char_sub_idx, char_sub in enumerate(sequence): + char_sub = chr_(char_sub) + if char_sub == till_char: + break + + number += char_sub + + number = int(number or 0) + char_sub_idx += 1 + + return number, char_sub_idx + + while encoded: + char = encoded[0] + char = chr_(char) + + if char == 'd': # Dictionary + stack_items.append(create_dict) + stack_containers.append(create_dict) + encoded = encoded[1:] + + elif char == 'l': # List + stack_items.append(create_list) + stack_containers.append(create_list) + encoded = encoded[1:] + + elif char == 'i': # Integer + number, char_sub_idx = parse_forward('e', encoded[1:]) + char_sub_idx += 1 + + stack_items.append(number) + encoded = encoded[char_sub_idx:] + + elif char.isdigit(): # String + str_len, char_sub_idx = parse_forward(':', encoded) + last_char_idx = char_sub_idx + str_len + + string = encoded[char_sub_idx:last_char_idx] + try: + string = string.decode('utf-8') + except UnicodeDecodeError: + # Considered bytestring (e.g. `pieces` hashes concatenation). + pass + + stack_items.append(string) + encoded = encoded[last_char_idx:] + + elif char == 'e': # End of a dictionary or a list. + compress_stack() + encoded = encoded[1:] + + else: + raise BencodeDecodingError('Unable to interpret `%s` char.' % char) + + if len(stack_items) == 1: + stack_items = stack_items.pop() + + return stack_items + + @classmethod + def read_string(cls, string): + """Decodes a given bencoded string or bytestring. + + Returns decoded structure(s). + + :param str string: + :rtype: list + """ + if PY3 and not isinstance(string, byte_types): + string = string.encode() + + return cls.decode(string) + + @classmethod + def read_file(cls, filepath): + """Decodes bencoded data of a given file. + + Returns decoded structure(s). + + :param str filepath: + :rtype: list + """ + with open(filepath, mode='rb') as f: + contents = f.read() + return cls.decode(contents) diff --git a/lib/torrentool/cli.py b/lib/torrentool/cli.py new file mode 100644 index 00000000..d07a8ee3 --- /dev/null +++ b/lib/torrentool/cli.py @@ -0,0 +1,94 @@ +from __future__ import division +import click +from os import path, getcwd + +from . import VERSION +from .api import Torrent +from .utils import humanize_filesize, upload_to_cache_server, get_open_trackers_from_remote, \ + get_open_trackers_from_local +from .exceptions import RemoteUploadError, RemoteDownloadError + + +@click.group() +@click.version_option(version='.'.join(map(str, VERSION))) +def start(): + """Torrentool command line utilities.""" + + +@start.group() +def torrent(): + """Torrent-related commands.""" + + +@torrent.command() +@click.argument('torrent_path', type=click.Path(exists=True, writable=False, dir_okay=False)) +def info(torrent_path): + """Print out information from .torrent file.""" + + my_torrent = Torrent.from_file(torrent_path) + + size = my_torrent.total_size + + click.secho('Name: %s' % my_torrent.name, fg='blue') + click.secho('Files:') + for file_tuple in my_torrent.files: + click.secho(file_tuple.name) + + click.secho('Hash: %s' % my_torrent.info_hash, fg='blue') + click.secho('Size: %s (%s)' % (humanize_filesize(size), size), fg='blue') + click.secho('Magnet: %s' % my_torrent.get_magnet(), fg='yellow') + + +@torrent.command() +@click.argument('source', type=click.Path(exists=True, writable=False)) +@click.option('--dest', default=getcwd, type=click.Path(file_okay=False), help='Destination path to put .torrent file into. Default: current directory.') +@click.option('--tracker', default=None, help='Tracker announce URL (multiple comma-separated values supported).') +@click.option('--open_trackers', default=False, is_flag=True, help='Add open trackers announce URLs.') +@click.option('--comment', default=None, help='Arbitrary comment.') +@click.option('--cache', default=False, is_flag=True, help='Upload file to torrent cache services.') +def create(source, dest, tracker, open_trackers, comment, cache): + """Create torrent file from a single file or a directory.""" + + source_title = path.basename(source).replace('.', '_').replace(' ', '_') + dest = '%s.torrent' % path.join(dest, source_title) + + click.secho('Creating torrent from %s ...' % source) + + my_torrent = Torrent.create_from(source) + + if comment: + my_torrent.comment = comment + + urls = [] + + if tracker: + urls = tracker.split(',') + + if open_trackers: + click.secho('Fetching an up-to-date open tracker list ...') + try: + urls.extend(get_open_trackers_from_remote()) + except RemoteDownloadError: + click.secho('Failed. Using built-in open tracker list.', fg='red', err=True) + urls.extend(get_open_trackers_from_local()) + + if urls: + my_torrent.announce_urls = urls + + my_torrent.to_file(dest) + + click.secho('Torrent file created: %s' % dest, fg='green') + click.secho('Torrent info hash: %s' % my_torrent.info_hash, fg='blue') + + if cache: + click.secho('Uploading to %s torrent cache service ...') + try: + result = upload_to_cache_server(dest) + click.secho('Cached torrent URL: %s' % result, fg='yellow') + + except RemoteUploadError as e: + click.secho('Failed: %s' % e, fg='red', err=True) + + +def main(): + start(obj={}) diff --git a/lib/torrentool/exceptions.py b/lib/torrentool/exceptions.py new file mode 100644 index 00000000..53118482 --- /dev/null +++ b/lib/torrentool/exceptions.py @@ -0,0 +1,27 @@ + +class TorrentoolException(Exception): + """Base torrentool exception. All others are inherited from it.""" + + +class BencodeError(TorrentoolException): + """Base exception for bencode related errors.""" + + +class BencodeDecodingError(BencodeError): + """Raised when torrentool is unable to decode bencoded data.""" + + +class BencodeEncodingError(BencodeError): + """Raised when torrentool is unable to encode data into bencode.""" + + +class TorrentError(TorrentoolException): + """Base exception for Torrent object related errors.""" + + +class RemoteUploadError(TorrentoolException): + """Base class for upload to remotes related issues.""" + + +class RemoteDownloadError(TorrentoolException): + """Base class for issues related to downloads from remotes.""" diff --git a/lib/torrentool/repo/open_trackers.ini b/lib/torrentool/repo/open_trackers.ini new file mode 100644 index 00000000..c0ca0aa0 --- /dev/null +++ b/lib/torrentool/repo/open_trackers.ini @@ -0,0 +1,8 @@ +udp://tracker.coppersurfer.tk:6969/announce +udp://tracker.internetwarriors.net:1337/announce +udp://tracker.leechers-paradise.org:6969/announce +udp://tracker.opentrackr.org:1337/announce +udp://tracker.openbittorrent.com:80/announce +udp://tracker.sktorrent.net:6969/announce +udp://tracker.zer0day.to:1337/announce +udp://exodus.desync.com:6969/announce diff --git a/lib/torrentool/torrent.py b/lib/torrentool/torrent.py new file mode 100644 index 00000000..8472c8cb --- /dev/null +++ b/lib/torrentool/torrent.py @@ -0,0 +1,436 @@ +from calendar import timegm +from collections import namedtuple +from datetime import datetime +from functools import reduce +from hashlib import sha1 +from os import walk, sep +from os.path import join, isdir, getsize, normpath, basename + +try: + from urllib.parse import urlencode +except ImportError: # Py2 + from urllib import urlencode + +from .bencode import Bencode +from .exceptions import TorrentError +from .utils import get_app_version + + +_ITERABLE_TYPES = (list, tuple, set) + + +TorrentFile = namedtuple('TorrentFile', ['name', 'length']) + + +class Torrent(object): + """Represents a torrent file, and exposes utilities to work with it.""" + + _filepath = None + + def __init__(self, dict_struct=None): + dict_struct = dict_struct or {'info': {}} + self._struct = dict_struct + + def __str__(self): + return 'Torrent: %s' % self.name + + announce_urls = property() + """List of lists of tracker announce URLs.""" + + comment = property() + """Optional. Free-form textual comments of the author.""" + + creation_date = property() + """Optional. The creation time of the torrent, in standard UNIX epoch format. UTC.""" + + created_by = property() + """Optional. Name and version of the program used to create the .torrent""" + + private = property() + """Optional. If True the client MUST publish its presence to get other peers + ONLY via the trackers explicitly described in the metainfo file. If False or is not present, + the client may obtain peer from other means, e.g. PEX peer exchange, dht. + + """ + + name = property() + """Torrent name (title).""" + + webseeds = property() + """A list of URLs where torrent data can be retrieved. + + See also: Torrent.httpseeds + + http://bittorrent.org/beps/bep_0019.html + """ + + httpseeds = property() + """A list of URLs where torrent data can be retrieved. + + See also and prefer Torrent.webseeds + + http://bittorrent.org/beps/bep_0017.html + """ + + def _list_getter(self, key): + return self._struct.get(key, []) + + def _list_setter(self, key, val): + if val is None: + try: + del self._struct[key] + return + except KeyError: + return + + if not isinstance(val, _ITERABLE_TYPES): + val = [val] + + self._struct[key] = val + + @webseeds.getter + def webseeds(self): + return self._list_getter('url-list') + + @webseeds.setter + def webseeds(self, val): + self._list_setter('url-list', val) + + @httpseeds.getter + def httpseeds(self): + return self._list_getter('httpseeds') + + @httpseeds.setter + def httpseeds(self, val): + self._list_setter('httpseeds', val) + + @property + def files(self): + """Files in torrent. + + List of namedtuples (filepath, size). + + :rtype: list[TorrentFile] + """ + files = [] + info = self._struct.get('info') + + if not info: + return files + + if 'files' in info: + base = info['name'] + + for f in info['files']: + files.append(TorrentFile(join(base, *f['path']), f['length'])) + + else: + files.append(TorrentFile(info['name'], info['length'])) + + return files + + @property + def total_size(self): + """Total size of all files in torrent.""" + return reduce(lambda prev, curr: prev + curr[1], self.files, 0) + + @property + def info_hash(self): + """Hash of torrent file info section. Also known as torrent hash.""" + info = self._struct.get('info') + + if not info: + return None + + return sha1(Bencode.encode(info)).hexdigest() + + @property + def magnet_link(self): + """Magnet link using BTIH (BitTorrent Info Hash) URN.""" + return self.get_magnet(detailed=False) + + @announce_urls.getter + def announce_urls(self): + """List of lists of announce (tracker) URLs. + + First inner list is considered as primary announcers list, + the following lists as back-ups. + + http://bittorrent.org/beps/bep_0012.html + + """ + urls = self._struct.get('announce-list') + + if not urls: + urls = self._struct.get('announce') + if not urls: + return [] + urls = [[urls]] + + return urls + + @announce_urls.setter + def announce_urls(self, val): + self._struct['announce'] = '' + self._struct['announce-list'] = [] + + def set_single(val): + del self._struct['announce-list'] + self._struct['announce'] = val + + if isinstance(val, _ITERABLE_TYPES): + length = len(val) + + if length: + if length == 1: + set_single(val[0]) + else: + for item in val: + if not isinstance(item, _ITERABLE_TYPES): + item = [item] + self._struct['announce-list'].append(item) + self._struct['announce'] = val[0] + + else: + set_single(val) + + @comment.getter + def comment(self): + return self._struct.get('comment') + + @comment.setter + def comment(self, val): + self._struct['comment'] = val + + @creation_date.getter + def creation_date(self): + date = self._struct.get('creation date') + if date is not None: + date = datetime.utcfromtimestamp(int(date)) + return date + + @creation_date.setter + def creation_date(self, val): + self._struct['creation date'] = timegm(val.timetuple()) + + @created_by.getter + def created_by(self): + return self._struct.get('created by') + + @created_by.setter + def created_by(self, val): + self._struct['created by'] = val + + @private.getter + def private(self): + return self._struct.get('info', {}).get('private', False) + + @private.setter + def private(self, val): + if not val: + try: + del self._struct['info']['private'] + except KeyError: + pass + else: + self._struct['info']['private'] = 1 + + @name.getter + def name(self): + return self._struct.get('info', {}).get('name', None) + + @name.setter + def name(self, val): + self._struct['info']['name'] = val + + def get_magnet(self, detailed=True): + """Returns torrent magnet link, consisting of BTIH (BitTorrent Info Hash) URN + anr optional other information. + + :param bool|list|tuple|set detailed: + For boolean - whether additional info (such as trackers) should be included. + For iterable - expected allowed parameter names: + tr - trackers + ws - webseeds + + """ + result = 'magnet:?xt=urn:btih:' + self.info_hash + + def add_tr(): + urls = self.announce_urls + if not urls: + return + + trackers = [] + + urls = urls[0] # Only primary announcers are enough. + for url in urls: + trackers.append(('tr', url)) + + if trackers: + return urlencode(trackers) + + def add_ws(): + webseeds = [('ws', url) for url in self.webseeds] + if webseeds: + return urlencode(webseeds) + + params_map = { + 'tr': add_tr, + 'ws': add_ws, + } + + if detailed: + details = [] + + if isinstance(detailed, _ITERABLE_TYPES): + requested_params = detailed + else: + requested_params = params_map.keys() + + for param in requested_params: + param_val = params_map[param]() + param_val and details.append(param_val) + + if details: + result += '&%s' % '&'.join(details) + + return result + + def to_file(self, filepath=None): + """Writes Torrent object into file, either + + :param filepath: + """ + if filepath is None and self._filepath is None: + raise TorrentError('Unable to save torrent to file: no filepath supplied.') + + if filepath is not None: + self._filepath = filepath + + with open(self._filepath, mode='wb') as f: + f.write(self.to_string()) + + def to_string(self): + """Returns bytes representing torrent file. + + :param str encoding: Encoding used by strings in Torrent object. + :rtype: bytearray + """ + return Bencode.encode(self._struct) + + @classmethod + def _get_target_files_info(cls, src_path): + src_path = u'%s' % src_path # Force walk() to return unicode names. + + is_dir = isdir(src_path) + target_files = [] + + if is_dir: + for base, _, files in walk(src_path): + target_files.extend([join(base, fname) for fname in sorted(files)]) + + else: + target_files.append(src_path) + + target_files_ = [] + total_size = 0 + for fpath in target_files: + file_size = getsize(fpath) + if not file_size: + continue + target_files_.append((fpath, file_size, normpath(fpath.replace(src_path, '')).strip(sep).split(sep))) + total_size += file_size + + return target_files_, total_size + + @classmethod + def create_from(cls, src_path): + """Returns Torrent object created from a file or a directory. + + :param str src_path: + :rtype: Torrent + """ + is_dir = isdir(src_path) + target_files, size_data = cls._get_target_files_info(src_path) + + SIZE_MIN = 32768 # 32 KiB + SIZE_DEFAULT = 262144 # 256 KiB + SIZE_MAX = 1048576 # 1 MiB + + CHUNKS_MIN = 1000 # todo use those limits as advised + CHUNKS_MAX = 2200 + + size_piece = SIZE_MIN + if size_data > SIZE_MIN: + size_piece = SIZE_DEFAULT + + if size_piece > SIZE_MAX: + size_piece = SIZE_MAX + + def read(filepath): + with open(filepath, 'rb') as f: + while True: + chunk = f.read(size_piece - len(pieces_buffer)) + chunk_size = len(chunk) + if chunk_size == 0: + break + yield chunk + + pieces = bytearray() + pieces_buffer = bytearray() + + for fpath, _, _ in target_files: + for chunk in read(fpath): + pieces_buffer += chunk + + if len(pieces_buffer) == size_piece: + pieces += sha1(pieces_buffer).digest()[:20] + pieces_buffer = bytearray() + + if len(pieces_buffer): + pieces += sha1(pieces_buffer).digest()[:20] + pieces_buffer = bytearray() + + info = { + 'name': basename(src_path), + 'pieces': bytes(pieces), + 'piece length': size_piece, + } + + if is_dir: + files = [] + + for _, length, path in target_files: + files.append({'length': length, 'path': path}) + + info['files'] = files + + else: + info['length'] = target_files[0][1] + + torrent = cls({'info': info}) + torrent.created_by = get_app_version() + torrent.creation_date = datetime.utcnow() + + return torrent + + @classmethod + def from_string(cls, string): + """Alternative constructor to get Torrent object from string. + + :param str string: + :rtype: Torrent + """ + return cls(Bencode.read_string(string)) + + @classmethod + def from_file(cls, filepath): + """Alternative constructor to get Torrent object from file. + + :param str filepath: + :rtype: Torrent + """ + torrent = cls(Bencode.read_file(filepath)) + torrent._filepath = filepath + return torrent diff --git a/lib/torrentool/utils.py b/lib/torrentool/utils.py new file mode 100644 index 00000000..7d346cd7 --- /dev/null +++ b/lib/torrentool/utils.py @@ -0,0 +1,91 @@ +import math +from os import path + +from .exceptions import RemoteUploadError, RemoteDownloadError + + +OPEN_TRACKERS_FILENAME = 'open_trackers.ini' +REMOTE_TIMEOUT = 4 + + +def get_app_version(): + """Returns full version string including application name + suitable for putting into Torrent.created_by. + + """ + from torrentool import VERSION + return 'torrentool/%s' % '.'.join(map(str, VERSION)) + + +def humanize_filesize(bytes_size): + """Returns human readable filesize. + + :param int bytes_size: + :rtype: str + """ + if not bytes_size: + return '0 B' + + names = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB') + + name_idx = int(math.floor(math.log(bytes_size, 1024))) + size = round(bytes_size / math.pow(1024, name_idx), 2) + + return '%s %s' % (size, names[name_idx]) + + +def upload_to_cache_server(fpath): + """Uploads .torrent file to a cache server. + + Returns upload file URL. + + :rtype: str + """ + url_base = 'http://torrage.info' + url_upload = '%s/autoupload.php' % url_base + url_download = '%s/torrent.php?h=' % url_base + file_field = 'torrent' + + try: + import requests + + response = requests.post(url_upload, files={file_field: open(fpath, 'rb')}, timeout=REMOTE_TIMEOUT) + response.raise_for_status() + + info_cache = response.text + return url_download + info_cache + + except (ImportError, requests.RequestException) as e: + + # Now trace is lost. `raise from` to consider. + raise RemoteUploadError('Unable to upload to %s: %s' % (url_upload, e)) + + +def get_open_trackers_from_remote(): + """Returns open trackers announce URLs list from remote repo.""" + + url_base = 'https://raw.githubusercontent.com/idlesign/torrentool/master/torrentool/repo' + url = '%s/%s' % (url_base, OPEN_TRACKERS_FILENAME) + + try: + import requests + + response = requests.get(url, timeout=REMOTE_TIMEOUT) + response.raise_for_status() + + open_trackers = response.text.splitlines() + + except (ImportError, requests.RequestException) as e: + + # Now trace is lost. `raise from` to consider. + raise RemoteDownloadError('Unable to download from %s: %s' % (url, e)) + + return open_trackers + + +def get_open_trackers_from_local(): + """Returns open trackers announce URLs list from local backup.""" + with open(path.join(path.dirname(__file__), 'repo', OPEN_TRACKERS_FILENAME)) as f: + open_trackers = map(str.strip, f.readlines()) + + return list(open_trackers) diff --git a/platformcode/launcher.py b/platformcode/launcher.py index 6d1c2022..1307c672 100644 --- a/platformcode/launcher.py +++ b/platformcode/launcher.py @@ -261,7 +261,6 @@ def run(item=None): # Special action for searching, first asks for the words then call the "search" function elif item.action == "search": - # from core.support import dbg;dbg() if filetools.isfile(temp_search_file) and config.get_setting('videolibrary_kodi'): itemlist = [] f = filetools.read(temp_search_file) @@ -486,7 +485,7 @@ def play_from_library(item): # Modify the action (currently the video library needs "findvideos" since this is where the sources are searched item.action = item.next_action if item.next_action else "findvideos" - # from core.support import dbg;dbg() + if item.contentType == 'movie' or item.contentType != 'movie' and config.get_setting('next_ep') < 3: window_type = config.get_setting("window_type", "videolibrary") else: window_type = 1 diff --git a/platformcode/platformtools.py b/platformcode/platformtools.py index 802b8af2..5fdb150e 100644 --- a/platformcode/platformtools.py +++ b/platformcode/platformtools.py @@ -400,7 +400,7 @@ def viewmodeMonitor(): currentMode = int(win.getFocusId()) # logger.debug('CM', currentMode, 'CN',currentModeName, 'label',xbmc.getInfoLabel('Container.FolderPath')) # if not parent_info: - # from core.support import dbg;dbg() + if currentModeName and 'plugin.video.kod' in parent_info and 50 <= currentMode < 1000:# and currentMode >= 50: # inside addon and in itemlist view # logger.debug('CAMBIO VISUALE') content, Type = getCurrentView(Item().fromurl(item_info) if item_info else Item(), Item().fromurl(parent_info)) @@ -424,7 +424,7 @@ def getCurrentView(item=None, parent_item=None): if not parent_item: logger.debug('ESCO') return None, None - + # if not parent_item: # info = xbmc.getInfoLabel('Container.FolderPath') # if not info: @@ -1001,7 +1001,6 @@ def play_video(item, strm=False, force_direct=False, autoplay=False): set_infolabels(xlistitem, item, True) # if it is a video in mpd format, the listitem is configured to play it ith the inpustreamaddon addon implemented in Kodi 17 - # from core.support import dbg;dbg() if mpd or item.manifest =='mpd': if not install_inputstream(): return @@ -1505,8 +1504,6 @@ def play_torrent(item, xlistitem, mediaurl): if torr_client in ['elementum'] and item.downloadFilename: torrent.elementum_download(item) else: - # time.sleep(3) - # from core.support import dbg;dbg() if item.fromLibrary and item.play_from == 'window': xlistitem.setPath(torrent_options[selection][1] % mediaurl) playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) diff --git a/servers/torrent.py b/servers/torrent.py index 6f854287..51fb8603 100755 --- a/servers/torrent.py +++ b/servers/torrent.py @@ -2,10 +2,11 @@ import re, os, sys, time, requests, xbmc, xbmcaddon -from core import filetools, jsontools +from core import filetools, httptools, jsontools from core.support import info, match from platformcode import config, platformtools from lib.guessit import guessit +from torrentool.api import Torrent if sys.version_info[0] >= 3: import urllib.parse as urllib @@ -86,7 +87,8 @@ def elementum_download(item): else: TorrentName = match(item.url, patron=r'btih(?::|%3A)([^&%]+)', string=True).match post = 'uri=%s&file=null&all=1' % urllib.quote_plus(item.url) - match(elementum_host + 'add', post=post, timeout=5, alfa_s=True, ignore_response_code=True) + res = httptools.downloadpage(elementum_host + 'add', post=post, timeout=5, alfa_s=True, ignore_response_code=True) + # match(elementum_host + 'add', post=post, timeout=5, alfa_s=True, ignore_response_code=True) while not filetools.isfile(filetools.join(elementum_setting.getSetting('torrents_path'), TorrentName + '.torrent')): time.sleep(1) diff --git a/specials/downloads.py b/specials/downloads.py index 6ff6b739..7f28714d 100644 --- a/specials/downloads.py +++ b/specials/downloads.py @@ -1065,8 +1065,9 @@ def save_download_movie(item): if not platformtools.dialog_yesno(config.get_localized_string(30101), config.get_localized_string(70189)): platformtools.dialog_ok(config.get_localized_string(30101), item.contentTitle + '\n' + config.get_localized_string(30109)) else: + # from core.support import dbg;dbg() play_item = select_server(item) - if play_item == 'Auto': + if type(play_item) == str and play_item == 'Auto': start_download(item) else: play_item = item.clone(**play_item.__dict__) @@ -1110,7 +1111,7 @@ def save_download_tvshow(item): if len(episodes) == 1: play_item = select_server(episodes[0]) if play_item: # not pressed cancel - if play_item == 'Auto': + if type(play_item) == str and play_item == 'Auto': start_download(episodes[0]) else: play_item = episodes[0].clone(**play_item.__dict__)