summaryrefslogtreecommitdiffstats
path: root/docs/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'docs/plugins')
-rw-r--r--docs/plugins/account_plugin.rst11
-rw-r--r--docs/plugins/addon_plugin.rst163
-rw-r--r--docs/plugins/base_plugin.rst117
-rw-r--r--docs/plugins/crypter_plugin.rst69
-rw-r--r--docs/plugins/hoster_plugin.rst57
-rwxr-xr-xdocs/plugins/overview.rst33
6 files changed, 450 insertions, 0 deletions
diff --git a/docs/plugins/account_plugin.rst b/docs/plugins/account_plugin.rst
new file mode 100644
index 000000000..e683f1604
--- /dev/null
+++ b/docs/plugins/account_plugin.rst
@@ -0,0 +1,11 @@
+.. _account_plugin:
+
+Account - Premium Access
+========================
+
+Example
+-------
+
+MultiHoster
+-----------
+
diff --git a/docs/plugins/addon_plugin.rst b/docs/plugins/addon_plugin.rst
new file mode 100644
index 000000000..8f1adf39a
--- /dev/null
+++ b/docs/plugins/addon_plugin.rst
@@ -0,0 +1,163 @@
+.. _write_addons:
+
+Addon - Add new functionality
+=============================
+
+A Hook is a python file which is located at :file:`pyload/plugins/hooks`.
+The :class:`HookManager <pyload.HookManager.HookManager>` will load it automatically on startup. Only one instance exists
+over the complete lifetime of pyload. Your hook can interact on various events called by the :class:`HookManager <pyload.HookManager.HookManager>`,
+do something completely autonomic and has full access to the :class:`Api <pyload.Api.Api>` and every detail of pyLoad.
+The UpdateManager, CaptchaTrader, UnRar and many more are implemented as hooks.
+
+Hook header
+-----------
+
+Your hook needs to subclass :class:`Hook <pyload.plugins.Hook.Hook>` and will inherit all of its methods, so make sure to check it's documentation!
+
+All hooks should start with something like this: ::
+
+ from pyload.plugins.Hook import Hook
+
+ class YourHook(Hook):
+ __name__ = "YourHook"
+ __version__ = "0.1"
+ __description__ = "Does really cool stuff"
+ __config__ = [ ("activated" , "bool" , "Activated" , "True" ) ]
+ __threaded__ = ["downloadFinished"]
+ __author_name__ = ("Me")
+ __author_mail__ = ("me@has-no-mail.com")
+
+All meta-data is defined in the header, you need at least one option at ``__config__`` so the user can toggle your
+hook on and off. Don't overwrite the ``init`` method if not necessary, use ``setup`` instead.
+
+Using the Config
+----------------
+
+We are taking a closer look at the ``__config__`` parameter.
+You can add more config values as desired by adding tuples of the following format to the config list: ``("name", "type", "description", "default value")``.
+When everything went right you can access the config values with ``self.getConfig(name)`` and ``self.setConfig(name,value``.
+
+
+Interacting on Events
+---------------------
+
+The next step is to think about where your Hook action takes place.
+
+The easiest way is to overwrite specific methods defined by the :class:`Hook <pyload.plugins.Hook.Hook>` base class.
+The name is indicating when the function gets called.
+See :class:`Hook <pyload.plugins.Hook.Hook>` page for a complete listing.
+
+You should be aware of the arguments the hooks are called with, whether its a :class:`PyFile <pyload.PyFile.PyFile>`
+or :class:`PyPackage <pyload.PyPackage.PyPackage>` you should read the relevant documentation to know how to access it's great power and manipulate them.
+
+What a basic excerpt would look like: ::
+
+ from pyload.plugins.Hook import Hook
+
+ class YourHook(Hook):
+ """
+ Your Hook code here.
+ """
+
+ def activate(self):
+ print "Yay, the core is ready let's do some work."
+
+ def downloadFinished(self, pyfile):
+ print "A Download just finished."
+
+Another important feature to mention can be seen at the ``__threaded__`` parameter. Function names listed will be executed
+in a thread, in order to not block the main thread. This should be used for all kinds of long lived processing tasks.
+
+Another and more flexible and powerful way is to use the event listener.
+All hook methods exists as event and very useful additional events are dispatched by the core. For a little overview look
+at :class:`HookManager <pyload.HookManager.HookManager>`. Keep in mind that you can define your own events and other people may listen on them.
+
+For your convenience it's possible to register listeners automatically via the ``event_map`` attribute.
+It requires a `dict` that maps event names to function names or a `list` of function names. It's important that all names are strings ::
+
+ from pyload.plugins.Hook import Hook
+
+ class YourHook(Hook):
+ """
+ Your Hook code here.
+ """
+ event_map = {"downloadFinished" : "doSomeWork",
+ "allDownloadsFnished": "someMethod",
+ "coreReady": "initialize"}
+
+ def initialize(self):
+ print "Initialized."
+
+ def doSomeWork(self, pyfile):
+ print "This is equivalent to the above example."
+
+ def someMethod(self):
+ print "The underlying event (allDownloadsFinished) for this method is not available through the base class"
+
+An advantage of the event listener is that you are able to register and remove the listeners at runtime.
+Use `self.manager.listenTo("name", function)`, `self.manager.removeEvent("name", function)` and see doc for
+:class:`HookManager <pyload.HookManager.HookManager>`. Contrary to ``event_map``, ``function`` has to be a reference
+and **not** a `string`.
+
+We introduced events because it scales better if there is a huge amount of events and hooks. So all future interactions will be exclusively
+available as event and not accessible through overwriting hook methods. However you can safely do this, it will not be removed and is easier to implement.
+
+
+Providing
+ RPC services
+----------------------
+
+You may have noticed that pyLoad has an :class:`Api <pyload.Api.Api>`, which can be used internal or called by clients via RPC.
+So probably clients want to be able to interact with your hook to request it's state or invoke some action.
+
+Sounds complicated but is very easy to do. Just use the ``Expose`` decorator: ::
+
+ from pyload.plugins.Hook import Hook, Expose
+
+ class YourHook(Hook):
+ """
+ Your Hook code here.
+ """
+
+ @Expose
+ def invoke(self, arg):
+ print "Invoked with", arg
+
+Thats all, it's available via the :class:`Api <pyload.Api.Api>` now. If you want to use it read :ref:`access_api`.
+Here is a basic example: ::
+
+ #Assuming client is a ThriftClient or Api object
+
+ print client.getServices()
+ print client.call(ServiceCall("YourHook", "invoke", "an argument"))
+
+Providing status information
+----------------------------
+Your hook can store information in a ``dict`` that can easily be retrievied via the :class:`Api <pyload.Api.Api>`.
+
+Just store everything in ``self.info``. ::
+
+ from pyload.plugins.Hook import Hook
+
+ class YourHook(Hook):
+ """
+ Your Hook code here.
+ """
+
+ def setup(self):
+ self.info = {"running": False}
+
+ def activate(self):
+ self.info["running"] = True
+
+Usable with: ::
+
+ #Assuming client is a ThriftClient or Api object
+
+ print client.getAllInfo()
+
+Example
+-------
+ Sorry but you won't find an example here ;-)
+
+ Look at :file:`pyload/plugins/hooks` and you will find plenty examples there.
diff --git a/docs/plugins/base_plugin.rst b/docs/plugins/base_plugin.rst
new file mode 100644
index 000000000..5fa110fe7
--- /dev/null
+++ b/docs/plugins/base_plugin.rst
@@ -0,0 +1,117 @@
+.. _base_plugin:
+
+Base Plugin - And here it begins...
+===================================
+
+A Plugin in pyLoad is a python file located at one of the subfolders in :file:`pyload/plugins/`.
+All different plugin types inherit from :class:`Base <pyload.plugins.Base.Base>`, which defines basic methods
+and meta data. You should read this section carefully, because it's the base for all plugin development. It
+is also a good idea to look at the class diagram [1]_ for all plugin types to get an overview.
+At last you should look at several already existing plugin to get a more detailed idea of how
+they have to look like and what is possible with them.
+
+Meta Data
+---------
+
+All important data which must be known by pyLoad is set using class attributes pre- and suffixed with ``__``.
+An overview of acceptable values can be found in :class:`Base <pyload.plugins.Base.Base>` source code.
+Unneeded attributes can be left out, except ``__version__``. Nevertheless please fill out most information
+as you can, when you want to submit your plugin to the public repository.
+
+Additionally :class:`Crypter <pyload.plugins.Crypter.Crypter>` and :class:`Hoster <pyload.plugins.Hoster.Hoster>`
+needs to have a specific regexp [2]_ ``__pattern__``. This will be matched against input url's and if a suited
+plugin is found it is selected to handle the url.
+
+For localization pyLoad supports gettext [3]_, to mark strings for translation surround them with ``_("...")``.
+
+You don't need to subclass :class:`Base <pyload.plugins.Base.Base>` directly, but the
+intermediate type according to your plugin. As an example we choose a hoster plugin, but the same is true for all
+plugin types.
+
+How a basic hoster plugin header could look like::
+
+ from pyload.plugin.Hoster import Hoster
+
+ class MyFileHoster(Hoster):
+ __version__ = "0.1"
+ __description__ = _("Short description of the plugin")
+ __long_description = _("""An even longer description
+ is not needed for hoster plugins,
+ but an addon plugin should have it, so the users know what it is doing.""")
+
+In future examples the meta data will be left out, but remember it's required in every plugin!
+
+Config Entries
+--------------
+
+Every plugin is allowed to add entries to the configuration. These are defined via ``__config__`` and consist
+of a list with tuples in the format of ``(name, type, verbose_name, default_value)`` or
+``(name, type, verbose_name, short_description, default_value)``.
+
+Example from Youtube plugin::
+
+ class YoutubeCom:
+ __config__ = [("quality", "sd;hd;fullhd", _("Quality Setting"), "hd"),
+ ("fmt", "int", _("FMT Number 0-45"), _("Desired FMT number, look them up at wikipedia"), 0),
+ (".mp4", "bool", _("Allow .mp4"), True)]
+
+
+At runtime the desired config values can be retrieved with ``self.getConfig(name)`` and set with
+``self.setConfig(name, value)``.
+
+Tagging Guidelines
+------------------
+
+To categorize a plugin, a list of keywords can be assigned via ``__tags__`` attribute. You may add arbitrary
+tags as you like, but please look at this table first to choose your tags. With standardised keywords we can generate
+a better overview of the plugins and provide some search criteria.
+
+=============== =================================================================
+Keyword Meaning
+=============== =================================================================
+image Anything related to image(hoster)
+video Anything related to video(hoster)
+captcha A plugin that needs captcha decrypting
+interaction A plugin that makes use of interaction with the user
+free A hoster without any premium service
+premium_only A hoster only usable with account
+ip_check A hoster that checks ip, that can be avoided with reconnect
+=============== =================================================================
+
+Basic Methods
+-------------
+
+All methods can be looked up at :class:`Base <pyload.plugins.Base.Base>`. To note some important ones:
+
+The pyload core instance is accessible at ``self.core`` attribute
+and the :class:`Api <pyload.Api.Api>` at ``self.core.api``
+
+With ``self.load(...)`` you can load any url and get the result. This method is only available to Hoster and Crypter.
+For other plugins use ``getURL(...)`` or ``getRequest()``.
+
+Use ``self.store(...)`` and ``self.retrieve(...)`` to store data persistently into the database.
+
+Make use of ``logInfo, logError, logWarning, logDebug`` for logging purposes.
+
+Debugging
+---------
+
+One of the most important aspects in software programming is debugging. It is especially important
+for plugins which heavily rely on external input, which is true for all hoster and crypter plugins.
+To enable debugging functionality start pyLoad with the ``-d`` option or enable it in the config.
+
+You should use ``self.logDebug(msg)`` when ever it is reasonable. It is a good pratice to log server output
+or the calculation of results and then check in the log if it really is what you are expecting.
+
+For further debugging you can install ipython [4]_, and set breakpoints with ``self.core.breakpoint()``.
+It will open the python debugger [5]_ and pause the plugin thread.
+To open a ipython shell in the running programm use ``self.shell()``.
+These methods are useful to gain access to the code flow at runtime and check or modify variables.
+
+
+.. rubric:: Footnotes
+.. [1] :ref:`plugin_hierarchy`
+.. [2] http://docs.python.org/library/re.html
+.. [3] http://docs.python.org/library/gettext.html
+.. [4] http://ipython.org/
+.. [5] http://docs.python.org/library/pdb.html \ No newline at end of file
diff --git a/docs/plugins/crypter_plugin.rst b/docs/plugins/crypter_plugin.rst
new file mode 100644
index 000000000..b10dd27f9
--- /dev/null
+++ b/docs/plugins/crypter_plugin.rst
@@ -0,0 +1,69 @@
+.. _crypter_plugin:
+
+Crypter - Extract links from pages
+==================================
+
+We are starting with the simplest plugin, the :class:`Crypter <pyload.plugins.Crypter.Crypter>`.
+It's job is to take an url as input and generate a new package or links, for example by filtering the urls or
+loading a page and extracting links from the html code. You need to define the ``__pattern__`` to match
+target urls and subclass from :class:`Crypter <pyload.plugins.Crypter.Crypter>`. ::
+
+ from pyload.plugin.Crypter import Crypter
+
+ class MyFileCrypter(Crypter):
+ __pattern__ = r"mycrypter.com/id/([0-9]+)"
+
+ def decryptURL(self, url):
+
+ urls = ["http://get.pyload.org/src", "http://get.pyload.org/debian", "http://get.pyload.org/win"]
+ return urls
+
+You have to overwrite at least one of ``.decryptFile``, ``.decryptURL``, ``.decryptURLs``. The first one
+is only useful for container files, whereas the last is useful when it's possible to handle a bunch of urls
+at once. If in doubt, just overwrite `decryptURL`.
+
+Generating Packages
+-------------------
+
+When finished with decrypting just return the urls as list and they will be added to the package. You can also
+create new Packages if needed by instantiating a :class:`Package` instance, which will look like the following::
+
+ from pyload.plugin.Crypter import Crypter, Package
+
+ class MyFileCrypter(Crypter):
+
+ def decryptURL(self, url):
+
+ html = self.load(url)
+
+ # .decrypt_from_content is only an example method here and will return a list of urls
+ urls = self.decrypt_from_content(html)
+ return Package("my new package", urls)
+
+And that's basically all you need to know. Just as a little side-note if you want to use decrypter in
+your code you can use::
+
+ plugin = self.core.pluginManager.loadClass("crypter", "NameOfThePlugin")
+ # Core instance is needed for decrypting
+ # decrypted will be a list of urls
+ decrypted = plugin.decrypt(core, urls)
+
+
+SimpleCrypter
+-------------
+
+For simple crypter services there is the :class:`SimpleCrypter <pyload.plugins.internal.SimpleCrypter.SimpleCrypter>` class which handles most of the workflow. Only the regexp
+pattern has to be defined.
+
+Exmaple::
+
+ from pyload.plugins.internal.SimpleCrypter import SimpleCrypter
+
+ class MyFileCrypter(SimpleCrypter):
+
+Testing
+-------
+
+Please append a test link at :file:`tests/crypterlinks.txt` followed by `||xy`, where xy is the number of
+expected links/packages to extract.
+Our testrunner will be able to check your plugin periodical for functionality. \ No newline at end of file
diff --git a/docs/plugins/hoster_plugin.rst b/docs/plugins/hoster_plugin.rst
new file mode 100644
index 000000000..55a973463
--- /dev/null
+++ b/docs/plugins/hoster_plugin.rst
@@ -0,0 +1,57 @@
+.. _hoster_plugin:
+
+Hoster - Load files to disk
+===========================
+
+We head to the next important section, the ``process`` method of your plugin.
+In fact the ``process`` method is the only functionality your plugin has to provide, but its always a good idea to split up tasks to not produce spaghetti code.
+An example ``process`` function could look like this ::
+
+ from pyload.plugin.Hoster import Hoster
+
+ class MyFileHoster(Hoster):
+ """
+ plugin code
+ """
+
+ def setup():
+ #TODO
+
+ def process(self, pyfile):
+ html = self.load(pyfile.url) # load the content of the orginal pyfile.url to html
+
+ # parse the name from the site and set attribute in pyfile
+ pyfile.name = self.myFunctionToParseTheName(html)
+ parsed_url = self.myFunctionToParseUrl(html)
+
+ # download the file, destination is determined by pyLoad
+ self.download(parsed_url)
+
+You need to know about the :class:`PyFile <pyload.PyFile.PyFile>` class, since an instance of it is given as a parameter to every pyfile.
+Some tasks your plugin should handle: check if the file is online, get filename, wait if needed, download the file, etc..
+
+Common Tasks
+----------
+
+Some hosters require you to wait a specific time. Just set the time with ``self.setWait(seconds)`` or
+``self.setWait(seconds, True)`` if you want pyLoad to perform a reconnect if needed.
+
+To handle captcha input just use ``self.decryptCaptcha(url, ...)``, it will be send to clients
+or handled by :class:`Addon <pyload.plugins.Addon.Addon>` plugins
+
+
+Online status fetching
+----------------------
+
+SimpleHoster
+------------
+
+
+Testing
+-------
+
+
+Examples
+--------
+
+The best examples are the already existing plugins in :file:`pyload/plugins/`. \ No newline at end of file
diff --git a/docs/plugins/overview.rst b/docs/plugins/overview.rst
new file mode 100755
index 000000000..bbea86756
--- /dev/null
+++ b/docs/plugins/overview.rst
@@ -0,0 +1,33 @@
+.. _overview:
+
+================
+Extending pyLoad
+================
+
+.. pull-quote::
+ Any sufficiently advanced technology is indistinguishable from magic.
+
+ -- Arthur C. Clarke
+
+
+.. rubric:: Motivation
+
+pyLoad offers a comfortable and powerful plugin system to make extensions possible. With it you only need to have some
+python knowledge and can just start right away writing your own plugins. This document gives you an overview about the
+conceptual part. You should not leave out the :doc:`Base <base_plugin>` part, since it contains basic functionality for all plugin types.
+A class diagram visualizing the relationship can be found below [1]_
+
+.. rubric:: Contents
+
+.. toctree::
+
+ base_plugin.rst
+ crypter_plugin.rst
+ hoster_plugin.rst
+ account_plugin.rst
+ addon_plugin.rst
+
+
+.. rubric:: Footnotes
+
+.. [1] :ref:`plugin_hierarchy` \ No newline at end of file