Compare commits

..

No commits in common. "a9913e88bea815286253d2487dbf8c5f6e372587" and "1fad214fc04e1159eb45f46c98f2e80cd376d456" have entirely different histories.

14 changed files with 108 additions and 46 deletions

2
.gitignore vendored
View File

@ -9,9 +9,7 @@ __pycache__/
# Distribution / packaging # Distribution / packaging
.Python .Python
.env
env/ env/
.venv/
venv/ venv/
venv2/ venv2/
build/ build/

16
Readme.md Normal file
View File

@ -0,0 +1,16 @@
# Simple example plugin for Kodi mediacenter
This is a simple yet fully functional example of a video plugin for [Kodi](http://kodi.tv) mediacenter.
Please read the comments in the plugin code for more details.
An installable .zip can be downloaded from "[Releases](https://github.com/romanvm/plugin.video.example/releases)" tab.
Do **not** try to install a .zip generated by GitHub.
**Note**: the purpose of this example plugin is to show you how to organize and play your media content in Kodi.
The methods of obtaining such content, for example parsing websites or working with various APIs,
are beyond the scope of this example.
The plugin uses a pre-defined set of free sample videos from [www.vidsplay.com](http://www.vidsplay.com/).
**Warning**: the "master" branch is only compatible with Kody 17.0 (Krypton) and above. For older versions see the "legacy" branch.
License: [GPL v.3](http://www.gnu.org/copyleft/gpl.html)

View File

@ -1,25 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<addon id="plugin.video.nickreboot" <addon id="plugin.video.example"
version="1.0.0" version="2.3.0"
name="Nick Reboot" name="Example Kodi Video Plugin"
provider-name="Spacefreak18"> provider-name="Roman_V_M">
<requires> <requires>
<import addon="xbmc.python" version="3.0.0"/> <import addon="xbmc.python" version="2.25.0"/>
<import addon="script.module.future" />
</requires> </requires>
<extension point="xbmc.python.pluginsource" library="main.py"> <extension point="xbmc.python.pluginsource" library="main.py">
<provides>video</provides> <provides>video</provides>
</extension> </extension>
<extension point="xbmc.addon.metadata"> <extension point="xbmc.addon.metadata">
<summary lang="en">Reboot Your Inner Child</summary> <summary lang="en">Example Kodi Video Plugin</summary>
<description lang="en_GB">Reboot Your Inner Child</description> <description lang="en_GB">An example video plugin for Kodi mediacenter.</description>
<disclaimer lang="en_GB">Reboot Your Inner Child</disclaimer> <disclaimer lang="en_GB">Free sample videos are provided by www.vidsplay.com.</disclaimer>
<assets> <assets>
<icon>resources/icon.png</icon> <icon>icon.png</icon>
<fanart>resources/bgimg.jpg</fanart> <fanart>fanart.jpg</fanart>
<screenshot>resources/screenshot0.png</screenshot> <screenshot>resources\screenshot-01.jpg</screenshot>
<screenshot>resources/screenshot1.png</screenshot> <screenshot>resources\screenshot-02.jpg</screenshot>
<screenshot>resources/screenshot2.jpg</screenshot> <screenshot>resources\screenshot-03.jpg</screenshot>
</assets> </assets>
<news>Super secret Addon from Paul</news> <news>Updated with latest artwork metadata</news>
</extension> </extension>
</addon> </addon>

BIN
fanart.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

107
main.py
View File

@ -1,28 +1,76 @@
# Module: main # -*- coding: utf-8 -*-
# Author: Paul Dino Jones # Module: default
# Created on: 2022.05.03 # Author: Roman V. M.
# Created on: 28.11.2014
# License: GPL v.3 https://www.gnu.org/copyleft/gpl.html # License: GPL v.3 https://www.gnu.org/copyleft/gpl.html
"""
Example video plugin that is compatible with both Python 2 and 3
Compatibility features are provided by ``script.module.future`` library addon.
"""
# Enable unicode strings by default as in Python 3
from __future__ import unicode_literals
# Monkey-patch standard libary names to enable Python 3-like behavior
from future import standard_library
standard_library.install_aliases()
# The above strings provide compatibility layer for Python 2
# so the code can work in both versions.
# In Python 3 they do nothing and can be safely removed.
# Normal imports for your addon:
import sys import sys
from urllib.parse import urlencode, parse_qsl from urllib.parse import urlencode, parse_qsl
import xbmcgui import xbmcgui
import xbmcplugin import xbmcplugin
# Get the plugin url in plugin:// notation. # Get the plugin url in plugin:// notation.
_URL = sys.argv[0] _url = sys.argv[0]
# Get the plugin handle as an integer number. # Get the plugin handle as an integer number.
_HANDLE = int(sys.argv[1]) _handle = int(sys.argv[1])
# Free sample videos are provided by www.vidsplay.com # Free sample videos are provided by www.vidsplay.com
# Here we use a fixed set of properties simply for demonstrating purposes # Here we use a fixed set of properties simply for demonstrating purposes
# In a "real life" plugin you will need to get info and links to video files/streams # In a "real life" plugin you will need to get info and links to video files/streams
# from some web-site or online service. # from some web-site or online service.
VIDEOS = {'Live': [{'name': 'Nick Reboot', VIDEOS = {'Animals': [{'name': 'Crab',
'thumb': 'https://res.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_170,w_170,f_auto,b_white,q_auto:eco,dpr_1/v1412260035/rd4ity1ftukrlofrw3gw.png', 'thumb': 'http://www.vidsplay.com/wp-content/uploads/2017/04/crab-screenshot.jpg',
'fanart': 'https://repo.brak.space/bgimg.jpg', 'video': 'http://www.vidsplay.com/wp-content/uploads/2017/04/crab.mp4',
'video': 'rtmp://nickreboot.brak.space/live/NickReboot', 'genre': 'Animals'},
'genre': 'Live Stream'}]} {'name': 'Alligator',
'thumb': 'http://www.vidsplay.com/wp-content/uploads/2017/04/alligator-screenshot.jpg',
'video': 'http://www.vidsplay.com/wp-content/uploads/2017/04/alligator.mp4',
'genre': 'Animals'},
{'name': 'Turtle',
'thumb': 'http://www.vidsplay.com/wp-content/uploads/2017/04/turtle-screenshot.jpg',
'video': 'http://www.vidsplay.com/wp-content/uploads/2017/04/turtle.mp4',
'genre': 'Animals'}
],
'Cars': [{'name': 'Postal Truck',
'thumb': 'http://www.vidsplay.com/wp-content/uploads/2017/05/us_postal-screenshot.jpg',
'video': 'http://www.vidsplay.com/wp-content/uploads/2017/05/us_postal.mp4',
'genre': 'Cars'},
{'name': 'Traffic',
'thumb': 'http://www.vidsplay.com/wp-content/uploads/2017/05/traffic1-screenshot.jpg',
'video': 'http://www.vidsplay.com/wp-content/uploads/2017/05/traffic1.mp4',
'genre': 'Cars'},
{'name': 'Traffic Arrows',
'thumb': 'http://www.vidsplay.com/wp-content/uploads/2017/05/traffic_arrows-screenshot.jpg',
'video': 'http://www.vidsplay.com/wp-content/uploads/2017/05/traffic_arrows.mp4',
'genre': 'Cars'}
],
'Food': [{'name': 'Chicken',
'thumb': 'http://www.vidsplay.com/wp-content/uploads/2017/05/bbq_chicken-screenshot.jpg',
'video': 'http://www.vidsplay.com/wp-content/uploads/2017/05/bbqchicken.mp4',
'genre': 'Food'},
{'name': 'Hamburger',
'thumb': 'http://www.vidsplay.com/wp-content/uploads/2017/05/hamburger-screenshot.jpg',
'video': 'http://www.vidsplay.com/wp-content/uploads/2017/05/hamburger.mp4',
'genre': 'Food'},
{'name': 'Pizza',
'thumb': 'http://www.vidsplay.com/wp-content/uploads/2017/05/pizza-screenshot.jpg',
'video': 'http://www.vidsplay.com/wp-content/uploads/2017/05/pizza.mp4',
'genre': 'Food'}
]}
def get_url(**kwargs): def get_url(**kwargs):
@ -33,7 +81,7 @@ def get_url(**kwargs):
:return: plugin call URL :return: plugin call URL
:rtype: str :rtype: str
""" """
return '{}?{}'.format(_URL, urlencode(kwargs)) return '{0}?{1}'.format(_url, urlencode(kwargs))
def get_categories(): def get_categories():
@ -42,7 +90,7 @@ def get_categories():
Here you can insert some parsing code that retrieves Here you can insert some parsing code that retrieves
the list of video categories (e.g. 'Movies', 'TV-shows', 'Documentaries' etc.) the list of video categories (e.g. 'Movies', 'TV-shows', 'Documentaries' etc.)
from some site or API. from some site or server.
.. note:: Consider using `generator functions <https://wiki.python.org/moin/Generators>`_ .. note:: Consider using `generator functions <https://wiki.python.org/moin/Generators>`_
instead of returning lists. instead of returning lists.
@ -58,7 +106,7 @@ def get_videos(category):
Get the list of videofiles/streams. Get the list of videofiles/streams.
Here you can insert some parsing code that retrieves Here you can insert some parsing code that retrieves
the list of video streams in the given category from some site or API. the list of video streams in the given category from some site or server.
.. note:: Consider using `generators functions <https://wiki.python.org/moin/Generators>`_ .. note:: Consider using `generators functions <https://wiki.python.org/moin/Generators>`_
instead of returning lists. instead of returning lists.
@ -77,10 +125,10 @@ def list_categories():
""" """
# Set plugin category. It is displayed in some skins as the name # Set plugin category. It is displayed in some skins as the name
# of the current section. # of the current section.
xbmcplugin.setPluginCategory(_HANDLE, 'My Video Collection') xbmcplugin.setPluginCategory(_handle, 'My Video Collection')
# Set plugin content. It allows Kodi to select appropriate views # Set plugin content. It allows Kodi to select appropriate views
# for this type of content. # for this type of content.
xbmcplugin.setContent(_HANDLE, 'videos') xbmcplugin.setContent(_handle, 'videos')
# Get video categories # Get video categories
categories = get_categories() categories = get_categories()
# Iterate through categories # Iterate through categories
@ -108,11 +156,11 @@ def list_categories():
# is_folder = True means that this item opens a sub-list of lower level items. # is_folder = True means that this item opens a sub-list of lower level items.
is_folder = True is_folder = True
# Add our item to the Kodi virtual folder listing. # Add our item to the Kodi virtual folder listing.
xbmcplugin.addDirectoryItem(_HANDLE, url, list_item, is_folder) xbmcplugin.addDirectoryItem(_handle, url, list_item, is_folder)
# Add a sort method for the virtual folder items (alphabetically, ignore articles) # Add a sort method for the virtual folder items (alphabetically, ignore articles)
xbmcplugin.addSortMethod(_HANDLE, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE) xbmcplugin.addSortMethod(_handle, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE)
# Finish creating a virtual folder. # Finish creating a virtual folder.
xbmcplugin.endOfDirectory(_HANDLE) xbmcplugin.endOfDirectory(_handle)
def list_videos(category): def list_videos(category):
@ -124,10 +172,10 @@ def list_videos(category):
""" """
# Set plugin category. It is displayed in some skins as the name # Set plugin category. It is displayed in some skins as the name
# of the current section. # of the current section.
xbmcplugin.setPluginCategory(_HANDLE, category) xbmcplugin.setPluginCategory(_handle, category)
# Set plugin content. It allows Kodi to select appropriate views # Set plugin content. It allows Kodi to select appropriate views
# for this type of content. # for this type of content.
xbmcplugin.setContent(_HANDLE, 'videos') xbmcplugin.setContent(_handle, 'videos')
# Get the list of videos in the category. # Get the list of videos in the category.
videos = get_videos(category) videos = get_videos(category)
# Iterate through videos. # Iterate through videos.
@ -138,12 +186,11 @@ def list_videos(category):
# 'mediatype' is needed for skin to display info for this ListItem correctly. # 'mediatype' is needed for skin to display info for this ListItem correctly.
list_item.setInfo('video', {'title': video['name'], list_item.setInfo('video', {'title': video['name'],
'genre': video['genre'], 'genre': video['genre'],
'fanart': video['fanart'],
'mediatype': 'video'}) 'mediatype': 'video'})
# Set graphics (thumbnail, fanart, banner, poster, landscape etc.) for the list item. # Set graphics (thumbnail, fanart, banner, poster, landscape etc.) for the list item.
# Here we use the same image for all items for simplicity's sake. # Here we use the same image for all items for simplicity's sake.
# In a real-life plugin you need to set each image accordingly. # In a real-life plugin you need to set each image accordingly.
list_item.setArt({'thumb': video['thumb'], 'icon': video['thumb'], 'fanart': video['fanart']}) list_item.setArt({'thumb': video['thumb'], 'icon': video['thumb'], 'fanart': video['thumb']})
# Set 'IsPlayable' property to 'true'. # Set 'IsPlayable' property to 'true'.
# This is mandatory for playable items! # This is mandatory for playable items!
list_item.setProperty('IsPlayable', 'true') list_item.setProperty('IsPlayable', 'true')
@ -154,11 +201,12 @@ def list_videos(category):
# is_folder = False means that this item won't open any sub-list. # is_folder = False means that this item won't open any sub-list.
is_folder = False is_folder = False
# Add our item to the Kodi virtual folder listing. # Add our item to the Kodi virtual folder listing.
xbmcplugin.addDirectoryItem(_HANDLE, url, list_item, is_folder) xbmcplugin.addDirectoryItem(_handle, url, list_item, is_folder)
# Add a sort method for the virtual folder items (alphabetically, ignore articles) # Add a sort method for the virtual folder items (alphabetically, ignore articles)
xbmcplugin.addSortMethod(_HANDLE, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE) xbmcplugin.addSortMethod(_handle, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE)
# Finish creating a virtual folder. # Finish creating a virtual folder.
xbmcplugin.endOfDirectory(_HANDLE) xbmcplugin.endOfDirectory(_handle)
def play_video(path): def play_video(path):
""" """
@ -170,7 +218,7 @@ def play_video(path):
# Create a playable item with a path to play. # Create a playable item with a path to play.
play_item = xbmcgui.ListItem(path=path) play_item = xbmcgui.ListItem(path=path)
# Pass the item to the Kodi player. # Pass the item to the Kodi player.
xbmcplugin.setResolvedUrl(_HANDLE, True, listitem=play_item) xbmcplugin.setResolvedUrl(_handle, True, listitem=play_item)
def router(paramstring): def router(paramstring):
@ -196,12 +244,11 @@ def router(paramstring):
# If the provided paramstring does not contain a supported action # If the provided paramstring does not contain a supported action
# we raise an exception. This helps to catch coding errors, # we raise an exception. This helps to catch coding errors,
# e.g. typos in action names. # e.g. typos in action names.
raise ValueError('Invalid paramstring: {}!'.format(paramstring)) raise ValueError('Invalid paramstring: {0}!'.format(paramstring))
else: else:
# If the plugin is called from Kodi UI without any parameters, # If the plugin is called from Kodi UI without any parameters,
# display the list of video categories # display the list of video categories
#list_categories() list_categories()
list_videos("Live")
if __name__ == '__main__': if __name__ == '__main__':

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

BIN
resources/screenshot-01.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
resources/screenshot-02.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
resources/screenshot-03.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 808 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB