Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

script.copacetic.helper 1.0.12 #2599

Merged
merged 1 commit into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions script.copacetic.helper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ All code contained in this project is licensed under GPL 3.0.
* __jurialmunkey__ for all the best-practice code examples from [plugin.video.themoviedb.helper](https://github.com/jurialmunkey/plugin.video.themoviedb.helper) and forum support.

### Changelog
**1.0.11**
- Removed visualisation waveform setting from list of settings changed by SettingsMonitor

**1.0.11**
- Reordered addon.xml back to how it was before 1.0.9 due to removal of script.copacetic.helper as a plugin source selectable for widgets.
- Enhanced Slideshow_Monitor class so that it can now fetch fanarts from containers with plugin sources when they are available then use these fanarts in the custom background slideshow. In this way, you can use a custom path to populate a global custom fanart slideshow even without any content in your local library

**1.0.10**
- read_fanart() method added in 1.0.10 now triggers on services monitor initialise rather than the first time that the SlideShow monitor is run so it should display backgrounds slightly quicker

**1.0.10**
- Custom path for Global slideshows can now be refreshed on first entry or on change of path without needing Kodi to restart https://github.com/realcopacetic/script.copacetic.helper/issues/6
- Added new methods to SlideShow monitor class enabling the service monitor to save current global slideshow fanarts to XML on exit, then make these available during initialisation. The aim of this is to serve the last fanart URL from the previous session while new fanarts are being fetched by the slideshow monitor, which should minimise the black-screen delay on starting up Kodi on slower hardware while fanarts are fetched for the first time https://github.com/realcopacetic/script.copacetic.helper/issues/4

**1.0.9**
- Background slideshows from custom paths/playlists are now generated via the background fanart fetching service and available globally throughout the skin, via a window property. Previously this was done in-skin using a container so would not be available persistently across windows.
- Removed glitch in background slideshows causing them to fetch new fanarts too often
Expand All @@ -21,9 +35,6 @@ All code contained in this project is licensed under GPL 3.0.
- Switched incorrect labels Enabled/Disabled for script enabler toggle function
- Reordered addon.xml extension points to update how addon is categorised in Kodi repository

TO DO
- Add check of json repsonse before notifying of addon status toggle success

**1.0.8**
- Push dbid of corresponding cropped clearlogo to window prop for comparison so cropped clearlogos only show on correct listitems.

Expand Down
4 changes: 2 additions & 2 deletions script.copacetic.helper/addon.xml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<addon id="script.copacetic.helper" name="Copacetic Helper" version="1.0.9" provider-name="realcopacetic">
<addon id="script.copacetic.helper" name="Copacetic Helper" version="1.0.12" provider-name="realcopacetic">
<requires>
<import addon="xbmc.python" version="3.0.1" />
<import addon="script.module.pil" version="5.1.0" />
</requires>
<extension point="xbmc.service" library="service.py" />
<extension point="xbmc.python.pluginsource" library="plugin.py">
<provides>video</provides>
</extension>
<extension point="xbmc.service" library="service.py" />
<extension point="xbmc.python.script" library="script.py">
<provides>executable</provides>
</extension>
Expand Down
143 changes: 98 additions & 45 deletions script.copacetic.helper/resources/lib/service/art.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@

from resources.lib.utilities import (CROPPED_FOLDERPATH, LOOKUP_XML,
TEMP_FOLDERPATH, condition, infolabel,
json_call, log, os, skin_string,
validate_path, window_property, xbmc,
xbmcvfs)
json_call, log, os, validate_path,
window_property, xbmc, xbmcvfs)


class ImageEditor():
Expand Down Expand Up @@ -215,66 +214,108 @@ def _return_average_color(self, image):

class SlideshowMonitor:
def __init__(self):
self.lookup = LOOKUP_XML
self.art = {}
self.art_types = ['global', 'movies',
'tvshows', 'videos', 'artists', 'custom']
self.on_next_run_flag = True
self.custom_path = infolabel(
'Skin.String(Background_Slideshow_Custom_Path)')
self.refresh_count = self.refresh_interval = self._get_refresh_interval()
self.fetch_count = self.fetch_interval = self.refresh_interval * 40

def background_slideshow(self):
# Check if refresh interval has been adjusted in skin settings
# If refresh interval has been adjusted in skin settings
if self.refresh_interval != self._get_refresh_interval():
self.refresh_interval = self._get_refresh_interval()
self.fetch_interval = self.refresh_interval * 40
# Fech art every 40 x refresh interval
if self.fetch_count >= self.fetch_interval:

# Capture plugin art if it's available and on_next_run flag is true
if condition(
'Integer.IsGreater(Container(3300).NumItems,0)'
) and 'plugin://' in self.custom_path and self.on_next_run_flag:
self._get_plugin_arts()

# Fech art every 40 x refresh interval, reset if custom path changes
if self.fetch_count >= self.fetch_interval or self.custom_path != infolabel('Skin.String(Background_Slideshow_Custom_Path)'):
self.custom_path = infolabel(
'Skin.String(Background_Slideshow_Custom_Path)')
if 'plugin://' in self.custom_path and not self.on_next_run_flag:
self.on_next_run_flag = True
log('Monitor fetching background art')
self.art = self._get_art()
self.fetch_count = 1
else:
self.fetch_count += 1

# Set art every refresh interval
if self.refresh_count >= self.refresh_interval:
if self.art.get('all'):
self._set_art('Background_Global', self.art['all'])
if self.art.get('movies'):
self._set_art('Background_Movies', self.art['movies'])
if self.art.get('tvshows'):
self._set_art('Background_TVShows', self.art['tvshows'])
if self.art.get('videos'):
self._set_art('Background_Videos', self.art['videos'])
if self.art.get('artists'):
self._set_art('Background_Artists', self.art['artists'])
if self.art.get('custom'):
self._set_art('Background_Custom', self.art['custom'])
for type in self.art_types:
if self.art.get(type):
self._set_art(f'background_{type}', self.art[type])
self.refresh_count = 1
else:
self.refresh_count += 1

def read_fanart(self):
lookup_tree = ET.parse(self.lookup)
root = lookup_tree.getroot()
for type in self.art_types:
# try search on background tag, if it doesn't exist then create it at root level
try:
for node in root.find('backgrounds'):
if type in node.attrib['type'] and validate_path(node.find('path').text):
path = node.find('path').text
window_property(f'background_{type}_fanart', set=path)
except TypeError:
ET.SubElement(root, 'backgrounds')
lookup_tree.write(self.lookup, encoding="utf-8")

def _get_refresh_interval(self):
try:
self.refresh_interval_check = int(
infolabel('Skin.String(Background_Interval)')
)
except ValueError:
self.refresh_interval_check = 10
return self.refresh_interval_check
def write_art(self):
lookup_tree = ET.parse(self.lookup)
root = lookup_tree.getroot()
for type in self.art_types:
current_fanart = infolabel(f'Window(home).Property(background_{type}_fanart)')
for node in root.find('backgrounds'):
if type in node.attrib['type']:
background = node.find('path')
background.text = current_fanart
break
else:
background = ET.SubElement(root.find('backgrounds'), 'background')
background.attrib['type'] = type
path = ET.SubElement(background, 'path')
path.text = current_fanart
lookup_tree.write(self.lookup, encoding="utf-8")

def _get_plugin_arts(self):
if self.on_next_run_flag:
self.art['custom'] = []
num_items = int(infolabel('Container(3300).NumItems'))
for i in range(num_items):
item = {
'title': infolabel(
f'Container(3300).ListItem({i}).Label'),
'fanart': infolabel(
f'Container(3300).ListItem({i}).Art(fanart)'),
'clearlogo': infolabel(
f'Container(3300).ListItem({i}).Art(clearlogo)')
}
if item['fanart']:
self.art['custom'].append(item)
self.on_next_run_flag = False

def _get_art(self):
self.art = {}
self.art['movies'] = []
self.art['tvshows'] = []
self.art['artists'] = []
self.art['videos'] = []
self.art['all'] = []
self.art['custom'] = []

for type in self.art_types:
self.art[type] = []

# Populate custom path/playlist slideshow if selected in skin settings
custom_path = infolabel(
'Skin.String(Background_Slideshow_Custom_Path)')
if custom_path and condition('Skin.String(Background_Slideshow,Custom)'):
if self.custom_path and 'plugin://' not in self.custom_path and condition('Skin.String(Background_Slideshow,Custom)'):
query = json_call('Files.GetDirectory',
params={'directory': custom_path},
params={'directory': self.custom_path},
sort={'method': 'random'},
limit=40, parent='get_directory')

try:
for result in query['result']['files']:
type = result['type']
Expand All @@ -292,7 +333,7 @@ def _get_art(self):
except KeyError:
pass

# Populate global slidshows
# Populate video and music slidshows from library
for item in ['movies', 'tvshows', 'artists']:
dbtype = 'Video' if item != 'artists' else 'Audio'
query = json_call(f'{dbtype}Library.Get{item}', properties=['art'], sort={
Expand All @@ -305,13 +346,23 @@ def _get_art(self):
self.art[item].append(data)
except KeyError:
pass

self.art['videos'] = self.art['movies'] + self.art['tvshows']

# Populate global slideshow
for list in self.art:
if self.art[list]:
self.art['all'] = self.art['all'] + self.art[list]
self.art['global'] = self.art['global'] + self.art[list]
return self.art

def _get_refresh_interval(self):
try:
self.refresh_interval_check = int(
infolabel('Skin.String(Background_Interval)')
)
except ValueError:
self.refresh_interval_check = 10
return self.refresh_interval_check

def _set_art(self, key, items):
art = random.choice(items)
art.pop('set.fanart', None)
Expand All @@ -320,15 +371,17 @@ def _set_art(self, key, items):
key, value) in art.items() if 'fanart' in key}
fanart = random.choice(list(fanarts.values()))
fanart = self._url_decode_path(fanart)
window_property(f'{key}_Fanart', set=fanart)
window_property(f'{key}_fanart', set=fanart)
# clearlogo if present otherwise clear
clearlogo = art.get('clearlogo', False)
if clearlogo:
clearlogo = self._url_decode_path(clearlogo)
window_property(f'{key}_Clearlogo', set=clearlogo)
window_property(f'{key}_clearlogo', set=clearlogo)
# title
window_property(f'{key}_title', set=art.get('title', False))

def _url_decode_path(self, path):
path = path[:-1] if path.endswith('/') else path
path = path.replace('image://', '')
path = urllib.unquote(path.replace('image://', ''))
return path
return path
12 changes: 5 additions & 7 deletions script.copacetic.helper/resources/lib/service/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

XMLSTR = '''<?xml version="1.0" encoding="utf-8"?>
<data>
<backgrounds />
<clearlogos />
</data>
'''
Expand Down Expand Up @@ -56,6 +57,7 @@ def _on_start(self):
log('Monitor started', force=True)
self.start = False
self.player_monitor = PlayerMonitor()
self.art_monitor.read_fanart()
else:
log('Monitor resumed', force=True) if self._conditions_met() else None
while not self.abortRequested() and self._conditions_met():
Expand All @@ -64,11 +66,7 @@ def _on_start(self):

def _conditions_met(self):
return (
self._get_skindir() and not self.idle and
(
condition('!Skin.HasSetting(Background_Disabled)') or
condition('Skin.HasSetting(Crop_Clearlogos)')
)
self._get_skindir() and not self.idle
)

def _get_skindir(self):
Expand Down Expand Up @@ -148,8 +146,7 @@ def poller(self):

# slideshow window is visible run SlideshowMonitor()
elif condition(
'!Skin.HasSetting(Background_Disabled) + ['
'Window.IsVisible(home) | '
'[Window.IsVisible(home) | '
'Window.IsVisible(skinsettings) | '
'Window.IsVisible(appearancesettings) | '
'Window.IsVisible(mediasettings) | '
Expand Down Expand Up @@ -231,6 +228,7 @@ def _on_stop(self):
if not self.abortRequested():
self._on_start()
else:
self.art_monitor.write_art()
del self.player_monitor
del self.settings_monitor
del self.art_monitor
Expand Down
2 changes: 1 addition & 1 deletion script.copacetic.helper/resources/lib/service/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from resources.lib.script.actions import clean_filename
from resources.lib.service.art import ImageEditor
from resources.lib.utilities import condition, window_property
from resources.lib.utilities import condition, json_call, log, window_property


class PlayerMonitor(Player):
Expand Down
3 changes: 1 addition & 2 deletions script.copacetic.helper/resources/lib/service/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ def __init__(self):
'videolibrary.tvshowartwhitelist': ['keyart', 'square', 'clearlogo', 'clearlogo-alt', 'clearlogo-billboard'],
'musiclibrary.showallitems': False,
'musiclibrary.showcompilationartists': False,
'pictures.generatethumbs': True,
'musicplayer.visualisation': 'visualization.waveform'
'pictures.generatethumbs': True
}
self.settings_to_change = {}

Expand Down
Loading