Kodi Community Forum

Full Version: SimplePlugin micro-framework for Kodi content plugins
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
SimplePlugin micro-framework simplifies creating content plugins for Kodi. It was inspired by xbmcswift2 framework for the same purpose and has similar features like defining Kodi virtual folder contents using lists of dictionaries with item properties, persistent storage and cache decorator for functions.
Unfortunately, xbmcswift2 seems to have been abandoned by its author and does not support the newest features of the Kodi plugin API, so I've decided to create my own plugin micro-framework. Initially, it was used in my private projects, but now I've decided to make a public release.

A minimal example:
PHP Code:
from simpleplugin import Plugin

plugin 
Plugin()

# Free video sample is provided by www.vidsplay.com

# Define action via decorator
@plugin.action()
def root(params):
    
"""Root virtual folder"""
    
# Create 1-item list with a link to subfolder item
    
return [{'label''Subfolder',
            
'url'plugin.get_url(action='subfolder')}]


@
plugin.action()
def subfolder(params):
    
"""Virtual subfolder"""
    
# Create 1-item list with a link to a playable video.
    
return [{'label''Ocean Birds',
            
'thumb''http://www.vidsplay.com/vids/ocean_birds.jpg',
            
'url'plugin.get_url(action='play'url='http://www.vidsplay.com/vids/ocean_birds.mp4'),
            
'is_playable'True}]


@
plugin.action()
def play(params):
    
"""Play video"""
    
# Return a string containing a playable video URL
    
return params['url']


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

You will find more detailed info about SimplePlugin in the project documentation.

Links:

The latest release: https://github.com/romanvm/script.module...ses/latest
Source code: https://github.com/romanvm/script.module.simpleplugin
An example SimplePlugin-based video plugin: https://github.com/romanvm/plugin.video....in.example

License: GPL v.3.
Good evening.
When you plan to update this add-on in the official repositories?
This is the very first post in this topic for more than a year, and you are like the 2nd or 3rd person who has asked me anything about this library via different channels. So I guess this library is used only by a couple of people except me and, to my knowledge, there are no addons depending on it in the official repo. So I have no plans to maintain it in the official repo and, in fact, want to remove that outdated version which is still there.

Those who want to use this library in their addons can download the latest version from GitHub.
In developing my addon, I would like to use addons from the official add-ons repository. To reduce the amount of redundant code, I decided to take your add-on. It's not difficult to add an addition to your repository.
Or you can advise some similar add-ons from the official repository?
The forum language is English only please
(2017-02-24, 16:49)DarrenHill Wrote: [ -> ]The forum language is English only please
Message text changed on English
There was xbmcswift2 library but it is seriously outdated and, I guess, incompatible with the current Kodi Python API. Actually, we have discussed this internally and decided that it's better not to bloat the official repo with niche addon libraries that aren't really used by anybody other than their developers themselves.

As for SimplePlugin, it is a single file with no third-party dependencies, only the Standard Library and Kodi Python API, so you can just drop simpleplugin.py in your addon.
I think better, it's include this add-on in my repository.
Thanks for the answer
Does simpleplugin have support for displaying progress bars? (e.g. of a long download)
(2017-03-02, 21:54)ventolin Wrote: [ -> ]Does simpleplugin have support for displaying progress bars? (e.g. of a long download)

You don't need any third-party library for that. xbmcgui.DialogProgress and xbmcgui.DialogProgressBG work just fine.

SimplePlugin, as the name implies, is targeted primarily for content plugins. UI and user interaction are completely different stuff. And I'd recommend to read SimplePlugin documentation that describes in details what it actually can do.
Cool, I was just asking in case it provided a nice wrapper for that functionality. Thanks!
Let's say we have a plugin, "plugin.music.my_plugin", and in its addon_data directory, there's another directory containing MP3s called "some_directory".

The following code will display a single ListItem on the root menu which, when selected, will display the media files in the directory:

Code:
PLUGIN_NAME = 'plugin.music.my_plugin'

@plugin.action()
def root(params):
    return [
        {'label': 'test', 'url': 'special://profile/addon_data/{}/some_directory/'.format(PLUGIN_NAME)}
    ]

Now, let's say we want to execute some code when the ListItem is selected and then *redirect* the user to this directory, displaying whatever media it contains. How is this best done?

I've tried it by simply returning the url string from the action function:

Code:
PLUGIN_NAME = 'plugin.music.my_plugin'

@plugin.action()
def root(params):
    return [
        {'label': 'test', 'url': plugin.get_url(action='do_stuff')}
    ]

@plugin.action()
def do_stuff(params):
    # do some stuff here
    #
    return 'special://profile/addon_data/{}/some_directory/'.format(PLUGIN_NAME)

However, this doesn't work - instead, an empty list is displayed. Does simpleplugin have a "redirect" function or something similar? Or is there another way to do what I'm trying to do?
(2017-03-11, 18:46)ventolin Wrote: [ -> ]However, this doesn't work - instead, an empty list is displayed. Does simpleplugin have a "redirect" function or something similar? Or is there another way to do what I'm trying to do?

There is no hidden stuff or secret recipes. Everything is in the documentation.

Only playback actions can return strings that must contain paths to media files to play. If you want to use an URL in Kodi VFS notation, you need to either set it as 'url' property of a list item or use ActivateWindow built-in function.
Hi,

I want to use this module, but I'm having a problem.
Code:
# -*- coding: utf-8 -*-
#

from __future__ import unicode_literals
import xbmcgui, xbmcplugin
import socket, pickle, struct
from simpleplugin import Plugin, debug_exception

plugin = Plugin()

# Free video sample is provided by www.vidsplay.com

@plugin.action()
def root():
    """
    Root virtual folder

    This action is mandatory.
    """
    plugin.log_debug("Got till here...")

    my_dict = {"path" : xbmc.getInfoLabel('ListItem.Path').decode("utf-8"), "filename" : xbmc.getInfoLabel('ListItem.FileName').decode("utf-8")}
    plugin.log_debug("my_dict = %s" % my_dict)
        
    if my_dict["path"] != "":
        s = socket.socket()
        host = 'localhost' # needs to be in quote
        port = 1247
        s.connect((host, port))
        data = pickle.dumps(my_dict)
        datasize = len(data)
        s.send(struct.pack("<i", datasize))
        s.send(data)
        s.close()
    else:
        # We didn't get Listitem infolabels, abort
        error_dialog = xbmcgui.Dialog()
        error_dialog.notification('Bluray Iso Utils', 'Failed to get ListItems info!', xbmcgui.NOTIFICATION_ERROR, 5000)

    plugin.log_debug("And got till here...")

if __name__ == '__main__':
    with debug_exception(plugin.log_debug):
        plugin.run()

Above is the code I'm using. I get the following in the kodi.log:
Code:
03:05:50.185 T:6724   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: <Plugin ['plugin://script.service.bluray_iso_utils/', '1', '?hello world']>
03:05:50.185 T:6724   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: Actions: ['root']
03:05:50.185 T:6724   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: Called action "root" with params "<Params {}>"
03:05:50.185 T:6724   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: Got till here...
03:05:50.185 T:6724   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: my_dict = {u'path': u'F:\\Over_te_copieren\\_test_strm\\', u'filename': u'test1.strm'}
03:05:50.192 T:6724   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: And got till here...
03:05:50.192 T:6724   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: Action return value: None
03:05:50.192 T:6724   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: The action "root" has not returned any valid data to process.
03:05:50.193 T:6724    INFO: CPythonInvoker(4, C:\Users\Max Renn\AppData\Roaming\Kodi\addons\script.service.bluray_iso_utils\mytestscript.py): script successfully run
03:05:50.202 T:6724    INFO: Python script stopped
03:05:50.202 T:6724   DEBUG: Thread LanguageInvoker 6724 terminating
03:05:50.222 T:2196   DEBUG: ------ Window Init (DialogBusy.xml) ------
03:05:50.222 T:6660   DEBUG: Thread scriptobs 6660 terminating
03:05:50.222 T:2196   ERROR: Playlist Player: skipping unplayable item: 0, path [plugin://script.service.bluray_iso_utils/?hello world]
03:05:50.222 T:2196   DEBUG: Keyboard: scancode: 0x1c, sym: 0x000d, unicode: 0x0000, modifier: 0x0
03:05:50.222 T:5328   DEBUG: Thread BackgroundLoader start, auto delete: false
03:05:50.225 T:2196   DEBUG: ------ Window Deinit (DialogBusy.xml) ------
03:05:50.226 T:2196   DEBUG: ------ Window Init (DialogNotification.xml) ------
03:05:50.229 T:5328   DEBUG: Thread BackgroundLoader 5328 terminating
03:05:51.093 T:2196   DEBUG: ------ Window Init (Pointer.xml) ------
03:05:51.744 T:7544   DEBUG: Version Check: Already notified one time for upgrading.
03:05:51.744 T:7544    INFO: CPythonInvoker(0, C:\Users\Max Renn\AppData\Roaming\Kodi\addons\service.xbmc.versioncheck\service.py): script successfully run

my_dict gets send to the server, so that works.

But I want to suppress the : ''Playlist" ' Can't find a next item to play' notification dialog.

How do I do this? I read the documentation (at: http://romanvm.github.io/script.module.s.../misc.html ), but I'm a bit slow today because I can't get it to work...
Code:
# -*- coding: utf-8 -*-
#

from __future__ import unicode_literals
import xbmcgui, xbmcplugin
import socket, pickle, struct
from simpleplugin import Plugin, debug_exception

plugin = Plugin()

# Free video sample is provided by www.vidsplay.com

@plugin.action()
def root():
    """
    Root virtual folder

    This action is mandatory.
    """
    plugin.log_debug("Got till here...")

    return [{'is_folder': False, 'url': plugin.get_url(action='my_play')}]

@plugin.action()
def my_play():
    my_dict = {"path" : xbmc.getInfoLabel('ListItem.Path').decode("utf-8"), "filename" : xbmc.getInfoLabel('ListItem.FileName').decode("utf-8")}
    plugin.log_debug("my_dict = %s" % my_dict)
        
    if my_dict["path"] != "":
        s = socket.socket()
        host = 'localhost' # needs to be in quote
        port = 1247
        s.connect((host, port))
        data = pickle.dumps(my_dict)
        datasize = len(data)
        s.send(struct.pack("<i", datasize))
        s.send(data)
        s.close()
    else:
        # We didn't get Listitem infolabels, abort
        error_dialog = xbmcgui.Dialog()
        error_dialog.notification('Bluray Iso Utils', 'Failed to get ListItems info!', xbmcgui.NOTIFICATION_ERROR, 5000)

    plugin.log_debug("And got till here...")

if __name__ == '__main__':
    with debug_exception(plugin.log_debug):
        plugin.run()

Here, I even never enter the my_play function. I'm getting this in kodi.log:
Code:
03:25:50.596 T:4212   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: <Plugin ['plugin://script.service.bluray_iso_utils/', '1', '?hello world']>
03:25:50.597 T:4212   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: Actions: ['my_play', 'root']
03:25:50.597 T:4212   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: Called action "root" with params "<Params {}>"
03:25:50.597 T:4212   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: Got till here...
03:25:50.598 T:4212   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: Action return value: [{u'url': 'plugin://script.service.bluray_iso_utils/?action=my_play', u'is_folder': False}]
03:25:50.598 T:4212   DEBUG: script.service.bluray_iso_utils [v.0.5.0]: Creating listing from ListContext(listing=[{u'url': 'plugin://script.service.bluray_iso_utils/?action=my_play', u'is_folder': False}], succeeded=True, update_listing=False, cache_to_disk=False, sort_methods=None, view_mode=None, content=None, category=None)
03:25:50.649 T:6860   DEBUG: ------ Window Init (DialogBusy.xml) ------
03:25:50.650 T:4212    INFO: CPythonInvoker(4, C:\Users\Max Renn\AppData\Roaming\Kodi\addons\script.service.bluray_iso_utils\mytestscript.py): script successfully run
03:25:50.665 T:4212    INFO: Python script stopped
03:25:50.665 T:4212   DEBUG: Thread LanguageInvoker 4212 terminating
03:25:50.666 T:6860   DEBUG: Loading settings for
03:25:50.666 T:6860   DEBUG: CPlayerCoreFactory::GetPlayers()
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: considering rule: system rules
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: matches rule: system rules
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: considering rule: mms/udp
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: considering rule: lastfm/shout
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: considering rule: rtmp
03:25:50.667 T:7332   DEBUG: Thread scriptobs 7332 terminating
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: considering rule: rtsp
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: considering rule: streams
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: considering rule: dvd
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: considering rule: dvdimage
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: considering rule: sdp/asf
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: considering rule: nsv
03:25:50.667 T:6860   DEBUG: CPlayerSelectionRule::GetPlayers: considering rule: radio
03:25:50.667 T:6860   DEBUG: CPlayerCoreFactory::GetPlayers: matched 0 rules with players
03:25:50.667 T:6860   DEBUG: CPlayerCoreFactory::GetPlayers: adding videodefaultplayer (VideoPlayer)
03:25:50.667 T:6860   DEBUG: CPlayerCoreFactory::GetPlayers: for video=1, audio=0
03:25:50.667 T:6860   DEBUG: CPlayerCoreFactory::GetPlayers: for video=1, audio=1
03:25:50.667 T:6860   DEBUG: CPlayerCoreFactory::GetPlayers: added 1 players
03:25:50.669 T:6860   DEBUG: Radio UECP (RDS) Processor - new CDVDRadioRDSData::CDVDRadioRDSData
03:25:50.669 T:6860  NOTICE: VideoPlayer: Opening:
03:25:50.669 T:6860 WARNING: CDVDMessageQueue(player)::Put MSGQ_NOT_INITIALIZED
03:25:50.669 T:6860   ERROR: DXVA::CProcessorHD::IsFormatSupported: Unsupported format 104 for 1.
03:25:50.669 T:6860   ERROR: DXVA::CProcessorHD::IsFormatSupported: Unsupported format 105 for 1.
03:25:50.669 T:2160   DEBUG: Thread VideoPlayer start, auto delete: false
03:25:50.669 T:2160  NOTICE: Creating InputStream
03:25:50.669 T:2160   ERROR: CVideoPlayer::OpenInputStream - error opening []
03:25:50.669 T:2160  NOTICE: CVideoPlayer::OnExit()
03:25:50.669 T:2160   DEBUG: CApplication::OnPlayBackStopped: play state was 1, starting 1
03:25:50.669 T:2160   DEBUG: Thread VideoPlayer 2160 terminating
03:25:50.670 T:6860   DEBUG: CApplication::OnPlayBackStopped: play state was 3, starting 0
03:25:50.670 T:6860   ERROR: Playlist Player: skipping unplayable item: 0, path [plugin://script.service.bluray_iso_utils/?hello world]

I'm probably missing some basic python thing, but what?

Help...
@Wimpie
Please provide full debug logs via some pastebin site and describe your setup with as much details as possible. There's something weird is going on in your case.
Pages: 1 2