Skip to content

Commit

Permalink
Merge pull request #2610 from bossanova808/script.service.playbackres…
Browse files Browse the repository at this point in the history
…umer@matrix

[script.service.playbackresumer@matrix] 2.0.6
  • Loading branch information
basrieter authored May 4, 2024
2 parents 0cf9b0e + b3d5f00 commit 156fea7
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 58 deletions.
6 changes: 2 additions & 4 deletions script.service.playbackresumer/addon.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="script.service.playbackresumer" name="Kodi Playback Resumer" version="2.0.5" provider-name="bossanova808, bradvido88">
<addon id="script.service.playbackresumer" name="Kodi Playback Resumer" version="2.0.6" provider-name="bossanova808, bradvido88">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
</requires>
Expand All @@ -15,9 +15,7 @@
<source>https://github.com/bossanova808/script.service.playbackresumer</source>
<forum>https://forum.kodi.tv/showthread.php?tid=355383</forum>
<email>bossanova808@gmail.com</email>
<news>v2.0.5
- Fix odd reported bug with resume points
</news>
<news>v2.0.6 - Add support for non-library videos</news>
<assets>
<icon>icon.png</icon>
</assets>
Expand Down
4 changes: 4 additions & 0 deletions script.service.playbackresumer/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
v2.0.6

- Add support for non library videos

v2.0.5
- Fix wierd bug with resume points: https://forum.kodi.tv/showthread.php?tid=355383&pid=3163480#pid3163480

Expand Down
72 changes: 50 additions & 22 deletions script.service.playbackresumer/resources/lib/common.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
# -*- coding: utf-8 -*-

"""
Handy utility functions for Kodi Addons
By bossanova808
Free in all senses....
VERSION 0.2.3 2021-06-21
(For Kodi Matrix & later)
Handy utility functions & constants for Kodi Addons
For Kodi Matrix & later
By bossanova808 - freely released
VERSION 0.2.7 2024-04-19
Changelog:
0.2.7 - Fix getting the major Kodi version (& change float -> int), as it was failing on e.g. 'RC' being in the string apparently
0.2.6 - (SkinPatcher) - add float KODI_VERSION_FLOAT constant, alongside string KODI_VERSION
0.2.5 - (Skin) - move to storing copy of latest in bossanova808 repo and adding this mini changelog
For latest version - ALWAYS COPY BACK ANY CHANGES, plus do changelog, and a version & date bump above:
https://github.com/bossanova808/repository.bossanova808/blob/main/latest-common/common.py
"""

import sys
import traceback

import xbmc
import xbmcvfs
import xbmcgui
import xbmcaddon
import json


ADDON = xbmcaddon.Addon()
ADDON_NAME = ADDON.getAddonInfo('name')
Expand All @@ -25,6 +39,7 @@
LANGUAGE = ADDON.getLocalizedString
PROFILE = xbmcvfs.translatePath(ADDON.getAddonInfo('profile'))
KODI_VERSION = xbmc.getInfoLabel('System.BuildVersion')
KODI_VERSION_INT = int(KODI_VERSION.split(".")[0])
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
HOME_WINDOW = xbmcgui.Window(10000)
WEATHER_WINDOW = xbmcgui.Window(12600)
Expand All @@ -49,6 +64,8 @@
"""

unit_testing = False

# Testing outside of Kodi
if not xbmc.getUserAgent():

xbmc = None
Expand All @@ -63,6 +80,7 @@ def log(message, exception_instance=None, level=None):
print(f'EXCPT: {traceback.format_exc(exception_instance)}')


# Running inside of Kodi
else:

def log(message, exception_instance=None, level=xbmc.LOGDEBUG):
Expand Down Expand Up @@ -105,42 +123,43 @@ def set_property(window, name, value=""):
def get_property(window, name):
"""
Return the value of a window property
@param window:
@param name:
@return:
:param window: the Kodi window to get the property value from
:param name: the name of the property to get
:return: the value of the window property
"""
return window.getProperty(name)


def get_property_as_bool(window, name):
"""
Return the value of a window property as a boolean
@param window:
@param name:
@return:
:param window: the Kodi window to get the property value from
:param name: the name of the property to get
:return: the value of the window property in boolean form
"""
return window.getProperty(name).lower() == "true"


def send_kodi_json(human_description, json_string):
"""
Send a JSON command to Kodi, logging the human description, command, and result returned.
Send a JSON command to Kodi, logging the human description, command, and result as returned.
:param human_description: Required. A human sensible description of what the command is aiming to do/retrieve.
:param json_string: Required. The json command to send.
:return the json object loaded from the result string
"""
log(f'KODI JSON RPC command: {human_description} [{json_string}]')
result = xbmc.executeJSONRPC(json_string)
log(f'KODI JSON RPC result: {result}')
return result
return json.loads(result)


def get_setting(setting):
"""
Helper function to get string type from settings
@param setting:
@return: setting value
:param setting: The addon setting to return
:return: the setting value
"""
return ADDON.getSetting(setting).strip()

Expand All @@ -149,8 +168,8 @@ def get_setting_as_bool(setting):
"""
Helper function to get bool type from settings
@param setting:
@return: setting value as boolen
:param setting: The addon setting to return
:return: the setting value as boolean
"""
return get_setting(setting).lower() == "true"

Expand All @@ -159,10 +178,10 @@ def notify(message, notification_type=xbmcgui.NOTIFICATION_ERROR, duration=5000)
"""
Send a notification to the user via the Kodi GUI
@param message: the message to send
@param notification_type: xbmcgui.NOTIFICATION_ERROR (default), xbmcgui.NOTIFICATION_WARNING, or xbmcgui.NOTIFICATION_INFO
@param duration: time to display notification in milliseconds, default 5000
@return: None
:param message: the message to send
:param notification_type: xbmcgui.NOTIFICATION_ERROR (default), xbmcgui.NOTIFICATION_WARNING, or xbmcgui.NOTIFICATION_INFO
:param duration: time to display notification in milliseconds, default 5000
:return: None
"""
dialog = xbmcgui.Dialog()

Expand All @@ -171,6 +190,15 @@ def notify(message, notification_type=xbmcgui.NOTIFICATION_ERROR, duration=5000)
notification_type,
duration)

def is_playback_paused():
"""
Helper function to return Kodi player state.
(Odd this is needed, it should be a testable state on Player really..)
:return: boolean indicating player paused state
"""
return bool(xbmc.getCondVisibility("Player.Paused"))


def footprints(startup=True):
"""
Expand All @@ -180,7 +208,7 @@ def footprints(startup=True):
"""
if startup:
log(f'Starting...', level=xbmc.LOGINFO)
log(f'Kodi Version: {KODI_VERSION}', level=xbmc.LOGINFO)
log(f'Kodi System.BuildVersion: {KODI_VERSION}, which is Kodi major version: {KODI_VERSION_INT}', level=xbmc.LOGINFO)
log(f'Addon arguments: {ADDON_ARGUMENTS}', level=xbmc.LOGINFO)
else:
log(f'Exiting...', level=xbmc.LOGINFO)
Expand Down
73 changes: 49 additions & 24 deletions script.service.playbackresumer/resources/lib/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ def update_resume_point(self, seconds):

# Short circuits

# No library ID or weird library ID
if not Store.library_id or Store.library_id < 0:
log(f"No/invalid library id ({Store.library_id}) for {Store.currently_playing_file_path} so can't set a resume point")
# Weird library ID
if Store.library_id and Store.library_id < 0:
log(f"No/invalid library id ({Store.library_id}) for {Store.currently_playing_file_path}")
return
# Kodi doing its normal stopping thing
if seconds == -2:
Expand All @@ -150,52 +150,77 @@ def update_resume_point(self, seconds):

# Log what we are doing
if seconds == 0:
log(f'Removing resume point for {Store.type_of_video} id {Store.library_id}')
log(f'Removing resume point for: {Store.currently_playing_file_path}, type: {Store.type_of_video}, library id: {Store.library_id}')
else:
log(f'Setting resume point for {Store.type_of_video} id {Store.library_id} to {seconds} seconds')
log(f'Setting resume point for: {Store.currently_playing_file_path}, type: {Store.type_of_video}, library id: {Store.library_id}, to: {seconds} seconds')

# Determine the JSON-RPC setFooDetails method to use and what the library id name is based of the type of video
id_name = None
if Store.type_of_video == 'episode':
method = 'SetEpisodeDetails'
get_method = 'GetEpisodeDetails'
method = 'VideoLibrary.SetEpisodeDetails'
get_method = 'VideoLibrary.GetEpisodeDetails'
id_name = 'episodeid'
elif Store.type_of_video == 'movie':
method = 'SetMovieDetails'
get_method = 'GetMovieDetails'
method = 'VideoLibrary.SetMovieDetails'
get_method = 'VideoLibrary.GetMovieDetails'
id_name = 'movieid'
elif Store.type_of_video == 'musicvideo':
method = 'SetMusicVideoDetails'
get_method = 'GetMusicVideoDetails'
method = 'VideoLibrary.SetMusicVideoDetails'
get_method = 'VideoLibrary.GetMusicVideoDetails'
id_name = 'musicvideoid'
else:
log(f'Can\'t update resume point as did not recognise type of video [{Store.type_of_video}]')
return
log(f'Did not recognise type of video [{Store.type_of_video}] - assume non-library video')
method = 'Files.SetFileDetails'
get_method = 'Files.GetFileDetails'

query = json.dumps({
json_dict = {
"jsonrpc": "2.0",
"id": "setResumePoint",
"method": "VideoLibrary." + method,
"params": {
"method": method,
}
if id_name:
params = {
id_name: Store.library_id,
"resume": {
"position": seconds,
"total": Store.length_of_currently_playing_file
}
}
})
send_kodi_json(f'Set resume point to {seconds}, total to {Store.length_of_currently_playing_file}', query)
else:
params = {
"file": Store.currently_playing_file_path,
"media": "video",
"resume": {
"position": seconds,
"total": Store.length_of_currently_playing_file
}
}

json_dict['params'] = params
query = json.dumps(json_dict)
send_kodi_json(f'Set resume point for: {Store.currently_playing_file_path}, type: {Store.type_of_video}, id: {Store.library_id}, to: {seconds} seconds, total: {Store.length_of_currently_playing_file}', query)

# For debugging - let's retrieve and log the current resume point...
query = json.dumps({
# For debugging - let's retrieve and log the current resume point to check it was actually set as intended...
json_dict = {
"jsonrpc": "2.0",
"id": "getResumePoint",
"method": "VideoLibrary." + get_method,
"params": {
"method": get_method,
}
if id_name:
params = {
id_name: Store.library_id,
"properties": ["resume"],
}
})
send_kodi_json(f'Check new resume point & total for id {Store.library_id}', query)
else:
params = {
"file": Store.currently_playing_file_path,
"media": "video",
"properties": ["resume"],
}

json_dict['params'] = params
query = json.dumps(json_dict)
send_kodi_json(f'Check new resume point & total for: {Store.currently_playing_file_path}, type: {Store.type_of_video}, id: {Store.library_id}', query)

def resume_if_was_playing(self):
"""
Expand Down
13 changes: 5 additions & 8 deletions script.service.playbackresumer/resources/lib/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Store:
# Store the full path of the currently playing file
currently_playing_file_path = ''
# What type of video is it? episode, movie, musicvideo
type_of_video = 'unknown'
type_of_video = None
# What is the library id of this video, if there is one?
library_id = -1
# if the video was paused, at what time was it paused?
Expand Down Expand Up @@ -194,18 +194,15 @@ def update_current_playing_file_path(filepath):
json_response = json.loads(xbmc.executeJSONRPC(json.dumps(query)))
log(f'JSON-RPC Files.GetFileDetails response: {json.dumps(json_response)}')

Store.type_of_video = 'unknown'

try:
Store.type_of_video = json_response['result']['filedetails']['type']
except:
except KeyError:
Store.library_id = -1
log(f'Error determining type of video; probably not in Kodi\'s library: {Store.currently_playing_file_path}')
log(f"ERROR: Kodi did not return even an 'unknown' file type for: {Store.currently_playing_file_path}")

if Store.type_of_video == 'episode' or Store.type_of_video == 'movie' or Store.type_of_video == 'musicvideo':
if Store.type_of_video in ['episode', 'movie', 'musicvideo']:
Store.library_id = json_response['result']['filedetails']['id']
log(f'The library id for this {Store.type_of_video} is {Store.library_id}')
else:
Store.library_id = None
log(f'Unsupported type of video {Store.type_of_video} for {Store.currently_playing_file_path}')

log(f'Kodi type: {Store.type_of_video}, library id: {Store.library_id}')

0 comments on commit 156fea7

Please sign in to comment.