summaryrefslogtreecommitdiff
path: root/plugin.video.metalvideo/addon.py
diff options
context:
space:
mode:
Diffstat (limited to 'plugin.video.metalvideo/addon.py')
-rw-r--r--plugin.video.metalvideo/addon.py282
1 files changed, 282 insertions, 0 deletions
diff --git a/plugin.video.metalvideo/addon.py b/plugin.video.metalvideo/addon.py
new file mode 100644
index 0000000..11c627c
--- /dev/null
+++ b/plugin.video.metalvideo/addon.py
@@ -0,0 +1,282 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2016 William Forde (willforde+kodi@gmail.com)
+#
+# License: GPLv2, see LICENSE for more details
+#
+# 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 2
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+from __future__ import unicode_literals
+from codequick import Route, Resolver, Listitem, utils, run
+import xbmcgui
+import re
+
+# Localized string Constants
+VIDEO_OF_THE_DAY = 30004
+WATCHING_NOW = 30005
+TOP_VIDEOS = 30002
+SELECT_TOP = 30001
+PARTY_MODE = 589
+
+# Base url constructor
+url_constructor = utils.urljoin_partial("http://metalvideo.com")
+
+
+# noinspection PyUnusedLocal
+@Route.register
+def root(plugin, content_type):
+ """
+ :param Route plugin: The plugin parent object.
+ :param str content_type: The type of content been listed e.g. video, music. This is passed in from kodi and
+ we have no use for it as of yet.
+ """
+ yield Listitem.recent(recent_videos)
+ yield Listitem.from_dict(plugin.localize(TOP_VIDEOS), top_videos)
+ yield Listitem.from_dict(plugin.localize(WATCHING_NOW), watching_now)
+ yield Listitem.search(video_list)
+
+ # Fetch HTML Source
+ url = url_constructor("/mobile/category.html")
+ html = plugin.request.get(url, headers={"Cookie": "COOKIE_DEVICE=mobile"})
+ root_elem = html.parse(u"ul", attrs={"id": "category_listing"})
+ for elem in root_elem.iterfind("li"):
+ a_tag = elem.find("a")
+ item = Listitem()
+
+ # Set label with video count added
+ item.label = "%s (%s)" % (a_tag.text, elem.find("span").text)
+ item.set_callback(video_list, cat=a_tag.get("href"))
+ yield item
+
+ # Add the video items here so that show at the end of the listing
+ yield Listitem.from_dict(plugin.localize(VIDEO_OF_THE_DAY), play_video, params={"url": "index.html"})
+ yield Listitem.from_dict(plugin.localize(PARTY_MODE), party_play, params={"url": "randomizer.php"})
+
+
+@Route.register
+def recent_videos(plugin, url="newvideos.php"):
+ """
+ :param Route plugin: The plugin parent object.
+ :param unicode url: The url resource containing recent videos.
+ """
+ # Fetch HTML Source
+ url = url_constructor(url)
+ html = plugin.request.get(url)
+ root_elem = html.parse("div", attrs={"id": "browse_main"})
+ node = root_elem.find("./div[@id='newvideos_results']")[0]
+ for elem in node.iterfind("./tr"):
+ if not elem.attrib:
+ item = Listitem()
+ item.art["thumb"] = elem.find(".//img").get("src")
+
+ artist = elem[1].text
+ track = elem[1][0][0].text
+ item.label = "%s - %s" % (artist, track)
+ item.info["artist"] = [artist]
+
+ url = elem.find(".//a").get("href")
+ item.context.related(related, url=url)
+ item.set_callback(play_video, url=url)
+ yield item
+
+ # Fetch next page url
+ next_tag = root_elem.findall("./div[@class='pagination']/a")
+ if next_tag and next_tag[-1].text.startswith("next"):
+ yield Listitem.next_page(url=next_tag[-1].get("href"))
+
+
+@Route.register
+def watching_now(plugin):
+ # Fetch HTML Source
+ url = url_constructor("/index.html")
+ html = plugin.request.get(url)
+ root_elem = html.parse("ul", attrs={"id": "mycarousel"})
+ for elem in root_elem.iterfind("li"):
+ a_tag = elem.find(".//a[@title]")
+ item = Listitem()
+
+ # Fetch label & thumb
+ item.label = a_tag.text
+ item.art["thumb"] = elem.find(".//img").get("src")
+
+ url = a_tag.get("href")
+ item.context.related(related, url=url)
+ item.set_callback(play_video, url=url)
+ yield item
+
+
+@Route.register
+def top_videos(plugin):
+ """:param Route plugin: The plugin parent object."""
+ # Fetch HTML Source
+ url = url_constructor("/topvideos.html")
+ html = plugin.request.get(url)
+ titles = []
+ urls = []
+
+ # Parse categories
+ root_elem = html.parse("select", attrs={"name": "categories"})
+ for group in root_elem.iterfind("optgroup"):
+ for elem in group:
+ urls.append(elem.get("value"))
+ titles.append(elem.text.strip())
+
+ # Display list for Selection
+ dialog = xbmcgui.Dialog()
+ ret = dialog.select(plugin.localize(SELECT_TOP), titles)
+ if ret >= 0:
+ # Fetch HTML Source
+ url = urls[ret]
+ html = plugin.request.get(url)
+ root_elem = html.parse("div", attrs={"id": "topvideos_results"})
+ for elem in root_elem.iterfind(".//tr"):
+ if not elem.attrib:
+ item = Listitem()
+ a_tag = elem[3][0]
+
+ artist = elem[2].text
+ item.label = "%s %s - %s" % (elem[0].text, artist, a_tag.text)
+ item.art["thumb"] = elem.find(".//img").get("src")
+ item.info["count"] = elem[4].text.replace(",", "")
+ item.info["artist"] = [artist]
+
+ url = a_tag.get("href")
+ item.context.related(related, url=url)
+ item.set_callback(play_video, url=url)
+ yield item
+
+
+@Route.register
+def related(plugin, url):
+ """
+ :param Route plugin: The plugin parent object.
+ :param unicode url: The url of a video.
+ """
+ # Fetch HTML Source
+ url = url_constructor(url)
+ html = plugin.request.get(url)
+ root_elem = html.parse("div", attrs={"id": "tabs_related"})
+
+ # Parse the xml
+ for elem in root_elem.iterfind(u"div"):
+ a_tag = elem.find("./a[@class='song_name']")
+
+ item = Listitem()
+ item.label = a_tag.text
+ item.art["thumb"] = elem.find("./a/img").get("src")
+
+ url = a_tag.get("href")
+ item.context.related(related, url=url)
+ item.set_callback(play_video, url=url)
+ yield item
+
+
+@Route.register
+def video_list(plugin, url=None, cat=None, search_query=None):
+ """
+ :param Route plugin: The plugin parent object.
+ :param unicode url: The url resource containing lists of videos or next page.
+ :param unicode cat: A category url e.g. Alternative, Folk Metal.
+ :param unicode search_query: The video search term to use for searching.
+ """
+ if search_query:
+ url = url_constructor("search.php?keywords=%s&btn=Search" % search_query)
+ elif cat:
+ sortby = (u"date.html", u"artist.html", u"rating.html", u"views.html")[plugin.setting.get_int("sort")]
+ base, _ = url_constructor(cat).rsplit("-", 1)
+ url = "-".join((base, sortby))
+ else:
+ url = url_constructor(url)
+
+ html = plugin.request.get(url)
+ root_elem = html.parse("div", attrs={"id": "browse_main"})
+ for elem in root_elem.iterfind(u".//div[@class='video_i']"):
+ item = Listitem()
+ item.art["thumb"] = elem.find(".//img").get("src")
+
+ # Extract url and remove first 'a' tag section
+ # This makes it easir to extract 'artist' and 'song' name later
+ a_tag = elem.find("a")
+ url = a_tag.get("href")
+ elem.remove(a_tag)
+
+ # Fetch title
+ span_tags = tuple(node.text for node in elem.findall(".//span"))
+ item.label = "%s - %s" % span_tags
+ item.info["artist"] = [span_tags[0]]
+
+ # Add related video context item
+ item.context.related(related, url=url)
+ item.set_callback(play_video, url=url)
+ yield item
+
+ # Fetch next page url
+ next_tag = root_elem.findall(".//div[@class='pagination']/a")
+ if next_tag and next_tag[-1].text.startswith("next"):
+ yield Listitem.next_page(url=next_tag[-1].get("href"))
+
+
+@Resolver.register
+def play_video(plugin, url):
+ """
+ :param Resolver plugin: The plugin parent object.
+ :param unicode url: The url of a video.
+ :returns: A playable video url.
+ """
+ # Attemp to find url using extract_source(YTDL) first
+ url = url_constructor(url)
+ source_url = plugin.extract_source(url)
+ if source_url:
+ return source_url
+
+ # Fallback to search for direct file
+ html = plugin.request.get(url).text
+ search_regx = 'file:\s+\'(\S+?)\''
+ match = re.findall(search_regx, html)
+ if match:
+ return match[0]
+
+
+@Resolver.register
+def party_play(plugin, url):
+ """
+ :param Resolver plugin: The plugin parent object.
+ :param unicode url: The url to a video.
+ :return: A playlist with the first item been a playable video url and the seconde been a callback url that
+ will fetch the next video url to play.
+ """
+ # Attempt to fetch a video url 3 times
+ attempts = 0
+ while attempts < 3:
+ try:
+ url = play_video(plugin, url)
+ except Exception as e:
+ # Raise the Exception if we are on the last run of the loop
+ if attempts == 2:
+ raise e
+ else:
+ if url:
+ # Break from loop when we have a url
+ return plugin.create_loopback(url)
+
+ # Increment attempts counter
+ attempts += 1
+
+ # All 3 attemps failed to resolve a url
+ return None
+
+
+# Initiate Startup
+if __name__ == "__main__":
+ run()