[contrib] add a xbmc plugin that interracts with videoob
This commit is contained in:
parent
d479a86c4d
commit
d1839bfcd4
23 changed files with 1036 additions and 0 deletions
22
contrib/plugin.video.videoobmc/addon.xml
Normal file
22
contrib/plugin.video.videoobmc/addon.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<addon id="plugin.video.videoobmc"
|
||||
name="Videoobmc"
|
||||
version="0.1.0"
|
||||
provider-name="Bezleputh">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="2.0"/>
|
||||
</requires>
|
||||
<extension point="xbmc.python.pluginsource" library="default.py">
|
||||
<provides>video</provides>
|
||||
</extension>
|
||||
<extension point="xbmc.addon.metadata">
|
||||
<platform>linux</platform>
|
||||
|
||||
<summary>Plugin for Videoob</summary>
|
||||
<summary lang="fr"></summary>
|
||||
<description>[B]Videoob[/B] is an application able to handle videos on supported websites. More information available on http://videoob.org/.
|
||||
</description>
|
||||
<description lang="fr">[B]Videoob[/B] est une application qui permet de récupérer des vidéos depuis divers sites. Pour plus d'informations à ce sujet, n'hésitez pas à visiter le site : http://videoob.org/
|
||||
</description>
|
||||
</extension>
|
||||
</addon>
|
||||
8
contrib/plugin.video.videoobmc/changelog.txt
Normal file
8
contrib/plugin.video.videoobmc/changelog.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[B]Dependance :[B]
|
||||
- weboob (http://weboob.org/install)
|
||||
|
||||
[B]Version 0.1.0[/B]
|
||||
-Call weboob directly
|
||||
|
||||
[B]Version 0.0.1[/B]
|
||||
-First try
|
||||
31
contrib/plugin.video.videoobmc/default.py
Normal file
31
contrib/plugin.video.videoobmc/default.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
import resources.lib.base.common_xbmc as common_xbmc
|
||||
import resources.lib.constants as constants
|
||||
from resources.lib.actions import actions
|
||||
|
||||
# Plugin constants
|
||||
version = "0.1.0"
|
||||
plugin = "videoobmc" + version
|
||||
addon_id = "plugin.video.videoobmc"
|
||||
author = "Bezleputh"
|
||||
mail = "carton_ben@yahoo.fr"
|
||||
|
||||
#import lxml.html import Element
|
||||
#print Element.__file__
|
||||
|
||||
#TODO gestion du logger, gestion des modules via XBMC (activation/desactivation)
|
||||
|
||||
#Bug encodge des categories
|
||||
#corriger version 1 pour que v2 et v1 donctionnent
|
||||
|
||||
if (__name__ == "__main__"):
|
||||
if not (sys.argv[2]):
|
||||
actions[constants.DISPLAY_MENU]()._do()
|
||||
else:
|
||||
params = common_xbmc.parse_params(sys.argv[2])
|
||||
action = params.get("action")
|
||||
if (action):
|
||||
actions[action]()._do(params)
|
||||
else:
|
||||
common_xbmc.display_error(" ARGV Nothing done.. verify params " + repr(params))
|
||||
20
contrib/plugin.video.videoobmc/default_test.py
Executable file
20
contrib/plugin.video.videoobmc/default_test.py
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import resources.lib.test.common_test as common_xbmc
|
||||
import resources.lib.constants as constants
|
||||
|
||||
from resources.lib.actions import actions
|
||||
|
||||
print sys.argv
|
||||
if len(sys.argv) < 2:
|
||||
actions[constants.DISPLAY_MENU]()._do()
|
||||
else:
|
||||
params = common_xbmc.parse_params(sys.argv[1])
|
||||
#print params
|
||||
action = params.get("action")
|
||||
if (action):
|
||||
actions[action]()._do(params)
|
||||
else:
|
||||
common_xbmc.display_error(" ARGV Nothing done.. verify params " + repr(params))
|
||||
BIN
contrib/plugin.video.videoobmc/icon.png
Normal file
BIN
contrib/plugin.video.videoobmc/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.8 KiB |
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<strings>
|
||||
<!-- Les strings de 30000 à 30999 sont réservées aux paramètres plug-in -->
|
||||
<string id="30000">Search</string>
|
||||
<string id="30001">Search: </string>
|
||||
|
||||
<string id="30100">Download</string>
|
||||
<string id="30110">Information</string>
|
||||
|
||||
<string id="30200">Error!</string>
|
||||
|
||||
<string id="30300">Information</string>
|
||||
<string id="30301">Download started</string>
|
||||
<string id="30302">Download succeed</string>
|
||||
|
||||
<string id="30510">Download folder: </string>
|
||||
<string id="30520">Number of videos per backends: </string>
|
||||
<string id="30530">Display Non Safe For Work videos: </string>
|
||||
<string id="30540">Enable debug mode: </string>
|
||||
<string id="30550">Update weboob backends</string>
|
||||
<string id="30551">Start updating weboob backends</string>
|
||||
<string id="30552">Weboob backends successfully updated</string>
|
||||
</strings>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<strings>
|
||||
<!-- Les strings de 30000 à 30999 sont réservées aux paramètres plug-in -->
|
||||
<string id="30000">Recherche</string>
|
||||
<string id="30001">Recherche: </string>
|
||||
|
||||
<string id="30100">Télécharger</string>
|
||||
<string id="30110">Information</string>
|
||||
|
||||
<string id="30200">Erreur!</string>
|
||||
|
||||
<string id="30300">Information</string>
|
||||
<string id="30301">Lancement du téléchargement</string>
|
||||
<string id="30302">Fichier téléchargé avec succès</string>
|
||||
|
||||
<string id="30510">Répertoire de Téléchargement:</string>
|
||||
<string id="30520">Nombre de vidéos par backends:</string>
|
||||
<string id="30530">Afficher les vidéos interdites aux moins de 18 ans :</string>
|
||||
<string id="30540">Enable debug mode :</string>
|
||||
<string id="30550">Mise à jour des modules weboob</string>
|
||||
<string id="30551">Debut de la mise à jour</string>
|
||||
<string id="30552">Weboob est maintenant à jour</string>
|
||||
</strings>
|
||||
1
contrib/plugin.video.videoobmc/resources/lib/__init__.py
Normal file
1
contrib/plugin.video.videoobmc/resources/lib/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Dummy file to make this directory a package.
|
||||
108
contrib/plugin.video.videoobmc/resources/lib/actions.py
Normal file
108
contrib/plugin.video.videoobmc/resources/lib/actions.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
import constants
|
||||
|
||||
from base.actions import BaseAction
|
||||
from menu import MenuItem, MenuItemVideo, MenuItemPath
|
||||
from threading import Thread
|
||||
from videoobmc2 import Videoobmc
|
||||
|
||||
if hasattr(sys.modules["__main__"], "common_xbmc"):
|
||||
common_xbmc = sys.modules["__main__"].common_xbmc
|
||||
else:
|
||||
import common_xbmc
|
||||
|
||||
|
||||
class VideoobBaseAction(BaseAction):
|
||||
def __init__(self):
|
||||
count = common_xbmc.get_settings('nbVideoPerBackend')
|
||||
numbers = ["10", "25", "50", "100"]
|
||||
self.videoobmc = Videoobmc(count=numbers[int(count)], nsfw=common_xbmc.get_settings('nsfw'))
|
||||
|
||||
|
||||
class DisplayMenuAction(VideoobBaseAction):
|
||||
def _do(self, param={}):
|
||||
backends = self.videoobmc.backends
|
||||
if backends:
|
||||
MenuItem(common_xbmc.get_translation('30000'), constants.SEARCH).add_to_menu()
|
||||
for backend in backends:
|
||||
icon = self.videoobmc.get_backend_icon(backend)
|
||||
MenuItem(backend, constants.DISPLAY_BACKENDS, backend=backend, iconimage=icon).add_to_menu()
|
||||
common_xbmc.end_of_directory(False)
|
||||
else:
|
||||
common_xbmc.display_error(" Please install and configure weboob")
|
||||
|
||||
|
||||
class DisplayCollectionMenuAction(VideoobBaseAction):
|
||||
def _do(self, param={}):
|
||||
path = param.get('path') if 'path' in param.keys() else ''
|
||||
collections, videos = self.videoobmc.ls(param.get('backend'), path=path)
|
||||
threads = []
|
||||
|
||||
for col in collections:
|
||||
MenuItemPath(col).add_to_menu()
|
||||
for video in videos:
|
||||
aThread = Thread(target=self.add_videos, args=(video, video.backend))
|
||||
threads.append(aThread)
|
||||
aThread.start()
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
common_xbmc.end_of_directory(False)
|
||||
|
||||
def add_videos(self, _video, backend):
|
||||
print _video
|
||||
video = self.videoobmc.get_video(_video, backend)
|
||||
if video:
|
||||
MenuItemVideo(video).add_to_menu()
|
||||
|
||||
|
||||
class DownloadAction(VideoobBaseAction):
|
||||
def _do(self, param={}):
|
||||
_id = param.get('id')
|
||||
backend = param.get('backend')
|
||||
if _id:
|
||||
aThread = Thread(target=self.download, args=(_id, backend))
|
||||
aThread.start()
|
||||
common_xbmc.display_info(common_xbmc.get_translation('30301'))
|
||||
common_xbmc.end_of_directory(False)
|
||||
|
||||
def download(self, _id, backend):
|
||||
dl_dir = common_xbmc.get_settings('downloadPath')
|
||||
self.videoobmc.download(_id, dl_dir if dl_dir else common_xbmc.get_addon_dir(), backend)
|
||||
common_xbmc.display_info(common_xbmc.get_translation('30302'))
|
||||
|
||||
|
||||
class SearchAction(VideoobBaseAction):
|
||||
def _do(self, param={}):
|
||||
pattern = common_xbmc.ask_user('', common_xbmc.get_translation('30001'))
|
||||
if pattern:
|
||||
for video in self.videoobmc.search(pattern, param.get('backend')):
|
||||
MenuItemVideo(video).add_to_menu()
|
||||
common_xbmc.end_of_directory(False)
|
||||
|
||||
|
||||
class DisplayBackendsAction(VideoobBaseAction):
|
||||
def _do(self, param={}):
|
||||
backend = param.get('backend')
|
||||
if backend:
|
||||
MenuItem('Search', constants.SEARCH, backend=backend).add_to_menu()
|
||||
DisplayCollectionMenuAction()._do(param)
|
||||
else:
|
||||
common_xbmc.end_of_directory(False)
|
||||
|
||||
|
||||
class UpdateWeboobAction(VideoobBaseAction):
|
||||
def _do(self, param={}):
|
||||
common_xbmc.display_info(common_xbmc.get_translation('30551'))
|
||||
self.videoobmc.update()
|
||||
common_xbmc.display_info(common_xbmc.get_translation('30552'))
|
||||
|
||||
|
||||
actions = {constants.DISPLAY_MENU: DisplayMenuAction,
|
||||
constants.DISPLAY_COLLECTION_MENU: DisplayCollectionMenuAction,
|
||||
constants.SEARCH: SearchAction,
|
||||
constants.DOWNLOAD: DownloadAction,
|
||||
constants.DISPLAY_BACKENDS: DisplayBackendsAction,
|
||||
constants.UPDATE: UpdateWeboobAction}
|
||||
|
|
@ -0,0 +1 @@
|
|||
# Dummy file to make this directory a package.
|
||||
16
contrib/plugin.video.videoobmc/resources/lib/base/actions.py
Normal file
16
contrib/plugin.video.videoobmc/resources/lib/base/actions.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class BaseAction():
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@abstractmethod
|
||||
def _do(self, param=None):
|
||||
"""
|
||||
Overload this method in application type subclass
|
||||
if you want to associate an action to the menu
|
||||
"""
|
||||
pass
|
||||
|
||||
actions = {}
|
||||
157
contrib/plugin.video.videoobmc/resources/lib/base/common_xbmc.py
Normal file
157
contrib/plugin.video.videoobmc/resources/lib/base/common_xbmc.py
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
import xbmcaddon
|
||||
|
||||
import urllib
|
||||
import sys
|
||||
|
||||
from traceback import print_exc
|
||||
|
||||
|
||||
def get_addon():
|
||||
if hasattr(sys.modules["__main__"], "addon_id"):
|
||||
_id = sys.modules["__main__"].addon_id
|
||||
return xbmcaddon.Addon(id=_id)
|
||||
|
||||
|
||||
def get_translation(key):
|
||||
addon = get_addon()
|
||||
if addon:
|
||||
return addon.getLocalizedString(int(key))
|
||||
|
||||
|
||||
def get_settings(key):
|
||||
addon = get_addon()
|
||||
if addon:
|
||||
return addon.getSetting(key)
|
||||
|
||||
|
||||
def get_addon_dir():
|
||||
addon = get_addon()
|
||||
if addon:
|
||||
addonDir = addon.getAddonInfo("path")
|
||||
else:
|
||||
addonDir = xbmc.translatePath("special://profile/addon_data/")
|
||||
|
||||
return addonDir
|
||||
|
||||
|
||||
def display_error(msg):
|
||||
xbmc.executebuiltin("XBMC.Notification(%s, %s)" % (get_translation('30200').decode('utf-8'), msg))
|
||||
print msg
|
||||
print_exc(msg)
|
||||
|
||||
|
||||
def display_info(msg):
|
||||
xbmc.executebuiltin("XBMC.Notification(%s, %s, 3000, DefaultFolder.png)" % (get_translation('30300').encode('utf-8'),
|
||||
msg.encode('utf-8')))
|
||||
#print msg
|
||||
print_exc()
|
||||
|
||||
|
||||
def parse_params(param_str):
|
||||
param_dic = {}
|
||||
# Parameters are on the 3rd arg passed to the script
|
||||
param_str = sys.argv[2]
|
||||
if len(param_str) > 1:
|
||||
param_str = param_str.replace('?', '')
|
||||
|
||||
# Ignore last char if it is a '/'
|
||||
if param_str[len(param_str) - 1] == '/':
|
||||
param_str = param_str[0:len(param_str) - 2]
|
||||
|
||||
# Processing each parameter splited on '&'
|
||||
for param in param_str.split('&'):
|
||||
try:
|
||||
# Spliting couple key/value
|
||||
key, value = param.split('=')
|
||||
except:
|
||||
key = param
|
||||
value = ''
|
||||
|
||||
key = urllib.unquote_plus(key)
|
||||
value = urllib.unquote_plus(value)
|
||||
|
||||
# Filling dictionnary
|
||||
param_dic[key] = value
|
||||
|
||||
return param_dic
|
||||
|
||||
|
||||
def ask_user(content, title):
|
||||
keyboard = xbmc.Keyboard(content, title)
|
||||
keyboard.doModal()
|
||||
if keyboard.isConfirmed() and keyboard.getText():
|
||||
return keyboard.getText()
|
||||
return ""
|
||||
|
||||
|
||||
def create_param_url(param_dic, quote_plus=False):
|
||||
"""
|
||||
Create an plugin URL based on the key/value passed in a dictionary
|
||||
"""
|
||||
url = sys.argv[0]
|
||||
sep = '?'
|
||||
|
||||
try:
|
||||
for param in param_dic:
|
||||
if quote_plus:
|
||||
url = url + sep + urllib.quote_plus(param) + '=' + urllib.quote_plus(param_dic[param])
|
||||
else:
|
||||
url = "%s%s%s=%s" % (url, sep, param, param_dic[param])
|
||||
|
||||
sep = '&'
|
||||
except Exception, msg:
|
||||
display_error("create_param_url %s" % msg)
|
||||
url = None
|
||||
return url
|
||||
|
||||
|
||||
def create_list_item(name, itemInfoType="Video", itemInfoLabels=None, iconimage="DefaultFolder.png",
|
||||
c_items=None, isPlayable=False):
|
||||
lstItem = xbmcgui.ListItem(label=name, iconImage=iconimage, thumbnailImage=iconimage)
|
||||
|
||||
if c_items:
|
||||
lstItem.addContextMenuItems(c_items, replaceItems=True)
|
||||
|
||||
if itemInfoLabels:
|
||||
iLabels = itemInfoLabels
|
||||
else:
|
||||
iLabels = {"Title": name, }
|
||||
|
||||
lstItem.setInfo(type=itemInfoType, infoLabels=iLabels)
|
||||
if isPlayable:
|
||||
lstItem.setProperty('IsPlayable', "true")
|
||||
|
||||
return lstItem
|
||||
|
||||
|
||||
def add_menu_item(params={}):
|
||||
url = create_param_url(params)
|
||||
if params.get('name'):
|
||||
if params.get('iconimage'):
|
||||
lstItem = create_list_item(params.get('name'), iconimage=params.get('iconimage'))
|
||||
else:
|
||||
lstItem = create_list_item(params.get('name'))
|
||||
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=lstItem, isFolder=True)
|
||||
else:
|
||||
display_error('add_menu_item : Fail to add item to menu')
|
||||
|
||||
|
||||
def add_menu_link(params={}):
|
||||
if params.get('name') and params.get('iconimage') and params.get('url') and \
|
||||
params.get('itemInfoLabels') and params.get('c_items'):
|
||||
url = params.get('url')
|
||||
lstItem = create_list_item(params.get('name'), iconimage=params.get('iconimage'),
|
||||
itemInfoLabels=params.get('itemInfoLabels'), c_items=params.get('c_items'),
|
||||
isPlayable=True)
|
||||
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=lstItem)
|
||||
else:
|
||||
display_error('add_menu_link : Fail to add item to menu')
|
||||
|
||||
|
||||
def end_of_directory(update=False):
|
||||
xbmcplugin.endOfDirectory(handle=int(sys.argv[1]), succeeded=True, updateListing=update) # , cacheToDisc=True)
|
||||
40
contrib/plugin.video.videoobmc/resources/lib/base/menu.py
Normal file
40
contrib/plugin.video.videoobmc/resources/lib/base/menu.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
if hasattr(sys.modules["__main__"], "common_xbmc"):
|
||||
common_xbmc = sys.modules["__main__"].common_xbmc
|
||||
else:
|
||||
import common_xbmc
|
||||
|
||||
|
||||
class BaseMenuItem():
|
||||
|
||||
def __init__(self, name, action, iconimage="DefaultFolder.png"):
|
||||
self.params = {}
|
||||
self.params['name'] = name
|
||||
self.params['action'] = action
|
||||
self.params['iconimage'] = iconimage
|
||||
|
||||
def get(self, element):
|
||||
return self.params[element]
|
||||
|
||||
def add_to_menu(self):
|
||||
common_xbmc.add_menu_item(self.params)
|
||||
|
||||
|
||||
class BaseMenuLink(BaseMenuItem):
|
||||
|
||||
def __init__(self, name, url, action, iconimage="DefaultFolder.png"):
|
||||
BaseMenuItem.__init__(self, name, action, iconimage)
|
||||
self.params["url"] = url
|
||||
|
||||
def createVideoContextMenu(self):
|
||||
return ""
|
||||
|
||||
def create_info_labels(self):
|
||||
return ""
|
||||
|
||||
def add_to_menu(self):
|
||||
self.params["itemInfoLabels"] = self.create_info_labels()
|
||||
self.params["c_items"] = self.createVideoContextMenu()
|
||||
common_xbmc.add_menu_link(self.params)
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import simplejson as json
|
||||
|
||||
if hasattr(sys.modules["__main__"], "common_xbmc"):
|
||||
common_xbmc = sys.modules["__main__"].common_xbmc
|
||||
else:
|
||||
import common_xbmc
|
||||
|
||||
|
||||
class Weboobmc():
|
||||
def __init__(self, count=10):
|
||||
self.count = count
|
||||
|
||||
def update(self):
|
||||
#weboob-config update
|
||||
self._call_weboob('weboob-config', 'update')
|
||||
|
||||
def _call_weboob(self, application, command, options={}, argument=""):
|
||||
options['-n'] = self.count
|
||||
_opt = " ".join(["%s %s " % (k, v) for k, v in options.items()])
|
||||
_cmd = "%s %s %s %s" % (application, _opt, command, argument)
|
||||
#print _cmd.encode('utf-8')
|
||||
return subprocess.check_output(_cmd, shell=True)
|
||||
|
||||
def _json_call_weboob(self, application, command, options={}, argument=""):
|
||||
options['-f'] = 'json'
|
||||
try:
|
||||
result = self._call_weboob(application, command, options, argument)
|
||||
m = re.search(r"(\[{.+\}])", result)
|
||||
if m:
|
||||
result = u'%s' % m.group(1)
|
||||
#print result
|
||||
return json.loads(result) if result else []
|
||||
except subprocess.CalledProcessError as e:
|
||||
common_xbmc.display_error(" Error while calling weboob : %s " % e)
|
||||
|
||||
def get_loaded_backends(self, caps):
|
||||
#weboob-config list ICapVideo -f json
|
||||
backends = self._json_call_weboob('weboob-config', 'list', argument=caps)
|
||||
for backend in backends:
|
||||
if "_enabled=0" not in backend['Configuration']:
|
||||
yield backend['Name'] # , self.get_backend_icon(backend['Module'])
|
||||
|
||||
def get_backend_icon(self, module):
|
||||
if 'WEBOOB_WORKDIR' in os.environ:
|
||||
datadir = os.environ.get('WEBOOB_WORKDIR')
|
||||
else:
|
||||
datadir = os.path.join(os.environ.get('XDG_DATA_HOME',
|
||||
os.path.join(os.path.expanduser('~'), '.local', 'share')
|
||||
), 'weboob')
|
||||
icons_dir = os.path.join(datadir, 'icons')
|
||||
|
||||
return os.path.join(icons_dir, '%s.png' % module)
|
||||
|
||||
def is_category(self, obj):
|
||||
return 'split_path' in obj.keys()
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from weboob.tools.application.base import BaseApplication
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
|
||||
class Weboobmc(BaseApplication):
|
||||
def __init__(self, count=10):
|
||||
BaseApplication.__init__(self)
|
||||
self.count = int(count)
|
||||
self._is_default_count = False
|
||||
|
||||
def update(self):
|
||||
self.weboob.update()
|
||||
|
||||
def get_backend_icon(self, module):
|
||||
minfo = self.weboob.repositories.get_module_info(module)
|
||||
return self.weboob.repositories.get_module_icon_path(minfo)
|
||||
|
||||
def obj_to_filename(self, obj, dest=None, default=None):
|
||||
"""
|
||||
This method can be used to get a filename from an object, using a mask
|
||||
filled by information of this object.
|
||||
All patterns are braces-enclosed, and are name of available fields in
|
||||
the object.
|
||||
:param obj: object type obj: BaseObject param dest: dest given by user (default None)
|
||||
type dest: str param default: default file mask (if not given, this is
|
||||
:'{id}-{title}.{ext}') type default: str rtype: str
|
||||
"""
|
||||
|
||||
if default is None:
|
||||
default = '{id}-{title}.{ext}'
|
||||
if dest is None:
|
||||
dest = '.'
|
||||
if os.path.isdir(dest):
|
||||
dest = os.path.join(dest, default)
|
||||
|
||||
def repl(m):
|
||||
field = m.group(1)
|
||||
if hasattr(obj, field):
|
||||
return re.sub('[?:/]', '-', '%s' % getattr(obj, field))
|
||||
else:
|
||||
return m.group(0)
|
||||
|
||||
return re.sub(r'\{(.+?)\}', repl, dest)
|
||||
|
||||
def download_obj(self, obj, dest):
|
||||
|
||||
def check_exec(executable):
|
||||
with open('/dev/null', 'w') as devnull:
|
||||
process = subprocess.Popen(['which', executable], stdout=devnull)
|
||||
if process.wait() != 0:
|
||||
print 'Please install "%s"' % executable
|
||||
return False
|
||||
return True
|
||||
|
||||
dest = self.obj_to_filename(obj, dest)
|
||||
if obj.url.startswith('rtmp'):
|
||||
if not check_exec('rtmpdump'):
|
||||
return 1
|
||||
args = ('rtmpdump', '-e', '-r', obj.url, '-o', dest)
|
||||
elif obj.url.startswith('mms'):
|
||||
if not check_exec('mimms'):
|
||||
return 1
|
||||
args = ('mimms', '-r', obj.url, dest)
|
||||
elif u'm3u8' == obj.ext:
|
||||
_dest, _ = os.path.splitext(dest)
|
||||
dest = u'%s.%s' % (_dest, 'mp4')
|
||||
args = ('wget',) + tuple(line for line in self.read_url(obj.url) if not line.startswith('#')) + ('-O', dest)
|
||||
else:
|
||||
if check_exec('wget'):
|
||||
args = ('wget', '-c', obj.url, '-O', dest)
|
||||
elif check_exec('curl'):
|
||||
args = ('curl', '-C', '-', obj.url, '-o', dest)
|
||||
else:
|
||||
return 1
|
||||
os.spawnlp(os.P_WAIT, args[0], *args)
|
||||
10
contrib/plugin.video.videoobmc/resources/lib/constants.py
Normal file
10
contrib/plugin.video.videoobmc/resources/lib/constants.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
DISPLAY_MENU = "display_menu"
|
||||
DISPLAY_COLLECTION_MENU = "display_collection_menu"
|
||||
DISPLAY_BACKENDS = "display_backends"
|
||||
|
||||
SEARCH = "search"
|
||||
VIDEO = "video"
|
||||
DOWNLOAD = "download"
|
||||
UPDATE = "update"
|
||||
72
contrib/plugin.video.videoobmc/resources/lib/menu.py
Normal file
72
contrib/plugin.video.videoobmc/resources/lib/menu.py
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
import constants
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from base.menu import BaseMenuItem, BaseMenuLink
|
||||
|
||||
if hasattr(sys.modules["__main__"], "common_xbmc"):
|
||||
common_xbmc = sys.modules["__main__"].common_xbmc
|
||||
else:
|
||||
import common_xbmc
|
||||
|
||||
|
||||
class MenuItem(BaseMenuItem):
|
||||
params = {}
|
||||
|
||||
def __init__(self, name, action, iconimage="DefaultFolder.png", backend=''):
|
||||
BaseMenuItem.__init__(self, name, action, iconimage)
|
||||
self.params['backend'] = backend
|
||||
|
||||
|
||||
class MenuItemPath(MenuItem):
|
||||
|
||||
def __init__(self, collection, action=constants.DISPLAY_COLLECTION_MENU, iconimage="DefaultFolder.png"):
|
||||
MenuItem.__init__(self, collection.title, action, iconimage, collection.fullid.split('@')[-1])
|
||||
self.params["path"] = '/'.join(collection.split_path)
|
||||
|
||||
|
||||
class MenuItemVideo(BaseMenuLink):
|
||||
def __init__(self, video, iconimage="DefaultFolder.png"):
|
||||
name = '[%s] %s' % (video.backend, video.title)
|
||||
BaseMenuLink.__init__(self, name, video.url, constants.VIDEO,
|
||||
video.thumbnail.url if video.thumbnail.url else iconimage)
|
||||
self.video = video
|
||||
self.params["id"] = self.video.id
|
||||
|
||||
def createVideoContextMenu(self):
|
||||
cm = []
|
||||
|
||||
#Information
|
||||
cm.append((common_xbmc.get_translation('30110'), "XBMC.Action(Info)"))
|
||||
|
||||
#Téléchargement
|
||||
url = "%s?action=%s&id=%s&backend=%s" % (sys.argv[0], constants.DOWNLOAD, self.video.id, self.video.backend)
|
||||
cm.append((common_xbmc.get_translation('30100'), "XBMC.PlayMedia(%s)" % (url)))
|
||||
|
||||
return cm
|
||||
|
||||
def create_info_labels(self):
|
||||
date, year = self.format_date(self.video.date)
|
||||
|
||||
duration = 0
|
||||
if self.video.duration:
|
||||
duration = u'%s' % str(self.video.duration.total_seconds()/60) if isinstance(self.video.duration, timedelta) else self.video.duration
|
||||
|
||||
description = u'%s' % self.video.description
|
||||
|
||||
return {"Title": self.video.title,
|
||||
"Year": year,
|
||||
"Plot": description,
|
||||
"PlotOutline": description[0:30] if len(description) > 30 else description,
|
||||
"Director": self.video.author if self.video.author else 'Unknown',
|
||||
"Duration": duration,
|
||||
"Date": date}
|
||||
|
||||
def format_date(self, video_date):
|
||||
date = datetime.now().strftime("%d/%m/%Y")
|
||||
if video_date:
|
||||
date = video_date.strftime("%d/%m/%Y")
|
||||
|
||||
year = date.split('/')[-1]
|
||||
return date, year
|
||||
|
|
@ -0,0 +1 @@
|
|||
# Dummy file to make this directory a package.
|
||||
110
contrib/plugin.video.videoobmc/resources/lib/test/common_test.py
Normal file
110
contrib/plugin.video.videoobmc/resources/lib/test/common_test.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import urllib
|
||||
|
||||
|
||||
def get_addon():
|
||||
pass
|
||||
|
||||
|
||||
def get_translation(key):
|
||||
translation = {'30000': 'Recherche',
|
||||
'30001': 'Recherche :',
|
||||
'30100': 'Télécharger',
|
||||
'30110': 'Information',
|
||||
'30200': 'Erreur!',
|
||||
'30300': 'Information',
|
||||
'30301': 'Lancement du téléchargement',
|
||||
'30302': 'Fichier téléchargé avec succès',
|
||||
'30551': 'Debut de la mise à jour',
|
||||
'30552': 'Weboob est maintenant à jour'}
|
||||
return translation.get(key)
|
||||
|
||||
|
||||
def get_addon_dir():
|
||||
return '/home/benjamin'
|
||||
|
||||
|
||||
def get_settings(key):
|
||||
settings = {'downloadPath': get_addon_dir(),
|
||||
'nbVideoPerBackend': '0',
|
||||
'nsfw': 'False'}
|
||||
return settings.get(key)
|
||||
|
||||
|
||||
def display_error(error):
|
||||
print "%s: %s" % ("ERROR", error)
|
||||
|
||||
|
||||
def display_info(msg):
|
||||
print "%s: %s" % ("INFO", msg)
|
||||
|
||||
|
||||
def parse_params(paramStr):
|
||||
|
||||
paramDic = {}
|
||||
# Parameters are on the 3rd arg passed to the script
|
||||
if len(paramStr) > 1:
|
||||
paramStr = paramStr.replace('?', '')
|
||||
|
||||
# Ignore last char if it is a '/'
|
||||
if paramStr[len(paramStr) - 1] == '/':
|
||||
paramStr = paramStr[0:len(paramStr) - 2]
|
||||
|
||||
# Processing each parameter splited on '&'
|
||||
for param in paramStr.split('&'):
|
||||
try:
|
||||
# Spliting couple key/value
|
||||
key, value = param.split('=')
|
||||
except:
|
||||
key = param
|
||||
value = ''
|
||||
|
||||
key = urllib.unquote_plus(key)
|
||||
value = urllib.unquote_plus(value)
|
||||
|
||||
# Filling dictionnary
|
||||
paramDic[key] = value
|
||||
return paramDic
|
||||
|
||||
|
||||
def ask_user(content, title):
|
||||
return raw_input(title)
|
||||
|
||||
|
||||
def create_param_url(paramsDic, quote_plus=False):
|
||||
|
||||
#url = sys.argv[0]
|
||||
url = ''
|
||||
sep = '?'
|
||||
|
||||
try:
|
||||
for param in paramsDic:
|
||||
if quote_plus:
|
||||
url = url + sep + urllib.quote_plus(param) + '=' + urllib.quote_plus(paramsDic[param])
|
||||
else:
|
||||
url = "%s%s%s=%s" % (url, sep, param, paramsDic[param])
|
||||
|
||||
sep = '&'
|
||||
except Exception, msg:
|
||||
display_error("create_param_url %s" % msg)
|
||||
url = None
|
||||
return url
|
||||
|
||||
|
||||
def add_menu_item(params={}):
|
||||
print '%s => "%s"' % (params.get('name'), create_param_url(params))
|
||||
|
||||
|
||||
def add_menu_link(params={}):
|
||||
print '[%s] %s (%s)' % (params.get('id'), params.get('name'), params.get('url'))
|
||||
#print params.get('itemInfoLabels')
|
||||
#print params.get('c_items')
|
||||
|
||||
|
||||
def end_of_directory(update=False):
|
||||
print '******************************************************'
|
||||
|
||||
|
||||
def download_video(url, name, dir='./'):
|
||||
print 'Downlaod a video %s from %s' % (name, url)
|
||||
94
contrib/plugin.video.videoobmc/resources/lib/videoobmc.py
Normal file
94
contrib/plugin.video.videoobmc/resources/lib/videoobmc.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
|
||||
from datetime import timedelta, datetime
|
||||
from .base.weboobmc import Weboobmc
|
||||
from weboob.capabilities.video import BaseVideo
|
||||
from weboob.capabilities.image import BaseImage
|
||||
from weboob.capabilities.collection import Collection
|
||||
|
||||
class Videoobmc(Weboobmc):
|
||||
def __init__(self, count=10, nsfw=False):
|
||||
Weboobmc.__init__(self, count=count)
|
||||
self.backends =list(self.get_loaded_backends('CapVideo'))
|
||||
_nsfw = 'on' if nsfw else 'off'
|
||||
self._call_weboob('videoob', 'nsfw', argument=_nsfw)
|
||||
|
||||
def search(self, pattern, backend=''):
|
||||
#videoob search pattern -f json
|
||||
options = {'--select': 'id,title,date,description,author,duration,thumbnail,url'}
|
||||
if backend:
|
||||
options['-b'] = backend
|
||||
_videos = self._json_call_weboob('videoob', 'search', argument=pattern, options=options)
|
||||
if _videos:
|
||||
for _video in _videos:
|
||||
yield self.create_video_from_json(_video)
|
||||
|
||||
def create_video_from_json(self, _video):
|
||||
video = BaseVideo()
|
||||
video.id = u'%s' % _video['id']
|
||||
video.backend = u'%s' % _video['id'].split('@')[-1]
|
||||
|
||||
if 'url' in _video.keys():
|
||||
video.url = u'%s' % _video['url']
|
||||
|
||||
if 'thumbnail' in _video.keys() and _video['thumbnail'] and 'url' in _video['thumbnail'].keys():
|
||||
video.thumbnail = BaseImage()
|
||||
video.thumbnail.url = u'%s' % _video['thumbnail']['url']
|
||||
else:
|
||||
video.thumbnail.url = u''
|
||||
video.title = u'%s' % _video['title']
|
||||
|
||||
if _video['date']:
|
||||
|
||||
try:
|
||||
datetime.strptime(_video['date'].split('.')[0], '%Y-%m-%d %H:%M:%S')
|
||||
except TypeError:
|
||||
datetime(*(time.strptime(_video['date'].split('.')[0], '%Y-%m-%d %H:%M:%S')[0:6]))
|
||||
|
||||
video.description = u'%s' % _video['description']
|
||||
video.author = u'%s' % _video['author']
|
||||
|
||||
if _video['duration']:
|
||||
_duration = _video['duration'].split(':')
|
||||
video.duration = timedelta(hours=int(_duration[0]), minutes=int(_duration[1]), seconds=int(_duration[2]))
|
||||
|
||||
return video
|
||||
|
||||
def get_video(self, video, backend):
|
||||
#videoob info _id -f json
|
||||
_video = self._json_call_weboob('videoob', 'info', argument=video.id)
|
||||
if _video and len(_video) > 0:
|
||||
return self.create_video_from_json(_video[0])
|
||||
|
||||
def ls(self, backend, path=''):
|
||||
options = {'-b': backend}
|
||||
result = self._json_call_weboob('videoob', 'ls', options=options, argument=path)
|
||||
return self.separate_collections_and_videos(result)
|
||||
|
||||
def separate_collections_and_videos(self, objs):
|
||||
videos = []
|
||||
categories = []
|
||||
for obj in objs:
|
||||
if self.is_category(obj):
|
||||
categories.append(self.create_category_from_json(obj))
|
||||
else:
|
||||
video = BaseVideo()
|
||||
video.id = obj['id'].split('@')[0]
|
||||
video.backend = obj['id'].split('@')[-1]
|
||||
videos.append(video)
|
||||
return categories, videos
|
||||
|
||||
def create_category_from_json(self, obj):
|
||||
collection = Collection(obj['split_path'].split('/'))
|
||||
collection.title = obj['title']
|
||||
collection.id = obj['id'].split('@')[0]
|
||||
collection.backend = obj['id'].split('@')[1]
|
||||
return collection
|
||||
|
||||
def download(self, _id, path, backend):
|
||||
#videoob download _id path
|
||||
options = {'-b': backend}
|
||||
self._call_weboob('videoob', 'download', options=options, argument=u'%s %s' % (_id, path))
|
||||
93
contrib/plugin.video.videoobmc/resources/lib/videoobmc.py
Normal file
93
contrib/plugin.video.videoobmc/resources/lib/videoobmc.py
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
|
||||
from datetime import timedelta, datetime
|
||||
from .base.weboobmc import Weboobmc
|
||||
from weboob.capabilities.video import BaseVideo
|
||||
from weboob.capabilities.image import BaseImage
|
||||
from weboob.capabilities.collection import Collection
|
||||
|
||||
class Videoobmc(Weboobmc):
|
||||
def __init__(self, count=10, nsfw=False):
|
||||
Weboobmc.__init__(self, count=count)
|
||||
self.backends =list(self.get_loaded_backends('CapVideo'))
|
||||
_nsfw = 'on' if nsfw else 'off'
|
||||
self._call_weboob('videoob', 'nsfw', argument=_nsfw)
|
||||
|
||||
def search(self, pattern, backend=''):
|
||||
#videoob search pattern -f json
|
||||
options = {'--select': 'id,title,date,description,author,duration,thumbnail,url'}
|
||||
if backend:
|
||||
options['-b'] = backend
|
||||
_videos = self._json_call_weboob('videoob', 'search', argument=pattern, options=options)
|
||||
if _videos:
|
||||
for _video in _videos:
|
||||
yield self.create_video_from_json(_video)
|
||||
|
||||
def create_video_from_json(self, _video):
|
||||
video = BaseVideo()
|
||||
video.id = u'%s' % _video['id']
|
||||
print video.id
|
||||
print _video['id']
|
||||
video.backend = u'%s' % _video['id'].split('@')[-1]
|
||||
|
||||
if 'url' in _video.keys():
|
||||
video.url = u'%s' % _video['url']
|
||||
|
||||
if 'thumbnail' in _video.keys() and _video['thumbnail'] and 'url' in _video['thumbnail'].keys():
|
||||
video.thumbnail = BaseImage()
|
||||
video.thumbnail.url = u'%s' % _video['thumbnail']['url']
|
||||
else:
|
||||
video.thumbnail.url = u''
|
||||
video.title = u'%s' % _video['title']
|
||||
|
||||
if _video['date']:
|
||||
|
||||
try:
|
||||
video.date = datetime.strptime(_video['date'].split('.')[0], '%Y-%m-%d %H:%M:%S')
|
||||
except TypeError:
|
||||
video.date = datetime(*(time.strptime(_video['date'].split('.')[0], '%Y-%m-%d %H:%M:%S')[0:6]))
|
||||
|
||||
video.description = u'%s' % _video['description']
|
||||
video.author = u'%s' % _video['author']
|
||||
|
||||
if _video['duration']:
|
||||
_duration = _video['duration'].split(':')
|
||||
video.duration = timedelta(hours=int(_duration[0]), minutes=int(_duration[1]), seconds=int(_duration[2]))
|
||||
|
||||
return video
|
||||
|
||||
def get_video(self, _id, backend):
|
||||
#videoob info _id -f json
|
||||
_video = self._json_call_weboob('videoob', 'info', argument=_id)
|
||||
if _video and len(_video) > 0:
|
||||
return self.create_video_from_json(_video[0])
|
||||
|
||||
def ls(self, backend, path=''):
|
||||
options = {'-b': backend}
|
||||
result = self._json_call_weboob('videoob', 'ls', options=options, argument=path)
|
||||
return self.separate_collections_and_videos(result)
|
||||
|
||||
def separate_collections_and_videos(self, objs):
|
||||
videos = []
|
||||
categories = []
|
||||
for obj in objs:
|
||||
if self.is_category(obj):
|
||||
categories.append(self.create_category_from_json(obj))
|
||||
else:
|
||||
#videos.append(self.get_video(obj['id']))
|
||||
videos.append(self.create_video_from_json(obj))
|
||||
return categories, videos
|
||||
|
||||
def create_category_from_json(self, obj):
|
||||
collection = Collection(obj['split_path'].split('/'))
|
||||
collection.title = obj['title']
|
||||
collection.id = obj['id'].split('@')[0]
|
||||
collection.backend = obj['id'].split('@')[1]
|
||||
return collection
|
||||
|
||||
def download(self, _id, path):
|
||||
#videoob download _id path
|
||||
self._call_weboob('videoob', 'download', argument=u'%s %s' % (_id, path))
|
||||
53
contrib/plugin.video.videoobmc/resources/lib/videoobmc2.py
Normal file
53
contrib/plugin.video.videoobmc/resources/lib/videoobmc2.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .base.weboobmc2 import Weboobmc
|
||||
from weboob.capabilities.video import BaseVideo, CapVideo
|
||||
from weboob.capabilities.collection import CapCollection, Collection
|
||||
|
||||
|
||||
class Videoobmc(Weboobmc):
|
||||
def __init__(self, count=10, nsfw=False):
|
||||
Weboobmc.__init__(self, count=count)
|
||||
self.backends = self.weboob.load_backends(CapVideo)
|
||||
self.nsfw = nsfw
|
||||
|
||||
def search(self, pattern, backend=''):
|
||||
kwargs = {'pattern': pattern,
|
||||
'nsfw': self.nsfw,
|
||||
'backends': backend}
|
||||
|
||||
fields = ['id', 'title', 'date', 'description', 'author', 'duration', 'thumbnail', 'url']
|
||||
try:
|
||||
for _backend, video in self.weboob.do(self._do_complete, self.count, fields, 'search_videos', **kwargs):
|
||||
yield video
|
||||
except Exception as e:
|
||||
print e
|
||||
|
||||
def get_video(self, video, _backend):
|
||||
backend = self.weboob.get_backend(_backend)
|
||||
fields = ['id', 'title', 'date', 'description', 'author', 'duration', 'thumbnail', 'url']
|
||||
return backend.fillobj(video, fields)
|
||||
|
||||
def ls(self, backend, path=''):
|
||||
kwargs = {'split_path': path.split('/') if path else [],
|
||||
'caps': CapCollection,
|
||||
'objs': (BaseVideo, ),
|
||||
'backends': backend}
|
||||
fields = [] # ['id', 'title', 'date', 'description', 'author', 'duration', 'thumbnail', 'url']
|
||||
result = self.weboob.do(self._do_complete, self.count, fields, 'iter_resources', **kwargs)
|
||||
return self.separate_collections_and_videos(result)
|
||||
|
||||
def separate_collections_and_videos(self, objs):
|
||||
videos = []
|
||||
categories = []
|
||||
for backend, obj in objs:
|
||||
if isinstance(obj, Collection):
|
||||
categories.append(obj)
|
||||
else:
|
||||
videos.append(obj)
|
||||
return categories, videos
|
||||
|
||||
def download(self, _id, dest, backend):
|
||||
for backend, _video in self.weboob.do('get_video', _id, backends=backend):
|
||||
self.download_obj(_video, dest)
|
||||
11
contrib/plugin.video.videoobmc/resources/settings.xml
Normal file
11
contrib/plugin.video.videoobmc/resources/settings.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<settings>
|
||||
<!-- GENERAL -->
|
||||
<category label="$LOCALIZE[128]">
|
||||
<setting label="30510" type="folder" id="downloadPath" default="" />
|
||||
<setting label="30520" type="enum" id="nbVideoPerBackend" default="1" values="10|25|50|100" />
|
||||
<setting label="30530" type="bool" id="nsfw" default="False" />
|
||||
<setting label="30550" type="action" action="RunScript(plugin.video.videoobmc, plugin.video.videoobmc, action=update)"/>
|
||||
</category>
|
||||
</settings>
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue