mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-07 13:41:15 -07:00
Run futurize --stage1
This commit is contained in:
parent
221be380ee
commit
ab6196589b
36 changed files with 736 additions and 497 deletions
|
@ -17,7 +17,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
|
|
@ -13,13 +13,18 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import range
|
||||
|
||||
import datetime
|
||||
import os
|
||||
from Queue import Queue
|
||||
import queue
|
||||
import sqlite3
|
||||
import sys
|
||||
import subprocess
|
||||
import threading
|
||||
import datetime
|
||||
import uuid
|
||||
|
||||
# Some cut down versions of Python may not include this module and it's not critical for us
|
||||
|
@ -34,24 +39,25 @@ from apscheduler.triggers.interval import IntervalTrigger
|
|||
from UniversalAnalytics import Tracker
|
||||
import pytz
|
||||
|
||||
import activity_handler
|
||||
import activity_pinger
|
||||
import common
|
||||
import database
|
||||
import datafactory
|
||||
import libraries
|
||||
import logger
|
||||
import mobile_app
|
||||
import newsletters
|
||||
import newsletter_handler
|
||||
import notification_handler
|
||||
import notifiers
|
||||
import plextv
|
||||
import users
|
||||
import versioncheck
|
||||
import web_socket
|
||||
import webstart
|
||||
import plexpy.config
|
||||
from plexpy import activity_handler
|
||||
from plexpy import activity_pinger
|
||||
from plexpy import common
|
||||
from plexpy import database
|
||||
from plexpy import datafactory
|
||||
from plexpy import libraries
|
||||
from plexpy import logger
|
||||
from plexpy import mobile_app
|
||||
from plexpy import newsletters
|
||||
from plexpy import newsletter_handler
|
||||
from plexpy import notification_handler
|
||||
from plexpy import notifiers
|
||||
from plexpy import plextv
|
||||
from plexpy import users
|
||||
from plexpy import versioncheck
|
||||
from plexpy import web_socket
|
||||
from plexpy import webstart
|
||||
from plexpy import config
|
||||
|
||||
|
||||
PROG_DIR = None
|
||||
FULL_PATH = None
|
||||
|
@ -74,7 +80,7 @@ DOCKER = False
|
|||
SCHED = None
|
||||
SCHED_LOCK = threading.Lock()
|
||||
|
||||
NOTIFY_QUEUE = Queue()
|
||||
NOTIFY_QUEUE = queue.Queue()
|
||||
|
||||
INIT_LOCK = threading.Lock()
|
||||
_INITIALIZED = False
|
||||
|
@ -128,7 +134,7 @@ def initialize(config_file):
|
|||
global UMASK
|
||||
global _UPDATE
|
||||
|
||||
CONFIG = plexpy.config.Config(config_file)
|
||||
CONFIG = config.Config(config_file)
|
||||
CONFIG_FILE = config_file
|
||||
|
||||
assert CONFIG is not None
|
||||
|
@ -137,7 +143,7 @@ def initialize(config_file):
|
|||
return False
|
||||
|
||||
if CONFIG.HTTP_PORT < 21 or CONFIG.HTTP_PORT > 65535:
|
||||
plexpy.logger.warn("HTTP_PORT out of bounds: 21 < %s < 65535", CONFIG.HTTP_PORT)
|
||||
logger.warn("HTTP_PORT out of bounds: 21 < %s < 65535", CONFIG.HTTP_PORT)
|
||||
CONFIG.HTTP_PORT = 8181
|
||||
|
||||
if not CONFIG.HTTPS_CERT:
|
||||
|
@ -162,7 +168,7 @@ def initialize(config_file):
|
|||
' - {}'.format(common.PLATFORM_LINUX_DISTRO) if common.PLATFORM_LINUX_DISTRO else ''
|
||||
))
|
||||
logger.info("{} (UTC{})".format(
|
||||
plexpy.SYS_TIMEZONE.zone, plexpy.SYS_UTC_OFFSET
|
||||
SYS_TIMEZONE.zone, SYS_UTC_OFFSET
|
||||
))
|
||||
logger.info("Python {}".format(
|
||||
sys.version
|
||||
|
@ -379,29 +385,29 @@ def win_system_tray():
|
|||
from systray import SysTrayIcon
|
||||
|
||||
def tray_open(sysTrayIcon):
|
||||
launch_browser(plexpy.CONFIG.HTTP_HOST, plexpy.HTTP_PORT, plexpy.HTTP_ROOT)
|
||||
launch_browser(CONFIG.HTTP_HOST, HTTP_PORT, HTTP_ROOT)
|
||||
|
||||
def tray_check_update(sysTrayIcon):
|
||||
versioncheck.check_update()
|
||||
|
||||
def tray_update(sysTrayIcon):
|
||||
if plexpy.UPDATE_AVAILABLE:
|
||||
plexpy.SIGNAL = 'update'
|
||||
if UPDATE_AVAILABLE:
|
||||
SIGNAL = 'update'
|
||||
else:
|
||||
hover_text = common.PRODUCT + ' - No Update Available'
|
||||
plexpy.WIN_SYS_TRAY_ICON.update(hover_text=hover_text)
|
||||
WIN_SYS_TRAY_ICON.update(hover_text=hover_text)
|
||||
|
||||
def tray_restart(sysTrayIcon):
|
||||
plexpy.SIGNAL = 'restart'
|
||||
SIGNAL = 'restart'
|
||||
|
||||
def tray_quit(sysTrayIcon):
|
||||
plexpy.SIGNAL = 'shutdown'
|
||||
SIGNAL = 'shutdown'
|
||||
|
||||
if plexpy.UPDATE_AVAILABLE:
|
||||
icon = os.path.join(plexpy.PROG_DIR, 'data/interfaces/', plexpy.CONFIG.INTERFACE, 'images/logo_tray-update.ico')
|
||||
if UPDATE_AVAILABLE:
|
||||
icon = os.path.join(PROG_DIR, 'data/interfaces/', CONFIG.INTERFACE, 'images/logo_tray-update.ico')
|
||||
hover_text = common.PRODUCT + ' - Update Available!'
|
||||
else:
|
||||
icon = os.path.join(plexpy.PROG_DIR, 'data/interfaces/', plexpy.CONFIG.INTERFACE, 'images/logo_tray.ico')
|
||||
icon = os.path.join(PROG_DIR, 'data/interfaces/', CONFIG.INTERFACE, 'images/logo_tray.ico')
|
||||
hover_text = common.PRODUCT
|
||||
|
||||
menu_options = (('Open Tautulli', None, tray_open, 'default'),
|
||||
|
@ -413,11 +419,11 @@ def win_system_tray():
|
|||
logger.info("Launching system tray icon.")
|
||||
|
||||
try:
|
||||
plexpy.WIN_SYS_TRAY_ICON = SysTrayIcon(icon, hover_text, menu_options, on_quit=tray_quit)
|
||||
plexpy.WIN_SYS_TRAY_ICON.start()
|
||||
WIN_SYS_TRAY_ICON = SysTrayIcon(icon, hover_text, menu_options, on_quit=tray_quit)
|
||||
WIN_SYS_TRAY_ICON.start()
|
||||
except Exception as e:
|
||||
logger.error("Unable to launch system tray icon: %s." % e)
|
||||
plexpy.WIN_SYS_TRAY_ICON = None
|
||||
WIN_SYS_TRAY_ICON = None
|
||||
|
||||
|
||||
def initialize_scheduler():
|
||||
|
@ -2055,12 +2061,12 @@ def initialize_tracker():
|
|||
'dataSource': 'server',
|
||||
'appName': common.PRODUCT,
|
||||
'appVersion': common.RELEASE,
|
||||
'appId': plexpy.INSTALL_TYPE,
|
||||
'appInstallerId': plexpy.CONFIG.GIT_BRANCH,
|
||||
'appId': INSTALL_TYPE,
|
||||
'appInstallerId': CONFIG.GIT_BRANCH,
|
||||
'dimension1': '{} {}'.format(common.PLATFORM, common.PLATFORM_RELEASE), # App Platform
|
||||
'dimension2': common.PLATFORM_LINUX_DISTRO, # Linux Distro
|
||||
'userLanguage': plexpy.SYS_LANGUAGE,
|
||||
'documentEncoding': plexpy.SYS_ENCODING,
|
||||
'userLanguage': SYS_LANGUAGE,
|
||||
'documentEncoding': SYS_ENCODING,
|
||||
'noninteractive': True
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import time
|
||||
|
@ -21,12 +25,12 @@ from apscheduler.triggers.date import DateTrigger
|
|||
import pytz
|
||||
|
||||
import plexpy
|
||||
import activity_processor
|
||||
import datafactory
|
||||
import helpers
|
||||
import logger
|
||||
import notification_handler
|
||||
import pmsconnect
|
||||
from plexpy import activity_processor
|
||||
from plexpy import datafactory
|
||||
from plexpy import helpers
|
||||
from plexpy import logger
|
||||
from plexpy import notification_handler
|
||||
from plexpy import pmsconnect
|
||||
|
||||
|
||||
ACTIVITY_SCHED = None
|
||||
|
|
|
@ -13,20 +13,23 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
|
||||
import threading
|
||||
import time
|
||||
|
||||
import plexpy
|
||||
import activity_handler
|
||||
import activity_processor
|
||||
import database
|
||||
import helpers
|
||||
import libraries
|
||||
import logger
|
||||
import notification_handler
|
||||
import plextv
|
||||
import pmsconnect
|
||||
import web_socket
|
||||
from plexpy import activity_handler
|
||||
from plexpy import activity_processor
|
||||
from plexpy import database
|
||||
from plexpy import helpers
|
||||
from plexpy import libraries
|
||||
from plexpy import logger
|
||||
from plexpy import notification_handler
|
||||
from plexpy import plextv
|
||||
from plexpy import pmsconnect
|
||||
from plexpy import web_socket
|
||||
|
||||
|
||||
monitor_lock = threading.Lock()
|
||||
|
|
|
@ -13,17 +13,21 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
|
||||
from collections import defaultdict
|
||||
import json
|
||||
import time
|
||||
|
||||
import plexpy
|
||||
import database
|
||||
import helpers
|
||||
import libraries
|
||||
import logger
|
||||
import pmsconnect
|
||||
import users
|
||||
from plexpy import database
|
||||
from plexpy import helpers
|
||||
from plexpy import libraries
|
||||
from plexpy import logger
|
||||
from plexpy import pmsconnect
|
||||
from plexpy import users
|
||||
|
||||
|
||||
class ActivityProcessor(object):
|
||||
|
@ -498,7 +502,7 @@ class ActivityProcessor(object):
|
|||
if state:
|
||||
values['state'] = state
|
||||
|
||||
for k, v in kwargs.iteritems():
|
||||
for k, v in kwargs.items():
|
||||
values[k] = v
|
||||
|
||||
keys = {'session_key': session_key}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
|
@ -17,6 +16,10 @@
|
|||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
|
||||
import hashlib
|
||||
import inspect
|
||||
import json
|
||||
|
@ -30,22 +33,22 @@ import cherrypy
|
|||
import xmltodict
|
||||
|
||||
import plexpy
|
||||
import config
|
||||
import database
|
||||
import helpers
|
||||
import libraries
|
||||
import logger
|
||||
import mobile_app
|
||||
import notification_handler
|
||||
import notifiers
|
||||
import newsletter_handler
|
||||
import newsletters
|
||||
import users
|
||||
from plexpy import config
|
||||
from plexpy import database
|
||||
from plexpy import helpers
|
||||
from plexpy import libraries
|
||||
from plexpy import logger
|
||||
from plexpy import mobile_app
|
||||
from plexpy import notification_handler
|
||||
from plexpy import notifiers
|
||||
from plexpy import newsletter_handler
|
||||
from plexpy import newsletters
|
||||
from plexpy import users
|
||||
|
||||
|
||||
class API2:
|
||||
class API2(object):
|
||||
def __init__(self, **kwargs):
|
||||
self._api_valid_methods = self._api_docs().keys()
|
||||
self._api_valid_methods = list(self._api_docs().keys())
|
||||
self._api_authenticated = False
|
||||
self._api_out_type = 'json' # default
|
||||
self._api_msg = None
|
||||
|
@ -201,7 +204,7 @@ class API2:
|
|||
except IndexError:
|
||||
# We assume this is a traceback
|
||||
tl = (len(templog) - 1)
|
||||
templog[tl]['msg'] += helpers.sanitize(unicode(line.replace('\n', ''), 'utf-8'))
|
||||
templog[tl]['msg'] += helpers.sanitize(str(line.replace('\n', ''), 'utf-8'))
|
||||
continue
|
||||
|
||||
if len(line) > 1 and temp_loglevel_and_time is not None and loglvl in line:
|
||||
|
@ -209,7 +212,7 @@ class API2:
|
|||
d = {
|
||||
'time': temp_loglevel_and_time[0],
|
||||
'loglevel': loglvl,
|
||||
'msg': helpers.sanitize(unicode(msg.replace('\n', ''), 'utf-8')),
|
||||
'msg': helpers.sanitize(str(msg.replace('\n', ''), 'utf-8')),
|
||||
'thread': thread
|
||||
}
|
||||
templog.append(d)
|
||||
|
@ -227,7 +230,7 @@ class API2:
|
|||
|
||||
if search:
|
||||
logger.api_debug("Tautulli APIv2 :: Searching log values for '%s'" % search)
|
||||
tt = [d for d in templog for k, v in d.items() if search.lower() in v.lower()]
|
||||
tt = [d for d in templog for k, v in list(d.items()) if search.lower() in v.lower()]
|
||||
|
||||
if len(tt):
|
||||
templog = tt
|
||||
|
@ -235,7 +238,7 @@ class API2:
|
|||
if regex:
|
||||
tt = []
|
||||
for l in templog:
|
||||
stringdict = ' '.join('{}{}'.format(k, v) for k, v in l.items())
|
||||
stringdict = ' '.join('{}{}'.format(k, v) for k, v in list(l.items()))
|
||||
if reg.search(stringdict):
|
||||
tt.append(l)
|
||||
|
||||
|
@ -271,10 +274,10 @@ class API2:
|
|||
config = {}
|
||||
|
||||
# Truthify the dict
|
||||
for k, v in conf.iteritems():
|
||||
for k, v in conf.items():
|
||||
if isinstance(v, dict):
|
||||
d = {}
|
||||
for kk, vv in v.iteritems():
|
||||
for kk, vv in v.items():
|
||||
if vv == '0' or vv == '1':
|
||||
d[kk] = bool(vv)
|
||||
else:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
|
@ -18,12 +20,16 @@
|
|||
#########################################
|
||||
|
||||
|
||||
import urllib
|
||||
from __future__ import absolute_import
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
|
||||
from common import USER_AGENT
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
|
||||
from plexpy.common import USER_AGENT
|
||||
|
||||
|
||||
class PlexPyURLopener(urllib.FancyURLopener):
|
||||
class PlexPyURLopener(urllib.request.FancyURLopener):
|
||||
version = USER_AGENT
|
||||
|
||||
|
||||
|
@ -44,7 +50,7 @@ class AuthURLOpener(PlexPyURLopener):
|
|||
self.numTries = 0
|
||||
|
||||
# call the base class
|
||||
urllib.FancyURLopener.__init__(self)
|
||||
urllib.request.FancyURLopener.__init__(self)
|
||||
|
||||
def prompt_user_passwd(self, host, realm):
|
||||
"""
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
|
@ -13,17 +15,21 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import distro
|
||||
import platform
|
||||
from collections import OrderedDict
|
||||
|
||||
import version
|
||||
from plexpy import version
|
||||
|
||||
|
||||
# Identify Our Application
|
||||
PRODUCT = 'Tautulli'
|
||||
PLATFORM = platform.system()
|
||||
PLATFORM_RELEASE = platform.release()
|
||||
PLATFORM_VERSION = platform.version()
|
||||
PLATFORM_LINUX_DISTRO = ' '.join(x for x in platform.linux_distribution() if x)
|
||||
PLATFORM_LINUX_DISTRO = ' '.join(x for x in distro.linux_distribution() if x)
|
||||
PLATFORM_DEVICE_NAME = platform.node()
|
||||
BRANCH = version.PLEXPY_BRANCH
|
||||
RELEASE = version.PLEXPY_RELEASE_VERSION
|
||||
|
@ -98,7 +104,7 @@ PLATFORM_NAMES = {
|
|||
'xbmc': 'xbmc',
|
||||
'xbox': 'xbox'
|
||||
}
|
||||
PLATFORM_NAMES = OrderedDict(sorted(PLATFORM_NAMES.items(), key=lambda k: k[0], reverse=True))
|
||||
PLATFORM_NAMES = OrderedDict(sorted(list(PLATFORM_NAMES.items()), key=lambda k: k[0], reverse=True))
|
||||
|
||||
MEDIA_FLAGS_AUDIO = {
|
||||
'ac.?3': 'dolby_digital',
|
||||
|
@ -147,7 +153,7 @@ VIDEO_QUALITY_PROFILES = {
|
|||
96: '0.096 Mbps',
|
||||
64: '0.064 Mbps'
|
||||
}
|
||||
VIDEO_QUALITY_PROFILES = OrderedDict(sorted(VIDEO_QUALITY_PROFILES.items(), key=lambda k: k[0], reverse=True))
|
||||
VIDEO_QUALITY_PROFILES = OrderedDict(sorted(list(VIDEO_QUALITY_PROFILES.items()), key=lambda k: k[0], reverse=True))
|
||||
|
||||
AUDIO_QUALITY_PROFILES = {
|
||||
512: '512 kbps',
|
||||
|
@ -157,7 +163,7 @@ AUDIO_QUALITY_PROFILES = {
|
|||
128: '128 kbps',
|
||||
96: '96 kbps'
|
||||
}
|
||||
AUDIO_QUALITY_PROFILES = OrderedDict(sorted(AUDIO_QUALITY_PROFILES.items(), key=lambda k: k[0], reverse=True))
|
||||
AUDIO_QUALITY_PROFILES = OrderedDict(sorted(list(AUDIO_QUALITY_PROFILES.items()), key=lambda k: k[0], reverse=True))
|
||||
|
||||
HW_DECODERS = [
|
||||
'dxva2',
|
||||
|
|
110
plexpy/config.py
110
plexpy/config.py
|
@ -13,6 +13,10 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from past.builtins import basestring
|
||||
from builtins import object
|
||||
|
||||
import arrow
|
||||
import os
|
||||
import re
|
||||
|
@ -22,7 +26,7 @@ import time
|
|||
from configobj import ConfigObj
|
||||
|
||||
import plexpy
|
||||
import logger
|
||||
from plexpy import logger
|
||||
|
||||
|
||||
def bool_int(value):
|
||||
|
@ -49,7 +53,7 @@ _CONFIG_DEFINITIONS = {
|
|||
'PMS_IS_REMOTE': (int, 'PMS', 0),
|
||||
'PMS_LOGS_FOLDER': (str, 'PMS', ''),
|
||||
'PMS_LOGS_LINE_CAP': (int, 'PMS', 1000),
|
||||
'PMS_NAME': (unicode, 'PMS', ''),
|
||||
'PMS_NAME': (str, 'PMS', ''),
|
||||
'PMS_PORT': (int, 'PMS', 32400),
|
||||
'PMS_TOKEN': (str, 'PMS', ''),
|
||||
'PMS_SSL': (int, 'PMS', 0),
|
||||
|
@ -345,35 +349,35 @@ _CONFIG_DEFINITIONS = {
|
|||
'NOTIFY_CONCURRENT_BY_IP': (int, 'Monitoring', 0),
|
||||
'NOTIFY_CONCURRENT_THRESHOLD': (int, 'Monitoring', 2),
|
||||
'NOTIFY_WATCHED_PERCENT': (int, 'Monitoring', 85),
|
||||
'NOTIFY_ON_START_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_START_BODY_TEXT': (unicode, 'Monitoring', '{user} ({player}) started playing {title}.'),
|
||||
'NOTIFY_ON_STOP_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_STOP_BODY_TEXT': (unicode, 'Monitoring', '{user} ({player}) has stopped {title}.'),
|
||||
'NOTIFY_ON_PAUSE_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_PAUSE_BODY_TEXT': (unicode, 'Monitoring', '{user} ({player}) has paused {title}.'),
|
||||
'NOTIFY_ON_RESUME_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_RESUME_BODY_TEXT': (unicode, 'Monitoring', '{user} ({player}) has resumed {title}.'),
|
||||
'NOTIFY_ON_BUFFER_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_BUFFER_BODY_TEXT': (unicode, 'Monitoring', '{user} ({player}) is buffering {title}.'),
|
||||
'NOTIFY_ON_WATCHED_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_WATCHED_BODY_TEXT': (unicode, 'Monitoring', '{user} ({player}) has watched {title}.'),
|
||||
'NOTIFY_ON_CREATED_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_CREATED_BODY_TEXT': (unicode, 'Monitoring', '{title} was recently added to Plex.'),
|
||||
'NOTIFY_ON_EXTDOWN_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_EXTDOWN_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server remote access is down.'),
|
||||
'NOTIFY_ON_INTDOWN_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_INTDOWN_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server is down.'),
|
||||
'NOTIFY_ON_EXTUP_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_EXTUP_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server remote access is back up.'),
|
||||
'NOTIFY_ON_INTUP_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_INTUP_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server is back up.'),
|
||||
'NOTIFY_ON_PMSUPDATE_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_PMSUPDATE_BODY_TEXT': (unicode, 'Monitoring', 'An update is available for the Plex Media Server (version {update_version}).'),
|
||||
'NOTIFY_ON_CONCURRENT_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_CONCURRENT_BODY_TEXT': (unicode, 'Monitoring', '{user} has {user_streams} concurrent streams.'),
|
||||
'NOTIFY_ON_NEWDEVICE_SUBJECT_TEXT': (unicode, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_NEWDEVICE_BODY_TEXT': (unicode, 'Monitoring', '{user} is streaming from a new device: {player}.'),
|
||||
'NOTIFY_SCRIPTS_ARGS_TEXT': (unicode, 'Monitoring', ''),
|
||||
'NOTIFY_ON_START_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_START_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) started playing {title}.'),
|
||||
'NOTIFY_ON_STOP_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_STOP_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) has stopped {title}.'),
|
||||
'NOTIFY_ON_PAUSE_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_PAUSE_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) has paused {title}.'),
|
||||
'NOTIFY_ON_RESUME_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_RESUME_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) has resumed {title}.'),
|
||||
'NOTIFY_ON_BUFFER_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_BUFFER_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) is buffering {title}.'),
|
||||
'NOTIFY_ON_WATCHED_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_WATCHED_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) has watched {title}.'),
|
||||
'NOTIFY_ON_CREATED_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_CREATED_BODY_TEXT': (str, 'Monitoring', '{title} was recently added to Plex.'),
|
||||
'NOTIFY_ON_EXTDOWN_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_EXTDOWN_BODY_TEXT': (str, 'Monitoring', 'The Plex Media Server remote access is down.'),
|
||||
'NOTIFY_ON_INTDOWN_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_INTDOWN_BODY_TEXT': (str, 'Monitoring', 'The Plex Media Server is down.'),
|
||||
'NOTIFY_ON_EXTUP_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_EXTUP_BODY_TEXT': (str, 'Monitoring', 'The Plex Media Server remote access is back up.'),
|
||||
'NOTIFY_ON_INTUP_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_INTUP_BODY_TEXT': (str, 'Monitoring', 'The Plex Media Server is back up.'),
|
||||
'NOTIFY_ON_PMSUPDATE_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_PMSUPDATE_BODY_TEXT': (str, 'Monitoring', 'An update is available for the Plex Media Server (version {update_version}).'),
|
||||
'NOTIFY_ON_CONCURRENT_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_CONCURRENT_BODY_TEXT': (str, 'Monitoring', '{user} has {user_streams} concurrent streams.'),
|
||||
'NOTIFY_ON_NEWDEVICE_SUBJECT_TEXT': (str, 'Monitoring', 'Tautulli ({server_name})'),
|
||||
'NOTIFY_ON_NEWDEVICE_BODY_TEXT': (str, 'Monitoring', '{user} is streaming from a new device: {player}.'),
|
||||
'NOTIFY_SCRIPTS_ARGS_TEXT': (str, 'Monitoring', ''),
|
||||
'OSX_NOTIFY_APP': (str, 'OSX_Notify', '/Applications/Tautulli'),
|
||||
'OSX_NOTIFY_ENABLED': (int, 'OSX_Notify', 0),
|
||||
'OSX_NOTIFY_ON_PLAY': (int, 'OSX_Notify', 0),
|
||||
|
@ -512,7 +516,7 @@ _CONFIG_DEFINITIONS = {
|
|||
'SLACK_ON_CONCURRENT': (int, 'Slack', 0),
|
||||
'SLACK_ON_NEWDEVICE': (int, 'Slack', 0),
|
||||
'SCRIPTS_ENABLED': (int, 'Scripts', 0),
|
||||
'SCRIPTS_FOLDER': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_FOLDER': (str, 'Scripts', ''),
|
||||
'SCRIPTS_TIMEOUT': (int, 'Scripts', 30),
|
||||
'SCRIPTS_ON_PLAY': (int, 'Scripts', 0),
|
||||
'SCRIPTS_ON_STOP': (int, 'Scripts', 0),
|
||||
|
@ -528,20 +532,20 @@ _CONFIG_DEFINITIONS = {
|
|||
'SCRIPTS_ON_PMSUPDATE': (int, 'Scripts', 0),
|
||||
'SCRIPTS_ON_CONCURRENT': (int, 'Scripts', 0),
|
||||
'SCRIPTS_ON_NEWDEVICE': (int, 'Scripts', 0),
|
||||
'SCRIPTS_ON_PLAY_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_STOP_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_PAUSE_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_RESUME_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_BUFFER_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_WATCHED_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_CREATED_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_EXTDOWN_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_EXTUP_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_INTDOWN_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_INTUP_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_PMSUPDATE_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_CONCURRENT_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_NEWDEVICE_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_PLAY_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_STOP_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_PAUSE_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_RESUME_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_BUFFER_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_WATCHED_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_CREATED_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_EXTDOWN_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_EXTUP_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_INTDOWN_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_INTUP_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_PMSUPDATE_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_CONCURRENT_SCRIPT': (str, 'Scripts', ''),
|
||||
'SCRIPTS_ON_NEWDEVICE_SCRIPT': (str, 'Scripts', ''),
|
||||
'SYNCHRONOUS_MODE': (str, 'Advanced', 'NORMAL'),
|
||||
'TELEGRAM_BOT_TOKEN': (str, 'Telegram', ''),
|
||||
'TELEGRAM_ENABLED': (int, 'Telegram', 0),
|
||||
|
@ -680,7 +684,7 @@ class Config(object):
|
|||
""" Initialize the config with values from a file """
|
||||
self._config_file = config_file
|
||||
self._config = ConfigObj(self._config_file, encoding='utf-8')
|
||||
for key in _CONFIG_DEFINITIONS.keys():
|
||||
for key in list(_CONFIG_DEFINITIONS.keys()):
|
||||
self.check_setting(key)
|
||||
self._upgrade()
|
||||
self._blacklist()
|
||||
|
@ -689,8 +693,8 @@ class Config(object):
|
|||
""" Add tokens and passwords to blacklisted words in logger """
|
||||
blacklist = set()
|
||||
|
||||
for key, subkeys in self._config.iteritems():
|
||||
for subkey, value in subkeys.iteritems():
|
||||
for key, subkeys in self._config.items():
|
||||
for subkey, value in subkeys.items():
|
||||
if isinstance(value, basestring) and len(value.strip()) > 5 and \
|
||||
subkey.upper() not in _WHITELIST_KEYS and any(bk in subkey.upper() for bk in _BLACKLIST_KEYS):
|
||||
blacklist.add(value.strip())
|
||||
|
@ -733,14 +737,14 @@ class Config(object):
|
|||
|
||||
# first copy over everything from the old config, even if it is not
|
||||
# correctly defined to keep from losing data
|
||||
for key, subkeys in self._config.items():
|
||||
for key, subkeys in list(self._config.items()):
|
||||
if key not in new_config:
|
||||
new_config[key] = {}
|
||||
for subkey, value in subkeys.items():
|
||||
for subkey, value in list(subkeys.items()):
|
||||
new_config[key][subkey] = value
|
||||
|
||||
# next make sure that everything we expect to have defined is so
|
||||
for key in _CONFIG_DEFINITIONS.keys():
|
||||
for key in list(_CONFIG_DEFINITIONS.keys()):
|
||||
key, definition_type, section, ini_key, default = self._define(key)
|
||||
self.check_setting(key)
|
||||
if section not in new_config:
|
||||
|
@ -784,7 +788,7 @@ class Config(object):
|
|||
"""
|
||||
Given a big bunch of key value pairs, apply them to the ini.
|
||||
"""
|
||||
for name, value in kwargs.items():
|
||||
for name, value in list(kwargs.items()):
|
||||
key, definition_type, section, ini_key, default = self._define(name)
|
||||
self._config[section][ini_key] = definition_type(value)
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import object
|
||||
|
||||
import arrow
|
||||
import os
|
||||
import sqlite3
|
||||
|
@ -21,7 +24,8 @@ import threading
|
|||
import time
|
||||
|
||||
import plexpy
|
||||
import logger
|
||||
from plexpy import logger
|
||||
|
||||
|
||||
FILENAME = "tautulli.db"
|
||||
db_lock = threading.Lock()
|
||||
|
@ -198,21 +202,21 @@ class MonitorDatabase(object):
|
|||
trans_type = 'update'
|
||||
changes_before = self.connection.total_changes
|
||||
|
||||
gen_params = lambda my_dict: [x + " = ?" for x in my_dict.keys()]
|
||||
gen_params = lambda my_dict: [x + " = ?" for x in list(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())
|
||||
self.action(update_query, list(value_dict.values()) + list(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())) + ")"
|
||||
"INSERT INTO " + table_name + " (" + ", ".join(list(value_dict.keys()) + list(key_dict.keys())) + ")" +
|
||||
" VALUES (" + ", ".join(["?"] * len(list(value_dict.keys()) + list(key_dict.keys()))) + ")"
|
||||
)
|
||||
try:
|
||||
self.action(insert_query, value_dict.values() + key_dict.values())
|
||||
self.action(insert_query, list(value_dict.values()) + list(key_dict.values()))
|
||||
except sqlite3.IntegrityError:
|
||||
logger.info("Tautulli Database :: Queries failed: %s and %s", update_query, insert_query)
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,17 +15,24 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import absolute_import
|
||||
from builtins import next
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
from past.utils import old_div
|
||||
|
||||
import json
|
||||
from itertools import groupby
|
||||
|
||||
import plexpy
|
||||
import common
|
||||
import database
|
||||
import datatables
|
||||
import helpers
|
||||
import logger
|
||||
import pmsconnect
|
||||
import session
|
||||
from plexpy import common
|
||||
from plexpy import database
|
||||
from plexpy import datatables
|
||||
from plexpy import helpers
|
||||
from plexpy import logger
|
||||
from plexpy import pmsconnect
|
||||
from plexpy import session
|
||||
|
||||
|
||||
class DataFactory(object):
|
||||
|
@ -208,7 +217,7 @@ class DataFactory(object):
|
|||
|
||||
if item['percent_complete'] >= watched_percent[item['media_type']]:
|
||||
watched_status = 1
|
||||
elif item['percent_complete'] >= watched_percent[item['media_type']]/2:
|
||||
elif item['percent_complete'] >= old_div(watched_percent[item['media_type']],2):
|
||||
watched_status = 0.5
|
||||
else:
|
||||
watched_status = 0
|
||||
|
@ -655,7 +664,7 @@ class DataFactory(object):
|
|||
for item in result:
|
||||
# Rename Mystery platform names
|
||||
platform = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform'])
|
||||
platform_name = next((v for k, v in common.PLATFORM_NAMES.iteritems() if k in platform.lower()), 'default')
|
||||
platform_name = next((v for k, v in common.PLATFORM_NAMES.items() if k in platform.lower()), 'default')
|
||||
|
||||
row = {'total_plays': item['total_plays'],
|
||||
'total_duration': item['total_duration'],
|
||||
|
@ -985,7 +994,7 @@ class DataFactory(object):
|
|||
'pre_tautulli': pre_tautulli
|
||||
}
|
||||
|
||||
stream_output = {k: v or '' for k, v in stream_output.iteritems()}
|
||||
stream_output = {k: v or '' for k, v in stream_output.items()}
|
||||
return stream_output
|
||||
|
||||
def get_metadata_details(self, rating_key):
|
||||
|
@ -1518,7 +1527,7 @@ class DataFactory(object):
|
|||
# function to map rating keys pairs
|
||||
def get_pairs(old, new):
|
||||
pairs = {}
|
||||
for k, v in old.iteritems():
|
||||
for k, v in old.items():
|
||||
if k in new:
|
||||
pairs.update({v['rating_key']: new[k]['rating_key']})
|
||||
if 'children' in old[k]:
|
||||
|
@ -1533,7 +1542,7 @@ class DataFactory(object):
|
|||
|
||||
if mapping:
|
||||
logger.info("Tautulli DataFactory :: Updating metadata in the database.")
|
||||
for old_key, new_key in mapping.iteritems():
|
||||
for old_key, new_key in mapping.items():
|
||||
metadata = pms_connect.get_metadata_details(new_key)
|
||||
|
||||
if metadata:
|
||||
|
|
|
@ -13,11 +13,14 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import object
|
||||
|
||||
import re
|
||||
|
||||
import database
|
||||
import helpers
|
||||
import logger
|
||||
from plexpy import database
|
||||
from plexpy import helpers
|
||||
from plexpy import logger
|
||||
|
||||
|
||||
class DataTables(object):
|
||||
|
@ -90,7 +93,7 @@ class DataTables(object):
|
|||
filtered = self.ssp_db.select(query, args=args)
|
||||
|
||||
# Remove NULL rows
|
||||
filtered = [row for row in filtered if not all(v is None for v in row.values())]
|
||||
filtered = [row for row in filtered if not all(v is None for v in list(row.values()))]
|
||||
|
||||
# Build grand totals
|
||||
totalcount = self.ssp_db.select('SELECT COUNT(id) as total_count from %s' % table_name)[0]['total_count']
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,13 +15,18 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
from builtins import range
|
||||
from builtins import object
|
||||
|
||||
import datetime
|
||||
|
||||
import plexpy
|
||||
import common
|
||||
import database
|
||||
import logger
|
||||
import session
|
||||
from plexpy import common
|
||||
from plexpy import database
|
||||
from plexpy import logger
|
||||
from plexpy import session
|
||||
|
||||
|
||||
class Graphs(object):
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,6 +15,16 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import absolute_import
|
||||
from past.builtins import cmp
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import zip
|
||||
from builtins import str
|
||||
from past.builtins import basestring
|
||||
from past.utils import old_div
|
||||
|
||||
import base64
|
||||
import cloudinary
|
||||
from cloudinary.api import delete_resources_by_tag
|
||||
|
@ -20,12 +32,15 @@ from cloudinary.uploader import upload
|
|||
from cloudinary.utils import cloudinary_url
|
||||
import datetime
|
||||
from functools import wraps
|
||||
import geoip2.database, geoip2.errors
|
||||
import geoip2.database
|
||||
import geoip2.errors
|
||||
import gzip
|
||||
import hashlib
|
||||
import imghdr
|
||||
from itertools import izip_longest
|
||||
import ipwhois, ipwhois.exceptions, ipwhois.utils
|
||||
from itertools import zip_longest
|
||||
import ipwhois
|
||||
import ipwhois.exceptions
|
||||
import ipwhois.utils
|
||||
from IPy import IP
|
||||
import json
|
||||
import math
|
||||
|
@ -38,13 +53,18 @@ import socket
|
|||
import sys
|
||||
import time
|
||||
import unicodedata
|
||||
import urllib, urllib2
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
from xml.dom import minidom
|
||||
import xmltodict
|
||||
|
||||
import plexpy
|
||||
import logger
|
||||
import request
|
||||
from plexpy import logger
|
||||
from plexpy import request
|
||||
from plexpy.api2 import API2
|
||||
|
||||
|
||||
|
@ -158,7 +178,7 @@ def latinToAscii(unicrap):
|
|||
|
||||
def convert_milliseconds(ms):
|
||||
|
||||
seconds = ms / 1000
|
||||
seconds = old_div(ms, 1000)
|
||||
gmtime = time.gmtime(seconds)
|
||||
if seconds > 3600:
|
||||
minutes = time.strftime("%H:%M:%S", gmtime)
|
||||
|
@ -172,7 +192,7 @@ def convert_milliseconds_to_minutes(ms):
|
|||
|
||||
if str(ms).isdigit():
|
||||
seconds = float(ms) / 1000
|
||||
minutes = round(seconds / 60, 0)
|
||||
minutes = round(old_div(seconds, 60), 0)
|
||||
|
||||
return math.trunc(minutes)
|
||||
|
||||
|
@ -224,9 +244,9 @@ def human_duration(s, sig='dhms'):
|
|||
hd = ''
|
||||
|
||||
if str(s).isdigit() and s > 0:
|
||||
d = int(s / 86400)
|
||||
h = int((s % 86400) / 3600)
|
||||
m = int(((s % 86400) % 3600) / 60)
|
||||
d = int(old_div(s, 86400))
|
||||
h = int(old_div((s % 86400), 3600))
|
||||
m = int(old_div(((s % 86400) % 3600), 60))
|
||||
s = int(((s % 86400) % 3600) % 60)
|
||||
|
||||
hd_list = []
|
||||
|
@ -277,7 +297,7 @@ def get_age(date):
|
|||
|
||||
def bytes_to_mb(bytes):
|
||||
|
||||
mb = int(bytes) / 1048576
|
||||
mb = old_div(int(bytes), 1048576)
|
||||
size = '%.1f MB' % mb
|
||||
return size
|
||||
|
||||
|
@ -318,7 +338,7 @@ def replace_all(text, dic, normalize=False):
|
|||
if not text:
|
||||
return ''
|
||||
|
||||
for i, j in dic.iteritems():
|
||||
for i, j in dic.items():
|
||||
if normalize:
|
||||
try:
|
||||
if sys.platform == 'darwin':
|
||||
|
@ -476,7 +496,7 @@ def get_percent(value1, value2):
|
|||
value2 = cast_to_float(value2)
|
||||
|
||||
if value1 != 0 and value2 != 0:
|
||||
percent = (value1 / value2) * 100
|
||||
percent = (old_div(value1, value2)) * 100
|
||||
else:
|
||||
percent = 0
|
||||
|
||||
|
@ -543,11 +563,11 @@ def sanitize_out(*dargs, **dkwargs):
|
|||
|
||||
def sanitize(obj):
|
||||
if isinstance(obj, basestring):
|
||||
return unicode(obj).replace('<', '<').replace('>', '>')
|
||||
return str(obj).replace('<', '<').replace('>', '>')
|
||||
elif isinstance(obj, list):
|
||||
return [sanitize(o) for o in obj]
|
||||
elif isinstance(obj, dict):
|
||||
return {k: sanitize(v) for k, v in obj.iteritems()}
|
||||
return {k: sanitize(v) for k, v in obj.items()}
|
||||
elif isinstance(obj, tuple):
|
||||
return tuple(sanitize(list(obj)))
|
||||
else:
|
||||
|
@ -596,9 +616,9 @@ def install_geoip_db():
|
|||
# Retrieve the GeoLite2 gzip file
|
||||
logger.debug("Tautulli Helpers :: Downloading GeoLite2 gzip file from MaxMind...")
|
||||
try:
|
||||
maxmind = urllib.URLopener()
|
||||
maxmind = urllib.request.URLopener()
|
||||
maxmind.retrieve(maxmind_url + geolite2_gz, temp_gz)
|
||||
md5_checksum = urllib2.urlopen(maxmind_url + geolite2_md5).read()
|
||||
md5_checksum = urllib.request.urlopen(maxmind_url + geolite2_md5).read()
|
||||
except Exception as e:
|
||||
logger.error("Tautulli Helpers :: Failed to download GeoLite2 gzip file from MaxMind: %s" % e)
|
||||
return False
|
||||
|
@ -1151,7 +1171,7 @@ def grouper(iterable, n, fillvalue=None):
|
|||
"Collect data into fixed-length chunks or blocks"
|
||||
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
|
||||
args = [iter(iterable)] * n
|
||||
return izip_longest(fillvalue=fillvalue, *args)
|
||||
return zip_longest(fillvalue=fillvalue, *args)
|
||||
|
||||
|
||||
def traverse_map(obj, func):
|
||||
|
@ -1162,7 +1182,7 @@ def traverse_map(obj, func):
|
|||
|
||||
elif isinstance(obj, dict):
|
||||
new_obj = {}
|
||||
for k, v in obj.iteritems():
|
||||
for k, v in obj.items():
|
||||
new_obj[traverse_map(k, func)] = traverse_map(v, func)
|
||||
|
||||
else:
|
||||
|
@ -1187,7 +1207,7 @@ def mask_config_passwords(config):
|
|||
cfg['value'] = ' '
|
||||
|
||||
elif isinstance(config, dict):
|
||||
for cfg, val in config.iteritems():
|
||||
for cfg, val in config.items():
|
||||
# Check for a password config keys and if the password is not blank
|
||||
if 'password' in cfg and val != '':
|
||||
# Set the password to blank so it is not exposed in the HTML form
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of PlexPy.
|
||||
|
@ -16,16 +15,22 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from past.builtins import basestring
|
||||
from builtins import object
|
||||
|
||||
from functools import partial
|
||||
from multiprocessing.dummy import Pool as ThreadPool
|
||||
from urlparse import urljoin
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import certifi
|
||||
import urllib3
|
||||
|
||||
import plexpy
|
||||
import helpers
|
||||
import logger
|
||||
from plexpy import helpers
|
||||
from plexpy import logger
|
||||
|
||||
|
||||
class HTTPHandler(object):
|
||||
|
@ -78,7 +83,7 @@ class HTTPHandler(object):
|
|||
Output: list
|
||||
"""
|
||||
|
||||
self.uri = uri.encode('utf-8')
|
||||
self.uri = uri
|
||||
self.request_type = request_type.upper()
|
||||
self.output_format = output_format.lower()
|
||||
self.return_type = return_type
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,18 +15,23 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
from builtins import next
|
||||
from builtins import object
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
import plexpy
|
||||
import common
|
||||
import database
|
||||
import datatables
|
||||
import helpers
|
||||
import logger
|
||||
import plextv
|
||||
import pmsconnect
|
||||
import session
|
||||
from plexpy import common
|
||||
from plexpy import database
|
||||
from plexpy import datatables
|
||||
from plexpy import helpers
|
||||
from plexpy import logger
|
||||
from plexpy import plextv
|
||||
from plexpy import pmsconnect
|
||||
from plexpy import session
|
||||
|
||||
|
||||
def refresh_libraries():
|
||||
|
@ -213,7 +220,7 @@ def update_labels():
|
|||
% section_id)
|
||||
|
||||
error_keys = set()
|
||||
for rating_key, labels in key_mappings.iteritems():
|
||||
for rating_key, labels in key_mappings.items():
|
||||
try:
|
||||
labels = ';'.join(labels)
|
||||
monitor_db.action('UPDATE session_history_metadata SET labels = ? '
|
||||
|
@ -540,7 +547,7 @@ class Libraries(object):
|
|||
if search_value:
|
||||
searchable_columns = [d['data'] for d in json_data['columns'] if d['searchable']] + ['title']
|
||||
for row in rows:
|
||||
for k,v in row.iteritems():
|
||||
for k,v in row.items():
|
||||
if k in searchable_columns and search_value in v.lower():
|
||||
results.append(row)
|
||||
break
|
||||
|
|
|
@ -1,11 +1,28 @@
|
|||
"""
|
||||
Locking-related classes
|
||||
"""
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import plexpy.logger
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli 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.
|
||||
#
|
||||
# Tautulli 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 Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import object
|
||||
|
||||
import queue
|
||||
import time
|
||||
import threading
|
||||
import Queue
|
||||
|
||||
from plexpy import logger
|
||||
|
||||
|
||||
class TimedLock(object):
|
||||
|
@ -28,7 +45,7 @@ class TimedLock(object):
|
|||
self.lock = threading.Lock()
|
||||
self.last_used = 0
|
||||
self.minimum_delta = minimum_delta
|
||||
self.queue = Queue.Queue()
|
||||
self.queue = queue.Queue()
|
||||
|
||||
def __enter__(self):
|
||||
"""
|
||||
|
@ -39,14 +56,14 @@ class TimedLock(object):
|
|||
sleep_amount = self.minimum_delta - delta
|
||||
if sleep_amount >= 0:
|
||||
# zero sleeps give the cpu a chance to task-switch
|
||||
plexpy.logger.debug('Sleeping %s (interval)', sleep_amount)
|
||||
logger.debug('Sleeping %s (interval)', sleep_amount)
|
||||
time.sleep(sleep_amount)
|
||||
while not self.queue.empty():
|
||||
try:
|
||||
seconds = self.queue.get(False)
|
||||
plexpy.logger.debug('Sleeping %s (queued)', seconds)
|
||||
logger.debug('Sleeping %s (queued)', seconds)
|
||||
time.sleep(seconds)
|
||||
except Queue.Empty:
|
||||
except queue.Empty:
|
||||
continue
|
||||
self.queue.task_done()
|
||||
|
||||
|
@ -65,7 +82,7 @@ class TimedLock(object):
|
|||
"""
|
||||
# We use a queue so that we don't have to synchronize
|
||||
# across threads and with or without locks
|
||||
plexpy.logger.info('Adding %s to queue', seconds)
|
||||
logger.info('Adding %s to queue', seconds)
|
||||
self.queue.put(seconds)
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
|
@ -13,11 +15,15 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
|
||||
import os
|
||||
|
||||
import plexpy
|
||||
import helpers
|
||||
import logger
|
||||
from plexpy import helpers
|
||||
from plexpy import logger
|
||||
|
||||
|
||||
def get_log_tail(window=20, parsed=True, log_type="server"):
|
||||
|
||||
|
@ -45,7 +51,7 @@ def get_log_tail(window=20, parsed=True, log_type="server"):
|
|||
try:
|
||||
log_time = i.split(' [')[0]
|
||||
log_level = i.split('] ', 1)[1].split(' - ', 1)[0]
|
||||
log_msg = unicode(i.split('] ', 1)[1].split(' - ', 1)[1], 'utf-8')
|
||||
log_msg = str(i.split('] ', 1)[1].split(' - ', 1)[1], 'utf-8')
|
||||
full_line = [log_time, log_level, log_msg]
|
||||
clean_lines.append(full_line)
|
||||
except:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
|
@ -13,6 +15,10 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
from past.builtins import basestring
|
||||
|
||||
from logutils.queue import QueueHandler, QueueListener
|
||||
from logging import handlers
|
||||
|
||||
|
@ -27,9 +33,10 @@ import threading
|
|||
import traceback
|
||||
|
||||
import plexpy
|
||||
import helpers
|
||||
from plexpy.helpers import is_public_ip
|
||||
from plexpy.config import _BLACKLIST_KEYS, _WHITELIST_KEYS
|
||||
|
||||
|
||||
# These settings are for file logging only
|
||||
FILENAME = "tautulli.log"
|
||||
FILENAME_API = "tautulli_api.log"
|
||||
|
@ -54,7 +61,7 @@ def blacklist_config(config):
|
|||
blacklist = set()
|
||||
blacklist_keys = ['HOOK', 'APIKEY', 'KEY', 'PASSWORD', 'TOKEN']
|
||||
|
||||
for key, value in config.iteritems():
|
||||
for key, value in config.items():
|
||||
if isinstance(value, basestring) and len(value.strip()) > 5 and \
|
||||
key.upper() not in _WHITELIST_KEYS and (key.upper() in blacklist_keys or
|
||||
any(bk in key.upper() for bk in _BLACKLIST_KEYS)):
|
||||
|
@ -113,14 +120,14 @@ class PublicIPFilter(logging.Filter):
|
|||
# Currently only checking for ipv4 addresses
|
||||
ipv4 = re.findall(r'[0-9]+(?:\.[0-9]+){3}(?!\d*-[a-z0-9]{6})', record.msg)
|
||||
for ip in ipv4:
|
||||
if helpers.is_public_ip(ip):
|
||||
if is_public_ip(ip):
|
||||
record.msg = record.msg.replace(ip, ip.partition('.')[0] + '.***.***.***')
|
||||
|
||||
args = []
|
||||
for arg in record.args:
|
||||
ipv4 = re.findall(r'[0-9]+(?:\.[0-9]+){3}(?!\d*-[a-z0-9]{6})', arg) if isinstance(arg, basestring) else []
|
||||
for ip in ipv4:
|
||||
if helpers.is_public_ip(ip):
|
||||
if is_public_ip(ip):
|
||||
arg = arg.replace(ip, ip.partition('.')[0] + '.***.***.***')
|
||||
args.append(arg)
|
||||
record.args = tuple(args)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,12 +15,13 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
|
||||
import time
|
||||
|
||||
import plexpy
|
||||
import database
|
||||
import helpers
|
||||
import logger
|
||||
from plexpy import database
|
||||
from plexpy import logger
|
||||
|
||||
|
||||
TEMP_DEVICE_TOKEN = None
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,6 +15,8 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
|
@ -20,9 +24,9 @@ from apscheduler.triggers.cron import CronTrigger
|
|||
import email.utils
|
||||
|
||||
import plexpy
|
||||
import database
|
||||
import logger
|
||||
import newsletters
|
||||
from plexpy import database
|
||||
from plexpy import logger
|
||||
from plexpy import newsletters
|
||||
|
||||
|
||||
NEWSLETTER_SCHED = None
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,6 +15,11 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import next
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
|
||||
import arrow
|
||||
from collections import OrderedDict
|
||||
import json
|
||||
|
@ -23,14 +30,14 @@ import os
|
|||
import re
|
||||
|
||||
import plexpy
|
||||
import common
|
||||
import database
|
||||
import helpers
|
||||
import libraries
|
||||
import logger
|
||||
import newsletter_handler
|
||||
import pmsconnect
|
||||
from notifiers import send_notification, EMAIL
|
||||
from plexpy import common
|
||||
from plexpy import database
|
||||
from plexpy import helpers
|
||||
from plexpy import libraries
|
||||
from plexpy import logger
|
||||
from plexpy import newsletter_handler
|
||||
from plexpy import pmsconnect
|
||||
from plexpy.notifiers import send_notification, EMAIL
|
||||
|
||||
|
||||
AGENT_IDS = {
|
||||
|
@ -229,11 +236,11 @@ def set_newsletter_config(newsletter_id=None, agent_id=None, **kwargs):
|
|||
email_config_prefix = 'newsletter_email_'
|
||||
|
||||
newsletter_config = {k[len(config_prefix):]: kwargs.pop(k)
|
||||
for k in kwargs.keys() if k.startswith(config_prefix)}
|
||||
for k in list(kwargs.keys()) if k.startswith(config_prefix)}
|
||||
email_config = {k[len(email_config_prefix):]: kwargs.pop(k)
|
||||
for k in kwargs.keys() if k.startswith(email_config_prefix)}
|
||||
for k in list(kwargs.keys()) if k.startswith(email_config_prefix)}
|
||||
|
||||
for cfg, val in email_config.iteritems():
|
||||
for cfg, val in email_config.items():
|
||||
# Check for a password config keys and a blank password from the HTML form
|
||||
if 'password' in cfg and val == ' ':
|
||||
# Get the previous password so we don't overwrite it with a blank value
|
||||
|
@ -418,7 +425,7 @@ class Newsletter(object):
|
|||
return default
|
||||
|
||||
new_config = {}
|
||||
for k, v in default.iteritems():
|
||||
for k, v in default.items():
|
||||
if isinstance(v, int):
|
||||
new_config[k] = helpers.cast_to_int(config.get(k, v))
|
||||
elif isinstance(v, list):
|
||||
|
@ -602,50 +609,50 @@ class Newsletter(object):
|
|||
return parameters
|
||||
|
||||
def build_text(self):
|
||||
from notification_handler import CustomFormatter
|
||||
from plexpy.notification_handler import CustomFormatter
|
||||
custom_formatter = CustomFormatter()
|
||||
|
||||
try:
|
||||
subject = custom_formatter.format(unicode(self.subject), **self.parameters)
|
||||
subject = custom_formatter.format(str(self.subject), **self.parameters)
|
||||
except LookupError as e:
|
||||
logger.error("Tautulli Newsletter :: Unable to parse parameter %s in newsletter subject. Using fallback." % e)
|
||||
subject = unicode(self._DEFAULT_SUBJECT).format(**self.parameters)
|
||||
subject = str(self._DEFAULT_SUBJECT).format(**self.parameters)
|
||||
except Exception as e:
|
||||
logger.error("Tautulli Newsletter :: Unable to parse custom newsletter subject: %s. Using fallback." % e)
|
||||
subject = unicode(self._DEFAULT_SUBJECT).format(**self.parameters)
|
||||
subject = str(self._DEFAULT_SUBJECT).format(**self.parameters)
|
||||
|
||||
try:
|
||||
body = custom_formatter.format(unicode(self.body), **self.parameters)
|
||||
body = custom_formatter.format(str(self.body), **self.parameters)
|
||||
except LookupError as e:
|
||||
logger.error("Tautulli Newsletter :: Unable to parse parameter %s in newsletter body. Using fallback." % e)
|
||||
body = unicode(self._DEFAULT_BODY).format(**self.parameters)
|
||||
body = str(self._DEFAULT_BODY).format(**self.parameters)
|
||||
except Exception as e:
|
||||
logger.error("Tautulli Newsletter :: Unable to parse custom newsletter body: %s. Using fallback." % e)
|
||||
body = unicode(self._DEFAULT_BODY).format(**self.parameters)
|
||||
body = str(self._DEFAULT_BODY).format(**self.parameters)
|
||||
|
||||
try:
|
||||
message = custom_formatter.format(unicode(self.message), **self.parameters)
|
||||
message = custom_formatter.format(str(self.message), **self.parameters)
|
||||
except LookupError as e:
|
||||
logger.error("Tautulli Newsletter :: Unable to parse parameter %s in newsletter message. Using fallback." % e)
|
||||
message = unicode(self._DEFAULT_MESSAGE).format(**self.parameters)
|
||||
message = str(self._DEFAULT_MESSAGE).format(**self.parameters)
|
||||
except Exception as e:
|
||||
logger.error("Tautulli Newsletter :: Unable to parse custom newsletter message: %s. Using fallback." % e)
|
||||
message = unicode(self._DEFAULT_MESSAGE).format(**self.parameters)
|
||||
message = str(self._DEFAULT_MESSAGE).format(**self.parameters)
|
||||
|
||||
return subject, body, message
|
||||
|
||||
def build_filename(self):
|
||||
from notification_handler import CustomFormatter
|
||||
from plexpy.notification_handler import CustomFormatter
|
||||
custom_formatter = CustomFormatter()
|
||||
|
||||
try:
|
||||
filename = custom_formatter.format(unicode(self.filename), **self.parameters)
|
||||
filename = custom_formatter.format(str(self.filename), **self.parameters)
|
||||
except LookupError as e:
|
||||
logger.error("Tautulli Newsletter :: Unable to parse parameter %s in newsletter filename. Using fallback." % e)
|
||||
filename = unicode(self._DEFAULT_FILENAME).format(**self.parameters)
|
||||
filename = str(self._DEFAULT_FILENAME).format(**self.parameters)
|
||||
except Exception as e:
|
||||
logger.error("Tautulli Newsletter :: Unable to parse custom newsletter subject: %s. Using fallback." % e)
|
||||
filename = unicode(self._DEFAULT_FILENAME).format(**self.parameters)
|
||||
filename = str(self._DEFAULT_FILENAME).format(**self.parameters)
|
||||
|
||||
return filename
|
||||
|
||||
|
@ -682,7 +689,7 @@ class RecentlyAdded(Newsletter):
|
|||
_TEMPLATE = 'recently_added.html'
|
||||
|
||||
def _get_recently_added(self, media_type=None):
|
||||
from notification_handler import format_group_index
|
||||
from plexpy.notification_handler import format_group_index
|
||||
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
|
||||
|
@ -798,7 +805,7 @@ class RecentlyAdded(Newsletter):
|
|||
return recently_added
|
||||
|
||||
def retrieve_data(self):
|
||||
from notification_handler import get_img_info, set_hash_image_info
|
||||
from plexpy.notification_handler import get_img_info, set_hash_image_info
|
||||
|
||||
if not self.config['incl_libraries']:
|
||||
logger.warn("Tautulli Newsletters :: Failed to retrieve %s newsletter data: no libraries selected." % self.NAME)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -14,6 +16,17 @@
|
|||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import absolute_import
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import next
|
||||
from builtins import map
|
||||
from builtins import str
|
||||
from builtins import range
|
||||
from past.builtins import basestring
|
||||
from past.utils import old_div
|
||||
|
||||
import arrow
|
||||
import bleach
|
||||
from collections import Counter, defaultdict
|
||||
|
@ -31,19 +44,16 @@ import time
|
|||
import musicbrainzngs
|
||||
|
||||
import plexpy
|
||||
import activity_processor
|
||||
import common
|
||||
import database
|
||||
import datafactory
|
||||
import libraries
|
||||
import logger
|
||||
import helpers
|
||||
import notifiers
|
||||
import plextv
|
||||
import pmsconnect
|
||||
import request
|
||||
import users
|
||||
from newsletter_handler import notify as notify_newsletter
|
||||
from plexpy import activity_processor
|
||||
from plexpy import common
|
||||
from plexpy import database
|
||||
from plexpy import datafactory
|
||||
from plexpy import logger
|
||||
from plexpy import helpers
|
||||
from plexpy import notifiers
|
||||
from plexpy import pmsconnect
|
||||
from plexpy import request
|
||||
from plexpy.newsletter_handler import notify as notify_newsletter
|
||||
|
||||
|
||||
def process_queue():
|
||||
|
@ -254,7 +264,7 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
|
|||
# Cast the condition values to the correct type
|
||||
try:
|
||||
if parameter_type == 'str':
|
||||
values = [unicode(v).lower() for v in values]
|
||||
values = [str(v).lower() for v in values]
|
||||
|
||||
elif parameter_type == 'int':
|
||||
values = [helpers.cast_to_int(v) for v in values]
|
||||
|
@ -270,7 +280,7 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
|
|||
# Cast the parameter value to the correct type
|
||||
try:
|
||||
if parameter_type == 'str':
|
||||
parameter_value = unicode(parameter_value).lower()
|
||||
parameter_value = str(parameter_value).lower()
|
||||
|
||||
elif parameter_type == 'int':
|
||||
parameter_value = helpers.cast_to_int(parameter_value)
|
||||
|
@ -540,9 +550,9 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
|
|||
transcode_decision = 'Direct Play'
|
||||
|
||||
if notify_action != 'on_play':
|
||||
stream_duration = int((time.time() -
|
||||
stream_duration = int(old_div((time.time() -
|
||||
helpers.cast_to_int(session.get('started', 0)) -
|
||||
helpers.cast_to_int(session.get('paused_counter', 0))) / 60)
|
||||
helpers.cast_to_int(session.get('paused_counter', 0))), 60))
|
||||
else:
|
||||
stream_duration = 0
|
||||
|
||||
|
@ -1137,19 +1147,19 @@ def build_notify_text(subject='', body='', notify_action=None, parameters=None,
|
|||
subject = str_formatter(subject)
|
||||
except LookupError as e:
|
||||
logger.error("Tautulli NotificationHandler :: Unable to parse parameter %s in notification subject. Using fallback." % e)
|
||||
subject = unicode(default_subject).format(**parameters)
|
||||
subject = str(default_subject).format(**parameters)
|
||||
except Exception as e:
|
||||
logger.error("Tautulli NotificationHandler :: Unable to parse custom notification subject: %s. Using fallback." % e)
|
||||
subject = unicode(default_subject).format(**parameters)
|
||||
subject = str(default_subject).format(**parameters)
|
||||
|
||||
try:
|
||||
body = str_formatter(body)
|
||||
except LookupError as e:
|
||||
logger.error("Tautulli NotificationHandler :: Unable to parse parameter %s in notification body. Using fallback." % e)
|
||||
body = unicode(default_body).format(**parameters)
|
||||
body = str(default_body).format(**parameters)
|
||||
except Exception as e:
|
||||
logger.error("Tautulli NotificationHandler :: Unable to parse custom notification body: %s. Using fallback." % e)
|
||||
body = unicode(default_body).format(**parameters)
|
||||
body = str(default_body).format(**parameters)
|
||||
|
||||
return subject, body, script_args
|
||||
|
||||
|
@ -1165,7 +1175,7 @@ def strip_tag(data, agent_id=None):
|
|||
'u': [],
|
||||
'a': ['href'],
|
||||
'font': ['color']}
|
||||
data = bleach.clean(data, tags=whitelist.keys(), attributes=whitelist, strip=True)
|
||||
data = bleach.clean(data, tags=list(whitelist.keys()), attributes=whitelist, strip=True)
|
||||
|
||||
elif agent_id in (10, 14, 20):
|
||||
# Don't remove tags for Email, Slack, and Discord
|
||||
|
@ -1178,11 +1188,11 @@ def strip_tag(data, agent_id=None):
|
|||
'code': [],
|
||||
'pre': [],
|
||||
'a': ['href']}
|
||||
data = bleach.clean(data, tags=whitelist.keys(), attributes=whitelist, strip=True)
|
||||
data = bleach.clean(data, tags=list(whitelist.keys()), attributes=whitelist, strip=True)
|
||||
|
||||
else:
|
||||
whitelist = {}
|
||||
data = bleach.clean(data, tags=whitelist.keys(), attributes=whitelist, strip=True)
|
||||
data = bleach.clean(data, tags=list(whitelist.keys()), attributes=whitelist, strip=True)
|
||||
|
||||
# Resubstitute temporary tokens for < and > in parameter prefix and suffix
|
||||
return data.replace('%temp_lt_token%', '<').replace('%temp_gt_token%', '>')
|
||||
|
@ -1194,8 +1204,8 @@ def format_group_index(group_keys):
|
|||
num = []
|
||||
num00 = []
|
||||
|
||||
for k, g in groupby(enumerate(group_keys), lambda (i, x): i-x):
|
||||
group = map(itemgetter(1), g)
|
||||
for k, g in groupby(enumerate(group_keys), lambda i_x: i_x[0]-i_x[1]):
|
||||
group = list(map(itemgetter(1), g))
|
||||
g_min, g_max = min(group), max(group)
|
||||
|
||||
if g_min == g_max:
|
||||
|
@ -1586,7 +1596,7 @@ def lookup_musicbrainz_info(musicbrainz_type=None, rating_key=None, artist=None,
|
|||
def str_format(s, parameters):
|
||||
custom_formatter = CustomFormatter()
|
||||
if isinstance(s, basestring):
|
||||
return custom_formatter.format(unicode(s), **parameters)
|
||||
return custom_formatter.format(str(s), **parameters)
|
||||
return s
|
||||
|
||||
|
||||
|
@ -1602,11 +1612,11 @@ class CustomFormatter(Formatter):
|
|||
elif conversion == 'r':
|
||||
return repr(value)
|
||||
elif conversion == 'u': # uppercase
|
||||
return unicode(value).upper()
|
||||
return str(value).upper()
|
||||
elif conversion == 'l': # lowercase
|
||||
return unicode(value).lower()
|
||||
return str(value).lower()
|
||||
elif conversion == 'c': # capitalize
|
||||
return unicode(value).title()
|
||||
return str(value).title()
|
||||
else:
|
||||
return value
|
||||
|
||||
|
@ -1616,7 +1626,7 @@ class CustomFormatter(Formatter):
|
|||
match = re.match(pattern, format_spec)
|
||||
if value and match:
|
||||
groups = match.groupdict()
|
||||
items = [x.strip() for x in unicode(value).split(',')]
|
||||
items = [x.strip() for x in str(value).split(',')]
|
||||
start = groups['start'] or None
|
||||
end = groups['end'] or None
|
||||
if start is not None:
|
||||
|
@ -1666,7 +1676,7 @@ class CustomFormatter(Formatter):
|
|||
|
||||
if prefix or suffix:
|
||||
real_format_string = '{' + real_format_string + '}'
|
||||
_, field_name, format_spec, conversion, _, _ = self.parse(real_format_string).next()
|
||||
_, field_name, format_spec, conversion, _, _ = next(self.parse(real_format_string))
|
||||
|
||||
yield literal_text, field_name, format_spec, conversion, prefix, suffix
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,6 +15,13 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import next
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
|
||||
import base64
|
||||
import bleach
|
||||
import json
|
||||
|
@ -28,8 +37,8 @@ import subprocess
|
|||
import sys
|
||||
import threading
|
||||
import time
|
||||
from urllib import urlencode
|
||||
from urlparse import urlparse
|
||||
from urllib.parse import urlencode
|
||||
from urllib.parse import urlparse
|
||||
import uuid
|
||||
|
||||
try:
|
||||
|
@ -54,14 +63,14 @@ import twitter
|
|||
import pynma
|
||||
|
||||
import plexpy
|
||||
import common
|
||||
import database
|
||||
import helpers
|
||||
import logger
|
||||
import mobile_app
|
||||
import pmsconnect
|
||||
import request
|
||||
import users
|
||||
from plexpy import common
|
||||
from plexpy import database
|
||||
from plexpy import helpers
|
||||
from plexpy import logger
|
||||
from plexpy import mobile_app
|
||||
from plexpy import pmsconnect
|
||||
from plexpy import request
|
||||
from plexpy import users
|
||||
|
||||
|
||||
BROWSER_NOTIFIERS = {}
|
||||
|
@ -438,7 +447,7 @@ def get_notifiers(notifier_id=None, notify_action=None):
|
|||
% (', '.join(notify_actions), where), args=args)
|
||||
|
||||
for item in result:
|
||||
item['active'] = int(any([item.pop(k) for k in item.keys() if k in notify_actions]))
|
||||
item['active'] = int(any([item.pop(k) for k in list(item.keys()) if k in notify_actions]))
|
||||
|
||||
return result
|
||||
|
||||
|
@ -483,7 +492,7 @@ def get_notifier_config(notifier_id=None, mask_passwords=False):
|
|||
|
||||
notifier_actions = {}
|
||||
notifier_text = {}
|
||||
for k in result.keys():
|
||||
for k in list(result.keys()):
|
||||
if k in notify_actions:
|
||||
subject = result.pop(k + '_subject')
|
||||
body = result.pop(k + '_body')
|
||||
|
@ -581,15 +590,15 @@ def set_notifier_config(notifier_id=None, agent_id=None, **kwargs):
|
|||
config_prefix = agent['name'] + '_'
|
||||
|
||||
actions = {k: helpers.cast_to_int(kwargs.pop(k))
|
||||
for k in kwargs.keys() if k in notify_actions}
|
||||
for k in list(kwargs.keys()) if k in notify_actions}
|
||||
subject_text = {k: kwargs.pop(k)
|
||||
for k in kwargs.keys() if k.startswith(notify_actions) and k.endswith('_subject')}
|
||||
for k in list(kwargs.keys()) if k.startswith(notify_actions) and k.endswith('_subject')}
|
||||
body_text = {k: kwargs.pop(k)
|
||||
for k in kwargs.keys() if k.startswith(notify_actions) and k.endswith('_body')}
|
||||
for k in list(kwargs.keys()) if k.startswith(notify_actions) and k.endswith('_body')}
|
||||
notifier_config = {k[len(config_prefix):]: kwargs.pop(k)
|
||||
for k in kwargs.keys() if k.startswith(config_prefix)}
|
||||
for k in list(kwargs.keys()) if k.startswith(config_prefix)}
|
||||
|
||||
for cfg, val in notifier_config.iteritems():
|
||||
for cfg, val in notifier_config.items():
|
||||
# Check for a password config keys and a blank password from the HTML form
|
||||
if 'password' in cfg and val == ' ':
|
||||
# Get the previous password so we don't overwrite it with a blank value
|
||||
|
@ -793,7 +802,7 @@ class Notifier(object):
|
|||
return default
|
||||
|
||||
new_config = {}
|
||||
for k, v in default.iteritems():
|
||||
for k, v in default.items():
|
||||
if isinstance(v, int):
|
||||
new_config[k] = helpers.cast_to_int(config.get(k, v))
|
||||
elif isinstance(v, list):
|
||||
|
@ -1404,9 +1413,9 @@ class EMAIL(Notifier):
|
|||
user_emails_cc.update(emails)
|
||||
user_emails_bcc.update(emails)
|
||||
|
||||
user_emails_to = [{'value': k, 'text': v} for k, v in user_emails_to.iteritems()]
|
||||
user_emails_cc = [{'value': k, 'text': v} for k, v in user_emails_cc.iteritems()]
|
||||
user_emails_bcc = [{'value': k, 'text': v} for k, v in user_emails_bcc.iteritems()]
|
||||
user_emails_to = [{'value': k, 'text': v} for k, v in user_emails_to.items()]
|
||||
user_emails_cc = [{'value': k, 'text': v} for k, v in user_emails_cc.items()]
|
||||
user_emails_bcc = [{'value': k, 'text': v} for k, v in user_emails_bcc.items()]
|
||||
|
||||
return user_emails_to, user_emails_cc, user_emails_bcc
|
||||
|
||||
|
@ -2019,7 +2028,7 @@ class IFTTT(Notifier):
|
|||
}
|
||||
|
||||
def agent_notify(self, subject='', body='', action='', **kwargs):
|
||||
event = unicode(self.config['event']).format(action=action)
|
||||
event = str(self.config['event']).format(action=action)
|
||||
|
||||
data = {'value1': subject.encode('utf-8'),
|
||||
'value2': body.encode('utf-8')}
|
||||
|
@ -3043,7 +3052,7 @@ class SCRIPTS(Notifier):
|
|||
for root, dirs, files in os.walk(scriptdir):
|
||||
for f in files:
|
||||
name, ext = os.path.splitext(f)
|
||||
if ext in self.script_exts.keys():
|
||||
if ext in list(self.script_exts.keys()):
|
||||
rfp = os.path.join(os.path.relpath(root, scriptdir), f)
|
||||
fp = os.path.join(root, f)
|
||||
scripts[fp] = rfp
|
||||
|
@ -3187,7 +3196,7 @@ class SCRIPTS(Notifier):
|
|||
def _return_config_options(self):
|
||||
config_option = [{'label': 'Supported File Types',
|
||||
'description': '<span class="inline-pre">' + \
|
||||
', '.join(self.script_exts.keys()) + '</span>',
|
||||
', '.join(list(self.script_exts.keys())) + '</span>',
|
||||
'input_type': 'help'
|
||||
},
|
||||
{'label': 'Script Folder',
|
||||
|
@ -3947,7 +3956,7 @@ def upgrade_config_to_db():
|
|||
|
||||
# Update the new config with the old config values
|
||||
notifier_config = {}
|
||||
for conf, val in notifier_default_config.iteritems():
|
||||
for conf, val in notifier_default_config.items():
|
||||
c_key = agent_config_key + '_' + config_key_overrides.get(agent, {}).get(conf, conf)
|
||||
notifier_config[agent + '_' + conf] = agent_config.get(c_key, val)
|
||||
|
||||
|
@ -3964,15 +3973,15 @@ def upgrade_config_to_db():
|
|||
|
||||
# Reverse the dict to {script: [actions]}
|
||||
script_actions = {}
|
||||
for k, v in action_scripts.items():
|
||||
for k, v in list(action_scripts.items()):
|
||||
if v: script_actions.setdefault(v, set()).add(k)
|
||||
|
||||
# Add a new script notifier for each script if the action was enabled
|
||||
for script, actions in script_actions.items():
|
||||
for script, actions in list(script_actions.items()):
|
||||
if any(agent_actions[a] for a in actions):
|
||||
temp_config = notifier_config
|
||||
temp_config.update({a: 0 for a in agent_actions.keys()})
|
||||
temp_config.update({a + '_subject': '' for a in agent_actions.keys()})
|
||||
temp_config.update({a: 0 for a in list(agent_actions.keys())})
|
||||
temp_config.update({a + '_subject': '' for a in list(agent_actions.keys())})
|
||||
for a in actions:
|
||||
if agent_actions[a]:
|
||||
temp_config[a] = agent_actions[a]
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,17 +15,20 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
|
||||
import arrow
|
||||
import sqlite3
|
||||
from xml.dom import minidom
|
||||
|
||||
import plexpy
|
||||
import activity_pinger
|
||||
import activity_processor
|
||||
import database
|
||||
import helpers
|
||||
import logger
|
||||
import users
|
||||
from plexpy import activity_pinger
|
||||
from plexpy import activity_processor
|
||||
from plexpy import database
|
||||
from plexpy import helpers
|
||||
from plexpy import logger
|
||||
from plexpy import users
|
||||
|
||||
|
||||
def extract_plexivity_xml(xml=None):
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
|
@ -16,17 +15,22 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import next
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
|
||||
import base64
|
||||
import json
|
||||
|
||||
import plexpy
|
||||
import common
|
||||
import helpers
|
||||
import http_handler
|
||||
import logger
|
||||
import users
|
||||
import pmsconnect
|
||||
import session
|
||||
from plexpy import common
|
||||
from plexpy import helpers
|
||||
from plexpy import http_handler
|
||||
from plexpy import logger
|
||||
from plexpy import users
|
||||
from plexpy import pmsconnect
|
||||
from plexpy import session
|
||||
|
||||
|
||||
def get_server_resources(return_presence=False, return_server=False, **kwargs):
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,16 +15,19 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
|
||||
import sqlite3
|
||||
from xml.dom import minidom
|
||||
|
||||
import plexpy
|
||||
import activity_pinger
|
||||
import activity_processor
|
||||
import database
|
||||
import helpers
|
||||
import logger
|
||||
import users
|
||||
from plexpy import activity_pinger
|
||||
from plexpy import activity_processor
|
||||
from plexpy import database
|
||||
from plexpy import helpers
|
||||
from plexpy import logger
|
||||
from plexpy import users
|
||||
|
||||
|
||||
def extract_plexwatch_xml(xml=None):
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,20 +15,27 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import next
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import urllib
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
|
||||
import plexpy
|
||||
import activity_processor
|
||||
import common
|
||||
import helpers
|
||||
import http_handler
|
||||
import logger
|
||||
import plextv
|
||||
import session
|
||||
import users
|
||||
from plexpy import activity_processor
|
||||
from plexpy import common
|
||||
from plexpy import helpers
|
||||
from plexpy import http_handler
|
||||
from plexpy import logger
|
||||
from plexpy import plextv
|
||||
from plexpy import session
|
||||
from plexpy import users
|
||||
|
||||
|
||||
def get_server_friendly_name():
|
||||
|
@ -101,7 +110,7 @@ class PmsConnect(object):
|
|||
|
||||
Output: array
|
||||
"""
|
||||
uri = '/status/sessions/terminate?sessionId=%s&reason=%s' % (session_id, urllib.quote_plus(reason))
|
||||
uri = '/status/sessions/terminate?sessionId=%s&reason=%s' % (session_id, urllib.parse.quote_plus(reason))
|
||||
request = self.request_handler.make_request(uri=uri,
|
||||
request_type='GET',
|
||||
output_format=output_format)
|
||||
|
@ -352,7 +361,7 @@ class PmsConnect(object):
|
|||
|
||||
Output: array
|
||||
"""
|
||||
uri = '/hubs/search?query=' + urllib.quote(query.encode('utf8')) + '&limit=' + limit + '&includeCollections=1'
|
||||
uri = '/hubs/search?query=' + urllib.parse.quote(query.encode('utf8')) + '&limit=' + limit + '&includeCollections=1'
|
||||
request = self.request_handler.make_request(uri=uri,
|
||||
request_type='GET',
|
||||
output_format=output_format)
|
||||
|
@ -726,7 +735,7 @@ class PmsConnect(object):
|
|||
# Workaround for for duration sometimes reported in minutes for a show
|
||||
duration = helpers.get_xml_attr(metadata_main, 'duration')
|
||||
if duration.isdigit() and int(duration) < 1000:
|
||||
duration = unicode(int(duration) * 60 * 1000)
|
||||
duration = str(int(duration) * 60 * 1000)
|
||||
|
||||
metadata = {'media_type': metadata_type,
|
||||
'section_id': section_id,
|
||||
|
@ -1534,7 +1543,7 @@ class PmsConnect(object):
|
|||
if not platform and helpers.get_xml_attr(player_info, 'product') == 'DLNA':
|
||||
platform = 'DLNA'
|
||||
|
||||
platform_name = next((v for k, v in common.PLATFORM_NAMES.iteritems() if k in platform.lower()), 'default')
|
||||
platform_name = next((v for k, v in common.PLATFORM_NAMES.items() if k in platform.lower()), 'default')
|
||||
|
||||
player_details = {'ip_address': helpers.get_xml_attr(player_info, 'address').split('::ffff:')[-1],
|
||||
'ip_address_public': helpers.get_xml_attr(player_info, 'remotePublicAddress').split('::ffff:')[-1],
|
||||
|
@ -2254,7 +2263,7 @@ class PmsConnect(object):
|
|||
hub_identifier = helpers.get_xml_attr(h, 'hubIdentifier')
|
||||
|
||||
if size == '0' or not hub_identifier.startswith('collection.related') or \
|
||||
media_type not in children_results_list.keys():
|
||||
media_type not in list(children_results_list.keys()):
|
||||
continue
|
||||
|
||||
result_data = []
|
||||
|
@ -2280,7 +2289,7 @@ class PmsConnect(object):
|
|||
}
|
||||
children_results_list[media_type].append(children_output)
|
||||
|
||||
output = {'results_count': sum(len(s) for s in children_results_list.items()),
|
||||
output = {'results_count': sum(len(s) for s in list(children_results_list.items())),
|
||||
'results_list': children_results_list,
|
||||
}
|
||||
|
||||
|
@ -2648,9 +2657,9 @@ class PmsConnect(object):
|
|||
img = '{}/{}'.format(img.rstrip('/'), int(time.time()))
|
||||
|
||||
if clip:
|
||||
params = {'url': '%s&%s' % (img, urllib.urlencode({'X-Plex-Token': self.token}))}
|
||||
params = {'url': '%s&%s' % (img, urllib.parse.urlencode({'X-Plex-Token': self.token}))}
|
||||
else:
|
||||
params = {'url': 'http://127.0.0.1:32400%s?%s' % (img, urllib.urlencode({'X-Plex-Token': self.token}))}
|
||||
params = {'url': 'http://127.0.0.1:32400%s?%s' % (img, urllib.parse.urlencode({'X-Plex-Token': self.token}))}
|
||||
|
||||
params['width'] = width
|
||||
params['height'] = height
|
||||
|
@ -2663,7 +2672,7 @@ class PmsConnect(object):
|
|||
if blur:
|
||||
params['blur'] = blur
|
||||
|
||||
uri = '/photo/:/transcode?%s' % urllib.urlencode(params)
|
||||
uri = '/photo/:/transcode?%s' % urllib.parse.urlencode(params)
|
||||
result = self.request_handler.make_request(uri=uri,
|
||||
request_type='GET',
|
||||
return_type=True)
|
||||
|
@ -2705,7 +2714,7 @@ class PmsConnect(object):
|
|||
|
||||
for h in hubs:
|
||||
if helpers.get_xml_attr(h, 'size') == '0' or \
|
||||
helpers.get_xml_attr(h, 'type') not in search_results_list.keys():
|
||||
helpers.get_xml_attr(h, 'type') not in list(search_results_list.keys()):
|
||||
continue
|
||||
|
||||
if h.getElementsByTagName('Video'):
|
||||
|
@ -2737,7 +2746,7 @@ class PmsConnect(object):
|
|||
metadata = self.get_metadata_details(rating_key=rating_key)
|
||||
search_results_list[metadata['media_type']].append(metadata)
|
||||
|
||||
output = {'results_count': sum(len(s) for s in search_results_list.values()),
|
||||
output = {'results_count': sum(len(s) for s in list(search_results_list.values())),
|
||||
'results_list': search_results_list
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
|
@ -13,6 +15,9 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from xml.dom import minidom
|
||||
|
||||
|
@ -21,13 +26,13 @@ import collections
|
|||
import requests
|
||||
|
||||
import plexpy
|
||||
import plexpy.lock
|
||||
import logger
|
||||
from plexpy import lock
|
||||
from plexpy import logger
|
||||
|
||||
|
||||
# Dictionary with last request times, for rate limiting.
|
||||
last_requests = collections.defaultdict(int)
|
||||
fake_lock = plexpy.lock.FakeLock()
|
||||
fake_lock = lock.FakeLock()
|
||||
|
||||
|
||||
def request_response(url, method="get", auto_raise=True,
|
||||
|
@ -319,7 +324,7 @@ def server_message(response, return_msg=False):
|
|||
|
||||
if return_msg:
|
||||
try:
|
||||
return unicode(message, 'UTF-8')
|
||||
return str(message, 'UTF-8')
|
||||
except:
|
||||
return message
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,10 +15,13 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import str
|
||||
|
||||
import cherrypy
|
||||
|
||||
import common
|
||||
import users
|
||||
from plexpy import common
|
||||
from plexpy import users
|
||||
|
||||
|
||||
def get_session_info():
|
||||
|
@ -216,14 +221,14 @@ def mask_session_info(list_of_dicts, mask_metadata=True):
|
|||
|
||||
for d in list_of_dicts:
|
||||
if session_user_id and not (str(d.get('user_id')) == session_user_id or d.get('user') == session_user):
|
||||
for k, v in keys_to_mask.iteritems():
|
||||
for k, v in keys_to_mask.items():
|
||||
if k in d: d[k] = keys_to_mask[k]
|
||||
|
||||
if not mask_metadata:
|
||||
continue
|
||||
|
||||
if str(d.get('section_id','')) not in session_library_ids:
|
||||
for k, v in metadata_to_mask.iteritems():
|
||||
for k, v in metadata_to_mask.items():
|
||||
if k in d: d[k] = metadata_to_mask[k]
|
||||
continue
|
||||
|
||||
|
@ -247,7 +252,7 @@ def mask_session_info(list_of_dicts, mask_metadata=True):
|
|||
if d_content_rating in f_content_rating or set(d_labels).intersection(set(f_labels)):
|
||||
continue
|
||||
|
||||
for k, v in metadata_to_mask.iteritems():
|
||||
for k, v in metadata_to_mask.items():
|
||||
if k in d: d[k] = metadata_to_mask[k]
|
||||
|
||||
return list_of_dicts
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,18 +15,25 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import next
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
|
||||
import httpagentparser
|
||||
import time
|
||||
|
||||
import plexpy
|
||||
import common
|
||||
import database
|
||||
import datatables
|
||||
import helpers
|
||||
import libraries
|
||||
import logger
|
||||
import plextv
|
||||
import session
|
||||
from plexpy import common
|
||||
from plexpy import database
|
||||
from plexpy import datatables
|
||||
from plexpy import helpers
|
||||
from plexpy import libraries
|
||||
from plexpy import logger
|
||||
from plexpy import plextv
|
||||
from plexpy import session
|
||||
|
||||
|
||||
def refresh_users():
|
||||
|
@ -509,7 +518,7 @@ class Users(object):
|
|||
for item in result:
|
||||
# Rename Mystery platform names
|
||||
platform = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform'])
|
||||
platform_name = next((v for k, v in common.PLATFORM_NAMES.iteritems() if k in platform.lower()), 'default')
|
||||
platform_name = next((v for k, v in common.PLATFORM_NAMES.items() if k in platform.lower()), 'default')
|
||||
|
||||
row = {'player_name': item['player'],
|
||||
'platform': platform,
|
||||
|
@ -757,7 +766,7 @@ class Users(object):
|
|||
return None
|
||||
|
||||
def get_filters(self, user_id=None):
|
||||
import urlparse
|
||||
import urllib.parse
|
||||
|
||||
if not user_id:
|
||||
return {}
|
||||
|
@ -772,12 +781,12 @@ class Users(object):
|
|||
result = {}
|
||||
|
||||
filters_list = {}
|
||||
for k, v in result.iteritems():
|
||||
for k, v in result.items():
|
||||
filters = {}
|
||||
|
||||
for f in v.split('|'):
|
||||
if 'contentRating=' in f or 'label=' in f:
|
||||
filters.update(dict(urlparse.parse_qsl(f)))
|
||||
filters.update(dict(urllib.parse.parse_qsl(f)))
|
||||
|
||||
filters['content_rating'] = tuple(f for f in filters.pop('contentRating', '').split(',') if f)
|
||||
filters['labels'] = tuple(f for f in filters.pop('label', '').split(',') if f)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
|
@ -13,6 +15,12 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import absolute_import
|
||||
from builtins import next
|
||||
from builtins import str
|
||||
from past.utils import old_div
|
||||
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
|
@ -20,9 +28,9 @@ import subprocess
|
|||
import tarfile
|
||||
|
||||
import plexpy
|
||||
import common
|
||||
import logger
|
||||
import request
|
||||
from plexpy import common
|
||||
from plexpy import logger
|
||||
from plexpy import request
|
||||
|
||||
|
||||
def runGit(args):
|
||||
|
@ -44,7 +52,7 @@ def runGit(args):
|
|||
logger.debug('Trying to execute: "' + cmd + '" with shell in ' + plexpy.PROG_DIR)
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, cwd=plexpy.PROG_DIR)
|
||||
output, err = p.communicate()
|
||||
output = output.strip()
|
||||
output = output.strip().decode()
|
||||
|
||||
logger.debug('Git output: ' + output)
|
||||
except OSError:
|
||||
|
@ -372,7 +380,7 @@ def read_changelog(latest_only=False, since_prev_release=False):
|
|||
output[-1] += '<h' + header_level + '>' + header_text + '</h' + header_level + '>'
|
||||
|
||||
elif line_list_match:
|
||||
line_level = len(line_list_match.group(1)) / 2
|
||||
line_level = old_div(len(line_list_match.group(1)), 2)
|
||||
line_text = line_list_match.group(2)
|
||||
|
||||
if line_level > prev_level:
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -15,6 +17,11 @@
|
|||
|
||||
# Mostly borrowed from https://github.com/trakt/Plex-Trakt-Scrobbler
|
||||
|
||||
from __future__ import absolute_import
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import str
|
||||
|
||||
import json
|
||||
import threading
|
||||
import time
|
||||
|
@ -22,11 +29,12 @@ import time
|
|||
import websocket
|
||||
|
||||
import plexpy
|
||||
import activity_handler
|
||||
import activity_pinger
|
||||
import activity_processor
|
||||
import database
|
||||
import logger
|
||||
from plexpy import activity_handler
|
||||
from plexpy import activity_pinger
|
||||
from plexpy import activity_processor
|
||||
from plexpy import database
|
||||
from plexpy import logger
|
||||
|
||||
|
||||
name = 'websocket'
|
||||
opcode_data = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
|
@ -18,15 +20,21 @@
|
|||
# Form based authentication for CherryPy. Requires the
|
||||
# Session tool to be loaded.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from urllib import quote, unquote
|
||||
from urllib.parse import quote, unquote
|
||||
|
||||
import cherrypy
|
||||
from hashing_passwords import check_hash
|
||||
import jwt
|
||||
|
||||
import plexpy
|
||||
import logger
|
||||
from plexpy import logger
|
||||
from plexpy.database import MonitorDatabase
|
||||
from plexpy.users import Users, refresh_users
|
||||
from plexpy.plextv import PlexTV
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# This file is part of Tautulli.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,11 +15,19 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import next
|
||||
from builtins import str
|
||||
from past.builtins import basestring
|
||||
from builtins import object
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import threading
|
||||
import urllib
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
|
||||
import cherrypy
|
||||
from cherrypy.lib.static import serve_file, serve_download
|
||||
|
@ -30,30 +40,30 @@ from mako import exceptions
|
|||
import websocket
|
||||
|
||||
import plexpy
|
||||
import activity_pinger
|
||||
import common
|
||||
import config
|
||||
import database
|
||||
import datafactory
|
||||
import graphs
|
||||
import helpers
|
||||
import http_handler
|
||||
import libraries
|
||||
import log_reader
|
||||
import logger
|
||||
import newsletter_handler
|
||||
import newsletters
|
||||
import mobile_app
|
||||
import notification_handler
|
||||
import notifiers
|
||||
import plextv
|
||||
import plexivity_import
|
||||
import plexwatch_import
|
||||
import pmsconnect
|
||||
import users
|
||||
import versioncheck
|
||||
import web_socket
|
||||
import webstart
|
||||
from plexpy import activity_pinger
|
||||
from plexpy import common
|
||||
from plexpy import config
|
||||
from plexpy import database
|
||||
from plexpy import datafactory
|
||||
from plexpy import graphs
|
||||
from plexpy import helpers
|
||||
from plexpy import http_handler
|
||||
from plexpy import libraries
|
||||
from plexpy import log_reader
|
||||
from plexpy import logger
|
||||
from plexpy import newsletter_handler
|
||||
from plexpy import newsletters
|
||||
from plexpy import mobile_app
|
||||
from plexpy import notification_handler
|
||||
from plexpy import notifiers
|
||||
from plexpy import plextv
|
||||
from plexpy import plexivity_import
|
||||
from plexpy import plexwatch_import
|
||||
from plexpy import pmsconnect
|
||||
from plexpy import users
|
||||
from plexpy import versioncheck
|
||||
from plexpy import web_socket
|
||||
from plexpy import webstart
|
||||
from plexpy.api2 import API2
|
||||
from plexpy.helpers import checked, addtoapi, get_ip, create_https_certificates, build_datatables_json, sanitize_out
|
||||
from plexpy.session import get_session_info, get_session_user_id, allow_session_user, allow_session_library
|
||||
|
@ -288,7 +298,7 @@ class WebInterface(object):
|
|||
if '{machine_id}' in endpoint:
|
||||
endpoint = endpoint.format(machine_id=plexpy.CONFIG.PMS_IDENTIFIER)
|
||||
|
||||
return base_url + endpoint + '?' + urllib.urlencode(kwargs)
|
||||
return base_url + endpoint + '?' + urllib.parse.urlencode(kwargs)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
|
@ -2364,13 +2374,13 @@ class WebInterface(object):
|
|||
try:
|
||||
temp_loglevel_and_time = l.split(' - ', 1)
|
||||
loglvl = temp_loglevel_and_time[1].split(' ::', 1)[0].strip()
|
||||
msg = helpers.sanitize(unicode(l.split(' : ', 1)[1].replace('\n', ''), 'utf-8'))
|
||||
msg = helpers.sanitize(str(l.split(' : ', 1)[1].replace('\n', ''), 'utf-8'))
|
||||
fa([temp_loglevel_and_time[0], loglvl, msg])
|
||||
except IndexError:
|
||||
# Add traceback message to previous msg.
|
||||
tl = (len(filt) - 1)
|
||||
n = len(l) - len(l.lstrip(' '))
|
||||
ll = ' ' * (2 * n) + helpers.sanitize(unicode(l[n:], 'utf-8'))
|
||||
ll = ' ' * (2 * n) + helpers.sanitize(str(l[n:], 'utf-8'))
|
||||
filt[tl][2] += '<br>' + ll
|
||||
continue
|
||||
|
||||
|
@ -2918,14 +2928,14 @@ class WebInterface(object):
|
|||
|
||||
# Remove config with 'hsec-' prefix and change home_sections to list
|
||||
if kwargs.get('home_sections'):
|
||||
for k in kwargs.keys():
|
||||
for k in list(kwargs.keys()):
|
||||
if k.startswith('hsec-'):
|
||||
del kwargs[k]
|
||||
kwargs['home_sections'] = kwargs['home_sections'].split(',')
|
||||
|
||||
# Remove config with 'hscard-' prefix and change home_stats_cards to list
|
||||
if kwargs.get('home_stats_cards'):
|
||||
for k in kwargs.keys():
|
||||
for k in list(kwargs.keys()):
|
||||
if k.startswith('hscard-'):
|
||||
del kwargs[k]
|
||||
kwargs['home_stats_cards'] = kwargs['home_stats_cards'].split(',')
|
||||
|
@ -2935,7 +2945,7 @@ class WebInterface(object):
|
|||
|
||||
# Remove config with 'hlcard-' prefix and change home_library_cards to list
|
||||
if kwargs.get('home_library_cards'):
|
||||
for k in kwargs.keys():
|
||||
for k in list(kwargs.keys()):
|
||||
if k.startswith('hlcard-'):
|
||||
del kwargs[k]
|
||||
kwargs['home_library_cards'] = kwargs['home_library_cards'].split(',')
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
|
@ -13,14 +15,21 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import str
|
||||
from builtins import object
|
||||
|
||||
import os
|
||||
import sys
|
||||
from urllib import urlencode
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import cherrypy
|
||||
|
||||
import plexpy
|
||||
import cherrypy
|
||||
import logger
|
||||
import webauth
|
||||
from plexpy import logger
|
||||
from plexpy import webauth
|
||||
from plexpy.helpers import create_https_certificates
|
||||
from plexpy.webserve import WebInterface
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue