From 1bb6ebf544b43cacf7c0755c5a8608b79b95e2d6 Mon Sep 17 00:00:00 2001 From: RaNaN Date: Sat, 7 Jan 2012 20:11:16 +0100 Subject: MultiHoster plugin type, some fixes, new documentation structure --- docs/plugins/account_plugin.rst | 5 ++ docs/plugins/base_plugin.rst | 5 ++ docs/plugins/crypter_plugin.rst | 5 ++ docs/plugins/hook_plugin.rst | 162 ++++++++++++++++++++++++++++++++++++++++ docs/plugins/hoster_plugin.rst | 102 +++++++++++++++++++++++++ docs/plugins/overview.rst | 31 ++++++++ 6 files changed, 310 insertions(+) create mode 100644 docs/plugins/account_plugin.rst create mode 100644 docs/plugins/base_plugin.rst create mode 100644 docs/plugins/crypter_plugin.rst create mode 100644 docs/plugins/hook_plugin.rst create mode 100644 docs/plugins/hoster_plugin.rst create mode 100755 docs/plugins/overview.rst (limited to 'docs/plugins') diff --git a/docs/plugins/account_plugin.rst b/docs/plugins/account_plugin.rst new file mode 100644 index 000000000..75bf61a75 --- /dev/null +++ b/docs/plugins/account_plugin.rst @@ -0,0 +1,5 @@ +.. _account_plugin: + +Account - Premium Access +======================== + diff --git a/docs/plugins/base_plugin.rst b/docs/plugins/base_plugin.rst new file mode 100644 index 000000000..62ab248ef --- /dev/null +++ b/docs/plugins/base_plugin.rst @@ -0,0 +1,5 @@ +.. _base_plugin: + +Base Plugin - And here it begins... +=================================== + diff --git a/docs/plugins/crypter_plugin.rst b/docs/plugins/crypter_plugin.rst new file mode 100644 index 000000000..d910ec412 --- /dev/null +++ b/docs/plugins/crypter_plugin.rst @@ -0,0 +1,5 @@ +.. _crypter_plugin: + +Crypter - Extract links from pages +================================== + diff --git a/docs/plugins/hook_plugin.rst b/docs/plugins/hook_plugin.rst new file mode 100644 index 000000000..e263ece2e --- /dev/null +++ b/docs/plugins/hook_plugin.rst @@ -0,0 +1,162 @@ +.. _write_hooks: + +Hook - Do everything you want +============================= + +A Hook is a python file which is located at :file:`module/plugins/hooks`. +The :class:`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 `, +do something complete autonomic and has full access to the :class:`Api ` and every detail of pyLoad. +The UpdateManager, CaptchaTrader, UnRar and many more are realised as hooks. + +Hook header +----------- + +Your hook needs to subclass :class:`Hook ` and will inherit all of its method, make sure to check its documentation! + +All Hooks should start with something like this: :: + + from module.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. Dont't overwrite the ``init`` method if not neccesary, 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 places. + +The easiest way is to overwrite specific methods defined by the :class:`Hook ` base class. +The name is indicating when the function gets called. +See :class:`Hook ` page for a complete listing. + +You should be aware of the arguments the Hooks are called with, whether its a :class:`PyFile ` +or :class:`PyPackage ` you should read its related documentation to know how to access her great power and manipulate them. + +A basic excerpt would look like: :: + + from module.plugins.Hook import Hook + + class YourHook(Hook): + """ + Your Hook code here. + """ + + def coreReady(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 kind of longer processing tasks. + +Another and more flexible and powerful way is to use 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 `. Keep in mind that you can define own events and other people may listen on them. + +For your convenience it's possible to register listeners automatical 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 module.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.addEvent("name", function)`, `self.manager.removeEvent("name", function)` and see doc for +:class:`HookManager `. Contrary to ``event_map``, ``function`` has to be a reference +and **not** a `string`. + +We introduced events because it scales better if there a a huge amount of events and hooks. So all future interaction will be exclusive +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 noticed that pyLoad has an :class:`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 module.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 ` 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 `. + +Just store everything in ``self.info``. :: + + from module.plugins.Hook import Hook + + class YourHook(Hook): + """ + Your Hook code here. + """ + + def setup(self): + self.info = {"running": False} + + def coreReady(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:`module/plugins/hooks` and you will find plenty examples there. diff --git a/docs/plugins/hoster_plugin.rst b/docs/plugins/hoster_plugin.rst new file mode 100644 index 000000000..59f35f5cb --- /dev/null +++ b/docs/plugins/hoster_plugin.rst @@ -0,0 +1,102 @@ +.. _hoster_plugin: + +Hoster - Load files to disk +=========================== + +A Plugin is a python file located at one of the subfolders in :file:`module/plugins/`. Either :file:`hoster`, :file:`crypter` +or :file:`container`, depending of it's type. + +There are three kinds of different plugins: **Hoster**, **Crypter**, **Container**. +All kind of plugins inherit from the base :class:`Plugin `. You should know its +convenient methods, they make your work easier ;-) + +Every plugin defines a ``__pattern__`` and when the user adds urls, every url is matched against the pattern defined in +the plugin. In case the ``__pattern__`` matched on the url the plugin will be assigned to handle it and instanciated when +pyLoad begins to download/decrypt the url. + +Plugin header +------------- + +How basic hoster plugin header could look like: :: + + from module.plugin.Hoster import Hoster + + class MyFileHoster(Hoster): + __version__ = "0.1" + __pattern__ = r"http://myfilehoster.example.com/file_id/[0-9]+" + __config__ = [] + +You have to define these meta-data, ``__pattern__`` has to be a regexp that sucessfully compiles with +``re.compile(__pattern__)``. + +Just like :ref:`write_hooks` you can add and use config values exatly the same way. +If you want a Crypter or Container plugin, just replace the word Hoster with your desired plugin type. + + +Hoster plugins +-------------- + +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 module.plugin.Hoster import Hoster + + class MyFileHoster(Hoster): + """ + plugin code + """ + + 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 ` class, since an instance of it is given as parameter to every pyfile. +Some tasks your plugin should handle: proof if file is online, get filename, wait if needed, download the file, etc.. + +Wait times +__________ + +Some hoster 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. + +Captcha decrypting +__________________ + +To handle captcha input just use ``self.decryptCaptcha(url, ...)``, it will be send to clients +or handled by :class:`Hook ` plugins + +Crypter +------- + +What about Decrypter and Container plugins? +Well, they work nearly the same, only that the function they have to provide is named ``decrypt`` + +Example: :: + + from module.plugin.Crypter import Crypter + + class MyFileCrypter(Crypter): + """ + plugin code + """ + def decrypt(self, pyfile): + + urls = ["http://get.pyload.org/src", "http://get.pyload.org/debian", "http://get.pyload.org/win"] + + self.packages.append(("pyLoad packages", urls, "pyLoad packages")) # urls list of urls + +They can access all the methods from :class:`Plugin `, but the important thing is they +have to append all packages they parsed to the `self.packages` list. Simply append tuples with `(name, urls, folder)`, +where urls is the list of urls contained in the packages. Thats all of your work, pyLoad will know what to do with them. + +Examples +-------- + +Best examples are already existing plugins in :file:`module/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..23913b787 --- /dev/null +++ b/docs/plugins/overview.rst @@ -0,0 +1,31 @@ +.. _overview: + +================ +Extending pyLoad +================ + +.. pull-quote:: + Any sufficiently advanced technology is indistinguishable from magic. + + -- Arthur C. Clarke + + +.. rubric:: Motivation + +pyLoad offers an comfortable and powerful plugin system to make extending 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 overwiew about the +conceptual part. You should not left out the `Base` part, since it contains basic functionality for all plugins types. + +.. rubric:: Contents + +.. toctree:: + + base_plugin.rst + crypter_plugin.rst + hoster_plugin.rst + account_plugin.rst + hook_plugin.rst + + + +.. rubric:: Footnotes \ No newline at end of file -- cgit v1.2.3 From 9ebdb33dcd859e02e02aff628922047c865e7124 Mon Sep 17 00:00:00 2001 From: RaNaN Date: Mon, 9 Jan 2012 21:29:18 +0100 Subject: updated docs --- docs/plugins/hook_plugin.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/plugins') diff --git a/docs/plugins/hook_plugin.rst b/docs/plugins/hook_plugin.rst index e263ece2e..be1097057 100644 --- a/docs/plugins/hook_plugin.rst +++ b/docs/plugins/hook_plugin.rst @@ -1,6 +1,6 @@ .. _write_hooks: -Hook - Do everything you want +Hook - Do whatever you want ============================= A Hook is a python file which is located at :file:`module/plugins/hooks`. -- cgit v1.2.3 From c654f31efc548957f10e3f4c1a5060dcea1dcdec Mon Sep 17 00:00:00 2001 From: RaNaN Date: Tue, 10 Jan 2012 00:04:24 +0100 Subject: changed HEAD request --- docs/plugins/base_plugin.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'docs/plugins') diff --git a/docs/plugins/base_plugin.rst b/docs/plugins/base_plugin.rst index 62ab248ef..4ffe2e457 100644 --- a/docs/plugins/base_plugin.rst +++ b/docs/plugins/base_plugin.rst @@ -3,3 +3,22 @@ Base Plugin - And here it begins... =================================== + +Meta Data +--------- + + +Config Entries +-------------- + + +Tagging Guidelines +------------------ + + +Basic Methods +------------- + +Debugging +--------- + -- cgit v1.2.3 From 180a9ee57a6f4eaa5f4bdd7a272057231f6a5c88 Mon Sep 17 00:00:00 2001 From: RaNaN Date: Sun, 29 Jan 2012 20:09:11 +0100 Subject: doc for base plugin --- docs/plugins/base_plugin.rst | 84 +++++++++++++++++++++++++++++++++++++++++ docs/plugins/crypter_plugin.rst | 20 ++++++++++ docs/plugins/hoster_plugin.rst | 62 +++--------------------------- docs/plugins/overview.rst | 2 +- 4 files changed, 111 insertions(+), 57 deletions(-) (limited to 'docs/plugins') diff --git a/docs/plugins/base_plugin.rst b/docs/plugins/base_plugin.rst index 4ffe2e457..e3c72994b 100644 --- a/docs/plugins/base_plugin.rst +++ b/docs/plugins/base_plugin.rst @@ -3,22 +3,106 @@ Base Plugin - And here it begins... =================================== +A Plugin in pyLoad is a python file located at one of the subfolders in :file:`module/plugins/`. +All different plugin types inherit from :class:`Base `, which defines basic methods +and meta data. You should read this section carefully, because it's the base for all plugin development. +After that it is a good idea to look at several already existing plugin to get a more detailed idea of how +they have to look like and whats 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 acceptible values can be found in :class:`Base ` source code. +Non needed 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 repo. + +You don't need to subclass :class:`Base ` directly, but the +intermediate type according to your plugin. As example we choose an Hoster plugin, but the same is true for all +plugin types. + +For localization pyLoad supports gettext [1]_, to mark strings for translation surround them with ``_("...")``. + +How basic hoster plugin header could look like:: + + from module.plugin.Hoster import Hoster + + class MyFileHoster(Hoster): + __version__ = "0.1" + __description__ = _("Short description of the plugin") + __long_description = _("""A even longer description + is not needed for hoster plugin, + but hook plugin should have it so the user knows what they doing.""") Config Entries -------------- +Every plugin is allowed to add entries to the config. These are defined via ``__config__`` and consists +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 setted 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 uses of interaction with user +free A hoster without any premium service +premium_only A hoster only useable 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 `. To note some important ones: + +The pyload core instance is accessible at ``self.core`` attribute +and the :class:`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 persistantly 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 ``-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 it what you are expecting. + +For further debugging you can install ipython [2]_, and set breakpoints with ``self.core.breakpoint()``. +It will open the python debugger [3]_ and pause the plugin thread. +To open a ipython shell in the running programm use ``self.shell()``. +These methods are usefull to gain access to the code flow at runtime and check or modify variables. + + +.. rubric:: Footnotes +.. [1] http://docs.python.org/library/gettext.html +.. [2] http://ipython.org/ +.. [3] 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 index d910ec412..639d58abf 100644 --- a/docs/plugins/crypter_plugin.rst +++ b/docs/plugins/crypter_plugin.rst @@ -3,3 +3,23 @@ Crypter - Extract links from pages ================================== +What about Decrypter and Container plugins? +Well, they work nearly the same, only that the function they have to provide is named ``decrypt`` + +Example: :: + + from module.plugin.Crypter import Crypter + + class MyFileCrypter(Crypter): + """ + plugin code + """ + def decrypt(self, pyfile): + + urls = ["http://get.pyload.org/src", "http://get.pyload.org/debian", "http://get.pyload.org/win"] + + self.packages.append(("pyLoad packages", urls, "pyLoad packages")) # urls list of urls + +They can access all the methods from :class:`Plugin `, but the important thing is they +have to append all packages they parsed to the `self.packages` list. Simply append tuples with `(name, urls, folder)`, +where urls is the list of urls contained in the packages. Thats all of your work, pyLoad will know what to do with them. diff --git a/docs/plugins/hoster_plugin.rst b/docs/plugins/hoster_plugin.rst index 59f35f5cb..e4575a001 100644 --- a/docs/plugins/hoster_plugin.rst +++ b/docs/plugins/hoster_plugin.rst @@ -3,39 +3,6 @@ Hoster - Load files to disk =========================== -A Plugin is a python file located at one of the subfolders in :file:`module/plugins/`. Either :file:`hoster`, :file:`crypter` -or :file:`container`, depending of it's type. - -There are three kinds of different plugins: **Hoster**, **Crypter**, **Container**. -All kind of plugins inherit from the base :class:`Plugin `. You should know its -convenient methods, they make your work easier ;-) - -Every plugin defines a ``__pattern__`` and when the user adds urls, every url is matched against the pattern defined in -the plugin. In case the ``__pattern__`` matched on the url the plugin will be assigned to handle it and instanciated when -pyLoad begins to download/decrypt the url. - -Plugin header -------------- - -How basic hoster plugin header could look like: :: - - from module.plugin.Hoster import Hoster - - class MyFileHoster(Hoster): - __version__ = "0.1" - __pattern__ = r"http://myfilehoster.example.com/file_id/[0-9]+" - __config__ = [] - -You have to define these meta-data, ``__pattern__`` has to be a regexp that sucessfully compiles with -``re.compile(__pattern__)``. - -Just like :ref:`write_hooks` you can add and use config values exatly the same way. -If you want a Crypter or Container plugin, just replace the word Hoster with your desired plugin type. - - -Hoster plugins --------------- - 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 :: @@ -61,40 +28,23 @@ You need to know about the :class:`PyFile ` class, since a Some tasks your plugin should handle: proof if file is online, get filename, wait if needed, download the file, etc.. Wait times -__________ +---------- Some hoster 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. Captcha decrypting -__________________ +------------------ To handle captcha input just use ``self.decryptCaptcha(url, ...)``, it will be send to clients or handled by :class:`Hook ` plugins -Crypter -------- - -What about Decrypter and Container plugins? -Well, they work nearly the same, only that the function they have to provide is named ``decrypt`` +User interaction +---------------- -Example: :: - - from module.plugin.Crypter import Crypter - - class MyFileCrypter(Crypter): - """ - plugin code - """ - def decrypt(self, pyfile): - - urls = ["http://get.pyload.org/src", "http://get.pyload.org/debian", "http://get.pyload.org/win"] - - self.packages.append(("pyLoad packages", urls, "pyLoad packages")) # urls list of urls +Testing +------- -They can access all the methods from :class:`Plugin `, but the important thing is they -have to append all packages they parsed to the `self.packages` list. Simply append tuples with `(name, urls, folder)`, -where urls is the list of urls contained in the packages. Thats all of your work, pyLoad will know what to do with them. Examples -------- diff --git a/docs/plugins/overview.rst b/docs/plugins/overview.rst index 23913b787..68ad96dfd 100755 --- a/docs/plugins/overview.rst +++ b/docs/plugins/overview.rst @@ -14,7 +14,7 @@ Extending pyLoad pyLoad offers an comfortable and powerful plugin system to make extending 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 overwiew about the -conceptual part. You should not left out the `Base` part, since it contains basic functionality for all plugins types. +conceptual part. You should not left out the :doc:`Base ` part, since it contains basic functionality for all plugins types. .. rubric:: Contents -- cgit v1.2.3 From 7df4718276a12b7f19a73d3b789c791d57bf4342 Mon Sep 17 00:00:00 2001 From: RaNaN Date: Sun, 29 Jan 2012 22:57:41 +0100 Subject: doc for crypter plugin --- docs/plugins/base_plugin.rst | 19 ++++++++++----- docs/plugins/crypter_plugin.rst | 54 ++++++++++++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 18 deletions(-) (limited to 'docs/plugins') diff --git a/docs/plugins/base_plugin.rst b/docs/plugins/base_plugin.rst index e3c72994b..1849f3986 100644 --- a/docs/plugins/base_plugin.rst +++ b/docs/plugins/base_plugin.rst @@ -17,11 +17,15 @@ An overview of acceptible values can be found in :class:`Base ` and :class:`Crypter ` +needs to have a specific regexp [1]_ ``__pattern__``. This will be matched against input urls and if a suited +plugin is found it is selected to handle the url. + You don't need to subclass :class:`Base ` directly, but the intermediate type according to your plugin. As example we choose an Hoster plugin, but the same is true for all plugin types. -For localization pyLoad supports gettext [1]_, to mark strings for translation surround them with ``_("...")``. +For localization pyLoad supports gettext [2]_, to mark strings for translation surround them with ``_("...")``. How basic hoster plugin header could look like:: @@ -34,6 +38,8 @@ How basic hoster plugin header could look like:: is not needed for hoster plugin, but hook plugin should have it so the user knows what they doing.""") +In future examples the meta data will be left out, but remember it's required in every plugin! + Config Entries -------------- @@ -96,13 +102,14 @@ To enable debugging functionality start pyLoad with ``-d`` option or enable it i 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 it what you are expecting. -For further debugging you can install ipython [2]_, and set breakpoints with ``self.core.breakpoint()``. -It will open the python debugger [3]_ and pause the plugin thread. +For further debugging you can install ipython [3]_, and set breakpoints with ``self.core.breakpoint()``. +It will open the python debugger [4]_ and pause the plugin thread. To open a ipython shell in the running programm use ``self.shell()``. These methods are usefull to gain access to the code flow at runtime and check or modify variables. .. rubric:: Footnotes -.. [1] http://docs.python.org/library/gettext.html -.. [2] http://ipython.org/ -.. [3] http://docs.python.org/library/pdb.html \ No newline at end of file +.. [1] http://docs.python.org/library/re.html +.. [2] http://docs.python.org/library/gettext.html +.. [3] http://ipython.org/ +.. [4] 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 index 639d58abf..1497ced07 100644 --- a/docs/plugins/crypter_plugin.rst +++ b/docs/plugins/crypter_plugin.rst @@ -3,23 +3,53 @@ Crypter - Extract links from pages ================================== -What about Decrypter and Container plugins? -Well, they work nearly the same, only that the function they have to provide is named ``decrypt`` - -Example: :: +We are starting with the simplest plugin, the :class:`Crypter `. +It's job is it to take a url as input and generate 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 `. :: from module.plugin.Crypter import Crypter class MyFileCrypter(Crypter): - """ - plugin code - """ - def decrypt(self, pyfile): + __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 usefull 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 module.plugin.Crypter import Crypter, Package + + class MyFileCrypter(Crypter): + + def decryptURL(self, url): + + html = self.load(url) + + # .decrypt_from_content is only a 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 little side-note if you want to use decrypter in +your code you can use:: + + plugin = self.core.pluginManager.loadClass("crypter", "NameOfThePlugin") + # decrypted will be a list of urls + decrypted = plugin.decrypt(urls) - self.packages.append(("pyLoad packages", urls, "pyLoad packages")) # urls list of urls +Testing +------- -They can access all the methods from :class:`Plugin `, but the important thing is they -have to append all packages they parsed to the `self.packages` list. Simply append tuples with `(name, urls, folder)`, -where urls is the list of urls contained in the packages. Thats all of your work, pyLoad will know what to do with them. +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 -- cgit v1.2.3 From da4cf026ad116518fefc3429b74a8cd94aeef73f Mon Sep 17 00:00:00 2001 From: RaNaN Date: Sun, 5 Feb 2012 17:27:13 +0100 Subject: updated documentation + diagrams --- docs/plugins/base_plugin.rst | 24 +++++++++++++----------- docs/plugins/crypter_plugin.rst | 18 ++++++++++++++++-- docs/plugins/hoster_plugin.rst | 5 +++++ docs/plugins/overview.rst | 5 ++++- 4 files changed, 38 insertions(+), 14 deletions(-) (limited to 'docs/plugins') diff --git a/docs/plugins/base_plugin.rst b/docs/plugins/base_plugin.rst index 1849f3986..911f5d429 100644 --- a/docs/plugins/base_plugin.rst +++ b/docs/plugins/base_plugin.rst @@ -5,8 +5,9 @@ Base Plugin - And here it begins... A Plugin in pyLoad is a python file located at one of the subfolders in :file:`module/plugins/`. All different plugin types inherit from :class:`Base `, which defines basic methods -and meta data. You should read this section carefully, because it's the base for all plugin development. -After that it is a good idea to look at several already existing plugin to get a more detailed idea of how +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 whats possible with them. Meta Data @@ -18,15 +19,15 @@ Non needed attributes can be left out, except ``__version__``. Nevertheless plea as you can, when you want to submit your plugin to the public repo. Additionally :class:`Crypter ` and :class:`Crypter ` -needs to have a specific regexp [1]_ ``__pattern__``. This will be matched against input urls and if a suited +needs to have a specific regexp [2]_ ``__pattern__``. This will be matched against input urls 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 ` directly, but the intermediate type according to your plugin. As example we choose an Hoster plugin, but the same is true for all plugin types. -For localization pyLoad supports gettext [2]_, to mark strings for translation surround them with ``_("...")``. - How basic hoster plugin header could look like:: from module.plugin.Hoster import Hoster @@ -102,14 +103,15 @@ To enable debugging functionality start pyLoad with ``-d`` option or enable it i 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 it what you are expecting. -For further debugging you can install ipython [3]_, and set breakpoints with ``self.core.breakpoint()``. -It will open the python debugger [4]_ and pause the plugin thread. +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 usefull to gain access to the code flow at runtime and check or modify variables. .. rubric:: Footnotes -.. [1] http://docs.python.org/library/re.html -.. [2] http://docs.python.org/library/gettext.html -.. [3] http://ipython.org/ -.. [4] http://docs.python.org/library/pdb.html \ No newline at end of file +.. [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 index 1497ced07..4e7803808 100644 --- a/docs/plugins/crypter_plugin.rst +++ b/docs/plugins/crypter_plugin.rst @@ -19,7 +19,7 @@ target urls and subclass from :class:`Crypter `. 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 usefull when it's possible to handle a bunch of urls +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 @@ -44,8 +44,22 @@ And that's basically all you need to know. Just as little side-note if you want 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(urls) + decrypted = plugin.decrypt(core, urls) + + +SimpleCrypter +------------- + +For simple crypter services there is the :class:`SimpleCrypter ` class which handles most of the workflow. Only the regexp +pattern have to be defined. + +Exmaple:: + + from module.plugins.internal.SimpleCrypter import SimpleCrypter + + class MyFileCrypter(SimpleCrypter): Testing ------- diff --git a/docs/plugins/hoster_plugin.rst b/docs/plugins/hoster_plugin.rst index e4575a001..ee112b570 100644 --- a/docs/plugins/hoster_plugin.rst +++ b/docs/plugins/hoster_plugin.rst @@ -42,6 +42,11 @@ or handled by :class:`Hook ` plugins User interaction ---------------- + +SimpleHoster +------------ + + Testing ------- diff --git a/docs/plugins/overview.rst b/docs/plugins/overview.rst index 68ad96dfd..0388db7e2 100755 --- a/docs/plugins/overview.rst +++ b/docs/plugins/overview.rst @@ -15,6 +15,7 @@ Extending pyLoad pyLoad offers an comfortable and powerful plugin system to make extending 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 overwiew about the conceptual part. You should not left out the :doc:`Base ` part, since it contains basic functionality for all plugins types. +A class diagram visualizing the relationship can be find below [1]_ .. rubric:: Contents @@ -28,4 +29,6 @@ conceptual part. You should not left out the :doc:`Base ` part, sin -.. rubric:: Footnotes \ No newline at end of file +.. rubric:: Footnotes + +.. [1] :ref:`plugin_hierarchy` \ No newline at end of file -- cgit v1.2.3 From 4df2b77fdf42046fe19bd371be7c7255986b5980 Mon Sep 17 00:00:00 2001 From: RaNaN Date: Tue, 6 Mar 2012 13:36:39 +0100 Subject: renamed hooks to addons, new filemanager and database, many new api methods you will loose ALL your LINKS, webinterface will NOT work --- docs/plugins/addon_plugin.rst | 162 ++++++++++++++++++++++++++++++++++++++++++ docs/plugins/hook_plugin.rst | 162 ------------------------------------------ docs/plugins/overview.rst | 2 +- 3 files changed, 163 insertions(+), 163 deletions(-) create mode 100644 docs/plugins/addon_plugin.rst delete mode 100644 docs/plugins/hook_plugin.rst (limited to 'docs/plugins') diff --git a/docs/plugins/addon_plugin.rst b/docs/plugins/addon_plugin.rst new file mode 100644 index 000000000..57c7e4a96 --- /dev/null +++ b/docs/plugins/addon_plugin.rst @@ -0,0 +1,162 @@ +.. _write_addons: + +Addon - Add new functionality +============================= + +A Hook is a python file which is located at :file:`module/plugins/hooks`. +The :class:`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 `, +do something complete autonomic and has full access to the :class:`Api ` and every detail of pyLoad. +The UpdateManager, CaptchaTrader, UnRar and many more are realised as hooks. + +Hook header +----------- + +Your hook needs to subclass :class:`Hook ` and will inherit all of its method, make sure to check its documentation! + +All Hooks should start with something like this: :: + + from module.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. Dont't overwrite the ``init`` method if not neccesary, 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 places. + +The easiest way is to overwrite specific methods defined by the :class:`Hook ` base class. +The name is indicating when the function gets called. +See :class:`Hook ` page for a complete listing. + +You should be aware of the arguments the Hooks are called with, whether its a :class:`PyFile ` +or :class:`PyPackage ` you should read its related documentation to know how to access her great power and manipulate them. + +A basic excerpt would look like: :: + + from module.plugins.Hook import Hook + + class YourHook(Hook): + """ + Your Hook code here. + """ + + def coreReady(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 kind of longer processing tasks. + +Another and more flexible and powerful way is to use 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 `. Keep in mind that you can define own events and other people may listen on them. + +For your convenience it's possible to register listeners automatical 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 module.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.addEvent("name", function)`, `self.manager.removeEvent("name", function)` and see doc for +:class:`HookManager `. Contrary to ``event_map``, ``function`` has to be a reference +and **not** a `string`. + +We introduced events because it scales better if there a a huge amount of events and hooks. So all future interaction will be exclusive +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 noticed that pyLoad has an :class:`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 module.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 ` 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 `. + +Just store everything in ``self.info``. :: + + from module.plugins.Hook import Hook + + class YourHook(Hook): + """ + Your Hook code here. + """ + + def setup(self): + self.info = {"running": False} + + def coreReady(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:`module/plugins/hooks` and you will find plenty examples there. diff --git a/docs/plugins/hook_plugin.rst b/docs/plugins/hook_plugin.rst deleted file mode 100644 index be1097057..000000000 --- a/docs/plugins/hook_plugin.rst +++ /dev/null @@ -1,162 +0,0 @@ -.. _write_hooks: - -Hook - Do whatever you want -============================= - -A Hook is a python file which is located at :file:`module/plugins/hooks`. -The :class:`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 `, -do something complete autonomic and has full access to the :class:`Api ` and every detail of pyLoad. -The UpdateManager, CaptchaTrader, UnRar and many more are realised as hooks. - -Hook header ------------ - -Your hook needs to subclass :class:`Hook ` and will inherit all of its method, make sure to check its documentation! - -All Hooks should start with something like this: :: - - from module.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. Dont't overwrite the ``init`` method if not neccesary, 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 places. - -The easiest way is to overwrite specific methods defined by the :class:`Hook ` base class. -The name is indicating when the function gets called. -See :class:`Hook ` page for a complete listing. - -You should be aware of the arguments the Hooks are called with, whether its a :class:`PyFile ` -or :class:`PyPackage ` you should read its related documentation to know how to access her great power and manipulate them. - -A basic excerpt would look like: :: - - from module.plugins.Hook import Hook - - class YourHook(Hook): - """ - Your Hook code here. - """ - - def coreReady(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 kind of longer processing tasks. - -Another and more flexible and powerful way is to use 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 `. Keep in mind that you can define own events and other people may listen on them. - -For your convenience it's possible to register listeners automatical 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 module.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.addEvent("name", function)`, `self.manager.removeEvent("name", function)` and see doc for -:class:`HookManager `. Contrary to ``event_map``, ``function`` has to be a reference -and **not** a `string`. - -We introduced events because it scales better if there a a huge amount of events and hooks. So all future interaction will be exclusive -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 noticed that pyLoad has an :class:`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 module.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 ` 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 `. - -Just store everything in ``self.info``. :: - - from module.plugins.Hook import Hook - - class YourHook(Hook): - """ - Your Hook code here. - """ - - def setup(self): - self.info = {"running": False} - - def coreReady(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:`module/plugins/hooks` and you will find plenty examples there. diff --git a/docs/plugins/overview.rst b/docs/plugins/overview.rst index 0388db7e2..70db5ac90 100755 --- a/docs/plugins/overview.rst +++ b/docs/plugins/overview.rst @@ -25,7 +25,7 @@ A class diagram visualizing the relationship can be find below [1]_ crypter_plugin.rst hoster_plugin.rst account_plugin.rst - hook_plugin.rst + addon_plugin.rst -- cgit v1.2.3 From 5a7f415a25d8e036a37851fcd5e9be81caae2804 Mon Sep 17 00:00:00 2001 From: X3n0m0rph59 Date: Sun, 22 Apr 2012 16:54:06 +0200 Subject: Fixed spelling in the documentation --- docs/plugins/addon_plugin.rst | 33 +++++++++++++++++---------------- docs/plugins/base_plugin.rst | 36 ++++++++++++++++++------------------ docs/plugins/crypter_plugin.rst | 8 ++++---- docs/plugins/hoster_plugin.rst | 8 ++++---- docs/plugins/overview.rst | 8 ++++---- 5 files changed, 47 insertions(+), 46 deletions(-) (limited to 'docs/plugins') diff --git a/docs/plugins/addon_plugin.rst b/docs/plugins/addon_plugin.rst index 57c7e4a96..c2258f2aa 100644 --- a/docs/plugins/addon_plugin.rst +++ b/docs/plugins/addon_plugin.rst @@ -6,15 +6,15 @@ Addon - Add new functionality A Hook is a python file which is located at :file:`module/plugins/hooks`. The :class:`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 `, -do something complete autonomic and has full access to the :class:`Api ` and every detail of pyLoad. -The UpdateManager, CaptchaTrader, UnRar and many more are realised as hooks. +do something completely autonomic and has full access to the :class:`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 ` and will inherit all of its method, make sure to check its documentation! +Your hook needs to subclass :class:`Hook ` and will inherit all of its methods, so make sure to check it's documentation! -All Hooks should start with something like this: :: +All hooks should start with something like this: :: from module.plugins.Hook import Hook @@ -28,7 +28,7 @@ All Hooks should start with something like this: :: __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. Dont't overwrite the ``init`` method if not neccesary, use ``setup`` instead. +hook on and off. Don't overwrite the ``init`` method if not necessary, use ``setup`` instead. Using the Config ---------------- @@ -41,16 +41,16 @@ When everything went right you can access the config values with ``self.getConfi Interacting on Events --------------------- -The next step is to think about where your Hook action takes places. +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 ` base class. The name is indicating when the function gets called. See :class:`Hook ` page for a complete listing. -You should be aware of the arguments the Hooks are called with, whether its a :class:`PyFile ` -or :class:`PyPackage ` you should read its related documentation to know how to access her great power and manipulate them. +You should be aware of the arguments the hooks are called with, whether its a :class:`PyFile ` +or :class:`PyPackage ` you should read the relevant documentation to know how to access it's great power and manipulate them. -A basic excerpt would look like: :: +What a basic excerpt would look like: :: from module.plugins.Hook import Hook @@ -66,13 +66,13 @@ A basic excerpt would look like: :: 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 kind of longer processing tasks. +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 event listener. +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 `. Keep in mind that you can define own events and other people may listen on them. +at :class:`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 automatical via the ``event_map`` attribute. +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 module.plugins.Hook import Hook @@ -99,14 +99,15 @@ Use `self.manager.addEvent("name", function)`, `self.manager.removeEvent("name", :class:`HookManager `. Contrary to ``event_map``, ``function`` has to be a reference and **not** a `string`. -We introduced events because it scales better if there a a huge amount of events and hooks. So all future interaction will be exclusive +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 +Providing + RPC services ---------------------- -You may noticed that pyLoad has an :class:`Api `, which can be used internal or called by clients via RPC. +You may have noticed that pyLoad has an :class:`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: :: diff --git a/docs/plugins/base_plugin.rst b/docs/plugins/base_plugin.rst index 911f5d429..91a6eef44 100644 --- a/docs/plugins/base_plugin.rst +++ b/docs/plugins/base_plugin.rst @@ -8,43 +8,43 @@ All different plugin types inherit from :class:`Base ` 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 whats possible with them. +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 acceptible values can be found in :class:`Base ` source code. -Non needed 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 repo. +An overview of acceptable values can be found in :class:`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 ` and :class:`Crypter ` -needs to have a specific regexp [2]_ ``__pattern__``. This will be matched against input urls and if a suited +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 ` directly, but the -intermediate type according to your plugin. As example we choose an Hoster plugin, but the same is true for all +intermediate type according to your plugin. As an example we choose a hoster plugin, but the same is true for all plugin types. -How basic hoster plugin header could look like:: +How a basic hoster plugin header could look like:: from module.plugin.Hoster import Hoster class MyFileHoster(Hoster): __version__ = "0.1" __description__ = _("Short description of the plugin") - __long_description = _("""A even longer description - is not needed for hoster plugin, - but hook plugin should have it so the user knows what they doing.""") + __long_description = _("""An even longer description + is not needed for hoster plugins, + but the hook plugin should have it, so the users know what they are 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 config. These are defined via ``__config__`` and consists +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)``. @@ -56,7 +56,7 @@ Example from Youtube plugin:: (".mp4", "bool", _("Allow .mp4"), True)] -At runtime the desired config values can be retrieved with ``self.getConfig(name)`` and setted with +At runtime the desired config values can be retrieved with ``self.getConfig(name)`` and set with ``self.setConfig(name, value)``. Tagging Guidelines @@ -72,9 +72,9 @@ 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 uses of interaction with user +interaction A plugin that makes use of interaction with the user free A hoster without any premium service -premium_only A hoster only useable with account +premium_only A hoster only usable with account ip_check A hoster that checks ip, that can be avoided with reconnect =============== =========================================================== @@ -89,7 +89,7 @@ and the :class:`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 persistantly into the database. +Use ``self.store(...)`` and ``self.retrieve(...)`` to store data persistently into the database. Make use of ``logInfo, logError, logWarning, logDebug`` for logging purposes. @@ -98,15 +98,15 @@ 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 ``-d`` option or enable it in the config. +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 it what you are expecting. +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 usefull to gain access to the code flow at runtime and check or modify variables. +These methods are useful to gain access to the code flow at runtime and check or modify variables. .. rubric:: Footnotes diff --git a/docs/plugins/crypter_plugin.rst b/docs/plugins/crypter_plugin.rst index 4e7803808..8c54dccb1 100644 --- a/docs/plugins/crypter_plugin.rst +++ b/docs/plugins/crypter_plugin.rst @@ -4,7 +4,7 @@ Crypter - Extract links from pages ================================== We are starting with the simplest plugin, the :class:`Crypter `. -It's job is it to take a url as input and generate new package or links, for example by filtering the urls or +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 `. :: @@ -36,11 +36,11 @@ create new Packages if needed by instantiating a :class:`Package` instance, whic html = self.load(url) - # .decrypt_from_content is only a example method here and will return a list of urls + # .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 little side-note if you want to use decrypter in +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") @@ -53,7 +53,7 @@ SimpleCrypter ------------- For simple crypter services there is the :class:`SimpleCrypter ` class which handles most of the workflow. Only the regexp -pattern have to be defined. +pattern has to be defined. Exmaple:: diff --git a/docs/plugins/hoster_plugin.rst b/docs/plugins/hoster_plugin.rst index ee112b570..b7546a313 100644 --- a/docs/plugins/hoster_plugin.rst +++ b/docs/plugins/hoster_plugin.rst @@ -24,13 +24,13 @@ An example ``process`` function could look like this :: # download the file, destination is determined by pyLoad self.download(parsed_url) -You need to know about the :class:`PyFile ` class, since an instance of it is given as parameter to every pyfile. -Some tasks your plugin should handle: proof if file is online, get filename, wait if needed, download the file, etc.. +You need to know about the :class:`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.. Wait times ---------- -Some hoster require you to wait a specific time. Just set the time with ``self.setWait(seconds)`` or +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. Captcha decrypting @@ -54,4 +54,4 @@ Testing Examples -------- -Best examples are already existing plugins in :file:`module/plugins/`. \ No newline at end of file +The best examples are the already existing plugins in :file:`module/plugins/`. \ No newline at end of file diff --git a/docs/plugins/overview.rst b/docs/plugins/overview.rst index 70db5ac90..b3debb053 100755 --- a/docs/plugins/overview.rst +++ b/docs/plugins/overview.rst @@ -12,10 +12,10 @@ Extending pyLoad .. rubric:: Motivation -pyLoad offers an comfortable and powerful plugin system to make extending 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 overwiew about the -conceptual part. You should not left out the :doc:`Base ` part, since it contains basic functionality for all plugins types. -A class diagram visualizing the relationship can be find below [1]_ +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 ` part, since it contains basic functionality for all plugin types. +A class diagram visualizing the relationship can be found below [1]_ .. rubric:: Contents -- cgit v1.2.3 From e8eaa91da9e1236d8009df0d79bebe023de8933f Mon Sep 17 00:00:00 2001 From: RaNaN Date: Sun, 6 May 2012 13:04:15 +0200 Subject: little documentation update --- docs/plugins/account_plugin.rst | 6 ++++++ docs/plugins/base_plugin.rst | 6 +++--- docs/plugins/hoster_plugin.rst | 14 +++++++------- docs/plugins/overview.rst | 1 - 4 files changed, 16 insertions(+), 11 deletions(-) (limited to 'docs/plugins') diff --git a/docs/plugins/account_plugin.rst b/docs/plugins/account_plugin.rst index 75bf61a75..e683f1604 100644 --- a/docs/plugins/account_plugin.rst +++ b/docs/plugins/account_plugin.rst @@ -3,3 +3,9 @@ Account - Premium Access ======================== +Example +------- + +MultiHoster +----------- + diff --git a/docs/plugins/base_plugin.rst b/docs/plugins/base_plugin.rst index 91a6eef44..f6813cf40 100644 --- a/docs/plugins/base_plugin.rst +++ b/docs/plugins/base_plugin.rst @@ -66,9 +66,9 @@ To categorize a plugin, a list of keywords can be assigned via ``__tags__`` attr 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 @@ -76,7 +76,7 @@ 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 ------------- diff --git a/docs/plugins/hoster_plugin.rst b/docs/plugins/hoster_plugin.rst index b7546a313..9cd99a1f5 100644 --- a/docs/plugins/hoster_plugin.rst +++ b/docs/plugins/hoster_plugin.rst @@ -13,6 +13,9 @@ An example ``process`` function could look like this :: """ plugin code """ + + def setup(): + #TODO def process(self, pyfile): html = self.load(pyfile.url) # load the content of the orginal pyfile.url to html @@ -27,21 +30,18 @@ An example ``process`` function could look like this :: You need to know about the :class:`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.. -Wait times +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. -Captcha decrypting ------------------- - To handle captcha input just use ``self.decryptCaptcha(url, ...)``, it will be send to clients -or handled by :class:`Hook ` plugins +or handled by :class:`Addon ` plugins -User interaction ----------------- +Online status fetching +---------------------- SimpleHoster ------------ diff --git a/docs/plugins/overview.rst b/docs/plugins/overview.rst index b3debb053..bbea86756 100755 --- a/docs/plugins/overview.rst +++ b/docs/plugins/overview.rst @@ -28,7 +28,6 @@ A class diagram visualizing the relationship can be found below [1]_ addon_plugin.rst - .. rubric:: Footnotes .. [1] :ref:`plugin_hierarchy` \ No newline at end of file -- cgit v1.2.3