Compare commits

...

25 Commits

Author SHA1 Message Date
JonnyWong16
3742f33d08 v1.4.16 2016-11-25 18:47:22 -08:00
JonnyWong16
82ac33dd75 Fix websocket for new json response on PMS 1.3.0 2016-11-25 18:42:23 -08:00
JonnyWong16
8c7c0101cd Dynamically update stream and transcoder tooltip percent 2016-11-14 21:10:48 -08:00
JonnyWong16
4d6179dfdd Fix typo 2016-11-14 20:49:01 -08:00
JonnyWong16
ef85fba2e5 v1.4.15 2016-11-11 13:08:13 -08:00
JonnyWong16
960b601384 Remove error cmd from API 2016-11-11 12:59:35 -08:00
JonnyWong16
21d9091b43 Merge pull request #898 from Hellowlol/ap_fix
fix result type, fixup responses from the ui to api
2016-11-11 12:48:18 -08:00
Hellowlol
e881c32797 Update md add exception traceback to the browser 2016-11-05 22:24:37 +01:00
JonnyWong16
57f1af05f5 Redirect to proper HTTP root on state change 2016-11-05 12:13:53 -07:00
JonnyWong16
254f41a2cc Fix line breaks in info page summaries 2016-11-05 11:50:41 -07:00
JonnyWong16
59ce3404c9 Fix Plex.tv authentication with special characters 2016-11-05 11:48:53 -07:00
Hellowlol
11c7342299 fix result type, fixup responses from the ui to api
todo unify json responses from webserve.py
2016-11-04 00:52:01 +01:00
JonnyWong16
8b0959aa69 Fix API SQL command 2016-10-28 20:35:39 -07:00
JonnyWong16
9cd8ed12b9 Add percent to stream and transcoder progress tooltip 2016-10-28 20:34:54 -07:00
JonnyWong16
78b10d7ab5 Use HTTPS for app.plex.tv 2016-10-21 22:45:17 -07:00
JonnyWong16
a322ec2b23 Allow refreshing images if not using sessions 2016-10-16 13:08:36 -07:00
JonnyWong16
1f23654735 Merge pull request #870 from XusBadia/patch-2
Incremented table width to 100%
2016-10-13 21:52:49 -07:00
Xus Badia
e1112b95c7 Changed max-width to accommodate wider displays 2016-10-14 06:11:50 +02:00
JonnyWong16
4e043109bf Temporary fix for info pages with missing metadata
* Will be fixed properly in v2
2016-10-13 20:42:11 -07:00
Xus Badia
58e670443d Incremented table width to 100%
Table width (header and body/back) incremented to 100% but adding max-width so it isn't too wide in wide-screen monitors.
2016-10-13 11:56:02 +02:00
JonnyWong16
86a9230da8 v1.4.14 2016-10-12 21:06:34 -07:00
JonnyWong16
86f84766c1 Allow disable script timeout 2016-10-12 21:00:52 -07:00
JonnyWong16
fdc7078e5c API key readonly instead of disabled 2016-10-12 20:55:32 -07:00
JonnyWong16
c649ebfcc0 Fix typo under Flush Temporary Sessions 2016-10-10 22:47:41 -07:00
JonnyWong16
6aa0d4cd0b Check for metadata before attempting to write session history 2016-10-10 22:46:48 -07:00
18 changed files with 163 additions and 110 deletions

4
API.md
View File

@@ -142,6 +142,10 @@ Returns:
``` ```
### delete_temp_sessions
Flush out all of the temporary sessions in the database.
### delete_user ### delete_user
Delete a user from PlexPy. Also erases all history for the user. Delete a user from PlexPy. Also erases all history for the user.

View File

@@ -1,5 +1,33 @@
# Changelog # Changelog
## v1.4.16 (2016-11-25)
* Fix: Websocket for new json response on PMS 1.3.0.
* Fix: Update stream and transcoder tooltip percent.
* Fix: Typo in the edit user modal.
## v1.4.15 (2016-11-11)
* New: Add stream and transcoder progress percent to the current activity tooltip.
* Fix: Refreshing of images in the cache when authentication is disabled.
* Fix: Plex.tv authentication with special characters in the username or password.
* Fix: Line breaks in the info page summaries.
* Fix: Redirect to the proper http root when restarting.
* Fix: API result type and responses showing incorrectly. (Thanks @Hellowlol)
* Change: Use https URL for app.plex.tv.
* Change: Show API traceback errors in the browser with debugging enabled. (Thanks @Hellowlol)
* Change: Increase table width on mobile devices and max width set to 1750px. (Thanks @XusBadia)
## v1.4.14 (2016-10-12)
* Fix: History logging locking up if media is removed from Plex before PlexPy can save the session.
* Fix: Unable to save API key in the settings.
* Fix: Some typos in the settings. (Thanks @Leafar3456)
* Change: Disable script timeout by setting timeout to 0 seconds.
## v1.4.13 (2016-10-08) ## v1.4.13 (2016-10-08)
* New: Option to set the number of days to keep PlexPy backups. * New: Option to set the number of days to keep PlexPy backups.

View File

@@ -2251,7 +2251,8 @@ a .home-platforms-list-cover-face:hover
margin-right: auto; margin-right: auto;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
width: 90%; width: 100%;
max-width: 1750px;
overflow: hidden; overflow: hidden;
} }
.table-card-header { .table-card-header {
@@ -2263,7 +2264,8 @@ a .home-platforms-list-cover-face:hover
margin-right: auto; margin-right: auto;
margin-top: 20px; margin-top: 20px;
margin-bottom: -20px; margin-bottom: -20px;
width: 90%; width: 100%;
max-width: 1750px;
overflow: hidden; overflow: hidden;
} }
.table-card-back td { .table-card-back td {
@@ -3059,6 +3061,9 @@ a:hover .overlay-refresh-image:hover {
#plex-log-levels label { #plex-log-levels label {
margin-bottom: 0; margin-bottom: 0;
} }
#api_key.form-control[disabled] { #api_key.form-control[readonly] {
background-color: #555; background-color: #555;
} }
#api_key.form-control[readonly]:focus {
background-color: #fff;
}

View File

@@ -207,8 +207,8 @@ DOCUMENTATION :: END
% endif % endif
<div class="dashboard-activity-progress"> <div class="dashboard-activity-progress">
<div class="dashboard-activity-progress-bar"> <div class="dashboard-activity-progress-bar">
<div id="bufferbar-${data['session_key']}" class="bufferbar" style="width: ${data['transcode_progress']}%" data-toggle="tooltip" title="Transcoder Progress">${data['transcode_progress']}%</div> <div id="bufferbar-${data['session_key']}" class="bufferbar" style="width: ${data['transcode_progress']}%" data-toggle="tooltip" title="Transcoder Progress ${data['transcode_progress']}%">${data['transcode_progress']}%</div>
<div id="bar-${data['session_key']}" class="bar" style="width: ${data['progress_percent']}%" data-toggle="tooltip" title="Stream Progress">${data['progress_percent']}%</div> <div id="bar-${data['session_key']}" class="bar" style="width: ${data['progress_percent']}%" data-toggle="tooltip" title="Stream Progress ${data['progress_percent']}%">${data['progress_percent']}%</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -45,7 +45,7 @@ DOCUMENTATION :: END
<input type="text" class="form-control" id="friendly_name" name="friendly_name" value="${data['friendly_name']}" size="30"> <input type="text" class="form-control" id="friendly_name" name="friendly_name" value="${data['friendly_name']}" size="30">
</div> </div>
</div> </div>
<p class="help-block">Replace all occurances of the username with this name.</p> <p class="help-block">Replace all occurrences of the username with this name.</p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="profile_url">Profile Picture URL</label> <label for="profile_url">Profile Picture URL</label>

View File

@@ -204,8 +204,11 @@
// update the progress bars // update the progress bars
// percent - 3 because of 3px padding-right // percent - 3 because of 3px padding-right
$('#bufferbar-' + key).width(parseInt(s.transcode_progress) - 3 + '%').html(s.transcode_progress + '%'); $('#bufferbar-' + key).width(parseInt(s.transcode_progress) - 3 + '%').html(s.transcode_progress + '%')
$('#bar-' + key).width(parseInt(s.progress_percent) - 3 + '%').html(s.progress_percent + '%'); .attr('data-original-title', 'Transcoder Progress ' + s.transcode_progress + '%');
$('#bar-' + key).width(parseInt(s.progress_percent) - 3 + '%').html(s.progress_percent + '%')
.attr('data-original-title', 'Stream Progress ' + s.progress_percent + '%');
// add temporary class so we know which instances are still active // add temporary class so we know which instances are still active
instance.addClass('updated-temp'); instance.addClass('updated-temp');

View File

@@ -53,6 +53,9 @@ DOCUMENTATION :: END
if re.match(pattern, codec): if re.match(pattern, codec):
return file return file
return codec return codec
def br(text):
return text.replace('\n', '<br /><br />')
%> %>
<%inherit file="base.html"/> <%inherit file="base.html"/>
@@ -112,9 +115,9 @@ DOCUMENTATION :: END
<div class="col-md-9"> <div class="col-md-9">
<div class="summary-content-poster hidden-xs hidden-sm"> <div class="summary-content-poster hidden-xs hidden-sm">
% if data['media_type'] == 'track': % if data['media_type'] == 'track':
<a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['parent_rating_key']}" target="_blank" title="View in Plex Web"> <a href="https://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['parent_rating_key']}" target="_blank" title="View in Plex Web">
% else: % else:
<a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['rating_key']}" target="_blank" title="View in Plex Web"> <a href="https://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['rating_key']}" target="_blank" title="View in Plex Web">
% endif % endif
% if data['media_type'] == 'episode': % if data['media_type'] == 'episode':
<div class="summary-poster-face-episode" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=280&fallback=art);"> <div class="summary-poster-face-episode" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=280&fallback=art);">
@@ -250,7 +253,7 @@ DOCUMENTATION :: END
</div> </div>
% endif % endif
<div class="summary-content-summary"> <div class="summary-content-summary">
<p> ${data['summary']} </p> <p> ${data['summary'] | br, n} </p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -541,7 +541,7 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="input-group"> <div class="input-group">
<input class="form-control" type="text" name="api_key" id="api_key" value="${config['api_key']}" size="20" disabled> <input class="form-control" type="text" name="api_key" id="api_key" value="${config['api_key']}" size="20" readonly>
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-form" type="button" id="generate_api">Generate</button> <button class="btn btn-form" type="button" id="generate_api">Generate</button>
</span> </span>
@@ -762,8 +762,8 @@
<div class="form-group"> <div class="form-group">
<label>Flush Temporary Sessions</label> <label>Flush Temporary Sessions</label>
<p class="help-block"> <p class="help-block">
Attempt to fix hisotry logging by flushing out all of the temporary sessions in the database.<br /> Attempt to fix history logging by flushing out all of the temporary sessions in the database.<br />
Warning: This will reset all currently active sessions. For emergeny use only when history logging is stuck! Warning: This will reset all currently active sessions. For emergency use only when history logging is stuck!
</p> </p>
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">

View File

@@ -47,7 +47,7 @@
// Redirect to home page after countdown. // Redirect to home page after countdown.
function reloadPage() { function reloadPage() {
window.location.href = "index"; window.location.href = "${new_http_root}index";
} }
</script> </script>
</%def> </%def>

View File

@@ -221,7 +221,7 @@ class ActivityProcessor(object):
logger.debug(u"PlexPy ActivityProcessor :: Fetching metadata for item ratingKey %s" % session['rating_key']) logger.debug(u"PlexPy ActivityProcessor :: Fetching metadata for item ratingKey %s" % session['rating_key'])
pms_connect = pmsconnect.PmsConnect() pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_metadata_details(rating_key=str(session['rating_key'])) result = pms_connect.get_metadata_details(rating_key=str(session['rating_key']))
if result: if result and result['metadata']:
metadata = result['metadata'] metadata = result['metadata']
else: else:
return False return False

View File

@@ -47,7 +47,7 @@ class API2:
self._api_cmd = None self._api_cmd = None
self._api_apikey = None self._api_apikey = None
self._api_callback = None # JSONP self._api_callback = None # JSONP
self._api_result_type = 'failed' self._api_result_type = 'error'
self._api_profileme = None # For profiling the api call self._api_profileme = None # For profiling the api call
self._api_kwargs = None # Cleaned kwargs self._api_kwargs = None # Cleaned kwargs
@@ -64,12 +64,12 @@ class API2:
return docs return docs
def docs_md(self): def docs_md(self):
""" Return the api docs formatted with markdown. """ """ Return the api docs formatted with markdown."""
return self._api_make_md() return self._api_make_md()
def docs(self): def docs(self):
""" Return the api docs as a dict where commands are keys, docstring are value. """ """ Return the api docs as a dict where commands are keys, docstring are value."""
return self._api_docs() return self._api_docs()
@@ -95,7 +95,7 @@ class API2:
self._api_msg = 'Parameter cmd is required. Possible commands are: %s' % ', '.join(self._api_valid_methods) self._api_msg = 'Parameter cmd is required. Possible commands are: %s' % ', '.join(self._api_valid_methods)
elif 'cmd' in kwargs and kwargs.get('cmd') not in self._api_valid_methods: elif 'cmd' in kwargs and kwargs.get('cmd') not in self._api_valid_methods:
self._api_msg = 'Unknown command: %s. Possible commands are: %s' % (kwargs.get('cmd', ''), ', '.join(self._api_valid_methods)) self._api_msg = 'Unknown command: %s. Possible commands are: %s' % (kwargs.get('cmd', ''), ', '.join(sorted(self._api_valid_methods)))
self._api_callback = kwargs.pop('callback', None) self._api_callback = kwargs.pop('callback', None)
self._api_apikey = kwargs.pop('apikey', None) self._api_apikey = kwargs.pop('apikey', None)
@@ -147,7 +147,6 @@ class API2:
] ]
``` ```
""" """
logfile = os.path.join(plexpy.CONFIG.LOG_DIR, logger.FILENAME) logfile = os.path.join(plexpy.CONFIG.LOG_DIR, logger.FILENAME)
templog = [] templog = []
start = int(kwargs.get('start', 0)) start = int(kwargs.get('start', 0))
@@ -213,7 +212,6 @@ class API2:
if order == 'desc': if order == 'desc':
templog = templog[::-1] templog = templog[::-1]
self.data = templog
return templog return templog
def get_settings(self, key=''): def get_settings(self, key=''):
@@ -257,7 +255,7 @@ class API2:
config[k]['interface_list'] = interface_list config[k]['interface_list'] = interface_list
if key: if key:
return config.get(key, None) return config.get(key)
return config return config
@@ -277,81 +275,69 @@ class API2:
None None
``` ```
""" """
if not plexpy.CONFIG.API_SQL or not query: if not plexpy.CONFIG.API_SQL:
self._api_msg = 'SQL not enabled for the API.'
return
if not query:
self._api_msg = 'No SQL query provided.'
return return
# allow the user to shoot them self # allow the user to shoot them self
# in the foot but not in the head.. # in the foot but not in the head..
if not len(os.listdir(plexpy.BACKUP_DIR)): if not len(os.listdir(plexpy.CONFIG.BACKUP_DIR)):
self.backupdb() self.backup_db()
else: else:
# If the backup is less then 24 h old lets make a backup # If the backup is less then 24 h old lets make a backup
if any([os.path.getctime(os.path.join(plexpy.BACKUP_DIR, file_)) < if any([os.path.getctime(os.path.join(plexpy.CONFIG.BACKUP_DIR, file_)) < (time.time() - 86400)
(time.time() - 86400) for file_ in os.listdir(plexpy.BACKUP_DIR)]): and file_.endswith('.db') for file_ in os.listdir(plexpy.CONFIG.BACKUP_DIR)]):
self.backupdb() self.backup_db()
db = database.MonitorDatabase() db = database.MonitorDatabase()
rows = db.select(query) rows = db.select(query)
self.data = rows
return rows return rows
def backup_config(self): def backup_config(self):
""" Create a manual backup of the `config.ini` file. """ """ Create a manual backup of the `config.ini` file."""
data = config.make_backup() data = config.make_backup()
self._api_result_type = 'success' if data else 'error'
if data:
self.result_type = 'success'
else:
self.result_type = 'failed'
return data return data
def backup_db(self): def backup_db(self):
""" Create a manual backup of the `plexpy.db` file. """ """ Create a manual backup of the `plexpy.db` file."""
data = database.make_backup() data = database.make_backup()
self._api_result_type = 'success' if data else 'error'
if data:
self.result_type = 'success'
else:
self.result_type = 'failed'
return data return data
def restart(self, **kwargs): def restart(self, **kwargs):
""" Restart PlexPy. """ """ Restart PlexPy."""
plexpy.SIGNAL = 'restart' plexpy.SIGNAL = 'restart'
self.msg = 'Restarting plexpy' self._api_msg = 'Restarting plexpy'
self.result_type = 'success' self._api_result_type = 'success'
def update(self, **kwargs): def update(self, **kwargs):
""" Check for PlexPy updates on Github. """ """ Check for PlexPy updates on Github."""
plexpy.SIGNAL = 'update' plexpy.SIGNAL = 'update'
self.msg = 'Updating plexpy' self._api_msg = 'Updating plexpy'
self.result_type = 'success' self._api_result_type = 'success'
def refresh_libraries_list(self, **kwargs): def refresh_libraries_list(self, **kwargs):
""" Refresh the PlexPy libraries list. """ """ Refresh the PlexPy libraries list."""
data = pmsconnect.refresh_libraries() data = pmsconnect.refresh_libraries()
self._api_result_type = 'success' if data else 'error'
if data:
self.result_type = 'success'
else:
self.result_type = 'failed'
return data return data
def refresh_users_list(self, **kwargs): def refresh_users_list(self, **kwargs):
""" Refresh the PlexPy users list. """ """ Refresh the PlexPy users list."""
data = plextv.refresh_users() data = plextv.refresh_users()
self._api_result_type = 'success' if data else 'error'
if data:
self.result_type = 'success'
else:
self.result_type = 'failed'
return data return data
@@ -418,30 +404,30 @@ General optional parameters:
string: "apikey" string: "apikey"
``` ```
""" """
data = None
apikey = hashlib.sha224(str(random.getrandbits(256))).hexdigest()[0:32] apikey = hashlib.sha224(str(random.getrandbits(256))).hexdigest()[0:32]
if plexpy.CONFIG.HTTP_USERNAME and plexpy.CONFIG.HTTP_PASSWORD: if plexpy.CONFIG.HTTP_USERNAME and plexpy.CONFIG.HTTP_PASSWORD:
if username == plexpy.HTTP_USERNAME and password == plexpy.CONFIG.HTTP_PASSWORD: if username == plexpy.HTTP_USERNAME and password == plexpy.CONFIG.HTTP_PASSWORD:
if plexpy.CONFIG.API_KEY: if plexpy.CONFIG.API_KEY:
self.data = plexpy.CONFIG.API_KEY data = plexpy.CONFIG.API_KEY
else: else:
self.data = apikey data = apikey
plexpy.CONFIG.API_KEY = apikey plexpy.CONFIG.API_KEY = apikey
plexpy.CONFIG.write() plexpy.CONFIG.write()
else: else:
self.msg = 'Authentication is enabled, please add the correct username and password to the parameters' self._api_msg = 'Authentication is enabled, please add the correct username and password to the parameters'
else: else:
if plexpy.CONFIG.API_KEY: if plexpy.CONFIG.API_KEY:
self.data = plexpy.CONFIG.API_KEY data = plexpy.CONFIG.API_KEY
else: else:
# Make a apikey if the doesn't exist # Make a apikey if the doesn't exist
self.data = apikey data = apikey
plexpy.CONFIG.API_KEY = apikey plexpy.CONFIG.API_KEY = apikey
plexpy.CONFIG.write() plexpy.CONFIG.write()
return self.data return data
def _api_responds(self, result_type='success', data=None, msg=''): def _api_responds(self, result_type='error', data=None, msg=''):
""" Formats the result to a predefined dict so we can hange it the to """ Formats the result to a predefined dict so we can hange it the to
the desired output by _api_out_as """ the desired output by _api_out_as """
@@ -478,6 +464,7 @@ General optional parameters:
logger.info(u'PlexPy APIv2 :: ' + traceback.format_exc()) logger.info(u'PlexPy APIv2 :: ' + traceback.format_exc())
out['message'] = traceback.format_exc() out['message'] = traceback.format_exc()
out['result'] = 'error' out['result'] = 'error'
elif self._api_out_type == 'xml': elif self._api_out_type == 'xml':
cherrypy.response.headers['Content-Type'] = 'application/xml' cherrypy.response.headers['Content-Type'] = 'application/xml'
try: try:
@@ -519,13 +506,16 @@ General optional parameters:
# We allow this to fail so we get a # We allow this to fail so we get a
# traceback in the browser # traceback in the browser
if self._api_debug:
result = call(**self._api_kwargs)
else:
try: try:
result = call(**self._api_kwargs) result = call(**self._api_kwargs)
except Exception as e: except Exception as e:
logger.error(u'PlexPy APIv2 :: Failed to run %s %s %s' % (self._api_cmd, self._api_kwargs, e)) logger.error(u'PlexPy APIv2 :: Failed to run %s with %s: %s' % (self._api_cmd, self._api_kwargs, e))
if self._api_debug:
cherrypy.request.show_tracebacks = True
# Reraise the exception so the traceback hits the browser
raise
self._api_msg = 'Check the logs'
ret = None ret = None
# The api decorated function can return different result types. # The api decorated function can return different result types.
@@ -556,4 +546,14 @@ General optional parameters:
else: else:
self._api_result_type = 'error' self._api_result_type = 'error'
# Since some of them metods use a api like response for the ui
# {result: error, message: 'Some shit happend'}
if isinstance(ret, dict):
if ret.get('message'):
self._api_msg = ret.get('message', {})
ret = {}
if ret.get('result'):
self._api_result_type = ret.get('result')
return self._api_out_as(self._api_responds(result_type=self._api_result_type, msg=self._api_msg, data=ret)) return self._api_out_as(self._api_responds(result_type=self._api_result_type, msg=self._api_msg, data=ret))

View File

@@ -666,7 +666,7 @@ def build_notify_text(session=None, timeline=None, notify_action=None, agent_id=
remaining_duration = duration - view_offset remaining_duration = duration - view_offset
# Build Plex URL # Build Plex URL
metadata['plex_url'] = 'http://app.plex.tv/web/app#!/server/{0}/details/%2Flibrary%2Fmetadata%2F{1}'.format( metadata['plex_url'] = 'https://app.plex.tv/web/app#!/server/{0}/details/%2Flibrary%2Fmetadata%2F{1}'.format(
plexpy.CONFIG.PMS_IDENTIFIER, str(rating_key)) plexpy.CONFIG.PMS_IDENTIFIER, str(rating_key))
# Get media IDs from guid and build URLs # Get media IDs from guid and build URLs

View File

@@ -2207,13 +2207,17 @@ class Scripts(object):
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
cwd=self.script_folder) cwd=self.script_folder)
if self.script_timeout:
timer = threading.Timer(self.script_timeout, kill_script, (process,)) timer = threading.Timer(self.script_timeout, kill_script, (process,))
else:
timer = None
try: try:
timer.start() if timer: timer.start()
output, error = process.communicate() output, error = process.communicate()
status = process.returncode status = process.returncode
finally: finally:
timer.cancel() if timer: timer.cancel()
except OSError as e: except OSError as e:
logger.error(u"PlexPy Notifiers :: Failed to run script: %s" % e) logger.error(u"PlexPy Notifiers :: Failed to run script: %s" % e)
@@ -2420,7 +2424,7 @@ class Scripts(object):
{'label': 'Script Timeout', {'label': 'Script Timeout',
'value': self.script_timeout, 'value': self.script_timeout,
'name': 'scripts_timeout', 'name': 'scripts_timeout',
'description': 'The number of seconds to wait before killing the script.', 'description': 'The number of seconds to wait before killing the script. 0 to disable timeout.',
'input_type': 'number' 'input_type': 'number'
} }
] ]

View File

@@ -144,7 +144,7 @@ class PlexTV(object):
Plex.tv authentication Plex.tv authentication
""" """
def __init__(self, username=None, password=None, token=None): def __init__(self, username='', password='', token=None):
self.protocol = 'HTTPS' self.protocol = 'HTTPS'
self.username = username self.username = username
self.password = password self.password = password
@@ -168,7 +168,7 @@ class PlexTV(object):
def get_plex_auth(self, output_format='raw'): def get_plex_auth(self, output_format='raw'):
uri = '/users/sign_in.xml' uri = '/users/sign_in.xml'
base64string = base64.encodestring('%s:%s' % (self.username, self.password)).replace('\n', '') base64string = base64.b64encode(('%s:%s' % (self.username, self.password)).encode('utf-8'))
headers = {'Content-Type': 'application/xml; charset=utf-8', headers = {'Content-Type': 'application/xml; charset=utf-8',
'X-Plex-Device-Name': 'PlexPy', 'X-Plex-Device-Name': 'PlexPy',
'X-Plex-Product': 'PlexPy', 'X-Plex-Product': 'PlexPy',

View File

@@ -564,7 +564,6 @@ class PmsConnect(object):
for a in xml_head: for a in xml_head:
if a.getAttribute('size'): if a.getAttribute('size'):
if a.getAttribute('size') != '1': if a.getAttribute('size') != '1':
metadata_list = {'metadata': None}
return metadata_list return metadata_list
if a.getElementsByTagName('Directory'): if a.getElementsByTagName('Directory'):

View File

@@ -1,2 +1,2 @@
PLEXPY_VERSION = "master" PLEXPY_VERSION = "master"
PLEXPY_RELEASE_VERSION = "1.4.13" PLEXPY_RELEASE_VERSION = "1.4.16"

View File

@@ -142,11 +142,12 @@ def process(opcode, data):
try: try:
info = json.loads(data) info = json.loads(data)
except Exception as ex: except Exception as e:
logger.warn(u"PlexPy WebSocket :: Error decoding message from websocket: %s" % ex) logger.warn(u"PlexPy WebSocket :: Error decoding message from websocket: %s" % e)
logger.debug(data) logger.debug(data)
return False return False
info = info.get('NotificationContainer', info)
type = info.get('type') type = info.get('type')
if not type: if not type:
@@ -154,9 +155,9 @@ def process(opcode, data):
if type == 'playing': if type == 'playing':
# logger.debug('%s.playing %s' % (name, info)) # logger.debug('%s.playing %s' % (name, info))
try: time_line = info.get('PlaySessionStateNotification', info.get('_children'))
time_line = info.get('_children')
except: if not time_line:
logger.debug(u"PlexPy WebSocket :: Session found but unable to get timeline data.") logger.debug(u"PlexPy WebSocket :: Session found but unable to get timeline data.")
return False return False

View File

@@ -329,7 +329,9 @@ class WebInterface(object):
@cherrypy.expose @cherrypy.expose
@cherrypy.tools.json_out() @cherrypy.tools.json_out()
@requireAuth(member_of("admin")) @requireAuth(member_of("admin"))
@addtoapi()
def delete_temp_sessions(self, **kwargs): def delete_temp_sessions(self, **kwargs):
""" Flush out all of the temporary sessions in the database."""
result = database.delete_sessions() result = database.delete_sessions()
@@ -3212,8 +3214,13 @@ class WebInterface(object):
quote = self.random_arnold_quotes() quote = self.random_arnold_quotes()
plexpy.SIGNAL = signal plexpy.SIGNAL = signal
if plexpy.CONFIG.HTTP_ROOT:
new_http_root = '/' + plexpy.CONFIG.HTTP_ROOT.strip('/') + '/'
else:
new_http_root = '/'
return serve_template(templatename="shutdown.html", title=title, return serve_template(templatename="shutdown.html", title=title,
message=message, timer=timer, quote=quote) new_http_root=new_http_root, message=message, timer=timer, quote=quote)
@cherrypy.expose @cherrypy.expose
@requireAuth(member_of("admin")) @requireAuth(member_of("admin"))
@@ -3245,14 +3252,14 @@ class WebInterface(object):
if source == 'history': if source == 'history':
data_factory = datafactory.DataFactory() data_factory = datafactory.DataFactory()
result = data_factory.get_metadata_details(rating_key=rating_key) result = data_factory.get_metadata_details(rating_key=rating_key)
if result: if result and result['metadata']:
metadata = result['metadata'] metadata = result['metadata']
poster_url = data_factory.get_poster_url(metadata=metadata) poster_url = data_factory.get_poster_url(metadata=metadata)
metadata['poster_url'] = poster_url metadata['poster_url'] = poster_url
else: else:
pms_connect = pmsconnect.PmsConnect() pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_metadata_details(rating_key=rating_key, get_media_info=True) result = pms_connect.get_metadata_details(rating_key=rating_key, get_media_info=True)
if result: if result and result['metadata']:
metadata = result['metadata'] metadata = result['metadata']
data_factory = datafactory.DataFactory() data_factory = datafactory.DataFactory()
poster_url = data_factory.get_poster_url(metadata=metadata) poster_url = data_factory.get_poster_url(metadata=metadata)
@@ -3289,8 +3296,7 @@ class WebInterface(object):
refresh = False refresh = False
if kwargs.get('refresh'): if kwargs.get('refresh'):
mo = member_of('admin') refresh = False if get_session_user_id() else True
refresh = True if mo() else False
kwargs['refresh'] = refresh kwargs['refresh'] = refresh