# -*- coding: utf-8 -*- from traceback import print_exc from pyload.utils import to_list, has_method, uniqify from pyload.utils.fs import exists, remove, fs_encode from pyload.utils.packagetools import parseNames from Base import Base, Retry class Package: """ Container that indicates that a new package should be created """ def __init__(self, name, urls=None, folder=None): self.name = name self.urls = urls if urls else [] self.folder = folder # nested packages self.packs = [] def addURL(self, url): self.urls.append(url) def addPackage(self, pack): self.packs.append(pack) def getAllURLs(self): urls = self.urls for p in self.packs: urls.extend(p.getAllURLs()) return urls # same name and urls is enough to be equal for packages def __eq__(self, other): return self.name == other.name and self.urls == other.urls def __repr__(self): return u"<CrypterPackage name=%s, links=%s, packs=%s" % (self.name, self.urls, self.packs) def __hash__(self): return hash(self.name) ^ hash(frozenset(self.urls)) ^ hash(self.name) class PyFileMockup: """ Legacy class needed by old crypter plugins """ def __init__(self, url, pack): self.url = url self.name = url self._package = pack self.packageid = pack.id if pack else -1 def package(self): return self._package class Crypter(Base): """ Base class for (de)crypter plugins. Overwrite decrypt* methods. How to use decrypt* methods: You have to overwrite at least one method of decryptURL, decryptURLs, decryptFile. After decrypting and generating urls/packages you have to return the result. Valid return Data is: :class:`Package` instance Crypter.Package A **new** package will be created with the name and the urls of the object. List of urls and `Package` instances All urls in the list will be added to the **current** package. For each `Package`\ instance a new package will be created. """ #: Prefix to annotate that the submited string for decrypting is indeed file content CONTENT_PREFIX = "filecontent:" @classmethod def decrypt(cls, core, url_or_urls): """Static method to decrypt urls or content. Can be used by other plugins. To decrypt file content prefix the string with ``CONTENT_PREFIX `` as seen above. :param core: pyLoad `Core`, needed in decrypt context :param url_or_urls: List of urls or single url/ file content :return: List of decrypted urls, all package info removed """ urls = to_list(url_or_urls) p = cls(core) try: result = p.processDecrypt(urls) finally: p.clean() ret = [] for url_or_pack in result: if isinstance(url_or_pack, Package): #package ret.extend(url_or_pack.getAllURLs()) else: # single url ret.append(url_or_pack) # eliminate duplicates return uniqify(ret) def __init__(self, core, package=None, password=None): Base.__init__(self, core) self.req = core.requestFactory.getRequest() # Package the plugin was initialized for, don't use this, its not guaranteed to be set self.package = package #: Password supplied by user self.password = password #: Propose a renaming of the owner package self.rename = None # For old style decrypter, do not use these! self.packages = [] self.urls = [] self.pyfile = None self.init() def init(self): """More init stuff if needed""" def setup(self): """Called everytime before decrypting. A Crypter plugin will be most likely used for several jobs.""" def decryptURL(self, url): """Decrypt a single url :param url: url to decrypt :return: See :class:`Crypter` Documentation """ if url.startswith("http"): # basic method to redirect return self.decryptFile(self.load(url)) else: self.fail(_("Not existing file or unsupported protocol")) def decryptURLs(self, urls): """Decrypt a bunch of urls :param urls: list of urls :return: See :class:`Crypter` Documentation """ raise NotImplementedError def decryptFile(self, content): """Decrypt file content :param content: content to decrypt as string :return: See :class:`Crypter` Documentation """ raise NotImplementedError def generatePackages(self, urls): """Generates :class:`Package` instances and names from urls. Useful for many different links and no\ given package name. :param urls: list of urls :return: list of `Package` """ return [Package(name, purls) for name, purls in parseNames([(url,url) for url in urls]).iteritems()] def _decrypt(self, urls): """ Internal method to select decrypting method :param urls: List of urls/content :return: """ cls = self.__class__ # separate local and remote files content, urls = self.getLocalContent(urls) if has_method(cls, "decryptURLs"): self.setup() result = to_list(self.decryptURLs(urls)) elif has_method(cls, "decryptURL"): result = [] for url in urls: self.setup() result.extend(to_list(self.decryptURL(url))) elif has_method(cls, "decrypt"): self.logDebug("Deprecated .decrypt() method in Crypter plugin") result = [] for url in urls: self.pyfile = PyFileMockup(url, self.package) self.setup() self.decrypt(self.pyfile) result.extend(self.convertPackages()) else: if not has_method(cls, "decryptFile") or urls: self.logDebug("No suited decrypting method was overwritten in plugin") result = [] if has_method(cls, "decryptFile"): for f, c in content: self.setup() result.extend(to_list(self.decryptFile(c))) try: if f.startswith("tmp_"): remove(f) except : pass return result def processDecrypt(self, urls): """Catches all exceptions in decrypt methods and return results :return: Decrypting results """ try: return self._decrypt(urls) except Exception: if self.core.debug: print_exc() return [] def getLocalContent(self, urls): """Load files from disk and separate to file content and url list :param urls: :return: list of (filename, content), remote urls """ content = [] # do nothing if no decryptFile method if hasattr(self.__class__, "decryptFile"): remote = [] for url in urls: path = None if url.startswith("http"): # skip urls directly pass elif url.startswith(self.CONTENT_PREFIX): path = url elif exists(url): path = url elif exists(self.core.path(url)): path = self.core.path(url) if path: try: if path.startswith(self.CONTENT_PREFIX): content.append(("", path[len(self.CONTENT_PREFIX)])) else: f = open(fs_encode(path), "rb") content.append((f.name, f.read())) f.close() except IOError, e: self.logError("IOError", e) else: remote.append(url) #swap filtered url list urls = remote return content, urls def retry(self): """ Retry decrypting, will only work once. Somewhat deprecated method, should be avoided. """ raise Retry() def convertPackages(self): """ Deprecated """ self.logDebug("Deprecated method .convertPackages()") res = [Package(name, urls) for name, urls in self.packages] res.extend(self.urls) return res def clean(self): if hasattr(self, "req"): self.req.close() del self.req