diff --git a/plugins/favorites/manifest.json b/plugins/favorites/manifest.json
index 98fbb31..b6f006b 100644
--- a/plugins/favorites/manifest.json
+++ b/plugins/favorites/manifest.json
@@ -1,11 +1,11 @@
{
"manifest": {
- "name": "Favorites Plugin",
+ "name": "Favorites",
"author": "ITDominator",
"version": "0.0.1",
"support": "",
"requests": {
- "ui_target": "plugin_control_list",
+ "ui_target": "main_menu_bttn_box_bar",
"pass_fm_events": "true",
"pass_ui_objects": ["path_entry"],
"bind_keys": []
diff --git a/plugins/favorites/plugin.py b/plugins/favorites/plugin.py
index c2d8f18..ac061ca 100644
--- a/plugins/favorites/plugin.py
+++ b/plugins/favorites/plugin.py
@@ -26,7 +26,7 @@ def daemon_threaded(fn):
class Plugin:
def __init__(self):
- self.name = "Favorites Plugin" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
+ self.name = "Favorites" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
# where self.name should not be needed for message comms
self.path = os.path.dirname(os.path.realpath(__file__))
self._GLADE_FILE = f"{self.path}/favorites.glade"
diff --git a/plugins/movie_tv_info/__init__.py b/plugins/movie_tv_info/__init__.py
new file mode 100644
index 0000000..d36fa8c
--- /dev/null
+++ b/plugins/movie_tv_info/__init__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Module
+"""
diff --git a/plugins/movie_tv_info/__main__.py b/plugins/movie_tv_info/__main__.py
new file mode 100644
index 0000000..a576329
--- /dev/null
+++ b/plugins/movie_tv_info/__main__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Package
+"""
diff --git a/plugins/movie_tv_info/manifest.json b/plugins/movie_tv_info/manifest.json
new file mode 100644
index 0000000..9290b70
--- /dev/null
+++ b/plugins/movie_tv_info/manifest.json
@@ -0,0 +1,12 @@
+{
+ "manifest": {
+ "name": "Movie/TV Info",
+ "author": "ITDominator",
+ "version": "0.0.1",
+ "support": "",
+ "requests": {
+ "ui_target": "context_menu",
+ "pass_fm_events": "true"
+ }
+ }
+}
diff --git a/plugins/movie_tv_info/movie_tv_info.glade b/plugins/movie_tv_info/movie_tv_info.glade
new file mode 100644
index 0000000..3c8887e
--- /dev/null
+++ b/plugins/movie_tv_info/movie_tv_info.glade
@@ -0,0 +1,220 @@
+
+
+
+
+
+
+
+
+
diff --git a/plugins/movie_tv_info/plugin.py b/plugins/movie_tv_info/plugin.py
new file mode 100644
index 0000000..67f01c5
--- /dev/null
+++ b/plugins/movie_tv_info/plugin.py
@@ -0,0 +1,216 @@
+# Python imports
+import os, threading, subprocess, time, inspect, requests, shutil
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+gi.require_version('GdkPixbuf', '2.0')
+from gi.repository import Gtk, GLib, GdkPixbuf
+
+# Application imports
+from tmdbscraper import scraper
+
+
+# NOTE: Threads WILL NOT die with parent's destruction.
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
+ return wrapper
+
+# NOTE: Threads WILL die with parent's destruction.
+def daemon_threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
+ return wrapper
+
+
+
+
+class Plugin:
+ def __init__(self):
+ self.path = os.path.dirname(os.path.realpath(__file__))
+ self.name = "Movie/TV Info" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
+ # where self.name should not be needed for message comms
+ self._GLADE_FILE = f"{self.path}/movie_tv_info.glade"
+
+ self._builder = None
+ self._dialog = None
+ self._thumbnail_preview_img = None
+ self._tmdb = scraper.get_tmdb_scraper()
+ self._state = None
+ self._overview = None
+ self._file_name = None
+ self._file_location = None
+ self._trailer_link = None
+
+ self._event_system = None
+ self._event_sleep_time = .5
+ self._event_message = None
+
+
+ def get_ui_element(self):
+ self._builder = Gtk.Builder()
+ self._builder.add_from_file(self._GLADE_FILE)
+
+ classes = [self]
+ handlers = {}
+ for c in classes:
+ methods = None
+ try:
+ methods = inspect.getmembers(c, predicate=inspect.ismethod)
+ handlers.update(methods)
+ except Exception as e:
+ print(repr(e))
+
+ self._builder.connect_signals(handlers)
+
+ self._thumbnailer_dialog = self._builder.get_object("info_dialog")
+ self._overview = self._builder.get_object("textbuffer")
+ self._file_name = self._builder.get_object("file_name")
+ self._file_location = self._builder.get_object("file_location")
+ self._thumbnail_preview_img = self._builder.get_object("thumbnail_preview_img")
+ self._file_hash = self._builder.get_object("file_hash")
+ self._trailer_link = self._builder.get_object("trailer_link")
+
+ button = Gtk.Button(label=self.name)
+ button.connect("button-release-event", self._show_info_page)
+ return button
+
+ def set_fm_event_system(self, fm_event_system):
+ self._event_system = fm_event_system
+
+ def run(self):
+ self._module_event_observer()
+
+
+ @threaded
+ def _show_info_page(self, widget=None, eve=None):
+ self._event_system.push_gui_event([self.name, "get_current_state", ()])
+ self.wait_for_fm_message()
+
+ state = self._event_message
+ self._event_message = None
+
+ GLib.idle_add(self._process_changes, (state))
+
+ def _process_changes(self, state):
+ self._state = None
+
+ if len(state.selected_files) == 1:
+ self._state = state
+ self._set_ui_data()
+ response = self._thumbnailer_dialog.run()
+ if response in [Gtk.ResponseType.CLOSE, Gtk.ResponseType.DELETE_EVENT]:
+ self._thumbnailer_dialog.hide()
+
+ def _set_ui_data(self):
+ title, path, trailer, video_data = self.get_video_data()
+ keys = video_data.keys() if video_data else None
+
+ overview_text = video_data["overview"] if video_data else f"...NO {self.name.upper()} DATA..."
+
+ self.set_text_data(title, path, overview_text)
+ self.set_thumbnail(video_data) if video_data else ...
+ self.set_trailer_link(trailer)
+
+ print(video_data["videos"]) if not keys in ("", None) and "videos" in keys else ...
+
+ def get_video_data(self):
+ uri = self._state.selected_files[0]
+ path = self._state.tab.get_current_directory()
+ parts = uri.split("/")
+ _title = parts[ len(parts) - 1 ]
+
+ try:
+ title = _title.split("(")[0].strip()
+ startIndex = _title.index('(') + 1
+ endIndex = _title.index(')')
+ date = title[startIndex:endIndex]
+ except Exception as e:
+ print(repr(e))
+ title = _title
+ date = None
+
+ try:
+
+ video_data = self._tmdb.search(title, date)[0]
+ video_id = video_data["id"]
+ try:
+ results = self._tmdb.tmdbapi.get_movie(str(video_id), append_to_response="videos")["videos"]["results"]
+ for result in results:
+ if "YouTube" in result["site"]:
+ trailer = result["key"]
+
+ if not trailer:
+ raise Exception("No key found. Defering to none...")
+ except Exception as e:
+ print("No trailer found...")
+ trailer = None
+
+ except Exception as e:
+ print(repr(e))
+ video_data = None
+
+ return title, path, trailer, video_data
+
+
+ def set_text_data(self, title, path, overview_text):
+ self._file_name.set_text(title)
+ self._file_location.set_text(path)
+ self._overview.set_text(overview_text)
+
+ @threaded
+ def set_thumbnail(self, video_data):
+ background_url = video_data["backdrop_path"]
+ # background_url = video_data["poster_path"]
+ background_pth = "/tmp/sfm_mvtv_info.jpg"
+
+ try:
+ os.remove(background_pth)
+ except Exception as e:
+ ...
+
+ r = requests.get(background_url, stream = True)
+
+ if r.status_code == 200:
+ r.raw.decode_content = True
+ with open(background_pth,'wb') as f:
+ shutil.copyfileobj(r.raw, f)
+
+ print('Cover Background Image sucessfully retreived...')
+ preview_pixbuf = GdkPixbuf.Pixbuf.new_from_file(background_pth)
+ self._thumbnail_preview_img.set_from_pixbuf(preview_pixbuf)
+ else:
+ print('Cover Background Image Couldn\'t be retreived...')
+
+ def set_trailer_link(self, trailer):
+ if trailer:
+ self._trailer_link.set_uri(f"https://www.youtube.com/watch?v={trailer}")
+
+
+
+
+ def wait_for_fm_message(self):
+ while not self._event_message:
+ pass
+
+ @daemon_threaded
+ def _module_event_observer(self):
+ while True:
+ time.sleep(self._event_sleep_time)
+ event = self._event_system.read_module_event()
+ if event:
+ try:
+ if event[0] == self.name:
+ target_id, method_target, data = self._event_system.consume_module_event()
+
+ if not method_target:
+ self._event_message = data
+ else:
+ method = getattr(self.__class__, f"{method_target}")
+ if data:
+ data = method(*(self, *data))
+ else:
+ method(*(self,))
+ except Exception as e:
+ print(repr(e))
diff --git a/plugins/movie_tv_info/tmdbscraper/scraper.py b/plugins/movie_tv_info/tmdbscraper/scraper.py
new file mode 100644
index 0000000..db25e2b
--- /dev/null
+++ b/plugins/movie_tv_info/tmdbscraper/scraper.py
@@ -0,0 +1,22 @@
+import json
+import sys
+
+from .lib.tmdbscraper.tmdb import TMDBMovieScraper
+from .lib.tmdbscraper.fanarttv import get_details as get_fanarttv_artwork
+from .lib.tmdbscraper.imdbratings import get_details as get_imdb_details
+from .lib.tmdbscraper.traktratings import get_trakt_ratinginfo
+from .scraper_datahelper import combine_scraped_details_info_and_ratings, \
+ combine_scraped_details_available_artwork, find_uniqueids_in_text, get_params
+from .scraper_config import configure_scraped_details, PathSpecificSettings, \
+ configure_tmdb_artwork, is_fanarttv_configured
+
+
+
+
+
+
+def get_tmdb_scraper():
+ language = 'en-US'
+ certcountry = 'us'
+ ADDON_SETTINGS = None
+ return TMDBMovieScraper(ADDON_SETTINGS, language, certcountry)
diff --git a/plugins/movie_tv_info/tmdbscraper/scraper_config.py b/plugins/movie_tv_info/tmdbscraper/scraper_config.py
new file mode 100644
index 0000000..76a042b
--- /dev/null
+++ b/plugins/movie_tv_info/tmdbscraper/scraper_config.py
@@ -0,0 +1,111 @@
+def configure_scraped_details(details, settings):
+ details = _configure_rating_prefix(details, settings)
+ details = _configure_keeporiginaltitle(details, settings)
+ details = _configure_trailer(details, settings)
+ details = _configure_multiple_studios(details, settings)
+ details = _configure_default_rating(details, settings)
+ details = _configure_tags(details, settings)
+ return details
+
+def configure_tmdb_artwork(details, settings):
+ if 'available_art' not in details:
+ return details
+
+ art = details['available_art']
+ fanart_enabled = settings.getSettingBool('fanart')
+ if not fanart_enabled:
+ if 'fanart' in art:
+ del art['fanart']
+ if 'set.fanart' in art:
+ del art['set.fanart']
+ if not settings.getSettingBool('landscape'):
+ if 'landscape' in art:
+ if fanart_enabled:
+ art['fanart'] = art.get('fanart', []) + art['landscape']
+ del art['landscape']
+ if 'set.landscape' in art:
+ if fanart_enabled:
+ art['set.fanart'] = art.get('set.fanart', []) + art['set.landscape']
+ del art['set.landscape']
+
+ return details
+
+def is_fanarttv_configured(settings):
+ return settings.getSettingBool('enable_fanarttv_artwork')
+
+def _configure_rating_prefix(details, settings):
+ if details['info'].get('mpaa'):
+ details['info']['mpaa'] = settings.getSettingString('certprefix') + details['info']['mpaa']
+ return details
+
+def _configure_keeporiginaltitle(details, settings):
+ if settings.getSettingBool('keeporiginaltitle'):
+ details['info']['title'] = details['info']['originaltitle']
+ return details
+
+def _configure_trailer(details, settings):
+ if details['info'].get('trailer') and not settings.getSettingBool('trailer'):
+ del details['info']['trailer']
+ return details
+
+def _configure_multiple_studios(details, settings):
+ if not settings.getSettingBool('multiple_studios'):
+ details['info']['studio'] = details['info']['studio'][:1]
+ return details
+
+def _configure_default_rating(details, settings):
+ imdb_default = bool(details['ratings'].get('imdb')) and settings.getSettingString('RatingS') == 'IMDb'
+ trakt_default = bool(details['ratings'].get('trakt')) and settings.getSettingString('RatingS') == 'Trakt'
+ default_rating = 'themoviedb'
+ if imdb_default:
+ default_rating = 'imdb'
+ elif trakt_default:
+ default_rating = 'trakt'
+ if default_rating not in details['ratings']:
+ default_rating = list(details['ratings'].keys())[0] if details['ratings'] else None
+ for rating_type in details['ratings'].keys():
+ details['ratings'][rating_type]['default'] = rating_type == default_rating
+ return details
+
+def _configure_tags(details, settings):
+ if not settings.getSettingBool('add_tags'):
+ del details['info']['tag']
+ return details
+
+# pylint: disable=invalid-name
+try:
+ basestring
+except NameError: # py2 / py3
+ basestring = str
+
+#pylint: disable=redefined-builtin
+class PathSpecificSettings(object):
+ # read-only shim for typed `xbmcaddon.Addon().getSetting*` methods
+ def __init__(self, settings_dict, log_fn):
+ self.data = settings_dict
+ self.log = log_fn
+
+ def getSettingBool(self, id):
+ return self._inner_get_setting(id, bool, False)
+
+ def getSettingInt(self, id):
+ return self._inner_get_setting(id, int, 0)
+
+ def getSettingNumber(self, id):
+ return self._inner_get_setting(id, float, 0.0)
+
+ def getSettingString(self, id):
+ return self._inner_get_setting(id, basestring, '')
+
+ def _inner_get_setting(self, setting_id, setting_type, default):
+ value = self.data.get(setting_id)
+ if isinstance(value, setting_type):
+ return value
+ self._log_bad_value(value, setting_id)
+ return default
+
+ def _log_bad_value(self, value, setting_id):
+ if value is None:
+ self.log("requested setting ({0}) was not found.".format(setting_id))
+ else:
+ self.log('failed to load value "{0}" for setting {1}'.format(value, setting_id))
diff --git a/plugins/movie_tv_info/tmdbscraper/scraper_datahelper.py b/plugins/movie_tv_info/tmdbscraper/scraper_datahelper.py
new file mode 100644
index 0000000..23504e0
--- /dev/null
+++ b/plugins/movie_tv_info/tmdbscraper/scraper_datahelper.py
@@ -0,0 +1,54 @@
+import re
+try:
+ from urlparse import parse_qsl
+except ImportError: # py2 / py3
+ from urllib.parse import parse_qsl
+
+# get addon params from the plugin path querystring
+def get_params(argv):
+ result = {'handle': int(argv[0])}
+ if len(argv) < 2 or not argv[1]:
+ return result
+
+ result.update(parse_qsl(argv[1].lstrip('?')))
+ return result
+
+def combine_scraped_details_info_and_ratings(original_details, additional_details):
+ def update_or_set(details, key, value):
+ if key in details:
+ details[key].update(value)
+ else:
+ details[key] = value
+
+ if additional_details:
+ if additional_details.get('info'):
+ update_or_set(original_details, 'info', additional_details['info'])
+ if additional_details.get('ratings'):
+ update_or_set(original_details, 'ratings', additional_details['ratings'])
+ return original_details
+
+def combine_scraped_details_available_artwork(original_details, additional_details):
+ if additional_details and additional_details.get('available_art'):
+ available_art = additional_details['available_art']
+ if not original_details.get('available_art'):
+ original_details['available_art'] = available_art
+ else:
+ for arttype, artlist in available_art.items():
+ original_details['available_art'][arttype] = \
+ artlist + original_details['available_art'].get(arttype, [])
+
+ return original_details
+
+def find_uniqueids_in_text(input_text):
+ result = {}
+ res = re.search(r'(themoviedb.org/movie/)([0-9]+)', input_text)
+ if (res):
+ result['tmdb'] = res.group(2)
+ res = re.search(r'imdb....?/title/tt([0-9]+)', input_text)
+ if (res):
+ result['imdb'] = 'tt' + res.group(1)
+ else:
+ res = re.search(r'imdb....?/Title\?t{0,2}([0-9]+)', input_text)
+ if (res):
+ result['imdb'] = 'tt' + res.group(1)
+ return result
diff --git a/plugins/movie_tv_info/tmdbscraper/scraper_xbmc.py b/plugins/movie_tv_info/tmdbscraper/scraper_xbmc.py
new file mode 100644
index 0000000..c976407
--- /dev/null
+++ b/plugins/movie_tv_info/tmdbscraper/scraper_xbmc.py
@@ -0,0 +1,175 @@
+import json
+import sys
+import xbmc
+import xbmcaddon
+import xbmcgui
+import xbmcplugin
+
+from lib.tmdbscraper.tmdb import TMDBMovieScraper
+from lib.tmdbscraper.fanarttv import get_details as get_fanarttv_artwork
+from lib.tmdbscraper.imdbratings import get_details as get_imdb_details
+from lib.tmdbscraper.traktratings import get_trakt_ratinginfo
+from scraper_datahelper import combine_scraped_details_info_and_ratings, \
+ combine_scraped_details_available_artwork, find_uniqueids_in_text, get_params
+from scraper_config import configure_scraped_details, PathSpecificSettings, \
+ configure_tmdb_artwork, is_fanarttv_configured
+
+ADDON_SETTINGS = xbmcaddon.Addon()
+ID = ADDON_SETTINGS.getAddonInfo('id')
+
+def log(msg, level=xbmc.LOGDEBUG):
+ xbmc.log(msg='[{addon}]: {msg}'.format(addon=ID, msg=msg), level=level)
+
+def get_tmdb_scraper(settings):
+ language = settings.getSettingString('language')
+ certcountry = settings.getSettingString('tmdbcertcountry')
+ return TMDBMovieScraper(ADDON_SETTINGS, language, certcountry)
+
+def search_for_movie(title, year, handle, settings):
+ log("Find movie with title '{title}' from year '{year}'".format(title=title, year=year), xbmc.LOGINFO)
+ title = _strip_trailing_article(title)
+ search_results = get_tmdb_scraper(settings).search(title, year)
+ if not search_results:
+ return
+ if 'error' in search_results:
+ header = "The Movie Database Python error searching with web service TMDB"
+ xbmcgui.Dialog().notification(header, search_results['error'], xbmcgui.NOTIFICATION_WARNING)
+ log(header + ': ' + search_results['error'], xbmc.LOGWARNING)
+ return
+
+ for movie in search_results:
+ listitem = _searchresult_to_listitem(movie)
+ uniqueids = {'tmdb': str(movie['id'])}
+ xbmcplugin.addDirectoryItem(handle=handle, url=build_lookup_string(uniqueids),
+ listitem=listitem, isFolder=True)
+
+_articles = [prefix + article for prefix in (', ', ' ') for article in ("the", "a", "an")]
+def _strip_trailing_article(title):
+ title = title.lower()
+ for article in _articles:
+ if title.endswith(article):
+ return title[:-len(article)]
+ return title
+
+def _searchresult_to_listitem(movie):
+ movie_info = {'title': movie['title']}
+ movie_label = movie['title']
+
+ movie_year = movie['release_date'].split('-')[0] if movie.get('release_date') else None
+ if movie_year:
+ movie_label += ' ({})'.format(movie_year)
+ movie_info['year'] = movie_year
+
+ listitem = xbmcgui.ListItem(movie_label, offscreen=True)
+
+ listitem.setInfo('video', movie_info)
+ if movie['poster_path']:
+ listitem.setArt({'thumb': movie['poster_path']})
+
+ return listitem
+
+# Low limit because a big list of artwork can cause trouble in some cases
+# (a column can be too large for the MySQL integration),
+# and how useful is a big list anyway? Not exactly rhetorical, this is an experiment.
+IMAGE_LIMIT = 10
+
+def add_artworks(listitem, artworks):
+ for arttype, artlist in artworks.items():
+ if arttype == 'fanart':
+ continue
+ for image in artlist[:IMAGE_LIMIT]:
+ listitem.addAvailableArtwork(image['url'], arttype)
+
+ fanart_to_set = [{'image': image['url'], 'preview': image['preview']}
+ for image in artworks['fanart'][:IMAGE_LIMIT]]
+ listitem.setAvailableFanart(fanart_to_set)
+
+def get_details(input_uniqueids, handle, settings):
+ if not input_uniqueids:
+ return False
+ details = get_tmdb_scraper(settings).get_details(input_uniqueids)
+ if not details:
+ return False
+ if 'error' in details:
+ header = "The Movie Database Python error with web service TMDB"
+ xbmcgui.Dialog().notification(header, details['error'], xbmcgui.NOTIFICATION_WARNING)
+ log(header + ': ' + details['error'], xbmc.LOGWARNING)
+ return False
+
+ details = configure_tmdb_artwork(details, settings)
+
+ if settings.getSettingString('RatingS') == 'IMDb' or settings.getSettingBool('imdbanyway'):
+ imdbinfo = get_imdb_details(details['uniqueids'])
+ if 'error' in imdbinfo:
+ header = "The Movie Database Python error with website IMDB"
+ log(header + ': ' + imdbinfo['error'], xbmc.LOGWARNING)
+ else:
+ details = combine_scraped_details_info_and_ratings(details, imdbinfo)
+
+ if settings.getSettingString('RatingS') == 'Trakt' or settings.getSettingBool('traktanyway'):
+ traktinfo = get_trakt_ratinginfo(details['uniqueids'])
+ details = combine_scraped_details_info_and_ratings(details, traktinfo)
+
+ if is_fanarttv_configured(settings):
+ fanarttv_info = get_fanarttv_artwork(details['uniqueids'],
+ settings.getSettingString('fanarttv_clientkey'),
+ settings.getSettingString('fanarttv_language'),
+ details['_info']['set_tmdbid'])
+ details = combine_scraped_details_available_artwork(details, fanarttv_info)
+
+ details = configure_scraped_details(details, settings)
+
+ listitem = xbmcgui.ListItem(details['info']['title'], offscreen=True)
+ listitem.setInfo('video', details['info'])
+ listitem.setCast(details['cast'])
+ listitem.setUniqueIDs(details['uniqueids'], 'tmdb')
+ add_artworks(listitem, details['available_art'])
+
+ for rating_type, value in details['ratings'].items():
+ if 'votes' in value:
+ listitem.setRating(rating_type, value['rating'], value['votes'], value['default'])
+ else:
+ listitem.setRating(rating_type, value['rating'], defaultt=value['default'])
+
+ xbmcplugin.setResolvedUrl(handle=handle, succeeded=True, listitem=listitem)
+ return True
+
+def find_uniqueids_in_nfo(nfo, handle):
+ uniqueids = find_uniqueids_in_text(nfo)
+ if uniqueids:
+ listitem = xbmcgui.ListItem(offscreen=True)
+ xbmcplugin.addDirectoryItem(
+ handle=handle, url=build_lookup_string(uniqueids), listitem=listitem, isFolder=True)
+
+def build_lookup_string(uniqueids):
+ return json.dumps(uniqueids)
+
+def parse_lookup_string(uniqueids):
+ try:
+ return json.loads(uniqueids)
+ except ValueError:
+ log("Can't parse this lookup string, is it from another add-on?\n" + uniqueids, xbmc.LOGWARNING)
+ return None
+
+def run():
+ params = get_params(sys.argv[1:])
+ enddir = True
+ if 'action' in params:
+ settings = ADDON_SETTINGS if not params.get('pathSettings') else \
+ PathSpecificSettings(json.loads(params['pathSettings']), lambda msg: log(msg, xbmc.LOGWARNING))
+ action = params["action"]
+ if action == 'find' and 'title' in params:
+ search_for_movie(params["title"], params.get("year"), params['handle'], settings)
+ elif action == 'getdetails' and 'url' in params:
+ enddir = not get_details(parse_lookup_string(params["url"]), params['handle'], settings)
+ elif action == 'NfoUrl' and 'nfo' in params:
+ find_uniqueids_in_nfo(params["nfo"], params['handle'])
+ else:
+ log("unhandled action: " + action, xbmc.LOGWARNING)
+ else:
+ log("No action in 'params' to act on", xbmc.LOGWARNING)
+ if enddir:
+ xbmcplugin.endOfDirectory(params['handle'])
+
+if __name__ == '__main__':
+ run()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
index a0e36e9..18f1374 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
@@ -10,6 +10,12 @@ from utils.settings import Settings
from core.controller import Controller
+class App_Launch_Exception(Exception):
+ ...
+
+class Controller_Start_Exceptio(Exception):
+ ...
+
class Application(IPCServer):
""" Create Settings and Controller classes. Bind signal to Builder. Inherit from Builtins to bind global methods and classes. """
@@ -32,7 +38,7 @@ class Application(IPCServer):
message = f"FILE|{args.new_tab}"
self.send_ipc_message(message)
- raise Exception("IPC Server Exists: Will send path(s) to it and close...\nNote: If no fm exists, remove /tmp/solarfm-ipc.sock")
+ raise App_Launch_Exception("IPC Server Exists: Will send path(s) to it and close...\nNote: If no fm exists, remove /tmp/solarfm-ipc.sock")
settings = Settings()
@@ -40,7 +46,7 @@ class Application(IPCServer):
controller = Controller(args, unknownargs, settings)
if not controller:
- raise Exception("Controller exited and doesn't exist...")
+ raise Controller_Start_Exceptio("Controller exited and doesn't exist...")
# Gets the methods from the classes and sets to handler.
# Then, builder connects to any signals it needs.
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py
index 4f96b2d..0995a8f 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py
@@ -50,7 +50,8 @@ class ManifestProcessor:
if "ui_target" in keys:
if requests["ui_target"] in [
- "none", "other", "main_Window", "main_menu_bar", "path_menu_bar", "plugin_control_list",
+ "none", "other", "main_Window", "main_menu_bar",
+ "main_menu_bttn_box_bar", "path_menu_bar", "plugin_control_list",
"context_menu", "window_1", "window_2", "window_3", "window_4"
]:
if requests["ui_target"] == "other":
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py
index d793f72..d9fd59e 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py
@@ -65,7 +65,9 @@ class Plugins:
def load_plugin_module(self, path, folder, target):
os.chdir(path)
- sys.path.insert(0, path)
+ sys.path.insert(0, path) # NOTE: I think I'm not using this correctly...
+ # The folder and target aren't working to create parent package references, so using as stopgap.
+ # The above is probably polutling import logic and will cause unforseen import issues.
spec = importlib.util.spec_from_file_location(folder, target)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
diff --git a/user_config/usr/share/solarfm/Main_Window.glade b/user_config/usr/share/solarfm/Main_Window.glade
index c7d8965..2bf3bd8 100644
--- a/user_config/usr/share/solarfm/Main_Window.glade
+++ b/user_config/usr/share/solarfm/Main_Window.glade
@@ -1008,7 +1008,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
-
+
True
False
5
@@ -2364,283 +2364,362 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
-
+