class SkinPatcher: def __init__(self): self.core = RequiredFeature('core').request() self.plugin = RequiredFeature('plugin').request() self.base_path = '/usr/share/kodi/addons/skin.osmc/16x9/' self.shortcut_path = '/usr/share/kodi/addons/skin.osmc/shortcuts/' self.widget = 'Includes_Widgets.xml' self.var = 'Variables.xml' self.home = 'Home.xml' self.override = 'overrides.xml' self.widget_backup = 'Includes_Widgets.backup' self.var_backup = 'Variables.backup' self.home_backup = 'Home.backup' self.override_backup = 'overrides.backup' self.id = None self.supported = self.core.get_active_skin() == 'skin.osmc' \ and os.path.isfile(os.path.join(self.base_path, self.widget)) \ and os.path.isfile(os.path.join(self.base_path, self.var)) \ and os.path.isfile(os.path.join(self.base_path, self.home)) \ and os.path.isfile(os.path.join(self.shortcut_path, self.override)) self.rollback_supported = os.path.isfile(os.path.join(self.base_path, self.widget_backup)) \ and os.path.isfile(os.path.join(self.base_path, self.var_backup)) \ and os.path.isfile(os.path.join(self.base_path, self.home_backup)) \ and os.path.isfile(os.path.join(self.shortcut_path, self.override_backup)) def backup(self): shutil.copy(os.path.join(self.base_path, self.widget), os.path.join(self.base_path, self.widget_backup)) shutil.copy(os.path.join(self.base_path, self.var), os.path.join(self.base_path, self.var_backup)) shutil.copy(os.path.join(self.base_path, self.home), os.path.join(self.base_path, self.home_backup)) shutil.copy(os.path.join(self.shortcut_path, self.override), os.path.join(self.shortcut_path, self.override_backup)) def patch(self): if self.supported: self.backup() self.patch_widget() self.patch_home() self.patch_var() self.patch_override() self.plugin.set_setting('luna_widget_patched', 'true') else: print 'Not Supported' def patch_widget(self): xml_root = ElementTree.ElementTree(file=os.path.join(self.base_path, self.widget)).getroot() include = ElementTree.SubElement(xml_root, 'include', name="Luna") content = ElementTree.SubElement(include, 'content') for i in range(0, 20): item = ElementTree.SubElement(content, 'item') ElementTree.SubElement(item, 'icon').text = "$INFO[Window.Property(Luna.%s.icon)]" % i ElementTree.SubElement(item, 'thumb').text = "$INFO[Window.Property(Luna.%s.thumb)]" % i ElementTree.SubElement(item, 'label').text = "$INFO[Window.Property(Luna.%s.name)]" % i ElementTree.SubElement(item, 'property', name="fanart").text = "$INFO[Window.Property(Luna.%s.fanart)]" % i ElementTree.SubElement(item, 'onclick')\ .text = "RunPlugin(plugin://script.luna/games/launch-from-widget/%s)" % i ElementTree.SubElement(item, 'visible').text = "!IsEmpty(Window.Property(Luna.%s.name))" % i indent(include) tree = ElementTree.ElementTree(xml_root) tree.write(os.path.join(self.base_path, self.widget)) def patch_home(self): xml_root = ElementTree.ElementTree(file=os.path.join(self.base_path, self.home)).getroot() print self.plugin.get_setting('luna_force_fanart') controls = xml_root.find('controls') control_group = None for control in controls: print control.get('type') if control.get('type') == 'image': print "Found Image Control" if self.plugin.get_setting('luna_force_fanart', bool): control.find('visible').text = "True" print 'Visible Text is %s' % control.find('visible').text if control.get('type') == 'group': control_group = control break inner_control_group = None for control in control_group: if control.get('type') == 'group': inner_control_group = control_group break widget_control = None for control in inner_control_group: if control.get('type') == 'group': widget_control = control break inner_widget_control = None for control in widget_control: if control.get('id') is not None: inner_widget_control = control break current_max_id = "" myosmc_control = None for control in inner_widget_control: if control.get('id') is not None: current_max_id = control.get('id') myosmc_control = control current_max_id = int(current_max_id) + 1 self.id = current_max_id luna_control = copy.deepcopy(myosmc_control) luna_control.set('id', str(current_max_id)) luna_control.find('include').text = "Luna" luna_control.find('visible').text = "StringCompare(Container(9000).ListItem.Property(Widget),Luna)" luna_item_layout = luna_control.find('itemlayout') luna_item_layout.set('width', "270") luna_focus_layout = luna_control.find('focusedlayout') luna_focus_layout.set('width', "270") for control in luna_item_layout: if control.get('type') == 'image': control.find('width').text = "250" if control.find('texture') is not None and control.find('texture').text == 'common/black.png': control.find('texture').text = "" for control in luna_focus_layout: if control.get('type') == 'image': control.find('width').text = "250" if control.find('texture') is not None and control.find('texture').text == 'common/black.png': control.find('texture').text = "" inner_widget_control.append(luna_control) tree = ElementTree.ElementTree(xml_root) tree.write(os.path.join(self.base_path, self.home)) def patch_var(self): xml_root = ElementTree.ElementTree(file=os.path.join(self.base_path, self.var)).getroot() label_group = None heading_group = None fanart_group = None for var in xml_root.findall('variable'): if var.get('name') == 'WidgetLabel': label_group = var if var.get('name') == 'WidgetHeading': heading_group = var if var.get('name') == 'WidgetFanart': fanart_group = var if label_group is not None and heading_group is not None and fanart_group is not None: break ElementTree.SubElement(label_group, "value", condition="StringCompare(Container(9000).ListItem.Property(Widget),Luna)")\ .text = "$INFO[Container(%s).ListItem.Label]" % self.id ElementTree.SubElement(heading_group, "value", condition="StringCompare(Container(9000).ListItem.Property(Widget),Luna)")\ .text = "Games" ElementTree.SubElement(fanart_group, "value", condition="StringCompare(Container(9000).ListItem.Property(Widget),Luna)")\ .text = "$INFO[Container(%s).ListItem.Property(fanart)]" % self.id tree = ElementTree.ElementTree(xml_root) tree.write(os.path.join(self.base_path, self.var)) def patch_override(self): xml_root = ElementTree.ElementTree(file=os.path.join(self.shortcut_path, self.override)).getroot() ElementTree.SubElement(xml_root, "widget", label="Luna").text = "Luna" ElementTree.SubElement(xml_root, "widgetdefault", labelID="script.luna").text = "Luna" tree = ElementTree.ElementTree(xml_root) tree.write(os.path.join(self.shortcut_path, self.override)) def rollback(self): if self.rollback_supported: shutil.move(os.path.join(self.base_path, self.widget_backup), os.path.join(self.base_path, self.widget)) shutil.move(os.path.join(self.base_path, self.var_backup), os.path.join(self.base_path, self.var)) shutil.move(os.path.join(self.base_path, self.home_backup), os.path.join(self.base_path, self.home)) shutil.move(os.path.join(self.shortcut_path, self.override_backup), os.path.join(self.shortcut_path, self.override)) self.plugin.set_setting('luna_widget_patched', 'false')
class IgdbScraper(AbstractScraper): def __init__(self): AbstractScraper.__init__(self) self.plugin = RequiredFeature('plugin').request() self.api_url = 'https://www.igdb.com/api/v1/games/%s' self.api_img_url = 'https://res.cloudinary.com/igdb/image/upload/t_%s/%s.jpg' self.cover_cache = self._set_up_path(os.path.join(self.base_path, 'art/poster/')) self.api_cache = self._set_up_path(os.path.join(self.base_path, 'api_cache/')) def name(self): return 'IGDB' def get_game_information(self, game_name): if self.plugin.get_setting('api_key_file', str) == "": return ApiResponse() request_name = game_name.replace(" ", "+").replace(":", "") response = self._gather_information(request_name) response.name = game_name return response def return_paths(self): return [self.cover_cache, self.api_cache] def is_enabled(self): return self.plugin.get_setting('enable_igdb', bool) def _gather_information(self, game): game_cover_path = self._set_up_path(os.path.join(self.cover_cache, game)) game_cache_path = self._set_up_path(os.path.join(self.api_cache, game)) file_path = os.path.join(game_cache_path, game+'.json') try: cp = ConfigParser.ConfigParser() cp.read(self.plugin.get_setting('api_key_file', str)) igdb_api_key = cp.get('API', 'igdb') except: xbmcgui.Dialog().notification( self.core().string('name'), self.core().string('scraper_failed') % (game, self.name()) ) return ApiResponse() url_opener = urllib2.build_opener() url_opener.addheaders = [ ('Accept', 'application/json'), ('Authorization', 'Token token=%s' % igdb_api_key) ] if not os.path.isfile(file_path): query_string = 'search?q=%s' % game response = url_opener.open(self.api_url % query_string) if response.getcode() in [200, 304]: search_results = json.load(url_opener.open(self.api_url % query_string)) print search_results if len(search_results) > 0: best_match_id = search_results['games'][0]['id'] else: return None else: raise IOError("Server failed with status code %s" % response.getcode()) else: best_match_id = None try: json_data = json.load(open(self._get_json_data(url_opener, game_cache_path, game, best_match_id))) except IOError: return None json_data = json_data['game'] api_response = ApiResponse() api_response.year = date_parser.parse(json_data['release_date']).year api_response.plot = json_data['summary'].encode('utf-8') for genre in json_data['genres']: api_response.genre.append(genre['name'].encode('utf-8')) if json_data['cover'] is not None: img_id = json_data['cover']['id'] cover_path = self._dump_image(game_cover_path, self.api_img_url % ('cover_big', img_id)) if cover_path is not None: api_response.posters.append(cover_path) return api_response def _get_json_data(self, url_opener, path, game, best_match_id): file_path = os.path.join(path, game+'_igdb.json') if not os.path.exists(file_path) and best_match_id is not None: response = url_opener.open(self.api_url % best_match_id) if response.getcode() in [200, 304]: json_response = json.load(response) with open(file_path, 'w') as response_file: json.dump(json_response, response_file) else: raise IOError("Server failed with status code %s" % response.get()) return file_path
class OmdbScraper(AbstractScraper): def __init__(self): AbstractScraper.__init__(self) self.plugin = RequiredFeature('plugin').request() self.api_url = 'http://www.omdbapi.com/?t=%s&plot=short&r=json&type=game' self.cover_cache = self._set_up_path(os.path.join(self.base_path, 'art/poster/')) self.api_cache = self._set_up_path(os.path.join(self.base_path, 'api_cache/')) def name(self): return 'OMDB' def get_game_information(self, game_name): request_name = game_name.replace(" ", "+").replace(":", "") response = self._gather_information(request_name) response.name = game_name return response def return_paths(self): return [self.cover_cache, self.api_cache] def is_enabled(self): return self.plugin.get_setting('enable_omdb', bool) def _gather_information(self, game): game_cover_path = self._set_up_path(os.path.join(self.cover_cache, game)) game_cache_path = self._set_up_path(os.path.join(self.api_cache, game)) json_file = self._get_json_data(game_cache_path, game) try: json_data = json.load(open(json_file)) except: xbmcgui.Dialog().notification( self.core().string('name'), self.core().string('scraper_failed') % (game, self.name()) ) if json_file is not None and os.path.isfile(json_file): os.remove(json_file) return ApiResponse() if json_data['Response'] != 'False': json_data['posters'] = [] cover_path = self._dump_image(game_cover_path, json_data['Poster']) if cover_path is not None: json_data['posters'].append(cover_path) del json_data['Poster'] response = dict((k.lower(), v) for k, v in json_data.iteritems()) if 'genre' in response: response['genre'] = response.get('genre').split(',') response['genre'] = [str(v).strip() for v in response.get('genre')] return ApiResponse.from_dict(**response) else: return ApiResponse() def _get_json_data(self, path, game): file_path = os.path.join(path, game+'_omdb.json') if not os.path.exists(file_path): json_response = json.load(urllib2.urlopen(self.api_url % game)) with open(file_path, 'w') as response_file: json.dump(json_response, response_file) return file_path
core.logger.info('Launching game %s' % internal_game_id) game_controller.launch_game(internal_game_id) del core del game_controller if __name__ == '__main__': core = RequiredFeature('core').request() update_storage = plugin.get_storage('update', TTL=24*60) if not update_storage.get('checked'): updater = RequiredFeature('update-service').request() updater.check_for_update() del updater core.check_script_permissions() if plugin.get_setting('host', str): game_refresh_required = False try: from resources.lib.model.game import Game if plugin.get_storage('game_version')['version'] != Game.version: game_refresh_required = True except KeyError: game_refresh_required = True if game_refresh_required: game_controller = RequiredFeature('game-controller').request() game_controller.get_games() del game_controller plugin.run()
game_controller.launch_game(internal_game_id) del core del game_controller if __name__ == '__main__': core = RequiredFeature('core').request() update_storage = plugin.get_storage('update', TTL=240 * 60) if not update_storage.get('checked'): updater = RequiredFeature('update-service').request() updater.check_for_update() del updater core.check_script_permissions() if plugin.get_setting('host', str): game_refresh_required = False try: from resources.lib.model.game import Game if plugin.get_storage('game_version')['version'] != Game.version: game_refresh_required = True except KeyError: game_refresh_required = True if game_refresh_required: game_controller = RequiredFeature('game-controller').request() game_controller.get_games() del game_controller if os.path.isfile("/storage/moonlight/zerotier.conf"):
class TgdbScraper(AbstractScraper): def __init__(self): AbstractScraper.__init__(self) self.plugin = RequiredFeature('plugin').request() self.core = RequiredFeature('core').request() self.api_url = 'http://thegamesdb.net/api/GetGame.php?name=%s' self.cover_cache = self._set_up_path(os.path.join(self.base_path, 'art/poster/')) self.fanart_cache = self._set_up_path(os.path.join(self.base_path, 'art/fanart/')) self.api_cache = os.path.join(self.base_path, 'api_cache/') def name(self): return 'TGDB' def get_game_information(self, game_name): request_name = game_name.replace(" ", "+").replace(":", "") response = self._gather_information(request_name) response.name = game_name return response def return_paths(self): return [self.cover_cache, self.fanart_cache, self.api_cache] def is_enabled(self): return self.plugin.get_setting('enable_tgdb', bool) def _gather_information(self, game): game_cover_path = self._set_up_path(os.path.join(self.cover_cache, game)) game_fanart_path = self._set_up_path(os.path.join(self.fanart_cache, game)) xml_response_file = self._get_xml_data(game) try: xml_root = ElementTree(file=xml_response_file).getroot() except: xbmcgui.Dialog().notification( self.core.string('name'), self.core.string('scraper_failed') % (game, self.name()) ) if xml_response_file is not None and os.path.isfile(xml_response_file): os.remove(xml_response_file) return ApiResponse() dict_response = self._parse_xml_to_dict(xml_root) if dict_response: posters = dict_response['posters'] dict_response['posters'] = [] for poster in posters: dict_response['posters'].append(self._dump_image(game_cover_path, poster)) local_arts = {} for art in dict_response.get('fanarts'): art.set_thumb(self._dump_image(game_fanart_path, art.get_thumb())) local_arts[os.path.basename(art.get_thumb())] = art dict_response['fanarts'] = local_arts return ApiResponse.from_dict(**dict_response) def _get_xml_data(self, game): file_path = os.path.join(self.api_cache, game, game+'_tgdb.xml') if not os.path.isfile(file_path): curl = subprocess.Popen(['curl', '-XGET', self.api_url % game], stdout=subprocess.PIPE) with open(file_path, 'w') as response_file: response_file.write(curl.stdout.read()) return file_path @staticmethod def _parse_xml_to_dict(root): """ :rtype: dict :type root: Element """ data = {'year': 'N/A', 'plot': 'N/A', 'posters': [], 'genre': [], 'fanarts': []} similar_id = [] base_img_url = root.find('baseImgUrl').text for game in root.findall('Game'): if game.find('Platform').text == 'PC': if game.find('ReleaseDate') is not None: data['year'] = os.path.basename(game.find('ReleaseDate').text) if game.find('Overview') is not None: data['plot'] = game.find('Overview').text for img in game.find('Images'): if img.get('side') == 'front': data['posters'].append(os.path.join(base_img_url, img.text)) if img.tag == 'fanart': image = Fanart() image.set_original(os.path.join(base_img_url, img.find('original').text)) image.set_thumb(os.path.join(base_img_url, img.find('thumb').text)) data['fanarts'].append(image) del image if game.find('Genres') is not None: for genre in game.find('Genres'): data['genre'].append(str(genre.text)) if game.find('Similar') is not None: for similar in game.find('Similar'): if similar.tag == 'Game': similar_id.append(similar.find('id').text) break for game in root.findall('Game'): if game.find('id').text in similar_id: for img in game.find('Images'): if img.tag == 'fanart': image = Fanart() image.set_original(os.path.join(base_img_url, img.find('original').text)) image.set_thumb(os.path.join(base_img_url, img.find('thumb').text)) data['fanarts'].append(image) del image return data
class IgdbScraper(AbstractScraper): def __init__(self): AbstractScraper.__init__(self) self.plugin = RequiredFeature('plugin').request() self.api_url = 'https://www.igdb.com/api/v1/games/%s' self.api_img_url = 'https://res.cloudinary.com/igdb/image/upload/t_%s/%s.jpg' self.cover_cache = self._set_up_path( os.path.join(self.base_path, 'art/poster/')) self.api_cache = self._set_up_path( os.path.join(self.base_path, 'api_cache/')) def name(self): return 'IGDB' def get_game_information(self, game_name): if self.plugin.get_setting('api_key_file', str) == "": return ApiResponse() request_name = game_name.replace(" ", "+").replace(":", "") response = self._gather_information(request_name) response.name = game_name return response def return_paths(self): return [self.cover_cache, self.api_cache] def is_enabled(self): return self.plugin.get_setting('enable_igdb', bool) def _gather_information(self, game): game_cover_path = self._set_up_path( os.path.join(self.cover_cache, game)) game_cache_path = self._set_up_path(os.path.join(self.api_cache, game)) file_path = os.path.join(game_cache_path, game + '.json') try: cp = ConfigParser.ConfigParser() cp.read(self.plugin.get_setting('api_key_file', str)) igdb_api_key = cp.get('API', 'igdb') except: xbmcgui.Dialog().notification( self.core().string('name'), self.core().string('scraper_failed') % (game, self.name())) return ApiResponse() url_opener = urllib2.build_opener() url_opener.addheaders = [('Accept', 'application/json'), ('Authorization', 'Token token=%s' % igdb_api_key)] if not os.path.isfile(file_path): query_string = 'search?q=%s' % game response = url_opener.open(self.api_url % query_string) if response.getcode() in [200, 304]: search_results = json.load( url_opener.open(self.api_url % query_string)) print search_results if len(search_results) > 0: best_match_id = search_results['games'][0]['id'] else: return None else: raise IOError("Server failed with status code %s" % response.getcode()) else: best_match_id = None try: json_data = json.load( open( self._get_json_data(url_opener, game_cache_path, game, best_match_id))) except IOError: return None json_data = json_data['game'] api_response = ApiResponse() api_response.year = date_parser.parse(json_data['release_date']).year api_response.plot = json_data['summary'].encode('utf-8') for genre in json_data['genres']: api_response.genre.append(genre['name'].encode('utf-8')) if json_data['cover'] is not None: img_id = json_data['cover']['id'] cover_path = self._dump_image( game_cover_path, self.api_img_url % ('cover_big', img_id)) if cover_path is not None: api_response.posters.append(cover_path) return api_response def _get_json_data(self, url_opener, path, game, best_match_id): file_path = os.path.join(path, game + '_igdb.json') if not os.path.exists(file_path) and best_match_id is not None: response = url_opener.open(self.api_url % best_match_id) if response.getcode() in [200, 304]: json_response = json.load(response) with open(file_path, 'w') as response_file: json.dump(json_response, response_file) else: raise IOError("Server failed with status code %s" % response.get()) return file_path
class OmdbScraper(AbstractScraper): def __init__(self): AbstractScraper.__init__(self) self.plugin = RequiredFeature('plugin').request() self.api_url = 'http://www.omdbapi.com/?t=%s&plot=short&r=json&type=game' self.cover_cache = self._set_up_path( os.path.join(self.base_path, 'art/poster/')) self.api_cache = self._set_up_path( os.path.join(self.base_path, 'api_cache/')) def name(self): return 'OMDB' def get_game_information(self, game_name): request_name = game_name.replace(" ", "+").replace(":", "") response = self._gather_information(request_name) response.name = game_name return response def return_paths(self): return [self.cover_cache, self.api_cache] def is_enabled(self): return self.plugin.get_setting('enable_omdb', bool) def _gather_information(self, game): game_cover_path = self._set_up_path( os.path.join(self.cover_cache, game)) game_cache_path = self._set_up_path(os.path.join(self.api_cache, game)) json_file = self._get_json_data(game_cache_path, game) try: json_data = json.load(open(json_file)) except: xbmcgui.Dialog().notification( self.core().string('name'), self.core().string('scraper_failed') % (game, self.name())) if json_file is not None and os.path.isfile(json_file): os.remove(json_file) return ApiResponse() if json_data['Response'] != 'False': json_data['posters'] = [] cover_path = self._dump_image(game_cover_path, json_data['Poster']) if cover_path is not None: json_data['posters'].append(cover_path) del json_data['Poster'] response = dict((k.lower(), v) for k, v in json_data.iteritems()) if 'genre' in response: response['genre'] = response.get('genre').split(',') response['genre'] = [ str(v).strip() for v in response.get('genre') ] return ApiResponse.from_dict(**response) else: return ApiResponse() def _get_json_data(self, path, game): file_path = os.path.join(path, game + '_omdb.json') if not os.path.exists(file_path): json_response = json.load(urllib2.urlopen(self.api_url % game)) with open(file_path, 'w') as response_file: json.dump(json_response, response_file) return file_path