diff --git a/core/jsontools.py b/core/jsontools.py
index 94d3c5b6..57ea27a8 100644
--- a/core/jsontools.py
+++ b/core/jsontools.py
@@ -44,6 +44,7 @@ def load(*args, **kwargs):
except:
logger.error("**NOT** able to load the JSON")
logger.error(traceback.format_exc())
+ logger.error('ERROR STACK ' + str(stack()[1][3]))
value = {}
return value
@@ -184,7 +185,7 @@ def update_node(dict_node, name_file, node, path=None):
# es un dict
if dict_data:
if node in dict_data:
- logger.debug(" the key exists %s" % node)
+ # logger.debug(" the key exists %s" % node)
dict_data[node] = dict_node
else:
logger.debug(" The key does NOT exist %s" % node)
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/platformtools.py b/platformcode/platformtools.py
index b9ce7472..265d2473 100644
--- a/platformcode/platformtools.py
+++ b/platformcode/platformtools.py
@@ -11,32 +11,22 @@
from __future__ import division
from __future__ import absolute_import
from past.utils import old_div
-#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
if PY3:
- #from future import standard_library
- #standard_library.install_aliases()
- import urllib.parse as urllib # Es muy lento en PY2. En PY3 es nativo
+ import urllib.parse as urllib
else:
- import urllib # Usamos el nativo de PY2 que es más rápido
+ import urllib
-import os
-
-import xbmc
-import xbmcgui
-import xbmcplugin
-import xbmcaddon
+import os, xbmc, xbmcgui, xbmcplugin, xbmcaddon
from channelselector import get_thumb
from core import channeltools
from core import trakt_tools, scrapertools
from core.item import Item
-from platformcode import logger
-from platformcode import config
-from platformcode import unify
+from platformcode import logger, config, unify
addon = xbmcaddon.Addon('plugin.video.kod')
addon_icon = os.path.join( addon.getAddonInfo( "path" ), "logo.png" )
@@ -808,7 +798,7 @@ def set_context_commands(item, item_url, parent_item, **kwargs):
elif item.contentSerieName:
# Descargar serie
- if item.contentType == "tvshow":
+ if item.contentType == "tvshow" and item.action not in ['findvideos']:
if item.channel == 'videolibrary':
context_commands.append((config.get_localized_string(60003), "XBMC.RunPlugin(%s?%s&%s)" %
(sys.argv[0], item_url,
@@ -818,7 +808,7 @@ def set_context_commands(item, item_url, parent_item, **kwargs):
context_commands.append((config.get_localized_string(60357), "XBMC.RunPlugin(%s?%s&%s)" %
(sys.argv[0], item_url, 'channel=downloads&action=save_download&download=season&from_channel=' + item.channel + '&from_action=' + item.action)))
# Descargar episodio
- elif item.contentType == "episode":
+ elif item.contentType == "episode" or item.action in ['findvideos']:
context_commands.append((config.get_localized_string(60356), "XBMC.RunPlugin(%s?%s&%s)" %
(sys.argv[0], item_url, 'channel=downloads&action=save_download&from_channel=' + item.channel + '&from_action=' + item.action)))
# Descargar temporada
@@ -1353,140 +1343,23 @@ def torrent_client_installed(show_tuple=False):
def play_torrent(item, xlistitem, mediaurl):
logger.info()
import time
- import traceback
-
- from core import filetools
- from core import httptools
- from lib import generictools
from servers import torrent
-
- # Si Libtorrent ha dado error de inicialización, no se pueden usar los clientes internos
- UNRAR = config.get_setting("unrar_path", server="torrent", default="")
- LIBTORRENT = config.get_setting("libtorrent_path", server="torrent", default='')
- size_rar = 2
- rar_files = []
- if item.password:
- size_rar = 3
-
-
- # Opciones disponibles para Reproducir torrents
torrent_options = torrent_client_installed(show_tuple=True)
- torrent_client = config.get_setting("torrent_client", server="torrent")
-
- # Si es Libtorrent y no está soportado, se ofrecen alternativas, si las hay...
if len(torrent_options) > 1:
selection = dialog_select(config.get_localized_string(70193), [opcion[0] for opcion in torrent_options])
else:
selection = 0
- # Si es Torrenter o Elementum con opción de Memoria, se ofrece la posibilidad ee usar Libtorrent temporalemente
- if selection >= 0 and LIBTORRENT and UNRAR and 'RAR-' in item.torrent_info and (
- "torrenter" in torrent_options[selection][0] \
- or ("elementum" in torrent_options[selection][0] and xbmcaddon.Addon(id="plugin.video.%s" % torrent_options[selection][0].replace('Plugin externo: ','')).getSetting('download_storage') == '1')):
- if dialog_yesno(torrent_options[selection][0], config.get_localized_string(70777), config.get_localized_string(70778), config.get_localized_string(70779) % size_rar):
- selection = 1
- else:
- return
- # Si es Elementum pero con opción de Memoria, se muestras los Ajustes de Elementum y se pide al usuario que cambie a "Usar Archivos"
- elif selection >= 0 and not LIBTORRENT and UNRAR and 'RAR-' in item.torrent_info and "elementum" in \
- torrent_options[selection][0] \
- and xbmcaddon.Addon(id="plugin.video.%s" % torrent_options[selection][0].capitalize()) \
- .getSetting('download_storage') == '1':
- if dialog_yesno(torrent_options[selection][0], config.get_localized_string(70780) % size_rar, config.get_localized_string(70781)):
- __settings__ = xbmcaddon.Addon(
- id="plugin.video.%s" % torrent_options[selection][0].capitalize())
- __settings__.openSettings() # Se visulizan los Ajustes de Elementum
- elementum_dl = xbmcaddon.Addon(
- id="plugin.video.%s" % torrent_options[selection][0].capitalize()) \
- .getSetting('download_storage')
- if elementum_dl != '1':
- config.set_setting("elementum_dl", "1", server="torrent") # Salvamos el cambio para restaurarlo luego
- return # Se sale, porque habrá refresco y cancelaría Kodi si no
-
- # Descarga de torrents a local
if selection >= 0:
- #### Compatibilidad con Kodi 18: evita cuelgues/cancelaciones cuando el .torrent se lanza desde pantalla convencional
- # if xbmc.getCondVisibility('Window.IsMedia'):
- xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, xlistitem) # Preparamos el entorno para evitar error Kod1 18
- time.sleep(0.5) # Dejamos tiempo para que se ejecute
-
- # Nuevo método de descarga previa del .torrent. Si da error, miramos si hay alternatica local.
- # Si ya es local, lo usamos
- url = ''
- url_stat = False
- torrents_path = ''
- referer = None
- post = None
- rar = False
- size = ''
- password = ''
- if item.password:
- password = item.password
-
- videolibrary_path = config.get_videolibrary_path() # Calculamos el path absoluto a partir de la Videoteca
- if scrapertools.find_single_match(videolibrary_path, '(^\w+:\/\/)'): # Si es una conexión REMOTA, usamos userdata local
- videolibrary_path = config.get_data_path() # Calculamos el path absoluto a partir de Userdata
- if not filetools.exists(videolibrary_path): # Si no existe el path, pasamos al modo clásico
- videolibrary_path = False
- else:
- torrents_path = filetools.join(videolibrary_path, 'temp_torrents', 'client_torrent.torrent') # path descarga temporal
- if not videolibrary_path or not filetools.exists(filetools.join(videolibrary_path, 'temp_torrents')): # Si no existe la carpeta temporal, la creamos
- filetools.mkdir(filetools.join(videolibrary_path, 'temp_torrents'))
-
- # Si hay headers, se pasar a la petición de descarga del .torrent
- headers = {}
- if item.headers:
- headers = item.headers
-
- # identificamos si es una url o un path de archivo
- if not item.url.startswith("\\") and not item.url.startswith("/") and not url_stat:
- timeout = 10
- if item.torrent_alt:
- timeout = 5
- # Si es una llamada con POST, lo preparamos
- if item.referer: referer = item.referer
- if item.post: post = item.post
- # Descargamos el .torrent
- size, url, torrent_f, rar_files = generictools.get_torrent_size(item.url, referer, post, torrents_path=torrents_path, timeout=timeout, lookup=False, headers=headers, short_pad=True)
- if url:
- url_stat = True
- item.url = url
- if "torrentin" in torrent_options[selection][0]:
- item.url = 'file://' + item.url
-
- if not url and item.torrent_alt: # Si hay error, se busca un .torrent alternativo
- if (item.torrent_alt.startswith("\\") or item.torrent_alt.startswith("/")) and videolibrary_path:
- item.url = item.torrent_alt # El .torrent alternativo puede estar en una url o en local
- elif not item.url.startswith("\\") and not item.url.startswith("/"):
- item.url = item.torrent_alt
-
- # Si es un archivo .torrent local, actualizamos el path relativo a path absoluto
- if (item.url.startswith("\\") or item.url.startswith("/")) and not url_stat and videolibrary_path: # .torrent alternativo local
- movies = config.get_setting("folder_movies")
- series = config.get_setting("folder_tvshows")
- if item.contentType == 'movie':
- folder = movies # películas
- else:
- folder = series # o series
- item.url = filetools.join(config.get_videolibrary_path(), folder, item.url) # dirección del .torrent local en la Videoteca
- if filetools.copy(item.url, torrents_path, silent=True): # se copia a la carpeta generíca para evitar problemas de encode
- item.url = torrents_path
- if "torrentin" in torrent_options[selection][0]: # Si es Torrentin, hay que añadir un prefijo
- item.url = 'file://' + item.url
- size, rar_files = generictools.get_torrent_size('', file_list=True, local_torr=torrents_path,short_pad=True)
-
- mediaurl = item.url
-
- if selection >= 0:
+ xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, xlistitem)
+ time.sleep(1)
mediaurl = urllib.quote_plus(item.url)
torr_client = torrent_options[selection][0]
- torr_setting = xbmcaddon.Addon(id="plugin.video.%s" %torr_client)
- # Llamada con más parámetros para completar el título
if torr_client in ['quasar', 'elementum'] and item.infoLabels['tmdb_id']:
if item.contentType == 'episode' and "elementum" not in torr_client:
mediaurl += "&episode=%s&library=&season=%s&show=%s&tmdb=%s&type=episode" % (item.infoLabels['episode'], item.infoLabels['season'], item.infoLabels['tmdb_id'], item.infoLabels['tmdb_id'])
@@ -1494,57 +1367,12 @@ def play_torrent(item, xlistitem, mediaurl):
mediaurl += "&library=&tmdb=%s&type=movie" % (item.infoLabels['tmdb_id'])
if torr_client in ['quasar', 'elementum'] and item.downloadFilename:
- torrent.elementum_monitor(item, torr_client)
+ torrent.elementum_download(item)
else:
xbmc.executebuiltin("PlayMedia(" + torrent_options[selection][1] % mediaurl + ")")
- # Si es un archivo RAR, monitorizamos el cliente Torrent hasta que haya descargado el archivo,
- # y después lo extraemos, incluso con RAR's anidados y con contraseña
- if 'RAR-' in size and torr_client in ['quasar', 'elementum'] and UNRAR:
- rar_file, save_path_videos, folder_torr = torrent.wait_for_download(item, mediaurl, rar_files, torr_client) # Esperamos mientras se descarga el RAR
- if rar_file and save_path_videos: # Si se ha descargado el RAR...
- dp = dialog_progress_bg('KoD %s' % torr_client)
- video_file, rar, video_path, erase_file_path = torrent.extract_files(rar_file, save_path_videos, password, dp, item, torr_client) # ... extraemos el vídeo del RAR
- dp.close()
-
- # Reproducimos el vídeo extraido, si no hay nada en reproducción
- while is_playing() and rar and not xbmc.abortRequested:
- time.sleep(3) # Repetimos cada intervalo
- if rar and not xbmc.abortRequested:
- time.sleep(1)
- video_play = filetools.join(video_path, video_file)
- log("##### video_play: %s" % video_play)
- playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
- playlist.clear()
- playlist.add(video_play, xlistitem)
- xbmc_player.play(playlist)
-
- # selectionamos que clientes torrent soportamos para el marcado de vídeos vistos: asumimos que todos funcionan
torrent.mark_auto_as_watched(item)
- # Si se ha extraido un RAR, se pregunta para borrar los archivos después de reproducir el vídeo (plugins externos)
- while is_playing() and rar and not xbmc.abortRequested:
- time.sleep(3) # Repetimos cada intervalo
- if rar and not xbmc.abortRequested:
- if dialog_yesno('KoD %s' % torr_client, config.get_localized_string(30031)):
- log("##### erase_file_path: %s" % erase_file_path)
- try:
- torr_data, deamon_url, index = torrent.get_tclient_data(folder_torr, torr_client)
- if torr_data and deamon_url:
- data = httptools.downloadpage('%sdelete/%s' % (deamon_url, index), timeout=5, alfa_s=True).data
- time.sleep(1)
- if filetools.isdir(erase_file_path):
- filetools.rmdirtree(erase_file_path)
- elif filetools.exists(erase_file_path) and filetools.isfile(erase_file_path):
- filetools.remove(erase_file_path)
- except:
- logger.error(traceback.format_exc(1))
- elementum_dl = config.get_setting("elementum_dl", server="torrent", default='') # Si salvamos el cambio de Elementum
- if elementum_dl:
- config.set_setting("elementum_dl", "", server="torrent") # lo reseteamos en Alfa
- xbmcaddon.Addon(id="plugin.video.%s" % torrent_options[selection][0].replace('Plugin externo: ', '')) \
- .setSetting('download_storage', elementum_dl) # y lo reseteamos en Elementum
-
def log(texto):
xbmc.log(texto, xbmc.LOGNOTICE)
\ No newline at end of file
diff --git a/resources/settings.xml b/resources/settings.xml
index df334b5a..e41734f2 100644
--- a/resources/settings.xml
+++ b/resources/settings.xml
@@ -106,9 +106,7 @@
-
-
diff --git a/servers/torrent.py b/servers/torrent.py
index 8c26a9fd..37fb07f2 100755
--- a/servers/torrent.py
+++ b/servers/torrent.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-import sys
+import os, sys
# from builtins import str
from builtins import range
@@ -10,67 +10,36 @@ VFS = True
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int; VFS = False
if PY3:
- #from future import standard_library
- #standard_library.install_aliases()
- import urllib.parse as urllib # Es muy lento en PY2. En PY3 es nativo
+ import urllib.parse as urllib
else:
import urllib
-import time
-import os
-import traceback
-import re
+import time, requests, xbmc, xbmcgui, xbmcaddon
+from core import filetools, jsontools
+from core.support import dbg, log, match
+# from core import httptools
+# from core import scrapertools
+from platformcode import config, platformtools
+from threading import Thread, currentThread
+from torrentool.api import Torrent
-try:
- import xbmc
- import xbmcgui
- import xbmcaddon
-except:
- pass
-
-from core import filetools
-from core import httptools
-from core import scrapertools
-from core import jsontools
-from platformcode import logger
-from platformcode import config
-from platformcode import platformtools
-
-trackers = [
- "udp://tracker.openbittorrent.com:80/announce",
- "http://tracker.torrentbay.to:6969/announce",
- "http://tracker.pow7.com/announce",
- "udp://tracker.ccc.de:80/announce",
- "udp://open.demonii.com:1337",
-
- "http://9.rarbg.com:2710/announce",
- "http://bt.careland.com.cn:6969/announce",
- "http://explodie.org:6969/announce",
- "http://mgtracker.org:2710/announce",
- "http://tracker.best-torrents.net:6969/announce",
- "http://tracker.tfile.me/announce",
- "http://tracker1.wasabii.com.tw:6969/announce",
- "udp://9.rarbg.com:2710/announce",
- "udp://9.rarbg.me:2710/announce",
- "udp://coppersurfer.tk:6969/announce",
-
- "http://www.spanishtracker.com:2710/announce",
- "http://www.todotorrents.com:2710/announce",
- ]
+elementum_setting = xbmcaddon.Addon(id='plugin.video.elementum')
+elementum_host = 'http://127.0.0.1:' + elementum_setting.getSetting('remote_port') + '/torrents/'
+extensions_list = ['aaf', '3gp', 'asf', 'avi', 'flv', 'mpeg', 'm1v', 'm2v', 'm4v', 'mkv', 'mov', 'mpg', 'mpe', 'mp4', 'ogg', 'wmv']
# Returns an array of possible video url's from the page_url
-def get_video_url(page_url, premium=False, user="", password="", video_password=""):
+def get_video_url(page_url, premium=False, user='', password='', video_password=''):
torrent_options = platformtools.torrent_client_installed(show_tuple=True)
if len(torrent_options) == 0:
from specials import elementum_download
elementum_download.download()
- logger.info("server=torrent, the url is the good")
+ log('server=torrent, the url is the good')
- if page_url.startswith("magnet:"):
- video_urls = [["magnet: [torrent]", page_url]]
+ if page_url.startswith('magnet:'):
+ video_urls = [['magnet: [torrent]', page_url]]
else:
- video_urls = [[".torrent [torrent]", page_url]]
+ video_urls = [['.torrent [torrent]', page_url]]
return video_urls
@@ -82,1366 +51,160 @@ class XBMCPlayer(xbmc.Player):
xbmc_player = XBMCPlayer()
-
-def caching_torrents(url, referer=None, post=None, torrents_path=None, timeout=10, \
- lookup=False, data_torrent=False, headers={}, proxy_retries=1):
- if torrents_path != None:
- logger.info("path = " + torrents_path)
- else:
- logger.info()
- if referer and post:
- logger.info('REFERER: ' + referer)
-
- torrent_file = ''
- t_hash = ''
- if referer:
- headers.update({'Content-Type': 'application/x-www-form-urlencoded', 'Referer': referer}) #Necesario para el Post del .Torrent
-
- """
- Descarga en el path recibido el .torrent de la url recibida, y pasa el decode
- Devuelve el path real del .torrent, o el path vacío si la operación no ha tenido éxito
- """
-
- videolibrary_path = config.get_videolibrary_path() #Calculamos el path absoluto a partir de la Videoteca
- if torrents_path == None:
- if not videolibrary_path:
- torrents_path = ''
- if data_torrent:
- return (torrents_path, torrent_file)
- return torrents_path #Si hay un error, devolvemos el "path" vacío
- torrents_path = filetools.join(videolibrary_path, 'temp_torrents_Alfa', 'cliente_torrent_Alfa.torrent') #path de descarga temporal
- if '.torrent' not in torrents_path:
- torrents_path += '.torrent' #path para dejar el .torrent
- #torrents_path_encode = filetools.encode(torrents_path) #encode utf-8 del path
- torrents_path_encode = torrents_path
-
- #if url.endswith(".rar") or url.startswith("magnet:"): #No es un archivo .torrent
- if url.endswith(".rar"): #No es un archivo .torrent
- logger.error('No es un archivo Torrent: ' + url)
- torrents_path = ''
- if data_torrent:
- return (torrents_path, torrent_file)
- return torrents_path #Si hay un error, devolvemos el "path" vacío
-
- try:
- #Descargamos el .torrent
- if url.startswith("magnet:"):
- if config.get_setting("magnet2torrent", server="torrent", default=False):
- torrent_file = magnet2torrent(url, headers=headers) #Convierte el Magnet en un archivo Torrent
- else:
- if data_torrent:
- return (url, torrent_file)
- return url
- if not torrent_file:
- logger.error('No es un archivo Magnet: ' + url)
- torrents_path = ''
- if data_torrent:
- return (torrents_path, torrent_file)
- return torrents_path #Si hay un error, devolvemos el "path" vacío
- else:
- if lookup:
- proxy_retries = 0
- if post: #Descarga con POST
- response = httptools.downloadpage(url, headers=headers, post=post, \
- follow_redirects=False, timeout=timeout, proxy_retries=proxy_retries)
- else: #Descarga sin post
- response = httptools.downloadpage(url, headers=headers, timeout=timeout, \
- proxy_retries=proxy_retries)
- if not response.sucess:
- logger.error('Archivo .torrent no encontrado: ' + url)
- torrents_path = ''
- if data_torrent:
- return (torrents_path, torrent_file)
- return torrents_path #Si hay un error, devolvemos el "path" vacío
- torrent_file = response.data
- torrent_file_uncoded = response.data
- if PY3 and isinstance(torrent_file, bytes):
- torrent_file = "".join(chr(x) for x in bytes(torrent_file_uncoded))
-
- #Si es un archivo .ZIP tratamos de extraer el contenido
- if torrent_file.startswith("PK"):
- logger.info("it's a zip archive: " + url)
-
- torrents_path_zip = filetools.join(videolibrary_path, 'temp_torrents_zip') #Carpeta de trabajo
- torrents_path_zip = filetools.encode(torrents_path_zip)
- torrents_path_zip_file = filetools.join(torrents_path_zip, 'temp_torrents_zip.zip') #Nombre del .zip
-
- import time
- filetools.rmdirtree(torrents_path_zip) #Borramos la carpeta temporal
- time.sleep(1) #Hay que esperar, porque si no da error
- filetools.mkdir(torrents_path_zip) #La creamos de nuevo
-
- if filetools.write(torrents_path_zip_file, torrent_file_uncoded, vfs=VFS): #Salvamos el .zip
- torrent_file = '' #Borramos el contenido en memoria
- try: #Extraemos el .zip
- from core import ziptools
- unzipper = ziptools.ziptools()
- unzipper.extract(torrents_path_zip_file, torrents_path_zip)
- except:
- import xbmc
- xbmc.executebuiltin('XBMC.Extract("%s", "%s")' % (torrents_path_zip_file, torrents_path_zip))
- time.sleep(1)
-
- for root, folders, files in filetools.walk(torrents_path_zip): #Recorremos la carpeta para leer el .torrent
- for file in files:
- if file.endswith(".torrent"):
- input_file = filetools.join(root, file) #nombre del .torrent
- torrent_file = filetools.read(input_file, vfs=VFS) #leemos el .torrent
- torrent_file_uncoded = torrent_file
- if PY3 and isinstance(torrent_file, bytes):
- torrent_file = "".join(chr(x) for x in bytes(torrent_file_uncoded))
-
- filetools.rmdirtree(torrents_path_zip) #Borramos la carpeta temporal
-
- #Si no es un archivo .torrent (RAR, HTML,..., vacío) damos error
- if not scrapertools.find_single_match(torrent_file, '^d\d+:.*?\d+:'):
- logger.error('No es un archivo Torrent: ' + url)
- torrents_path = ''
- if data_torrent:
- return (torrents_path, torrent_file)
- return torrents_path #Si hay un error, devolvemos el "path" vacío
-
- #Calculamos el Hash del Torrent y modificamos el path
- import bencode, hashlib
-
- decodedDict = bencode.bdecode(torrent_file_uncoded)
- if not PY3:
- t_hash = hashlib.sha1(bencode.bencode(decodedDict[b"info"])).hexdigest()
- else:
- t_hash = hashlib.sha1(bencode.bencode(decodedDict["info"])).hexdigest()
-
- if t_hash:
- torrents_path = filetools.join(filetools.dirname(torrents_path), t_hash + '.torrent')
- torrents_path_encode = filetools.join(filetools.dirname(torrents_path_encode), t_hash + '.torrent')
-
- #Salvamos el .torrent
- if not lookup:
- if not filetools.write(torrents_path_encode, torrent_file_uncoded, vfs=VFS):
- logger.error('ERROR: Archivo .torrent no escrito: ' + torrents_path_encode)
- torrents_path = '' #Si hay un error, devolvemos el "path" vacío
- torrent_file = '' #... y el buffer del .torrent
- if data_torrent:
- return (torrents_path, torrent_file)
- return torrents_path
- except:
- torrents_path = '' #Si hay un error, devolvemos el "path" vacío
- torrent_file = '' #... y el buffer del .torrent
- logger.error('Error en el proceso de descarga del .torrent: ' + url + ' / ' + torrents_path_encode)
- logger.error(traceback.format_exc())
-
- #logger.debug(torrents_path)
- if data_torrent:
- return (torrents_path, torrent_file)
- return torrents_path
-
-
-def magnet2torrent(magnet, headers={}):
- logger.info()
-
- torrent_file = ''
- info = ''
- post = ''
- LIBTORRENT_PATH = config.get_setting("libtorrent_path", server="torrent", default="")
- LIBTORRENT_MAGNET_PATH = filetools.join(config.get_setting("downloadpath"), 'magnet')
- MAGNET2TORRENT = config.get_setting("magnet2torrent", server="torrent", default=False)
- btih = scrapertools.find_single_match(magnet, 'urn:btih:([\w\d]+)\&').upper()
-
- if magnet.startswith('magnet') and MAGNET2TORRENT:
-
- # Tratamos de convertir el magnet on-line (opción más rápida, pero no se puede convertir más de un magnet a la vez)
- url_list = [
- ('https://itorrents.org/torrent/', 6, '', '.torrent')
- ] # Lista de servicios on-line testeados
- for url, timeout, id, sufix in url_list:
- if id:
- post = '%s=%s' % (id, magnet)
- else:
- url = '%s%s%s' % (url, btih, sufix)
- response = httptools.downloadpage(url, timeout=timeout, headers=headers, post=post)
- if not response.sucess:
- continue
- if not scrapertools.find_single_match(response.data, '^d\d+:.*?\d+:') and not response.data.startswith("PK"):
- continue
- torrent_file = response.data
- break
-
- #Usamos Libtorrent para la conversión del magnet como alternativa (es lento)
- if not torrent_file:
- lt, e, e1, e2 = import_libtorrent(LIBTORRENT_PATH) # Importamos Libtorrent
- if lt:
- ses = lt.session() # Si se ha importado bien, activamos Libtorrent
- ses.add_dht_router("router.bittorrent.com",6881)
- ses.add_dht_router("router.utorrent.com",6881)
- ses.add_dht_router("dht.transmissionbt.com",6881)
- if ses:
- filetools.mkdir(LIBTORRENT_MAGNET_PATH) # Creamos la carpeta temporal
- params = {
- 'save_path': LIBTORRENT_MAGNET_PATH,
- 'trackers': trackers,
- 'storage_mode': lt.storage_mode_t.storage_mode_allocate
- } # Creamos los parámetros de la sesión
-
- h = lt.add_magnet_uri(ses, magnet, params) # Abrimos la sesión
- i = 0
- while not h.has_metadata() and not xbmc.abortRequested: # Esperamos mientras Libtorrent abre la sesión
- h.force_dht_announce()
- time.sleep(1)
- i += 1
- logger.error(i)
- if i > 5:
- LIBTORRENT_PATH = '' # No puede convertir el magnet
- break
-
- if LIBTORRENT_PATH:
- info = h.get_torrent_info() # Obtiene la información del .torrent
- torrent_file = lt.bencode(lt.create_torrent(info).generate()) # Obtiene los datos del .torrent
- ses.remove_torrent(h) # Desactiva Libtorrent
- filetools.rmdirtree(LIBTORRENT_MAGNET_PATH) # Elimina la carpeta temporal
-
- return torrent_file
-
-
-def verify_url_torrent(url, timeout=5):
- """
- Verifica si el archivo .torrent al que apunta la url está disponible, descargándolo en un area temporal
- Entrada: url
- Salida: True o False dependiendo del resultado de la operación
- """
-
- if not url or url == 'javascript:;': #Si la url viene vacía...
- return False #... volvemos con error
- torrents_path = caching_torrents(url, timeout=timeout, lookup=True) #Descargamos el .torrent
- if torrents_path: #Si ha tenido éxito...
- return True
- else:
- return False
-
-
-# Reproductor Cliente Torrent propio (libtorrent)
-def bt_client(mediaurl, xlistitem, rar_files, subtitle=None, password=None, item=None):
- logger.info()
-
- # Importamos el cliente
- from btserver import Client
-
- played = False
- debug = False
-
- try:
- save_path_videos = ''
- save_path_videos = filetools.join(config.get_setting("bt_download_path", server="torrent", \
- default=config.get_setting("downloadpath")), 'BT-torrents')
- except:
- pass
- if not config.get_setting("bt_download_path", server="torrent") and save_path_videos:
- config.set_setting("bt_download_path", filetools.join(config.get_data_path(), 'downloads'), server="torrent")
- if not save_path_videos:
- save_path_videos = filetools.join(config.get_data_path(), 'downloads', 'BT-torrents')
- config.set_setting("bt_download_path", filetools.join(config.get_data_path(), 'downloads'), server="torrent")
-
- UNRAR = config.get_setting("unrar_path", server="torrent", default="")
- BACKGROUND = config.get_setting("mct_background_download", server="torrent", default=True)
- RAR = config.get_setting("mct_rar_unpack", server="torrent", default=True)
- try:
- BUFFER = int(config.get_setting("bt_buffer", server="torrent", default="50"))
- except:
- BUFFER = 50
- DOWNLOAD_LIMIT = config.get_setting("mct_download_limit", server="torrent", default="")
- if DOWNLOAD_LIMIT:
- try:
- DOWNLOAD_LIMIT = int(DOWNLOAD_LIMIT)
- except:
- DOWNLOAD_LIMIT = 0
- else:
- DOWNLOAD_LIMIT = 0
- UPLOAD_LIMIT = 100
-
- torr_client = 'BT'
- rar_file = ''
- rar_names = []
- rar = False
- rar_res = False
- bkg_user = False
- video_names = []
- video_file = ''
- video_path = ''
- videourl = ''
- msg_header = 'KoD %s Client Torrent' % torr_client
- extensions_list = ['.aaf', '.3gp', '.asf', '.avi', '.flv', '.mpeg',
- '.m1v', '.m2v', '.m4v', '.mkv', '.mov', '.mpg',
- '.mpe', '.mp4', '.ogg', '.rar', '.wmv', '.zip']
-
- for entry in rar_files:
- for file, path in list(entry.items()):
- if file == 'path' and '.rar' in str(path):
- for file_r in path:
- rar_names += [file_r]
- rar = True
- if RAR and BACKGROUND:
- bkg_user = True
- elif file == 'path' and not '.rar' in str(path):
- for file_r in path:
- if os.path.splitext(file_r)[1] in extensions_list:
- video_names += [file_r]
- elif file == '__name':
- video_path = path
- video_file = path
- if rar: rar_file = '%s/%s' % (video_path, rar_names[0])
- erase_file_path = filetools.join(save_path_videos, video_path)
- video_path = erase_file_path
- if video_names: video_file = video_names[0]
- if not video_file and mediaurl.startswith('magnet'):
- video_file = urllib.unquote_plus(scrapertools.find_single_match(mediaurl, '(?:\&|&)dn=([^\&]+)\&'))
- erase_file_path = filetools.join(save_path_videos, video_file)
-
- if rar and RAR and not UNRAR:
- if not platformtools.dialog_yesno(msg_header, config.get_localized_string(70791)):
- return
-
- # Iniciamos el cliente:
- c = Client(url=mediaurl, is_playing_fnc=xbmc_player.isPlaying, wait_time=None, auto_shutdown=False, timeout=10,
- temp_path=save_path_videos, print_status=debug, auto_delete=False)
-
- activo = True
- finalizado = False
- dp_cerrado = True
-
- # Mostramos el progreso
- if rar and RAR and BACKGROUND: # Si se descarga un RAR...
- progreso = platformtools.dialog_progress_bg(msg_header)
- platformtools.dialog_notification(config.get_localized_string(70790), config.get_localized_string(70769), time=10000)
- else:
- progreso = platformtools.dialog_progress('%s Torrent Client' % torr_client, '')
- dp_cerrado = False
-
- # Mientras el progreso no sea cancelado ni el cliente cerrado
- try:
- while not c.closed and not xbmc.abortRequested:
- # Obtenemos el estado del torrent
- s = c.status
- if debug:
- # Montamos las tres lineas con la info del torrent
- txt = '%.2f%% de %.1fMB %s | %.1f kB/s' % \
- (s.progress_file, s.file_size, s.str_state, s._download_rate)
- txt2 = 'S: %d(%d) P: %d(%d) | DHT:%s (%d) | Trakers: %d | Pi: %d(%d)' % \
- (s.num_seeds, s.num_complete, s.num_peers, s.num_incomplete, s.dht_state, s.dht_nodes,
- s.trackers, s.pieces_sum, s.pieces_len)
- txt3 = 'Origen Peers TRK: %d DHT: %d PEX: %d LSD %d ' % \
- (s.trk_peers, s.dht_peers, s.pex_peers, s.lsd_peers)
- else:
- txt = '%.2f%% de %.1fMB %s | %.1f kB/s' % \
- (s.progress_file, s.file_size, s.str_state, s._download_rate)
- txt2 = 'S: %d(%d) P: %d(%d) | DHT:%s (%d) | Trakers: %d | Pi: %d(%d)' % \
- (s.num_seeds, s.num_complete, s.num_peers, s.num_incomplete, s.dht_state, s.dht_nodes,
- s.trackers, s.pieces_sum, s.pieces_len)
- txt3 = video_file
-
- if rar and RAR and BACKGROUND or bkg_user:
- progreso.update(s.buffer, txt, txt2)
- else:
- progreso.update(s.buffer, txt, txt2, txt3)
- time.sleep(1)
-
- if (not bkg_user and progreso.iscanceled()) and (not (rar and RAR and BACKGROUND) and progreso.iscanceled()):
-
- if not dp_cerrado:
- progreso.close()
- dp_cerrado = True
- if 'Finalizado' in s.str_state or 'Seeding' in s.str_state:
- """
- if not rar and platformtools.dialog_yesno(msg_header, config.get_localized_string(70198)):
- played = False
- dp_cerrado = False
- progreso = platformtools.dialog_progress(msg_header, '')
- progreso.update(s.buffer, txt, txt2, txt3)
- else:
- """
- dp_cerrado = False
- progreso = platformtools.dialog_progress(msg_header, '')
- break
-
- else:
- if not platformtools.dialog_yesno(msg_header, config.get_localized_string(30031), config.get_localized_string(30032)):
- dp_cerrado = False
- progreso = platformtools.dialog_progress(msg_header, '')
- break
-
- else:
- bkg_user = True
- if not dp_cerrado: progreso.close()
- dp_cerrado = False
- progreso = platformtools.dialog_progress_bg(msg_header)
- progreso.update(s.buffer, txt, txt2)
- if not c.closed:
- c.set_speed_limits(DOWNLOAD_LIMIT, UPLOAD_LIMIT) # Bajamos la velocidad en background
-
- # Si el buffer se ha llenado y la reproduccion no ha sido iniciada, se inicia
- if ((s.pieces_sum >= BUFFER or 'Finalizado' in s.str_state or 'Seeding' in s.str_state) and not rar and not bkg_user) or \
- (s.pieces_sum >= s.pieces_len - 3 and s.pieces_len > 0 and ('Finalizado' in s.str_state or 'Seeding' \
- in s.str_state) and (rar or bkg_user)) and not played:
-
- if rar and RAR and UNRAR:
- c.stop()
- activo = False
- finalizado = True
- bkg_user = False
- dp_cerrado = False
- video_file, rar_res, video_path, erase_file_path = extract_files(rar_file, \
- save_path_videos, password, progreso, item, torr_client) # ... extraemos el vídeo del RAR
- if rar_res and not xbmc.abortRequested:
- time.sleep(1)
- else:
- break
- elif (rar and not UNRAR) or (rar and not RAR):
- break
- elif bkg_user:
- finalizado = True
- break
-
- # Cerramos el progreso
- if not dp_cerrado:
- progreso.close()
- dp_cerrado = True
-
- # Reproducimos el vídeo extraido, si no hay nada en reproducción
- if not c.closed:
- c.set_speed_limits(DOWNLOAD_LIMIT, UPLOAD_LIMIT) # Bajamos la velocidad en background
- bkg_auto = True
- while xbmc_player.isPlaying() and not xbmc.abortRequested:
- time.sleep(3)
-
- # Obtenemos el playlist del torrent
- #videourl = c.get_play_list()
- if not rar_res: # Es un Magnet ?
- video_file = filetools.join(save_path_videos, s.file_name)
- if erase_file_path == save_path_videos:
- erase_file_path = video_file
- videourl = video_file
- else:
- videourl = filetools.join(video_path, video_file)
-
- # Iniciamos el reproductor
- playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
- playlist.clear()
- playlist.add(videourl, xlistitem)
- # xbmc_player = xbmc_player
- log("##### videourl: %s" % videourl)
- xbmc_player.play(playlist)
-
- # Marcamos como reproducido para que no se vuelva a iniciar
- played = True
-
- mark_auto_as_watched(item)
-
- # Y esperamos a que el reproductor se cierre
- bkg_auto = True
- dp_cerrado = True
- while xbmc_player.isPlaying() and not xbmc.abortRequested:
- time.sleep(1)
-
- if xbmc.getCondVisibility('Player.Playing'):
- if not dp_cerrado:
- dp_cerrado = True
- progreso.close()
-
- if xbmc.getCondVisibility('Player.Paused') and not rar_res:
- if not c.closed: s = c.status
- txt = '%.2f%% de %.1fMB %s | %.1f kB/s' % \
- (s.progress_file, s.file_size, s.str_state, s._download_rate)
- txt2 = 'S: %d(%d) P: %d(%d) | DHT:%s (%d) | Trakers: %d | Pi: %d(%d)' % \
- (s.num_seeds, s.num_complete, s.num_peers, s.num_incomplete, s.dht_state, s.dht_nodes,
- s.trackers, s.pieces_sum, s.pieces_len)
- txt3 = video_file[:99]
- if dp_cerrado:
- dp_cerrado = False
- progreso = xbmcgui.DialogProgressBG()
- progreso.create(msg_header)
- progreso.update(s.buffer, msg_header, '[CR][CR]' + txt + '[CR]' + txt2)
-
- if not dp_cerrado:
- dp_cerrado = True
- progreso.close()
-
- # Miramos si se ha completado la descarga para borrar o no los archivos
- if activo:
- s = c.status
- if s.pieces_sum == s.pieces_len:
- finalizado = True
- break
-
- if not platformtools.dialog_yesno(msg_header, config.get_localized_string(30031), config.get_localized_string(30032)):
- progreso = platformtools.dialog_progress(msg_header, '')
- dp_cerrado = False
- break
- else:
- bkg_user = True
- played = False
- if not dp_cerrado: progreso.close()
- progreso = platformtools.dialog_progress_bg(msg_header)
- progreso.update(s.buffer, txt, txt2)
- dp_cerrado = False
- continue
-
- # Cuando este cerrado, Volvemos a mostrar el dialogo
- if not (rar and bkg_user):
- progreso = platformtools.dialog_progress(msg_header, '')
- progreso.update(s.buffer, txt, txt2, txt3)
- dp_cerrado = False
-
- break
- except:
- logger.error(traceback.format_exc(1))
- return
-
- if not dp_cerrado:
- if rar or bkg_user:
- progreso.update(100, config.get_localized_string(70200), " ")
- else:
- progreso.update(100, config.get_localized_string(70200), " ", " ")
-
- # Detenemos el cliente
- if activo and not c.closed:
- c.stop()
- activo = False
-
- # Cerramos el progreso
- if not dp_cerrado:
- progreso.close()
- dp_cerrado = True
-
- # Y borramos los archivos de descarga restantes
- time.sleep(1)
- if filetools.exists(erase_file_path) and not bkg_user:
- if finalizado and not platformtools.dialog_yesno(msg_header, config.get_localized_string(70792)):
- return
- log("##### erase_file_path: %s" % erase_file_path)
- for x in range(10):
- if filetools.isdir(erase_file_path):
- if erase_file_path != save_path_videos:
- filetools.rmdirtree(erase_file_path)
- else:
- break
- else:
- filetools.remove(erase_file_path)
- time.sleep(5)
- if not filetools.exists(erase_file_path):
- break
-
-
-def call_torrent_via_web(mediaurl, torr_client):
- # Usado para llamar a los clientes externos de Torrents para automatizar la descarga de archivos que contienen .RAR
- logger.info()
-
- post = ''
- ELEMENTUMD_HOST = "http://localhost:65220"
- if torr_client == 'elementum':
- try:
- ADDON = xbmcaddon.Addon("plugin.video.elementum")
- except:
- ADDON = False
- if ADDON:
- ELEMENTUMD_HOST = "http://" + ADDON.getSetting("remote_host") + ":" + ADDON.getSetting("remote_port")
-
- local_host = {"quasar": ["http://localhost:65251/torrents/", "add?uri"], \
- "elementum": ["%s/torrents/" % ELEMENTUMD_HOST, "add"]}
-
- if torr_client == "quasar":
- uri = '%s%s=%s' % (local_host[torr_client][0], local_host[torr_client][1], mediaurl)
- elif torr_client == "elementum":
- uri = '%s%s' % (local_host[torr_client][0], local_host[torr_client][1])
- post = 'uri=%s&file=null&all=1' % mediaurl
-
- if post:
- response = httptools.downloadpage(uri, post=post, timeout=5, alfa_s=True, ignore_response_code=True)
- else:
- response = httptools.downloadpage(uri, timeout=5, alfa_s=True, ignore_response_code=True)
-
- return response.sucess
-
-
def mark_auto_as_watched(item):
- time_limit = time.time() + 150 #Marcamos el timepo máx. de buffering
- while not platformtools.is_playing() and time.time() < time_limit: #Esperamos mientra buffera
- time.sleep(5) #Repetimos cada intervalo
- #logger.debug(str(time_limit))
+ time_limit = time.time() + 150
+ while not platformtools.is_playing() and time.time() < time_limit:
+ time.sleep(5)
if item.subtitle:
time.sleep(5)
xbmc_player.setSubtitles(item.subtitle)
- #subt = xbmcgui.ListItem(path=item.url, thumbnailImage=item.thumbnail)
- #subt.setSubtitles([item.subtitle])
- if item.strm_path and platformtools.is_playing(): #Sólo si es de Videoteca
+ if item.strm_path and platformtools.is_playing():
from platformcode import xbmc_videolibrary
- xbmc_videolibrary.mark_auto_as_watched(item) #Marcamos como visto al terminar
- #logger.debug("Llamado el marcado")
-
-
-def wait_for_download(item, mediaurl, rar_files, torr_client, password='', size='', rar_control={}):
- logger.info()
-
- from subprocess import Popen, PIPE, STDOUT
-
- # Analizamos los archivos dentro del .torrent
- rar = False
- rar_names = []
- rar_names_abs = []
- folder = ''
- if rar_control:
- for x, entry in enumerate(rar_control['rar_files']):
- if '__name' in entry:
- folder = rar_control['rar_files'][x]['__name']
- break
- rar_names = [rar_control['rar_names'][0]]
- else:
- for entry in rar_files:
- for file, path in list(entry.items()):
- if file == 'path' and '.rar' in str(path):
- for file_r in path:
- rar_names += [file_r]
- rar = True
- elif file == '__name':
- folder = path
-
- if not folder: # Si no se detecta el folder...
- return ('', '', '') # ... no podemos hacer nada
-
- if not rar_names:
- return ('', '', folder)
- rar_file = '%s/%s' % (folder, rar_names[0])
- log("##### rar_file: %s" % rar_file)
- if len(rar_names) > 1:
- log("##### rar_names: %s" % str(rar_names))
-
- # Localizamos el path de descarga del .torrent
- save_path_videos = ''
- __settings__ = xbmcaddon.Addon(id="plugin.video.%s" % torr_client) # Apunta settings del cliente torrent
- if torr_client == 'torrenter':
- save_path_videos = str(xbmc.translatePath(__settings__.getSetting('storage')))
- if not save_path_videos:
- save_path_videos = str(filetools.join(xbmc.translatePath("special://home/"), \
- "cache", "xbmcup", "plugin.video.torrenter", "Torrenter"))
- else:
- save_path_videos = str(xbmc.translatePath(__settings__.getSetting('download_path')))
- if __settings__.getSetting('download_storage') == '1': # Descarga en memoria?
- return ('', '', folder) # volvemos
- if not save_path_videos: # No hay path de descarga?
- return ('', '', folder) # Volvemos
- log("##### save_path_videos: %s" % save_path_videos)
-
- # Si es nueva descarga, ponemos un archivo de control para reiniciar el UNRar si ha habido cancelación de Kodi
- # Si ya existe el archivo (llamada), se reinicia el proceso de UNRar donde se quedó
- if rar_control:
- if 'downloading' not in rar_control['status']:
- log("##### Torrent DESCARGADO Anteriormente: %s" % str(folder))
- return (rar_file, save_path_videos, folder)
- else:
- rar_control = {
- 'torr_client': torr_client,
- 'rar_files': rar_files,
- 'rar_names': rar_names,
- 'size': size,
- 'password': password,
- 'download_path': filetools.join(save_path_videos, folder),
- 'status': 'downloading',
- 'error': 0,
- 'error_msg': '',
- 'item': item.tourl(),
- 'mediaurl': mediaurl
- }
-
- if torr_client == 'quasar': # Quasar no copia en .torrent
- ret = filetools.copy(item.url, filetools.join(save_path_videos, 'torrents', \
- filetools.basename(item.url)), silent=True)
-
- # Esperamos mientras el .torrent se descarga. Verificamos si el .RAR está descargado al completo
- platformtools.dialog_notification(config.get_localized_string(70803), "", time=10000)
-
- # Plan A: usar el monitor del cliente torrent para ver el status de la descarga
- loop = 3600 # Loop de 10 horas hasta crear archivo
- wait_time = 10
- time.sleep(wait_time)
- fast = False
- ret = filetools.write(filetools.join(rar_control['download_path'], '_rar_control.json'), jsontools.dump(rar_control))
-
- for x in range(loop):
- if xbmc.abortRequested:
- return ('', '', folder)
- torr_data, deamon_url, index = get_tclient_data(folder, torr_client)
- if not torr_data or not deamon_url:
- if len(filetools.listdir(rar_control['download_path'], silent=True)) <= 1:
- filetools.remove(filetools.join(rar_control['download_path'], '_rar_control.json'), silent=True)
- filetools.rmdir(rar_control['download_path'], silent=True)
- return ('', '', folder) # Volvemos
- if (torr_client in ['quasar'] or torr_client in ['elementum']) and not \
- torr_data['label'].startswith('0.00%') and not fast:
- platformtools.dialog_notification(config.get_localized_string(60200), config.get_localized_string(70769), time=10000)
- fast = True
- if not torr_data['label'].startswith('100.00%'):
- log("##### Downloading: %s, ID: %s" % (scrapertools.find_single_match(torr_data['label'], '(^.*?\%)'), index))
- time.sleep(wait_time)
- continue
-
- update_rar_control(rar_control['download_path'], status='downloaded')
- log("##### Torrent FINALIZED: %s" % str(folder))
- return (rar_file, save_path_videos, folder)
-
- # Plan B: monitorizar con UnRAR si los archivos se han desacargado por completo
- unrar_path = config.get_setting("unrar_path", server="torrent", default="")
- if not unrar_path: # Si Unrar no está instalado...
- return ('', '', folder) # ... no podemos hacer nada
-
- cmd = []
- for rar_name in rar_names: # Preparamos por si es un archivo multiparte
- cmd.append(['%s' % unrar_path, 'l', '%s' % filetools.join(save_path_videos, folder, rar_name)])
-
- creationflags = ''
- if xbmc.getCondVisibility("system.platform.Windows"):
- creationflags = 0x08000000
- loop = 30 # Loop inicial de 5 minutos hasta crear archivo
- wait_time = 10
- loop_change = 0
- loop_error = 6
- part_name = ''
- y = 0
- returncode = ''
- fast = False
- while rar and not xbmc.abortRequested:
- for x in range(loop): # Loop corto (5 min.) o largo (10 h.)
- if xbmc.abortRequested:
- return ('', '', folder)
- if not rar or loop_change > 0:
- loop = loop_change # Paso de loop corto a largo
- loop_change = 0
- break
- try:
- responses = []
- for z, command in enumerate(cmd): # Se prueba por cada parte
- if xbmc.getCondVisibility("system.platform.Windows"):
- data_rar = Popen(command, bufsize=0, stdout=PIPE, stdin=PIPE, \
- stderr=STDOUT, creationflags=creationflags)
- else:
- data_rar = Popen(command, bufsize=0, stdout=PIPE, stdin=PIPE, \
- stderr=STDOUT)
- out_, error_ = data_rar.communicate()
- responses.append([z, str(data_rar.returncode), out_, error_]) # Se guarda la respuesta de cada parte
- except:
- logger.error(traceback.format_exc(1)) # Error de incompatibilidad de UnRAR
- rar = False
- break
- else:
- dl_files = 0
- for z, returncode, out__, error__ in responses: # Analizamos las respuestas
- if returncode == '0': # Ya se ha descargado... parte ...
- dl_files += 1
- part_name = scrapertools.find_single_match(str(out__), '(\.part\d+.rar)')
- log("##### Torrent downloading: %s, %s" % (part_name, str(returncode)))
- if dl_files == len(cmd): # ... o todo
- fast = True
- rar = False
- break # ... o sólo una parte
- elif returncode == '10': # archivo no existe
- if loop != 30: # Si el archivo es borrado durante el proceso ...
- rar = False
- break #... abortamos
- elif returncode == '6': # En proceso de descarga
- y += 1
- #if loop == 30 and y == len(responses): # Si es la primera vez en proceso ...
- if loop == 30 and y == 1: # Si es la primera vez en proceso ...
- if torr_client in ['quasar']:
- platformtools.dialog_notification(config.get_localized_string(60200), config.get_localized_string(70769), time=10000)
- loop_change = 3600 # ... pasamos a un loop de 10 horas
- elif loop <= 6: # Recuerado el error desconocido
- loop_change = 3600 # ... pasamos a un loop de 10 horas
- loop_error = 6 # Restauramos loop_error por si acaso
- break
- elif returncode == '1': # Ha alcanzado el fin de archivo ??? pasamos
- part_name = scrapertools.find_single_match(str(out__), '(\.part\d+.rar)')
- log("##### Torrent downloading: %s, %s" % (part_name, str(returncode)))
- else: # No entendemos el error
- loop_change = loop_error # ... pasamos a un loop de 1 minutos para reintentar
- loop_error += -1
- break #... abortamos
-
- if str(returncode) in ['0', '6', '10']:
- log("##### Torrent downloading: %s" % str(returncode))
- else:
- log("##### Torrent downloading: %s, %s" % (str(out__), str(returncode)))
- if not rar or fast:
- fast = False
- break
- time.sleep(wait_time) # Esperamos un poco y volvemos a empezar
- else:
- rar = False
- break
-
- if str(returncode) == '0':
- log("##### Torrent FINALIZED: %s" % str(returncode))
- else:
- rar_file = ''
- logger.error('##### Torrent NO DESCARGADO: %s, %s' % (str(out__), str(returncode)))
-
- return (rar_file, save_path_videos, folder)
-
-
-def get_tclient_data(folder, torr_client):
-
- # Monitoriza el estado de descarga del torrent en Quasar y Elementum
- ELEMENTUMD_HOST = "http://localhost:65220"
- if torr_client == 'elementum':
- try:
- ADDON = xbmcaddon.Addon("plugin.video.elementum")
- except:
- ADDON = False
- if ADDON:
- ELEMENTUMD_HOST = "http://" + ADDON.getSetting("remote_host") + ":" + ADDON.getSetting("remote_port")
-
- local_host = {"quasar": "http://localhost:65251/torrents/", "elementum": "%s/torrents/" % ELEMENTUMD_HOST}
- torr = ''
- torr_id = ''
- x = 0
- y = ''
-
- try:
- data = httptools.downloadpage(local_host[torr_client], timeout=5, alfa_s=True).data
- if not data:
- return '', local_host[torr_client], 0
-
- data = jsontools.load(data)
- data = data['items']
- for x, torr in enumerate(data):
- if not folder in torr['label']:
- continue
- if "elementum" in torr_client:
- torr_id = scrapertools.find_single_match(str(torr), 'torrents\/move\/(.*?)\)')
- break
- else:
- return '', local_host[torr_client], 0
- except:
- log(traceback.format_exc(1))
- return '', local_host[torr_client], 0
-
- if torr_id:
- y = torr_id
- else:
- y = x
- return torr, local_host[torr_client], y
-
-
-def extract_files(rar_file, save_path_videos, password, dp, item=None, \
- torr_client=None, rar_control={}, size='RAR', mediaurl=''):
- logger.info()
-
- from platformcode import custom_code
-
- if not rar_control:
- rar_control = {
- 'torr_client': torr_client,
- 'rar_files': [{"__name": "%s" % rar_file.split("/")[0]}],
- 'rar_names': [filetools.basename(rar_file)],
- 'size': size,
- 'password': password,
- 'download_path': save_path_videos,
- 'status': 'downloaded',
- 'error': 0,
- 'error_msg': '',
- 'item': item.tourl(),
- 'mediaurl': mediaurl
- }
- ret = filetools.write(filetools.join(rar_control['download_path'], '_rar_control.json'), jsontools.dump(rar_control))
-
- #reload(sys)
- #sys.setdefaultencoding('utf-8')
- sys.path.insert(0, config.get_setting("unrar_path", server="torrent", default="")\
- .replace('/unrar', '').replace('\\unrar,exe', ''))
-
- import rarfile
-
- # Verificamos si hay path para UnRAR
- rarfile.UNRAR_TOOL = config.get_setting("unrar_path", server="torrent", default="")
- if not rarfile.UNRAR_TOOL:
- if xbmc.getCondVisibility("system.platform.Android"):
- rarfile.UNRAR_TOOL = xbmc.executebuiltin("StartAndroidActivity(com.rarlab.rar)")
- return rar_file, False, '', ''
- log("##### unrar_path: %s" % rarfile.UNRAR_TOOL)
- rarfile.DEFAULT_CHARSET = 'utf-8'
-
- # Preparamos un path alternativo más corto para no sobrepasar la longitud máxima
- video_path = ''
- if item:
- if item.contentType == 'movie':
- video_path = '%s-%s' % (item.contentTitle, item.infoLabels['tmdb_id'])
- else:
- video_path = '%s-%sx%s-%s' % (item.contentSerieName, item.contentSeason, \
- item.contentEpisodeNumber, item.infoLabels['tmdb_id'])
- video_path = video_path.replace("á", "a").replace("é", "e").replace("í", "i").replace("ó", "o")\
- .replace("ú", "u").replace("ü", "u").replace("ñ", "n")\
- .replace("Á", "A").replace("É", "E").replace("Í", "I").replace("Ó", "O")\
- .replace("Ú", "U").replace("Ü", "U").replace("Ñ", "N")
-
- # Renombramos el path dejado en la descarga a uno más corto
- rename_status = False
- org_rar_file = rar_file
- org_save_path_videos = save_path_videos
- if video_path and '/' in rar_file:
- log("##### rar_file: %s" % rar_file)
- rename_status, rar_file = rename_rar_dir(org_rar_file, org_save_path_videos, video_path, torr_client)
-
- # Calculamos el path para del RAR
- if "/" in rar_file:
- folders = rar_file.split("/")
- erase_file_path = filetools.join(save_path_videos, folders[0])
- file_path = save_path_videos
- for f in folders:
- file_path = filetools.join(file_path, f)
- else:
- file_path = save_path_videos
- erase_file_path = save_path_videos
-
- # Calculamos el path para la extracción
- if "/" in rar_file:
- folders = rar_file.split("/")
- for f in folders:
- if not '.rar' in f:
- save_path_videos = filetools.join(save_path_videos, f)
- save_path_videos = filetools.join(save_path_videos, 'Extracted')
- if not filetools.exists(save_path_videos): filetools.mkdir(save_path_videos)
- log("##### save_path_videos: %s" % save_path_videos)
-
- rar_control = update_rar_control(erase_file_path, status='UnRARing')
-
- # Permite hasta 5 pasadas de extracción de .RARs anidados
- platformtools.dialog_notification(config.get_localized_string(70793), rar_file, time=5000)
- for x in range(5):
- try:
- if not PY3:
- archive = rarfile.RarFile(file_path.decode("utf8"))
- else:
- archive = rarfile.RarFile(file_path)
- except:
- log("##### ERROR in rar archive: %s" % rar_file)
- log("##### ERROR in rar folder: %s" % file_path)
- log(traceback.format_exc())
- error_msg = config.get_localized_string(70796)
- error_msg1 = config.get_localized_string(60015)
- platformtools.dialog_notification(error_msg, error_msg1)
- rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR')
- return rar_file, False, '', ''
-
- # Analizamos si es necesaria una contraseña, que debería estar en item.password
- if archive.needs_password():
- if not password:
- pass_path = filetools.split(file_path)[0]
- password = last_password_search(pass_path, erase_file_path)
- if not password :
- password = platformtools.dialog_input(heading=config.get_localized_string(70794) % pass_path)
- if not password:
- error_msg = config.get_localized_string(60309)
- rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR')
- dp.close()
- return custom_code.reactivate_unrar(init=False, mute=False)
- archive.setpassword(password)
- log("##### Password rar: %s" % password)
-
- # Miramos el contenido del RAR a extraer
- files = archive.infolist()
- info = []
- for idx, i in enumerate(files):
- if i.file_size == 0:
- files.pop(idx)
- continue
- filename = i.filename
- if "/" in filename:
- filename = filename.rsplit("/", 1)[1]
-
- info.append("%s - %.2f MB" % (filename, i.file_size / 1048576.0))
- if info:
- info.append(config.get_localized_string(70801))
- else:
- error_msg = config.get_localized_string(70797)
- error_msg1 = config.get_localized_string(70798)
- platformtools.dialog_notification(error_msg, error_msg1)
- rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR')
- dp.close()
- return custom_code.reactivate_unrar(init=False, mute=False)
-
- # Seleccionamos extraer TODOS los archivos del RAR
- #selection = xbmcgui.Dialog().select("Selecciona el fichero a extraer y reproducir", info)
- selection = len(info) - 1
- if selection < 0:
- error_msg = config.get_localized_string(70797)
- platformtools.dialog_notification(error_msg)
- rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR')
- return rar_file, False, '', ''
- else:
- try:
- log("##### RAR Extract INI #####")
- if selection == len(info) - 1:
- log("##### rar_file 1: %s" % file_path)
- log("##### save_path_videos 1: %s" % save_path_videos)
- dp.update(99, config.get_localized_string(70803), config.get_localized_string(70802))
- archive.extractall(save_path_videos)
- else:
- log("##### rar_file 2: %s" % file_path)
- log("##### save_path_videos 2: %s" % save_path_videos)
- dp.update(99, config.get_localized_string(70802), config.get_localized_string(70803) + " %s" % info[selection])
- archive.extract(files[selection], save_path_videos)
- log("##### RAR Extract END #####")
- except (rarfile.RarWrongPassword, rarfile.RarCRCError):
- log(traceback.format_exc(1))
- error_msg = config.get_localized_string(70799)
- error_msg1 = config.get_localized_string(60309)
- platformtools.dialog_notification(error_msg, error_msg1)
- rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg1, status='ERROR')
- dp.close()
- return custom_code.reactivate_unrar(init=False, mute=False)
- except rarfile.BadRarFile:
- log(traceback.format_exc(1))
- error_msg = config.get_localized_string(70799)
- error_msg1 = config.get_localized_string(60800)
- platformtools.dialog_notification(error_msg, error_msg1)
- rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg1, status='ERROR')
- #return rar_file, False, '', erase_file_path
- dp.close()
- return custom_code.reactivate_unrar(init=False, mute=False)
- except:
- log(traceback.format_exc(1))
- error_msg = config.get_localized_string(70799)
- error_msg1 = config.get_localized_string(60015)
- platformtools.dialog_notification(error_msg, error_msg1)
- rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR')
- dp.close()
- return custom_code.reactivate_unrar(init=False, mute=False)
-
- extensions_list = ['.aaf', '.3gp', '.asf', '.avi', '.flv', '.mpeg',
- '.m1v', '.m2v', '.m4v', '.mkv', '.mov', '.mpg',
- '.mpe', '.mp4', '.ogg', '.wmv']
-
- # Localizamos el path donde se ha dejado la extracción
- folder = True
- file_result = filetools.listdir(save_path_videos)
- while folder:
- for file_r in file_result:
- if filetools.isdir(filetools.join(save_path_videos, file_r)):
- file_result_alt = filetools.listdir(filetools.join(save_path_videos, file_r))
- if file_result_alt:
- file_result = file_result_alt
- save_path_videos = filetools.join(save_path_videos, file_r)
- else:
- folder = False
- break
- else:
- folder = False
-
- # Si hay RARs anidados, ajustamos los paths para la siguiente pasada
- if '.rar' in str(file_result):
- for file_r in file_result:
- if '.rar' in file_r:
- rar_file = file_r
- file_path = str(filetools.join(save_path_videos, rar_file))
- save_path_videos = filetools.join(save_path_videos, 'Extracted')
- rar_control = update_rar_control(erase_file_path, newextract=(rar_file))
- if not filetools.exists(save_path_videos): filetools.mkdir(save_path_videos)
- platformtools.dialog_notification(config.get_localized_string(70804), rar_file, time=5000)
-
- # Si ya se ha extraido todo, preparamos el retorno
- else:
- video_list = []
- for file_r in file_result:
- if os.path.splitext(file_r)[1] in extensions_list:
- video_list += [file_r]
- if len(video_list) == 0:
- error_msg = config.get_localized_string(70797)
- error_msg1 = config.get_localized_string(70798)
- platformtools.dialog_notification(error_msg, error_msg1)
- rar_control = update_rar_control(erase_file_path, error=True, error_msg=error_msg, status='ERROR')
- dp.close()
- return custom_code.reactivate_unrar(init=False, mute=False)
- else:
- log("##### Archive extracted: %s" % video_list[0])
- platformtools.dialog_notification(config.get_localized_string(70795), video_list[0], time=10000)
- log("##### Archive removes: %s" % file_path)
- #rar_control = update_rar_control(erase_file_path, status='DONE')
- ret = filetools.remove(filetools.join(erase_file_path, '_rar_control.json'), silent=True)
- return str(video_list[0]), True, save_path_videos, erase_file_path
-
-
-def rename_rar_dir(rar_file, save_path_videos, video_path, torr_client):
- logger.info()
-
- rename_status = False
- folders = rar_file.split("/")
- if filetools.exists(filetools.join(save_path_videos, folders[0])) and video_path not in folders[0]:
- if not PY3:
- src = filetools.join(save_path_videos, folders[0]).decode("utf8")
- dst = filetools.join(save_path_videos, video_path).decode("utf8")
- dst_file = video_path.decode("utf8")
- else:
- src = filetools.join(save_path_videos, folders[0])
- dst = filetools.join(save_path_videos, video_path)
- dst_file = video_path
-
- for x in range(20):
- if xbmc.abortRequested:
- return rename_status, rar_file
- xbmc.sleep(1000)
-
- # Se para la actividad para que libere los archivos descargados
- if torr_client in ['quasar', 'elementum']:
- torr_data, deamon_url, index = get_tclient_data(folders[0], torr_client)
- if torr_data and deamon_url:
- log("##### Client URL: %s" % '%spause/%s' % (deamon_url, index))
- data = httptools.downloadpage('%spause/%s' % (deamon_url, index), timeout=5, alfa_s=True).data
-
- try:
- if filetools.exists(src):
- filetools.rename(src, dst_file, silent=True, strict=True)
- elif not filetools.exists(dst_file):
- break
- except:
- log("##### Rename ERROR: SRC: %s" % src)
- log(traceback.format_exc(1))
- else:
- if filetools.exists(dst):
- log("##### Renamed: SRC: %s" % src)
- log("##### TO: DST: %s" % dst)
- rar_file = video_path + '/' + folders[1]
- rename_status = True
- update_rar_control(dst, newpath=dst)
- break
-
- return rename_status, rar_file
-
-
-def last_password_search(pass_path, erase_file_path=''):
- logger.info(pass_path)
-
- if not erase_file_path:
- erase_file_path = pass_path
-
- # Busca en el Path de extracción si hay algún archivo que contenga la URL donde pueda estar la CONTRASEÑA
- password = ''
- patron_url = '(http.*\:\/\/(?:www.)?\w+\.\w+\/.*?)[\n|\r|$]'
- patron_pass = ')", "", httptools.downloadpage(url).data)
- password = scrapertools.find_single_match(data, patron_pass)
- if password:
- update_rar_control(erase_file_path, password=password, status='UnRARing: Password update')
- break
- except:
- log(traceback.format_exc(1))
-
- log("##### Password Extracted: %s" % password)
- return password
-
-
-def update_rar_control(path, newpath='', newextract='', password='', error='', error_msg='', status=''):
-
- try:
- rar_control = {}
- rar_control = jsontools.load(filetools.read(filetools.join(path, '_rar_control.json')))
- if rar_control:
- if newpath:
- rar_control['download_path'] = newpath
- for x, entry in enumerate(rar_control['rar_files']):
- if '__name' in entry:
- rar_control['rar_files'][x]['__name'] = filetools.basename(newpath)
- break
- if newextract:
- for x, entry in enumerate(rar_control['rar_files']):
- if '__name' in entry:
- #rar_control['rar_files'][x]['__name'] = filetools.join(rar_control['rar_files'][x]['__name'], 'Extracted')
- rar_control['rar_files'][x]['__name'] = rar_control['rar_files'][x]['__name'] + '/Extracted'
- break
- rar_control['rar_names'] = [newextract]
- if password: rar_control['password'] = password
- if error: rar_control['error'] += 1
- if error_msg: rar_control['error_msg'] = error_msg
- if status and status not in rar_control['status']: rar_control['status'] = status
- ret = filetools.write(filetools.join(rar_control['download_path'], '_rar_control.json'), \
- jsontools.dump(rar_control))
- logger.debug('%s, %s, %s, %s, %s, %s' % (rar_control['download_path'], \
- rar_control['rar_names'][0], rar_control['password'], \
- str(rar_control['error']), rar_control['error_msg'], rar_control['status']))
- except:
- log(traceback.format_exc(1))
-
- return rar_control
-
-
-def import_libtorrent(LIBTORRENT_PATH):
- logger.info(LIBTORRENT_PATH)
-
- e = ''
- e1 = ''
- e2 = ''
- fp = ''
- pathname = ''
- description = ''
- lt = ''
-
- try:
- sys.path.insert(0, LIBTORRENT_PATH)
- if LIBTORRENT_PATH:
- try:
- if not xbmc.getCondVisibility("system.platform.android"):
- import libtorrent as lt
- pathname = LIBTORRENT_PATH
- else:
- import imp
- from ctypes import CDLL
- dll_path = os.path.join(LIBTORRENT_PATH, 'liblibtorrent.so')
- liblibtorrent = CDLL(dll_path)
-
- path_list = [LIBTORRENT_PATH, xbmc.translatePath('special://xbmc')]
- fp, pathname, description = imp.find_module('libtorrent', path_list)
-
- # Esta parte no funciona en Android. Por algún motivo da el error "dlopen failed: library "liblibtorrent.so" not found"
- # Hay que encontrar un hack para rodear el problema. Lo siguiente ha sido probado sin éxito:
- #if fp: fp.close()
- #fp = filetools.file_open(filetools.join(LIBTORRENT_PATH, 'libtorrent.so'), mode='rb') # Usa XbmcVFS
- #fp = open(os.path.join(LIBTORRENT_PATH, 'libtorrent.so'), 'rb')
-
- try:
- lt = imp.load_module('libtorrent', fp, pathname, description)
- finally:
- if fp: fp.close()
-
- except Exception as e1:
- logger.error(traceback.format_exc(1))
- log('fp = ' + str(fp))
- log('pathname = ' + str(pathname))
- log('description = ' + str(description))
- if fp: fp.close()
- from lib.python_libtorrent.python_libtorrent import get_libtorrent
- lt = get_libtorrent()
-
- except Exception as e2:
- try:
- logger.error(traceback.format_exc())
- if fp: fp.close()
- e = e1 or e2
- ok = platformtools.dialog_ok(config.get_localized_string(30035), config.get_localized_string(30036), config.get_localized_string(60015), str(e2))
- except:
- pass
-
- try:
- if not e1 and e2: e1 = e2
- except:
- try:
- if e2:
- e1 = e2
- else:
- e1 = ''
- e2 = ''
- except:
- e1 = ''
- e2 = ''
-
- return lt, e, e1, e2
+ xbmc_videolibrary.mark_auto_as_watched(item)
########################## ELEMENTUM MONITOR TEST ##########################
-def elementum_monitor(item, torr_client):
- host = 'http://127.0.0.1:65220/torrents/'
- torr_setting = xbmcaddon.Addon(id="plugin.video.elementum")
- # from core.support import dbg;dbg()
- data = httptools.downloadpage(host, alfa_s=True).data
- items = jsontools.load(data)['items']
- if not items:
- set_elementum()
- call_torrent_via_web(urllib.quote_plus(item.url), torr_client)
+elementumHost = 'http://127.0.0.1:65220/torrents/'
- result = 0
- while result < 100:
- data = httptools.downloadpage(host, alfa_s=True).data
- items = jsontools.load(data)['items']
+def elementum_download(item):
+ sleep = False
+ if elementum_setting.getSetting('logger_silent') == False:
+ elementum_setting.setSetting('logger_silent', 'true')
+ sleep = True
+ if elementum_setting.getSetting('download_storage') != 0:
+ config.set_setting('elementumtype', elementum_setting.getSetting('download_storage')) # Backup Setting
+ elementum_setting.setSetting('download_storage', '0') # Set Setting
+ sleep = True
+ if elementum_setting.getSetting('download_path') != config.get_setting('downloadpath'):
+ elementum_setting.setSetting('download_path', config.get_setting('downloadpath')) # Backup Setting
+ config.set_setting('elementumdl', elementum_setting.getSetting('download_path')) # Set Setting
+ sleep = True
+ if sleep: time.sleep(3)
- partials = []
- files = []
- titles = []
-
- for it in items:
- p = scrapertools.find_single_match(it['label'], r'(\d+\.\d+)%')
- f = scrapertools.find_single_match(it['path'], r'resume=([^&]+)')
- t = it['info']['title']
- partials.append(p)
- files.append(f)
- titles.append(t)
-
- partial = 0
- for i, p in enumerate(partials):
- partial += float(p)
- update_download_info(files[i], p, titles[i])
-
- result = partial / len(items)
- xbmc.sleep(5000)
-
- log('All Torrent downloaded.')
- xbmc.sleep(1000)
- unset_elementum()
+ path = filetools.join(config.get_data_path(),'elementum_torrent.txt')
+ url = urllib.quote_plus(item.url)
+ filetools.write(path, url)
-def update_download_info(file, partial, title):
- extensions_list = ['aaf', '3gp', 'asf', 'avi', 'flv', 'mpeg', 'm1v', 'm2v', 'm4v', 'mkv', 'mov', 'mpg', 'mpe', 'mp4', 'ogg', 'wmv']
+def stop_elementum_monitor():
+ config.set_setting('stop_elementum_monitor', True)
+ time.sleep(2)
+
+def start_elementum_monitor():
+ config.set_setting('stop_elementum_monitor', False)
+ time.sleep(3)
+ Thread(target=elementum_monitor).start()
+
+
+
+def elementum_monitor():
+ log('Start Elementum Monitor')
+ path = filetools.join(config.get_data_path(),'elementum_torrent.txt')
+ partials = []
+ while True:
+ if filetools.isfile(path):
+ log('Add Torrent')
+ url = filetools.read(path)
+ TorrentName = match(url, patron=r'btih(?::|%3A)([^&%]+)', string=True).match
+ uri = elementum_host + 'add'
+ post = 'uri=%s&file=null&all=1' % url
+ match(uri, post=post, timeout=5, alfa_s=True, ignore_response_code=True)
+ filetools.remove(path)
+ while not filetools.isfile(filetools.join(elementum_setting.getSetting('torrents_path'), TorrentName + '.torrent')):
+ time.sleep(1)
+ else:
+ data = match(elementum_host, alfa_s=True).data
+ if data:
+ json = jsontools.load(data)['items']
+
+ for it in json:
+ Partial = float(match(it['label'], patron=r'(\d+\.\d+)%').match)
+ Title = it['info']['title']
+ TorrentName = match(it['path'], patron=r'resume=([^&]+)').match
+ File, Json = find_file(TorrentName)
+ update_download_info(Partial, Title, TorrentName, File, Json)
+ partials.append(Partial)
+
+ result = 0
+ for p in partials:
+ result += p
+
+ if result / len(partials) == 100:
+ unset_elementum()
+
+ time.sleep(1)
+
+
+def find_file(File):
path = xbmc.translatePath(config.get_setting('downloadlistpath'))
- dlpath = filetools.join(config.get_setting('downloadpath'), title)
- log('DL PATH: ' + dlpath)
- sep = '/' if dlpath.startswith('smb') else os.sep
- if filetools.isdir(dlpath) and partial > 0:
- dlfiles = filetools.listdir(dlpath)
- for f in dlfiles:
- log('FILE TYPE: ' + f.split('.')[-1])
- if f.split('.')[-1] in extensions_list:
- dlpath = filetools.join(dlpath.split(sep)[-1], f)
- else:
- dlpath = dlpath.split(sep)[-1]
-
files = filetools.listdir(path)
+ # dbg()
for f in files:
- json = jsontools.load(filetools.read(filetools.join(path, f)))
- if 'downloadServer' in json and file in json['downloadServer']['url'] or \
- 'url' in json and file in json['url']:
- jsontools.update_node(dlpath, f, "downloadFilename", path)
- jsontools.update_node(float(partial), f, "downloadProgress", path)
- if float(partial) == 100:
- jsontools.update_node(2, f, "downloadStatus", path)
+ filepath = filetools.join(path, f)
+ json = jsontools.load(filetools.read(filepath))
+ if ('downloadServer' in json and 'url' in json['downloadServer'] and File in json['downloadServer']['url']) or ('url' in json and File in json['url']):
+ break
+ return filetools.split(filepath)[-1], json
+
+
+def update_download_info(Partial, Title, TorrentName, File, Json):
+ path = xbmc.translatePath(config.get_setting('downloadlistpath'))
+ dlpath = filetools.join(config.get_setting('downloadpath'), Title)
+ tpath = filetools.join(xbmc.translatePath(elementum_setting.getSetting('torrents_path')), TorrentName)
+ if Json['downloadSize'] == 0:
+ size = Torrent.from_file(tpath + '.torrent').total_size
+ jsontools.update_node(size, File, 'downloadSize', path)
+ if Json['downloadFilename'] != dlpath and 'backupFilename' not in Json:
+ jsontools.update_node(Json['downloadFilename'], File, 'backupFilename', path)
+ jsontools.update_node(dlpath, File, 'downloadFilename', path)
+ if Json['downloadProgress'] != Partial and Partial != 0:
+ jsontools.update_node(Partial, File, 'downloadProgress', path)
+ jsontools.update_node(4, File, 'downloadStatus', path)
+ if Partial == 100:
+ jsontools.update_node(Json['downloadSize'], File, 'downloadCompleted', path)
+ jsontools.update_node(2, File, 'downloadStatus', path)
+ requests.get(elementum_host + 'pause/' + TorrentName)
+ filetools.remove(tpath + '.torrent')
+ time.sleep(5)
+ rename(TorrentName, path)
+
+
+def rename(TorrentName, Path):
+ File, Json = find_file(TorrentName)
+ path = Json['downloadFilename']
+ if filetools.isdir(path):
+ extension = ''
+ files = filetools.listdir(path)
+ sep = '/' if path.lower().startswith("smb://") else os.sep
+ oldName = path.split(sep)[-1]
+ newName = Json['backupFilename']
+ for f in files:
+ ext = f.split('.')[-1]
+ if ext in extensions_list: extension = '.' + ext
+ filetools.rename(filetools.join(path, f), f.replace(oldName, newName))
+ filetools.rename(path, newName)
+ jsontools.update_node(filetools.join(newName,newName + extension), File, 'downloadFilename', Path)
+
+ else:
+ oldName = filetools.split(path)[-1]
+ newName = Json['backupFilename'] + '.' + oldName.split('.')[-1]
+ filetools.rename(path, newName)
+ jsontools.update_node(newName, File, 'downloadFilename', Path)
-def set_elementum():
- log('SET Elementum')
- torr_setting = xbmcaddon.Addon(id="plugin.video.elementum")
- config.set_setting('elementumtype', torr_setting.getSetting('download_storage'))
- config.set_setting('elementumdl', torr_setting.getSetting('download_path'))
- torr_setting.setSetting('download_storage', '0')
- torr_setting.setSetting('download_path', config.get_setting('downloadpath'))
- xbmc.sleep(3000)
def unset_elementum():
log('UNSET Elementum')
- torr_setting = xbmcaddon.Addon(id="plugin.video.elementum")
Sleep = False
if config.get_setting('elementumtype'):
- torr_setting.setSetting('download_storage', str(config.get_setting('elementumtype')))
+ elementum_setting.setSetting('download_storage', str(config.get_setting('elementumtype')))
Sleep = True
if config.get_setting('elementumdl'):
- torr_setting.setSetting('download_path', config.get_setting('elementumdl'))
+ elementum_setting.setSetting('download_path', config.get_setting('elementumdl'))
Sleep = True
if Sleep:
- xbmc.sleep(3000)
+ time.sleep(3)
########################## ELEMENTUM MONITOR TEST ##########################
-
-def log(texto):
- try:
- xbmc.log(texto, xbmc.LOGNOTICE)
- except:
- pass
diff --git a/service.py b/service.py
index 3e440373..bb6f03f5 100644
--- a/service.py
+++ b/service.py
@@ -397,6 +397,8 @@ if __name__ == "__main__":
custom_code.init()
from threading import Thread
Thread(target=viewmodeMonitor).start()
+ from servers import torrent
+ Thread(target=torrent.elementum_monitor).start()
if not config.get_setting("update", "videolibrary") == 2:
check_for_update(overwrite=False)
diff --git a/specials/downloads.py b/specials/downloads.py
index c45f02f4..637a0c1b 100644
--- a/specials/downloads.py
+++ b/specials/downloads.py
@@ -22,6 +22,7 @@ from core.downloader import Downloader
from core.item import Item
from platformcode import config, logger
from platformcode import platformtools
+from core.support import log, dbg, typo
kb = '0xFF65B3DA'
kg = '0xFF65DAA8'
@@ -920,6 +921,7 @@ def get_episodes(item):
def write_json(item):
logger.info()
+ channel = item.channel
item.action = "menu"
item.channel = "downloads"
item.downloadStatus = STATUS_CODES.stoped
@@ -933,7 +935,14 @@ def write_json(item):
if name in item.__dict__:
item.__dict__.pop(name)
- path = filetools.join(DOWNLOAD_LIST_PATH, str(time.time()) + ".json")
+ naming = item.fulltitle + typo(item.infoLabels['IMDBNumber'], '_ []') + typo(channel, '_ []')
+ naming += typo(item.contentLanguage, '_ []') if item.contentLanguage else ''
+ naming += typo(item.quality, '_ []') if item.quality else ''
+
+ path = filetools.join(DOWNLOAD_LIST_PATH, naming + ".json")
+ if filetools.isfile(path):
+ filetools.remove(path)
+
item.path = path
filetools.write(path, item.tojson())
time.sleep(0.1)
@@ -1039,6 +1048,7 @@ def save_download_movie(item):
progreso.update(0, config.get_localized_string(60062))
item.downloadFilename = filetools.validate_path("%s [%s]" % (item.contentTitle.strip(), item.infoLabels['IMDBNumber']))
+ item.backupFilename = filetools.validate_path("%s [%s]" % (item.contentTitle.strip(), item.infoLabels['IMDBNumber']))
write_json(item)
@@ -1059,8 +1069,7 @@ def save_download_movie(item):
def save_download_tvshow(item):
- logger.info("contentAction: %s | contentChannel: %s | contentType: %s | contentSerieName: %s" % (
- item.contentAction, item.contentChannel, item.contentType, item.contentSerieName))
+ logger.info("contentAction: %s | contentChannel: %s | contentType: %s | contentSerieName: %s" % (item.contentAction, item.contentChannel, item.contentType, item.contentSerieName))
progreso = platformtools.dialog_progress_bg(config.get_localized_string(30101), config.get_localized_string(70188))
try:
@@ -1088,9 +1097,8 @@ def save_download_tvshow(item):
progreso.close()
if not platformtools.dialog_yesno(config.get_localized_string(30101), config.get_localized_string(70189)):
- platformtools.dialog_ok(config.get_localized_string(30101),
- str(len(episodes)) + config.get_localized_string(30110) + item.contentSerieName,
- config.get_localized_string(30109))
+ platformtools.dialog_ok(config.get_localized_string(30101), str(len(episodes)) + config.get_localized_string(30110) + item.contentSerieName, config.get_localized_string(30109))
+
else:
if len(episodes) == 1:
play_item = select_server(episodes[0])