Giter VIP home page Giter VIP logo

kodi-plugin-routing's Introduction

GitHub release Build Status License: GPLv3 Contributors

Plugin routing

Library for building and parsing URLs in Kodi plugins.

Example

import routing
from xbmcgui import ListItem
from xbmcplugin import addDirectoryItem, endOfDirectory

plugin = routing.Plugin()

@plugin.route('/')
def index():
    addDirectoryItem(plugin.handle, plugin.url_for(show_category, "one"), ListItem("Category One"), True)
    addDirectoryItem(plugin.handle, plugin.url_for(show_category, "two"), ListItem("Category Two"), True)
    addDirectoryItem(plugin.handle, plugin.url_for(show_directory, "/dir/one"), ListItem("Directory One"), True)
    addDirectoryItem(plugin.handle, plugin.url_for(show_directory, "/dir/two"), ListItem("Directory Two"), True)
    endOfDirectory(plugin.handle)

@plugin.route('/category/<category_id>')
def show_category(category_id):
    addDirectoryItem(plugin.handle, "", ListItem("Hello category %s!" % category_id))
    endOfDirectory(plugin.handle)

@plugin.route('/directory/<path:dir>')
def show_directory(dir):
    addDirectoryItem(plugin.handle, "", ListItem("List directory %s!" % dir))
    endOfDirectory(plugin.handle)

if __name__ == '__main__':
    plugin.run()

Creating rules

The route() decorator binds a function to an URL pattern. The pattern is a path expression consisting of static parts and variable parts of an URL. Variables are enclosed in angle brackets as <variable_name> and will be passed to the function as keyword arguments.

For example:

@plugin.route('/hello/<what>')
def hello(what):
    # will be called for all incoming URLs like "/hello/world", "/hello/123" etc.
    # 'what' will contain "world", "123" etc. depending on the URL.
    pass

In case your variable contains slashes (i.e. is a path or URL) and you want to match this, you can use the path identifier in the patern.

@plugin.route('/url/<path:url>')
def parse_url(url):
    # will be called for all incoming URLs like "/url/https://foo.bar/baz" etc.
    # 'url' can be any string with slashes.
    pass

Routes can also be registered manually with the add_route method.

Building URLs

url_for() can be used to build URLs for registered functions. It takes a reference to a function and a number of arguments that corresponds to variables in the URL rule.

For example:

plugin.url_for(hello, what="world")

will read the rule for hello, fill in the variable parts and return a final URL:

plugin://my.addon.id/hello/world

which can be passed to xbmcplugin.addDirectoryItem(). All variable parts must be passed to url_for either as ordered arguments or keyword arguments.

Keywords that does not occur in the function/pattern will be added as query string in the returned URL.

Alternatively, URLs can be created directly from a path with url_for_path. For example url_for_path('/foo/bar') will return plugin://my.addon.id/foo/bar. Unlike url_for this method will not check that the path is valid.

Query string

The query string part of the URL is parsed with urlparse.parse_qs and is accessible via the plugin.args attribute. The dictionary keys corresponds to query variables and values to lists of query values.

Example:

@plugin.route('/')
def index():
    url = plugin.url_for(search, query="hello world")
    addDirectoryItem(plugin.handle, url, ListItem("Search"))
    # ...


@plugin.route('/search')
def search():
    query = plugin.args['query'][0]
    addDirectoryItem(plugin.handle, "", ListItem("You searched for '%s'" % query))
    # ...

Creating a dependency in your addon

To get kodi to install this dependency you will have to add a command to your addons.xml.

    <requires>
        <import addon="xbmc.python" version="2.25.0" />
        <import addon="script.module.routing" version="0.2.0"/>
    </requires>

kodi-plugin-routing's People

Contributors

dagwieers avatar razzeee avatar rechi avatar tamland avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

kodi-plugin-routing's Issues

Can we have a new release?

We need the fix from #16 for the upcoming release of script.module.inputstreamhelper.
This is currently blocking the release.

Discussion about project governance

Given that script.module.routing is a critical piece of any add-on using it and in light of recent events I would like to discuss this project's governance. I count at least 4 active contributors which is a good foundation for a close collaboration. And the momentum is here now to make this happen.

Note that even though what happened was not directly caused by this project, in my opinion it is a symptom of governance issues.

cc @bigretromike @da3dsoul @michaelarnauts

Convert Values Retrieved from URLs

It took a bit to debug and find this.
I pass False to url_for, and it give the proper result. Then in the method that gets called, I get str('False').

if 'False':
    # this is too true for False

This doesn't need to convert fancy things, but simple int, bool, and maybe float would save a lot of annoying work sanitizing a parameter that could easily be checked for param.isnumeric() or param.lower() == 'true'.

Creating sub menu for Kodi addon?

Hi there,

Can I use this script to create a submenu for an addon? For example if I have:

@plugin.route('/')
def main_menu():
    """
    main menu 
    """
    items = [
    {
            'label': plugin.get_string(30010),
            'path': plugin.url_for('Music),
            'thumbnail': "test.jpg"},
    ]
    return items

How would I make a submenu when you click 'Music' that lists 'Rock', 'Pop', etc?

Stripping of trailing slashes breaks stuff

Related to #22 and #12

It seems the recent changes in routing regarding the stripping of slashes broke a few things. I also learned that Kodi doesn't like urls without a trailing slash when they point to a directory listing. See xbmc/xbmc#16516 (comment)
Stripping them seems like a bad idea then. They won't work when bookmarked since the last part gets stripped of if it doesn't end with a slash.

IMO, this should be the right behavior then:

  • @plugin.route('/tvshows/category/<category>/'): plugin.url_for() should include the slash
  • @plugin.route('/tvshows/category/<category>'): plugin.url_for() should not include the slash
  • a route to /tvshows/category/ should end up on the function with the @plugin.route('/tvshows/category/<category>/') decorator
  • a route to /tvshows/category should end up on the function with the @plugin.route('/tvshows/category/<category>') decorator

I guess this was the behaviour of 0.2.0.

The one thing that could be added is that:

  • a route to /tvshows/category could also end up on the function with the @plugin.route('/tvshows/category/<category>/') decorator
  • a route to /tvshows/category/ should also end up on the function with the @plugin.route('/tvshows/category/<category>') decorator

@tamland and @dagwieers what do you think?

error after upgrade from v0.2.1 to v0.2.3 in kodi

the problem occurs when i use the addon missingmovies to find new movies on disk.
it is only on folders with german characters like äÄöÖüÜß
with v.0.2.1 ther are no problems but with v0.2.3 there comes up an error.
I tried the 0.2.2 to, by manual installation, so i dont know if it is a reference, but the same error as in 0.2.3 comes up.
error_missingmovies.txt

Plugin not working

I have the following code in my main.py

import routing
plugin = routing.Plugin()

On the second line if throws the following error:

2019-11-10 12:05:07.804 T:11008 ERROR: EXCEPTION Thrown (PythonToCppException) : -->Python callback/script returned the following error<-- - NOTE: IGNORING THIS CAN LEAD TO MEMORY LEAKS! Error Type: <type 'exceptions.AttributeError'> Error Contents: 'module' object has no attribute 'Plugin' Traceback (most recent call last): File "C:\Users\Gianni\AppData\Roaming\Kodi\addons\plugin.video.yelo\routing.py", line 2, in <module> import routing File "C:\Users\Gianni\AppData\Roaming\Kodi\addons\plugin.video.yelo\routing.py", line 14, in <module> plugin = routing.Plugin() AttributeError: 'module' object has no attribute 'Plugin' -->End of Python script error report<--

I am using version 0.2.3

Is there a way to redirect one url to another ?

@routing_plugin.route('/filter/<filter_id>/group/<group_id>/')
When using .. item via kodi's webui/kore it will cut <group_id>/ , next time it will cut group/ from url.

Can I somehow redirect urls so when someone enter /filter/<filter_id>/group/ it redirect him to /filter/<filter_id>/?

Support for trailing slashes

When using Kodi built-in ActivateWindow it fails to work with URLs that has no trailing slash.

Because of this, we have to include a trailing slash, and because script.module.routing does not support trailing slashes for routes. We need to add a second identical route with a trailing slash.

Now the problem is that if you add a second route with trailing slash, that any generated URL for this route, will also include a trailing slash. And we don't really want all this.

We would rather have script.module.routing support trailing slashes for routes by default. (Or as an option)

Allow Override Static URLs

I don't know what to call this.
In most languages, types are static, and you can easily say:

@route("/menu/image/<image_id>")
def get_by_id(image_id):
    pass

@route("/menu/image/unsorted")
def get_unsorted():
    pass

image_id is clearly an int or str in this context, but when we have a rule that states that get_unsorted should be called, then it should match that exact URL rather than feeding "unsorted" as a parameter to get_by_id.
Due to the fact that rules are stored as a dict, they are not predictably sorted by nature, which makes them fast, at least. When you have a situation like the above, it will randomly work or not work. If it was FIFO, then at least you could define the route that you want to be prioritized first.

The solution I propose is to get all routes that match the url, see if one matches it exactly and use that, then somehow determine which one to use instead if it doesn't match exactly and matches multiple. Ideally, it would tell the developer somehow if there are multiple matches, so developers don't need to chase a typing and regex issue like I did to find this.

Broken in v18 with reusepython

My addon uses script.module.routing and I've found it's stopped working with the Kodi v18 alpha 2 nightlies.

I've narrowed it down to this change in Kodi:
xbmc/xbmc#13814

The current issue is the routes are not followed with this change included in Kodi when using script.module.routing. I can't quite figure out what the specific issue is, but I'm guessing it's the way sys.argv is called within the script.

pass variable parts with splash at the end

Hi, I am from Vietnam, sorry my writing English skill isn't very good.
I face a problem in this library.
when I pass an "url with slash at the end" as argument to plugin.url_for():

xbmcplugin.addDirectoryItem(plugin.handle, plugin.url_for(list_video, "http://qrt.vn/video/chuyen-muc-thoi-su/)", ListItem(subcate.string), True)

In list_video function:

@plugin.route("/listvideo/<path:url>")
def list_video(url):
     pass

now the argument url have value: "http://qrt.vn/video/chuyen-muc-thoi-su" (lost slash character at the end)

I wonder if this is a bug?

Variables are Escaped in Query, but Nowhere Else

The query strings are handled by the Python urllib urlencode(sequence) -> string and parse_qs(string) -> list(tuple). These handle escaping for you.
If you pass a variable to args or kwargs with the intention of them not going into the query, though, they are not escaped.

make_path():
args = tuple(quote_plus(str(a), '') for a in args)
...
url_kwargs = dict(((k, quote_plus(str(v), '')) for k, v in list(kwargs.items()) if k in self._keywords))

will escape in a place that reflects url_for and other important places, and

match():
...
return dict((k, unquote_plus(v)) for k, v in match.groupdict().items()) if match else None

will unescape it.

The important thing to note is that, theoretically, this could be a breaking change for some people, if they are expecting to handle that themselves. In most, if not all, cases, it should be fine, but it's better to bring it up here rather than put it in a PR to never see the light of day.

Please document <path:foo> syntax

I just switched my addon from xbmcswift2 to kodi-plugin-routing. Since the route syntax is slightly different between those 2, I promptly introduced a bug (voc/plugin.video.media-ccc-de#5).

@plugin.route('/dir/<subdir>')

in xbmcswift2 matched "/dir/foo" and "/dir/foo/bar", while in kodi-plugin-routing it only matches the first. I realized that in kodi-plugin-routing, I can achieve the same be using the route

@plugin.route('/dir/<path:subdir>')

to restore the old behaviour. Since this is mentioned nowhere in the README, please add some kind of description of this feature, so other don't fall into the same trap as I did.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.