Looking for new items...
@@ -138,6 +138,51 @@
complete: function(xhr, status) {
$("#recentlyAdded").html(xhr.responseText);
highlightAddedScrollerButton();
+ if ($('.dashboard-recent-media-instance li[data-type=movie]').length) { $('#toggle-recently-added-movie').removeClass('disabled'); }
+ if ($('.dashboard-recent-media-instance li[data-type=season]').length) { $('#toggle-recently-added-season').removeClass('disabled'); }
+ if ($('.dashboard-recent-media-instance li[data-type=album]').length) { $('#toggle-recently-added-album').removeClass('disabled'); }
+
+ $('.toggle-recently-added-type').not('.disabled').click(function () {
+ var scroller = $("#recently-added-row-scroller");
+ var media_type = $(this).data('type');
+ var margin_right = $(this).hasClass('text-muted') ? '25px' : 0;
+ var toggle_items = $('.dashboard-recent-media-instance li[data-type=' + media_type + ']');
+ var containerWidth = $("body").find(".container-fluid").width();
+
+ if (margin_right == 0) {
+ toggle_items.animate({ width: 'toggle', marginRight: margin_right }, 1000, function () {
+ toggle_items.hide();
+
+ var scroller_width = $('.dashboard-recent-media-instance li:visible').length * 175;
+ scroller.width(scroller_width);
+
+ if (scroller_width < containerWidth) {
+ $("#recently-added-page-right").addClass("disabled").blur();
+ } else {
+ $("#recently-added-page-right").removeClass("disabled");
+ }
+ })
+ } else {
+ scroller.width(50 * 175);
+ toggle_items.animate({ width: 'toggle', marginRight: margin_right }, 1000, function () {
+ toggle_items.show();
+
+ var scroller_width = $('.dashboard-recent-media-instance li:visible').length * 175;
+ scroller.width(scroller_width);
+
+ if (scroller_width < containerWidth) {
+ $("#recently-added-page-right").addClass("disabled").blur();
+ } else {
+ $("#recently-added-page-right").removeClass("disabled");
+ }
+ })
+ }
+
+ leftTotal = 0;
+ scroller.animate({ left: leftTotal }, 1000);
+ $("#recently-added-page-left").addClass("disabled").blur();
+ $(this).toggleClass('text-muted').blur();
+ });
}
});
}
@@ -156,7 +201,7 @@
function highlightAddedScrollerButton() {
var scroller = $("#recently-added-row-scroller");
- var numElems = scroller.find("li").length;
+ var numElems = scroller.find("li:visible").length;
scroller.width(numElems * 175);
if (scroller.width() > $("body").find(".container-fluid").width()) {
$("#recently-added-page-right").removeClass("disabled");
@@ -192,13 +237,6 @@
$("#recently-added-page-right").removeClass("disabled");
}
});
-
- $('.toggle-recently-added-type').click(function () {
- var media_type = $(this).data('type');
- var margin_right = $(this).hasClass('text-muted') ? '25px' : 0;
- $('.dashboard-recent-media-instance li[data-type=' + media_type + ']').animate({ width: 'toggle', marginRight: margin_right }, 1000);
- $(this).toggleClass('text-muted').blur();
- });
%def>
diff --git a/plexpy/common.py b/plexpy/common.py
index 9dce4283..440c4728 100644
--- a/plexpy/common.py
+++ b/plexpy/common.py
@@ -42,6 +42,7 @@ notify_strings[NOTIFY_STOPPED] = "Playback stopped"
DEFAULT_USER_THUMB = "interfaces/default/images/gravatar-default-80x80.png"
DEFAULT_POSTER_THUMB = "interfaces/default/images/poster.png"
DEFAULT_COVER_THUMB = "interfaces/default/images/cover.png"
+DEFAULT_ART = "interfaces/default/images/art.png"
PLATFORM_NAME_OVERRIDES = {'Konvergo': 'Plex Media Player',
'Mystery 3': 'Playstation 3',
diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py
index 47473af3..7401b3e8 100644
--- a/plexpy/datafactory.py
+++ b/plexpy/datafactory.py
@@ -148,7 +148,7 @@ class DataFactory(object):
dict = {'recordsFiltered': query['filteredCount'],
'recordsTotal': query['totalCount'],
- 'data': helpers.filter_datatable_session(rows),
+ 'data': helpers.filter_session_info(rows, 'user_id'),
'draw': query['draw'],
'filter_duration': helpers.human_duration(filter_duration, sig='dhm'),
'total_duration': helpers.human_duration(total_duration, sig='dhm')
@@ -168,7 +168,7 @@ class DataFactory(object):
if stat == 'top_tv':
top_tv = []
try:
- query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, ' \
+ query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.d) AS total_duration ' \
'FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - ' \
' (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) ' \
@@ -196,6 +196,7 @@ class DataFactory(object):
'last_play': item['last_watch'],
'grandparent_thumb': item['grandparent_thumb'],
'thumb': '',
+ 'section_id': item['section_id'],
'user': '',
'friendly_name': '',
'platform_type': '',
@@ -206,12 +207,12 @@ class DataFactory(object):
home_stats.append({'stat_id': stat,
'stat_type': sort_type,
- 'rows': top_tv})
+ 'rows': helpers.filter_session_info(top_tv, 'section_id')})
elif stat == 'popular_tv':
popular_tv = []
try:
- query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, ' \
+ query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
'COUNT(DISTINCT t.user_id) AS users_watched, ' \
'MAX(t.started) AS last_watch, COUNT(t.id) as total_plays, SUM(t.d) AS total_duration ' \
'FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - ' \
@@ -239,6 +240,7 @@ class DataFactory(object):
'total_plays': item['total_plays'],
'grandparent_thumb': item['grandparent_thumb'],
'thumb': '',
+ 'section_id': item['section_id'],
'user': '',
'friendly_name': '',
'platform_type': '',
@@ -248,12 +250,12 @@ class DataFactory(object):
popular_tv.append(row)
home_stats.append({'stat_id': stat,
- 'rows': popular_tv})
+ 'rows': helpers.filter_session_info(popular_tv, 'section_id')})
elif stat == 'top_movies':
top_movies = []
try:
- query = 'SELECT t.id, t.full_title, t.rating_key, t.thumb, ' \
+ query = 'SELECT t.id, t.full_title, t.rating_key, t.thumb, t.section_id, ' \
'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.d) AS total_duration ' \
'FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - ' \
' (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) ' \
@@ -281,6 +283,7 @@ class DataFactory(object):
'last_play': item['last_watch'],
'grandparent_thumb': '',
'thumb': item['thumb'],
+ 'section_id': item['section_id'],
'user': '',
'friendly_name': '',
'platform_type': '',
@@ -290,12 +293,12 @@ class DataFactory(object):
top_movies.append(row)
home_stats.append({'stat_id': stat,
'stat_type': sort_type,
- 'rows': top_movies})
+ 'rows': helpers.filter_session_info(top_movies, 'section_id')})
elif stat == 'popular_movies':
popular_movies = []
try:
- query = 'SELECT t.id, t.full_title, t.rating_key, t.thumb, ' \
+ query = 'SELECT t.id, t.full_title, t.rating_key, t.thumb, t.section_id, ' \
'COUNT(DISTINCT t.user_id) AS users_watched, ' \
'MAX(t.started) AS last_watch, COUNT(t.id) as total_plays, SUM(t.d) AS total_duration ' \
'FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - ' \
@@ -323,6 +326,7 @@ class DataFactory(object):
'total_plays': item['total_plays'],
'grandparent_thumb': '',
'thumb': item['thumb'],
+ 'section_id': item['section_id'],
'user': '',
'friendly_name': '',
'platform_type': '',
@@ -332,12 +336,12 @@ class DataFactory(object):
popular_movies.append(row)
home_stats.append({'stat_id': stat,
- 'rows': popular_movies})
+ 'rows': helpers.filter_session_info(popular_movies, 'section_id')})
elif stat == 'top_music':
top_music = []
try:
- query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, ' \
+ query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.d) AS total_duration ' \
'FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - ' \
' (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) ' \
@@ -365,6 +369,7 @@ class DataFactory(object):
'last_play': item['last_watch'],
'grandparent_thumb': item['grandparent_thumb'],
'thumb': '',
+ 'section_id': item['section_id'],
'user': '',
'friendly_name': '',
'platform_type': '',
@@ -375,12 +380,12 @@ class DataFactory(object):
home_stats.append({'stat_id': stat,
'stat_type': sort_type,
- 'rows': top_music})
+ 'rows': helpers.filter_session_info(top_music, 'section_id')})
elif stat == 'popular_music':
popular_music = []
try:
- query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, ' \
+ query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
'COUNT(DISTINCT t.user_id) AS users_watched, ' \
'MAX(t.started) AS last_watch, COUNT(t.id) as total_plays, SUM(t.d) AS total_duration ' \
'FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - ' \
@@ -408,6 +413,7 @@ class DataFactory(object):
'total_plays': item['total_plays'],
'grandparent_thumb': item['grandparent_thumb'],
'thumb': '',
+ 'section_id': item['section_id'],
'user': '',
'friendly_name': '',
'platform_type': '',
@@ -417,7 +423,7 @@ class DataFactory(object):
popular_music.append(row)
home_stats.append({'stat_id': stat,
- 'rows': popular_music})
+ 'rows': helpers.filter_session_info(popular_music, 'section_id')})
elif stat == 'top_users':
top_users = []
@@ -470,7 +476,7 @@ class DataFactory(object):
home_stats.append({'stat_id': stat,
'stat_type': sort_type,
- 'rows': helpers.filter_datatable_session(top_users)})
+ 'rows': helpers.mask_session_info(top_users)})
elif stat == 'top_platforms':
top_platform = []
@@ -567,7 +573,7 @@ class DataFactory(object):
last_watched.append(row)
home_stats.append({'stat_id': stat,
- 'rows': helpers.filter_datatable_session(last_watched)})
+ 'rows': helpers.filter_session_info(last_watched, 'user_id')})
elif stat == 'most_concurrent':
@@ -686,7 +692,7 @@ class DataFactory(object):
}
library_stats.append(library)
- return library_stats
+ return helpers.filter_session_info(library_stats, 'section_id')
def get_stream_details(self, row_id=None):
monitor_db = database.MonitorDatabase()
diff --git a/plexpy/helpers.py b/plexpy/helpers.py
index 8b2fffaf..334beed3 100644
--- a/plexpy/helpers.py
+++ b/plexpy/helpers.py
@@ -33,7 +33,8 @@ from xml.dom import minidom
import xmltodict
import plexpy
-from api2 import API2
+from plexpy import common
+from plexpy.api2 import API2
def addtoapi(*dargs, **dkwargs):
@@ -564,18 +565,102 @@ def uploadToImgur(imgPath, imgTitle=''):
return img_url
-def filter_datatable_session(list_of_dicts):
+
+def allow_session_user(user_id):
+ """
+ Returns True or False if the user_id is allowed for the user session
+ """
import cherrypy
from plexpy.webauth import SESSION_KEY
if cherrypy.config.get('tools.auth.on'):
- _session = {}
- _cp_session = cherrypy.session.get(SESSION_KEY)
- _session['username'], _session['user_id'], _session['user_group'], _session['expiry'] = \
- _cp_session if _cp_session else (None, None, None, None)
+ _session = cherrypy.session.get(SESSION_KEY)
+ if str(user_id) != _session['user_id']:
+ return False
+
+ return True
+
+def allow_session_library(section_id):
+ """
+ Returns True or False if the section_id is allowed for the user session
+ """
+ import cherrypy
+ from plexpy.webauth import SESSION_KEY
+
+ if cherrypy.config.get('tools.auth.on'):
+ _session = cherrypy.session.get(SESSION_KEY)
+ if str(section_id) not in _session['user_libraries']:
+ return False
+
+ return True
+
+def filter_session_info(list_of_dicts, filter_key=None):
+ """
+ Filters a list of dictionary items to only return the info for the current logged in user
+ """
+ import cherrypy
+ from plexpy.webauth import SESSION_KEY
+
+ if cherrypy.config.get('tools.auth.on'):
+ _session = cherrypy.session.get(SESSION_KEY)
+
+ if filter_key == 'user_id' and _session['user_id']:
+ session_user_id = str(_session['user_id'])
+ return [d for d in list_of_dicts if str(d.get('user_id','')) == session_user_id]
+
+ elif filter_key == 'section_id' and _session['user_libraries']:
+ session_library_ids = _session['user_libraries']
+ return [d for d in list_of_dicts if str(d.get('section_id','')) in session_library_ids]
+
+ return list_of_dicts
+
+def mask_session_info(list_of_dicts, mask_metadata=False):
+ """
+ Masks user info in a list of dictionary items to only display info for the current logged in user
+ """
+ import cherrypy
+ from plexpy.webauth import SESSION_KEY
+
+ if cherrypy.config.get('tools.auth.on'):
+ _session = cherrypy.session.get(SESSION_KEY)
+
+ keys_to_mask = {'user_id': '',
+ 'user': '',
+ 'friendly_name': 'Plex User',
+ 'user_thumb': common.DEFAULT_USER_THUMB,
+ 'ip_address': 'N/A',
+ 'machine_id': ''
+ }
+
+ metadata_to_mask = {'media_index': '',
+ 'parent_media_index': '',
+ 'art': common.DEFAULT_ART,
+ 'parent_thumb': common.DEFAULT_POSTER_THUMB,
+ 'grandparent_thumb': common.DEFAULT_POSTER_THUMB,
+ 'thumb': common.DEFAULT_POSTER_THUMB,
+ 'bif_thumb': '',
+ 'grandparent_title': '',
+ 'parent_title': '',
+ 'title': '',
+ 'rating_key': '',
+ 'parent_rating_key': '',
+ 'grandparent_rating_key': '',
+ 'year': ''
+ }
if _session['user_id']:
session_user_id = str(_session['user_id'])
- return [d for d in list_of_dicts if str(d.get('user_id')) == session_user_id]
+ session_library_ids = _session['user_libraries']
+
+ for d in list_of_dicts:
+ if not (str(d.get('user_id')) == session_user_id or d.get('user') == _session['user']):
+ for k, v in keys_to_mask.iteritems():
+ if k in d: d[k] = keys_to_mask[k]
+
+ if mask_metadata and str(d.get('section_id','')) not in session_library_ids:
+ for k, v in metadata_to_mask.iteritems():
+ if k in d: d[k] = metadata_to_mask[k]
+
+ return list_of_dicts
return list_of_dicts
\ No newline at end of file
diff --git a/plexpy/libraries.py b/plexpy/libraries.py
index 4b35376b..88997e31 100644
--- a/plexpy/libraries.py
+++ b/plexpy/libraries.py
@@ -222,7 +222,7 @@ class Libraries(object):
dict = {'recordsFiltered': query['filteredCount'],
'recordsTotal': query['totalCount'],
- 'data': rows,
+ 'data': helpers.filter_session_info(rows, 'section_id'),
'draw': query['draw']
}
@@ -700,7 +700,7 @@ class Libraries(object):
}
user_stats.append(row)
- return helpers.filter_datatable_session(user_stats)
+ return helpers.filter_session_info(user_stats, 'user_id')
def get_recently_watched(self, section_id=None, limit='10'):
monitor_db = database.MonitorDatabase()
diff --git a/plexpy/plextv.py b/plexpy/plextv.py
index 3b41adbf..13864a97 100644
--- a/plexpy/plextv.py
+++ b/plexpy/plextv.py
@@ -401,7 +401,7 @@ class PlexTV(object):
synced_items.append(sync_details)
- return helpers.filter_datatable_session(synced_items)
+ return helpers.filter_session_info(synced_items, 'user_id')
def get_server_urls(self, include_https=True):
diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py
index a2c63754..35d0255f 100644
--- a/plexpy/pmsconnect.py
+++ b/plexpy/pmsconnect.py
@@ -94,7 +94,7 @@ class PmsConnect(object):
Retrieve data from Plex Server
"""
- def __init__(self):
+ def __init__(self, token=None):
if plexpy.CONFIG.PMS_URL:
url_parsed = urlparse(plexpy.CONFIG.PMS_URL)
hostname = url_parsed.hostname
@@ -105,9 +105,11 @@ class PmsConnect(object):
port = plexpy.CONFIG.PMS_PORT
self.protocol = 'http'
+ token = token if token else plexpy.CONFIG.PMS_TOKEN
+
self.request_handler = http_handler.HTTPHandler(host=hostname,
port=port,
- token=plexpy.CONFIG.PMS_TOKEN)
+ token=token)
def get_sessions(self, output_format=''):
"""
@@ -492,7 +494,8 @@ class PmsConnect(object):
}
recents_list.append(recent_items)
- output = {'recently_added': sorted(recents_list, key=lambda k: k['added_at'], reverse=True)}
+ output = {'recently_added': helpers.filter_session_info(
+ sorted(recents_list, key=lambda k: k['added_at'], reverse=True), 'section_id')}
return output
def get_metadata_details(self, rating_key='', get_media_info=False):
@@ -972,7 +975,7 @@ class PmsConnect(object):
session_list.append(session_output)
output = {'stream_count': helpers.get_xml_attr(xml_head[0], 'size'),
- 'sessions': session_list
+ 'sessions': helpers.mask_session_info(session_list, True)
}
return output
diff --git a/plexpy/users.py b/plexpy/users.py
index acac14ba..38ac9ce6 100644
--- a/plexpy/users.py
+++ b/plexpy/users.py
@@ -190,7 +190,7 @@ class Users(object):
dict = {'recordsFiltered': query['filteredCount'],
'recordsTotal': query['totalCount'],
- 'data': helpers.filter_datatable_session(rows),
+ 'data': helpers.filter_session_info(rows, 'user_id'),
'draw': query['draw']
}
@@ -284,7 +284,7 @@ class Users(object):
dict = {'recordsFiltered': query['filteredCount'],
'recordsTotal': query['totalCount'],
- 'data': helpers.filter_datatable_session(rows),
+ 'data': helpers.filter_session_info(rows, 'user_id'),
'draw': query['draw']
}
diff --git a/plexpy/webauth.py b/plexpy/webauth.py
index 4b11d408..9c2adf82 100644
--- a/plexpy/webauth.py
+++ b/plexpy/webauth.py
@@ -26,6 +26,7 @@ from datetime import datetime, timedelta
import plexpy
from plexpy import logger
from plexpy.users import Users, user_login
+from plexpy.pmsconnect import PmsConnect
SESSION_KEY = '_cp_username'
@@ -50,11 +51,10 @@ def check_auth(*args, **kwargs):
conditions that the user must fulfill"""
conditions = cherrypy.request.config.get('auth.require', None)
if conditions is not None:
- cp_sesssion = cherrypy.session.get(SESSION_KEY)
- username, user_id, user_group, expiry = cp_sesssion if cp_sesssion else (None, None, None, None)
+ _session = cherrypy.session.get(SESSION_KEY)
- if (username and expiry) and expiry > datetime.now():
- cherrypy.request.login = username
+ if _session and (_session['user'] and _session['expiry']) and _session['expiry'] > datetime.now():
+ cherrypy.request.login = _session['user']
for condition in conditions:
# A condition is just a callable that returns true or false
if not condition():
@@ -148,14 +148,25 @@ class AuthController(object):
if user_group == 'guest':
user_details = Users().get_details(user=username)
user_id = user_details['user_id']
+
+ user_tokens = Users().get_tokens(user_id=user_details['user_id'])
+ server_token = user_tokens['server_token']
+
+ library_details = PmsConnect(token=server_token).get_server_children()
+ user_libraries = tuple(d['section_id'] for d in library_details['libraries_list'])
else:
user_id = None
+ user_libraries = None
expiry = datetime.now() + (timedelta(days=30) if remember_me == '1' else timedelta(minutes=60))
cherrypy.session.regenerate()
cherrypy.request.login = username
- cherrypy.session[SESSION_KEY] = (username, user_id, user_group, expiry)
+ cherrypy.session[SESSION_KEY] = {'user_id': user_id,
+ 'user': username,
+ 'user_group': user_group,
+ 'user_libraries': user_libraries,
+ 'expiry': expiry}
self.on_login(username)
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
@@ -169,11 +180,10 @@ class AuthController(object):
if not plexpy.CONFIG.HTTP_PASSWORD:
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
- cp_sesssion = cherrypy.session.get(SESSION_KEY)
- username, user_id, user_group, expiry = cp_sesssion if cp_sesssion else (None, None, None, None)
+ _session = cherrypy.session.get(SESSION_KEY)
cherrypy.session[SESSION_KEY] = None
- if username:
+ if _session['user']:
cherrypy.request.login = None
- self.on_logout(username)
+ self.on_logout(_session['user'])
raise cherrypy.HTTPRedirect("login")
\ No newline at end of file
diff --git a/plexpy/webserve.py b/plexpy/webserve.py
index a9771194..0d1f6449 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -49,15 +49,13 @@ def serve_template(templatename, **kwargs):
server_name = plexpy.CONFIG.PMS_NAME
- _session = {'username': None,
- 'user_id': None,
+ _session = {'user_id': None,
+ 'user': None,
'user_group': 'admin',
'expiry': None}
if cherrypy.config.get('tools.auth.on'):
- _cp_session = cherrypy.session.get(SESSION_KEY)
- _session['username'], _session['user_id'], _session['user_group'], _session['expiry'] = \
- _cp_session if _cp_session else (None, None, 'admin', None)
+ _session = cherrypy.session.get(SESSION_KEY)
try:
template = _hplookup.get_template(templatename)
@@ -173,6 +171,7 @@ class WebInterface(object):
return serve_template(templatename="index.html", title="Home", config=config)
@cherrypy.expose
+ @requireAuth()
@addtoapi()
def get_date_formats(self):
""" Get the date and time formats used by plexpy """
@@ -193,6 +192,7 @@ class WebInterface(object):
return json.dumps(formats)
@cherrypy.expose
+ @requireAuth()
def get_current_activity(self, **kwargs):
try:
@@ -215,6 +215,7 @@ class WebInterface(object):
return serve_template(templatename="current_activity.html", data=None)
@cherrypy.expose
+ @requireAuth()
def get_current_activity_header(self, **kwargs):
try:
@@ -230,6 +231,7 @@ class WebInterface(object):
return serve_template(templatename="current_activity_header.html", data=None)
@cherrypy.expose
+ @requireAuth()
def home_stats(self, **kwargs):
data_factory = datafactory.DataFactory()
@@ -250,6 +252,7 @@ class WebInterface(object):
return serve_template(templatename="home_stats.html", title="Stats", data=stats_data)
@cherrypy.expose
+ @requireAuth()
def library_stats(self, **kwargs):
data_factory = datafactory.DataFactory()
@@ -260,6 +263,7 @@ class WebInterface(object):
return serve_template(templatename="library_stats.html", title="Library Stats", data=stats_data)
@cherrypy.expose
+ @requireAuth()
def get_recently_added(self, count='0', **kwargs):
try: