summaryrefslogtreecommitdiff
path: root/plugin.video.hak5/resources
diff options
context:
space:
mode:
Diffstat (limited to 'plugin.video.hak5/resources')
-rw-r--r--plugin.video.hak5/resources/__init__.py1
-rw-r--r--plugin.video.hak5/resources/images/fanart-blur.jpgbin0 -> 525847 bytes
-rw-r--r--plugin.video.hak5/resources/images/next-page.pngbin0 -> 14529 bytes
-rw-r--r--plugin.video.hak5/resources/language/resource.language.en_gb/strings.po80
-rw-r--r--plugin.video.hak5/resources/lib/__init__.py1
-rw-r--r--plugin.video.hak5/resources/lib/hak5_const.py24
-rw-r--r--plugin.video.hak5/resources/lib/hak5_list_episodes.py305
-rw-r--r--plugin.video.hak5/resources/lib/hak5_list_seasons.py142
-rw-r--r--plugin.video.hak5/resources/lib/hak5_main.py115
-rw-r--r--plugin.video.hak5/resources/lib/hak5_play.py123
-rw-r--r--plugin.video.hak5/resources/settings.xml0
11 files changed, 791 insertions, 0 deletions
diff --git a/plugin.video.hak5/resources/__init__.py b/plugin.video.hak5/resources/__init__.py
new file mode 100644
index 0000000..309e220
--- /dev/null
+++ b/plugin.video.hak5/resources/__init__.py
@@ -0,0 +1 @@
+# Enables relative imports \ No newline at end of file
diff --git a/plugin.video.hak5/resources/images/fanart-blur.jpg b/plugin.video.hak5/resources/images/fanart-blur.jpg
new file mode 100644
index 0000000..72139d8
--- /dev/null
+++ b/plugin.video.hak5/resources/images/fanart-blur.jpg
Binary files differ
diff --git a/plugin.video.hak5/resources/images/next-page.png b/plugin.video.hak5/resources/images/next-page.png
new file mode 100644
index 0000000..b12e695
--- /dev/null
+++ b/plugin.video.hak5/resources/images/next-page.png
Binary files differ
diff --git a/plugin.video.hak5/resources/language/resource.language.en_gb/strings.po b/plugin.video.hak5/resources/language/resource.language.en_gb/strings.po
new file mode 100644
index 0000000..ae509ad
--- /dev/null
+++ b/plugin.video.hak5/resources/language/resource.language.en_gb/strings.po
@@ -0,0 +1,80 @@
+# XBMC Media Center language file
+# Addon Name: Hak5
+# Addon id: plugin.video.hak5
+msgid ""
+msgstr ""
+"Project-Id-Version: XBMC-Addons\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
+"POT-Creation-Date: 2016-05-30 03:54+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE\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 "Addon Summary"
+msgid "Watch videos from hak5.com"
+msgstr ""
+
+msgctxt "Addon Description"
+msgid "Watch videos from hak5.com. The videos are from the shows Hak5, Haktik, Threatwire, Tekthing, Pineapple University and Metasploit"
+msgstr ""
+
+msgctxt "Addon Disclaimer"
+msgid "For bugs, requests or general questions visit the hak5.com thread on the Kodi forum"
+msgstr ""
+
+msgctxt "#30104"
+msgid "Refresh"
+msgstr ""
+
+msgctxt "#30105"
+msgid "Episode Info"
+msgstr ""
+
+msgctxt "#30106"
+msgid "Error getting page: %s"
+msgstr ""
+
+msgctxt "#30107"
+msgid "No video found."
+msgstr ""
+
+msgctxt "#30108"
+msgid "Error playing media file."
+msgstr ""
+
+msgctxt "#30200"
+msgid "Next page"
+msgstr ""
+
+msgctxt "#30301"
+msgid "Recently Added Episodes"
+msgstr ""
+
+msgctxt "#30302"
+msgid "Hak5 Seasons"
+msgstr ""
+
+msgctxt "#30303"
+msgid "Haktik Recently Added Episodes"
+msgstr ""
+
+msgctxt "#30304"
+msgid "Threatwire Recently Added Episodes"
+msgstr ""
+
+msgctxt "#30305"
+msgid "Tekthing Recently Added Episodes"
+msgstr ""
+
+msgctxt "#30306"
+msgid "Pineapple University Recently Added Episodes"
+msgstr ""
+
+msgctxt "#30307"
+msgid "Metasploit Recently Added Episodes"
+msgstr "" \ No newline at end of file
diff --git a/plugin.video.hak5/resources/lib/__init__.py b/plugin.video.hak5/resources/lib/__init__.py
new file mode 100644
index 0000000..309e220
--- /dev/null
+++ b/plugin.video.hak5/resources/lib/__init__.py
@@ -0,0 +1 @@
+# Enables relative imports \ No newline at end of file
diff --git a/plugin.video.hak5/resources/lib/hak5_const.py b/plugin.video.hak5/resources/lib/hak5_const.py
new file mode 100644
index 0000000..129de87
--- /dev/null
+++ b/plugin.video.hak5/resources/lib/hak5_const.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+import os
+import xbmcaddon
+
+#
+# Constants
+#
+ADDON = "plugin.video.hak5"
+SETTINGS = xbmcaddon.Addon()
+LANGUAGE = SETTINGS.getLocalizedString
+IMAGES_PATH = os.path.join(xbmcaddon.Addon().getAddonInfo('path'), 'resources', 'images')
+HAK5RECENTLYADDEDURL = 'http://www.hak5.org/category/episodes'
+HAK5SEASONSURLHTTP = 'http://www.hak5.org/category/episodes'
+HAK5SEASONSURLHTTPS = 'https://www.hak5.org/category/episodes'
+HAKTIKRECENTLYADDEDURL = 'http://www.hak5.org/category/episodes/haktip/page/001'
+THREATWIRERECENTLYADDEDURL = 'http://www.hak5.org/category/episodes/threatwire/page/001'
+TEKTHINGRECENTLYADDEDURL = 'http://www.hak5.org/category/episodes/tekthing/page/001'
+PINEAPPLEUNIVERSITYRECENTLYADDEDURL = 'http://www.hak5.org/category/episodes/pineapple-university'
+METASPLOITRECENTLYADDEDURL = 'http://www.hak5.org/category/episodes/metasploit-minute/page/001'
+HEADERS = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}
+DATE = "2017-07-09"
+VERSION = "1.0.0" \ No newline at end of file
diff --git a/plugin.video.hak5/resources/lib/hak5_list_episodes.py b/plugin.video.hak5/resources/lib/hak5_list_episodes.py
new file mode 100644
index 0000000..5979c73
--- /dev/null
+++ b/plugin.video.hak5/resources/lib/hak5_list_episodes.py
@@ -0,0 +1,305 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#
+# Imports
+#
+import os
+import requests
+import sys
+import urllib
+import urlparse
+import re
+import HTMLParser
+import xbmc
+import xbmcgui
+import xbmcplugin
+from BeautifulSoup import BeautifulSoup
+
+from hak5_const import ADDON, LANGUAGE, IMAGES_PATH, HEADERS, DATE, VERSION
+
+#
+# Main class
+#
+class Main:
+ #
+ # Init
+ #
+ def __init__(self):
+ # Get the command line arguments
+ # Get the plugin url in plugin:// notation
+ self.plugin_url = sys.argv[0]
+ # Get the plugin handle as an integer number
+ self.plugin_handle = int(sys.argv[1])
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s, %s = %s" % (
+ ADDON, VERSION, DATE, "ARGV", repr(sys.argv), "File", str(__file__)), xbmc.LOGDEBUG)
+
+ # Parse parameters
+ self.video_list_page_url = urlparse.parse_qs(urlparse.urlparse(sys.argv[2]).query)['url'][0]
+ self.next_page_possible = urlparse.parse_qs(urlparse.urlparse(sys.argv[2]).query)['next_page_possible'][0]
+
+ self.video_list_page_url = str(self.video_list_page_url).replace('https', 'http')
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "self.video_list_page_url", str(self.video_list_page_url)),
+ xbmc.LOGDEBUG)
+
+ if self.next_page_possible == 'True':
+ # Determine current item number, next item number, next_url
+ pos_of_page = self.video_list_page_url.rfind('page/')
+ if pos_of_page >= 0:
+ page_number_str = str(
+ self.video_list_page_url[pos_of_page + len('page/'):pos_of_page + len('page/') + len('000')])
+ page_number = int(page_number_str)
+ page_number_next = page_number + 1
+ if page_number_next >= 100:
+ page_number_next_str = str(page_number_next)
+ elif page_number_next >= 10:
+ page_number_next_str = '0' + str(page_number_next)
+ else:
+ page_number_next_str = '00' + str(page_number_next)
+ self.next_url = str(self.video_list_page_url).replace(page_number_str, page_number_next_str)
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "self.next_url", str(urllib.unquote_plus(self.next_url))),
+ xbmc.LOGDEBUG)
+
+ #
+ # Get the videos...
+ #
+ self.getVideos()
+
+ #
+ # Get videos...
+ #
+ def getVideos(self):
+ #
+ # Init
+ #
+ previous_video_page_url = ''
+ after_tab_episodes = False
+ # Create a list for our items.
+ listing = []
+
+ #
+ # Get HTML page
+ #
+ response = requests.get(self.video_list_page_url, headers=HEADERS)
+ html_source = response.text
+ html_source = html_source.encode('utf-8', 'ignore')
+
+ # Parse response
+ soup = BeautifulSoup(html_source)
+
+ # xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ # ADDON, VERSION, DATE, "html_source", str(html_source)), xbmc.LOGDEBUG)
+
+ # <div id="post-8791" class="blog-item video-item post-8791 post type-post status-publish format-standard has-post-thumbnail hentry category-haktip ...
+ # <div class="row ">
+ # <div class="col-md-6 col-sm-6">
+ # <div class="item-thumbnail">
+ # <a href="https://www.hak5.org/episodes/season-22/hak5-2217-bushveld-b-roll-hack-across-the-planet" title="Hak5 2217 – Bushveld B-Roll – Hack Across the Planet">
+ # <img width="300" height="169" src="https://www.hak5.org/wp-content/uploads/2017/07/hak5-2217-bushveld-b-roll-hack-a-300x169.jpg" ...
+ # </a>
+ # </div>
+ # <div class="clearfix"></div>
+ # </div><!--/col6-->
+ # <div class="col-md-6 col-sm-6">
+ # <div class="item-head row">
+ # <div class="col-md-10 col-sm-10 col-xs-9">
+ # <h3><a class="maincolor2hover" href="https://www.hak5.org/episodes/season-22/hak5-2217-bushveld-b-roll-hack-across-the-planet" rel="8851"
+ # title="Hak5 2217 – Bushveld B-Roll – Hack Across the Planet">Hak5 2217 &#8211; Bushveld B-Roll &#8211; Hack Across the Planet</a></h3>
+ # <div class="blog-meta">
+ # <span><a href="https://www.hak5.org/author/snubs" title="Posts by Shannon Morse" rel="author">Shannon Morse</a></span> |
+ # <span><a href="https://www.hak5.org/category/episodes/season-22" rel="category tag">Season 22</a></span>
+ # |
+ # <span><a href="https://www.hak5.org/episodes/season-22/hak5-2217-bushveld-b-roll-hack-across-the-planet#respond">0 Comments</a></span>
+ # </div>
+ # </div>
+ # <div class="col-md-2 col-sm-2 col-xs-3">
+ # <div class="blog-date">
+ # <span>05</span>
+ # <span>Jul</span>
+ # </div>
+ # </div>
+ # </div>
+ # <div class="blog-excerpt">
+ # <p>Happy 4th of July! Sign up for the London meetup at https://HackAcrossThePlanet.com &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- Shop: ...
+ # <a href="https://www.hak5.org/episodes/season-22/hak5-2217-bushveld-b-roll-hack-across-the-planet" class="readmore maincolor2 bordercolor2 bgcolor2hover ...
+ # </div>
+ # </div><!--/col6-->
+ # </div><!--/row-->
+ # <div class="clearfix"></div>
+ # </div>
+
+ episodes = soup.findAll('div', attrs={'id': re.compile("^" + 'post')})
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "len(episodes)", str(len(episodes))), xbmc.LOGDEBUG)
+
+ for episode in episodes:
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "episode)", str(episode)), xbmc.LOGDEBUG)
+
+ video_page_url = episode.a['href']
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "video_page_url", str(video_page_url)), xbmc.LOGDEBUG)
+
+ try:
+ thumbnail_url = episode.img['src']
+ except:
+ thumbnail_url = ''
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "thumbnail_url", str(thumbnail_url)), xbmc.LOGDEBUG)
+
+ title = episode.a['title']
+
+ # Clean up title
+ try:
+ title = title.encode('utf-8')
+ except:
+ pass
+
+ title = title.replace('-', ' ')
+ title = title.replace('/', ' ')
+ title = title.replace(' i ', ' I ')
+ title = title.replace(' ii ', ' II ')
+ title = title.replace(' iii ', ' III ')
+ title = title.replace(' iv ', ' IV ')
+ title = title.replace(' v ', ' V ')
+ title = title.replace(' vi ', ' VI ')
+ title = title.replace(' vii ', ' VII ')
+ title = title.replace(' viii ', ' VIII ')
+ title = title.replace(' ix ', ' IX ')
+ title = title.replace(' x ', ' X ')
+ title = title.replace(' xi ', ' XI ')
+ title = title.replace(' xii ', ' XII ')
+ title = title.replace(' xiii ', ' XIII ')
+ title = title.replace(' xiv ', ' XIV ')
+ title = title.replace(' xv ', ' XV ')
+ title = title.replace(' xvi ', ' XVI ')
+ title = title.replace(' xvii ', ' XVII ')
+ title = title.replace(' xviii ', ' XVIII ')
+ title = title.replace(' xix ', ' XIX ')
+ title = title.replace(' xx ', ' XXX ')
+ title = title.replace(' xxi ', ' XXI ')
+ title = title.replace(' xxii ', ' XXII ')
+ title = title.replace(' xxiii ', ' XXIII ')
+ title = title.replace(' xxiv ', ' XXIV ')
+ title = title.replace(' xxv ', ' XXV ')
+ title = title.replace(' xxvi ', ' XXVI ')
+ title = title.replace(' xxvii ', ' XXVII ')
+ title = title.replace(' xxviii ', ' XXVIII ')
+ title = title.replace(' xxix ', ' XXIX ')
+ title = title.replace(' xxx ', ' XXX ')
+ title = title.replace(' ', ' ')
+ # welcome to unescaping-hell
+ title = title.replace('&amp;#039;', "'")
+ title = title.replace('&amp;#39;', "'")
+ title = title.replace('&amp;quot;', '"')
+ title = title.replace("&#039;", "'")
+ title = title.replace("&#39;", "'")
+ title = title.replace('&amp;amp;', '&')
+ title = title.replace('&amp;', '&')
+ title = title.replace('&quot;', '"')
+ title = title.replace('&ldquo;', '"')
+ title = title.replace('&rdquo;', '"')
+ title = title.replace('&rsquo;', "'")
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "title", str(title)), xbmc.LOGDEBUG)
+
+ # lets find the blog date month and year
+ search_for_string = 'https://www.hak5.org/wp-content/uploads/'
+ blog_date = episode.findAll('img', attrs={'src': re.compile('^' + search_for_string)})
+ blog_date = str(blog_date)
+ blog_date_year_start_pos = blog_date.find(search_for_string) + len(search_for_string)
+ blog_date_year_end_pos = blog_date.find('/', blog_date_year_start_pos)
+ blog_date_year = blog_date[blog_date_year_start_pos: blog_date_year_end_pos]
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "blog_date_year)", str(blog_date_year)), xbmc.LOGDEBUG)
+
+ blog_date_month_start_pos = blog_date_year_end_pos + 1
+ blog_date_month_end_pos = blog_date_month_start_pos + 2
+ blog_date_month = blog_date[blog_date_month_start_pos:blog_date_month_end_pos]
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "blog_date_month)", str(blog_date_month)), xbmc.LOGDEBUG)
+
+ # lets find the blog date day
+ blog_date = episode.findAll('div', attrs={'class': re.compile("^" + 'blog-date')})
+ blog_date = str(blog_date)
+ blog_date_day_start_pos = blog_date.find('<span>')
+ blog_date_day_end_pos = blog_date.find('</span>')
+ blog_date_day = blog_date[blog_date_day_start_pos + len('<span>'):blog_date_day_end_pos]
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "blog_date_day)", str(blog_date_day)), xbmc.LOGDEBUG)
+
+ video_date = blog_date_year + '-' + blog_date_month + '-' + blog_date_day + ' 00:00:01'
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "video_date)", str(video_date)), xbmc.LOGDEBUG)
+
+ # Unescaping the plot
+ try:
+ plot = HTMLParser.HTMLParser().unescape(episode.p.text)
+ except:
+ plot = title
+
+ add_sort_methods()
+
+ context_menu_items = []
+ # Add refresh option to context menu
+ context_menu_items.append((LANGUAGE(30104), 'Container.Refresh'))
+ # Add episode info to context menu
+ context_menu_items.append((LANGUAGE(30105), 'XBMC.Action(Info)'))
+
+ list_item = xbmcgui.ListItem(label=title, thumbnailImage=thumbnail_url)
+ list_item.setInfo("video",
+ {"title": title, "studio": ADDON, "dateadded": video_date, "year": blog_date_year,
+ "plot": plot})
+ list_item.setInfo("mediatype", "video")
+ list_item.setArt({'thumb': thumbnail_url, 'icon': thumbnail_url,
+ 'fanart': os.path.join(IMAGES_PATH, 'fanart-blur.jpg')})
+ list_item.setProperty('IsPlayable', 'true')
+ parameters = {"action": "play", "video_page_url": video_page_url, "title": title}
+ url = self.plugin_url + '?' + urllib.urlencode(parameters)
+ is_folder = False
+ # Adding context menu items to context menu
+ list_item.addContextMenuItems(context_menu_items, replaceItems=False)
+ # Add our item to the listing as a 3-element tuple.
+ listing.append((url, list_item, is_folder))
+
+ # Next page entry
+ if self.next_page_possible == 'True':
+ list_item = xbmcgui.ListItem(LANGUAGE(30200), thumbnailImage=os.path.join(IMAGES_PATH, 'next-page.png'))
+ list_item.setArt({'fanart': os.path.join(IMAGES_PATH, 'fanart-blur.jpg')})
+ list_item.setProperty('IsPlayable', 'false')
+ parameters = {"action": "list-episodes", "url": str(self.next_url),
+ "next_page_possible": self.next_page_possible}
+ url = self.plugin_url + '?' + urllib.urlencode(parameters)
+ is_folder = True
+ # Adding context menu items to context menu
+ list_item.addContextMenuItems(context_menu_items, replaceItems=False)
+ # Add our item to the listing as a 3-element tuple.
+ listing.append((url, list_item, is_folder))
+
+ # Add our listing to Kodi.
+ # Large lists and/or slower systems benefit from adding all items at once via addDirectoryItems
+ # instead of adding one by ove via addDirectoryItem.
+ xbmcplugin.addDirectoryItems(self.plugin_handle, listing, len(listing))
+ # Disable sorting
+ xbmcplugin.addSortMethod(handle=self.plugin_handle, sortMethod=xbmcplugin.SORT_METHOD_NONE)
+ # Finish creating a virtual folder.
+ xbmcplugin.endOfDirectory(self.plugin_handle)
+
+def add_sort_methods():
+ 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(int(sys.argv[1]), sortMethod=method)
diff --git a/plugin.video.hak5/resources/lib/hak5_list_seasons.py b/plugin.video.hak5/resources/lib/hak5_list_seasons.py
new file mode 100644
index 0000000..b0544aa
--- /dev/null
+++ b/plugin.video.hak5/resources/lib/hak5_list_seasons.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#
+# Imports
+#
+import os
+import requests
+import sys
+import urllib
+import urlparse
+import re
+import xbmc
+import xbmcgui
+import xbmcplugin
+from BeautifulSoup import BeautifulSoup
+
+from hak5_const import ADDON, DATE, VERSION, IMAGES_PATH, HEADERS, HAK5SEASONSURLHTTPS, LANGUAGE
+
+#
+# Main class
+#
+class Main:
+ def __init__(self):
+ # Get the command line arguments
+ # Get the plugin url in plugin:// notation
+ self.plugin_url = sys.argv[0]
+ # Get the plugin handle as an integer number
+ self.plugin_handle = int(sys.argv[1])
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s, %s = %s" % (
+ ADDON, VERSION, DATE, "ARGV", repr(sys.argv), "File", str(__file__)), xbmc.LOGDEBUG)
+
+ # Parse parameters...
+ self.plugin_category = urlparse.parse_qs(urlparse.urlparse(sys.argv[2]).query)['plugin_category'][0]
+ self.video_list_page_url = urlparse.parse_qs(urlparse.urlparse(sys.argv[2]).query)['url'][0]
+ self.next_page_possible = urlparse.parse_qs(urlparse.urlparse(sys.argv[2]).query)['next_page_possible'][0]
+
+ self.video_list_page_url = str(self.video_list_page_url).replace('https', 'http')
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "self.video_list_page_url", str(self.video_list_page_url)),
+ xbmc.LOGDEBUG)
+
+ #
+ # Get the videos...
+ #
+ self.getVideos()
+
+ #
+ # Get videos...
+ #
+ def getVideos(self):
+ #
+ # Init
+ #
+ # Create a list for our items.
+ listing = []
+
+ #
+ # Get HTML page...
+ #
+ response = requests.get(self.video_list_page_url, headers=HEADERS)
+ html_source = response.text
+ html_source = html_source.encode('utf-8', 'ignore')
+
+ # <a href="https://www.hak5.org/category/episodes/season-22" class="menu-link sub-menu-link">Season 22 </a>
+
+ # Parse response...
+ soup = BeautifulSoup(html_source)
+
+ # xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ # ADDON, VERSION, DATE, "html_source", str(html_source)), xbmc.LOGDEBUG)
+
+ seasons = soup.findAll('a', attrs={'href': re.compile("^" + HAK5SEASONSURLHTTPS)})
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "len(seasons)", str(len(seasons))), xbmc.LOGDEBUG)
+
+ for season in seasons:
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "season", str(season)), xbmc.LOGDEBUG)
+
+ # let's skip these links
+ # <a href="https://www.hak5.org/category/episodes/season-22" class="menu-link sub-menu-link">Season 22 </a>
+ if str(season).find("class=") > 0:
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (ADDON, VERSION, DATE, "skipped season that contains class=", str(season)), xbmc.LOGDEBUG)
+ continue
+
+ # let's skip these links
+ # <a href="https://www.hak5.org/category/episodes/season-22" rel="category tag">Season 22</a>
+ if str(season).find("rel=") > 0:
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (ADDON, VERSION, DATE, "skipped season that contains rel=", str(season)), xbmc.LOGDEBUG)
+ continue
+
+ # let's skip links that don't contain the word season
+ # <a title="Pineapple Uni" href="https://www.hak5.org/category/episodes/pineapple-university">Pineapple Uni</a>
+ if str(season).find("season") > 0:
+ pass
+ else:
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "skipped season that does not contain the word season", str(season)), xbmc.LOGDEBUG)
+ continue
+
+ url = season['href']
+ thumbnail_url = ''
+ title = season.text
+
+ add_sort_methods()
+
+ context_menu_items = []
+ # Add refresh option to context menu
+ context_menu_items.append((LANGUAGE(30104), 'Container.Refresh'))
+
+ # Add to list...
+ list_item = xbmcgui.ListItem(label=title, thumbnailImage=thumbnail_url)
+ list_item.setArt({'thumb': thumbnail_url, 'icon': thumbnail_url,
+ 'fanart': os.path.join(IMAGES_PATH, 'fanart-blur.jpg')})
+ list_item.setProperty('IsPlayable', 'false')
+ parameters = {"action": "list-episodes", "season_name": title, "url": url, "next_page_possible": "False",
+ "title": title}
+ url = self.plugin_url + '?' + urllib.urlencode(parameters)
+ is_folder = True
+ # Adding context menu items to context menu
+ list_item.addContextMenuItems(context_menu_items, replaceItems=False)
+ # Add our item to the listing as a 3-element tuple.
+ listing.append((url, list_item, is_folder))
+
+ # Add our listing to Kodi.
+ # Large lists and/or slower systems benefit from adding all items at once via addDirectoryItems
+ # instead of adding one by ove via addDirectoryItem.
+ xbmcplugin.addDirectoryItems(self.plugin_handle, listing, len(listing))
+ # Disable sorting
+ xbmcplugin.addSortMethod(handle=self.plugin_handle, sortMethod=xbmcplugin.SORT_METHOD_NONE)
+ # Finish creating a virtual folder.
+ xbmcplugin.endOfDirectory(self.plugin_handle)
+
+def add_sort_methods():
+ 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(int(sys.argv[1]), sortMethod=method) \ No newline at end of file
diff --git a/plugin.video.hak5/resources/lib/hak5_main.py b/plugin.video.hak5/resources/lib/hak5_main.py
new file mode 100644
index 0000000..49575a6
--- /dev/null
+++ b/plugin.video.hak5/resources/lib/hak5_main.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#
+# Imports
+#
+import sys
+import urllib
+import xbmcgui
+import xbmcplugin
+import os
+
+from hak5_const import LANGUAGE, IMAGES_PATH, HAK5RECENTLYADDEDURL, HAK5SEASONSURLHTTP, \
+ HAKTIKRECENTLYADDEDURL, THREATWIRERECENTLYADDEDURL, TEKTHINGRECENTLYADDEDURL, PINEAPPLEUNIVERSITYRECENTLYADDEDURL, \
+ METASPLOITRECENTLYADDEDURL
+#
+# Main class
+#
+class Main:
+ def __init__(self):
+ # Get the command line arguments
+ # Get the plugin url in plugin:// notation
+ self.plugin_url = sys.argv[0]
+ # Get the plugin handle as an integer number
+ self.plugin_handle = int(sys.argv[1])
+
+ #
+ # Hak5 Recently Added Episodes
+ #
+ parameters = {"action": "list-episodes", "plugin_category": LANGUAGE(30301), "url": HAK5RECENTLYADDEDURL,
+ "next_page_possible": "False"}
+ url = self.plugin_url + '?' + urllib.urlencode(parameters)
+ list_item = xbmcgui.ListItem(LANGUAGE(30301))
+ is_folder = True
+ list_item.setArt({'fanart': os.path.join(IMAGES_PATH, 'fanart-blur.jpg')})
+ list_item.setProperty('IsPlayable', 'false')
+ xbmcplugin.addDirectoryItem(handle=self.plugin_handle, url=url, listitem=list_item, isFolder=is_folder)
+
+ #
+ # Hak5 Seasons
+ #
+ parameters = {"action": "list-seasons", "plugin_category": LANGUAGE(30302), "url": HAK5SEASONSURLHTTP,
+ "next_page_possible": "False"}
+ url = self.plugin_url + '?' + urllib.urlencode(parameters)
+ list_item = xbmcgui.ListItem(LANGUAGE(30302))
+ is_folder = True
+ list_item.setArt({'fanart': os.path.join(IMAGES_PATH, 'fanart-blur.jpg')})
+ list_item.setProperty('IsPlayable', 'false')
+ xbmcplugin.addDirectoryItem(handle=self.plugin_handle, url=url, listitem=list_item, isFolder=is_folder)
+
+ #
+ # Haktik Recently Added Episodes
+ #
+ parameters = {"action": "list-episodes", "plugin_category": LANGUAGE(30303),
+ "url": HAKTIKRECENTLYADDEDURL,
+ "next_page_possible": "True"}
+ url = self.plugin_url + '?' + urllib.urlencode(parameters)
+ list_item = xbmcgui.ListItem(LANGUAGE(30303))
+ is_folder = True
+ list_item.setArt({'fanart': os.path.join(IMAGES_PATH, 'fanart-blur.jpg')})
+ list_item.setProperty('IsPlayable', 'false')
+ xbmcplugin.addDirectoryItem(handle=self.plugin_handle, url=url, listitem=list_item, isFolder=is_folder)
+
+ #
+ # Threatwire Recently Added Episodes
+ #
+ parameters = {"action": "list-episodes", "plugin_category": LANGUAGE(30304), "url": THREATWIRERECENTLYADDEDURL,
+ "next_page_possible": "True"}
+ url = self.plugin_url + '?' + urllib.urlencode(parameters)
+ list_item = xbmcgui.ListItem(LANGUAGE(30304))
+ is_folder = True
+ list_item.setArt({'fanart': os.path.join(IMAGES_PATH, 'fanart-blur.jpg')})
+ list_item.setProperty('IsPlayable', 'false')
+ xbmcplugin.addDirectoryItem(handle=self.plugin_handle, url=url, listitem=list_item, isFolder=is_folder)
+
+ #
+ # Tekthing Recently Added Episodes
+ #
+ parameters = {"action": "list-episodes", "plugin_category": LANGUAGE(30305), "url": TEKTHINGRECENTLYADDEDURL,
+ "next_page_possible": "True"}
+ url = self.plugin_url + '?' + urllib.urlencode(parameters)
+ list_item = xbmcgui.ListItem(LANGUAGE(30305))
+ is_folder = True
+ list_item.setArt({'fanart': os.path.join(IMAGES_PATH, 'fanart-blur.jpg')})
+ list_item.setProperty('IsPlayable', 'false')
+ xbmcplugin.addDirectoryItem(handle=self.plugin_handle, url=url, listitem=list_item, isFolder=is_folder)
+
+ #
+ # Pinapple University Recently Added Episodes
+ #
+ parameters = {"action": "list-episodes", "plugin_category": LANGUAGE(30306), "url": PINEAPPLEUNIVERSITYRECENTLYADDEDURL,
+ "next_page_possible": "False"}
+ url = self.plugin_url + '?' + urllib.urlencode(parameters)
+ list_item = xbmcgui.ListItem(LANGUAGE(30306))
+ is_folder = True
+ list_item.setArt({'fanart': os.path.join(IMAGES_PATH, 'fanart-blur.jpg')})
+ list_item.setProperty('IsPlayable', 'false')
+ xbmcplugin.addDirectoryItem(handle=self.plugin_handle, url=url, listitem=list_item, isFolder=is_folder)
+
+ #
+ # Metasploit Recently Added Episodes
+ #
+ parameters = {"action": "list-episodes", "plugin_category": LANGUAGE(30307), "url": METASPLOITRECENTLYADDEDURL,
+ "next_page_possible": "True"}
+ url = self.plugin_url + '?' + urllib.urlencode(parameters)
+ list_item = xbmcgui.ListItem(LANGUAGE(30307))
+ is_folder = True
+ list_item.setArt({'fanart': os.path.join(IMAGES_PATH, 'fanart-blur.jpg')})
+ list_item.setProperty('IsPlayable', 'false')
+ xbmcplugin.addDirectoryItem(handle=self.plugin_handle, url=url, listitem=list_item, isFolder=is_folder)
+
+ # Disable sorting
+ xbmcplugin.addSortMethod(handle=self.plugin_handle, sortMethod=xbmcplugin.SORT_METHOD_NONE)
+ # Finish creating a virtual folder.
+ xbmcplugin.endOfDirectory(self.plugin_handle)
diff --git a/plugin.video.hak5/resources/lib/hak5_play.py b/plugin.video.hak5/resources/lib/hak5_play.py
new file mode 100644
index 0000000..685a992
--- /dev/null
+++ b/plugin.video.hak5/resources/lib/hak5_play.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#
+# Imports
+#
+import requests
+import sys
+import urlparse
+import xbmc
+import xbmcgui
+import xbmcplugin
+
+from hak5_const import ADDON, LANGUAGE, HEADERS, DATE, VERSION
+
+#
+# Main class
+#
+class Main:
+ #
+ # Init
+ #
+ def __init__(self):
+ # Get the command line arguments
+ # Get the plugin url in plugin:// notation
+ self.plugin_url = sys.argv[0]
+ # Get the plugin handle as an integer number
+ self.plugin_handle = int(sys.argv[1])
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s, %s = %s" % (
+ ADDON, VERSION, DATE, "ARGV", repr(sys.argv), "File", str(__file__)), xbmc.LOGDEBUG)
+
+ # Parse parameters...
+ self.video_page_url = urlparse.parse_qs(urlparse.urlparse(sys.argv[2]).query)['video_page_url'][0]
+ # Get the title.
+ self.title = urlparse.parse_qs(urlparse.urlparse(sys.argv[2]).query)['title'][0]
+ self.title = str(self.title)
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "self.video_page_url", str(self.video_page_url)), xbmc.LOGDEBUG)
+
+ #
+ # Play video...
+ #
+ self.playVideo()
+
+ #
+ # Play video...
+ #
+ def playVideo(self):
+ #
+ # Init
+ #
+ dialog_wait = xbmcgui.DialogProgress()
+
+ #
+ # Get current list item details...
+ #
+ # title = unicode(xbmc.getInfoLabel("listitem.Title"), "utf-8")
+ thumbnail_url = xbmc.getInfoImage("list_item.Thumb")
+ # studio = unicode(xbmc.getInfoLabel("list_item.Studio"), "utf-8")
+ plot = unicode(xbmc.getInfoLabel("list_item.Plot"), "utf-8")
+ genre = unicode(xbmc.getInfoLabel("list_item.Genre"), "utf-8")
+
+ reply = ''
+ session = ''
+
+ # requests is sooooo nice, respect!
+ session = requests.Session()
+
+ # get the page that contains the video
+ self.video_page_url = str(self.video_page_url).replace('https','http')
+ reply = requests.get(self.video_page_url, headers=HEADERS)
+
+ html_source = reply.text
+ html_source = html_source.encode('utf-8', 'ignore')
+
+ # xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ # ADDON, VERSION, DATE, "html_source", str(html_source)), xbmc.LOGDEBUG)
+
+ video_url = ''
+ no_url_found = True
+ have_valid_url = False
+
+ # <meta property="og:url" content="https://youtu.be/K_EOLgX5Dqs"/>
+ # let's extract the youtube-id
+ html_source = str(html_source)
+ start_pos_meta_prop = html_source.find('meta property="og:url"')
+ if start_pos_meta_prop > 0:
+ search_for_string = '"/>'
+ end_pos_meta_prop = html_source.find(search_for_string, start_pos_meta_prop)
+ if end_pos_meta_prop > 0:
+ # the last 3 characters are removed
+ meta_prop = html_source[start_pos_meta_prop: end_pos_meta_prop]
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "meta_prop", str(meta_prop)), xbmc.LOGDEBUG)
+
+ pos_of_last_slash = meta_prop.rfind('/')
+ youtube_id = meta_prop[pos_of_last_slash + 1:]
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "youtube_id", str(youtube_id)), xbmc.LOGDEBUG)
+
+ video_url = makeYouTubePluginUrl(youtube_id)
+ no_url_found = False
+ have_valid_url = True
+
+ xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
+ ADDON, VERSION, DATE, "video_url", str(video_url)), xbmc.LOGDEBUG)
+
+ # Play video...
+ if have_valid_url:
+ list_item = xbmcgui.ListItem(path=video_url)
+ xbmcplugin.setResolvedUrl(self.plugin_handle, True, list_item)
+ #
+ # Alert user
+ #
+ elif no_url_found:
+ xbmcgui.Dialog().ok(LANGUAGE(30000), LANGUAGE(30107))
+
+def makeYouTubePluginUrl(youtube_id):
+ return 'plugin://plugin.video.youtube/play/?video_id=%s' % youtube_id \ No newline at end of file
diff --git a/plugin.video.hak5/resources/settings.xml b/plugin.video.hak5/resources/settings.xml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugin.video.hak5/resources/settings.xml