Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
lekma committed Mar 30, 2020
0 parents commit 3df8fbc
Show file tree
Hide file tree
Showing 14 changed files with 1,467 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# plugin.video.dlive
DLive Livestreaming Addon for Kodi.

Download the latest version from [here](https://github.com/lekma/plugin.video.dlive/releases/).
13 changes: 13 additions & 0 deletions addon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-


from __future__ import absolute_import, division, unicode_literals

import sys

from lib.dispatcher import dispatch


if __name__ == "__main__":
dispatch(*sys.argv)

29 changes: 29 additions & 0 deletions addon.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.dlive"
name="DLive"
version="0.1.0"
provider-name="lekma">
<requires>
<import addon="xbmc.python" version="2.26.0" />
<import addon="script.module.six" version="1.11.0" />
<import addon="script.module.kodi-six" version="0.1.2" />
<import addon="script.module.requests" version="2.22.0" />
<import addon="script.module.m3u8" version="0.3.7" />
<import addon="script.module.inputstreamhelper" version="0.4.3" />
</requires>
<extension point="xbmc.python.pluginsource" library="addon.py">
<provides>video</provides>
</extension>
<extension point="xbmc.addon.metadata">
<reuselanguageinvoker>true</reuselanguageinvoker>
<summary lang="en_GB">DLive Livestreaming Addon</summary>
<description lang="en_GB">DLive Livestreaming Addon</description>
<language>en</language>
<platform>all</platform>
<source>https://github.com/lekma/plugin.video.dlive</source>
<assets>
<icon>resources/media/icon.png</icon>
<fanart>resources/media/fanart.png</fanart>
</assets>
</extension>
</addon>
56 changes: 56 additions & 0 deletions lib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-


from __future__ import absolute_import, division, unicode_literals


_folders_schema_ = {
"livestreams": {
"": {
"id": 30004,
"action": "livestreams"
},
"featured": {
"id": 30007,
"action": "featured"
}
},
"categories": {
"": {
"id": 30006,
"action": "categories"
},
"search": {
"id": 30006,
"action": "search_categories"
}
},
"users": {
"recommended": {
"id": 30009,
"action": "recommended"
},
"search": {
"id": 30003,
"action": "search_users"
}
},
"search": {
"": {
"id": 30002
}
}
}


_home_folders_ = (
{"type": "livestreams", "style": "featured"},
{"type": "users", "style": "recommended"},
{"type": "livestreams"},
{"type": "categories"},
{"type": "search"}
)


_subfolders_defaults_ = ("users", "categories")

174 changes: 174 additions & 0 deletions lib/dispatcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# -*- coding: utf-8 -*-


from __future__ import absolute_import, division, unicode_literals


from six import wraps
from kodi_six import xbmc, xbmcplugin
from inputstreamhelper import Helper

from .utils import parse_query, get_setting, get_subfolders, more_item
from .utils import localized_string, search_dialog, log
from .dlive.api import service
from .dlive.objects import Folders, Home


def action(category=0):
def decorator(func):
func.__action__ = True
@wraps(func)
def wrapper(self, **kwargs):
try:
self.category = category
self.action = func.__name__
success = func(self, **kwargs)
except Exception:
success = False
raise
finally:
self.endDirectory(success)
del self.action, self.category
return wrapper
return decorator


class Dispatcher(object):

def __init__(self, url, handle):
self.url = url
self.handle = handle
self.limit = get_setting("items_per_page", int)
self.showNSFW = get_setting("show_nsfw", bool)
self.language = xbmc.getLanguage(xbmc.ISO_639_1)


# utils --------------------------------------------------------------------

def play(self, item, quality=0):
if quality == 6: # inputstream.adaptive
if not Helper("hls").check_inputstream():
return False
item.setProperty("inputstreamaddon", "inputstream.adaptive")
item.setProperty("inputstream.adaptive.manifest_type", "hls")
xbmcplugin.setResolvedUrl(self.handle, True, item)
return True

def addItem(self, item):
if item and not xbmcplugin.addDirectoryItem(self.handle, *item.asItem()):
raise
return True

def addItems(self, items, *args, **kwargs):
if not xbmcplugin.addDirectoryItems(
self.handle, [item.asItem()
for item in items.items(self.url, *args) if item]):
raise
if items.hasNextPage:
kwargs["after"] = items.endCursor
self.addItem(more_item(self.url, action=self.action, **kwargs))
if items.content:
xbmcplugin.setContent(self.handle, items.content)
if items.category:
self.setCategory(items.category)
return True

def setCategory(self, category):
xbmcplugin.setPluginCategory(self.handle, category)
self.category = 0

def endDirectory(self, success):
if success and self.category:
self.setCategory(localized_string(self.category))
xbmcplugin.endOfDirectory(self.handle, success)


# actions ------------------------------------------------------------------

@action()
def stream(self, **kwargs):
quality = get_setting("stream_quality", int)
item = service.stream(quality, **kwargs)
return self.play(item, quality) if item else False

@action()
def user(self, **kwargs):
stream, vods = service.user(first=self.limit, **kwargs)
if stream:
self.addItem(stream.item(self.url, "stream"))
return self.addItems(vods, **kwargs)

@action()
def category(self, **kwargs):
return self.addItems(
service.category(
first=self.limit, showNSFW=self.showNSFW, **kwargs),
"stream", **kwargs)

# --------------------------------------------------------------------------

@action()
def home(self, **kwargs):
return self.addItems(Home())

@action(30007)
def featured(self, **kwargs):
return self.addItems(
service.featured(userLanguageCode=self.language, **kwargs),
"stream")

@action(30009)
def recommended(self, **kwargs):
return self.addItems(service.recommended(**kwargs), "user")

@action(30004)
def livestreams(self, **kwargs):
return self.addItems(
service.livestreams(
first=self.limit, showNSFW=self.showNSFW, **kwargs),
"stream", **kwargs)

@action(30006)
def categories(self, **kwargs):
return self.addItems(
service.categories(first=self.limit, **kwargs),
"category", **kwargs)


# search -------------------------------------------------------------------

@action(30002)
def search(self, **kwargs):
return self.addItems(Folders(get_subfolders("search"), **kwargs))

@action(30003)
def search_users(self, **kwargs):
text = kwargs.pop("text", "") or search_dialog()
if text:
return self.addItems(
service.search_users(text=text, first=self.limit, **kwargs),
"user", text=text, **kwargs)
return False # failing here is a bit stupid

@action(30006)
def search_categories(self, **kwargs):
text = kwargs.pop("text", "") or search_dialog()
if text:
return self.addItems(
service.search_categories(text=text, first=self.limit, **kwargs),
"category", text=text, **kwargs)
return False # failing here is a bit stupid


# dispatch -----------------------------------------------------------------

def dispatch(self, **kwargs):
action = getattr(self, kwargs.pop("action", "home"))
if not callable(action) or not getattr(action, "__action__", False):
raise Exception("Invalid action '{}'".format(action.__name__))
return action(**kwargs)


def dispatch(url, handle, query, *args):
Dispatcher(url, int(handle)).dispatch(**parse_query(query))

Loading

0 comments on commit 3df8fbc

Please sign in to comment.