From aa239de05177c7ee013935dba69c146dc32a6a2f Mon Sep 17 00:00:00 2001 From: enen92 Date: Thu, 10 Aug 2017 18:46:30 +0100 Subject: [plugin.video.itsapixelthing] v1.0.0 [plugin.video.itsapixelthing] v1.0.0 --- plugin.video.itsapixelthing/resources/__init__.py | 0 .../resources/images/fanart.jpg | Bin 0 -> 690492 bytes .../resources/images/icon.png | Bin 0 -> 83203 bytes .../resources/images/screenshot-1.png | Bin 0 -> 1307374 bytes .../resources/images/screenshot-2.png | Bin 0 -> 1736158 bytes .../resources/images/screenshot-3.png | Bin 0 -> 1876632 bytes .../resources/images/screenshot-4.png | Bin 0 -> 2399262 bytes .../language/resource.language.en_gb/strings.po | 53 ++++++++ .../language/resource.language.pt_pt/strings.po | 57 ++++++++ .../resources/lib/__init__.py | 0 .../resources/lib/addonutils.py | 18 +++ .../resources/lib/kodiutils.py | 85 ++++++++++++ .../resources/lib/plugin.py | 62 +++++++++ .../resources/lib/youtubelib.py | 143 +++++++++++++++++++++ plugin.video.itsapixelthing/resources/settings.xml | 8 ++ 15 files changed, 426 insertions(+) create mode 100644 plugin.video.itsapixelthing/resources/__init__.py create mode 100644 plugin.video.itsapixelthing/resources/images/fanart.jpg create mode 100644 plugin.video.itsapixelthing/resources/images/icon.png create mode 100644 plugin.video.itsapixelthing/resources/images/screenshot-1.png create mode 100644 plugin.video.itsapixelthing/resources/images/screenshot-2.png create mode 100644 plugin.video.itsapixelthing/resources/images/screenshot-3.png create mode 100644 plugin.video.itsapixelthing/resources/images/screenshot-4.png create mode 100644 plugin.video.itsapixelthing/resources/language/resource.language.en_gb/strings.po create mode 100644 plugin.video.itsapixelthing/resources/language/resource.language.pt_pt/strings.po create mode 100644 plugin.video.itsapixelthing/resources/lib/__init__.py create mode 100644 plugin.video.itsapixelthing/resources/lib/addonutils.py create mode 100644 plugin.video.itsapixelthing/resources/lib/kodiutils.py create mode 100644 plugin.video.itsapixelthing/resources/lib/plugin.py create mode 100644 plugin.video.itsapixelthing/resources/lib/youtubelib.py create mode 100644 plugin.video.itsapixelthing/resources/settings.xml (limited to 'plugin.video.itsapixelthing/resources') diff --git a/plugin.video.itsapixelthing/resources/__init__.py b/plugin.video.itsapixelthing/resources/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugin.video.itsapixelthing/resources/images/fanart.jpg b/plugin.video.itsapixelthing/resources/images/fanart.jpg new file mode 100644 index 0000000..a8b07c7 Binary files /dev/null and b/plugin.video.itsapixelthing/resources/images/fanart.jpg differ diff --git a/plugin.video.itsapixelthing/resources/images/icon.png b/plugin.video.itsapixelthing/resources/images/icon.png new file mode 100644 index 0000000..39c4efb Binary files /dev/null and b/plugin.video.itsapixelthing/resources/images/icon.png differ diff --git a/plugin.video.itsapixelthing/resources/images/screenshot-1.png b/plugin.video.itsapixelthing/resources/images/screenshot-1.png new file mode 100644 index 0000000..b474bd2 Binary files /dev/null and b/plugin.video.itsapixelthing/resources/images/screenshot-1.png differ diff --git a/plugin.video.itsapixelthing/resources/images/screenshot-2.png b/plugin.video.itsapixelthing/resources/images/screenshot-2.png new file mode 100644 index 0000000..add0b18 Binary files /dev/null and b/plugin.video.itsapixelthing/resources/images/screenshot-2.png differ diff --git a/plugin.video.itsapixelthing/resources/images/screenshot-3.png b/plugin.video.itsapixelthing/resources/images/screenshot-3.png new file mode 100644 index 0000000..219bd2b Binary files /dev/null and b/plugin.video.itsapixelthing/resources/images/screenshot-3.png differ diff --git a/plugin.video.itsapixelthing/resources/images/screenshot-4.png b/plugin.video.itsapixelthing/resources/images/screenshot-4.png new file mode 100644 index 0000000..a53652d Binary files /dev/null and b/plugin.video.itsapixelthing/resources/images/screenshot-4.png differ diff --git a/plugin.video.itsapixelthing/resources/language/resource.language.en_gb/strings.po b/plugin.video.itsapixelthing/resources/language/resource.language.en_gb/strings.po new file mode 100644 index 0000000..cfc8717 --- /dev/null +++ b/plugin.video.itsapixelthing/resources/language/resource.language.en_gb/strings.po @@ -0,0 +1,53 @@ +# Kodi Media Center language file +# Addon Name: It's a Pixel THING +# Addon id: plugin.video.itsapixelthing +# Addon Provider: enen92 +msgid "" +msgstr "" +"Project-Id-Version: XBMC Addons\n" +"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Kodi Translation Team\n" +"Language-Team: English (http://www.transifex.com/projects/p/xbmc-addons/language/en/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: en\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgctxt "#32000" +msgid "All videos" +msgstr "" + +msgctxt "#32001" +msgid "Watch all uploaded videos" +msgstr "" + +msgctxt "#32002" +msgid "Videos on the next page" +msgstr "" + +msgctxt "#32003" +msgid "Could not get channel playlists" +msgstr "" + +msgctxt "#32004" +msgid "Could not get the playlist video list" +msgstr "" + +msgctxt "#32005" +msgid "Unable to get video details" +msgstr "" + +msgctxt "#32006" +msgid "Number of items per page" +msgstr "" + +msgctxt "#32007" +msgid "Enter directly in the all videos listing" +msgstr "" + +msgctxt "#32008" +msgid "Next page" +msgstr "" diff --git a/plugin.video.itsapixelthing/resources/language/resource.language.pt_pt/strings.po b/plugin.video.itsapixelthing/resources/language/resource.language.pt_pt/strings.po new file mode 100644 index 0000000..7e805a1 --- /dev/null +++ b/plugin.video.itsapixelthing/resources/language/resource.language.pt_pt/strings.po @@ -0,0 +1,57 @@ +# Kodi Media Center language file +# Addon Name: It's a Pixel THING +# Addon id: plugin.video.itsapixelthing +# Addon Provider: enen92 +msgid "" +msgstr "" +"Project-Id-Version: XBMC Addons\n" +"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Kodi Translation Team\n" +"Language-Team: Portuguese (http://www.transifex.com/projects/p/xbmc-addons/language/pt/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#This is a comment + +msgctxt "#32000" +msgid "All videos" +msgstr "Todos os vídeos" + +msgctxt "#32001" +msgid "Watch all uploaded videos" +msgstr "Veja todos os vídeos do canal" + +msgctxt "#32002" +msgid "Videos on the next page" +msgstr "Vídeos na próxima página" + +msgctxt "#32003" +msgid "Could not get channel playlists" +msgstr "Não conseguiu obter as playlists do canal" + +msgctxt "#32004" +msgid "Could not get the playlist video list" +msgstr "Não conseguiu obter a listagem de videos da playlist" + +msgctxt "#32005" +msgid "Unable to get video details" +msgstr "Não conseguiu obter os detalhes do vídeo" + +msgctxt "#32006" +msgid "Number of items per page" +msgstr "Número de items por página" + +msgctxt "#32007" +msgid "Enter directly in the all videos listing" +msgstr "Entrar directamente para a lista de todos os vídeos" + +msgctxt "#32008" +msgid "Next page" +msgstr "Próxima página" + + diff --git a/plugin.video.itsapixelthing/resources/lib/__init__.py b/plugin.video.itsapixelthing/resources/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugin.video.itsapixelthing/resources/lib/addonutils.py b/plugin.video.itsapixelthing/resources/lib/addonutils.py new file mode 100644 index 0000000..65c82a3 --- /dev/null +++ b/plugin.video.itsapixelthing/resources/lib/addonutils.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +import re + +def return_duration_as_seconds(string): + try: + totalseconds = 0 + hours = re.findall('(\d+)H',string) + minutes = re.findall('(\d+)M',string) + seconds = re.findall('(\d+)S',string) + if hours: + totalseconds += 3600*int(hours[0]) + if minutes: + totalseconds += 60*int(minutes[0]) + if seconds: + totalseconds += int(seconds[0]) + return str(totalseconds) + except IndexError: + return '0' diff --git a/plugin.video.itsapixelthing/resources/lib/kodiutils.py b/plugin.video.itsapixelthing/resources/lib/kodiutils.py new file mode 100644 index 0000000..1970dcf --- /dev/null +++ b/plugin.video.itsapixelthing/resources/lib/kodiutils.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- + +import xbmc +import xbmcaddon +import xbmcgui +import xbmcplugin +import json as json + +ADDON = xbmcaddon.Addon() +ADDON_ID = ADDON.getAddonInfo("id") + + +def notification(header, message, time=5000, icon=ADDON.getAddonInfo('icon'), sound=True): + xbmcgui.Dialog().notification(header, message, icon, time, sound) + + +def show_settings(): + ADDON.openSettings() + + +def log(message,level): + prefix = b"[%s] " % ADDON_ID + formatter = prefix + b'%(name)s: %(message)s' + try: + xbmc.log(formatter, level) + except UnicodeEncodeError: + xbmc.log(formatter.encode( + 'utf-8', 'ignore'), level) + + +def get_setting(setting): + return ADDON.getSetting(setting).strip().decode('utf-8') + + +def set_setting(setting, value): + ADDON.setSetting(setting, str(value)) + + +def get_setting_as_bool(setting): + return get_setting(setting).lower() == "true" + + +def get_setting_as_float(setting): + try: + return float(get_setting(setting)) + except ValueError: + return 0 + + +def get_setting_as_int(setting): + try: + return int(get_setting_as_float(setting)) + except ValueError: + return 0 + + +def get_string(string_id): + return ADDON.getLocalizedString(string_id).encode('utf-8', 'ignore') + + +def kodi_json_request(params): + data = json.dumps(params) + request = xbmc.executeJSONRPC(data) + + try: + response = json.loads(request) + except UnicodeDecodeError: + response = json.loads(request.decode('utf-8', 'ignore')) + + try: + if 'result' in response: + return response['result'] + return None + except KeyError: + log("[%s] %s" % + (params['method'], response['error']['message']),level=xbmc.LOGWARNING) + return None + +def add_sort_methods(handle): + sort_methods = [xbmcplugin.SORT_METHOD_UNSORTED, xbmcplugin.SORT_METHOD_LABEL, + xbmcplugin.SORT_METHOD_DATE, xbmcplugin.SORT_METHOD_DURATION, + xbmcplugin.SORT_METHOD_EPISODE] + for method in sort_methods: + xbmcplugin.addSortMethod(handle, sortMethod=method) + return \ No newline at end of file diff --git a/plugin.video.itsapixelthing/resources/lib/plugin.py b/plugin.video.itsapixelthing/resources/lib/plugin.py new file mode 100644 index 0000000..33e75a6 --- /dev/null +++ b/plugin.video.itsapixelthing/resources/lib/plugin.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +import routing +import xbmcaddon +import xbmcplugin +from resources.lib import kodiutils +from resources.lib import youtubelib +from xbmcgui import ListItem +from xbmcplugin import addDirectoryItem, endOfDirectory + + +ADDON = xbmcaddon.Addon() +ICON = ADDON.getAddonInfo("icon") +FANART = ADDON.getAddonInfo("fanart") +plugin = routing.Plugin() + + +@plugin.route('/') +def index(): + #All videos + liz = ListItem("[I]%s[/I]" % (kodiutils.get_string(32000))) + liz.setInfo(type="video", infoLabels={"plot": kodiutils.get_string(32001)}) + liz.setArt({"thumb": ICON, "icon": ICON, "fanart": FANART}) + addDirectoryItem(plugin.handle, plugin.url_for(all_videos, playlist="all"), liz, True) + #Playlists + for liz in youtubelib.get_playlists(): + addDirectoryItem(plugin.handle, plugin.url_for(all_videos, playlist=liz.getProperty("playlist_id")), liz, True) + xbmcplugin.setContent(plugin.handle, 'tvshows') + endOfDirectory(plugin.handle) + + +@plugin.route('/videos') +def all_videos(): + #grab kwargs + page_num = int(plugin.args["page"][0]) if "page" in plugin.args.keys() else 1 + token = plugin.args["token"][0] if "token" in plugin.args.keys() else "" + playlist = plugin.args["playlist"][0] if "playlist" in plugin.args.keys() else "all" + upload_playlist = youtubelib.get_upload_playlist() if playlist == "all" else playlist + + for liz in youtubelib.get_videos(playlist, upload_playlist, token, page_num): + if liz.getProperty("type") == "youtube_video": + addDirectoryItem(plugin.handle, plugin.url_for(play, liz.getProperty("videoid")), liz, False) + elif liz.getProperty("type") == "next": + addDirectoryItem(plugin.handle, plugin.url_for(all_videos, playlist=playlist, page=int(liz.getProperty("page")), token=liz.getProperty("token")), liz, True) + kodiutils.add_sort_methods(plugin.handle) + xbmcplugin.setContent(plugin.handle, 'episodes') + endOfDirectory(plugin.handle) + + +@plugin.route('/play/') +def play(videoid): + stream = 'plugin://plugin.video.youtube/play/?video_id=%s' % (videoid) + liz = ListItem() + liz.setPath(stream) + xbmcplugin.setResolvedUrl(plugin.handle, True, liz) + + +def run(): + if not kodiutils.get_setting_as_bool("enter_all_videos"): + plugin.run() + else: + plugin.redirect("/videos") diff --git a/plugin.video.itsapixelthing/resources/lib/youtubelib.py b/plugin.video.itsapixelthing/resources/lib/youtubelib.py new file mode 100644 index 0000000..d53f830 --- /dev/null +++ b/plugin.video.itsapixelthing/resources/lib/youtubelib.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +import requests +import kodiutils +import addonutils +import xbmc +import math +import re +import xbmcaddon +from xbmcgui import ListItem + +CHANNEL_ID = kodiutils.get_setting("channel_id") +YOUTUBE_API_KEY = kodiutils.get_setting("api_key") +TVSHOWTITLE = "It's a Pixel THING" +CAST = [TVSHOWTITLE] +STATUS = 'in production' + + +def get_playlists(): + api_endpoint = 'https://www.googleapis.com/youtube/v3/playlists?part=snippet,contentDetails&channelId=%s&maxResults=50&key=%s' % (CHANNEL_ID,YOUTUBE_API_KEY) + try: + resp = requests.get(api_endpoint).json() + except ValueError: + kodiutils.log(kodiutils.get_string(32003), xbmc.LOGERROR) + if "items" in resp.keys(): + for playlist in resp["items"]: + liz = ListItem(playlist["snippet"]["title"]) + infolabels = {"plot": playlist["snippet"]["localized"]["description"]} + liz.setInfo(type="video", infoLabels=infolabels) + liz.setArt({"thumb": playlist["snippet"]["thumbnails"]["high"]["url"], "fanart": xbmcaddon.Addon().getAddonInfo("fanart")}) + liz.setProperty("type","playlist") + liz.setProperty("playlist_id", playlist["id"]) + yield liz + + +def get_upload_playlist(): + api_endpoint = 'https://www.googleapis.com/youtube/v3/channels?part=contentDetails&id=%s&key=%s' % (CHANNEL_ID,YOUTUBE_API_KEY) + try: + resp = requests.get(api_endpoint).json() + except ValueError: + kodiutils.log(kodiutils.get_string(32004), xbmc.LOGERROR) + return None + if "items" in resp.keys(): + uploads_playlist = resp["items"][0]["contentDetails"]["relatedPlaylists"]["uploads"] + return uploads_playlist + +def get_videos(name,playlist_id,token="",page_num=1): + items_per_page = kodiutils.get_setting_as_int("items_per_page") + url_api = 'https://www.googleapis.com/youtube/v3/playlistItems?part=id,snippet,contentDetails&maxResults=%s&playlistId=%s&key=%s' \ + % (str(items_per_page), playlist_id, YOUTUBE_API_KEY) + if page_num != 1: + url_api += "&pageToken=%s" % (token) + + try: + resp = requests.get(url_api).json() + except ValueError: + kodiutils.log(kodiutils.get_string(32004), xbmc.LOGERROR) + resp = None + + if resp: + nextpagetoken = resp["nextPageToken"] if "nextPageToken" in resp.keys() else "" + availablevideos = resp["pageInfo"]["totalResults"] if "pageInfo" in resp.keys() and "totalResults" in resp["pageInfo"].keys() else 1 + + returnedVideos = resp["items"] + totalpages = int(math.ceil((float(availablevideos) / items_per_page))) + video_ids = [] + if returnedVideos: + for video in returnedVideos: + videoid = video["contentDetails"]["videoId"] + video_ids.append(videoid) + video_ids = ','.join(video_ids) + url_api = 'https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=%s&key=%s' % (video_ids,YOUTUBE_API_KEY) + try: + resp = requests.get(url_api).json() + except ValueError: + kodiutils.log(kodiutils.get_string(32005), xbmc.LOGERROR) + resp = None + + if resp: + returnedVideos = resp["items"] + + for video in returnedVideos: + title = video["snippet"]["title"] + plot = video["snippet"]["description"] + aired = video["snippet"]["publishedAt"] + thumb = video["snippet"]["thumbnails"]["high"]["url"] + videoid = video["id"] + # process duration + duration_string = video["contentDetails"]["duration"] + duration = addonutils.return_duration_as_seconds(duration_string) + try: + aired = re.compile('(.+?)-(.+?)-(.+?)T').findall(aired)[0] + date = aired[2] + '.' + aired[1] + '.' + aired[0] + aired = aired[0] + '-' + aired[1] + '-' + aired[2] + except IndexError: + aired = '' + date = '' + + infolabels = {'plot': plot.encode('utf-8'), 'aired': aired, 'date': date, 'tvshowtitle': TVSHOWTITLE, + 'title': title.encode('utf-8'), 'originaltitle': title.encode('utf-8'), 'status': STATUS, + 'cast': CAST, 'duration': duration} + + # Video and audio info + video_info = {'codec': 'avc1', 'aspect': 1.78} + audio_info = {'codec': 'aac', 'language': 'en'} + if video["contentDetails"]["definition"].lower() == 'hd': + video_info['width'] = 1280 + video_info['height'] = 720 + audio_info['channels'] = 2 + else: + video_info['width'] = 854 + video_info['height'] = 480 + audio_info['channels'] = 1 + if xbmcaddon.Addon(id='plugin.video.youtube').getSetting('kodion.video.quality.ask') == 'false' and xbmcaddon.Addon( + id='plugin.video.youtube').getSetting('kodion.video.quality') != '3' and xbmcaddon.Addon( + id='plugin.video.youtube').getSetting('kodion.video.quality') != '4': + video_info['width'] = 854 + video_info['height'] = 480 + audio_info['channels'] = 1 + + yield build_video_item(title.encode('utf-8'), thumb, videoid, infolabels, video_info, audio_info) + + if totalpages > 1 and (page_num + 1) <= totalpages: + nextpage = ListItem("[B]%s[/B] (%s/%s)" % (kodiutils.get_string(32008), str(page_num), str(totalpages))) + nextpage.setProperty("type", "next") + nextpage.setProperty("page", str(page_num+1)) + nextpage.setProperty("token", str(nextpagetoken)) + nextpage.setInfo(type="video",infoLabels={"plot": kodiutils.get_string(32002)}) + yield nextpage + +def build_video_item(name,thumb,videoid,info,video_info,audio_info): + liz=ListItem(name) + cm = [] + cm.append(("Info", 'XBMC.Action(Info)')) + thumb = thumb if thumb else xbmcaddon.Addon().getAddonInfo("icon") + liz.setProperty("type", "youtube_video") + liz.setProperty("videoid",videoid) + liz.setArt({'thumb': thumb, 'fanart': xbmcaddon.Addon().getAddonInfo("fanart"), "poster": thumb}) + liz.setInfo(type="Video", infoLabels=info) + liz.addStreamInfo('video', video_info) + liz.addStreamInfo('audio', audio_info) + liz.setProperty('IsPlayable', 'true') + liz.addContextMenuItems(cm, replaceItems=False) + return liz \ No newline at end of file diff --git a/plugin.video.itsapixelthing/resources/settings.xml b/plugin.video.itsapixelthing/resources/settings.xml new file mode 100644 index 0000000..43569f3 --- /dev/null +++ b/plugin.video.itsapixelthing/resources/settings.xml @@ -0,0 +1,8 @@ + + + + + + + + -- cgit v1.2.3