Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6121537216 | ||
|
|
2482f11a5a | ||
|
|
ade08f74a4 | ||
|
|
5eade9abe5 | ||
|
|
203986d54c | ||
|
|
8e8c376df3 |
@@ -1,70 +0,0 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
import sys
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
def indent(elem, level=0):
|
||||
'''
|
||||
Nicely formats output xml with newlines and spaces
|
||||
https://stackoverflow.com/a/33956544
|
||||
'''
|
||||
i = "\n" + level*" "
|
||||
if len(elem):
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = i + " "
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
for elem in elem:
|
||||
indent(elem, level+1)
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
else:
|
||||
if level and (not elem.tail or not elem.tail.strip()):
|
||||
elem.tail = i
|
||||
|
||||
|
||||
try:
|
||||
py_version = sys.argv[1]
|
||||
except IndexError:
|
||||
print('No version specified')
|
||||
sys.exit(1)
|
||||
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
# Load template file
|
||||
with open('{dir_path}/template.xml'.format(**locals()), 'r') as f:
|
||||
tree = ET.parse(f)
|
||||
root = tree.getroot()
|
||||
|
||||
# Load version dependencies
|
||||
with open('{dir_path}/{py_version}.yaml'.format(**locals()), 'r') as f:
|
||||
deps = yaml.safe_load(f)
|
||||
|
||||
# Load version and changelog
|
||||
with open('jellyfin-kodi/release.yaml', 'r') as f:
|
||||
data = yaml.safe_load(f)
|
||||
|
||||
# Populate xml template
|
||||
for dep in deps:
|
||||
ET.SubElement(root.find('requires'), 'import', attrib=dep)
|
||||
|
||||
# Update version string
|
||||
addon_version = data.get('version')
|
||||
root.attrib['version'] = '{addon_version}+{py_version}'.format(**locals())
|
||||
|
||||
# Changelog
|
||||
date = datetime.today().strftime('%Y-%m-%d')
|
||||
changelog = data.get('changelog')
|
||||
for section in root.findall('extension'):
|
||||
news = section.findall('news')
|
||||
if news:
|
||||
news[0].text = 'v{addon_version} ({date}):\n{changelog}'.format(**locals())
|
||||
|
||||
# Format xml tree
|
||||
indent(root)
|
||||
|
||||
# Write addon.xml
|
||||
tree.write('jellyfin-kodi/addon.xml', encoding='utf-8', xml_declaration=True)
|
||||
74
build.py
74
build.py
@@ -1,28 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
import os
|
||||
from pathlib import Path
|
||||
import xml.etree.ElementTree as ET
|
||||
import zipfile
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
def indent(elem, level=0):
|
||||
'''
|
||||
def indent(elem: ET.Element, level: int = 0) -> None:
|
||||
"""
|
||||
Nicely formats output xml with newlines and spaces
|
||||
https://stackoverflow.com/a/33956544
|
||||
'''
|
||||
i = "\n" + level*" "
|
||||
"""
|
||||
i = "\n" + level * " "
|
||||
if len(elem):
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = i + " "
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
for elem in elem:
|
||||
indent(elem, level+1)
|
||||
indent(elem, level + 1)
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
else:
|
||||
@@ -30,10 +30,10 @@ def indent(elem, level=0):
|
||||
elem.tail = i
|
||||
|
||||
|
||||
def create_addon_xml(config, source, py_version):
|
||||
'''
|
||||
def create_addon_xml(config: dict, source: str, py_version: str) -> None:
|
||||
"""
|
||||
Create addon.xml from template file
|
||||
'''
|
||||
"""
|
||||
# Load template file
|
||||
with open('{}/.config/template.xml'.format(source), 'r') as f:
|
||||
tree = ET.parse(f)
|
||||
@@ -63,21 +63,54 @@ def create_addon_xml(config, source, py_version):
|
||||
tree.write('{}/addon.xml'.format(source), encoding='utf-8', xml_declaration=True)
|
||||
|
||||
|
||||
def zip_files(py_version, source, target):
|
||||
'''
|
||||
def zip_files(py_version: str, source: str, target: str, dev: bool) -> None:
|
||||
"""
|
||||
Create installable addon zip archive
|
||||
'''
|
||||
"""
|
||||
archive_name = 'plugin.video.jellycon+{}.zip'.format(py_version)
|
||||
|
||||
with zipfile.ZipFile('{}/{}'.format(target, archive_name), 'w') as z:
|
||||
for root, dirs, files in os.walk(args.source):
|
||||
for filename in files:
|
||||
if 'plugin.video.jellycon' not in filename and 'pyo' not in filename:
|
||||
file_path = os.path.join(root, filename)
|
||||
for filename in filter(file_filter, files):
|
||||
file_path = os.path.join(root, filename)
|
||||
if dev or folder_filter(file_path):
|
||||
relative_path = os.path.join('plugin.video.jellycon', os.path.relpath(file_path, source))
|
||||
z.write(file_path, relative_path)
|
||||
|
||||
|
||||
def file_filter(file_name: str) -> bool:
|
||||
"""
|
||||
True if file_name is meant to be included
|
||||
"""
|
||||
return (
|
||||
not (file_name.startswith('plugin.video.jellycon') and file_name.endswith('.zip'))
|
||||
and not file_name.endswith('.pyo')
|
||||
and not file_name.endswith('.pyc')
|
||||
and not file_name.endswith('.pyd')
|
||||
)
|
||||
|
||||
|
||||
def folder_filter(folder_name: str) -> bool:
|
||||
"""
|
||||
True if folder_name is meant to be included
|
||||
"""
|
||||
filters = [
|
||||
'.ci',
|
||||
'.git',
|
||||
'.github',
|
||||
'.config',
|
||||
'.mypy_cache',
|
||||
'.pytest_cache',
|
||||
'__pycache__',
|
||||
]
|
||||
for f in filters:
|
||||
if f in folder_name.split(os.path.sep):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Build flags:')
|
||||
parser.add_argument(
|
||||
@@ -96,13 +129,16 @@ if __name__ == '__main__':
|
||||
type=Path,
|
||||
default=Path(__file__).absolute().parent)
|
||||
|
||||
parser.add_argument('--dev', dest='dev', action='store_true')
|
||||
parser.set_defaults(dev=False)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Load config file
|
||||
config_path = os.path.join(args.source, 'release.yaml')
|
||||
with open(config_path, 'r') as fh:
|
||||
config = yaml.safe_load(fh)
|
||||
release_config = yaml.safe_load(fh)
|
||||
|
||||
create_addon_xml(config, args.source, args.version)
|
||||
create_addon_xml(release_config, args.source, args.version)
|
||||
|
||||
zip_files(args.version, args.source, args.target)
|
||||
zip_files(args.version, args.source, args.target, args.dev)
|
||||
|
||||
@@ -471,7 +471,7 @@ def add_gui_item(url, item_details, display_options, folder=True, default_sort=F
|
||||
info_labels = {}
|
||||
|
||||
# add cast
|
||||
if item_details.cast is not None:
|
||||
if item_details.cast:
|
||||
if kodi_version >= 17:
|
||||
list_item.setCast(item_details.cast)
|
||||
else:
|
||||
|
||||
@@ -877,8 +877,7 @@ def external_subs(media_source, list_item, item_id):
|
||||
|
||||
def send_progress():
|
||||
home_window = HomeWindow()
|
||||
play_data_string = home_window.get_property('now_playing')
|
||||
play_data = json.loads(play_data_string)
|
||||
play_data = get_playing_data()
|
||||
|
||||
if play_data is None:
|
||||
return
|
||||
@@ -1011,7 +1010,15 @@ def prompt_for_stop_actions(item_id, data):
|
||||
xbmc.executebuiltin("Container.Refresh")
|
||||
|
||||
|
||||
def stop_all_playback(played_information):
|
||||
def stop_all_playback():
|
||||
|
||||
home_window = HomeWindow()
|
||||
played_information_string = home_window.get_property('played_information')
|
||||
if played_information_string:
|
||||
played_information = json.loads(played_information_string)
|
||||
else:
|
||||
played_information = {}
|
||||
|
||||
log.debug("stop_all_playback : {0}".format(played_information))
|
||||
|
||||
if len(played_information) == 0:
|
||||
@@ -1020,8 +1027,7 @@ def stop_all_playback(played_information):
|
||||
log.debug("played_information: {0}".format(played_information))
|
||||
clear_entries = []
|
||||
|
||||
home_screen = HomeWindow()
|
||||
home_screen.clear_property("currently_playing_id")
|
||||
home_window.clear_property("currently_playing_id")
|
||||
|
||||
for item in played_information:
|
||||
data = played_information.get(item)
|
||||
@@ -1061,33 +1067,53 @@ def stop_all_playback(played_information):
|
||||
for entry in clear_entries:
|
||||
del played_information[entry]
|
||||
|
||||
home_window.set_property('played_information', json.dumps(played_information))
|
||||
|
||||
|
||||
def get_playing_data():
|
||||
player = xbmc.Player()
|
||||
home_window = HomeWindow()
|
||||
play_data_string = home_window.get_property('now_playing')
|
||||
play_data = json.loads(play_data_string)
|
||||
|
||||
played_information_string = home_window.get_property('played_information')
|
||||
if played_information_string:
|
||||
played_information = json.loads(played_information_string)
|
||||
else:
|
||||
played_information = {}
|
||||
|
||||
playlist_data_string = home_window.get_property('playlist')
|
||||
playlist_data = json.loads(playlist_data_string)
|
||||
if playlist_data_string:
|
||||
playlist_data = json.loads(playlist_data_string)
|
||||
else:
|
||||
playlist_data = {}
|
||||
|
||||
item_id = play_data.get("item_id")
|
||||
|
||||
settings = xbmcaddon.Addon()
|
||||
server = settings.getSetting('server_address')
|
||||
try:
|
||||
playing_file = xbmc.Player().getPlayingFile()
|
||||
playing_file = player.getPlayingFile()
|
||||
except Exception as e:
|
||||
log.error("get_playing_data : getPlayingFile() : {0}".format(e))
|
||||
return None
|
||||
log.debug("get_playing_data : getPlayingFile() : {0}".format(playing_file))
|
||||
if server in playing_file:
|
||||
if item_id is not None and item_id in playing_file:
|
||||
return play_data
|
||||
elif item_id is not None and item_id not in playing_file and playing_file in playlist_data:
|
||||
if server in playing_file and item_id is not None:
|
||||
play_time = player.getTime()
|
||||
total_play_time = player.getTotalTime()
|
||||
|
||||
if item_id is not None and item_id not in playing_file and playing_file in playlist_data:
|
||||
# if the current now_playing data isn't correct, pull it from the playlist_data
|
||||
play_data = playlist_data.pop(playing_file)
|
||||
# Update now_playing data
|
||||
home_window.set_property('now_playing', json.dumps(play_data))
|
||||
home_window.set_property('playlist', json.dumps(playlist_data))
|
||||
return play_data
|
||||
|
||||
play_data["current_position"] = play_time
|
||||
play_data["duration"] = total_play_time
|
||||
played_information[item_id] = play_data
|
||||
home_window.set_property('now_playing', json.dumps(play_data))
|
||||
home_window.set_property('played_information', json.dumps(played_information))
|
||||
return play_data
|
||||
|
||||
return {}
|
||||
|
||||
@@ -1096,11 +1122,10 @@ class Service(xbmc.Player):
|
||||
|
||||
def __init__(self, *args):
|
||||
log.debug("Starting monitor service: {0}".format(args))
|
||||
self.played_information = {}
|
||||
|
||||
def onPlayBackStarted(self):
|
||||
# Will be called when xbmc starts playing a file
|
||||
stop_all_playback(self.played_information)
|
||||
stop_all_playback()
|
||||
|
||||
if not xbmc.Player().isPlaying():
|
||||
log.debug("onPlayBackStarted: not playing file!")
|
||||
@@ -1123,7 +1148,12 @@ class Service(xbmc.Player):
|
||||
if jellyfin_item_id is None:
|
||||
return
|
||||
|
||||
self.played_information[jellyfin_item_id] = play_data
|
||||
home_window = HomeWindow()
|
||||
played_information_string = home_window.get_property('played_information')
|
||||
played_information = json.loads(played_information_string)
|
||||
played_information[jellyfin_item_id] = play_data
|
||||
home_window.set_property('played_information', json.dumps(played_information))
|
||||
|
||||
log.debug("Sending Playback Started")
|
||||
postdata = {
|
||||
'QueueableMediaTypes': "Video",
|
||||
@@ -1145,12 +1175,12 @@ class Service(xbmc.Player):
|
||||
def onPlayBackEnded(self):
|
||||
# Will be called when kodi stops playing a file
|
||||
log.debug("onPlayBackEnded")
|
||||
stop_all_playback(self.played_information)
|
||||
stop_all_playback()
|
||||
|
||||
def onPlayBackStopped(self):
|
||||
# Will be called when user stops kodi playing a file
|
||||
log.debug("onPlayBackStopped")
|
||||
stop_all_playback(self.played_information)
|
||||
stop_all_playback()
|
||||
|
||||
def onPlayBackPaused(self):
|
||||
# Will be called when kodi pauses the video
|
||||
|
||||
Reference in New Issue
Block a user