# -*- coding: utf-8 -*- from __future__ import unicode_literals from kodi_six import xbmc, xbmcaddon, xbmcgui, xbmcplugin import sys import os import urllib import json import io import re import time from datetime import date, datetime, timedelta import random import math from unidecode import unidecode addonID = 'plugin.video.vidfltr' addon = xbmcaddon.Addon(id=addonID) pluginhandle = int(sys.argv[1]) translation = addon.getLocalizedString addonDir = xbmc.translatePath(addon.getAddonInfo('path')) defaultFanart = os.path.join(addonDir, 'resources/img/kodi/noicon.png') icon = os.path.join(addonDir, 'resources/img/kodi/icon.png') clearlogo = os.path.join(addonDir, 'resources/img/kodi/clearlogo.png') fanart = os.path.join(addonDir, 'resources/img/kodi/fanart.png') banner = os.path.join(addonDir, 'resources/img/kodi/banner.png') poster = os.path.join(addonDir, 'resources/img/kodi/poster.png') addon_work_folder = xbmc.translatePath("special://profile/addon_data/" + addonID) jsonVideos = xbmc.translatePath("special://profile/addon_data/" + addonID + "/videos.json") jsonArtists = xbmc.translatePath("special://profile/addon_data/" + addonID + "/artists.json") jsonStyles = xbmc.translatePath("special://profile/addon_data/" + addonID + "/styles.json") maxFileAge = int(addon.getSetting("maxFileAge")) maxFileAge = maxFileAge * 60 mediatype = addon.getSetting("mediatype") # show only official, fanmade or all videos? videoselection = str(addon.getSetting("videoselection")).lower() if videoselection != "2": # videoselection is set to official or fanmade # despite this selection show all in similar playlists and more from? relatedselection = str(addon.getSetting("relatedselection")).lower() else: # if videoselection is set to show all videos relatedselection should # also be true if the setting was set to false beforehand relatedselection = "true" playLocalFile = str(addon.getSetting("playLocalFile")).lower() filesinlists = int(addon.getSetting("filesinlists")) useYTDL = addon.getSetting("useytdl") # show if video is unofficial in title showunoffintitle = addon.getSetting("showunoffintitle") # summed up provider playcount considered to be played often pcounthigh = int(addon.getSetting("pcounthigh")) # local playcount considered to be played often lcounthigh = int(addon.getSetting("lcounthigh")) # addon developer playcount considered to be often played acounthigh = int(addon.getSetting("acounthigh")) # summed up provider comment count considered to be high ccounthigh = int(addon.getSetting("ccounthigh")) # like count considered to be high likecounthigh = int(addon.getSetting("likecounthigh")) # dislike count considered to be high dislikecounthigh = int(addon.getSetting("dislikecounthigh")) # needed for comapatibilty mode used with KODI <= 17 aka Krypton xbmcversion = int(xbmc.getInfoLabel('System.BuildVersion')[:2]) # get folder settings showincoming = addon.getSetting("show-incoming") showincominghits = addon.getSetting("show-incoming-hits") showrandom = addon.getSetting("show-random") showsorted = addon.getSetting("show-sorted") showstyles = addon.getSetting("show-styles") showartists = addon.getSetting("show-artists") showcountries = addon.getSetting("show-countries") showsearch = addon.getSetting("show-search") showmuslyrandom = addon.getSetting("show-musly-random") showupdate = addon.getSetting("show-update") # play from here. Does work in general but refreshes the container which is bit contra-productive in random lists ;) # But it's ok in a musly-list?: # XBMC.PlayMedia(plugin://plugin.video.vidfltr/?mode=sortTitlesBy&url=%26start%3d1555257469%26limit%3drelated%26sort%3dnone,isdir if not os.path.isdir(addon_work_folder): os.mkdir(addon_work_folder) def getVideos(): if not os.path.isfile(jsonVideos): updateData() else: fileTime = os.path.getmtime(jsonVideos) now = time.time() if now - fileTime > maxFileAge: updateData() with io.open(jsonVideos, 'r', encoding='utf-8') as f: data = json.load(f) return data def getArtists(): if not os.path.isfile(jsonArtists): updateData() else: fileTime = os.path.getmtime(jsonArtists) now = time.time() if now - fileTime > maxFileAge: updateData() with io.open(jsonArtists, 'r', encoding='utf-8') as f: data = json.load(f) return data def getStyles(): if not os.path.isfile(jsonStyles): updateData() else: fileTime = os.path.getmtime(jsonStyles) now = time.time() if now - fileTime > maxFileAge: updateData() with io.open(jsonStyles, 'r', encoding='utf-8') as f: data = json.load(f) return data def updateData(): try: target = urllib.URLopener() target.retrieve("https://vidfltr.slashproc.org/api/videos.json", jsonVideos) target = urllib.URLopener() target.retrieve("https://vidfltr.slashproc.org/api/artists.json", jsonArtists) target = urllib.URLopener() target.retrieve("https://vidfltr.slashproc.org/api/styles.json", jsonStyles) except: # return to getVideos/getArtists on network error or 404 or... # ...to make it not fatal if the json can't be updated return def alphabet(): alphabet = ['#'] for letter in range(65, 91): alphabet.append(chr(letter)) return alphabet def main(): if showincoming == "true": addDir(translation(30007), '&limit=all&sort=date&start=0', 'sortTitlesBy', fanart) if showincominghits == "true": addDir(translation(30008), '&limit=all&sort=date&start=0&hit=true', 'sortTitlesBy', fanart) if showrandom == "true": addDir(translation(30029), '', 'mainrandom', fanart) if showsorted == "true": addDir(translation(30012), '', 'mainsorted', fanart) if showstyles == "true": addDir(translation(30022), '&sort=all', 'styles', fanart) if showartists == "true": addDir(translation(30005), '', 'artists', fanart) if showcountries == "true": addDir(translation(30118), '', 'countrycodes', fanart) if showsearch == "true": addDir(translation(30001), '', 'search', fanart) if showupdate == "true": addDir(translation(30006), '', 'updateData', fanart) endOfDirectory() def artists(): for alpha in alphabet(): addDir(alpha, '&style=artists&limit=' + alpha + '&start=0', 'showArtist', fanart) endOfDirectory() def countrycodes(): data = getArtists() result = [] for entry in data: countrycode = entry['countrycode'] if countrycode != "" and countrycode not in result: result.append(countrycode) elif countrycode == "" and translation(30124) not in result: result.append(translation(30124)) result.sort() for entry in result: addDir(entry, '&style=country&limit=' + entry + '&start=0', 'showArtist', fanart, len(result)) endOfDirectory() def mainrandom(): addDir(translation(30034), '&limit=year&sort=random&start=0', 'sortTitlesBy', fanart) addDir(translation(30035), '&limit=year&sort=random&start=0&hit=true', 'sortTitlesBy', fanart) addDir(translation(30017), '&limit=all&sort=random&start=0', 'sortTitlesBy', fanart) addDir(translation(30031), '&limit=all&sort=random&start=0&hit=true', 'sortTitlesBy', fanart) addDir(translation(30117), '&sort=random', 'numbers', fanart) addDir(translation(30022), '&sort=random', 'styles', fanart) if showmuslyrandom == "true": addDir(translation(30024), '&limit=related&start=0&sort=none', 'sortTitlesBy', fanart) endOfDirectory() def mainsorted(): addDir(translation(30032), '&limit=all&sort=date&start=0', 'sortTitlesBy', fanart) addDir(translation(30033), '&limit=all&sort=date&start=0&hit=true', 'sortTitlesBy', fanart) addDir(translation(30117), '&sort=sorted', 'numbers', fanart) addDir(translation(30022), '&sort=sorted', 'styles', fanart) endOfDirectory() def numbers(): if sort == "random": addDir(translation(30111), '&limit=all&sort=random&start=0&count=pcount', 'sortTitlesBy', fanart) addDir(translation(30113), '&limit=all&sort=random&start=0&count=acount', 'sortTitlesBy', fanart) addDir(translation(30114), '&limit=all&sort=random&start=0&count=comments', 'sortTitlesBy', fanart) addDir(translation(30115), '&limit=all&sort=random&start=0&count=likes', 'sortTitlesBy', fanart) addDir(translation(30116), '&limit=all&sort=random&start=0&count=dislikes', 'sortTitlesBy', fanart) addDir(translation(30119), '&limit=all&sort=random&start=0&count=controversial', 'sortTitlesBy', fanart) endOfDirectory() if sort == "sorted": addDir(translation(30111), '&limit=all&sort=count&start=0&count=pcount', 'sortTitlesBy', fanart) addDir(translation(30113), '&limit=all&sort=count&start=0&count=acount', 'sortTitlesBy', fanart) addDir(translation(30114), '&limit=all&sort=count&start=0&count=comments', 'sortTitlesBy', fanart) addDir(translation(30115), '&limit=all&sort=count&start=0&count=likes', 'sortTitlesBy', fanart) addDir(translation(30116), '&limit=all&sort=count&start=0&count=dislikes', 'sortTitlesBy', fanart) addDir(translation(30119), '&limit=all&sort=count&start=0&count=controversial', 'sortTitlesBy', fanart) endOfDirectory() def play(url): data = getVideos() result = [] slug = url if slug != "": for entry in data: if entry['slug'] == slug: # play from local file. # does only work on addon developer pc -- atleast for now if playLocalFile == "true": musicvideoid = "-1" try: # try to find music video in library with utc time from slug dateadded = datetime.utcfromtimestamp(float(entry['slug'])) dateadded = str(dateadded) jsonRespond = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "params": {"sort": {"order": "ascending", "method": "title"}, "filter": {"operator": "contains", "field": "dateadded", "value": "%s"}, "properties": ["file", "playcount", "genre", "artist", "title"]}, "limits":{"end":0,"start":0}, "method": "VideoLibrary.GetMusicvideos", "id": "libMusicVideos"}' % dateadded) local = json.loads(jsonRespond) for vid in local["result"]["musicvideos"]: playback_url = vid["file"] musicvideoid = int(vid["musicvideoid"]) playcount = int(vid['playcount']) genre = vid['genre'] artist = vid['artist'] artist = ''.join(artist) title = vid['title'] title = (artist + ' - ' + title ) xbmc.log(msg="date from slug", level=xbmc.LOGDEBUG) except: try: # if not found because of inconsistencies based on MEZ vs. MESZ try to find music video in library with time from dateadded dateadded = entry['dateadded'] jsonRespond = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "params": {"sort": {"order": "ascending", "method": "title"}, "filter": {"operator": "contains", "field": "dateadded", "value": "%s"}, "properties": ["file", "playcount", "genre", "artist", "title"]}, "limits":{"end":0,"start":0}, "method": "VideoLibrary.GetMusicvideos", "id": "libMusicVideos"}' % dateadded) local = json.loads(jsonRespond) for vid in local["result"]["musicvideos"]: playback_url = vid["file"] musicvideoid = int(vid["musicvideoid"]) playcount = int(vid['playcount']) genre = vid['genre'] artist = vid['artist'] artist = ''.join(artist) title = vid['title'] title = (artist + ' - ' + title ) xbmc.log(msg="date from dateadded", level=xbmc.LOGDEBUG) except: if useYTDL == "true": # if play from local file is set but nonetheless not found use Provider-URL xbmc.log(msg="not found, play from provider", level=xbmc.LOGDEBUG) import YDStreamExtractor idVideo = entry['purl'] ytdl = YDStreamExtractor.getVideoInfo(idVideo) playback_url = ytdl.streamURL() musicvideoid = "-1" else: # without youtube_dl xbmc.log(msg="without ytdl", level=xbmc.LOGDEBUG) playback_url = entry['url'] # default mode: play from provider -- either with ytdl or vimeo/youtube/dailymotion addon else: xbmc.log(msg="play from provider", level=xbmc.LOGDEBUG) if useYTDL == "true": xbmc.log(msg="with ytdl", level=xbmc.LOGDEBUG) import YDStreamExtractor idVideo = entry['purl'] ytdl = YDStreamExtractor.getVideoInfo(idVideo) playback_url = ytdl.streamURL() else: if "vimeo" in entry['provider']: if xbmc.getCondVisibility("System.HasAddon(plugin.video.vimeo)") and int((xbmcaddon.Addon("plugin.video.vimeo").getAddonInfo("version").strip().replace(".",""))) >= 500: xbmc.log(msg="using the new Vimeo addon from jaylinski", level=xbmc.LOGNOTICE) playback_url = entry['url'] else: # quick hard coded workaround xbmc.log(msg="old vimeo addon is broken, playing with ytdl. Please install the new jaylinski Vimeo Addon from the official KODI Repo", level=xbmc.LOGNOTICE) import YDStreamExtractor idVideo = entry['purl'] ytdl = YDStreamExtractor.getVideoInfo(idVideo) playback_url = ytdl.streamURL() else: # without youtube_dl xbmc.log(msg="without ytdl", level=xbmc.LOGDEBUG) playback_url = entry['url'] item = xbmcgui.ListItem(path=playback_url) # avoids CCurlFile::Stat - Failed: Unsupported protocol(1) for plugin:// # disabled, unclear if this is the way to #item.setContentLookup(False) # add some Listitem info for local files # should already be set in addVideo() but it isn't, doh! if playLocalFile == "true" and not musicvideoid == "-1": item.setInfo(type="video", infoLabels={"mediatype": mediatype, "dbid": musicvideoid, "title": title, "genre": genre }) # doesn't work # item.setProperty('StartOffset', '56.4') # Bug? It maybe works with StartPercent, see https://forum.kodi.tv/showthread.php?tid=129188&pid=2443114#pid2443114 # item.setProperty("VolumeAmplification", "7") # xbmc.log(msg=item.getProperty('VolumeAmplification'), level=xbmc.LOGNOTICE) xbmc.log(msg=playback_url, level=xbmc.LOGNOTICE) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, item) def styles(): data = getVideos() channels = [] xbmc.log(msg=sort, level=xbmc.LOGDEBUG) for entry in data: if entry['style'] not in channels: channels.append(entry['style']) length = len(channels) channels.sort() for channel in channels: # don't use special folder icons as long as there is'nt a nice icon for every style # addDir(channel, '&style=' + channel + '&sort=' + sort, 'showChannel', getFanart(channel), length) # don't use special folder icons as long as there is'nt a nice icon for every style addDir(channel, '&style=' + channel + '&sort=' + sort, 'showChannel', fanart, length) endOfDirectory() def showChannel(style, sort): channel = style if channel != "": fanart = getFanart(channel) xbmc.log(msg=channel, level=xbmc.LOGDEBUG) if sort == "sorted" or sort == "all": addDir(translation(30032), '&limit=' + channel + '&sort=date&start=0', 'sortTitlesBy', fanart, 6) addDir(translation(30033), '&limit=' + channel + '&sort=date&start=0&hit=true', 'sortTitlesBy', fanart, 6) # genre splitted artists, only menu entry which uses the old initials mode and # therefore entries where the artist isn't at the beginning of the title are excluded :-( addDir(translation(30005), channel, 'sortTitleInitials', fanart, 6) if sort == "random" or sort == "all": addDir(translation(30029), '&limit=' + channel + '&sort=random&start=0', 'sortTitlesBy', fanart, 6) addDir(translation(30031), '&limit=' + channel + '&sort=random&start=0&hit=true', 'sortTitlesBy', fanart, 6) endOfDirectory() def sortTitleInitials(channel=""): data = getVideos() result = [] fanart = getFanart(channel) if channel != "": for entry in data: if entry['style'] == channel: if len(entry['title']) > 0: l = entry['title'][0].upper() if not re.match('^([a-z|A-Z])', l): l = '#' if l not in result: if videoselection == "0": if "true" in entry['official'].lower(): result.append(l) elif videoselection == "1": if "false" in entry['official'].lower(): result.append(l) else: result.append(l) result.sort() for entry in result: addDir(entry, channel + '|' + entry, 'sortTitles', fanart, len(result)) endOfDirectory() def sortArtistInitials(channel=""): data = getVideos() result = [] fanart = getFanart(channel) if channel != "": for entry in data: for artist in entry['artists']: # xbmc.log(msg=channel.encode('utf-8'), level=xbmc.LOGNOTICE) # xbmc.log(msg=artist.encode('utf-8'), level=xbmc.LOGNOTICE) if artist not in channel: if len(artist) > 0: l = artist[0].upper() if not re.match('^([a-z|A-Z])', l): l = '#' if l not in result: if videoselection == "0": if "true" in entry['official'].lower(): result.append(l) elif videoselection == "1": if "false" in entry['official'].lower(): result.append(l) else: result.append(l) result.sort() for entry in result: addDir(entry, channel + '|' + entry, 'sortArtists', fanart, len(result)) endOfDirectory() def sortTitles(channelInitial=""): xbmcplugin.setContent(pluginhandle, 'musicvideos') data = getVideos() result = [] params = channelInitial.split("|") channel = "" initial = "" if len(params) > 1: channel = params[0] initial = params[1] # else: # channel = params[0] fanart = getFanart(channel) # xbmc.log(msg=initial.encode('utf-8'), level=xbmc.LOGNOTICE) if channel != "": xbmc.log(msg=channel.encode('utf-8'), level=xbmc.LOGDEBUG) for entry in data: if entry['style'] == channel: i = entry['title'][0].upper() if initial == '#': if not re.match('^([a-z|A-Z])', i): if videoselection == "0": if "true" in entry['official'].lower(): result.append(entry) elif videoselection == "1": if "false" in entry['official'].lower(): result.append(entry) else: result.append(entry) else: if initial == i: if videoselection == "0": if "true" in entry['official'].lower(): result.append(entry) elif videoselection == "1": if "false" in entry['official'].lower(): result.append(entry) else: result.append(entry) # sloooow #elif channel in entry['artists']: # result.append(entry) result.sort(key=lambda entry: entry['title'].upper()) for entry in result: addVideo(entry) endOfDirectory() def showArtist(style, limit, start): # limit artists by first letter or country data = getArtists() preresult = [] result = [] # xbmc.log(msg=url, level=xbmc.LOGNOTICE) # xbmc.log(msg=style, level=xbmc.LOGNOTICE) # xbmc.log(msg=limit, level=xbmc.LOGNOTICE) start = int(start) end = start + filesinlists nextstart = end + 1 if style == 'artists': for entry in data: i = unidecode(entry['artist'])[0].upper() if limit == '#': if not re.match('^([a-z|A-Z])', i): preresult.append(entry) else: if limit == i: preresult.append(entry) elif style == 'country': for entry in data: i = entry['countrycode'] if limit == i: preresult.append(entry) elif limit == translation(30124) and i == "": preresult.append(entry) # limit the result list according to the "Video Selection" Setting for entry in preresult: if videoselection == "0": if entry['official'] >= "1": result.append(entry) elif videoselection == "1": if entry['unofficial'] >= "1": result.append(entry) elif videoselection == "2": result.append(entry) maximum = len(result) for entry in result[start:end]: artist = entry['artist'] official = int(entry['official']) unofficial = int(entry['unofficial']) if entry['fanart'] != "": fanart = entry['fanart'] elif entry['thumbnail'] != "": fanart = entry['thumbnail'] else: fanart = "" if videoselection == "0": addDir("%s (%s)" % (artist, official), unidecode(artist.upper()), 'sortArtists', fanart, official) elif videoselection == "1": addDir("%s (%s)" % (artist, unofficial), unidecode(artist.upper()), 'sortArtists', fanart, unofficial) elif videoselection == "2": addDir("%s (%s/%s)" % (artist, official, unofficial), unidecode(artist.upper()), 'sortArtists', fanart, (official + unofficial)) if maximum > end: pagemax = (float(maximum) / float(filesinlists)) pagenext = (float(nextstart) / float(filesinlists)) fanart = 'DefaultFolder.png' addDir("%s: %s/%s" % (translation(30036), str(int(math.ceil(pagenext))), str(int(math.ceil(pagemax)))), '&style=' + style + '&limit=' + limit + '&start=' + str(nextstart), 'showArtist', fanart) endOfDirectory() def sortArtists(channel=""): # get videos for individual artist xbmcplugin.setContent(pluginhandle, 'musicvideos') data = getVideos() result = [] # using param=string parameters does not work here because some obscure chars # are lost in translation so the old implementation from Mediathek Direkt with # splitting on | will be used. Have to take a deeper look to fix this... params = channel.split("|") # xbmc.log(msg=url, level=xbmc.LOGNOTICE) channel = params[0] if len(params) > 1: start = params[1] #show "more from..." artists if channel != "" and channel == "relartists": fanart = getFanart(channel) for entry in data: if entry['slug'] == start: artists_upper = [unidecode(artist.upper()) for artist in entry['artists']] channels = [] for channel in artists_upper: channels.append(channel) # nested loops don't look like an optimal solution and are a bit slow for entry in data: for artist in channels: if artist in [unidecode(name.upper()) for name in entry['artists']]: if entry not in result: # limit selection based on videoselection setting if show all isn't activated if relatedselection != "true" and videoselection != "2": if videoselection == "0": if "true" in entry['official'].lower(): result.append(entry) elif videoselection == "1": if "false" in entry['official'].lower(): result.append(entry) else: result.append(entry) # show artist elif channel != "": fanart = getFanart(channel) for entry in data: artists_upper = [unidecode(artist.upper()) for artist in entry['artists']] if channel in artists_upper: if videoselection == "0": if "true" in entry['official'].lower(): result.append(entry) elif videoselection == "1": if "false" in entry['official'].lower(): result.append(entry) else: result.append(entry) result.sort(key=lambda entry: entry['title'].upper()) for entry in result: addVideo(entry) endOfDirectory() def sortTitlesBy(limit, sort, start): xbmcplugin.setContent(pluginhandle, 'musicvideos') data = getVideos() result = [] channel = limit start = int(start) end = start + filesinlists nextstart = end + 1 fanart = getFanart(channel) # limit selection if "all" isn't activated in videoselection #if videoselection != "2" and channel != "related" and channel != "relartists": if videoselection != "2": data = limitselection(data) # limit to hits if hit != "" and hit == "true": for entry in data: if entry['hit'] == "true": result.append(entry) data = result result = [] # played often at provider if mycount != "" and mycount == "pcount" and sort != "count": for entry in data: # played often at provider if int(entry['pcount']) >= pcounthigh: result.append(entry) data = result result = [] # played often by addon developer if mycount != "" and mycount == "acount" and sort != "count": for entry in data: if int(entry['acount']) >= acounthigh: result.append(entry) data = result result = [] # often commented if mycount != "" and mycount == "comments" and sort != "count": for entry in data: if int(entry['comments']) >= ccounthigh: result.append(entry) data = result result = [] # often liked if mycount != "" and mycount == "likes" and sort != "count": for entry in data: if int(entry['likes']) >= likecounthigh: result.append(entry) data = result result = [] # often disliked if mycount != "" and mycount == "dislikes" and sort != "count": for entry in data: if int(entry['dislikes']) >= dislikecounthigh: result.append(entry) data = result result = [] # controversial based on nearly the same number of likes and dislikes if mycount != "" and mycount == "controversial": for entry in data: if int(entry['controversial']) == int(1): result.append(entry) data = result result = [] # limit by style if channel != "" and channel != "all" and channel != "year" and channel != "related": for entry in data: if entry['style'] == channel: result.append(entry) # or limit to last year # hm, either style or year, not both? elif channel != "" and channel == "year": lastyear = datetime.now() - timedelta(days=365) for entry in data: # ehrm, first version worked for many days but now fails? # related to https://bugs.python.org/issue27400 ? try: dateadded = datetime.strptime(entry['dateadded'], '%Y-%m-%d %H:%M:%S') except TypeError: import time dateadded = datetime.fromtimestamp(time.mktime(time.strptime(entry['dateadded'], '%Y-%m-%d %H:%M:%S'))) if dateadded >= lastyear: result.append(entry) # related tracks (generated with musly) elif channel != "" and channel == "related": # random musly list if start == 0: # pick a random item from the already limited list (based on videoselection setting) entry = random.choice(data) start = str(entry['slug']) else: start = str(start) # now we have to add the unlimited video list # It will be limited later, again, if the videoselection # setting should also be honoured in related lists data = getVideos() for entry in data: if entry['slug'] == start: slugs = [] # add slug for which the playlist should be shown slugs.extend(entry['slug'].split(", ")) # and add all related slugs slugs.extend(entry['related'].split(", ")) for entry in data: # add all related slugs if entry['slug'] in slugs: # filter out tracks with the same title, where only the video differs. # if a related slug is listed earlier in data (= newer video) the # main slug will be filtered out. # In this case we add it again later, after sorting the result if not filter(lambda result: result['title'] == entry['title'], result): result.append(entry) # slugs are sorted newest first because that's how they occur in the json # so we have to sort the result to the order in related aka the musly order (with prepended main slug) order_dict = {slug: index for index, slug in enumerate(slugs)} result.sort(key=lambda x: order_dict[x["slug"]]) # check if the first entries slug is the main slug # if not remove it and add back the main entry. # I know, this is really ugly :-( if result[0]['slug'] != start: result.pop(0) for entry in data: if entry['slug'] == start: result.insert(0, entry) # limit selection? if videoselection != "2" and relatedselection != "true": result = limitselection(result) maximum = len(result) # related is a list with max 21 entries, so we have to set start always to 0 start = 0 # "more from..." artists elif channel != "" and channel == "relartists": start = str(start) for entry in data: if entry['slug'] == start: # add slug for which the artists should be shown artists = "" artists = entry['artists'] for entry in data: xbmc.log(msg=str(artists), level=xbmc.LOGNOTICE) # add all entries with artist in artists if entry['artists'] in artists: result.append(entry) xbmc.log(msg=str(results), level=xbmc.LOGNOTICE) # limit selection? if videoselection != "2" and relatedselection != "true": result = limitselection(result) start = 0 # sorted lists (by date and by numbers. But NOT controversial, which is a fixed list) else: for entry in data: result.append(entry) if sort != "" and sort == "random": random.shuffle(result) result = result[:filesinlists] start = 0 end = filesinlists if sort != "" and sort == "date": if channel != "": modus = channel else: modus = 'all' result = result if sort != "" and sort == "count": if channel != "": modus = channel else: modus = 'all' if mycount != "" and mycount == "controversial": # controversial is either 0 or 1 so it does not makes sense to sort upon it # instead sort videos considered controversial by dislikes # change controversial in videos.json to a float? result = sorted(result, key = lambda i: (-1*float(i['dislikes']), i['title'])) else: # If count would'nt be negated one had to use reverse=true # but then the second sorting, by title would be reversed also # sort by negated count first and then by title in lexical order. result = sorted(result, key = lambda i: (-1*float(i[mycount]), i['title'])) # itemgetter, should be faster (hm, but does'nt work: ValueError: could not convert string to float: pcount) # importing "operator" for implementing itemgetter #from operator import itemgetter #result = sorted(result, key=itemgetter((float(mycount), 'title'),reverse = True)) # play all (TODO) # addDir(translation(30109), '', '', fanart) # back to VIDFLTR (only useful if the back history would be cleared) # if start > 0: # addDir(translation(30108), '', 'main', fanart) # hm, do I really want to replace the playlist on entering a folder? # playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) # playlist.clear() for entry in result[start:end]: addVideo(entry, mycount) maximum = len(result) if maximum > end and sort != "random" and channel != "year": pagemax = (float(maximum) / float(filesinlists)) pagenext = (float(nextstart) / float(filesinlists)) # next page link if sort != "" and sort == "count": addDir("%s: %s/%s" % (translation(30036), str(int(math.ceil(pagenext))), str(int(math.ceil(pagemax)))), '&limit=' + modus + '&sort=count&start=' + str(nextstart) + '&count=' + mycount, 'sortTitlesBy', fanart) elif hit != "" and hit == "true": addDir("%s: %s/%s" % (translation(30036), str(int(math.ceil(pagenext))), str(int(math.ceil(pagemax)))), '&limit=' + modus + '&sort=date&start=' + str(nextstart) + '&hit=true', 'sortTitlesBy', fanart) else: addDir("%s: %s/%s" % (translation(30036), str(int(math.ceil(pagenext))), str(int(math.ceil(pagemax)))), '&limit=' + modus + '&sort=date&start=' + str(nextstart), 'sortTitlesBy', fanart) endOfDirectory() def endOfDirectory(): # xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_UNSORTED) # xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_LABEL) # xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_DATEADDED) # xbmcplugin.addSortMethod( # pluginhandle, xbmcplugin.SORT_METHOD_PROGRAM_COUNT) # xbmcplugin.addSortMethod( # pluginhandle, xbmcplugin.SORT_METHOD_VIDEO_RUNTIME) # xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_GENRE) xbmcplugin.endOfDirectory(pluginhandle) def search(channel=""): xbmcplugin.setContent(pluginhandle, 'musicvideos') result = [] keyboard = xbmc.Keyboard('', translation(30002)) keyboard.doModal() if keyboard.isConfirmed() and keyboard.getText(): # search_string = keyboard.getText().encode('utf8').lower() search_string = keyboard.getText().lower() if len(search_string) > 1: data = getVideos() for entry in data: cEntry = entry if search_string in cEntry['title'].lower(): if channel != "": if cEntry['style'] == channel: # cEntry['title'] = cEntry['style']+': # '+cEntry['title'] if videoselection == "0": if "true" in entry['official'].lower(): result.append(cEntry) else: result.append(cEntry) else: # cEntry['title'] = cEntry['style']+': # '+cEntry['title'] if videoselection == "0": if "true" in entry['official'].lower(): result.append(cEntry) else: result.append(cEntry) elif search_string in cEntry['artist'].lower(): if channel != "": if cEntry['style'] == channel: # cEntry['title'] = cEntry['style']+': # '+cEntry['title'] if videoselection == "0": if "true" in entry['official'].lower(): result.append(cEntry) else: result.append(cEntry) else: # cEntry['title'] = cEntry['style']+': # '+cEntry['title'] if videoselection == "0": if "true" in entry['official'].lower(): result.append(cEntry) else: result.append(cEntry) result.sort(key=lambda entry: entry['title'].lower()) for entry in result: addVideo(entry) endOfDirectory() # not used (TODO?) def searchDate(channelDate=""): xbmcplugin.setContent(pluginhandle, 'musicvideos') channel = "" date = "" params = channelDate.split('|') channel = params[0] if len(params) > 1: date = params[1] result = [] if date == "": dialog = xbmcgui.Dialog() date = dialog.numeric(1, translation(30007)) date = re.sub('[^0-9|^\/]', '0', date) date = date.replace('/', '.') if (channel != "") and (len(date) == 10): data = getVideos() for entry in data: cEntry = entry if (entry['style'] == channel) and (entry['date'] == date): cEntry[1] = cEntry[2] + ': ' + cEntry[1] if videoselection == "0": if "true" in entry['official'].lower(): result.append(cEntry) else: result.append(cEntry) result.sort(key=lambda entry: entry['title'].lower()) for entry in result: addVideo(entry) endOfDirectory() # not used (TODO?) def downloadFile(video_url): # get best qualiy url bq_url = getBestQuality(video_url) # get filname from video_url filename = video_url.split('/')[-1] filetype = filename.split('.')[-1] # open browser dialog to choose destination dialog = xbmcgui.Dialog() download_dir = dialog.browse(3, translation(30102), "files") target = urllib.URLopener() fullPath = xbmc.translatePath(download_dir + filename) target.retrieve(video_url, fullPath) dialog.ok(addonID, translation(30101), str(fullPath)) # only used in style directories def getFanart(channel): fanart = os.path.join( addonDir, 'resources/images/fanart_' + channel + '.jpg') if not os.path.isfile(fanart.encode('utf-8')): fanart = icon return fanart def limitselection(data): xbmc.log(msg="limit by videoselection", level=xbmc.LOGDEBUG) result = [] for entry in data: if videoselection == "0": if "true" in entry['official']: result.append(entry) elif videoselection == "1": if "false" in entry['official']: result.append(entry) elif videoselection == "2": result.append(entry) return result def addDir(name, url, mode, iconimage, total=0): u = sys.argv[0] + "?url=" + urllib.quote_plus(url.encode('utf-8')) + "&mode=" + str(mode) # xbmc.log(msg=u, level=xbmc.LOGNOTICE) ok = True # if (xbmcversion < 17): liz = xbmcgui.ListItem(name, thumbnailImage=iconimage) # else: # With offscreen=true large lists (=folder) load much faster (needs >= krypton) # But at the end, the differences are minimal in VIDFLTR, so just drop it :-) # liz = xbmcgui.ListItem(name, iconImage=icon, thumbnailImage=iconimage, offscreen=True) description = "" if mode == "showChannel": data = getStyles() for style in data: if style['style'] == name: description = style['description'] liz.setInfo(type="Video", infoLabels={"Title": name, "PlotOutline": description, "Plot": description, "Tagline": description}) if iconimage: liz.setProperty("fanart_image", iconimage) else: liz.setProperty("fanart_image", fanart) if iconimage: liz.setArt({ 'fanart' : iconimage }) else: liz.setArt({ 'fanart' : fanart }) ok = xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=u, listitem=liz, totalItems=total, isFolder=True) return ok def addVideo(entry, mycount="playcount"): ok = True # initialize the global video playlist. The item will be added later # playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) # Altlast. Sinn dahinter? if len(entry) > 7: dateadded = datetime.utcfromtimestamp(float(entry['slug'])) dateadded = str(dateadded) slug = entry['slug'] url = '%s?mode=play&url=%s' % (sys.argv[0], slug) channel = entry['artist'] artist = entry['artist'] artists = entry['artists'] director = entry['director'] title = entry['title'] title = title.replace(""", '"') if "false" in entry['official'].lower() and "true" in showunoffintitle: title = (title + ' (vid by ' + director + ')') tracktitle = entry['tracktitle'] tracktitle = tracktitle.replace(""", '"') style = entry['style'] if mycount == "pcount": playcount = entry['pcount'] elif mycount == "lcount": playcount = entry['lcount'] else: playcount = entry['acount'] #slug = entry['slug'] if playLocalFile == "true": description = "Provider: " + entry['provider'] + "\nprovider playcount: " + entry['pcount'] + "\nlikes: " + entry['likes'] + "\ndislikes: " + entry['dislikes'] + "\ncomments: " + entry['comments'] else: description = "Provider: " + entry['provider'] + "" if "false" in entry['official'].lower(): description = ( "Unofficial Video by " + director + "\n" + description) #link = entry['slug'] fanart = entry['thumbnail'] duration = entry['duration'] # if (xbmcversion < 17): li = xbmcgui.ListItem(title) # else: # li = xbmcgui.ListItem(title, offscreen=True) li.setInfo(type="video", infoLabels={ "mediatype": mediatype, "Title": title, "Originaltitle": tracktitle, "Genre": style, "Director": director, "PlotOutline": description, "Plot": description, "Tagline": style, "Artist": entry['artists'], "dateadded": dateadded, "playcount": playcount, "Duration": duration}) li.setArt({'thumb': fanart}) li.setProperty("fanart_image", fanart) li.setProperty('IsPlayable', 'true') # I could add all entries to the current, global playlist but it doesn't look right # Imo at the end it's better if the user just uses "play from here" or the auto play next setting # playlist.add(url=url, listitem=li) li.addContextMenuItems([ (translation(30121),'XBMC.Container.Update(plugin://'+addonID+'/?mode=sortTitlesBy&url=%26start%3d'+slug+'%26limit%3drelated%26sort%3dnone)',), (translation(30122),'XBMC.Container.Update(plugin://'+addonID+'/?mode=sortArtists&url=relartists%7C'+slug+')',) ]) ok = xbmcplugin.addDirectoryItem(handle=pluginhandle, url=url, listitem=li) return ok def parameters_string_to_dict(parameters): paramDict = {} if parameters: paramPairs = parameters[1:].split("&") for paramsPair in paramPairs: paramSplits = paramsPair.split('=') if (len(paramSplits)) == 2: paramDict[paramSplits[0]] = paramSplits[1] return paramDict params = parameters_string_to_dict(sys.argv[2]) mode = urllib.unquote_plus(params.get('mode', '')).decode('utf-8') url = urllib.unquote_plus(params.get('url', '')).decode('utf-8') extraparams = parameters_string_to_dict(url) limit = urllib.unquote_plus(extraparams.get('limit', '')).decode('utf-8') sort = urllib.unquote_plus(extraparams.get('sort', '')).decode('utf-8') start = urllib.unquote_plus(extraparams.get('start', '')).decode('utf-8') start = str(start) style = urllib.unquote_plus(extraparams.get('style', '')).decode('utf-8') hit = urllib.unquote_plus(extraparams.get('hit', '')).decode('utf-8') mycount = urllib.unquote_plus(extraparams.get('count', '')).decode('utf-8') slug = urllib.unquote_plus(extraparams.get('slug', '')).decode('utf-8') #xbmc.log(msg=str(sys.argv), level=xbmc.LOGNOTICE) #xbmc.log(msg=str(mode), level=xbmc.LOGNOTICE) #xbmc.log(msg=str(url), level=xbmc.LOGNOTICE) #xbmc.log(msg=str(limit), level=xbmc.LOGDEBUG) #xbmc.log(msg=str(sort), level=xbmc.LOGDEBUG) #xbmc.log(msg=str(start), level=xbmc.LOGDEBUG) #xbmc.log(msg=str(style), level=xbmc.LOGNOTICE) if mode == 'updateData': updateData() elif mode == 'alphabet': alphabet() elif mode == 'showChannel': showChannel(style, sort) elif mode == 'search': search(url) elif mode == 'sortTitleInitials': sortTitleInitials(url) elif mode == 'sortTitles': sortTitles(url) elif mode == 'sortArtists': sortArtists(url) elif mode == 'showArtist': showArtist(style, limit, start) elif mode == 'sortTitlesBy': sortTitlesBy(limit, sort, start) elif mode == 'searchDate': searchDate(url) elif mode == 'mainsorted': mainsorted() elif mode == 'mainrandom': mainrandom() elif mode == 'numbers': numbers() elif mode == 'countrycodes': countrycodes() elif mode == 'artists': artists() elif mode == 'styles': styles() elif mode == 'related': related(limit) elif mode == 'play': play(url) else: main()