You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
999 lines
33 KiB
Python
999 lines
33 KiB
Python
# -*- coding: utf-8 -*-
|
|
import sys
|
|
import re
|
|
import os
|
|
|
|
import xbmc
|
|
import xbmcplugin
|
|
import xbmcgui
|
|
import xbmcaddon
|
|
import xbmcvfs
|
|
|
|
import six
|
|
from six.moves import urllib
|
|
|
|
from lib.general import *
|
|
from lib.rumble_user import RumbleUser
|
|
from lib.comments import CommentWindow
|
|
|
|
try:
|
|
import json
|
|
except ImportError:
|
|
import simplejson as json
|
|
|
|
BASE_URL = 'https://rumble.com'
|
|
PLUGIN_URL = sys.argv[0]
|
|
PLUGIN_ID = int(sys.argv[1])
|
|
PLUGIN_NAME = PLUGIN_URL.replace('plugin://','')
|
|
|
|
ADDON = xbmcaddon.Addon()
|
|
ADDON_ICON = ADDON.getAddonInfo('icon')
|
|
ADDON_NAME = ADDON.getAddonInfo('name')
|
|
|
|
HOME_DIR = 'special://home/addons/' + PLUGIN_NAME
|
|
RESOURCE_DIR = HOME_DIR + 'resources/'
|
|
MEDIA_DIR = RESOURCE_DIR + 'media/'
|
|
|
|
DATE_FORMAT = ADDON.getSetting('date_format')
|
|
|
|
RUMBLE_USER = RumbleUser()
|
|
|
|
if six.PY2:
|
|
favorites = xbmc.translatePath(os.path.join(ADDON.getAddonInfo('profile'), 'favorites.dat'))
|
|
else:
|
|
favorites = xbmcvfs.translatePath(os.path.join(ADDON.getAddonInfo('profile'), 'favorites.dat'))
|
|
|
|
|
|
def favorites_create():
|
|
|
|
""" creates favorite directory if doesn't exist """
|
|
|
|
if six.PY2:
|
|
addon_data_path = xbmc.translatePath(ADDON.getAddonInfo('profile'))
|
|
else:
|
|
addon_data_path = xbmcvfs.translatePath(ADDON.getAddonInfo('profile'))
|
|
|
|
if os.path.exists(addon_data_path) is False:
|
|
os.mkdir(addon_data_path)
|
|
|
|
xbmc.sleep(1)
|
|
|
|
|
|
def favorites_load( return_string = False ):
|
|
|
|
""" load favourites from file into variable """
|
|
|
|
if os.path.exists( favorites ):
|
|
fav_str = open( favorites ).read()
|
|
if return_string:
|
|
return fav_str
|
|
if fav_str:
|
|
return json.loads( fav_str )
|
|
else:
|
|
favorites_create()
|
|
|
|
# nothing to load, return type necessary
|
|
if return_string:
|
|
return ''
|
|
|
|
return []
|
|
|
|
|
|
def to_unicode( text, encoding='utf-8', errors='strict' ):
|
|
|
|
""" Forces text to unicode """
|
|
|
|
if isinstance(text, bytes):
|
|
return text.decode(encoding, errors=errors)
|
|
|
|
return text
|
|
|
|
|
|
def get_search_string( heading='', message='' ):
|
|
|
|
""" Ask the user for a search string """
|
|
|
|
search_string = None
|
|
|
|
keyboard = xbmc.Keyboard(message, heading)
|
|
keyboard.doModal()
|
|
|
|
if keyboard.isConfirmed():
|
|
search_string = to_unicode(keyboard.getText())
|
|
|
|
return search_string
|
|
|
|
|
|
def home_menu():
|
|
|
|
""" Creates home menu """
|
|
|
|
# Search
|
|
add_dir( get_string(137), '', 1, { 'thumb': 'search.png' } )
|
|
# Favorites
|
|
add_dir( get_string(1036), '', 7, { 'thumb': 'favorite.png' } )
|
|
|
|
if RUMBLE_USER.has_login_details():
|
|
# Subscriptions
|
|
add_dir( 'Subscriptions', BASE_URL + '/subscriptions', 3, { 'thumb': 'favorite.png' }, {}, 'subscriptions' )
|
|
# Following
|
|
add_dir( 'Following', BASE_URL + '/followed-channels', 3, { 'thumb': 'favorite.png' }, {}, 'following' )
|
|
# Watch Later
|
|
add_dir( 'Watch Later', BASE_URL + '/playlists/watch-later', 3, { 'thumb': 'favorite.png' }, {}, 'playlist' )
|
|
|
|
# Battle Leaderboard
|
|
add_dir( get_string(30050), BASE_URL + '/battle-leaderboard/recorded', 3, { 'thumb': 'leader.png' }, {}, 'top' )
|
|
|
|
# Categories
|
|
add_dir( get_string(30051), BASE_URL + '/browse', 3, { 'thumb': 'viral.png' }, {}, 'cat_list' )
|
|
|
|
# Live Streams
|
|
add_dir( get_string(30052), BASE_URL + '/browse/live', 3, { 'thumb': 'viral.png' }, {}, 'live_stream' )
|
|
|
|
# Settings
|
|
add_dir( get_string(5), '', 8, { 'thumb': 'settings.png' } )
|
|
|
|
xbmcplugin.endOfDirectory( PLUGIN_ID, cacheToDisc=False )
|
|
|
|
def search_menu():
|
|
|
|
""" Creates search menu """
|
|
|
|
# Search Video
|
|
add_dir( get_string(30100), BASE_URL + '/search/video?q=', 2, { 'thumb': 'search.png' }, {}, 'video' )
|
|
# Search Channel
|
|
add_dir( get_string(30101), BASE_URL + '/search/channel?q=', 2, { 'thumb': 'search.png' }, {}, 'channel' )
|
|
# Search User
|
|
add_dir( get_string(30102), BASE_URL + '/search/channel?q=', 2, { 'thumb': 'search.png' }, {}, 'user' )
|
|
|
|
xbmcplugin.endOfDirectory(PLUGIN_ID)
|
|
|
|
|
|
def pagination( url, page, cat, search=False ):
|
|
|
|
""" list directory items then show pagination """
|
|
|
|
if url > '':
|
|
|
|
page = int(page)
|
|
page_url = url
|
|
paginated = True
|
|
|
|
if page == 1:
|
|
if search:
|
|
page_url = url + search
|
|
elif search and cat == 'video':
|
|
page_url = url + search + "&page=" + str( page )
|
|
elif cat in {'channel', 'cat_video', 'user', 'other', 'subscriptions', 'live_stream' }:
|
|
page_url = url + "?page=" + str( page )
|
|
|
|
if cat in { 'following', 'top', 'cat_list' }:
|
|
paginated = False
|
|
|
|
amount = list_rumble( page_url, cat )
|
|
|
|
if paginated and amount > 15 and page < 10:
|
|
|
|
# for next page
|
|
page = page + 1
|
|
|
|
name = get_string(30150) + " " + str( page )
|
|
list_item = xbmcgui.ListItem(name)
|
|
|
|
link_params = {
|
|
'url': url,
|
|
'mode': '3',
|
|
'name': name,
|
|
'page': str( page ),
|
|
'cat': cat,
|
|
}
|
|
|
|
link = build_url( link_params )
|
|
|
|
if search and cat == 'video':
|
|
link = link + "&search=" + urllib.parse.quote_plus(search)
|
|
|
|
xbmcplugin.addDirectoryItem(PLUGIN_ID, link, list_item, True)
|
|
|
|
xbmcplugin.endOfDirectory(PLUGIN_ID)
|
|
|
|
|
|
def get_image( data, image_id ):
|
|
|
|
""" method to get an image from scraped page's CSS from the image ID """
|
|
|
|
image_re = re.compile(
|
|
"i.user-image--img--id-" + str( image_id ) + ".+?{\s*background-image: url(.+?);",
|
|
re.MULTILINE|re.DOTALL|re.IGNORECASE
|
|
).findall(data)
|
|
|
|
if image_re != []:
|
|
image = str(image_re[0]).replace('(', '').replace(')', '')
|
|
else:
|
|
image = ''
|
|
|
|
return image
|
|
|
|
|
|
def list_rumble( url, cat ):
|
|
|
|
""" Method to get and display items from Rumble """
|
|
|
|
amount = 0
|
|
headers = None
|
|
|
|
if 'subscriptions' in url or cat == 'following':
|
|
# make sure there is a session
|
|
# result is stored in a cookie
|
|
RUMBLE_USER.has_session()
|
|
|
|
data = request_get(url, None, headers)
|
|
|
|
# Fix for favorites & search
|
|
if cat in { 'other', 'channel' } and '/c/' in url:
|
|
cat = 'channel_video'
|
|
|
|
if 'search' in url:
|
|
if cat == 'video':
|
|
amount = dir_list_create( data, cat, 'video', True, 1 )
|
|
else:
|
|
amount = dir_list_create( data, cat, 'channel', True )
|
|
elif cat in { 'subscriptions', 'cat_video', 'live_stream', 'playlist' }:
|
|
amount = dir_list_create( data, cat, cat, False, 2 )
|
|
elif cat in { 'channel', 'top', 'other' }:
|
|
amount = dir_list_create( data, cat, 'video', False, 2 )
|
|
elif cat in { 'channel_video', 'user' }:
|
|
amount = dir_list_create( data, cat, 'channel_video', False, 2 )
|
|
elif cat == 'following':
|
|
amount = dir_list_create( data, cat, 'following', False, 2 )
|
|
elif cat == 'cat_list':
|
|
amount = dir_list_create( data, cat, cat, False )
|
|
|
|
return amount
|
|
|
|
|
|
def dir_list_create( data, cat, video_type='video', search = False, play=0 ):
|
|
|
|
""" create and display dir list based upon type """
|
|
|
|
amount = 0
|
|
|
|
if video_type == 'video':
|
|
videos = re.compile(r'href=\"([^\"]+)\"><div class=\"(?:[^\"]+)\"><img\s*class=\"video-item--img\"\s*src=\"([^\"]+)\"\s*alt=\"(?:[^\"]+)\"\s*>(?:<span class=\"video-item--watching\">[^\<]+</span>)?(?:<div class=video-item--overlay-rank>(?:[0-9]+)</div>)?</div><(?:[^\>]+)></span></a><div class=\"video-item--info\"><time class=\"video-item--meta video-item--time\" datetime=(.+?)-(.+?)-(.+?)T(?:.+?) title\=\"(?:[^\"]+)\">(?:[^\<]+)</time><h3 class=video-item--title>(.+?)</h3><address(?:[^\>]+)><a rel=author class=\"(?:[^\=]+)=(.+?)><div class=ellipsis-1>(.+?)</div>', re.MULTILINE|re.DOTALL|re.IGNORECASE).findall(data)
|
|
if videos:
|
|
amount = len(videos)
|
|
for link, img, year, month, day, title, channel_link, channel_name in videos:
|
|
|
|
info_labels = {}
|
|
|
|
if '<svg' in channel_name:
|
|
channel_name = channel_name.split('<svg')[0] + " (Verified)"
|
|
|
|
info_labels[ 'year' ] = year
|
|
video_title = '[B]' + clean_text( title ) + '[/B]\n[COLOR gold]' + channel_name + '[/COLOR] - [COLOR lime]' + get_date_formatted( DATE_FORMAT, year, month, day ) + '[/COLOR]'
|
|
images = { 'thumb': str(img), 'fanart': str(img) }
|
|
|
|
#open get url and open player
|
|
add_dir( video_title, BASE_URL + link, 4, images, info_labels, cat, False, True, play, { 'name' : channel_link, 'subscribe': True } )
|
|
|
|
elif video_type in { 'cat_video', 'subscriptions', 'live_stream', 'channel_video', 'playlist' }:
|
|
|
|
if video_type == 'live_stream':
|
|
videos_regex = r'<div class=\"thumbnail__grid\"\s*role=\"list\">(.*)<nav class=\"paginator\">'
|
|
elif video_type == 'playlist':
|
|
videos_regex = r'<ol\s*class=\"videostream__list\"(?:[^>]+)>(.*)</ol>'
|
|
else:
|
|
videos_regex = r'<ol\s*class=\"thumbnail__grid\">(.*)</ol>'
|
|
videos = re.compile(videos_regex, re.DOTALL|re.IGNORECASE).findall(data)
|
|
|
|
if videos:
|
|
if video_type == 'playlist':
|
|
videos = videos[0].split('"videostream videostream__list-item')
|
|
else:
|
|
videos = videos[0].split('"videostream thumbnail__grid-')
|
|
|
|
videos.pop(0)
|
|
amount = len(videos)
|
|
for video in videos:
|
|
|
|
video_title = ''
|
|
images = {}
|
|
info_labels = {}
|
|
subscribe_context = False
|
|
|
|
title = re.compile(r'<h3(?:[^\>]+)?>(.*)</h3>', re.DOTALL|re.IGNORECASE).findall(video)
|
|
link = re.compile(r'<a\sclass="videostream__link link"\sdraggable="false"\shref="([^\"]+)">', re.DOTALL|re.IGNORECASE).findall(video)
|
|
img = re.compile(r'<img\s*class=\"thumbnail__image\"\s*draggable=\"false\"\s*src=\"([^\"]+)\"', re.DOTALL|re.IGNORECASE).findall(video)
|
|
|
|
if title:
|
|
video_title = '[B]' + clean_text( title[0] ) + '[/B]'
|
|
if 'videostream__status--live' in video:
|
|
video_title += ' [COLOR red](Live)[/COLOR]'
|
|
if 'videostream__status--upcoming' in video:
|
|
video_title += ' [COLOR yellow](Upcoming)[/COLOR]'
|
|
|
|
channel_name = re.compile(r'<span\sclass="channel__name(?:[^\"]+)" title="(?:[^\"]+)">([^\<]+)</span>(\s*<svg class=channel__verified)?', re.DOTALL|re.IGNORECASE).findall(video)
|
|
channel_link = re.compile(r'<a\s*rel=\"author\"\s*class=\"channel__link\slink\s(?:[^\"]+)\"\s*href=\"([^\"]+)\"\s*>', re.DOTALL|re.IGNORECASE).findall(video)
|
|
|
|
if channel_name:
|
|
|
|
video_title += '\n[COLOR gold]' + clean_text( channel_name[0][0] )
|
|
if channel_name[0][1]:
|
|
video_title += " (Verified)"
|
|
video_title += '[/COLOR]'
|
|
|
|
if channel_link:
|
|
subscribe_context = { 'name' : channel_link[0], 'subscribe': True }
|
|
|
|
date_time = re.compile(r'<time\s*class=\"(?:[^\"]+)\"\s*datetime=\"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})-(\d{2}):(\d{2})\"', re.DOTALL|re.IGNORECASE).findall(video)
|
|
|
|
if date_time:
|
|
info_labels[ 'year' ] = date_time[0][0]
|
|
video_title += ' - [COLOR lime]' + get_date_formatted( DATE_FORMAT, date_time[0][0], date_time[0][1], date_time[0][2] ) + '[/COLOR]'
|
|
|
|
if img:
|
|
images = { 'thumb': str(img[0]), 'fanart': str(img[0]) }
|
|
|
|
duration = re.compile(r'videostream__status--duration\"\s*>([^<]+)</div>', re.DOTALL|re.IGNORECASE).findall(video)
|
|
|
|
if duration:
|
|
info_labels[ 'duration' ] = duration_to_secs( duration[0].strip() )
|
|
|
|
#open get url and open player
|
|
add_dir( video_title, BASE_URL + link[0], 4, images, info_labels, cat, False, True, play, subscribe_context )
|
|
|
|
return amount
|
|
|
|
elif video_type == 'cat_list':
|
|
cat_list = re.compile(r'<a\s*class=\"category__link link\"\s*href=\"([^\"]+)\"\s*>\s*<img\s*class=\"category__image\"\s*src=\"([^\"]+)\"\s*alt=(?:[^\>]+)>\s*<strong class=\"category__title\">([^\<]+)</strong>', re.DOTALL|re.IGNORECASE).findall(data)
|
|
if cat_list:
|
|
amount = len(cat_list)
|
|
for link, img, title in cat_list:
|
|
|
|
cat = 'channel_video'
|
|
images = { 'thumb': str(img), 'fanart': str(img) }
|
|
|
|
#open get url and open player
|
|
add_dir( clean_text( title ), BASE_URL + link.strip() + '/videos', 3, images, {}, cat )
|
|
|
|
elif video_type == 'following':
|
|
|
|
videos_regex = r'<ol\s*class=\"followed-channels__list\">(.*)</ol>'
|
|
videos = re.compile(videos_regex, re.DOTALL|re.IGNORECASE).findall(data)
|
|
if videos:
|
|
videos = videos[0].split('"followed-channel flex items-')
|
|
|
|
videos.pop(0)
|
|
amount = len(videos)
|
|
for video in videos:
|
|
|
|
video_title = ''
|
|
images = {}
|
|
|
|
title = re.compile(r'<span\s*class=\"clamp-2\">([^<]+)<\/span>', re.DOTALL|re.IGNORECASE).findall(video)
|
|
followers = re.compile(r'<div\s*class=\"followed-channel__followers(?:[^\"]+)\">([^<]+)</div>', re.DOTALL|re.IGNORECASE).findall(video)
|
|
link = re.compile(r'<a\s*class=\"(?:[^\"]+)\"\s*href=\"([^\"]+)\">', re.DOTALL|re.IGNORECASE).findall(video)
|
|
img = re.compile(r'<(?:img|span)\s*class=\"channel__avatar([^\"]+)\"\s*(?:src=\"([^\"]+)\")?', re.DOTALL|re.IGNORECASE).findall(video)
|
|
|
|
if title:
|
|
video_title = '[B]' + clean_text( title[0] ) + '[/B]'
|
|
|
|
if '<use href="#channel_verified" />' in video:
|
|
video_title += ' [COLOR gold](Verified)[/COLOR]'
|
|
|
|
link = link[0] if link else ""
|
|
|
|
if img:
|
|
if 'channel__letter' in img[0][0]:
|
|
if title:
|
|
image_url = MEDIA_DIR + 'letters/' + title[0][0].lower() + '.png'
|
|
else:
|
|
image_url = ''
|
|
else:
|
|
image_url = img[0][1]
|
|
|
|
images = { 'thumb': str(image_url), 'fanart': str(image_url) }
|
|
|
|
if 'channel__live' in img[0][0]:
|
|
video_title += ' [COLOR red](Live)[/COLOR]'
|
|
|
|
if followers:
|
|
video_title += '\n[COLOR green]' + followers[0].strip() + '[/COLOR]'
|
|
|
|
cat = 'user'
|
|
if '/user/' not in link:
|
|
cat = 'channel_video'
|
|
|
|
#open get url and open player
|
|
add_dir( video_title, BASE_URL + link, 3, images, {}, cat, True, True, play, { 'name' : link, 'subscribe': False } )
|
|
|
|
else:
|
|
|
|
channels = re.compile(r'a href=(.+?)>\s*<div class=\"channel-item--img\">\s*<i class=\'user-image (?:user-image--img user-image--img--id-([^\']+)\')?(?:user-image--letter\' data-letter=([a-zA-Z]))? data-js=user-image>\s*</i>\s*</div>\s*<h3 class=channel-item--title>(.+?)</h3>\s*<span class=channel-item--subscribers>(.+?) Followers</span>',re.DOTALL).findall(data)
|
|
if channels:
|
|
amount = len(channels)
|
|
for link, img_id, img_letter, channel_name, subscribers in channels:
|
|
|
|
# split channel and user
|
|
if search:
|
|
if cat == 'channel':
|
|
if '/c/' not in link:
|
|
continue
|
|
else:
|
|
if '/user/' not in link:
|
|
continue
|
|
|
|
if '<svg' in channel_name:
|
|
channel_name = channel_name.split('<svg')[0] + " (Verified)"
|
|
if img_id:
|
|
img = str( get_image( data, img_id ) )
|
|
else:
|
|
img = MEDIA_DIR + 'letters/' + img_letter + '.png'
|
|
images = { 'thumb': str(img), 'fanart': str(img) }
|
|
|
|
video_title = '[B]' + channel_name + '[/B]\n[COLOR palegreen]' + subscribers + '[/COLOR] [COLOR yellow]' + get_string(30156) + '[/COLOR]'
|
|
#open get url and open player
|
|
add_dir( video_title, BASE_URL + link, 3, images, {}, cat, True, True, play, { 'name' : link, 'subscribe': True } )
|
|
|
|
return amount
|
|
|
|
def get_video_id( url ):
|
|
|
|
"""
|
|
gets a video id from a URL
|
|
helps in resolving
|
|
"""
|
|
|
|
data = request_get(url)
|
|
|
|
# gets embed id from embed url
|
|
video_id = re.compile(
|
|
',\"embedUrl\":\"' + BASE_URL + '/embed/(.*?)\/\",',
|
|
re.MULTILINE|re.DOTALL|re.IGNORECASE
|
|
).findall(data)
|
|
|
|
if video_id:
|
|
return video_id[0]
|
|
return False
|
|
|
|
def get_playlist_video_id( url ):
|
|
|
|
"""
|
|
gets a playlist video id from a URL
|
|
helps in adding video to playlist
|
|
"""
|
|
|
|
data = request_get(url)
|
|
|
|
# gets embed id from embed url
|
|
video_id = re.compile(
|
|
'data-id=\"([0-9]+)\"',
|
|
re.MULTILINE|re.DOTALL|re.IGNORECASE
|
|
).findall(data)
|
|
|
|
if video_id:
|
|
return video_id[0]
|
|
return False
|
|
|
|
def resolver( url ):
|
|
|
|
""" Resolves a URL for rumble & returns resolved link to video """
|
|
|
|
# playback options - 0: high auto, 1: low auto, 2: quality select
|
|
playback_method = int( ADDON.getSetting('playbackMethod') )
|
|
|
|
media_url = False
|
|
|
|
if playback_method > 0:
|
|
urls = []
|
|
|
|
video_id = get_video_id( url )
|
|
|
|
if video_id:
|
|
|
|
# use site api to get video urls
|
|
# TODO: use as dict / array instead of using regex to get URLs
|
|
data = request_get(BASE_URL + '/embedJS/u3/?request=video&ver=2&v=' + video_id)
|
|
sizes = [ '1080', '720', '480', '360', 'hls' ]
|
|
|
|
for quality in sizes:
|
|
|
|
# get urls for quality
|
|
matches = re.compile(
|
|
'"' + quality + '".+?url.+?:"(.*?)"',
|
|
re.MULTILINE|re.DOTALL|re.IGNORECASE
|
|
).findall(data)
|
|
|
|
if matches:
|
|
if playback_method > 0:
|
|
urls.append(( quality, matches[0] ))
|
|
else:
|
|
media_url = matches[0]
|
|
break
|
|
|
|
# if not automatically selecting highest quality
|
|
if int( playback_method ) > 0:
|
|
|
|
# m3u8 check
|
|
if len( urls ) == 1 and '.m3u8' in urls[0][1]:
|
|
from lib.m3u8 import m3u8
|
|
m3u8_handler = m3u8()
|
|
urls = m3u8_handler.process( request_get( urls[0][1] ) )
|
|
|
|
# reverses array - small to large
|
|
if playback_method == 1:
|
|
urls = urls[::-1]
|
|
media_url = urls[0][1]
|
|
|
|
# quality select
|
|
elif playback_method == 2:
|
|
if len(urls) > 0:
|
|
selected_index = xbmcgui.Dialog().select(
|
|
'Select Quality', [(sourceItem[0] or '?') for sourceItem in urls]
|
|
)
|
|
if selected_index != -1:
|
|
media_url = urls[selected_index][1]
|
|
|
|
if media_url:
|
|
media_url = media_url.replace('\/', '/')
|
|
|
|
return media_url
|
|
|
|
|
|
def play_video( name, url, thumb, play=2 ):
|
|
|
|
""" method to play video """
|
|
|
|
# get video link
|
|
url = resolver(url)
|
|
|
|
if url:
|
|
|
|
# Use HTTP
|
|
if ADDON.getSetting('useHTTP') == 'true':
|
|
url = url.replace('https://', 'http://', 1)
|
|
|
|
list_item = xbmcgui.ListItem(name, path=url)
|
|
list_item.setArt({'icon': thumb, 'thumb': thumb})
|
|
|
|
info_labels={ 'Title': name, 'plot': '' }
|
|
|
|
item_set_info( list_item, info_labels )
|
|
|
|
if play == 1:
|
|
xbmc.Player().play(item=url, listitem=list_item)
|
|
elif play == 2:
|
|
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, list_item)
|
|
|
|
else:
|
|
xbmcgui.Dialog().ok( 'Error', 'Video not found' )
|
|
|
|
|
|
def search_items( url, cat ):
|
|
|
|
""" Searches rumble """
|
|
|
|
search_str = get_search_string(heading="Search")
|
|
|
|
if not search_str:
|
|
return False, 0
|
|
|
|
title = urllib.parse.quote_plus(search_str)
|
|
|
|
pagination( url, 1, cat, title )
|
|
|
|
|
|
def favorites_show():
|
|
|
|
""" Displays favorites """
|
|
|
|
data = favorites_load()
|
|
|
|
try:
|
|
|
|
amount = len(data)
|
|
if amount > 0:
|
|
for i in data:
|
|
name = i[0]
|
|
url = i[1]
|
|
mode = i[2]
|
|
images = { 'thumb': str(i[3]), 'fanart': str(i[4]) }
|
|
info_labels = { 'plot': str(i[5]) }
|
|
cat = i[6]
|
|
folder = ( i[7] == 'True' )
|
|
play = i[8]
|
|
|
|
add_dir( name, url, mode, images, info_labels, cat, folder, True, int(play) )
|
|
|
|
xbmcplugin.endOfDirectory(PLUGIN_ID)
|
|
else:
|
|
xbmcgui.Dialog().ok( get_string(14117), get_string(30155) )
|
|
|
|
except Exception:
|
|
|
|
xbmcplugin.endOfDirectory(PLUGIN_ID)
|
|
|
|
|
|
def favorite_add(name, url, fav_mode, thumb, fanart, plot, cat, folder, play):
|
|
|
|
""" add favorite from name """
|
|
|
|
data = favorites_load()
|
|
data.append((name, url, fav_mode, thumb, fanart, plot, cat, folder, play))
|
|
fav_file = open( favorites, 'w' )
|
|
fav_file.write(json.dumps(data))
|
|
fav_file.close()
|
|
|
|
notify( get_string(30152), name, thumb )
|
|
|
|
|
|
def favorite_remove( name ):
|
|
|
|
""" remove favorite from name """
|
|
|
|
# TODO: remove via something more unique instead
|
|
# TODO: remove via a method that doesn't require to loop through all favorites
|
|
|
|
data = favorites_load()
|
|
|
|
if data:
|
|
for index in range(len(data)):
|
|
if data[index][0] == name:
|
|
del data[index]
|
|
fav_file = open( favorites, 'w' )
|
|
fav_file.write(json.dumps(data))
|
|
fav_file.close()
|
|
break
|
|
|
|
notify( get_string(30154), name )
|
|
|
|
|
|
|
|
def favorites_import():
|
|
|
|
""" Due to plugin name change from original fork, the favorites will need to be imported """
|
|
|
|
if not xbmcgui.Dialog().yesno(
|
|
'Import Favorites',
|
|
'This will replace the favorites with the plugin.video.rumble.matrix version.\nProceed?',
|
|
nolabel = 'Cancel',
|
|
yeslabel = 'Ok'
|
|
):
|
|
return
|
|
|
|
# no point trying to run this as it didn't exist for python 2
|
|
if six.PY2:
|
|
notify( 'Favorites Not Found' )
|
|
return
|
|
|
|
# make sure path exists
|
|
favorites_create()
|
|
|
|
#load matrix favourites
|
|
rumble_matrix_dir = xbmcvfs.translatePath(os.path.join('special://home/userdata/addon_data/plugin.video.rumble.matrix', 'favorites.dat'))
|
|
|
|
if os.path.exists(rumble_matrix_dir):
|
|
rumble_matrix = open( rumble_matrix_dir ).read()
|
|
|
|
if rumble_matrix:
|
|
fav_file = open( favorites, 'w' )
|
|
fav_file.write(rumble_matrix)
|
|
fav_file.close()
|
|
notify( 'Imported Favorites' )
|
|
return
|
|
|
|
notify( 'Favorites Not Found' )
|
|
|
|
|
|
def login_session_reset():
|
|
|
|
""" Forces a rumble session reset """
|
|
|
|
RUMBLE_USER.reset_session_details()
|
|
# Session Reset
|
|
notify( get_string(30200) )
|
|
|
|
def login_test():
|
|
|
|
""" Method that resets session, then tests the login """
|
|
|
|
RUMBLE_USER.reset_session_details()
|
|
|
|
if RUMBLE_USER.has_login_details():
|
|
if RUMBLE_USER.login():
|
|
# Login Success
|
|
notify( get_string(30201) )
|
|
else:
|
|
# Login Failed
|
|
notify( get_string(30202) )
|
|
else:
|
|
# No details detected
|
|
notify( get_string(30203) )
|
|
|
|
def subscribe( name, action ):
|
|
|
|
""" Attempts to (un)subscribe to rumble channel """
|
|
|
|
# make sure we have a session
|
|
if RUMBLE_USER.has_session():
|
|
|
|
action_type = False
|
|
if '/user/' in name:
|
|
name = name.replace( '/user/', '' )
|
|
action_type = 'user'
|
|
elif '/c/' in name:
|
|
name = name.replace( '/c/', '' )
|
|
action_type = 'channel'
|
|
|
|
if action_type:
|
|
|
|
# subscribe to action
|
|
data = RUMBLE_USER.subscribe( action, action_type, name )
|
|
|
|
if data:
|
|
|
|
# Load data from JSON
|
|
data = json.loads(data)
|
|
|
|
# make sure everything looks fine
|
|
if data.get( 'user', False ) and data.get( 'data', False ) \
|
|
and data[ 'user' ][ 'logged_in' ] and data[ 'data' ][ 'thumb' ]:
|
|
|
|
if action == 'subscribe':
|
|
notify( 'Subscribed to ' + name, None, data[ 'data' ][ 'thumb' ] )
|
|
else:
|
|
notify( 'Unubscribed to ' + name, None, data[ 'data' ][ 'thumb' ] )
|
|
|
|
return True
|
|
|
|
notify( 'Unable to to perform action' )
|
|
|
|
return False
|
|
|
|
|
|
def add_dir( name, url, mode, images = {}, info_labels = {}, cat = '', folder=True, fav_context=False, play=0, subscribe_context=False ):
|
|
|
|
""" Adds directory items """
|
|
|
|
art_dict = {
|
|
'thumb': images.get( 'thumb', HOME_DIR + 'icon.png' ),
|
|
'fanart': images.get( 'fanart', HOME_DIR + 'fanart.jpg' ),
|
|
}
|
|
|
|
# set default image location to MEDIA_DIR
|
|
for art_type, art_loc in art_dict.items():
|
|
if art_loc:
|
|
if not art_loc.startswith( HOME_DIR ) and \
|
|
not art_loc.startswith( 'http' ) and \
|
|
not art_loc.startswith( '\\' ):
|
|
art_dict[ art_type ] = MEDIA_DIR + art_dict[ art_type ]
|
|
|
|
link_params = {
|
|
'url': url,
|
|
'mode': str( mode ),
|
|
'name': name,
|
|
'thumb': art_dict[ 'thumb' ],
|
|
'fanart': art_dict[ 'fanart' ],
|
|
'plot': info_labels.get( 'plot', '' ),
|
|
'cat': cat,
|
|
}
|
|
|
|
context_menu = []
|
|
|
|
if play:
|
|
link_params['play'] = str( play )
|
|
|
|
link = build_url( link_params )
|
|
|
|
list_item = xbmcgui.ListItem( name )
|
|
if folder:
|
|
list_item.setArt({'icon': 'DefaultFolder.png', 'thumb': art_dict[ 'thumb' ]})
|
|
else:
|
|
list_item.setArt({'icon': 'DefaultVideo.png', 'thumb': art_dict[ 'thumb' ]})
|
|
xbmcplugin.setContent(PLUGIN_ID, 'videos')
|
|
|
|
if play == 2 and mode == 4:
|
|
list_item.setProperty('IsPlayable', 'true')
|
|
context_menu.append((get_string(30158), 'Action(Queue)'))
|
|
|
|
if RUMBLE_USER.has_login_details():
|
|
# need to get current
|
|
params=get_params()
|
|
current_url = params.get( 'url', None )
|
|
|
|
if '/playlists/watch-later' in current_url:
|
|
# delete watch later context
|
|
context_menu.append(('Delete from Watch Later','RunPlugin(%s)' % build_url( {'mode': '12','url': url, 'cat':'delete'} )))
|
|
else:
|
|
# add watch later context
|
|
context_menu.append(('Add to Watch Later','RunPlugin(%s)' % build_url( {'mode': '12','url': url, 'cat':'add'} )))
|
|
|
|
info_labels['title'] = name
|
|
if play:
|
|
# adds information context menu
|
|
info_labels['mediatype'] = 'tvshow'
|
|
|
|
item_set_info( list_item, info_labels )
|
|
|
|
list_item.setProperty( 'fanart_image', art_dict[ 'fanart' ] )
|
|
|
|
if RUMBLE_USER.has_login_details():
|
|
|
|
if subscribe_context:
|
|
if subscribe_context['subscribe']:
|
|
context_menu.append(('Subscribe to ' + subscribe_context['name'],'RunPlugin(%s)' % build_url( {'mode': '11','name': subscribe_context['name'], 'cat': 'subscribe'} )))
|
|
else:
|
|
context_menu.append(('Unsubscribe to ' + subscribe_context['name'],'RunPlugin(%s)' % build_url( {'mode': '11','name': subscribe_context['name'], 'cat': 'unsubscribe'} )))
|
|
|
|
if play == 2 and mode == 4:
|
|
context_menu.append(('Comments','RunPlugin(%s)' % build_url( {'mode': '13','url': url} )))
|
|
|
|
if fav_context:
|
|
|
|
favorite_str = favorites_load( True )
|
|
|
|
try:
|
|
name_fav = json.dumps(name)
|
|
except Exception:
|
|
name_fav = name
|
|
|
|
try:
|
|
|
|
# checks fav name via string (I do not like how this is done, so will redo in future)
|
|
if name_fav in favorite_str:
|
|
context_menu.append((get_string(30153),'RunPlugin(%s)' % build_url( {'mode': '6','name': name} )))
|
|
else:
|
|
fav_params = {
|
|
'url': url,
|
|
'mode': '5',
|
|
'name': name,
|
|
'thumb': art_dict[ 'thumb' ],
|
|
'fanart': art_dict[ 'fanart' ],
|
|
'plot': info_labels.get( 'plot', '' ),
|
|
'cat': cat,
|
|
'folder': str(folder),
|
|
'fav_mode': str(mode),
|
|
'play': str(play),
|
|
}
|
|
|
|
context_menu.append((get_string(30151),'RunPlugin(%s)' %build_url( fav_params )))
|
|
except Exception:
|
|
pass
|
|
|
|
if context_menu:
|
|
list_item.addContextMenuItems(context_menu)
|
|
|
|
xbmcplugin.addDirectoryItem(handle=PLUGIN_ID, url=link, listitem=list_item, isFolder=folder)
|
|
|
|
def playlist_manage( url, action="add" ):
|
|
|
|
""" Adds to Rumble's Playlist """
|
|
video_id = get_playlist_video_id( url )
|
|
|
|
if video_id:
|
|
if action == "add":
|
|
RUMBLE_USER.playlist_add_video( video_id )
|
|
message = "Added to playlist"
|
|
else:
|
|
RUMBLE_USER.playlist_delete_video( video_id )
|
|
message = "Deleted from playlist"
|
|
else:
|
|
if action == "add":
|
|
message = "Cannot add to playlist"
|
|
else:
|
|
message = "Cannot delete from playlist"
|
|
|
|
notify( message, "Playlist" )
|
|
|
|
def comments_show( url ):
|
|
|
|
""" Retrieves and shows video's comments in a modal """
|
|
|
|
video_id = get_video_id( url )
|
|
|
|
if video_id:
|
|
win = CommentWindow(
|
|
'addon-rumble-comments.xml',
|
|
ADDON.getAddonInfo('path'),
|
|
'default',
|
|
video_id=video_id
|
|
)
|
|
win.doModal()
|
|
del win
|
|
else:
|
|
notify( "Cannot find comments", "Comments" )
|
|
|
|
def main():
|
|
|
|
""" main method to start plugin """
|
|
|
|
params=get_params()
|
|
|
|
mode=int(params.get( 'mode', 0 ))
|
|
page=int(params.get( 'page', 1 ))
|
|
play=int(params.get( 'play', 0 ))
|
|
fav_mode=int(params.get( 'fav_mode', 0 ))
|
|
|
|
url = params.get( 'url', None )
|
|
if url:
|
|
url=urllib.parse.unquote_plus(url)
|
|
|
|
name = params.get( 'name', None )
|
|
if name:
|
|
name = urllib.parse.unquote_plus(name)
|
|
|
|
thumb=params.get( 'thumb', None )
|
|
if thumb:
|
|
thumb=urllib.parse.unquote_plus(thumb)
|
|
|
|
fanart=params.get( 'fanart', None )
|
|
if fanart:
|
|
fanart=urllib.parse.unquote_plus(fanart)
|
|
|
|
plot=params.get( 'plot', None )
|
|
if plot:
|
|
plot=urllib.parse.unquote_plus(plot)
|
|
|
|
subtitle=params.get( 'subtitle', None )
|
|
if subtitle:
|
|
subtitle=urllib.parse.unquote_plus(subtitle)
|
|
|
|
cat=params.get( 'cat', None )
|
|
if cat:
|
|
cat=urllib.parse.unquote_plus(cat)
|
|
|
|
search=params.get( 'search', None )
|
|
if search:
|
|
search=urllib.parse.unquote_plus(search)
|
|
|
|
folder=params.get( 'folder', None )
|
|
if folder:
|
|
folder=urllib.parse.unquote_plus(folder)
|
|
|
|
folder=params.get( 'folder', None )
|
|
if folder:
|
|
folder=urllib.parse.unquote_plus(folder)
|
|
|
|
|
|
if mode==0:
|
|
home_menu()
|
|
elif mode==1:
|
|
search_menu()
|
|
elif mode==2:
|
|
search_items(url,cat)
|
|
elif mode==3:
|
|
if search and search is not None:
|
|
pagination(url, page, cat, search)
|
|
else:
|
|
pagination(url, page, cat)
|
|
elif mode==4:
|
|
play_video(name, url, thumb, play)
|
|
elif mode in [5,6]:
|
|
if '\\ ' in name:
|
|
name = name.split('\\ ')[1]
|
|
if ' - ' in name:
|
|
name = name.split(' - ')[0]
|
|
if mode == 5:
|
|
favorite_add( name, url, fav_mode, thumb, fanart, plot, cat, str(folder), str(play) )
|
|
else:
|
|
favorite_remove( name )
|
|
elif mode==7:
|
|
favorites_show()
|
|
elif mode==8:
|
|
ADDON.openSettings()
|
|
elif mode==9:
|
|
favorites_import()
|
|
elif mode==10:
|
|
login_session_reset()
|
|
elif mode==11:
|
|
subscribe(name, cat)
|
|
elif mode==12:
|
|
playlist_manage(url, cat)
|
|
elif mode==13:
|
|
comments_show(url)
|
|
elif mode==14:
|
|
login_test()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|