summaryrefslogtreecommitdiff
path: root/addon.py
diff options
context:
space:
mode:
Diffstat (limited to 'addon.py')
-rw-r--r--addon.py299
1 files changed, 299 insertions, 0 deletions
diff --git a/addon.py b/addon.py
new file mode 100644
index 0000000..ac2009f
--- /dev/null
+++ b/addon.py
@@ -0,0 +1,299 @@
+# Copyright © 2024 Jakob L. Kreuze <zerodaysfordays@sdf.org>
+#
+# 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 <https://www.gnu.org/licenses/>.
+
+"""
+Audio plugin for songs posted to Newgrounds.
+"""
+
+import os
+import sys
+from urllib.parse import urlencode, parse_qsl
+
+from newgrounds import Conduct, Grouping, Newgrounds, AUDIO_CATEGORIES
+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 f"{URL}?{urlencode(kwargs)}"
+
+
+def authentication_specified():
+ addon = Addon()
+ username = addon.getSetting("newgrounds.username")
+ password = addon.getSetting("newgrounds.password")
+ return username != "" and password != ""
+
+
+def get_session():
+ addon = Addon()
+
+ session = addon.getSetting("newgrounds.session")
+ if session is not None: # and test_logged_in(session):
+ return {SESSION_COOKIE_NAME: session}
+
+ username = addon.getSetting("newgrounds.username")
+ password = addon.getSetting("newgrounds.password")
+
+ if username == "" or password == "":
+ return {}
+
+ session = open_new_session(username, password)
+ addon.setSetting("newgrounds.session", session)
+
+ return {SESSION_COOKIE_NAME: session}
+
+
+class NewgroundsPlugin(object):
+ def __init__(self):
+ self.ng = Newgrounds()
+
+ def invoke(paramstring):
+ ng = NewgroundsPlugin()
+ params = dict(parse_qsl(paramstring))
+ if "action" in params:
+ method = getattr(ng, params["action"])
+ if method is None:
+ raise ValueError(f"Invalid paramstring: {paramstring}!")
+ else:
+ method = ng.default
+ method(**params)
+
+ def default(self, **kwargs):
+ self.list_front_page()
+
+ def prompt_search(self, **kwargs):
+ dialog = xbmcgui.Dialog()
+ term = dialog.input(heading="Search")
+ if term is not None:
+ url = get_url(action="search", term=term)
+ xbmc.executebuiltin("Container.Update(%s)" % url)
+
+ def search(self, **kwargs):
+ assert "term" in kwargs
+ term = kwargs["term"]
+ offset = int(kwargs.get("offset", 1))
+ video_urls = self.ng.search(term, conduct=Conduct.AUDIO, offset=offset)
+ next_page = get_url(action="search", term=term, offset=offset + 1)
+ self._list_songs(video_urls, "Search Results: {}".format(term), next_page)
+
+ def list_front_page(self, **kwargs):
+ """
+ Create the list of entrypoints in the Kodi interface.
+ """
+ xbmcplugin.setPluginCategory(HANDLE, "Newgrounds")
+ xbmcplugin.setContent(HANDLE, "songs")
+
+ groupings = {
+ "Featured": Grouping.FEATURED,
+ "Latest": Grouping.LATEST,
+ "Popular": Grouping.POPULAR,
+ }
+
+ # Add a search dialog
+ list_item = xbmcgui.ListItem(label="Search")
+ info_tag = list_item.getVideoInfoTag()
+ info_tag.setMediaType("song")
+ info_tag.setTitle("Search")
+ url = get_url(action="prompt_search")
+ is_folder = False
+ xbmcplugin.addDirectoryItem(HANDLE, url, list_item, is_folder)
+
+ # Add a dialog for displaying the user's feed
+ if authentication_specified():
+ list_item = xbmcgui.ListItem(label="Your Feed")
+ info_tag = list_item.getVideoInfoTag()
+ info_tag.setMediaType("song")
+ info_tag.setTitle("Your Feed")
+ url = get_url(action="list_feed")
+ is_folder = True
+ xbmcplugin.addDirectoryItem(HANDLE, url, list_item, is_folder)
+
+ for name, enum in groupings.items():
+ list_item = xbmcgui.ListItem(label=name)
+
+ info_tag = list_item.getVideoInfoTag()
+ info_tag.setMediaType("song")
+ info_tag.setTitle(name)
+ info_tag.setGenres([name])
+
+ url = get_url(action="list_categories", grouping=enum.value)
+ is_folder = True
+
+ xbmcplugin.addDirectoryItem(HANDLE, url, list_item, is_folder)
+
+ xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_NONE)
+ xbmcplugin.endOfDirectory(HANDLE)
+
+ def list_categories(self, **kwargs):
+ """
+ Create the list of audio categories in the Kodi interface.
+ """
+ grouping = kwargs.get("grouping", Grouping.FEATURED.value)
+ xbmcplugin.setPluginCategory(HANDLE, "Categories")
+ xbmcplugin.setContent(HANDLE, "songs")
+ for name in AUDIO_CATEGORIES.keys():
+ list_item = xbmcgui.ListItem(label=name)
+ info_tag = list_item.getMusicInfoTag()
+ info_tag.setMediaType("song")
+ info_tag.setTitle(name)
+ info_tag.setGenres([name])
+ url = get_url(action="list_audio", grouping=grouping, category=name)
+ is_folder = True
+ xbmcplugin.addDirectoryItem(HANDLE, url, list_item, is_folder)
+ xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_NONE)
+ xbmcplugin.endOfDirectory(HANDLE)
+
+ def list_audio(self, **kwargs):
+ """
+ Create the list of playable songs in the Kodi interface.
+ """
+ grouping = int(kwargs.get("grouping", 0))
+ offset = int(kwargs.get("offset", 0))
+ category = kwargs.get("category", "All")
+ songs = self.ng.front_page(
+ conduct=Conduct.AUDIO,
+ grouping=int(grouping),
+ interval="all",
+ category=category,
+ offset=offset,
+ )
+ next_page = get_url(
+ action="list_audio",
+ grouping=grouping,
+ category=category,
+ offset=offset + 20,
+ )
+ self._list_songs(songs, f"{category} - {grouping}", next_page)
+
+ def list_feed(self, **kwargs):
+ # offset = int(kwargs.get("offset", 1))
+ # next_page = get_url(action="list_series", offset=offset + 1)
+ addon = Addon()
+ username = addon.getSetting("newgrounds.username")
+ password = addon.getSetting("newgrounds.password")
+ self.ng.login(username, password)
+ songs = self.ng.feed(conduct=Conduct.AUDIO)
+ self._list_songs(songs, f"Feed - {username}")
+
+ # TODO: Support offsets.
+ def list_playlist(self, **kwargs):
+ assert "url" in kwargs
+ cards = self.ng.playlist_entries(kwargs["url"])
+ # next_page = get_url(action="list_playlist", offset=offset + 1)
+ self._list_cards(
+ title="Series",
+ cards=cards,
+ # next_page=next_page,
+ )
+
+ def _list_cards(self, cards, next_page=None, title=""):
+ """
+ Create the list of folders in the Kodi interface.
+ """
+ xbmcplugin.setPluginCategory(HANDLE, title)
+ xbmcplugin.setContent(HANDLE, "sonmgs")
+
+ for card in cards:
+ list_item = xbmcgui.ListItem(label=card.title)
+ if card.thumbnail is not None:
+ list_item.setArt({"thumb": card.thumbnail})
+
+ info_tag = list_item.getMusicInfoTag()
+ info_tag.setMediaType("song")
+ info_tag.setTitle(card.title)
+ info_tag.setDirectors([card.author])
+
+ url = get_url(action="list_playlist", url=card.url)
+ is_folder = True
+ xbmcplugin.addDirectoryItem(HANDLE, url, list_item, is_folder)
+
+ # Add a "continue" folder.
+ if next_page is not None and len(cards) > 0:
+ list_item = xbmcgui.ListItem(label="Next Page")
+ info_tag = list_item.getMusicInfoTag()
+ info_tag.setMediaType("song")
+ info_tag.setTitle("Next Page")
+ is_folder = True
+ xbmcplugin.addDirectoryItem(HANDLE, next_page, list_item, is_folder)
+
+ xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_NONE)
+ xbmcplugin.endOfDirectory(HANDLE)
+
+ def _list_songs(self, songs, title="Songs", next_page=None):
+ xbmcplugin.setPluginCategory(HANDLE, title)
+ xbmcplugin.setContent(HANDLE, "songs")
+
+ for song in songs:
+ list_item = xbmcgui.ListItem(label=song.title)
+ if song.thumbnail is not None:
+ list_item.setArt({"thumb": song.thumbnail})
+
+ info_tag = list_item.getMusicInfoTag()
+ info_tag.setMediaType("song")
+ info_tag.setTitle(song.title)
+ info_tag.setReleaseDate(song.upload_date.strftime("%Y-%m-%d"))
+ info_tag.setRating(song.score)
+ # info_tag.setPlaycount(song.views)
+ # info_tag.setPlot(song.description)
+ info_tag.setGenres([song.genre])
+ info_tag.setArtist(song.author)
+ # info_tag.setTags(song.tags)
+
+ list_item.setProperty("IsPlayable", "true")
+
+ url = get_url(action="play_audio", audio=song.content_url)
+ is_folder = False
+ xbmcplugin.addDirectoryItem(HANDLE, url, list_item, is_folder)
+
+ # Add a "continue" folder.
+ if next_page is not None and len(songs) > 0:
+ list_item = xbmcgui.ListItem(label="Next Page")
+ info_tag = list_item.getMusicInfoTag()
+ info_tag.setMediaType("audio")
+ info_tag.setTitle("Next Page")
+ is_folder = True
+ xbmcplugin.addDirectoryItem(HANDLE, next_page, list_item, is_folder)
+
+ xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_NONE)
+ xbmcplugin.endOfDirectory(HANDLE)
+
+ def play_audio(self, **kwargs):
+ assert "video" in kwargs
+ path = kwargs["audio"]
+ play_item = xbmcgui.ListItem(path=path)
+ play_item.setProperty("IsPlayable", "true")
+ xbmcplugin.setResolvedUrl(HANDLE, True, play_item)
+
+
+if __name__ == "__main__":
+ NewgroundsPlugin.invoke(sys.argv[2][1:])