summaryrefslogtreecommitdiffstats
path: root/pyload/plugins/Crypter.py
diff options
context:
space:
mode:
Diffstat (limited to 'pyload/plugins/Crypter.py')
-rw-r--r--pyload/plugins/Crypter.py286
1 files changed, 286 insertions, 0 deletions
diff --git a/pyload/plugins/Crypter.py b/pyload/plugins/Crypter.py
new file mode 100644
index 000000000..af3d5aba7
--- /dev/null
+++ b/pyload/plugins/Crypter.py
@@ -0,0 +1,286 @@
+# -*- coding: utf-8 -*-
+
+from pyload.Api import LinkStatus, DownloadStatus as DS
+from pyload.utils import to_list, has_method, uniqify
+from pyload.utils.fs import exists, remove, fs_encode
+from Base import Base, Retry
+
+
+class Package:
+ """ Container that indicates that a new package should be created """
+
+ def __init__(self, name=None, links=None):
+ self.name = name
+ self.links = []
+
+ if links:
+ self.addLinks(links)
+
+ # nested packages
+ self.packs = []
+
+ def addLinks(self, links):
+ """ Add one or multiple links to the package
+
+ :param links: One or list of urls or :class:`LinkStatus`
+ """
+ links = to_list(links)
+ for link in links:
+ if not isinstance(link, LinkStatus):
+ link = LinkStatus(link, link, -1, DS.Queued)
+
+ self.links.append(link)
+
+ def addPackage(self, pack):
+ self.packs.append(pack)
+
+ def getURLs(self):
+ return [link.url for link in self.links]
+
+ def getAllURLs(self):
+ urls = self.getURLs()
+ 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.links == other.links
+
+ def __repr__(self):
+ return u"<CrypterPackage name=%s, links=[%s], packs=%s" % (self.name, ",".join(str(l) for l in self.links),
+ self.packs)
+
+ def __hash__(self):
+ return hash(self.name) ^ hash(frozenset(self.links)) ^ 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 = None
+ 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:"
+
+ #: Optional name of an account plugin that should be used, but does not guarantee that one is available
+ USE_ACCOUNT = None
+
+ #: When True this crypter will not be decrypted directly and queued one by one.
+ # Needed for crypter that can't run in parallel or need to wait between decryption.
+ QUEUE_DECRYPT = False
+
+ @classmethod
+ def decrypt(cls, core, url_or_urls, password=None):
+ """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
+ :param password: optional password used for decrypting
+
+ :raises Exception: No decryption errors are cascaded
+ :return: List of decrypted urls, all package info removed
+ """
+ urls = to_list(url_or_urls)
+ p = cls(core, password)
+ try:
+ result = p._decrypt(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)
+
+ # TODO: pass user to crypter
+ # TODO: crypter could not only know url, but also the name and size
+ def __init__(self, core, password=None):
+ Base.__init__(self, core)
+
+ self.req = None
+ # load account if set
+ if self.USE_ACCOUNT:
+ self.account = self.core.accountManager.selectAccount(self.USE_ACCOUNT, self.user)
+ if self.account:
+ self.req = self.account.getAccountRequest()
+
+ if self.req is None:
+ self.req = core.requestFactory.getRequest()
+
+ #: Password supplied by user
+ self.password = password
+
+ # 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 _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)
+ result = []
+
+ if urls and has_method(cls, "decrypt"):
+ self.logDebug("Deprecated .decrypt() method in Crypter plugin")
+ result = []
+ for url in urls:
+ self.pyfile = PyFileMockup(url)
+ self.setup()
+ self.decrypt(self.pyfile)
+ result.extend(self.convertPackages())
+ elif urls:
+ method = True
+ try:
+ self.setup()
+ result = to_list(self.decryptURLs(urls))
+ except NotImplementedError:
+ method = False
+
+ # this will raise error if not implemented
+ if not method:
+ for url in urls:
+ self.setup()
+ result.extend(to_list(self.decryptURL(url)))
+
+ for f, c in content:
+ self.setup()
+ result.extend(to_list(self.decryptFile(c)))
+ try:
+ if f.startswith("tmp_"): remove(f)
+ except IOError:
+ self.logWarning(_("Could not remove file '%s'") % f)
+ self.core.print_exc()
+
+ return result
+
+ 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 \ No newline at end of file