# Copyright © 2024 Jakob L. Kreuze # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ Video plugin for animations posted to Newgrounds. """ import os import sys from urllib.parse import urlencode, parse_qsl from bs4 import BeautifulSoup import requests import xbmc import xbmcgui import xbmcplugin from xbmcaddon import Addon from xbmcvfs import translatePath URL = sys.argv[0] HANDLE = int(sys.argv[1]) ADDON_PATH = translatePath(Addon().getAddonInfo('path')) ICONS_DIR = os.path.join(ADDON_PATH, 'resources', 'images', 'icons') FANART_DIR = os.path.join(ADDON_PATH, 'resources', 'images', 'fanart') def get_url(**kwargs): """ Create a URL for calling the plugin recursively from the given set of keyword arguments. :param kwargs: "argument=value" pairs :return: plugin call URL :rtype: str """ return '{}?{}'.format(URL, urlencode(kwargs)) def get_genres(): """ Get the list of video genres Here you can insert some code that retrieves the list of video sections (in this case movie genres) from some site or API. :return: The list of video genres :rtype: list """ return VIDEOS def get_videos(genre_index): """ Get the list of videofiles/streams. Here you can insert some code that retrieves the list of video streams in the given section from some site or API. :param genre_index: genre index :type genre_index: int :return: the list of videos in the category :rtype: list """ return VIDEOS[genre_index] def list_genres(): """ Create the list of movie genres in the Kodi interface. """ xbmcplugin.setPluginCategory(HANDLE, 'Newgrounds') xbmcplugin.setContent(HANDLE, 'movies') genres = ["Featured"] # get_genres() for index, name in enumerate(genres): list_item = xbmcgui.ListItem(label=name) # list_item.setArt({'icon': genre_info['icon'], 'fanart': genre_info['fanart']}) info_tag = list_item.getVideoInfoTag() info_tag.setMediaType('video') info_tag.setTitle(name) info_tag.setGenres([name]) url = get_url(action='listing', genre_index=index) is_folder = True xbmcplugin.addDirectoryItem(HANDLE, url, list_item, is_folder) xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE) xbmcplugin.endOfDirectory(HANDLE) def get_video_url(url): """ TODO """ xbmc.log('Getting info for {}'.format(url), 2) html = requests.get(url).text soup = BeautifulSoup(html, 'html.parser') video_id = soup.find('meta', property='og:url')['content'] \ .strip('https://www.newgrounds.com/portal/view/') response = requests.get('https://www.newgrounds.com/portal/video/{}'.format(video_id), headers={ 'X-Requested-With': 'XMLHttpRequest' }).json() preferences = ['1080p', '720p', '360p'] for preference in preferences: if preference in response['sources']: source = next(filter(lambda x: x['type'] == 'video/mp4', response['sources'][preference])) if source is not None: return source['src'] def get_addon_video_info(url): """ Scrape metadata from `url`. """ xbmc.log('Getting info for {}'.format(url), 2) html = requests.get(url).text soup = BeautifulSoup(html, 'html.parser') return { 'title': soup.find('div', id="embed_header").h2.string, 'thumbnail': soup.find('meta', property="og:image")['content'], 'url': url } def list_videos(genre_index): """ Create the list of playable videos in the Kodi interface. :param genre_index: the index of genre in the list of movie genres :type genre_index: int """ html = requests.get("https://www.newgrounds.com/movies").text soup = BeautifulSoup(html, 'html.parser') videos = [get_addon_video_info(tag["href"]) for tag in soup.find_all('a', "inline-card-portalsubmission")] xbmcplugin.setPluginCategory(HANDLE, "Featured") #, genre_info['genre']) xbmcplugin.setContent(HANDLE, 'movies') # Get the list of videos in the category. # videos = genre_info['movies'] # Iterate through videos. for video in videos: list_item = xbmcgui.ListItem(label=video['title']) list_item.setArt({'thumb': video['thumbnail']}) info_tag = list_item.getVideoInfoTag() info_tag.setMediaType('movie') info_tag.setTitle(video['title']) # info_tag.setGenres([genre_info['genre']]) # info_tag.setPlot(video['plot']) # info_tag.setYear(video['year']) list_item.setProperty('IsPlayable', 'true') url = get_url(action='play', video=video['url']) is_folder = False xbmcplugin.addDirectoryItem(HANDLE, url, list_item, is_folder) xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE) xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_VIDEO_YEAR) xbmcplugin.endOfDirectory(HANDLE) def play_video(path): play_item = xbmcgui.ListItem(path=get_video_url(path)) play_item.setProperty('IsPlayable', 'true') xbmcplugin.setResolvedUrl(HANDLE, True, play_item) def router(paramstring): """ Router function that calls other functions depending on the provided paramstring :param paramstring: URL encoded plugin paramstring :type paramstring: str """ params = dict(parse_qsl(paramstring)) if not params: list_genres() elif params['action'] == 'listing': list_videos(int(params['genre_index'])) elif params['action'] == 'play': play_video(params['video']) else: raise ValueError(f'Invalid paramstring: {paramstring}!') if __name__ == '__main__': router(sys.argv[2][1:])