diff --git a/.build/main.yml b/.build/main.yml
index b2b407f3..542f5f03 100644
--- a/.build/main.yml
+++ b/.build/main.yml
@@ -78,7 +78,7 @@ stages:
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
- pip install setup
+ pip install packaging==21.3
pip install -r requirements.txt
workingDirectory: $(workingDirectory)
displayName: 'Install application dependencies'
@@ -126,12 +126,6 @@ stages:
flattenFolders: false
OverWrite: true
- - bash: |
- source .venv/bin/activate
- kodi-addon-checker --branch matrix $(build.artifactstagingdirectory)/$(addonName)/
- workingDirectory: '$(workingDirectory)'
- displayName: 'Run Kodi addon checker'
-
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: '$(build.artifactstagingdirectory)/$(addonName)'
@@ -140,6 +134,12 @@ stages:
archiveFile: '$(build.artifactstagingdirectory)/package/$(addonName)-$(addonVersion).zip'
replaceExistingArchive: true
+ - bash: |
+ source .venv/bin/activate
+ kodi-addon-checker --branch matrix $(build.artifactstagingdirectory)/$(addonName)/
+ workingDirectory: '$(workingDirectory)'
+ displayName: 'Run Kodi addon checker'
+
- task: CopyFiles@2
displayName: 'Copy addon files for repository'
inputs:
diff --git a/addon.xml b/addon.xml
index ccb99205..01f49c5c 100644
--- a/addon.xml
+++ b/addon.xml
@@ -3,7 +3,7 @@
-
+
executable game
diff --git a/changelog.md b/changelog.md
index 1476d520..509798f1 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,4 +1,5 @@
## Current
+- Custom skin view for View ROM
- More details about addon plugins
## Previous
diff --git a/requirements.txt b/requirements.txt
index 5cfa50fd..87b39290 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,6 +3,6 @@ kodi-addon-checker==0.0.26
Kodistubs==19.0.3
routing==0.2.3
pytest==6.2.5
-script.module.akl==1.0.9
+script.module.akl==1.0.11
requests==2.22.0
flake8==5.0.4
diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po
index 858e2f60..4fa20038 100644
--- a/resources/language/resource.language.en_gb/strings.po
+++ b/resources/language/resource.language.en_gb/strings.po
@@ -161,6 +161,10 @@ msgctxt "#40415"
msgid "Hide Global Reports"
msgstr "settings.xml"
+msgctxt "#40416"
+msgid "Launch ROM directly by default on item action"
+msgstr "settings.xml"
+
############################
# Setting options - Paths
############################
@@ -208,6 +212,86 @@ msgctxt "#40702"
msgid "Google scraper plugin"
msgstr "settings.xml"
+############################
+# Metadata labels
+############################
+
+msgctxt "#40801"
+msgid "Genre"
+msgstr ""
+
+msgctxt "#40802"
+msgid "Developer"
+msgstr ""
+
+msgctxt "#40803"
+msgid "Year released"
+msgstr ""
+
+msgctxt "#40804"
+msgid "ESRB Rating"
+msgstr ""
+
+msgctxt "#40805"
+msgid "PEGI Rating"
+msgstr ""
+
+msgctxt "#40806"
+msgid "Rating"
+msgstr ""
+
+msgctxt "#40807"
+msgid "Platform"
+msgstr ""
+
+msgctxt "#40808"
+msgid "Number of players"
+msgstr ""
+
+msgctxt "#40809"
+msgid "Number of players online"
+msgstr ""
+
+msgctxt "#40810"
+msgid "Tags"
+msgstr ""
+
+msgctxt "#40811"
+msgid "Plot"
+msgstr ""
+
+msgctxt "#40812"
+msgid "Title"
+msgstr ""
+
+msgctxt "#40813"
+msgid "Identifier"
+msgstr ""
+
+############################
+# Actions
+############################
+
+msgctxt "#40851"
+msgid "Launch"
+msgstr ""
+
+msgctxt "#40852"
+msgid "Play trailer"
+msgstr ""
+
+msgctxt "#40853"
+msgid "Edit Metadata"
+msgstr ""
+
+msgctxt "#40854"
+msgid "Edit Assets/Artwork"
+msgstr ""
+
+msgctxt "#40855"
+msgid "Scrape ROM"
+msgstr ""
+
############################
# Scraping settings
############################
diff --git a/resources/lib/commands/api_commands.py b/resources/lib/commands/api_commands.py
index 360d22e3..2b6900c1 100644
--- a/resources/lib/commands/api_commands.py
+++ b/resources/lib/commands/api_commands.py
@@ -137,7 +137,7 @@ def cmd_store_scanned_roms(args) -> bool:
api_rom_obj = ROMObj(rom_data)
rom_obj = ROM()
- rom_obj.update_with(api_rom_obj, overwrite_existing=True, update_scanned_data=True)
+ rom_obj.update_with(api_rom_obj, overwrite_existing_metadata=True, update_scanned_data=True)
rom_obj.set_platform(romcollection.get_platform())
rom_obj.scanned_with(scanner_id)
rom_obj.apply_romcollection_asset_paths(romcollection)
@@ -208,7 +208,9 @@ def cmd_store_scraped_roms(args) -> bool:
logger.debug('========================== Applied scraper settings ==========================')
logger.debug('Metadata IDs: {}'.format(', '.join(applied_settings.metadata_IDs_to_scrape)))
logger.debug('Asset IDs: {}'.format(', '.join(applied_settings.asset_IDs_to_scrape)))
- logger.debug('Overwrite existing: {}'.format('Yes' if applied_settings.overwrite_existing else 'No'))
+ logger.debug('Overwrite existing:')
+ logger.debug(' - Metadata {}'.format('Yes' if applied_settings.overwrite_existing_meta else 'No'))
+ logger.debug(' - Assets {}'.format('Yes' if applied_settings.overwrite_existing_assets else 'No'))
for rom_data in scraped_roms:
api_rom_obj = ROMObj(rom_data)
@@ -226,7 +228,8 @@ def cmd_store_scraped_roms(args) -> bool:
api_rom_obj,
metadata_to_update,
assets_to_update,
- overwrite_existing=applied_settings.overwrite_existing)
+ overwrite_existing_metadata=applied_settings.overwrite_existing_meta,
+ overwrite_existing_assets=applied_settings.overwrite_existing_assets)
#rom_obj.scraped_with(scraper_id)
rom_repository.update_rom(rom_obj)
@@ -277,7 +280,8 @@ def cmd_store_scraped_single_rom(args) -> bool:
rom.update_with(scraped_rom,
metadata_to_update,
assets_to_update,
- overwrite_existing=applied_settings.overwrite_existing)
+ overwrite_existing_metadata=applied_settings.overwrite_existing_meta,
+ overwrite_existing_assets=applied_settings.overwrite_existing_assets)
#rom_obj.scraped_with(scraper_id)
rom_repository.update_rom(rom)
diff --git a/resources/lib/commands/category_commands.py b/resources/lib/commands/category_commands.py
index 597d9c22..5cb3403f 100644
--- a/resources/lib/commands/category_commands.py
+++ b/resources/lib/commands/category_commands.py
@@ -224,21 +224,21 @@ def cmd_category_delete(args):
category_name = category.get_name()
if category.has_items():
- question = 'Category "{}" has {} sub-categories and {} romcollections. '.format(category_name, category.num_categories(), category.num_romcollections()) + \
- 'Deleting it will also delete related items. ' + \
- 'Are you sure you want to delete "{}"?'.format(category_name)
+ question = (f'Category "{category_name}" has {category.num_categories()} sub-categories and '
+ f'{category.num_romcollections()} romcollections. Deleting it will also delete related items. '
+ f'Are you sure you want to delete "{category_name}"?')
else:
- question = 'Category "{}" has no categories or romcollections. '.format(category_name) + \
- 'Are you sure you want to delete "{}"?'.format(category_name)
+ question = (f'Category "{category_name}" has no categories or romcollections. '
+ f'Are you sure you want to delete "{category_name}"?')
ret = kodi.dialog_yesno(question)
if not ret: return
- logger.info('Deleting category "{}" ID {}'.format(category_name, category.get_id()))
+ logger.info(f'Deleting category "{category_name}" ID {category.get_id()}')
repository.delete_category(category_id)
uow.commit()
- kodi.notify('Deleted category {0}'.format(category_name))
+ kodi.notify(f'Deleted category {category_name}')
AppMediator.async_cmd('RENDER_CATEGORY_VIEW', {'category_id': category.get_parent_id()})
AppMediator.async_cmd('CLEANUP_VIEWS')
AppMediator.sync_cmd('EDIT_CATEGORY', args)
diff --git a/resources/lib/commands/rom_commands.py b/resources/lib/commands/rom_commands.py
index f1868862..261c6a23 100644
--- a/resources/lib/commands/rom_commands.py
+++ b/resources/lib/commands/rom_commands.py
@@ -52,14 +52,14 @@ def cmd_edit_rom(args):
rom = repository.find_rom(rom_id)
options = collections.OrderedDict()
- options['ROM_EDIT_METADATA'] = 'Edit Metadata ...'
- options['ROM_EDIT_ASSETS'] = 'Edit Assets/Artwork ...'
+ options['ROM_EDIT_METADATA'] = f'{kodi.translate(40853)} ...'
+ options['ROM_EDIT_ASSETS'] = f'{kodi.translate(40854)} ...'
options['EDIT_ROM_STATUS'] = f'ROM status: {rom.get_finished_str()}'
if rom.has_launchers():
options['EDIT_ROM_LAUNCHERS'] = 'Manage associated launchers'
else: options['ADD_ROM_LAUNCHER'] = 'Add new launcher to ROM'
options['DELETE_ROM'] = 'Delete ROM'
- options['SCRAPE_ROM'] = 'Scrape ROM'
+ options['SCRAPE_ROM'] = kodi.translate(40855)
s = f'Edit ROM "{rom.get_name()}"'
selected_option = kodi.OrdDictionaryDialog().select(s, options)
diff --git a/resources/lib/commands/rom_scraper_commands.py b/resources/lib/commands/rom_scraper_commands.py
index c14e571c..c52277d6 100644
--- a/resources/lib/commands/rom_scraper_commands.py
+++ b/resources/lib/commands/rom_scraper_commands.py
@@ -41,7 +41,7 @@ def cmd_scrape_romcollection(args):
uow = UnitOfWork(globals.g_PATHS.DATABASE_FILE_PATH)
with uow:
collection_repository = ROMCollectionRepository(uow)
- collection = collection_repository.find_romcollection(romcollection_id)
+ collection = collection_repository.find_romcollection(romcollection_id)
scraper_settings:ScraperSettings = ScraperSettings.from_addon_settings()
@@ -53,14 +53,14 @@ def cmd_scrape_romcollection(args):
AppMediator.sync_cmd('ROMCOLLECTION_MANAGE_ROMS', args)
return
- scraper_settings.asset_IDs_to_scrape = selected_addon.get_supported_assets()
+ scraper_settings.asset_IDs_to_scrape = selected_addon.get_supported_assets()
scraper_settings.metadata_IDs_to_scrape = selected_addon.get_supported_metadata()
logger.debug(f'cmd_scrape_romcollection() Selected scraper#{selected_addon.get_name()}')
- args['scraper_settings'] = scraper_settings
- args['scraper_id'] = selected_addon.addon.get_id()
- args['scraper_supported_metadata'] = selected_addon.get_supported_metadata()
- args['scraper_supported_assets'] = selected_addon.get_supported_assets()
+ args['scraper_settings'] = scraper_settings
+ args['scraper_id'] = selected_addon.addon.get_id()
+ args['scraper_supported_metadata'] = selected_addon.get_supported_metadata()
+ args['scraper_supported_assets'] = selected_addon.get_supported_assets()
AppMediator.sync_cmd('SCRAPE_ROMS_WITH_SETTINGS', args)
@@ -76,22 +76,22 @@ def cmd_scrape_rom(args):
scraper_settings:ScraperSettings = ScraperSettings.from_addon_settings()
- dialog_title = f'Scrape ROM "{rom.get_name()}"'
- selected_addon = _select_scraper(uow, dialog_title, scraper_settings)
+ dialog_title = f'Scrape ROM "{rom.get_name()}"'
+ selected_addon = _select_scraper(uow, dialog_title, scraper_settings)
if selected_addon is None:
# >> Exits context menu
- logger.debug('SCRAPE_ROM: cmd_scrape_rom() Selected None. Closing context menu')
+ logger.debug('SCRAPE_ROM: Selected None. Closing context menu')
AppMediator.sync_cmd('EDIT_ROM', args)
return
- scraper_settings.asset_IDs_to_scrape = selected_addon.get_supported_assets()
+ scraper_settings.asset_IDs_to_scrape = selected_addon.get_supported_assets()
scraper_settings.metadata_IDs_to_scrape = selected_addon.get_supported_metadata()
- logger.debug('cmd_scrape_rom() Selected scraper#{}'.format(selected_addon.get_name()))
- args['scraper_settings'] = scraper_settings
- args['scraper_id'] = selected_addon.addon.get_id()
- args['scraper_supported_metadata'] = selected_addon.get_supported_metadata()
- args['scraper_supported_assets'] = selected_addon.get_supported_assets()
+ logger.debug(f'Selected scraper#{selected_addon.get_name()}')
+ args['scraper_settings'] = scraper_settings
+ args['scraper_id'] = selected_addon.addon.get_id()
+ args['scraper_supported_metadata'] = selected_addon.get_supported_metadata()
+ args['scraper_supported_assets'] = selected_addon.get_supported_assets()
AppMediator.sync_cmd('SCRAPE_ROM_WITH_SETTINGS', args)
@@ -114,15 +114,16 @@ def cmd_scrape_roms_in_romcollection(args):
metadata_to_scrape = [constants.METADATA_DESCRIPTIONS[meta_id] for meta_id in scraper_settings.metadata_IDs_to_scrape]
options = collections.OrderedDict()
- options['SCRAPER_METADATA_POLICY'] = 'Metadata scan policy: "{}"'.format(kodi.translate(scraper_settings.scrape_metadata_policy))
- options['SCRAPER_ASSET_POLICY'] = 'Asset scan policy: "{}"'.format(kodi.translate(scraper_settings.scrape_assets_policy))
- options['SCRAPER_GAME_SELECTION_MODE'] = 'Game selection mode: "{}"'.format(kodi.translate(scraper_settings.game_selection_mode))
+ options['SCRAPER_METADATA_POLICY'] = 'Metadata scan policy: "{}"'.format(kodi.translate(scraper_settings.scrape_metadata_policy))
+ options['SCRAPER_ASSET_POLICY'] = 'Asset scan policy: "{}"'.format(kodi.translate(scraper_settings.scrape_assets_policy))
+ options['SCRAPER_GAME_SELECTION_MODE'] = 'Game selection mode: "{}"'.format(kodi.translate(scraper_settings.game_selection_mode))
options['SCRAPER_ASSET_SELECTION_MODE'] = 'Asset selection mode: "{}"'.format(kodi.translate(scraper_settings.asset_selection_mode))
- options['SCRAPER_META_TO_SCRAPE'] = 'Metadata to scrape: "{}"'.format(', '.join(metadata_to_scrape))
- options['SCRAPER_ASSETS_TO_SCRAPE'] = 'Assets to scrape: "{}"'.format(', '.join([a.plural for a in assets_to_scrape]))
- options['SCRAPER_OVERWRITE_MODE'] = 'Overwrite existing files: "{}"'.format('Yes' if scraper_settings.overwrite_existing else 'No')
- options['SCRAPER_IGNORE_TITLES_MODE'] = 'Ignore scraped titles: "{}"'.format('Yes' if scraper_settings.ignore_scrap_title else 'No')
- options['SCRAPE'] = 'Scrape'
+ options['SCRAPER_META_TO_SCRAPE'] = 'Metadata to scrape: "{}"'.format(', '.join(metadata_to_scrape))
+ options['SCRAPER_ASSETS_TO_SCRAPE'] = 'Assets to scrape: "{}"'.format(', '.join([a.plural for a in assets_to_scrape]))
+ options['SCRAPER_OVERWRITE_META_MODE'] = 'Overwrite existing metadata: "{}"'.format('Yes' if scraper_settings.overwrite_existing_meta else 'No')
+ options['SCRAPER_OVERWRITE_ASSETS_MODE'] = 'Overwrite existing assets/files: "{}"'.format('Yes' if scraper_settings.overwrite_existing_assets else 'No')
+ options['SCRAPER_IGNORE_TITLES_MODE'] = 'Ignore scraped titles: "{}"'.format('Yes' if scraper_settings.ignore_scrap_title else 'No')
+ options['SCRAPE'] = 'Scrape'
dialog_title = f'Scrape collection "{collection.get_name()}" ROMs with "{selected_addon.get_name()}"'
selected_option = kodi.OrdDictionaryDialog().select(dialog_title, options, preselect='SCRAPE')
@@ -155,26 +156,27 @@ def cmd_scrape_rom_with_settings(args):
uow = UnitOfWork(globals.g_PATHS.DATABASE_FILE_PATH)
with uow:
addon_repository = AelAddonRepository(uow)
- roms_repository = ROMsRepository(uow)
+ roms_repository = ROMsRepository(uow)
- rom = roms_repository.find_rom(rom_id)
- addon = addon_repository.find(scraper_id)
- selected_addon = ScraperAddon(addon, scraper_settings)
+ rom = roms_repository.find_rom(rom_id)
+ addon = addon_repository.find(scraper_id)
+ selected_addon = ScraperAddon(addon, scraper_settings)
assets_to_scrape = g_assetFactory.get_asset_list_by_IDs(scraper_settings.asset_IDs_to_scrape)
metadata_to_scrape = [constants.METADATA_DESCRIPTIONS[meta_id] for meta_id in scraper_settings.metadata_IDs_to_scrape]
options = collections.OrderedDict()
- options['SCRAPER_METADATA_POLICY'] = 'Metadata scan policy: "{}"'.format(kodi.translate(scraper_settings.scrape_metadata_policy))
- options['SCRAPER_ASSET_POLICY'] = 'Asset scan policy: "{}"'.format(kodi.translate(scraper_settings.scrape_assets_policy))
- options['SCRAPER_SEARCH_TERM_MODE'] = f'Search term mode: "{kodi.translate(scraper_settings.search_term_mode)}"'
- options['SCRAPER_GAME_SELECTION_MODE'] = 'Game selection mode: "{}"'.format(kodi.translate(scraper_settings.game_selection_mode))
+ options['SCRAPER_METADATA_POLICY'] = 'Metadata scan policy: "{}"'.format(kodi.translate(scraper_settings.scrape_metadata_policy))
+ options['SCRAPER_ASSET_POLICY'] = 'Asset scan policy: "{}"'.format(kodi.translate(scraper_settings.scrape_assets_policy))
+ options['SCRAPER_SEARCH_TERM_MODE'] = f'Search term mode: "{kodi.translate(scraper_settings.search_term_mode)}"'
+ options['SCRAPER_GAME_SELECTION_MODE'] = 'Game selection mode: "{}"'.format(kodi.translate(scraper_settings.game_selection_mode))
options['SCRAPER_ASSET_SELECTION_MODE'] = 'Asset selection mode: "{}"'.format(kodi.translate(scraper_settings.asset_selection_mode))
- options['SCRAPER_META_TO_SCRAPE'] = 'Metadata to scrape: "{}"'.format(', '.join(metadata_to_scrape))
- options['SCRAPER_ASSETS_TO_SCRAPE'] = 'Assets to scrape: "{}"'.format(', '.join([a.plural for a in assets_to_scrape]))
- options['SCRAPER_OVERWRITE_MODE'] = 'Overwrite existing files: "{}"'.format('Yes' if scraper_settings.overwrite_existing else 'No')
- options['SCRAPER_IGNORE_TITLES_MODE'] = 'Ignore scraped titles: "{}"'.format('Yes' if scraper_settings.ignore_scrap_title else 'No')
- options['SCRAPE'] = 'Scrape'
+ options['SCRAPER_META_TO_SCRAPE'] = 'Metadata to scrape: "{}"'.format(', '.join(metadata_to_scrape))
+ options['SCRAPER_ASSETS_TO_SCRAPE'] = 'Assets to scrape: "{}"'.format(', '.join([a.plural for a in assets_to_scrape]))
+ options['SCRAPER_OVERWRITE_META_MODE'] = 'Overwrite existing metadata: "{}"'.format('Yes' if scraper_settings.overwrite_existing_meta else 'No')
+ options['SCRAPER_OVERWRITE_ASSETS_MODE'] = 'Overwrite existing assets/files: "{}"'.format('Yes' if scraper_settings.overwrite_existing_assets else 'No')
+ options['SCRAPER_IGNORE_TITLES_MODE'] = 'Ignore scraped titles: "{}"'.format('Yes' if scraper_settings.ignore_scrap_title else 'No')
+ options['SCRAPE'] = 'Scrape'
s = f'Scrape ROM "{rom.get_name()}" with "{selected_addon.get_name()}'
selected_option = kodi.OrdDictionaryDialog().select(s, options, preselect='SCRAPE')
@@ -203,22 +205,23 @@ def cmd_scrape_rom_metadata(args):
uow = UnitOfWork(globals.g_PATHS.DATABASE_FILE_PATH)
with uow:
roms_repository = ROMsRepository(uow)
- rom = roms_repository.find_rom(rom_id)
+ rom = roms_repository.find_rom(rom_id)
scraper_settings = ScraperSettings().from_addon_settings()
- scraper_settings.scrape_metadata_policy = constants.SCRAPE_POLICY_SCRAPE_ONLY
- scraper_settings.scrape_assets_policy = constants.SCRAPE_ACTION_NONE
- scraper_settings.search_term_mode = constants.SCRAPE_MANUAL
- scraper_settings.game_selection_mode = constants.SCRAPE_MANUAL
+ scraper_settings.scrape_metadata_policy = constants.SCRAPE_POLICY_SCRAPE_ONLY
+ scraper_settings.scrape_assets_policy = constants.SCRAPE_ACTION_NONE
+ scraper_settings.search_term_mode = constants.SCRAPE_MANUAL
+ scraper_settings.game_selection_mode = constants.SCRAPE_MANUAL
+ scraper_settings.overwrite_existing_meta = True
selected_addon = _select_scraper(uow, 'Scrape ROM metadata', scraper_settings)
if selected_addon is None:
# >> Exits context menu
- logger.debug('SCRAPE_ROM_METADATA: cmd_scrape_rom_metadata() Selected None. Closing context menu')
+ logger.debug('SCRAPE_ROM_METADATA: Selected None. Closing context menu')
AppMediator.sync_cmd('ROM_EDIT_METADATA', args)
return
- logger.debug(f'SCRAPE_ROM_METADATA: cmd_scrape_rom_metadata() Selected scraper#{selected_addon.get_name()}')
+ logger.debug(f'SCRAPE_ROM_METADATA: Selected scraper#{selected_addon.get_name()}')
scraper_settings.metadata_IDs_to_scrape = selected_addon.get_supported_metadata()
options = collections.OrderedDict()
@@ -251,13 +254,13 @@ def cmd_scrape_rom_asset(args):
rom = roms_repository.find_rom(rom_id)
scraper_settings = ScraperSettings()
- scraper_settings.scrape_assets_policy = constants.SCRAPE_POLICY_SCRAPE_ONLY
- scraper_settings.scrape_metadata_policy = constants.SCRAPE_ACTION_NONE
- scraper_settings.search_term_mode = constants.SCRAPE_MANUAL
- scraper_settings.game_selection_mode = constants.SCRAPE_MANUAL
- scraper_settings.asset_selection_mode = constants.SCRAPE_MANUAL
- scraper_settings.asset_IDs_to_scrape = [asset_id]
- scraper_settings.overwrite_existing = True
+ scraper_settings.scrape_assets_policy = constants.SCRAPE_POLICY_SCRAPE_ONLY
+ scraper_settings.scrape_metadata_policy = constants.SCRAPE_ACTION_NONE
+ scraper_settings.search_term_mode = constants.SCRAPE_MANUAL
+ scraper_settings.game_selection_mode = constants.SCRAPE_MANUAL
+ scraper_settings.asset_selection_mode = constants.SCRAPE_MANUAL
+ scraper_settings.asset_IDs_to_scrape = [asset_id]
+ scraper_settings.overwrite_existing_assets = True
selected_addon = _select_scraper(uow, 'Scrape ROM {} asset'.format(asset_to_scrape.name), scraper_settings)
if selected_addon is None:
@@ -267,7 +270,7 @@ def cmd_scrape_rom_asset(args):
return
# >> Execute scraper
- logger.debug('SCRAPE_ROM_ASSET: cmd_scrape_rom_asset() Selected scraper#{}'.format(selected_addon.get_name()))
+ logger.debug('SCRAPE_ROM_ASSET: Selected scraper#{}'.format(selected_addon.get_name()))
kodi.notify('Preparing scraper')
kodi.run_script(
@@ -284,19 +287,19 @@ def cmd_scrape_rom_assets(args):
rom = roms_repository.find_rom(rom_id)
scraper_settings = ScraperSettings.from_addon_settings()
- scraper_settings.scrape_assets_policy = constants.SCRAPE_POLICY_SCRAPE_ONLY
- scraper_settings.scrape_metadata_policy = constants.SCRAPE_ACTION_NONE
- scraper_settings.search_term_mode = constants.SCRAPE_MANUAL
- scraper_settings.asset_selection_mode = constants.SCRAPE_MANUAL
+ scraper_settings.scrape_assets_policy = constants.SCRAPE_POLICY_SCRAPE_ONLY
+ scraper_settings.scrape_metadata_policy = constants.SCRAPE_ACTION_NONE
+ scraper_settings.search_term_mode = constants.SCRAPE_MANUAL
+ scraper_settings.asset_selection_mode = constants.SCRAPE_MANUAL
selected_addon = _select_scraper(uow, 'Scrape ROM assets', scraper_settings)
if selected_addon is None:
# >> Exits context menu
- logger.debug('cmd_scrape_rom_assets() Selected None. Closing context menu')
+ logger.debug('SCRAPE_ROM_ASSETS: Selected None. Closing context menu')
AppMediator.sync_cmd('ROM_EDIT_ASSETS', args)
return
- logger.debug('cmd_scrape_rom_assets() Selected scraper#{}'.format(selected_addon.get_name()))
+ logger.debug('SCRAPE_ROM_ASSETS: Selected scraper#{}'.format(selected_addon.get_name()))
scraper_settings.asset_IDs_to_scrape = selected_addon.get_supported_assets()
asset_options = g_assetFactory.get_all()
@@ -344,7 +347,8 @@ def cmd_configure_scraper_metadata_policy(args):
args['scraper_settings'] = scraper_settings
AppMediator.sync_cmd(args['ret_cmd'], args)
return
-
+
+
@AppMediator.register('SCRAPER_ASSET_POLICY')
def cmd_configure_scraper_asset_policy(args):
scraper_settings:ScraperSettings = args['scraper_settings'] if 'scraper_settings' in args else ScraperSettings.from_addon_settings()
@@ -365,7 +369,8 @@ def cmd_configure_scraper_asset_policy(args):
scraper_settings.scrape_assets_policy = selected_option
args['scraper_settings'] = scraper_settings
AppMediator.sync_cmd(args['ret_cmd'], args)
-
+
+
@AppMediator.register('SCRAPER_SEARCH_TERM_MODE')
def cmd_configure_scraper_search_term_mode(args):
scraper_settings:ScraperSettings = args['scraper_settings'] if 'scraper_settings' in args else ScraperSettings.from_addon_settings()
@@ -385,6 +390,7 @@ def cmd_configure_scraper_search_term_mode(args):
AppMediator.sync_cmd(args['ret_cmd'], args)
return
+
@AppMediator.register('SCRAPER_GAME_SELECTION_MODE')
def cmd_configure_scraper_game_selection_mode(args):
scraper_settings:ScraperSettings = args['scraper_settings'] if 'scraper_settings' in args else ScraperSettings.from_addon_settings()
@@ -404,6 +410,7 @@ def cmd_configure_scraper_game_selection_mode(args):
AppMediator.sync_cmd(args['ret_cmd'], args)
return
+
@AppMediator.register('SCRAPER_ASSET_SELECTION_MODE')
def cmd_configure_scraper_asset_selection_mode(args):
scraper_settings:ScraperSettings = args['scraper_settings'] if 'scraper_settings' in args else ScraperSettings.from_addon_settings()
@@ -423,6 +430,7 @@ def cmd_configure_scraper_asset_selection_mode(args):
AppMediator.sync_cmd(args['ret_cmd'], args)
return
+
@AppMediator.register('SCRAPER_META_TO_SCRAPE')
def cmd_configure_scraper_metadata_to_scrape(args):
scraper_settings:ScraperSettings = args['scraper_settings'] if 'scraper_settings' in args else ScraperSettings.from_addon_settings()
@@ -444,6 +452,7 @@ def cmd_configure_scraper_metadata_to_scrape(args):
AppMediator.sync_cmd(args['ret_cmd'], args)
return
+
@AppMediator.register('SCRAPER_ASSETS_TO_SCRAPE')
def cmd_configure_scraper_assets_to_scrape(args):
scraper_settings:ScraperSettings = args['scraper_settings'] if 'scraper_settings' in args else ScraperSettings.from_addon_settings()
@@ -466,13 +475,23 @@ def cmd_configure_scraper_assets_to_scrape(args):
AppMediator.sync_cmd(args['ret_cmd'], args)
return
-@AppMediator.register('SCRAPER_OVERWRITE_MODE')
-def cmd_configure_scraper_overwrite_mode(args):
+
+@AppMediator.register('SCRAPER_OVERWRITE_META_MODE')
+def cmd_configure_scraper_overwrite_meta_mode(args):
scraper_settings:ScraperSettings = args['scraper_settings'] if 'scraper_settings' in args else ScraperSettings.from_addon_settings()
- scraper_settings.overwrite_existing = not scraper_settings.overwrite_existing
+ scraper_settings.overwrite_existing_meta = not scraper_settings.overwrite_existing_meta
args['scraper_settings'] = scraper_settings
AppMediator.sync_cmd(args['ret_cmd'], args)
+
+@AppMediator.register('SCRAPER_OVERWRITE_ASSETS_MODE')
+def cmd_configure_scraper_overwrite_assets_mode(args):
+ scraper_settings:ScraperSettings = args['scraper_settings'] if 'scraper_settings' in args else ScraperSettings.from_addon_settings()
+ scraper_settings.overwrite_existing_assets = not scraper_settings.overwrite_existing_assets
+ args['scraper_settings'] = scraper_settings
+ AppMediator.sync_cmd(args['ret_cmd'], args)
+
+
@AppMediator.register('SCRAPER_IGNORE_TITLES_MODE')
def cmd_configure_scraper_ignore_mode(args):
scraper_settings:ScraperSettings = args['scraper_settings'] if 'scraper_settings' in args else ScraperSettings.from_addon_settings()
diff --git a/resources/lib/commands/view_rendering_commands.py b/resources/lib/commands/view_rendering_commands.py
index 433d55fc..48da325c 100644
--- a/resources/lib/commands/view_rendering_commands.py
+++ b/resources/lib/commands/view_rendering_commands.py
@@ -275,7 +275,7 @@ def _render_root_view(categories_repository: CategoryRepository, romcollections_
for rom in root_roms:
try:
- root_items.append(_render_rom_listitem(rom))
+ root_items.append(render_rom_listitem(rom))
except Exception:
logger.exception(f"Exception while rendering list item ROM '{rom.get_name()}'")
@@ -336,7 +336,7 @@ def _render_category_view(category_obj: Category, categories_repository: Categor
for rom in roms:
try:
- view_items.append(_render_rom_listitem(rom))
+ view_items.append(render_rom_listitem(rom))
except Exception:
logger.exception(f"Exception while rendering list item ROM '{rom.get_name()}'")
@@ -362,11 +362,11 @@ def _render_romcollection_view(romcollection_obj: ROMCollection, roms_repository
for rom in roms:
try:
rom.apply_romcollection_asset_mapping(romcollection_obj)
- view_items.append(_render_rom_listitem(rom))
+ view_items.append(render_rom_listitem(rom))
except Exception:
logger.exception(f'Exception while rendering list item ROM "{rom.get_name()}"')
- logger.debug(f'Storing {len(view_items)} items for romcollection "{romcollection_obj.get_name()}" view.')
+ logger.debug(f'Found {len(view_items)} items for romcollection "{romcollection_obj.get_name()}" view.')
view_data['items'] = view_items
return view_data
@@ -435,10 +435,14 @@ def _render_romcollection_listitem(romcollection_obj: ROMCollection) -> dict:
'is_folder': True,
'type': 'video',
'info': {
- 'title' : romcollection_name, 'year' : romcollection_obj.get_releaseyear(),
- 'genre' : romcollection_obj.get_genre(), 'studio' : romcollection_obj.get_developer(),
- 'rating' : romcollection_obj.get_rating(), 'plot' : romcollection_obj.get_plot(),
- 'trailer' : romcollection_obj.get_trailer(), 'overlay' : ICON_OVERLAY
+ 'title': romcollection_name,
+ 'year': romcollection_obj.get_releaseyear(),
+ 'genre': romcollection_obj.get_genre(),
+ 'studio': romcollection_obj.get_developer(),
+ 'rating': romcollection_obj.get_rating(),
+ 'plot': romcollection_obj.get_plot(),
+ 'trailer': romcollection_obj.get_trailer(),
+ 'overlay': ICON_OVERLAY
},
'art': assets,
'properties': {
@@ -458,7 +462,7 @@ def _render_romcollection_listitem(romcollection_obj: ROMCollection) -> dict:
#if not settings.getSettingAsBool('display_hide_LB_scraper'): render_vcategory_LB_offline_scraper_row()
-def _render_rom_listitem(rom_obj: ROM) -> dict:
+def render_rom_listitem(rom_obj: ROM) -> dict:
# --- Do not render row if romcollection finished ---
if rom_obj.is_finished() and settings.getSettingAsBool('display_hide_finished'):
return
@@ -512,13 +516,22 @@ def _render_rom_listitem(rom_obj: ROM) -> dict:
AKL_MultiDisc_bool_value = constants.AKL_MULTIDISC_BOOL_VALUE_TRUE
list_name = rom_obj.get_name()
+ sub_label = rom_obj.get_rom_identifier()
if list_name is None or list_name == '':
- list_name = rom_obj.get_rom_identifier()
+ list_name = sub_label
+ if list_name == sub_label:
+ sub_label = None
+
+ if settings.getSettingAsBool("display_execute_rom_by_default"):
+ item_url = globals.router.url_for_path(f'execute/rom/{rom_obj.get_id()}')
+ else:
+ item_url = globals.router.url_for_path(f'rom/view/{rom_obj.get_id()}')
return {
'id': rom_obj.get_id(),
'name': list_name,
- 'url': globals.router.url_for_path(f'execute/rom/{rom_obj.get_id()}'),
+ 'name2': sub_label,
+ 'url': item_url,
'is_folder': False,
'type': 'video',
'info': {
@@ -533,6 +546,8 @@ def _render_rom_listitem(rom_obj: ROM) -> dict:
},
'art': assets,
'properties': {
+ 'entityid': rom_obj.get_id(),
+ 'identifier': rom_obj.get_rom_identifier(),
'platform': rom_obj.get_platform(),
'nplayers': rom_obj.get_number_of_players(),
'nplayers_online': rom_obj.get_number_of_players_online(),
diff --git a/resources/lib/domain.py b/resources/lib/domain.py
index 85798694..627aa3cd 100644
--- a/resources/lib/domain.py
+++ b/resources/lib/domain.py
@@ -2,7 +2,8 @@
#
# Advanced Kodi Launcher miscellaneous set of objects
#
-# Copyright (c) Wintermute0110 / Chrisism
+# Copyright (c) Chrisism
+# Portions (c) Wintermute0110
#
# 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
@@ -44,6 +45,11 @@ def _is_a_number(input: any):
def _is_empty(input: any) -> bool:
return input is None or (not _is_a_number(input) and len(input) == 0)
+def _is_empty_or_default(input: any, default: any):
+ if _is_empty(input):
+ return True
+ return input == default
+
# -------------------------------------------------------------------------------------------------
# Gets all required information about an asset: path, name, etc.
# Returns an object with all the information
@@ -202,6 +208,9 @@ def set_path(self, path_str):
def set_asset_info(self, info:AssetInfo):
self.asset_info = info
+ def is_assigned(self) -> bool:
+ return self.get_path() != ''
+
def clear(self):
self.entity_data['filepath'] = ''
@@ -849,7 +858,8 @@ def __init__(self, category_dic: typing.Dict[str, typing.Any] = None, assets: ty
def get_object_name(self): return 'Category'
- def get_assets_kind(self): return constants.KIND_ASSET_CATEGORY
+ def get_assets_kind(self):
+ return constants.KIND_ASSET_CATEGORY
def get_type(self): return constants.OBJ_CATEGORY
@@ -864,13 +874,16 @@ def num_categories(self) -> int:
return self.entity_data['num_categories'] if 'num_categories' in self.entity_data else 0
def has_items(self) -> bool:
- return len(self.num_romcollections()) > 0 or len(self.num_categories()) > 0
+ return self.num_romcollections() > 0 or self.num_categories() > 0
- def get_asset_ids_list(self): return constants.CATEGORY_ASSET_ID_LIST
+ def get_asset_ids_list(self):
+ return constants.CATEGORY_ASSET_ID_LIST
- def get_mappable_asset_ids_list(self): return constants.MAPPABLE_CATEGORY_ASSET_ID_LIST
+ def get_mappable_asset_ids_list(self):
+ return constants.MAPPABLE_CATEGORY_ASSET_ID_LIST
- def get_default_icon(self) -> str: return 'DefaultFolder.png'
+ def get_default_icon(self) -> str:
+ return 'DefaultFolder.png'
def get_NFO_name(self) -> io.FileName:
nfo_dir = io.FileName(settings.getSetting('categories_asset_dir'), isdir = True)
@@ -1024,13 +1037,16 @@ def set_platform(self, platform): self.entity_data['platform'] = platform
def get_box_sizing(self):
return self.entity_data['box_size'] if 'box_size' in self.entity_data else constants.BOX_SIZE_POSTER
- def set_box_sizing(self, box_size): self.entity_data['box_size'] = box_size
+ def set_box_sizing(self, box_size):
+ self.entity_data['box_size'] = box_size
- def get_asset_ids_list(self): return constants.LAUNCHER_ASSET_ID_LIST
+ def get_asset_ids_list(self):
+ return constants.LAUNCHER_ASSET_ID_LIST
def get_mappable_asset_ids_list(self): return constants.MAPPABLE_LAUNCHER_ASSET_ID_LIST
- def get_default_icon(self) -> str: return 'DefaultFolder.png'
+ def get_default_icon(self) -> str:
+ return 'DefaultGameAddons.png'
def get_ROM_mappable_asset_list(self) -> typing.List[AssetInfo]:
return g_assetFactory.get_asset_list_by_IDs(constants.MAPPABLE_ROM_ASSET_ID_LIST)
@@ -1312,7 +1328,8 @@ def __init__(self,
def get_object_name(self): return 'ROM'
- def get_assets_kind(self): return constants.KIND_ASSET_ROM
+ def get_assets_kind(self):
+ return constants.KIND_ASSET_ROM
def get_type(self): return constants.OBJ_ROM
@@ -1450,6 +1467,9 @@ def set_clone(self, clone):
def scanned_with(self, scanner_id: str):
self.entity_data['scanned_by_id'] = scanner_id
+ def get_scanned_data(self):
+ return self.scanned_data
+
def get_scanned_data_element(self, key:str):
return self.scanned_data[key] if key in self.scanned_data else None
@@ -1633,57 +1653,71 @@ def update_with(self,
api_rom_obj: api.ROMObj,
metadata_to_update=[],
assets_to_update=[],
- overwrite_existing=False,
+ overwrite_existing_metadata=False,
+ overwrite_existing_assets=False,
update_scanned_data=False):
+ logger.debug(f"Overwriting existing metadata in domain: {overwrite_existing_metadata}")
+ logger.debug(f"Overwriting existing assets in domain: {overwrite_existing_assets}")
+
if constants.META_TITLE_ID in metadata_to_update \
- and api_rom_obj.get_name():
+ and api_rom_obj.get_name() \
+ and (overwrite_existing_metadata or \
+ _is_empty_or_default(self.get_name(), constants.DEFAULT_META_TITLE)):
self.set_name(api_rom_obj.get_name())
if constants.META_PLOT_ID in metadata_to_update \
and api_rom_obj.get_plot() \
- and (overwrite_existing or _is_empty(self.get_plot())):
+ and (overwrite_existing_metadata or \
+ _is_empty_or_default(self.get_plot(), constants.DEFAULT_META_PLOT)):
self.set_plot(api_rom_obj.get_plot())
if constants.META_YEAR_ID in metadata_to_update \
and api_rom_obj.get_releaseyear() \
- and (overwrite_existing or _is_empty(self.get_releaseyear())):
+ and (overwrite_existing_metadata or \
+ _is_empty_or_default(self.get_releaseyear(), constants.DEFAULT_META_YEAR)):
self.set_releaseyear(api_rom_obj.get_releaseyear())
if constants.META_GENRE_ID in metadata_to_update \
and api_rom_obj.get_genre() \
- and (overwrite_existing or _is_empty(self.get_genre())):
+ and (overwrite_existing_metadata or \
+ _is_empty_or_default(self.get_genre(), constants.DEFAULT_META_GENRE)):
self.set_genre(api_rom_obj.get_genre())
if constants.META_DEVELOPER_ID in metadata_to_update \
and api_rom_obj.get_developer() \
- and (overwrite_existing or _is_empty(self.get_developer())):
+ and (overwrite_existing_metadata or \
+ _is_empty_or_default(self.get_developer(), constants.DEFAULT_META_DEVELOPER)):
self.set_developer(api_rom_obj.get_developer())
if constants.META_NPLAYERS_ID in metadata_to_update \
and api_rom_obj.get_number_of_players() \
- and (overwrite_existing or _is_empty(self.get_number_of_players())):
+ and (overwrite_existing_metadata or \
+ _is_empty_or_default(self.get_number_of_players(), constants.DEFAULT_META_NPLAYERS)):
self.set_number_of_players(api_rom_obj.get_number_of_players())
if constants.META_NPLAYERS_ONLINE_ID in metadata_to_update \
and api_rom_obj.get_number_of_players_online() \
- and (overwrite_existing or _is_empty(self.get_number_of_players_online())):
+ and (overwrite_existing_metadata or \
+ _is_empty_or_default(self.get_number_of_players_online(), constants.DEFAULT_META_NPLAYERS)):
self.set_number_of_players_online(api_rom_obj.get_number_of_players_online())
if constants.META_ESRB_ID in metadata_to_update\
and api_rom_obj.get_esrb_rating() \
- and (overwrite_existing or _is_empty(self.get_esrb_rating()) \
- or self.get_esrb_rating() == constants.ESRB_PENDING):
+ and (overwrite_existing_metadata or \
+ _is_empty_or_default(self.get_esrb_rating(), constants.DEFAULT_META_ESRB)):
self.set_esrb_rating(api_rom_obj.get_esrb_rating())
if constants.META_PEGI_ID in metadata_to_update\
- and api_rom_obj.get_pegi_rating() \
- and (overwrite_existing or _is_empty(self.get_pegi_rating())):
+ and api_rom_obj.get_pegi_rating() \
+ and (overwrite_existing_metadata or \
+ _is_empty_or_default(self.get_pegi_rating(), constants.DEFAULT_META_PEGI)):
self.set_pegi_rating(api_rom_obj.get_pegi_rating())
if constants.META_RATING_ID in metadata_to_update \
- and api_rom_obj.get_rating() \
- and (overwrite_existing or _is_empty(self.get_rating())):
+ and api_rom_obj.get_rating() \
+ and (overwrite_existing_metadata or \
+ _is_empty_or_default(self.get_rating(), constants.DEFAULT_META_RATING)):
self.set_rating(api_rom_obj.get_rating())
if constants.META_TAGS_ID in metadata_to_update and api_rom_obj.get_tags() is not None:
@@ -1691,20 +1725,24 @@ def update_with(self,
self.add_tag(tag)
if len(assets_to_update) > 0:
- for asset_id in self.get_asset_ids_list():
- if api_rom_obj.get_asset(asset_id) is not None:
+ for asset_id in assets_to_update:
+ existing_asset = self.get_asset(asset_id)
+ new_asset = api_rom_obj.get_asset(asset_id)
+ if new_asset is not None and \
+ (overwrite_existing_assets or existing_asset is None or not existing_asset.is_assigned()):
if asset_id == constants.ASSET_TRAILER_ID:
- self.set_trailer(api_rom_obj.get_asset(asset_id))
+ self.set_trailer(new_asset)
else:
asset_info = g_assetFactory.get_asset_info(asset_id)
- asset_path = io.FileName(api_rom_obj.get_asset(asset_id))
+ asset_path = io.FileName(new_asset)
self.set_asset(asset_info, asset_path)
if update_scanned_data:
scanned_name = api_rom_obj.get_name()
scanned_data = api_rom_obj.get_scanned_data()
- if scanned_name: self.set_name(scanned_name)
+ if scanned_name:
+ self.set_name(scanned_name)
for scanned_entry in list(scanned_data.keys()):
self.set_scanned_data_element(scanned_entry, scanned_data[scanned_entry])
diff --git a/resources/lib/editors.py b/resources/lib/editors.py
index 77754293..323dfe3d 100644
--- a/resources/lib/editors.py
+++ b/resources/lib/editors.py
@@ -3,7 +3,8 @@
# Advanced Kodi Launcher: metadata editor actions
#
-# Copyright (c) Wintermute0110 / Chrisism
+# Copyright (c) Chrisism
+# Portions (c) Wintermute0110
# Portions (c) 2010-2015 Angelscry
#
# This program is free software; you can redistribute it and/or modify
diff --git a/resources/lib/globals.py b/resources/lib/globals.py
index 6ef4f19b..ff6f7232 100644
--- a/resources/lib/globals.py
+++ b/resources/lib/globals.py
@@ -31,6 +31,7 @@
addon_author = addon.getAddonInfo('author')
addon_profile = addon.getAddonInfo('profile')
addon_type = addon.getAddonInfo('type')
+addon_path = addon.getAddonInfo('path')
# --- Addon paths and constant definition ---
# _PATH is a filename | _DIR is a directory.
diff --git a/resources/lib/queries.py b/resources/lib/queries.py
index bd1f9edf..5635220f 100644
--- a/resources/lib/queries.py
+++ b/resources/lib/queries.py
@@ -31,7 +31,7 @@
WHERE id =?
"""
INSERT_CATEGORY_ASSET = "INSERT INTO category_assets (category_id, asset_id) VALUES (?, ?)"
-DELETE_CATEGORY = "DELETE FROM category WHERE id = ?"
+DELETE_CATEGORY = "DELETE FROM categories WHERE id = ?"
INSERT_ROM_IN_CATEGORY = "INSERT INTO roms_in_category (rom_id, category_id) VALUES (?,?)"
INSERT_ROM_IN_ROOT_CATEGORY = "INSERT INTO roms_in_category (rom_id, category_id) VALUES (?,NULL)"
diff --git a/resources/lib/repositories.py b/resources/lib/repositories.py
index 33c83a96..fc122a18 100644
--- a/resources/lib/repositories.py
+++ b/resources/lib/repositories.py
@@ -1061,7 +1061,7 @@ def find_rom(self, rom_id:str) -> ROM:
rom_data = self._uow.single_result()
self._uow.execute(qry.SELECT_ROM_ASSETS, rom_id)
- assets_result_set = self._uow.result_set()
+ assets_result_set = self._uow.result_set()
assets = []
for asset_data in assets_result_set:
assets.append(Asset(asset_data))
diff --git a/resources/lib/viewqueries.py b/resources/lib/viewqueries.py
index 6dc21189..4693add3 100644
--- a/resources/lib/viewqueries.py
+++ b/resources/lib/viewqueries.py
@@ -34,9 +34,12 @@
from resources.lib import globals
from resources.lib.commands.mediator import AppMediator
from resources.lib.commands import view_rendering_commands
-from resources.lib.repositories import ViewRepository
+from resources.lib.repositories import ViewRepository, UnitOfWork, ROMsRepository, g_assetFactory
+
logger = logging.getLogger(__name__)
+
+
#
# Root view items
#
@@ -98,6 +101,7 @@ def qry_get_root_items():
return container
+
#
# View pre-rendered items.
#
@@ -107,10 +111,165 @@ def qry_get_view_items(view_id: str, is_virtual_view=False):
return container
#
-# View database unrendered items.
+# DB based items
#
-def qry_get_database_view_items(category_id: str, collection_value: str):
- return view_rendering_commands.cmd_render_virtual_collection(category_id, collection_value)
+def qry_get_view_item(rom_id: str):
+ uow = UnitOfWork(globals.g_PATHS.DATABASE_FILE_PATH)
+ container = None
+ with uow:
+ roms_repository = ROMsRepository(uow)
+ rom = roms_repository.find_rom(rom_id)
+
+ item = view_rendering_commands.render_rom_listitem(rom)
+ container = {
+ 'id': rom_id,
+ 'name': rom.get_name(),
+ 'obj_type': constants.OBJ_ROM,
+ 'items': [item]
+ }
+
+ return container
+
+def qry_get_view_metadata(rom_id: str):
+ uow = UnitOfWork(globals.g_PATHS.DATABASE_FILE_PATH)
+ container = None
+ with uow:
+ roms_repository = ROMsRepository(uow)
+ rom = roms_repository.find_rom(rom_id)
+
+ items = []
+ items.append({
+ 'id': 40801, 'is_folder': False, 'type': 'game',
+ 'url': globals.router.url_for_path(
+ f'/collection/virtual/{constants.VCATEGORY_GENRE_ID}/items?value={rom.get_genre()}'),
+ 'name': kodi.translate(40801), 'name2': rom.get_genre(),
+ 'info': {}, 'art': {}, 'properties': {'field': 'genre'}})
+ items.append({
+ 'id': 40803, 'is_folder': False, 'type': 'game',
+ 'url': globals.router.url_for_path(
+ f'/collection/virtual/{constants.VCATEGORY_YEARS_ID}/items?value={rom.get_releaseyear()}'),
+ 'name': kodi.translate(40803), 'name2': rom.get_releaseyear(),
+ 'info': {}, 'art': {}, 'properties': {'field': 'releaseyear'}})
+ items.append({
+ 'id': 40802, 'is_folder': False, 'type': 'game',
+ 'url': globals.router.url_for_path(
+ f'/collection/virtual/{constants.VCATEGORY_DEVELOPER_ID}/items?value={rom.get_developer()}'),
+ 'name': kodi.translate(40802), 'name2': rom.get_developer(),
+ 'info': {}, 'art': {}, 'properties': {'field': 'developer'}})
+ items.append({
+ 'id': 40806, 'is_folder': False, 'type': 'game',
+ 'url': globals.router.url_for_path(
+ f'/collection/virtual/{constants.VCATEGORY_RATING_ID}/items?value={rom.get_rating()}'),
+ 'name': kodi.translate(40806), 'name2': str(rom.get_rating()),
+ 'info': {}, 'art': {}, 'properties': {'field': 'rating'}})
+ items.append({
+ 'id': 40804, 'is_folder': False, 'type': 'game',
+ 'url': globals.router.url_for_path(
+ f'/collection/virtual/{constants.VCATEGORY_ESRB_ID}/items?value={rom.get_esrb_rating()}'),
+ 'name': kodi.translate(40804), 'name2': rom.get_esrb_rating(),
+ 'info': {}, 'art': {}, 'properties': {'field': 'esrb'}})
+ items.append({
+ 'id': 40805, 'is_folder': False, 'type': 'game',
+ 'url': globals.router.url_for_path(
+ f'/collection/virtual/{constants.VCATEGORY_PEGI_ID}/items?value={rom.get_pegi_rating()}'),
+ 'name': kodi.translate(40805), 'name2': rom.get_pegi_rating(),
+ 'info': {}, 'art': {}, 'properties': {'field': 'pegi'}})
+ items.append({
+ 'id': 40808, 'is_folder': False, 'type': 'game',
+ 'url': globals.router.url_for_path(
+ f'/collection/virtual/{constants.VCATEGORY_NPLAYERS_ID}/items?value={rom.get_number_of_players()}'),
+ 'name': kodi.translate(40808), 'name2': str(rom.get_number_of_players()),
+ 'info': {}, 'art': {}, 'properties': {'field': 'nplayers'}})
+ items.append({
+ 'id': 40809, 'is_folder': False, 'type': 'game',
+ 'url': globals.router.url_for_path('execute/command/reset_database'),
+ 'name': kodi.translate(40809), 'name2': str(rom.get_number_of_players_online()),
+ 'info': {}, 'art': {}, 'properties': {'field': 'nplayers_online'}})
+ items.append({
+ 'id': 40810, 'is_folder': False, 'type': 'game',
+ 'url': globals.router.url_for_path(
+ f'/collection/virtual/items?value={rom.get_genre()}'),
+ 'name': kodi.translate(40810), 'name2': ','.join(rom.get_tags()),
+ 'info': {}, 'art': {}, 'properties': {'field': 'tags'}})
+
+ container = {
+ 'id': rom_id,
+ 'name': rom.get_name(),
+ 'obj_type': constants.OBJ_ROM,
+ 'items': items
+ }
+
+ return container
+
+
+def qry_get_view_assets(rom_id: str):
+ uow = UnitOfWork(globals.g_PATHS.DATABASE_FILE_PATH)
+ container = None
+ with uow:
+ roms_repository = ROMsRepository(uow)
+ rom = roms_repository.find_rom(rom_id)
+
+ assigned_assets = rom.get_assets()
+ asset_ids = rom.get_asset_ids_list()
+ items = []
+ for asset_id in asset_ids:
+ asset = next((a for a in assigned_assets if a.get_asset_info_id() == asset_id), None)
+ asset_info = asset.asset_info if asset else g_assetFactory.get_asset_info(asset_id)
+ items.append({
+ 'id': asset_id,
+ 'is_folder': False,
+ 'type': 'pictures',
+ 'name': asset_info.name,
+ 'name2': asset.get_path() if asset else None,
+ 'url': asset.get_path() if asset else globals.router.url_for_path(
+ f'/execute/command/rom_edit_assets/?rom_id={rom_id}&selected_asset={asset_id}'),
+ 'info': {
+ 'title': asset_info.name,
+ 'picturepath': asset.get_path() if asset else None,
+ },
+ 'art': {
+ 'thumb': asset.get_path() if asset else 'DefaultAddonImages.png'
+ },
+ 'properties': {
+ 'is_set': str(asset and asset.is_assigned()),
+ 'assetid': asset_id
+ }
+ })
+
+ container = {
+ 'name': rom.get_name(),
+ 'id': rom_id,
+ 'obj_type': constants.OBJ_NONE,
+ 'items': items
+ }
+
+ return container
+
+
+def qry_get_view_scanned_data(rom_id: str):
+ uow = UnitOfWork(globals.g_PATHS.DATABASE_FILE_PATH)
+ container = None
+ with uow:
+ roms_repository = ROMsRepository(uow)
+ rom = roms_repository.find_rom(rom_id)
+ scanned_data = rom.get_scanned_data()
+ items = []
+ for key, value in scanned_data.items():
+ items.append({
+ 'is_folder': False, 'type': 'game',
+ 'name': key, 'name2': value,
+ 'url': globals.router.url_for_path(
+ f'/rom/{rom.get_id()}/view/scanneddata?field={key}'),
+ 'info': {}, 'art': {}, 'properties': {}})
+ container = {
+ 'id': rom_id,
+ 'name': rom.get_name(),
+ 'obj_type': constants.OBJ_ROM,
+ 'items': items
+ }
+
+ return container
+
#
# Utilities items
@@ -299,6 +458,7 @@ def qry_get_utilities_items():
return container
+
#
# Global Reports items
#
@@ -377,6 +537,7 @@ def qry_get_globalreport_items():
})
return container
+
#
# Default context menu items for the whole container.
#
@@ -417,6 +578,7 @@ def qry_container_context_menu_items(container_data) -> typing.List[typing.Tuple
return commands
+
#
# ListItem specific context menu items.
#
@@ -442,7 +604,7 @@ def qry_listitem_context_menu_items(list_item_data, container_data)-> typing.Lis
commands = []
if is_rom:
- commands.append(('View ROM', _context_menu_url_for(f'/rom/{item_id}/view')))
+ commands.append(('View ROM', _context_menu_url_for(f'/rom/view/{item_id}')))
commands.append(('Edit ROM', _context_menu_url_for(f'/rom/edit/{item_id}')))
commands.append(('Link ROM in other collection', _context_menu_url_for('/execute/command/link_rom',{'rom_id':item_id})))
commands.append(('Add ROM to AKL Favourites', _context_menu_url_for('/execute/command/add_rom_to_favourites',{'rom_id':item_id})))
@@ -468,8 +630,9 @@ def qry_listitem_context_menu_items(list_item_data, container_data)-> typing.Lis
return commands
+
def _context_menu_url_for(url: str, params: dict = None) -> str:
if params is not None:
url = '{}?{}'.format(url, urlencode(params))
url = globals.router.url_for_path(url)
- return 'RunPlugin({})'.format(url)
\ No newline at end of file
+ return f'RunPlugin({url})'
\ No newline at end of file
diff --git a/resources/lib/views.py b/resources/lib/views.py
index 6ec37d81..4922fe3d 100644
--- a/resources/lib/views.py
+++ b/resources/lib/views.py
@@ -35,6 +35,7 @@
import sys
import abc
import logging
+import typing
# --- Kodi stuff ---
import xbmc
@@ -46,6 +47,7 @@
from resources.lib import viewqueries, globals
from resources.lib.commands.mediator import AppMediator
+from resources.lib.commands import view_rendering_commands
from resources.lib.globals import router
logger = logging.getLogger(__name__)
@@ -63,8 +65,16 @@ def run_plugin(addon_argv):
# --- Bootstrap object instances ---
globals.g_bootstrap_instances()
+
+ argv = None
+ if sys.argv[0] == "addon.py":
+ argv = []
+ argv.append(sys.argv[1])
+ argv.append(router.handle)
+ argv.append(sys.argv[2])
+
try:
- router.run()
+ router.run(argv)
except Exception as e:
logger.error('Exception while executing route', exc_info=e)
kodi.notify_error('Failed to execute route or command')
@@ -80,7 +90,7 @@ def vw_route_render_root():
container = viewqueries.qry_get_root_items()
container_context_items = viewqueries.qry_container_context_menu_items(container)
- render_list_items(container, container_context_items)
+ _render_list_items(container, container_context_items)
xbmcplugin.endOfDirectory(handle = router.handle, succeeded = True, cacheToDisc = False)
@router.route('/category/')
@@ -103,7 +113,7 @@ def vw_route_render_collection(view_id: str):
if container_type == constants.OBJ_ROMCOLLECTION or container_type == constants.OBJ_COLLECTION_VIRTUAL:
kodi.notify('Collection {} has no items. Add ROMs'.format(container['name']))
else:
- render_list_items(container, container_context_items, filter)
+ _render_list_items(container, container_context_items, filter)
xbmcplugin.endOfDirectory(handle = router.handle, succeeded = True, cacheToDisc = False)
@@ -135,21 +145,21 @@ def vw_route_render_virtual_view(view_id: str):
if kodi.dialog_yesno(f"Virtual collection {container['name']} has no items. Regenerate the views now?"):
AppMediator.async_cmd('RENDER_VCATEGORY_VIEW', {'vcategory_id': container['parent_id']})
else:
- render_list_items(container, container_context_items, filter)
+ _render_list_items(container, container_context_items, filter)
xbmcplugin.endOfDirectory(handle = router.handle, succeeded = True, cacheToDisc = False)
@router.route('/collection/virtual//items')
def vw_route_render_virtual_items_view(category_id: str):
collection_value = router.args["value"][0]
- container = viewqueries.qry_get_database_view_items(category_id, collection_value)
+ container = view_rendering_commands.cmd_render_virtual_collection(category_id, collection_value)
container_context_items = viewqueries.qry_container_context_menu_items(container)
filter_type = router.args['filter'][0] if 'filter' in router.args else None
filter_term = router.args['term'][0] if 'term' in router.args else None
filter = vw_create_filter(filter_type, filter_term)
- render_list_items(container, container_context_items, filter)
+ _render_list_items(container, container_context_items, filter)
xbmcplugin.endOfDirectory(handle = router.handle, succeeded = True, cacheToDisc = False)
@@ -161,7 +171,7 @@ def vw_route_render_utilities():
container = viewqueries.qry_get_utilities_items()
container_context_items = viewqueries.qry_container_context_menu_items(container)
- render_list_items(container, container_context_items)
+ _render_list_items(container, container_context_items)
xbmcplugin.endOfDirectory(handle = router.handle, succeeded = True, cacheToDisc = False)
@router.route('/globalreports')
@@ -169,7 +179,7 @@ def vw_route_render_globalreports():
container = viewqueries.qry_get_globalreport_items()
container_context_items = viewqueries.qry_container_context_menu_items(container)
- render_list_items(container, container_context_items)
+ _render_list_items(container, container_context_items)
xbmcplugin.endOfDirectory(handle = router.handle, succeeded = True, cacheToDisc = False)
# -------------------------------------------------------------------------------------------------
@@ -218,19 +228,60 @@ def vw_edit_rom(rom_id: str):
AppMediator.async_cmd('EDIT_ROM', {'rom_id': rom_id })
# -------------------------------------------------------------------------------------------------
-# ROM execution
+# ROM execution / view
# -------------------------------------------------------------------------------------------------
@router.route('/execute/rom/')
def vw_route_execute_rom(rom_id):
AppMediator.async_cmd("EXECUTE_ROM", {'rom_id': rom_id} )
-
+
+
+@router.route('/rom/view/')
+def vw_view_rom(rom_id):
+ xbmc.executebuiltin('Dialog.Close(busydialog)')
+ container = viewqueries.qry_get_view_item(rom_id)
+ ui = ViewRomGUI('script-akl-romdetails.xml', globals.addon_path, 'default', '1080i', True,
+ container_id=19801,container_data=container)
+ ui.doModal()
+ del ui
+
+
+@router.route('/rom//metadata')
+def vw_view_rom_metadata(rom_id):
+ container = viewqueries.qry_get_view_metadata(rom_id)
+ _render_list_items(container)
+ xbmcplugin.endOfDirectory(handle = router.handle, succeeded = True, cacheToDisc = False)
+
+
+@router.route('/rom//assets')
+def vw_view_rom_assets(rom_id):
+ container = viewqueries.qry_get_view_assets(rom_id)
+ _render_list_items(container)
+ xbmcplugin.endOfDirectory(handle = router.handle, succeeded = True, cacheToDisc = False)
+
+
+@router.route('/rom//scanneddata')
+def vw_view_rom_metadata(rom_id):
+ container = viewqueries.qry_get_view_scanned_data(rom_id)
+ _render_list_items(container)
+ xbmcplugin.endOfDirectory(handle = router.handle, succeeded = True, cacheToDisc = False)
+
+@router.route('/rom//view/scanneddata')
+def vw_view_rom_metadata(rom_id):
+ field = router.args['field'][0] if 'field' in router.args else None
+ if not field:
+ kodi.notify_warn("No field specified")
+ return
+ container = viewqueries.qry_get_view_scanned_data(rom_id)
+ requested_item = next((i for i in container['items'] if i['name'] == field), None)
+ xbmcgui.Dialog().textviewer(str(field), str(requested_item['name2']))
+
# -------------------------------------------------------------------------------------------------
# UI render methods
# -------------------------------------------------------------------------------------------------
#
# Renders items for a view.
#
-def render_list_items(container_data:dict, container_context_items = [], filter_method:ListFilter = None):
+def _render_list_items(container_data:dict, container_context_items = [], filter_method:ListFilter = None):
vw_misc_set_all_sorting_methods()
vw_misc_set_AEL_Content(container_data['obj_type'] if 'obj_type' in container_data else constants.OBJ_NONE)
vw_misc_clear_AEL_Launcher_Content()
@@ -244,15 +295,9 @@ def render_list_items(container_data:dict, container_context_items = [], filter_
if filter_method and not filter_method.is_valid(list_item_data):
continue
- name = list_item_data['name']
- url_str = list_item_data['url']
- folder_flag = list_item_data['is_folder']
- item_type = list_item_data['type']
-
- list_item = xbmcgui.ListItem(name)
- list_item.setInfo(item_type, list_item_data['info'])
- list_item.setArt(list_item_data['art'])
- list_item.setProperties(list_item_data['properties'])
+ list_item = _render_list_item(list_item_data)
+ url_str = list_item_data['url']
+ folder_flag = list_item_data['is_folder']
if xbmc.getCondVisibility("!Skin.HasSetting(KioskMode.Enabled)"):
item_context_items = viewqueries.qry_listitem_context_menu_items(list_item_data, container_data)
@@ -260,6 +305,71 @@ def render_list_items(container_data:dict, container_context_items = [], filter_
xbmcplugin.addDirectoryItem(handle = router.handle, url = url_str, listitem = list_item, isFolder = folder_flag)
+def _render_list_item(list_item_data: dict) -> xbmcgui.ListItem:
+ name = list_item_data['name']
+ name2 = list_item_data['name2'] if 'name2' in list_item_data else ""
+ item_type = list_item_data['type']
+
+ list_item = xbmcgui.ListItem(name, label2=name2)
+ list_item.setInfo(item_type, list_item_data['info'])
+ list_item.setArt(list_item_data['art'])
+ list_item.setProperties(list_item_data['properties'])
+
+ return list_item
+
+
+class ViewRomGUI(xbmcgui.WindowXML):
+
+ def __init__(self, *args, **kwargs):
+ self.first_load = True
+
+ self.container_id = kwargs['container_id']
+ self.container_data = kwargs['container_data']
+
+ def onInit(self):
+ if self.first_load:
+ self._render_items()
+
+ self.textviewer_id = self.getProperty("TextViewerButtonId")
+ self.global_button_id = self.getProperty("GlobalButtonId")
+
+ def onClick(self, controlId: int):
+ str_control_id = str(controlId)
+
+ if str_control_id == self.textviewer_id:
+ plot = self.container_data["items"][0]["info"]["plot"]
+ xbmcgui.Dialog().textviewer(kodi.translate(40811), plot)
+
+ if str_control_id == self.global_button_id:
+ uri = self.getProperty("call")
+ args = self.getProperty("callargs")
+ uri_args = None
+ if args:
+ args_lst = args.split("=")
+ uri_args = { args_lst[0]: args_lst[1]}
+ self.close()
+ kodi.update_uri(uri, uri_args)
+
+ return super().onClick(controlId)
+
+ def _render_items(self):
+ self.first_load = False
+ self.clearList()
+ try:
+ cntrl = self.getControl(self.container_id)
+ # Container Properties
+ if 'properties' in self.container_data:
+ for property, value in self.container_data['properties'].items():
+ self.setContainerProperty(property, value)
+
+ for list_item_data in self.container_data['items']:
+ list_item = _render_list_item(list_item_data)
+ cntrl.addItem(list_item)
+ except RuntimeError as error:
+ logger.error(f'Control cannot be filled. Error: {error}')
+ pass
+
+
# -------------------------------------------------------------------------------------------------
# UI helper methods
# -------------------------------------------------------------------------------------------------
@@ -268,12 +378,13 @@ def render_list_items(container_data:dict, container_context_items = [], filter_
#
def vw_misc_set_all_sorting_methods():
# >> This must be called only if router.handle > 0, otherwise Kodi will complain in the log.
- if router.handle < 0: return
+ if router.handle < 0:
+ return
xbmcplugin.addSortMethod(handle = router.handle, sortMethod = xbmcplugin.SORT_METHOD_LABEL_IGNORE_FOLDERS)
xbmcplugin.addSortMethod(handle = router.handle, sortMethod = xbmcplugin.SORT_METHOD_VIDEO_YEAR)
xbmcplugin.addSortMethod(handle = router.handle, sortMethod = xbmcplugin.SORT_METHOD_STUDIO)
- xbmcplugin.addSortMethod(handle = router.handle, sortMethod = xbmcplugin.SORT_METHOD_GENRE)
xbmcplugin.addSortMethod(handle = router.handle, sortMethod = xbmcplugin.SORT_METHOD_UNSORTED)
+ xbmcplugin.addSortMethod(handle = router.handle, sortMethod = xbmcplugin.SORT_METHOD_GENRE)
#
# Set the AEL content type.
@@ -309,18 +420,29 @@ def vw_misc_clear_AEL_Launcher_Content():
xbmcgui.Window(constants.AKL_CONTENT_WINDOW_ID).setProperty(constants.AKL_LAUNCHER_BOXSIZE_LABEL, '')
def vw_create_filter(filter_on_type:str, filter_on_value:str) -> ListFilter:
- if filter_on_type is None: return None
- if filter_on_value == 'UNDEFINED': filter_on_value = ''
+ if filter_on_type is None:
+ return None
+ if filter_on_value == 'UNDEFINED':
+ filter_on_value = ''
- if filter_on_type == constants.META_TITLE_ID: return OnTitleFilter(filter_on_value)
- if filter_on_type == constants.META_DEVELOPER_ID: return OnDeveloperFilter(filter_on_value)
- if filter_on_type == constants.META_GENRE_ID: return OnGenreFilter(filter_on_value)
- if filter_on_type == constants.META_YEAR_ID: return OnReleaseYearFilter(filter_on_value)
- if filter_on_type == constants.META_RATING_ID: return OnRatingFilter(filter_on_value)
- if filter_on_type == constants.META_ESRB_ID: return OnESRBFilter(filter_on_value)
- if filter_on_type == constants.META_PEGI_ID: return OnPEGIFilter(filter_on_value)
- if filter_on_type == constants.META_NPLAYERS_ID: return OnNumberOfPlayersFilter(filter_on_value)
- if filter_on_type == 'platform': return OnPlatformFilter(filter_on_value)
+ if filter_on_type == constants.META_TITLE_ID:
+ return OnTitleFilter(filter_on_value)
+ if filter_on_type == constants.META_DEVELOPER_ID:
+ return OnDeveloperFilter(filter_on_value)
+ if filter_on_type == constants.META_GENRE_ID:
+ return OnGenreFilter(filter_on_value)
+ if filter_on_type == constants.META_YEAR_ID:
+ return OnReleaseYearFilter(filter_on_value)
+ if filter_on_type == constants.META_RATING_ID:
+ return OnRatingFilter(filter_on_value)
+ if filter_on_type == constants.META_ESRB_ID:
+ return OnESRBFilter(filter_on_value)
+ if filter_on_type == constants.META_PEGI_ID:
+ return OnPEGIFilter(filter_on_value)
+ if filter_on_type == constants.META_NPLAYERS_ID:
+ return OnNumberOfPlayersFilter(filter_on_value)
+ if filter_on_type == 'platform':
+ return OnPlatformFilter(filter_on_value)
logger.debug(f'Filter called without proper filter type. "{filter_on_type}"')
return None
@@ -370,4 +492,3 @@ def is_valid(self, subject: dict) -> bool:
class OnPlatformFilter(ListFilter):
def is_valid(self, subject: dict) -> bool:
return 'properties' in subject and 'platform' in subject['properties'] and subject['properties']['platform'] == self.filter_on_value
-
\ No newline at end of file
diff --git a/resources/settings.xml b/resources/settings.xml
index 904b2a5b..ff30e321 100644
--- a/resources/settings.xml
+++ b/resources/settings.xml
@@ -126,6 +126,11 @@
+
+ 0
+ false
+
+
0
false
diff --git a/resources/skins/default/1080i/script-akl-romdetails.xml b/resources/skins/default/1080i/script-akl-romdetails.xml
new file mode 100644
index 00000000..1654e319
--- /dev/null
+++ b/resources/skins/default/1080i/script-akl-romdetails.xml
@@ -0,0 +1,750 @@
+
+
+ 5000
+ SetProperty(TextViewerButtonId,138)
+ SetProperty(GlobalButtonId,19810)
+ SetProperty(DataListView,metadata)
+
+
+ Storage container for the item details
+ -3000
+ -3000
+ 1
+ 1
+
+
+
+
+ Hidden general action button
+ -3000
+ -3000
+ 1
+ 1
+
+
+
+ Main window
+ 50%
+ 1080
+ 50%
+ 1920
+
+ 1920
+ 1080
+ scale
+ colors/black.png
+
+
+ Conditional
+
+ 1920
+ 1080
+ scale
+ backgrounds/grayfade.jpg
+
+
+
+
+ Poster/Boxfront
+ 150
+ 66
+
+
+
+
+
+
+
+
+
+ -16
+ -16
+ 566
+ 841
+ colors/black.png
+ overlays/shadow.png
+ 20
+
+
+ 0.10
+ 4
+ 4
+ 526
+ 801
+ scale
+ $INFO[Container(19801).ListItem.Art(boxfront)]
+
+
+ !String.IsEmpty(Container(19801).ListItem.Art(thumb)) + !String.IsEqual(Container(19801).ListItem.Art(thumb),Container(19801).ListItem.Art(boxfront))
+
+ 4
+ 4
+ 526
+ 801
+ stretch
+ colors/black.png
+
+
+ 14
+ 4
+ 506
+ 801
+ keep
+ $INFO[Container(19801).ListItem.Art(thumb)]
+ overlays/shadow.png
+ 20
+
+
+
+
+
+ Lists and boxes
+
+
+
+
+
+
+
+
+ 620
+ -30
+
+
+ Plot box
+ 468
+
+ 754
+ 418
+ 40
+ 20
+ bottom
+
+ 19802
+ 4000
+ 4000
+ 4010
+ 4010
+ 5000
+ buttons/button-fo.png
+ dialogs/dialog-bg.png
+
+
+ 735
+ 512
+ 418
+ bottom
+ dialogs/dialog-bg.png
+
+
+ 40
+ 25
+ 670
+ 363
+
+ true
+
+
+
+
+ Metadata list
+ vertical
+ String.IsEmpty(Window.Property(DataListView)) | String.IsEqual(Window.Property(DataListView),metadata)
+ 755
+ 488
+ 377
+ -8
+ 5000
+ SetFocus(5000,4)
+ 19802
+ 140
+ 140
+ SetProperty(call,$INFO[Container(4000).ListItem.Property(action)])
+ SetProperty(callargs,$INFO[Container(4000).ListItem.Property(args)])
+ SendClick(,19810)
+
+
+ -
+
+ $INFO[Container(19801).ListItem.Label]
+ plugin://plugin.program.akl/collection/virtual/vcategory_title/items/
+ $INFO[Container(19801).ListItem.Label,value=,]
+ !String.IsEmpty(Container(19801).ListItem.Label)
+
+ -
+
+ $INFO[Container(19801).ListItem.Property(platform)]
+ plugin://plugin.program.akl/collection/virtual/vcategory_title/items/
+ $INFO[Container(19801).ListItem.Property(platform),value=,]
+ !String.IsEmpty(Container(19801).ListItem.Property(platform))
+
+ -
+
+ $INFO[Container(19801).ListItem.Property(identifier)]
+ plugin://plugin.program.akl/collection/virtual/vcategory_title/items/
+ $INFO[Container(19801).ListItem.Property(identifier),value=,]
+ !String.IsEmpty(Container(19801).ListItem.Property(identifier))
+
+ -
+
+ $INFO[Container(19801).ListItem.Genre]
+ plugin://plugin.program.akl/collection/virtual/vcategory_genre/items/
+ $INFO[Container(19801).ListItem.Genre,value=,]
+ !String.IsEmpty(Container(19801).ListItem.Genre)
+
+ -
+
+ $INFO[Container(19801).ListItem.Year]
+ plugin://plugin.program.akl/collection/virtual/vcategory_year/items/
+ $INFO[Container(19801).ListItem.Year,value=,]
+ !String.IsEmpty(Container(19801).ListItem.Year)
+
+ -
+
+ $INFO[Container(19801).ListItem.Studio]
+ plugin://plugin.program.akl/collection/virtual/vcategory_developer/items/
+ $INFO[Container(19801).ListItem.Studio,value=,]
+ !String.IsEmpty(Container(19801).ListItem.Studio)
+
+ -
+
+ $INFO[Container(19801).ListItem.Rating]
+ plugin://plugin.program.akl/collection/virtual/vcategory_rating/items/
+ $INFO[Container(19801).ListItem.Rating,value=,]
+ !String.IsEmpty(Container(19801).ListItem.Rating)
+
+ -
+
+ $INFO[Container(19801).ListItem.Property(esrb)]
+ plugin://plugin.program.akl/collection/virtual/vcategory_esrb/items/
+ $INFO[Container(19801).ListItem.Property(esrb),value=,]
+ !String.IsEmpty(Container(19801).ListItem.Property(esrb))
+
+ -
+
+ $INFO[Container(19801).ListItem.Property(pegi)]
+ plugin://plugin.program.akl/collection/virtual/vcategory_pegi/items/
+ $INFO[Container(19801).ListItem.Property(pegi),value=,]
+ !String.IsEmpty(Container(19801).ListItem.Property(pegi))
+
+ -
+
+ $INFO[Container(19801).ListItem.Property(nplayers)]
+ plugin://plugin.program.akl/collection/virtual/vcategory_nplayers/items/
+ $INFO[Container(19801).ListItem.Property(nplayers),value=,]
+ !String.IsEmpty(Container(19801).ListItem.Property(nplayers))
+
+ -
+
+ $INFO[Container(19801).ListItem.Property(nplayers_online)]
+
+ !String.IsEmpty(Container(19801).ListItem.Property(nplayers_online))
+
+ -
+
+ $INFO[Container(19801).ListItem.Property(tags)]
+
+ !String.IsEmpty(Container(19801).ListItem.Property(tags))
+
+
+
+
+ 472
+ 49
+ center
+ left
+
+
+
+ 0
+ 472
+ 49
+ 16
+ center
+ left
+ font12
+
+
+
+
+
+ 472
+ 49
+ center
+ left
+ lists/focus.png
+ lists/focus.png
+
+
+
+ 0
+ 472
+ 49
+ 16
+ center
+ left
+ font12
+
+
+
+
+
+
+ Scanned data list
+ vertical
+ String.IsEqual(Window.Property(DataListView),scanneddata)
+ 755
+ 488
+ 377
+ -8
+ 5000
+ SetFocus(5000,4)
+ 19802
+ 140
+ 140
+ $INFO[Container(19801).ListItem.Property(entityid),plugin://plugin.program.akl/rom/,/scanneddata]
+
+
+ 472
+ 49
+ center
+ left
+
+
+
+ 0
+ 472
+ 49
+ 16
+ center
+ left
+ font12
+
+
+
+
+
+ 472
+ 49
+ center
+ left
+ lists/focus.png
+ lists/focus.png
+
+
+
+ 0
+ 472
+ 49
+ 16
+ center
+ left
+ font12
+
+
+
+
+
+
+ Assets list
+ 158
+ 1235
+ 370
+ 19802
+ 19802
+ 140
+ 200
+ horizontal
+ $INFO[Container(19801).ListItem.Property(entityid),plugin://plugin.program.akl/rom/,/assets]
+
+
+ 10
+
+ 0
+ 264
+ 317
+ DefaultAddonImages.png
+ scale
+ overlays/shadow.png
+ 20
+
+
+ 20
+ 20
+ 224
+ 277
+ $INFO[ListItem.Thumb]
+ scale
+
+
+ 20
+ 80
+ 224
+ 10
+ overlays/overlayfade.png
+ Conditional
+
+
+ 25
+ 214
+ 67
+ 218
+ center
+ center
+ font12
+
+
+
+ 25
+ 214
+ 67
+ 245
+ center
+ center
+ font12
+ grey
+
+
+
+
+
+
+ 0
+ 10
+
+ 0
+ 264
+ 317
+ DefaultAddonImages.png
+ scale
+ overlays/shadow.png
+ 20
+
+
+ 20
+ 20
+ 224
+ 277
+ $INFO[ListItem.Thumb]
+ scale
+
+
+ 20
+ 224
+ 80
+ 10
+ overlays/overlayfade.png
+ Conditional
+
+
+ 25
+ 214
+ 67
+ 218
+ center
+ center
+ font12
+ true
+
+
+
+ 25
+ 214
+ 67
+ 245
+ center
+ center
+ font12
+ grey
+ true
+
+
+
+ 16
+ 16
+ 232
+ 285
+ buttons/thumbnail_focused.png
+
+
+
+
+
+
+ Buttons List
+ 0
+ 864
+ 1246
+ 400
+ 5000
+ 5000
+ 140
+ SetFocus(19802,$INFO[Container(5000).Position])
+ SetFocus(19802,4)
+ -18
+ center
+ horizontal
+ 200
+
+ 262
+ 142
+ center
+ top
+ buttons/button-fo.png
+ buttons/button-nofo.png
+ 22
+ 78
+ 108
+ 16
+ 48
+ font12
+ icons/launch.png
+ icons/launch.png
+ icons/launch.png
+ icons/launch.png
+ icons/launch.png
+ icons/launch.png
+
+ RunPlugin(plugin://plugin.program.akl/execute/rom/$INFO[Container(19801).ListItem.Property(entityid)])
+ Hidden
+
+
+ 262
+ 142
+ center
+ top
+ buttons/button-fo.png
+ buttons/button-nofo.png
+ 22
+ 78
+ 108
+ 16
+ 48
+ font12
+ icons/trailer.png
+ icons/trailer.png
+ icons/trailer.png
+ icons/trailer.png
+ icons/trailer.png
+ icons/trailer.png
+
+ PlayMedia($INFO[Container(19801).ListItem.Trailer])
+ Hidden
+
+
+ 262
+ 142
+ center
+ top
+ buttons/button-fo.png
+ buttons/button-nofo.png
+ buttons/button-fo.png
+ buttons/button-nofo.png
+ 35
+ 78
+ font12
+
+ Show metadata
+ SetProperty(DataListView,scanneddata)
+ SetProperty(DataListView,metadata)
+
+
+ 262
+ 142
+ center
+ top
+ buttons/button-fo.png
+ buttons/button-nofo.png
+ 22
+ 78
+ 108
+ 16
+ 48
+ font12
+ icons/system.png
+ icons/system.png
+ icons/system.png
+ icons/system.png
+ icons/system.png
+ icons/system.png
+
+ RunPlugin(plugin://plugin.program.akl/execute/command/rom_edit_metadata/?rom_id=$INFO[Container(19801).ListItem.Property(entityid)])
+ Hidden
+
+
+ 262
+ 142
+ center
+ top
+ buttons/button-fo.png
+ buttons/button-nofo.png
+ 22
+ 78
+ 108
+ 16
+ 48
+ font12
+ icons/media.png
+ icons/media.png
+ icons/media.png
+ icons/media.png
+ icons/media.png
+ icons/media.png
+
+ RunPlugin(plugin://plugin.program.akl/execute/command/rom_edit_assets/?rom_id=$INFO[Container(19801).ListItem.Property(entityid)])
+ Hidden
+
+
+ 262
+ 142
+ center
+ top
+ buttons/button-fo.png
+ buttons/button-nofo.png
+ 22
+ 78
+ 108
+ 16
+ 48
+ font12
+ icons/search.png
+ icons/search.png
+ icons/search.png
+ icons/search.png
+ icons/search.png
+ icons/search.png
+
+ RunPlugin(plugin://plugin.program.akl/execute/command/scrape_rom/?rom_id=$INFO[Container(19801).ListItem.Property(entityid)])
+ Hidden
+
+
+
+
+ -15
+ 924
+ 28
+ 28
+ overlays/arrowright.png
+ VisibleChange
+ WindowOpen
+ WindowClose
+ Control.IsVisible(5000) + Container(5000).HasPrevious
+ true
+
+
+ 1240
+ 924
+ 28
+ 28
+ overlays/arrowright.png
+ VisibleChange
+ WindowOpen
+ WindowClose
+ Control.IsVisible(5000) + Container(5000).HasNext
+ true
+
+
+ -15
+ 924
+ 28
+ 28
+
+
+ Control.Move(5000,-1)
+ Container(5000).HasPrevious + [Control.HasFocus(5000) | Control.HasFocus(5000500)]
+
+
+ -15
+ 320
+ 28
+ 28
+ overlays/arrowright.png
+ VisibleChange
+ WindowOpen
+ WindowClose
+ Control.IsVisible(19802) + Container(19802).HasPrevious
+
+
+ 1240
+ 320
+ 28
+ 28
+ overlays/arrowright.png
+ VisibleChange
+ WindowOpen
+ WindowClose
+ Control.IsVisible(19802) + Container(19802).HasNext
+
+
+ -15
+ 320
+ 28
+ 28
+
+
+ Control.Move(19802,-1)
+ Container(19802).HasPrevious + [Control.HasFocus(19802) | Control.HasFocus(19802500)]
+
+
+ 1240
+ 320
+ 28
+ 28
+
+
+ Control.Move(19802,1)
+ Container(19802).HasNext + [Control.HasFocus(19802) | Control.HasFocus(19802501)]
+
+
+
+
+ 80
+ 970
+ right
+ 1400
+ 44
+ font20_title
+ 99FFFFFF
+ 22000000
+ true
+
+ Control.HasFocus(6)
+ WindowOpen
+ WindowClose
+ Visible
+ Hidden
+
+
+
+ Header
+ 100
+ 100
+ 20
+ WindowOpen
+ WindowClose
+ 150
+ 0
+ vertical
+
+ 100%
+
+ center
+ font52_title
+ 22000000
+ 65
+ true
+ Conditional
+
+
+
+ 85
+ 22000000
+ center
+ 100
+ 100%
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/skins/default/media/backgrounds/grayfade.jpg b/resources/skins/default/media/backgrounds/grayfade.jpg
new file mode 100644
index 00000000..ca82ba10
Binary files /dev/null and b/resources/skins/default/media/backgrounds/grayfade.jpg differ
diff --git a/resources/skins/default/media/buttons/button-fo.png b/resources/skins/default/media/buttons/button-fo.png
new file mode 100644
index 00000000..05936eac
Binary files /dev/null and b/resources/skins/default/media/buttons/button-fo.png differ
diff --git a/resources/skins/default/media/buttons/button-nofo.png b/resources/skins/default/media/buttons/button-nofo.png
new file mode 100644
index 00000000..7b7e1e4b
Binary files /dev/null and b/resources/skins/default/media/buttons/button-nofo.png differ
diff --git a/resources/skins/default/media/buttons/thumbnail_focused.png b/resources/skins/default/media/buttons/thumbnail_focused.png
new file mode 100644
index 00000000..e9d8dfe1
Binary files /dev/null and b/resources/skins/default/media/buttons/thumbnail_focused.png differ
diff --git a/resources/skins/default/media/colors/black.png b/resources/skins/default/media/colors/black.png
new file mode 100644
index 00000000..2ff1770d
Binary files /dev/null and b/resources/skins/default/media/colors/black.png differ
diff --git a/resources/skins/default/media/dialogs/dialog-bg-nobo.png b/resources/skins/default/media/dialogs/dialog-bg-nobo.png
new file mode 100644
index 00000000..78ef2392
Binary files /dev/null and b/resources/skins/default/media/dialogs/dialog-bg-nobo.png differ
diff --git a/resources/skins/default/media/dialogs/dialog-bg.png b/resources/skins/default/media/dialogs/dialog-bg.png
new file mode 100644
index 00000000..7b7e1e4b
Binary files /dev/null and b/resources/skins/default/media/dialogs/dialog-bg.png differ
diff --git a/resources/skins/default/media/dialogs/separator-grey.png b/resources/skins/default/media/dialogs/separator-grey.png
new file mode 100644
index 00000000..28ca4152
Binary files /dev/null and b/resources/skins/default/media/dialogs/separator-grey.png differ
diff --git a/resources/skins/default/media/icons/addons.png b/resources/skins/default/media/icons/addons.png
new file mode 100644
index 00000000..4a5f3a3b
Binary files /dev/null and b/resources/skins/default/media/icons/addons.png differ
diff --git a/resources/skins/default/media/icons/android.png b/resources/skins/default/media/icons/android.png
new file mode 100644
index 00000000..400ead1e
Binary files /dev/null and b/resources/skins/default/media/icons/android.png differ
diff --git a/resources/skins/default/media/icons/download.png b/resources/skins/default/media/icons/download.png
new file mode 100644
index 00000000..e3c4e22f
Binary files /dev/null and b/resources/skins/default/media/icons/download.png differ
diff --git a/resources/skins/default/media/icons/enabled.png b/resources/skins/default/media/icons/enabled.png
new file mode 100644
index 00000000..c602a805
Binary files /dev/null and b/resources/skins/default/media/icons/enabled.png differ
diff --git a/resources/skins/default/media/icons/games.png b/resources/skins/default/media/icons/games.png
new file mode 100644
index 00000000..7ff17590
Binary files /dev/null and b/resources/skins/default/media/icons/games.png differ
diff --git a/resources/skins/default/media/icons/image.png b/resources/skins/default/media/icons/image.png
new file mode 100644
index 00000000..cdc69826
Binary files /dev/null and b/resources/skins/default/media/icons/image.png differ
diff --git a/resources/skins/default/media/icons/launch.png b/resources/skins/default/media/icons/launch.png
new file mode 100644
index 00000000..94277277
Binary files /dev/null and b/resources/skins/default/media/icons/launch.png differ
diff --git a/resources/skins/default/media/icons/media.png b/resources/skins/default/media/icons/media.png
new file mode 100644
index 00000000..1ee43583
Binary files /dev/null and b/resources/skins/default/media/icons/media.png differ
diff --git a/resources/skins/default/media/icons/rating.png b/resources/skins/default/media/icons/rating.png
new file mode 100644
index 00000000..2480dd2d
Binary files /dev/null and b/resources/skins/default/media/icons/rating.png differ
diff --git a/resources/skins/default/media/icons/search.png b/resources/skins/default/media/icons/search.png
new file mode 100644
index 00000000..b85be636
Binary files /dev/null and b/resources/skins/default/media/icons/search.png differ
diff --git a/resources/skins/default/media/icons/settings.png b/resources/skins/default/media/icons/settings.png
new file mode 100644
index 00000000..f985035a
Binary files /dev/null and b/resources/skins/default/media/icons/settings.png differ
diff --git a/resources/skins/default/media/icons/system.png b/resources/skins/default/media/icons/system.png
new file mode 100644
index 00000000..1206ffba
Binary files /dev/null and b/resources/skins/default/media/icons/system.png differ
diff --git a/resources/skins/default/media/icons/trailer.png b/resources/skins/default/media/icons/trailer.png
new file mode 100644
index 00000000..d58bc6a7
Binary files /dev/null and b/resources/skins/default/media/icons/trailer.png differ
diff --git a/resources/skins/default/media/icons/uninstall.png b/resources/skins/default/media/icons/uninstall.png
new file mode 100644
index 00000000..f6d7cccd
Binary files /dev/null and b/resources/skins/default/media/icons/uninstall.png differ
diff --git a/resources/skins/default/media/icons/update.png b/resources/skins/default/media/icons/update.png
new file mode 100644
index 00000000..7d305c7b
Binary files /dev/null and b/resources/skins/default/media/icons/update.png differ
diff --git a/resources/skins/default/media/lists/focus.png b/resources/skins/default/media/lists/focus.png
new file mode 100644
index 00000000..007c9c99
Binary files /dev/null and b/resources/skins/default/media/lists/focus.png differ
diff --git a/resources/skins/default/media/lists/panel.png b/resources/skins/default/media/lists/panel.png
new file mode 100644
index 00000000..0140fe04
Binary files /dev/null and b/resources/skins/default/media/lists/panel.png differ
diff --git a/resources/skins/default/media/overlays/arrowright.png b/resources/skins/default/media/overlays/arrowright.png
new file mode 100644
index 00000000..6f6e66d3
Binary files /dev/null and b/resources/skins/default/media/overlays/arrowright.png differ
diff --git a/resources/skins/default/media/overlays/folder.png b/resources/skins/default/media/overlays/folder.png
new file mode 100644
index 00000000..0072e7d2
Binary files /dev/null and b/resources/skins/default/media/overlays/folder.png differ
diff --git a/resources/skins/default/media/overlays/overlayfade.png b/resources/skins/default/media/overlays/overlayfade.png
new file mode 100644
index 00000000..6a0b5b3b
Binary files /dev/null and b/resources/skins/default/media/overlays/overlayfade.png differ
diff --git a/resources/skins/default/media/overlays/shadow.png b/resources/skins/default/media/overlays/shadow.png
new file mode 100644
index 00000000..d66bc61d
Binary files /dev/null and b/resources/skins/default/media/overlays/shadow.png differ