mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-12 08:16:06 -07:00
We're getting our own database!
First code for independent notifications (not linked to PlexWatch). New notifications panel in Settings (many types still untested). Standardise the parameters sent to current activity. Remove notifiers we cannot use. Styling fixes for sync tables.
This commit is contained in:
parent
b244e09c24
commit
349a850451
14 changed files with 401 additions and 354 deletions
|
@ -149,7 +149,7 @@ def main():
|
|||
'Cannot write to the data directory: ' + plexpy.DATA_DIR + '. Exiting...')
|
||||
|
||||
# Put the database in the DATA_DIR
|
||||
# plexpy.DB_FILE = os.path.join(plexpy.DATA_DIR, 'plexpy.db')
|
||||
plexpy.DB_FILE = os.path.join(plexpy.DATA_DIR, 'plexpy.db')
|
||||
|
||||
# Read config and start logging
|
||||
plexpy.initialize(config_file)
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation" class="active"><a href="#tabs-1" aria-controls="tabs-1" role="tab" data-toggle="tab">Web Interface</a></li>
|
||||
<li role="presentation"><a href="#tabs-2" aria-controls="tabs-2" role="tab" data-toggle="tab">Plex & PlexWatch</a></li>
|
||||
<!--<li role="presentation"><a href="#tabs-5" aria-controls="tabs-5" role="tab" data-toggle="tab">Notifications</a></li>-->
|
||||
<li role="presentation"><a href="#tabs-5" aria-controls="tabs-5" role="tab" data-toggle="tab">Notifications</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
|
@ -206,13 +206,13 @@
|
|||
</div>
|
||||
</div>
|
||||
<input type="button" class="btn btn-medium btn-primary save-button" value="Save" data-success="Changes saved successfully">
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-5">
|
||||
<div class="wellbg">
|
||||
<div class="wellbg" style="padding: 0px 0px 0px 20px;">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="row-fluid">
|
||||
<div class="span4">
|
||||
<fieldset>
|
||||
<div class="wellheader">
|
||||
<h3>Growl</h3>
|
||||
|
@ -298,17 +298,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<div class="wellheader">
|
||||
<h3>Synology NAS</h3>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" name="synoindex_enabled" id="synoindex" value="1" ${config['synoindex_enabled']}> Enable Synoindex
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="span4">
|
||||
<fieldset>
|
||||
<div class="wellheader">
|
||||
<h3>NotifyMyAndroid</h3>
|
||||
|
@ -348,21 +340,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<div class="wellheader">
|
||||
<h3>Logitech Media Server</h3>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" name="lms_enabled" id="lms" value="1" ${config['lms_enabled']}> Enable LMS Updates
|
||||
</div>
|
||||
<div id="lmsoptions">
|
||||
<div class="form-group">
|
||||
<label for="lms_host">LMS Host:Port</label>
|
||||
<input type="text" id="lms_host" name="lms_host" value="${config['lms_host']}" size="30">
|
||||
<p class="help-block">e.g. http://localhost:9000. Seperate hosts with commas.</p>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<div class="wellheader">
|
||||
|
@ -380,28 +357,6 @@
|
|||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<div class="wellheader">
|
||||
<h3>Subsonic</h3>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" name="subsonic_enabled" id="subsonic" value="1" ${config['subsonic_enabled']}> Enable Subsonic Updates
|
||||
</div>
|
||||
<div id="subsonicoptions">
|
||||
<div class="form-group">
|
||||
<label for="subsonic_host">Subsonic URL</label>
|
||||
<input type="text" id="subsonic_host" name="subsonic_host" value="${config['subsonic_host']}" size="30">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="subsonic_username">Subsonic Username</label>
|
||||
<input type="text" id="subsonic_username" name="subsonic_username" value="${config['subsonic_username']}" size="30">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="subsonic_password">Subsonic Password</label>
|
||||
<input type="password" id="subsonic_password" name="subsonic_password" value="${config['subsonic_password']}" size="30">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<div class="wellheader">
|
||||
<h3>Email</h3>
|
||||
|
@ -441,7 +396,7 @@
|
|||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="md-col-4">
|
||||
<div class="span4">
|
||||
<fieldset>
|
||||
<div class="wellheader">
|
||||
<h3>Pushover</h3>
|
||||
|
@ -484,6 +439,7 @@
|
|||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- We need to test Twitter first
|
||||
<fieldset>
|
||||
<div class="wellheader">
|
||||
<h3>Twitter</h3>
|
||||
|
@ -504,7 +460,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
-->
|
||||
<fieldset>
|
||||
<div class="wellheader">
|
||||
<h3>OS X</h3>
|
||||
|
@ -537,22 +493,13 @@
|
|||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<div class="wellheader">
|
||||
<h3>MPC</h3>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" name="mpc_enabled" id="mpc" value="1" ${config['mpc_enabled']}> Enable MPC Update
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
</div>
|
||||
<input type="button" class="btn btn-medium btn-primary" value="Save" onclick="doAjaxCall('configUpdate',$(this),'tabs',true);return false;" data-success="Changes saved successfully">
|
||||
<input type="button" class="btn btn-medium btn-primary save-button" value="Save" data-success="Changes saved successfully">
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -885,26 +832,6 @@
|
|||
}
|
||||
});
|
||||
|
||||
if ($("#lms").is(":checked"))
|
||||
{
|
||||
$("#lmsoptions").show();
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#lmsoptions").hide();
|
||||
}
|
||||
|
||||
$("#lms").click(function(){
|
||||
if ($("#lms").is(":checked"))
|
||||
{
|
||||
$("#lmsoptions").slideDown();
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#lmsoptions").slideUp();
|
||||
}
|
||||
});
|
||||
|
||||
if ($("#plex").is(":checked"))
|
||||
{
|
||||
$("#plexoptions").show();
|
||||
|
@ -1066,26 +993,6 @@
|
|||
}
|
||||
});
|
||||
|
||||
if ($("#subsonic").is(":checked"))
|
||||
{
|
||||
$("#subsonicoptions").show();
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#subsonicoptions").hide();
|
||||
}
|
||||
|
||||
$("#subsonic").click(function(){
|
||||
if ($("#subsonic").is(":checked"))
|
||||
{
|
||||
$("#subsonicoptions").slideDown();
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#subsonicoptions").slideUp();
|
||||
}
|
||||
});
|
||||
|
||||
if ($("#email").is(":checked"))
|
||||
{
|
||||
$("#email_options").show();
|
||||
|
|
|
@ -29,13 +29,8 @@ player Returns the name of the platform used to play the stream
|
|||
audioDecision Returns the audio transcode decision. Either 'transcode', 'copy' or 'direct play'.
|
||||
audioCodec Returns the name of the audio codec.
|
||||
audioChannels Returns the number of audio channels.
|
||||
|
||||
== Only if 'type' is 'episode' ==
|
||||
grandparentTitle Returns the name of the TV Show.
|
||||
|
||||
== Only if 'type' is 'track' ==
|
||||
artist Returns the name of the artist of a music track.
|
||||
album Returns the name of the album of the music track.
|
||||
grandparentTitle Returns the title of the item's grandparent.
|
||||
parentTitle Returns the title of the item's parent.
|
||||
|
||||
== Only if 'type' is 'episode' or 'movie' ==
|
||||
videoDecision Returns the video transcode decision. Either 'transcode', 'copy' or 'direct play'.
|
||||
|
@ -83,7 +78,7 @@ DOCUMENTATION :: END
|
|||
% elif a['type'] == 'movie':
|
||||
<a href="info?rating_key=${a['ratingKey']}">${a['title']}</a>
|
||||
% elif a['type'] == 'track':
|
||||
${a['artist']} - ${a['track']}
|
||||
${a['grandparentTitle']} - ${a['title']}
|
||||
% else:
|
||||
${a['grandparentTitle']} - ${a['title']}
|
||||
% endif
|
||||
|
@ -93,9 +88,9 @@ DOCUMENTATION :: END
|
|||
<div class='dashboard-activity-info-details-overlay'>
|
||||
<div class='dashboard-activity-info-details-content'>
|
||||
% if a['type'] == 'track':
|
||||
Artist: <strong>${a['artist']}</strong>
|
||||
Artist: <strong>${a['grandparentTitle']}</strong>
|
||||
<br>
|
||||
Album: <strong>${a['album']}</strong>
|
||||
Album: <strong>${a['parentTitle']}</strong>
|
||||
<br>
|
||||
% endif
|
||||
% if a['state'] == 'playing':
|
||||
|
|
|
@ -28,7 +28,8 @@ sync_table_options = {
|
|||
} else {
|
||||
$(td).html(cellData.toProperCase());
|
||||
}
|
||||
}
|
||||
},
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [1],
|
||||
|
@ -37,7 +38,8 @@ sync_table_options = {
|
|||
if (cellData !== '') {
|
||||
$(td).html('<a href="user?user=' + rowData['username'] + '">' + cellData + '</a>');
|
||||
}
|
||||
}
|
||||
},
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [2],
|
||||
|
@ -57,15 +59,18 @@ sync_table_options = {
|
|||
"data": "metadata_type",
|
||||
"render": function ( data, type, full ) {
|
||||
return data.toProperCase();
|
||||
}
|
||||
},
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [4],
|
||||
"data": "device_name"
|
||||
"data": "device_name",
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [5],
|
||||
"data": "platform"
|
||||
"data": "platform",
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [6],
|
||||
|
@ -77,19 +82,23 @@ sync_table_options = {
|
|||
} else {
|
||||
$(td).html('0MB');
|
||||
}
|
||||
}
|
||||
},
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [7],
|
||||
"data": "item_count"
|
||||
"data": "item_count",
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [8],
|
||||
"data": "item_complete_count"
|
||||
"data": "item_complete_count",
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [9],
|
||||
"data": "item_downloaded_count"
|
||||
"data": "item_downloaded_count",
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [10],
|
||||
|
@ -100,7 +109,8 @@ sync_table_options = {
|
|||
} else {
|
||||
$(td).html('<span class="badge">0%</span>');
|
||||
}
|
||||
}
|
||||
},
|
||||
"className": "no-wrap"
|
||||
}
|
||||
],
|
||||
"drawCallback": function (settings) {
|
||||
|
|
|
@ -33,17 +33,17 @@ from plexpy import helpers
|
|||
<table class="display" id="sync_table" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align='left' id="state">State</th>
|
||||
<th align='left' id="username">Username</th>
|
||||
<th align='left' id="title">Title</th>
|
||||
<th align='left' id="type">Type</th>
|
||||
<th align='left' id="device">Device</th>
|
||||
<th align='left' id="platform">Platform</th>
|
||||
<th align='left' id="size">Total Size</th>
|
||||
<th align='left' id="items">Total Items</th>
|
||||
<th align='left' id="converted">Converted</th>
|
||||
<th align='left' id="downloaded">Downloaded</th>
|
||||
<th align='left' id="percent_complete">Complete</th>
|
||||
<th class="desktop" align='left' id="state">State</th>
|
||||
<th class="all" align='left' id="username">Username</th>
|
||||
<th class="all" align='left' id="title">Title</th>
|
||||
<th class="desktop" align='left' id="type">Type</th>
|
||||
<th class="min-tablet" align='left' id="device">Device</th>
|
||||
<th class="desktop" align='left' id="platform">Platform</th>
|
||||
<th class="desktop" align='left' id="size">Total Size</th>
|
||||
<th class="min-tablet" align='left' id="items">Total Items</th>
|
||||
<th class="desktop" align='left' id="converted">Converted</th>
|
||||
<th class="desktop" align='left' id="downloaded">Downloaded</th>
|
||||
<th class="desktop" align='left' id="percent_complete">Complete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -222,17 +222,17 @@ from plexpy import helpers
|
|||
<table class="display" id="sync_table" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align='left' id="state">State</th>
|
||||
<th align='left' id="username">Username</th>
|
||||
<th align='left' id="sync_title">Title</th>
|
||||
<th align='left' id="type">Type</th>
|
||||
<th align='left' id="device">Device</th>
|
||||
<th align='left' id="sync_platform">Platform</th>
|
||||
<th align='left' id="size">Total Size</th>
|
||||
<th align='left' id="items">Total Items</th>
|
||||
<th align='left' id="converted">Converted</th>
|
||||
<th align='left' id="downloaded">Downloaded</th>
|
||||
<th align='left' id="sync_percent_complete">Complete</th>
|
||||
<th class="desktop" align='left' id="state">State</th>
|
||||
<th class="never" align='left' id="username">Username</th>
|
||||
<th class="all" align='left' id="sync_title">Title</th>
|
||||
<th class="desktop" align='left' id="type">Type</th>
|
||||
<th class="all" align='left' id="device">Device</th>
|
||||
<th class="desktop" align='left' id="sync_platform">Platform</th>
|
||||
<th class="desktop" align='left' id="size">Total Size</th>
|
||||
<th class="min-tablet" align='left' id="items">Total Items</th>
|
||||
<th class="desktop" align='left' id="converted">Converted</th>
|
||||
<th class="desktop" align='left' id="downloaded">Downloaded</th>
|
||||
<th class="desktop" align='left' id="sync_percent_complete">Complete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -6,7 +6,7 @@ from urllib import urlencode
|
|||
|
||||
__version__ = "0.1"
|
||||
|
||||
API_SERVER = 'nma.usk.bz'
|
||||
API_SERVER = 'www.notifymyandroid.com'
|
||||
ADD_PATH = '/publicapi/notify'
|
||||
|
||||
USER_AGENT="PyNMA/v%s"%__version__
|
||||
|
|
|
@ -29,7 +29,7 @@ import uuid
|
|||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
|
||||
from plexpy import versioncheck, logger
|
||||
from plexpy import versioncheck, logger, monitor
|
||||
import plexpy.config
|
||||
|
||||
PROG_DIR = None
|
||||
|
@ -256,6 +256,9 @@ def initialize_scheduler():
|
|||
minutes = 0
|
||||
schedule_job(versioncheck.checkGithub, 'Check GitHub for updates', hours=0, minutes=minutes)
|
||||
|
||||
if CONFIG.PMS_IP:
|
||||
schedule_job(monitor.check_active_sessions, 'Check for active sessions', hours=0, minutes=0, seconds=60)
|
||||
|
||||
# Start scheduler
|
||||
if start_jobs and len(SCHED.get_jobs()):
|
||||
try:
|
||||
|
@ -267,7 +270,7 @@ def initialize_scheduler():
|
|||
#SCHED.print_jobs()
|
||||
|
||||
|
||||
def schedule_job(function, name, hours=0, minutes=0):
|
||||
def schedule_job(function, name, hours=0, minutes=0, seconds=0):
|
||||
"""
|
||||
Start scheduled job if starting or restarting plexpy.
|
||||
Reschedule job if Interval Settings have changed.
|
||||
|
@ -277,16 +280,16 @@ def schedule_job(function, name, hours=0, minutes=0):
|
|||
|
||||
job = SCHED.get_job(name)
|
||||
if job:
|
||||
if hours == 0 and minutes == 0:
|
||||
if hours == 0 and minutes == 0 and seconds == 0:
|
||||
SCHED.remove_job(name)
|
||||
logger.info("Removed background task: %s", name)
|
||||
elif job.trigger.interval != datetime.timedelta(hours=hours, minutes=minutes):
|
||||
SCHED.reschedule_job(name, trigger=IntervalTrigger(
|
||||
hours=hours, minutes=minutes))
|
||||
hours=hours, minutes=minutes, seconds=seconds))
|
||||
logger.info("Re-scheduled background task: %s", name)
|
||||
elif hours > 0 or minutes > 0:
|
||||
elif hours > 0 or minutes > 0 or seconds > 0:
|
||||
SCHED.add_job(function, id=name, trigger=IntervalTrigger(
|
||||
hours=hours, minutes=minutes))
|
||||
hours=hours, minutes=minutes, seconds=seconds))
|
||||
logger.info("Scheduled background task: %s", name)
|
||||
|
||||
|
||||
|
@ -339,11 +342,23 @@ def dbcheck():
|
|||
conn.commit()
|
||||
c.close()
|
||||
|
||||
conn_db = sqlite3.connect(DB_FILE)
|
||||
c_db = conn_db.cursor()
|
||||
c_db.execute(
|
||||
'CREATE TABLE IF NOT EXISTS sessions (id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
||||
'session_key INTEGER, rating_key INTEGER, media_type TEXT)'
|
||||
)
|
||||
conn_db.commit()
|
||||
c_db.close()
|
||||
|
||||
def shutdown(restart=False, update=False):
|
||||
cherrypy.engine.exit()
|
||||
SCHED.shutdown(wait=False)
|
||||
|
||||
# Clear any sessions in the db - Not sure yet if we should do this. More testing required
|
||||
# logger.debug(u'Clearing Plex sessions.')
|
||||
# monitor.drop_session_db()
|
||||
|
||||
CONFIG.write()
|
||||
|
||||
if not restart and not update:
|
||||
|
|
|
@ -69,10 +69,7 @@ _CONFIG_DEFINITIONS = {
|
|||
'INTERFACE': (str, 'General', 'default'),
|
||||
'JOURNAL_MODE': (str, 'Advanced', 'wal'),
|
||||
'LAUNCH_BROWSER': (int, 'General', 1),
|
||||
'LMS_ENABLED': (int, 'LMS', 0),
|
||||
'LMS_HOST': (str, 'LMS', ''),
|
||||
'LOG_DIR': (str, 'General', ''),
|
||||
'MPC_ENABLED': (bool_int, 'MPC', False),
|
||||
'NMA_APIKEY': (str, 'NMA', ''),
|
||||
'NMA_ENABLED': (int, 'NMA', 0),
|
||||
'NMA_PRIORITY': (int, 'NMA', 0),
|
||||
|
@ -94,11 +91,6 @@ _CONFIG_DEFINITIONS = {
|
|||
'PUSHOVER_ENABLED': (int, 'Pushover', 0),
|
||||
'PUSHOVER_KEYS': (str, 'Pushover', ''),
|
||||
'PUSHOVER_PRIORITY': (int, 'Pushover', 0),
|
||||
'SUBSONIC_ENABLED': (int, 'Subsonic', 0),
|
||||
'SUBSONIC_HOST': (str, 'Subsonic', ''),
|
||||
'SUBSONIC_PASSWORD': (str, 'Subsonic', ''),
|
||||
'SUBSONIC_USERNAME': (str, 'Subsonic', ''),
|
||||
'SYNOINDEX_ENABLED': (int, 'Synoindex', 0),
|
||||
'TWITTER_ENABLED': (int, 'Twitter', 0),
|
||||
'TWITTER_PASSWORD': (str, 'Twitter', ''),
|
||||
'TWITTER_PREFIX': (str, 'Twitter', 'Headphones'),
|
||||
|
|
188
plexpy/monitor.py
Normal file
188
plexpy/monitor.py
Normal file
|
@ -0,0 +1,188 @@
|
|||
# This file is part of PlexPy.
|
||||
#
|
||||
# PlexPy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# PlexPy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from plexpy import logger, helpers, plexwatch, pmsconnect, notification_handler, config
|
||||
|
||||
from xml.dom import minidom
|
||||
from httplib import HTTPSConnection
|
||||
from httplib import HTTPConnection
|
||||
|
||||
import os
|
||||
import sqlite3
|
||||
import threading
|
||||
import plexpy
|
||||
|
||||
monitor_lock = threading.Lock()
|
||||
|
||||
def check_active_sessions():
|
||||
|
||||
with monitor_lock:
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
session_list = pms_connect.get_current_activity()
|
||||
monitor_db = MonitorDatabase()
|
||||
|
||||
if session_list['stream_count'] != '0':
|
||||
media_container = session_list['sessions']
|
||||
active_streams = []
|
||||
|
||||
for session in media_container:
|
||||
session_key = session['sessionKey']
|
||||
rating_key = session['ratingKey']
|
||||
media_type = session['type']
|
||||
friendly_name = session['friendly_name']
|
||||
platform = session['player']
|
||||
title = session['title']
|
||||
parent_title = session['parentTitle']
|
||||
grandparent_title = session['grandparentTitle']
|
||||
|
||||
write_session = monitor_db.write_session_key(session_key, rating_key, media_type)
|
||||
if write_session == 'insert':
|
||||
# User started playing a stream :: We notify here
|
||||
if media_type == 'track' or media_type == 'episode':
|
||||
item_title = grandparent_title + ' - ' + title
|
||||
elif media_type == 'movie':
|
||||
item_title = title
|
||||
else:
|
||||
item_title = title
|
||||
logger.info('%s (%s) starting playing %s' % (friendly_name, platform, item_title))
|
||||
pushmessage = '%s (%s) starting playing %s' % (friendly_name, platform, item_title)
|
||||
notification_handler.push_nofitications(pushmessage, 'PlexPy Playback started', 'Playback Started')
|
||||
|
||||
keys = {'session_key': session_key,
|
||||
'rating_key': rating_key}
|
||||
active_streams.append(keys)
|
||||
|
||||
# Check our temp table for what we must do with the new stream
|
||||
db_streams = monitor_db.select('SELECT session_key, rating_key, media_type FROM sessions')
|
||||
for result in db_streams:
|
||||
if any(d['session_key'] == str(result[0]) for d in active_streams):
|
||||
# The user's session is still active
|
||||
pass
|
||||
if any(d['rating_key'] == str(result[1]) for d in active_streams):
|
||||
# The user is still playing the same media item
|
||||
# Here we can check the play states
|
||||
pass
|
||||
else:
|
||||
# The user has stopped playing a stream
|
||||
monitor_db.action('DELETE FROM sessions WHERE session_key = ? AND rating_key = ?', [result[0], result[1]])
|
||||
else:
|
||||
# The user's session is no longer active
|
||||
monitor_db.action('DELETE FROM sessions WHERE session_key = ?', [result[0]])
|
||||
else:
|
||||
# No sessions exist
|
||||
# monitor_db.action('DELETE FROM sessions')
|
||||
pass
|
||||
|
||||
def drop_session_db():
|
||||
monitor_db = MonitorDatabase()
|
||||
monitor_db.action('DROP TABLE sessions')
|
||||
|
||||
def db_filename(filename="plexpy.db"):
|
||||
|
||||
return os.path.join(plexpy.DATA_DIR, filename)
|
||||
|
||||
def get_cache_size():
|
||||
# This will protect against typecasting problems produced by empty string and None settings
|
||||
if not plexpy.CONFIG.CACHE_SIZEMB:
|
||||
# sqlite will work with this (very slowly)
|
||||
return 0
|
||||
return int(plexpy.CONFIG.CACHE_SIZEMB)
|
||||
|
||||
|
||||
class MonitorDatabase(object):
|
||||
|
||||
def __init__(self, filename='plexpy.db'):
|
||||
self.filename = filename
|
||||
self.connection = sqlite3.connect(db_filename(filename), timeout=20)
|
||||
# Don't wait for the disk to finish writing
|
||||
self.connection.execute("PRAGMA synchronous = OFF")
|
||||
# Journal disabled since we never do rollbacks
|
||||
self.connection.execute("PRAGMA journal_mode = %s" % plexpy.CONFIG.JOURNAL_MODE)
|
||||
# 64mb of cache memory, probably need to make it user configurable
|
||||
self.connection.execute("PRAGMA cache_size=-%s" % (get_cache_size() * 1024))
|
||||
self.connection.row_factory = sqlite3.Row
|
||||
|
||||
def action(self, query, args=None):
|
||||
|
||||
if query is None:
|
||||
return
|
||||
|
||||
sql_result = None
|
||||
|
||||
try:
|
||||
with self.connection as c:
|
||||
if args is None:
|
||||
sql_result = c.execute(query)
|
||||
else:
|
||||
sql_result = c.execute(query, args)
|
||||
|
||||
except sqlite3.OperationalError, e:
|
||||
if "unable to open database file" in e.message or "database is locked" in e.message:
|
||||
logger.warn('Database Error: %s', e)
|
||||
else:
|
||||
logger.error('Database error: %s', e)
|
||||
raise
|
||||
|
||||
except sqlite3.DatabaseError, e:
|
||||
logger.error('Fatal Error executing %s :: %s', query, e)
|
||||
raise
|
||||
|
||||
return sql_result
|
||||
|
||||
def write_session_key(self, session_key=None, rating_key=None, media_type=None):
|
||||
|
||||
values = {'rating_key': rating_key,
|
||||
'media_type': media_type}
|
||||
|
||||
keys = {'session_key': session_key,
|
||||
'rating_key': rating_key}
|
||||
|
||||
result = self.upsert('sessions', values, keys)
|
||||
return result
|
||||
|
||||
def select(self, query, args=None):
|
||||
|
||||
sql_results = self.action(query, args).fetchall()
|
||||
|
||||
if sql_results is None or sql_results == [None]:
|
||||
return []
|
||||
|
||||
return sql_results
|
||||
|
||||
def upsert(self, table_name, value_dict, key_dict):
|
||||
|
||||
trans_type = 'update'
|
||||
changes_before = self.connection.total_changes
|
||||
|
||||
gen_params = lambda my_dict: [x + " = ?" for x in my_dict.keys()]
|
||||
|
||||
update_query = "UPDATE " + table_name + " SET " + ", ".join(gen_params(value_dict)) + \
|
||||
" WHERE " + " AND ".join(gen_params(key_dict))
|
||||
|
||||
self.action(update_query, value_dict.values() + key_dict.values())
|
||||
|
||||
if self.connection.total_changes == changes_before:
|
||||
trans_type = 'insert'
|
||||
insert_query = (
|
||||
"INSERT INTO " + table_name + " (" + ", ".join(value_dict.keys() + key_dict.keys()) + ")" +
|
||||
" VALUES (" + ", ".join(["?"] * len(value_dict.keys() + key_dict.keys())) + ")"
|
||||
)
|
||||
try:
|
||||
self.action(insert_query, value_dict.values() + key_dict.values())
|
||||
except sqlite3.IntegrityError:
|
||||
logger.info('Queries failed: %s and %s', update_query, insert_query)
|
||||
|
||||
# We want to know if it was an update or insert
|
||||
return trans_type
|
90
plexpy/notification_handler.py
Normal file
90
plexpy/notification_handler.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
# This file is part of PlexPy.
|
||||
#
|
||||
# PlexPy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# PlexPy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from plexpy import logger, config, notifiers
|
||||
|
||||
import plexpy
|
||||
|
||||
|
||||
def push_nofitications(push_message=None, subject=None, status_message=None):
|
||||
|
||||
if push_message:
|
||||
if not subject:
|
||||
subject = 'PlexPy'
|
||||
|
||||
if plexpy.CONFIG.GROWL_ENABLED:
|
||||
logger.info(u"Growl request")
|
||||
growl = notifiers.GROWL()
|
||||
growl.notify(push_message, status_message)
|
||||
|
||||
if plexpy.CONFIG.PROWL_ENABLED:
|
||||
logger.info(u"Prowl request")
|
||||
prowl = notifiers.PROWL()
|
||||
prowl.notify(push_message, status_message)
|
||||
|
||||
if plexpy.CONFIG.XBMC_ENABLED:
|
||||
xbmc = notifiers.XBMC()
|
||||
if plexpy.CONFIG.XBMC_NOTIFY:
|
||||
xbmc.notify(subject, push_message)
|
||||
|
||||
if plexpy.CONFIG.PLEX_ENABLED:
|
||||
plex = notifiers.Plex()
|
||||
if plexpy.CONFIG.PLEX_NOTIFY:
|
||||
plex.notify(subject, push_message)
|
||||
|
||||
if plexpy.CONFIG.NMA_ENABLED:
|
||||
nma = notifiers.NMA()
|
||||
nma.notify(subject, push_message)
|
||||
|
||||
if plexpy.CONFIG.PUSHALOT_ENABLED:
|
||||
logger.info(u"Pushalot request")
|
||||
pushalot = notifiers.PUSHALOT()
|
||||
pushalot.notify(push_message, status_message)
|
||||
|
||||
if plexpy.CONFIG.PUSHOVER_ENABLED:
|
||||
logger.info(u"Pushover request")
|
||||
pushover = notifiers.PUSHOVER()
|
||||
pushover.notify(push_message, status_message)
|
||||
|
||||
if plexpy.CONFIG.PUSHBULLET_ENABLED:
|
||||
logger.info(u"PushBullet request")
|
||||
pushbullet = notifiers.PUSHBULLET()
|
||||
pushbullet.notify(push_message, status_message)
|
||||
|
||||
if plexpy.CONFIG.TWITTER_ENABLED:
|
||||
logger.info(u"Sending Twitter notification")
|
||||
twitter = notifiers.TwitterNotifier()
|
||||
twitter.notify_download(push_message)
|
||||
|
||||
if plexpy.CONFIG.OSX_NOTIFY_ENABLED:
|
||||
# TODO: Get thumb in notification
|
||||
# from plexpy import cache
|
||||
# c = cache.Cache()
|
||||
# album_art = c.get_artwork_from_cache(None, release['AlbumID'])
|
||||
logger.info(u"Sending OS X notification")
|
||||
osx_notify = notifiers.OSX_NOTIFY()
|
||||
osx_notify.notify(subject, push_message)
|
||||
|
||||
if plexpy.CONFIG.BOXCAR_ENABLED:
|
||||
logger.info(u"Sending Boxcar2 notification")
|
||||
boxcar = notifiers.BOXCAR()
|
||||
boxcar.notify(subject, push_message)
|
||||
|
||||
if plexpy.CONFIG.EMAIL_ENABLED:
|
||||
logger.info(u"Sending Email notification")
|
||||
email = notifiers.Email()
|
||||
email.notify(subject=subject, message=push_message)
|
||||
else:
|
||||
logger.warning('Notification requested but no message received.')
|
|
@ -178,20 +178,6 @@ class PROWL(object):
|
|||
|
||||
self.notify('ZOMG Lazors Pewpewpew!', 'Test Message')
|
||||
|
||||
|
||||
class MPC(object):
|
||||
"""
|
||||
MPC library update
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
pass
|
||||
|
||||
def notify(self):
|
||||
subprocess.call(["mpc", "update"])
|
||||
|
||||
|
||||
class XBMC(object):
|
||||
"""
|
||||
XBMC notifications
|
||||
|
@ -225,25 +211,12 @@ class XBMC(object):
|
|||
if response:
|
||||
return response[0]['result']
|
||||
|
||||
def update(self):
|
||||
# From what I read you can't update the music library on a per directory or per path basis
|
||||
# so need to update the whole thing
|
||||
def notify(self, subject=None, message=None):
|
||||
|
||||
hosts = [x.strip() for x in self.hosts.split(',')]
|
||||
|
||||
for host in hosts:
|
||||
logger.info('Sending library update command to XBMC @ ' + host)
|
||||
request = self._sendjson(host, 'AudioLibrary.Scan')
|
||||
|
||||
if not request:
|
||||
logger.warn('Error sending update request to XBMC')
|
||||
|
||||
def notify(self, artist, album, albumartpath):
|
||||
|
||||
hosts = [x.strip() for x in self.hosts.split(',')]
|
||||
|
||||
header = "PlexPy"
|
||||
message = "%s - %s added to your library" % (artist, album)
|
||||
header = subject
|
||||
message = message
|
||||
time = "3000" # in ms
|
||||
|
||||
for host in hosts:
|
||||
|
@ -252,12 +225,12 @@ class XBMC(object):
|
|||
version = self._sendjson(host, 'Application.GetProperties', {'properties': ['version']})['version']['major']
|
||||
|
||||
if version < 12: #Eden
|
||||
notification = header + "," + message + "," + time + "," + albumartpath
|
||||
notification = header + "," + message + "," + time
|
||||
notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + notification + ')'}
|
||||
request = self._sendhttp(host, notifycommand)
|
||||
|
||||
else: #Frodo
|
||||
params = {'title': header, 'message': message, 'displaytime': int(time), 'image': albumartpath}
|
||||
params = {'title': header, 'message': message, 'displaytime': int(time)}
|
||||
request = self._sendjson(host, 'GUI.ShowNotification', params)
|
||||
|
||||
if not request:
|
||||
|
@ -267,48 +240,6 @@ class XBMC(object):
|
|||
logger.error('Error sending notification request to XBMC')
|
||||
|
||||
|
||||
class LMS(object):
|
||||
"""
|
||||
Class for updating a Logitech Media Server
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.hosts = plexpy.CONFIG.LMS_HOST
|
||||
|
||||
def _sendjson(self, host):
|
||||
data = {'id': 1, 'method': 'slim.request', 'params': ["", ["rescan"]]}
|
||||
data = json.JSONEncoder().encode(data)
|
||||
|
||||
content = {'Content-Type': 'application/json'}
|
||||
|
||||
req = urllib2.Request(host + '/jsonrpc.js', data, content)
|
||||
|
||||
try:
|
||||
handle = urllib2.urlopen(req)
|
||||
except Exception as e:
|
||||
logger.warn('Error opening LMS url: %s' % e)
|
||||
return
|
||||
|
||||
response = json.JSONDecoder().decode(handle.read())
|
||||
|
||||
try:
|
||||
return response['result']
|
||||
except:
|
||||
logger.warn('LMS returned error: %s' % response['error'])
|
||||
return response['error']
|
||||
|
||||
def update(self):
|
||||
|
||||
hosts = [x.strip() for x in self.hosts.split(',')]
|
||||
|
||||
for host in hosts:
|
||||
logger.info('Sending library rescan command to LMS @ ' + host)
|
||||
request = self._sendjson(host)
|
||||
|
||||
if request:
|
||||
logger.warn('Error sending rescan request to LMS')
|
||||
|
||||
|
||||
class Plex(object):
|
||||
def __init__(self):
|
||||
|
||||
|
@ -344,48 +275,18 @@ class Plex(object):
|
|||
|
||||
return response
|
||||
|
||||
def update(self):
|
||||
|
||||
# From what I read you can't update the music library on a per directory or per path basis
|
||||
# so need to update the whole thing
|
||||
|
||||
hosts = [x.strip() for x in self.server_hosts.split(',')]
|
||||
|
||||
for host in hosts:
|
||||
logger.info('Sending library update command to Plex Media Server@ ' + host)
|
||||
url = "%s/library/sections" % host
|
||||
try:
|
||||
xml_sections = minidom.parse(urllib.urlopen(url))
|
||||
except IOError, e:
|
||||
logger.warn("Error while trying to contact Plex Media Server: %s" % e)
|
||||
return False
|
||||
|
||||
sections = xml_sections.getElementsByTagName('Directory')
|
||||
if not sections:
|
||||
logger.info(u"Plex Media Server not running on: " + host)
|
||||
return False
|
||||
|
||||
for s in sections:
|
||||
if s.getAttribute('type') == "artist":
|
||||
url = "%s/library/sections/%s/refresh" % (host, s.getAttribute('key'))
|
||||
try:
|
||||
urllib.urlopen(url)
|
||||
except Exception as e:
|
||||
logger.warn("Error updating library section for Plex Media Server: %s" % e)
|
||||
return False
|
||||
|
||||
def notify(self, artist, album, albumartpath):
|
||||
def notify(self, subject=None, message=None):
|
||||
|
||||
hosts = [x.strip() for x in self.client_hosts.split(',')]
|
||||
|
||||
header = "PlexPy"
|
||||
message = "%s - %s added to your library" % (artist, album)
|
||||
header = subject
|
||||
message = message
|
||||
time = "3000" # in ms
|
||||
|
||||
for host in hosts:
|
||||
logger.info('Sending notification command to Plex Media Server @ ' + host)
|
||||
try:
|
||||
notification = header + "," + message + "," + time + "," + albumartpath
|
||||
notification = header + "," + message + "," + time
|
||||
notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + notification + ')'}
|
||||
request = self._sendhttp(host, notifycommand)
|
||||
|
||||
|
@ -397,7 +298,7 @@ class Plex(object):
|
|||
|
||||
|
||||
class NMA(object):
|
||||
def notify(self, artist=None, album=None, snatched=None):
|
||||
def notify(self, subject=None, message=None):
|
||||
title = 'PlexPy'
|
||||
api = plexpy.CONFIG.NMA_APIKEY
|
||||
nma_priority = plexpy.CONFIG.NMA_PRIORITY
|
||||
|
@ -406,12 +307,7 @@ class NMA(object):
|
|||
logger.debug(u"NMA API: " + api)
|
||||
logger.debug(u"NMA Priority: " + str(nma_priority))
|
||||
|
||||
if snatched:
|
||||
event = snatched + " snatched!"
|
||||
message = "PlexPy has snatched: " + snatched
|
||||
else:
|
||||
event = artist + ' - ' + album + ' complete!'
|
||||
message = "PlexPy has downloaded and postprocessed: " + artist + ' [' + album + ']'
|
||||
event = subject
|
||||
|
||||
logger.debug(u"NMA event: " + event)
|
||||
logger.debug(u"NMA message: " + message)
|
||||
|
@ -460,9 +356,9 @@ class PUSHBULLET(object):
|
|||
body=json.dumps(data))
|
||||
response = http_handler.getresponse()
|
||||
request_status = response.status
|
||||
logger.debug(u"PushBullet response status: %r" % request_status)
|
||||
logger.debug(u"PushBullet response headers: %r" % response.getheaders())
|
||||
logger.debug(u"PushBullet response body: %r" % response.read())
|
||||
# logger.debug(u"PushBullet response status: %r" % request_status)
|
||||
# logger.debug(u"PushBullet response headers: %r" % response.getheaders())
|
||||
# logger.debug(u"PushBullet response body: %r" % response.read())
|
||||
|
||||
if request_status == 200:
|
||||
logger.info(u"PushBullet notifications sent.")
|
||||
|
@ -474,10 +370,6 @@ class PUSHBULLET(object):
|
|||
logger.info(u"PushBullet notification failed serverside.")
|
||||
return False
|
||||
|
||||
def updateLibrary(self):
|
||||
#For uniformity reasons not removed
|
||||
return
|
||||
|
||||
def test(self, apikey, deviceid):
|
||||
|
||||
self.enabled = True
|
||||
|
@ -527,43 +419,6 @@ class PUSHALOT(object):
|
|||
return False
|
||||
|
||||
|
||||
class Synoindex(object):
|
||||
def __init__(self, util_loc='/usr/syno/bin/synoindex'):
|
||||
self.util_loc = util_loc
|
||||
|
||||
def util_exists(self):
|
||||
return os.path.exists(self.util_loc)
|
||||
|
||||
def notify(self, path):
|
||||
path = os.path.abspath(path)
|
||||
|
||||
if not self.util_exists():
|
||||
logger.warn("Error sending notification: synoindex utility not found at %s" % self.util_loc)
|
||||
return
|
||||
|
||||
if os.path.isfile(path):
|
||||
cmd_arg = '-a'
|
||||
elif os.path.isdir(path):
|
||||
cmd_arg = '-A'
|
||||
else:
|
||||
logger.warn("Error sending notification: Path passed to synoindex was not a file or folder.")
|
||||
return
|
||||
|
||||
cmd = [self.util_loc, cmd_arg, path]
|
||||
logger.info("Calling synoindex command: %s" % str(cmd))
|
||||
try:
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=plexpy.PROG_DIR)
|
||||
out, error = p.communicate()
|
||||
#synoindex never returns any codes other than '0', highly irritating
|
||||
except OSError, e:
|
||||
logger.warn("Error sending notification: %s" % str(e))
|
||||
|
||||
def notify_multiple(self, path_list):
|
||||
if isinstance(path_list, list):
|
||||
for path in path_list:
|
||||
self.notify(path)
|
||||
|
||||
|
||||
class PUSHOVER(object):
|
||||
|
||||
def __init__(self):
|
||||
|
@ -814,25 +669,6 @@ class BOXCAR(object):
|
|||
return False
|
||||
|
||||
|
||||
class SubSonicNotifier(object):
|
||||
|
||||
def __init__(self):
|
||||
self.host = plexpy.CONFIG.SUBSONIC_HOST
|
||||
self.username = plexpy.CONFIG.SUBSONIC_USERNAME
|
||||
self.password = plexpy.CONFIG.SUBSONIC_PASSWORD
|
||||
|
||||
def notify(self, albumpaths):
|
||||
# Correct URL
|
||||
if not self.host.lower().startswith("http"):
|
||||
self.host = "http://" + self.host
|
||||
|
||||
if not self.host.lower().endswith("/"):
|
||||
self.host = self.host + "/"
|
||||
|
||||
# Invoke request
|
||||
request.request_response(self.host + "musicFolderSettings.view?scanNow",
|
||||
auth=(self.username, self.password))
|
||||
|
||||
class Email(object):
|
||||
|
||||
def notify(self, subject, message):
|
||||
|
|
|
@ -581,6 +581,7 @@ class PmsConnect(object):
|
|||
progress = self.get_xml_attr(session, 'viewOffset')
|
||||
|
||||
session_output = {'sessionKey': self.get_xml_attr(session, 'sessionKey'),
|
||||
'art': self.get_xml_attr(session, 'art'),
|
||||
'parentThumb': self.get_xml_attr(session, 'parentThumb'),
|
||||
'thumb': self.get_xml_attr(session, 'thumb'),
|
||||
'user': self.get_xml_attr(session.getElementsByTagName('User')[0], 'title'),
|
||||
|
@ -588,13 +589,17 @@ class PmsConnect(object):
|
|||
self.get_xml_attr(session.getElementsByTagName('User')[0], 'title')),
|
||||
'player': self.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'state': self.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
||||
'artist': self.get_xml_attr(session, 'grandparentTitle'),
|
||||
'album': self.get_xml_attr(session, 'parentTitle'),
|
||||
'track': self.get_xml_attr(session, 'title'),
|
||||
'grandparentTitle': self.get_xml_attr(session, 'grandparentTitle'),
|
||||
'parentTitle': self.get_xml_attr(session, 'parentTitle'),
|
||||
'title': self.get_xml_attr(session, 'title'),
|
||||
'ratingKey': self.get_xml_attr(session, 'ratingKey'),
|
||||
'audioDecision': audio_decision,
|
||||
'audioChannels': audio_channels,
|
||||
'audioCodec': audio_codec,
|
||||
'videoDecision': '',
|
||||
'videoCodec': '',
|
||||
'height': '',
|
||||
'width': '',
|
||||
'duration': duration,
|
||||
'progress': progress,
|
||||
'progressPercent': str(helpers.get_percent(progress, duration)),
|
||||
|
@ -647,6 +652,7 @@ class PmsConnect(object):
|
|||
if self.get_xml_attr(session, 'type') == 'episode':
|
||||
session_output = {'sessionKey': self.get_xml_attr(session, 'sessionKey'),
|
||||
'art': self.get_xml_attr(session, 'art'),
|
||||
'parentThumb': self.get_xml_attr(session, 'parentThumb'),
|
||||
'thumb': thumb,
|
||||
'user': self.get_xml_attr(session.getElementsByTagName('User')[0], 'title'),
|
||||
'friendly_name': plex_watch.get_user_friendly_name(
|
||||
|
@ -654,6 +660,7 @@ class PmsConnect(object):
|
|||
'player': self.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'state': self.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
||||
'grandparentTitle': self.get_xml_attr(session, 'grandparentTitle'),
|
||||
'parentTitle': self.get_xml_attr(session, 'parentTitle'),
|
||||
'title': self.get_xml_attr(session, 'title'),
|
||||
'ratingKey': self.get_xml_attr(session, 'ratingKey'),
|
||||
'audioDecision': audio_decision,
|
||||
|
@ -673,11 +680,14 @@ class PmsConnect(object):
|
|||
session_output = {'sessionKey': self.get_xml_attr(session, 'sessionKey'),
|
||||
'art': self.get_xml_attr(session, 'art'),
|
||||
'thumb': thumb,
|
||||
'parentThumb': self.get_xml_attr(session, 'parentThumb'),
|
||||
'user': self.get_xml_attr(session.getElementsByTagName('User')[0], 'title'),
|
||||
'friendly_name': plex_watch.get_user_friendly_name(
|
||||
self.get_xml_attr(session.getElementsByTagName('User')[0], 'title')),
|
||||
'player': self.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'state': self.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
||||
'grandparentTitle': self.get_xml_attr(session, 'grandparentTitle'),
|
||||
'parentTitle': self.get_xml_attr(session, 'parentTitle'),
|
||||
'title': self.get_xml_attr(session, 'title'),
|
||||
'ratingKey': self.get_xml_attr(session, 'ratingKey'),
|
||||
'audioDecision': audio_decision,
|
||||
|
|
|
@ -274,8 +274,6 @@ class WebInterface(object):
|
|||
"xbmc_host": plexpy.CONFIG.XBMC_HOST,
|
||||
"xbmc_username": plexpy.CONFIG.XBMC_USERNAME,
|
||||
"xbmc_password": plexpy.CONFIG.XBMC_PASSWORD,
|
||||
"lms_enabled": checked(plexpy.CONFIG.LMS_ENABLED),
|
||||
"lms_host": plexpy.CONFIG.LMS_HOST,
|
||||
"plex_enabled": checked(plexpy.CONFIG.PLEX_ENABLED),
|
||||
"plex_client_host": plexpy.CONFIG.PLEX_CLIENT_HOST,
|
||||
"plex_username": plexpy.CONFIG.PLEX_USERNAME,
|
||||
|
@ -285,7 +283,6 @@ class WebInterface(object):
|
|||
"nma_priority": int(plexpy.CONFIG.NMA_PRIORITY),
|
||||
"pushalot_enabled": checked(plexpy.CONFIG.PUSHALOT_ENABLED),
|
||||
"pushalot_apikey": plexpy.CONFIG.PUSHALOT_APIKEY,
|
||||
"synoindex_enabled": checked(plexpy.CONFIG.SYNOINDEX_ENABLED),
|
||||
"pushover_enabled": checked(plexpy.CONFIG.PUSHOVER_ENABLED),
|
||||
"pushover_keys": plexpy.CONFIG.PUSHOVER_KEYS,
|
||||
"pushover_apitoken": plexpy.CONFIG.PUSHOVER_APITOKEN,
|
||||
|
@ -293,17 +290,12 @@ class WebInterface(object):
|
|||
"pushbullet_enabled": checked(plexpy.CONFIG.PUSHBULLET_ENABLED),
|
||||
"pushbullet_apikey": plexpy.CONFIG.PUSHBULLET_APIKEY,
|
||||
"pushbullet_deviceid": plexpy.CONFIG.PUSHBULLET_DEVICEID,
|
||||
"subsonic_enabled": checked(plexpy.CONFIG.SUBSONIC_ENABLED),
|
||||
"subsonic_host": plexpy.CONFIG.SUBSONIC_HOST,
|
||||
"subsonic_username": plexpy.CONFIG.SUBSONIC_USERNAME,
|
||||
"subsonic_password": plexpy.CONFIG.SUBSONIC_PASSWORD,
|
||||
"twitter_enabled": checked(plexpy.CONFIG.TWITTER_ENABLED),
|
||||
"osx_notify_enabled": checked(plexpy.CONFIG.OSX_NOTIFY_ENABLED),
|
||||
"osx_notify_app": plexpy.CONFIG.OSX_NOTIFY_APP,
|
||||
"boxcar_enabled": checked(plexpy.CONFIG.BOXCAR_ENABLED),
|
||||
"boxcar_token": plexpy.CONFIG.BOXCAR_TOKEN,
|
||||
"cache_sizemb": plexpy.CONFIG.CACHE_SIZEMB,
|
||||
"mpc_enabled": checked(plexpy.CONFIG.MPC_ENABLED),
|
||||
"email_enabled": checked(plexpy.CONFIG.EMAIL_ENABLED),
|
||||
"email_from": plexpy.CONFIG.EMAIL_FROM,
|
||||
"email_to": plexpy.CONFIG.EMAIL_TO,
|
||||
|
@ -333,11 +325,11 @@ class WebInterface(object):
|
|||
|
||||
checked_configs = [
|
||||
"launch_browser", "enable_https", "api_enabled", "freeze_db", "growl_enabled",
|
||||
"prowl_enabled", "xbmc_enabled", "lms_enabled",
|
||||
"prowl_enabled", "xbmc_enabled",
|
||||
"plex_enabled", "nma_enabled", "pushalot_enabled",
|
||||
"synoindex_enabled", "pushover_enabled", "pushbullet_enabled",
|
||||
"subsonic_enabled", "twitter_enabled", "osx_notify_enabled",
|
||||
"boxcar_enabled", "mpc_enabled", "email_enabled", "email_tls",
|
||||
"pushover_enabled", "pushbullet_enabled",
|
||||
"twitter_enabled", "osx_notify_enabled",
|
||||
"boxcar_enabled", "email_enabled", "email_tls",
|
||||
"grouping_global_history", "grouping_user_history", "grouping_charts", "pms_use_bif"
|
||||
]
|
||||
for checked_config in checked_configs:
|
||||
|
@ -873,6 +865,18 @@ class WebInterface(object):
|
|||
else:
|
||||
logger.warn('Unable to retrieve data.')
|
||||
|
||||
@cherrypy.expose
|
||||
def get_activity(self, **kwargs):
|
||||
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
result = pms_connect.get_current_activity()
|
||||
|
||||
if result:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps(result)
|
||||
else:
|
||||
logger.warn('Unable to retrieve data.')
|
||||
|
||||
@cherrypy.expose
|
||||
def get_full_users_list(self, **kwargs):
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue