Torrent Download:

- Supporta solo Film
 - Monitor di Download
 - Rinominazione dei file alla fine del download
This commit is contained in:
Alhaziel01
2020-05-05 19:30:45 +02:00
parent 380a55d574
commit a94ec1cd2a
14 changed files with 1040 additions and 1572 deletions

View File

@@ -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)

View File

@@ -0,0 +1 @@
VERSION = (1, 0, 2)

7
lib/torrentool/api.py Normal file
View File

@@ -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

204
lib/torrentool/bencode.py Normal file
View File

@@ -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)

94
lib/torrentool/cli.py Normal file
View File

@@ -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={})

View File

@@ -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."""

View File

@@ -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

436
lib/torrentool/torrent.py Normal file
View File

@@ -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

91
lib/torrentool/utils.py Normal file
View File

@@ -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)

View File

@@ -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)

View File

@@ -106,9 +106,7 @@
<setting id="download_adv" type="action" label="30030" visible="eq(-8,true)" action="RunPlugin(plugin://plugin.video.kod/?ew0KCSJhY3Rpb24iOiJjaGFubmVsX2NvbmZpZyIsDQoJImNvbmZpZyI6ImRvd25sb2FkcyIsDQogICAgImNoYW5uZWwiOiJzZXR0aW5nIg0KfQ==)"/>
<setting id="elementumtype" type="text" label="Elementum Download Type" visible="false"/>
<setting id="quasartype" type="text" label="Quasar Download Type" visible="false"/>
<setting id="elementumdl" type="folder" label="Elementum Download Backup" visible="false"/>
<setting id="quasardl" type="folder" label="Quasar Download Backup" visible="false"/>
</category>
<!-- News -->

File diff suppressed because it is too large Load Diff

View File

@@ -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)

View File

@@ -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])