#!/usr/bin/env python
# -*- coding: utf-8 -*-

# This file is part of PlexPy.
#
#  PlexPy is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  PlexPy is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with PlexPy.  If not, see <http://www.gnu.org/licenses/>.

from plexpy import logger, notifiers, plextv, pmsconnect, common, log_reader, datafactory, graphs, users
from plexpy.helpers import checked, radio

from mako.lookup import TemplateLookup
from mako import exceptions

import plexpy
import threading
import cherrypy
import hashlib
import random
import json
import os

try:
    # pylint:disable=E0611
    # ignore this error because we are catching the ImportError
    from collections import OrderedDict
    # pylint:enable=E0611
except ImportError:
    # Python 2.6.x fallback, from libs
    from ordereddict import OrderedDict


def serve_template(templatename, **kwargs):
    interface_dir = os.path.join(str(plexpy.PROG_DIR), 'data/interfaces/')
    template_dir = os.path.join(str(interface_dir), plexpy.CONFIG.INTERFACE)

    _hplookup = TemplateLookup(directories=[template_dir])

    try:
        template = _hplookup.get_template(templatename)
        return template.render(**kwargs)
    except:
        return exceptions.html_error_template().render()


class WebInterface(object):

    def __init__(self):
        self.interface_dir = os.path.join(str(plexpy.PROG_DIR), 'data/')

    @cherrypy.expose
    def index(self):
        if plexpy.CONFIG.FIRST_RUN_COMPLETE:
            raise cherrypy.HTTPRedirect("home")
        else:
            raise cherrypy.HTTPRedirect("welcome")

    @cherrypy.expose
    def home(self):
        config = {
            "home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
            "home_stats_cards": plexpy.CONFIG.HOME_STATS_CARDS,
            "home_library_cards": plexpy.CONFIG.HOME_LIBRARY_CARDS,
            "pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER
        }
        return serve_template(templatename="index.html", title="Home", config=config)

    @cherrypy.expose
    def welcome(self, **kwargs):
        config = {
            "launch_browser": checked(plexpy.CONFIG.LAUNCH_BROWSER),
            "refresh_users_on_startup": checked(plexpy.CONFIG.REFRESH_USERS_ON_STARTUP),
            "pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER,
            "pms_ip": plexpy.CONFIG.PMS_IP,
            "pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE),
            "pms_port": plexpy.CONFIG.PMS_PORT,
            "pms_token": plexpy.CONFIG.PMS_TOKEN,
            "pms_ssl": checked(plexpy.CONFIG.PMS_SSL),
            "pms_uuid": plexpy.CONFIG.PMS_UUID,
            "tv_notify_enable": checked(plexpy.CONFIG.TV_NOTIFY_ENABLE),
            "movie_notify_enable": checked(plexpy.CONFIG.MOVIE_NOTIFY_ENABLE),
            "music_notify_enable": checked(plexpy.CONFIG.MUSIC_NOTIFY_ENABLE),
            "tv_notify_on_start": checked(plexpy.CONFIG.TV_NOTIFY_ON_START),
            "movie_notify_on_start": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_START),
            "music_notify_on_start": checked(plexpy.CONFIG.MUSIC_NOTIFY_ON_START),
            "video_logging_enable": checked(plexpy.CONFIG.VIDEO_LOGGING_ENABLE),
            "music_logging_enable": checked(plexpy.CONFIG.MUSIC_LOGGING_ENABLE),
            "logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL,
            "check_github": checked(plexpy.CONFIG.CHECK_GITHUB)
        }

        # The setup wizard just refreshes the page on submit so we must redirect to home if config set.
        if plexpy.CONFIG.FIRST_RUN_COMPLETE:
            plexpy.initialize_scheduler()
            raise cherrypy.HTTPRedirect("home")
        else:
            return serve_template(templatename="welcome.html", title="Welcome", config=config)

    @cherrypy.expose
    def get_date_formats(self):
        if plexpy.CONFIG.DATE_FORMAT:
            date_format = plexpy.CONFIG.DATE_FORMAT
        else:
            date_format = 'YYYY-MM-DD'
        if plexpy.CONFIG.TIME_FORMAT:
            time_format = plexpy.CONFIG.TIME_FORMAT
        else:
            time_format = 'HH:mm'

        formats = {'date_format': date_format,
                   'time_format': time_format}

        cherrypy.response.headers['Content-type'] = 'application/json'
        return json.dumps(formats)

    @cherrypy.expose
    def home_stats(self, **kwargs):
        data_factory = datafactory.DataFactory()

        time_range = plexpy.CONFIG.HOME_STATS_LENGTH
        stats_type = plexpy.CONFIG.HOME_STATS_TYPE
        stats_count = plexpy.CONFIG.HOME_STATS_COUNT
        stats_cards = plexpy.CONFIG.HOME_STATS_CARDS.split(', ')
        notify_watched_percent = plexpy.CONFIG.NOTIFY_WATCHED_PERCENT

        stats_data = data_factory.get_home_stats(time_range=time_range,
                                                 stats_type=stats_type,
                                                 stats_count=stats_count,
                                                 stats_cards=stats_cards,
                                                 notify_watched_percent=notify_watched_percent)

        return serve_template(templatename="home_stats.html", title="Stats", data=stats_data)

    @cherrypy.expose
    def library_stats(self, **kwargs):
        pms_connect = pmsconnect.PmsConnect()

        library_cards = plexpy.CONFIG.HOME_LIBRARY_CARDS.split(', ')

        if library_cards == ['library_statistics_first']:
            library_cards = ['library_statistics']
            server_children = pms_connect.get_server_children()
            server_libraries = server_children['libraries_list']

            for library in server_libraries:
                library_cards.append(library['key'])

            plexpy.CONFIG.HOME_LIBRARY_CARDS = ', '.join(library_cards)
            plexpy.CONFIG.write()

        stats_data = pms_connect.get_library_stats(library_cards=library_cards)

        return serve_template(templatename="library_stats.html", title="Library Stats", data=stats_data)

    @cherrypy.expose
    def history(self):
        return serve_template(templatename="history.html", title="History")

    @cherrypy.expose
    def users(self):
        return serve_template(templatename="users.html", title="Users")

    @cherrypy.expose
    def graphs(self):

        config = {
            "music_logging_enable": plexpy.CONFIG.MUSIC_LOGGING_ENABLE
        }

        return serve_template(templatename="graphs.html", title="Graphs", config=config)

    @cherrypy.expose
    def sync(self):
        return serve_template(templatename="sync.html", title="Synced Items")

    @cherrypy.expose
    def user(self, user=None, user_id=None):
        user_data = users.Users()
        if user_id:
            try:
                user_details = user_data.get_user_details(user_id=user_id)
            except:
                logger.warn("Unable to retrieve friendly name for user_id %s " % user_id)
        elif user:
            try:
                user_details = user_data.get_user_details(user=user)
            except:
                logger.warn("Unable to retrieve friendly name for user %s " % user)
        else:
            logger.debug(u"User page requested but no parameters received.")
            raise cherrypy.HTTPRedirect("home")

        return serve_template(templatename="user.html", title="User", data=user_details)

    @cherrypy.expose
    def edit_user_dialog(self, user=None, user_id=None, **kwargs):
        user_data = users.Users()
        if user_id:
            result = user_data.get_user_friendly_name(user_id=user_id)
            status_message = ''
        elif user:
            result = user_data.get_user_friendly_name(user=user)
            status_message = ''
        else:
            result = None
            status_message = 'An error occured.'

        return serve_template(templatename="edit_user.html", title="Edit User", data=result, status_message=status_message)

    @cherrypy.expose
    def edit_user(self, user=None, user_id=None, friendly_name=None, **kwargs):
        if 'do_notify' in kwargs:
            do_notify = kwargs.get('do_notify')
        else:
            do_notify = 0
        if 'keep_history' in kwargs:
            keep_history = kwargs.get('keep_history')
        else:
            keep_history = 0
        if 'thumb' in kwargs:
            custom_avatar = kwargs['thumb']
        else:
            custom_avatar = ''

        user_data = users.Users()
        if user_id:
            try:
                user_data.set_user_friendly_name(user_id=user_id,
                                                 friendly_name=friendly_name,
                                                 do_notify=do_notify,
                                                 keep_history=keep_history)
                user_data.set_user_profile_url(user_id=user_id,
                                               profile_url=custom_avatar)

                status_message = "Successfully updated user."
                return status_message
            except:
                status_message = "Failed to update user."
                return status_message
        if user:
            try:
                user_data.set_user_friendly_name(user=user,
                                                 friendly_name=friendly_name,
                                                 do_notify=do_notify,
                                                 keep_history=keep_history)
                user_data.set_user_profile_url(user=user,
                                               profile_url=custom_avatar)

                status_message = "Successfully updated user."
                return status_message
            except:
                status_message = "Failed to update user."
                return status_message

    @cherrypy.expose
    def get_stream_data(self, row_id=None, user=None, **kwargs):

        data_factory = datafactory.DataFactory()
        stream_data = data_factory.get_stream_details(row_id)

        return serve_template(templatename="stream_data.html", title="Stream Data", data=stream_data, user=user)

    @cherrypy.expose
    def get_ip_address_details(self, ip_address=None, **kwargs):
        import socket

        try:
            socket.inet_aton(ip_address)
        except socket.error:
            ip_address = None

        return serve_template(templatename="ip_address_modal.html", title="IP Address Details", data=ip_address)

    @cherrypy.expose
    def get_user_list(self, **kwargs):

        user_data = users.Users()
        user_list = user_data.get_user_list(kwargs=kwargs)

        cherrypy.response.headers['Content-type'] = 'application/json'
        return json.dumps(user_list)

    @cherrypy.expose
    def checkGithub(self):
        from plexpy import versioncheck

        versioncheck.checkGithub()
        raise cherrypy.HTTPRedirect("home")

    @cherrypy.expose
    def logs(self):
        return serve_template(templatename="logs.html", title="Log", lineList=plexpy.LOG_LIST)

    @cherrypy.expose
    def clearLogs(self):
        plexpy.LOG_LIST = []
        logger.info("Web logs cleared")
        raise cherrypy.HTTPRedirect("logs")

    @cherrypy.expose
    def toggleVerbose(self):
        plexpy.VERBOSE = not plexpy.VERBOSE
        logger.initLogger(console=not plexpy.QUIET,
                          log_dir=plexpy.CONFIG.LOG_DIR, verbose=plexpy.VERBOSE)
        logger.info("Verbose toggled, set to %s", plexpy.VERBOSE)
        logger.debug("If you read this message, debug logging is available")
        raise cherrypy.HTTPRedirect("logs")

    @cherrypy.expose
    def getLog(self, start=0, length=100, **kwargs):
        start = int(start)
        length = int(length)
        search_value = ""
        search_regex = ""
        order_column = 0
        order_dir = "desc"

        if 'order[0][dir]' in kwargs:
            order_dir = kwargs.get('order[0][dir]', "desc")

        if 'order[0][column]' in kwargs:
            order_column = kwargs.get('order[0][column]', "0")

        if 'search[value]' in kwargs:
            search_value = kwargs.get('search[value]', "")

        if 'search[regex]' in kwargs:
            search_regex = kwargs.get('search[regex]', "")

        filtered = []
        if search_value == "":
            filtered = plexpy.LOG_LIST[::]
        else:
            filtered = [row for row in plexpy.LOG_LIST for column in row if search_value.lower() in column.lower()]

        sortcolumn = 0
        if order_column == '1':
            sortcolumn = 2
        elif order_column == '2':
            sortcolumn = 1
        filtered.sort(key=lambda x: x[sortcolumn], reverse=order_dir == "desc")

        rows = filtered[start:(start + length)]
        rows = [[row[0], row[2], row[1]] for row in rows]

        return json.dumps({
            'recordsFiltered': len(filtered),
            'recordsTotal': len(plexpy.LOG_LIST),
            'data': rows,
        })

    @cherrypy.expose
    def get_plex_log(self, window=1000, **kwargs):
        log_lines = []
        try:
            log_lines = {'data': log_reader.get_log_tail(window=window)}
        except:
            logger.warn("Unable to retrieve Plex Logs.")

        cherrypy.response.headers['Content-type'] = 'application/json'
        return json.dumps(log_lines)

    @cherrypy.expose
    def generateAPI(self):
        apikey = hashlib.sha224(str(random.getrandbits(256))).hexdigest()[0:32]
        logger.info("New API generated")
        return apikey

    @cherrypy.expose
    def settings(self):
        interface_dir = os.path.join(plexpy.PROG_DIR, 'data/interfaces/')
        interface_list = [name for name in os.listdir(interface_dir) if
                          os.path.isdir(os.path.join(interface_dir, name))]

        # Initialise blank passwords so we do not expose them in the html forms
        # but users are still able to clear them
        if plexpy.CONFIG.HTTP_PASSWORD != '':
            http_password = '    '
        else:
            http_password = ''

        config = {
            "http_host": plexpy.CONFIG.HTTP_HOST,
            "http_username": plexpy.CONFIG.HTTP_USERNAME,
            "http_port": plexpy.CONFIG.HTTP_PORT,
            "http_password": http_password,
            "launch_browser": checked(plexpy.CONFIG.LAUNCH_BROWSER),
            "enable_https": checked(plexpy.CONFIG.ENABLE_HTTPS),
            "https_cert": plexpy.CONFIG.HTTPS_CERT,
            "https_key": plexpy.CONFIG.HTTPS_KEY,
            "api_enabled": checked(plexpy.CONFIG.API_ENABLED),
            "api_key": plexpy.CONFIG.API_KEY,
            "update_db_interval": plexpy.CONFIG.UPDATE_DB_INTERVAL,
            "freeze_db": checked(plexpy.CONFIG.FREEZE_DB),
            "log_dir": plexpy.CONFIG.LOG_DIR,
            "cache_dir": plexpy.CONFIG.CACHE_DIR,
            "check_github": checked(plexpy.CONFIG.CHECK_GITHUB),
            "interface_list": interface_list,
            "cache_sizemb": plexpy.CONFIG.CACHE_SIZEMB,
            "pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER,
            "pms_ip": plexpy.CONFIG.PMS_IP,
            "pms_logs_folder": plexpy.CONFIG.PMS_LOGS_FOLDER,
            "pms_port": plexpy.CONFIG.PMS_PORT,
            "pms_token": plexpy.CONFIG.PMS_TOKEN,
            "pms_ssl": checked(plexpy.CONFIG.PMS_SSL),
            "pms_use_bif": checked(plexpy.CONFIG.PMS_USE_BIF),
            "pms_uuid": plexpy.CONFIG.PMS_UUID,
            "date_format": plexpy.CONFIG.DATE_FORMAT,
            "time_format": plexpy.CONFIG.TIME_FORMAT,
            "grouping_global_history": checked(plexpy.CONFIG.GROUPING_GLOBAL_HISTORY),
            "grouping_user_history": checked(plexpy.CONFIG.GROUPING_USER_HISTORY),
            "grouping_charts": checked(plexpy.CONFIG.GROUPING_CHARTS),
            "tv_notify_enable": checked(plexpy.CONFIG.TV_NOTIFY_ENABLE),
            "movie_notify_enable": checked(plexpy.CONFIG.MOVIE_NOTIFY_ENABLE),
            "music_notify_enable": checked(plexpy.CONFIG.MUSIC_NOTIFY_ENABLE),
            "tv_notify_on_start": checked(plexpy.CONFIG.TV_NOTIFY_ON_START),
            "movie_notify_on_start": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_START),
            "music_notify_on_start": checked(plexpy.CONFIG.MUSIC_NOTIFY_ON_START),
            "tv_notify_on_stop": checked(plexpy.CONFIG.TV_NOTIFY_ON_STOP),
            "movie_notify_on_stop": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_STOP),
            "music_notify_on_stop": checked(plexpy.CONFIG.MUSIC_NOTIFY_ON_STOP),
            "tv_notify_on_pause": checked(plexpy.CONFIG.TV_NOTIFY_ON_PAUSE),
            "movie_notify_on_pause": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_PAUSE),
            "music_notify_on_pause": checked(plexpy.CONFIG.MUSIC_NOTIFY_ON_PAUSE),
            "monitoring_interval": plexpy.CONFIG.MONITORING_INTERVAL,
            "monitoring_use_websocket": checked(plexpy.CONFIG.MONITORING_USE_WEBSOCKET),
            "refresh_users_interval": plexpy.CONFIG.REFRESH_USERS_INTERVAL,
            "refresh_users_on_startup": checked(plexpy.CONFIG.REFRESH_USERS_ON_STARTUP),
            "ip_logging_enable": checked(plexpy.CONFIG.IP_LOGGING_ENABLE),
            "video_logging_enable": checked(plexpy.CONFIG.VIDEO_LOGGING_ENABLE),
            "music_logging_enable": checked(plexpy.CONFIG.MUSIC_LOGGING_ENABLE),
            "logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL,
            "pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE),
            "notify_consecutive": checked(plexpy.CONFIG.NOTIFY_CONSECUTIVE),
            "notify_recently_added_grandparent": checked(plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT),
            "notify_watched_percent": plexpy.CONFIG.NOTIFY_WATCHED_PERCENT,
            "notify_on_start_subject_text": plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT,
            "notify_on_start_body_text": plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT,
            "notify_on_stop_subject_text": plexpy.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT,
            "notify_on_stop_body_text": plexpy.CONFIG.NOTIFY_ON_STOP_BODY_TEXT,
            "notify_on_pause_subject_text": plexpy.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT,
            "notify_on_pause_body_text": plexpy.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT,
            "notify_on_resume_subject_text": plexpy.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT,
            "notify_on_resume_body_text": plexpy.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT,
            "notify_on_buffer_subject_text": plexpy.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_TEXT,
            "notify_on_buffer_body_text": plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT,
            "notify_on_watched_subject_text": plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT,
            "notify_on_watched_body_text": plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT,
            "notify_on_created_subject_text": plexpy.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT,
            "notify_on_created_body_text": plexpy.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT,
            "home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
            "home_stats_type": checked(plexpy.CONFIG.HOME_STATS_TYPE),
            "home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT,
            "home_stats_cards": plexpy.CONFIG.HOME_STATS_CARDS,
            "home_library_cards": plexpy.CONFIG.HOME_LIBRARY_CARDS,
            "buffer_threshold": plexpy.CONFIG.BUFFER_THRESHOLD,
            "buffer_wait": plexpy.CONFIG.BUFFER_WAIT,
            "group_history_tables": checked(plexpy.CONFIG.GROUP_HISTORY_TABLES)
        }

        return serve_template(templatename="settings.html", title="Settings", config=config)

    @cherrypy.expose
    def configUpdate(self, **kwargs):
        # Handle the variable config options. Note - keys with False values aren't getting passed

        checked_configs = [
            "launch_browser", "enable_https", "api_enabled", "freeze_db", "check_github",
            "grouping_global_history", "grouping_user_history", "grouping_charts", "pms_use_bif", "pms_ssl",
            "tv_notify_enable", "movie_notify_enable", "music_notify_enable", "monitoring_use_websocket",
            "tv_notify_on_start", "movie_notify_on_start", "music_notify_on_start",
            "tv_notify_on_stop", "movie_notify_on_stop", "music_notify_on_stop",
            "tv_notify_on_pause", "movie_notify_on_pause", "music_notify_on_pause", "refresh_users_on_startup",
            "ip_logging_enable", "video_logging_enable", "music_logging_enable", "pms_is_remote", "home_stats_type",
            "group_history_tables", "notify_consecutive", "notify_recently_added_grandparent"
        ]
        for checked_config in checked_configs:
            if checked_config not in kwargs:
                # checked items should be zero or one. if they were not sent then the item was not checked
                kwargs[checked_config] = 0

        # If http password exists in config, do not overwrite when blank value received
        if 'http_password' in kwargs:
            if kwargs['http_password'] == '    ' and plexpy.CONFIG.HTTP_PASSWORD != '':
                kwargs['http_password'] = plexpy.CONFIG.HTTP_PASSWORD

        for plain_config, use_config in [(x[4:], x) for x in kwargs if x.startswith('use_')]:
            # the use prefix is fairly nice in the html, but does not match the actual config
            kwargs[plain_config] = kwargs[use_config]
            del kwargs[use_config]

        # Check if we should refresh our data
        refresh_users = False
        reschedule = False

        if 'monitoring_interval' in kwargs and 'refresh_users_interval' in kwargs:
            if (kwargs['monitoring_interval'] != str(plexpy.CONFIG.MONITORING_INTERVAL)) or \
                    (kwargs['refresh_users_interval'] != str(plexpy.CONFIG.REFRESH_USERS_INTERVAL)):
                reschedule = True

        if 'pms_ip' in kwargs:
            if kwargs['pms_ip'] != plexpy.CONFIG.PMS_IP:
                refresh_users = True

        if 'home_stats_cards' in kwargs:
            if kwargs['home_stats_cards'] != 'watch_statistics':
                kwargs['home_stats_cards'] = ', '.join(kwargs['home_stats_cards'])

        if 'home_library_cards' in kwargs:
            if kwargs['home_library_cards'] != 'library_statistics':
                kwargs['home_library_cards'] = ', '.join(kwargs['home_library_cards'])

        plexpy.CONFIG.process_kwargs(kwargs)

        # Write the config
        plexpy.CONFIG.write()

        # Get new server URLs for SSL communications.
        plextv.get_real_pms_url()

        # Reconfigure scheduler if intervals changed
        if reschedule:
            plexpy.initialize_scheduler()

        # Refresh users table if our server IP changes.
        if refresh_users:
            threading.Thread(target=plextv.refresh_users).start()

        raise cherrypy.HTTPRedirect("settings")

    @cherrypy.expose
    def set_notification_config(self, **kwargs):

        for plain_config, use_config in [(x[4:], x) for x in kwargs if x.startswith('use_')]:
            # the use prefix is fairly nice in the html, but does not match the actual config
            kwargs[plain_config] = kwargs[use_config]
            del kwargs[use_config]

        plexpy.CONFIG.process_kwargs(kwargs)

        # Write the config
        plexpy.CONFIG.write()

        cherrypy.response.status = 200

    @cherrypy.expose
    def do_state_change(self, signal, title, timer):
        message = title
        quote = self.random_arnold_quotes()
        plexpy.SIGNAL = signal

        return serve_template(templatename="shutdown.html", title=title,
                              message=message, timer=timer, quote=quote)

    @cherrypy.expose
    def get_history(self, user=None, user_id=None, grouping=0, **kwargs):

        if grouping == 'false':
            grouping = 0
        else:
            grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES

        watched_percent = plexpy.CONFIG.NOTIFY_WATCHED_PERCENT

        custom_where = []
        if user_id:
            custom_where.append(['session_history.user_id', user_id])
        elif user:
            custom_where.append(['session_history.user', user])
        if 'rating_key' in kwargs:
            rating_key = kwargs.get('rating_key', "")
            custom_where.append(['session_history.rating_key', rating_key])
        if 'parent_rating_key' in kwargs:
            rating_key = kwargs.get('parent_rating_key', "")
            custom_where.append(['session_history.parent_rating_key', rating_key])
        if 'grandparent_rating_key' in kwargs:
            rating_key = kwargs.get('grandparent_rating_key', "")
            custom_where.append(['session_history.grandparent_rating_key', rating_key])
        if 'start_date' in kwargs:
            start_date = kwargs.get('start_date', "")
            custom_where.append(['strftime("%Y-%m-%d", datetime(date, "unixepoch", "localtime"))', start_date])
        if 'reference_id' in kwargs:
            reference_id = kwargs.get('reference_id', "")
            custom_where.append(['session_history.reference_id', reference_id])
        if 'media_type' in kwargs:
            media_type = kwargs.get('media_type', "")
            if media_type != 'all':
               custom_where.append(['session_history_metadata.media_type', media_type])

        data_factory = datafactory.DataFactory()
        history = data_factory.get_history(kwargs=kwargs, custom_where=custom_where, grouping=grouping, watched_percent=watched_percent)

        cherrypy.response.headers['Content-type'] = 'application/json'
        return json.dumps(history)

    @cherrypy.expose
    def history_table_modal(self, start_date=None, **kwargs):

        return serve_template(templatename="history_table_modal.html", title="History Data", data=start_date)

    @cherrypy.expose
    def shutdown(self):
        return self.do_state_change('shutdown', 'Shutting Down', 15)

    @cherrypy.expose
    def restart(self):
        return self.do_state_change('restart', 'Restarting', 30)

    @cherrypy.expose
    def update(self):
        return self.do_state_change('update', 'Updating', 120)

    @cherrypy.expose
    def api(self, *args, **kwargs):
        from plexpy.api import Api

        a = Api()
        a.checkParams(*args, **kwargs)

        return a.fetchData()

    @cherrypy.expose
    def twitterStep1(self):
        cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
        tweet = notifiers.TwitterNotifier()
        return tweet._get_authorization()

    @cherrypy.expose
    def twitterStep2(self, key):
        cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
        tweet = notifiers.TwitterNotifier()
        result = tweet._get_credentials(key)
        logger.info(u"result: " + str(result))
        if result:
            return "Key verification successful"
        else:
            return "Unable to verify key"

    @cherrypy.expose
    def testTwitter(self):
        cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
        tweet = notifiers.TwitterNotifier()
        result = tweet.test_notify()
        if result:
            return "Tweet successful, check your twitter to make sure it worked"
        else:
            return "Error sending tweet"

    @cherrypy.expose
    def test_ifttt(self):
        cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
        event = notifiers.IFTTT()
        result = event.test()
        if result:
            return "Notification successful."
        else:
            return "Error sending event."

    @cherrypy.expose
    def osxnotifyregister(self, app):
        cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
        from osxnotify import registerapp as osxnotify

        result, msg = osxnotify.registerapp(app)
        if result:
            osx_notify = notifiers.OSX_NOTIFY()
            osx_notify.notify('Registered', result, 'Success :-)')
            logger.info('Registered %s, to re-register a different app, delete this app first' % result)
        else:
            logger.warn(msg)
        return msg

    @cherrypy.expose
    def get_pms_token(self):

        token = plextv.PlexTV()
        result = token.get_token()

        if result:
            return result
        else:
            logger.warn('Unable to retrieve Plex.tv token.')
            return False

    @cherrypy.expose
    def get_pms_sessions_json(self, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_sessions('json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')
            return False

    @cherrypy.expose
    def get_current_activity(self, **kwargs):

        try:
            pms_connect = pmsconnect.PmsConnect()
            result = pms_connect.get_current_activity()
        except:
            return serve_template(templatename="current_activity.html", data=None)

        if result:
            return serve_template(templatename="current_activity.html", data=result)
        else:
            logger.warn('Unable to retrieve data.')
            return serve_template(templatename="current_activity.html", data=None)

    @cherrypy.expose
    def get_current_activity_header(self, **kwargs):

        try:
            pms_connect = pmsconnect.PmsConnect()
            result = pms_connect.get_current_activity()
        except IOError, e:
            return serve_template(templatename="current_activity_header.html", data=None)

        if result:
            return serve_template(templatename="current_activity_header.html", data=result['stream_count'])
        else:
            logger.warn('Unable to retrieve data.')
            return serve_template(templatename="current_activity_header.html", data=None)

    @cherrypy.expose
    def get_recently_added(self, count='0', **kwargs):

        try:
            pms_connect = pmsconnect.PmsConnect()
            result = pms_connect.get_recently_added_details(count)
        except IOError, e:
            return serve_template(templatename="recently_added.html", data=None)

        if result:
            return serve_template(templatename="recently_added.html", data=result['recently_added'])
        else:
            logger.warn('Unable to retrieve data.')
            return serve_template(templatename="recently_added.html", data=None)

    @cherrypy.expose
    def pms_image_proxy(self, img='', width='0', height='0', fallback=None, **kwargs):
        try:
            pms_connect = pmsconnect.PmsConnect()
            result = pms_connect.get_image(img, width, height)
            cherrypy.response.headers['Content-type'] = result[1]
            return result[0]
        except:
            logger.warn('Image proxy queried but errors occured.')
            if fallback == 'poster':
                logger.info('Trying fallback image...')
                try:
                    fallback_image = open(self.interface_dir + common.DEFAULT_POSTER_THUMB, 'rb')
                    cherrypy.response.headers['Content-type'] = 'image/png'
                    return fallback_image
                except IOError, e:
                    logger.error('Unable to read fallback image. %s' % e)
            elif fallback == 'cover':
                logger.info('Trying fallback image...')
                try:
                    fallback_image = open(self.interface_dir + common.DEFAULT_COVER_THUMB, 'rb')
                    cherrypy.response.headers['Content-type'] = 'image/png'
                    return fallback_image
                except IOError, e:
                    logger.error('Unable to read fallback image. %s' % e)

            return None

    @cherrypy.expose
    def info(self, item_id=None, source=None, **kwargs):
        metadata = None
        query = None

        config = {
            "pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER
        }

        if source == 'history':
            data_factory = datafactory.DataFactory()
            metadata = data_factory.get_metadata_details(row_id=item_id)
        elif item_id == 'movie':
            metadata = {'media_type': 'library', 'library': 'movie', 'media_type_filter': 'movie', 'title': 'Movies'}
        elif item_id == 'show':
            metadata = {'media_type': 'library', 'library': 'show', 'media_type_filter': 'episode', 'title': 'TV Shows'}
        elif item_id == 'artist':
            metadata = {'media_type': 'library', 'library': 'artist', 'media_type_filter': 'track', 'title': 'Music'}
        else:
            pms_connect = pmsconnect.PmsConnect()
            result = pms_connect.get_metadata_details(rating_key=item_id)
            if result:
                metadata = result['metadata']
            else:
                data_factory = datafactory.DataFactory()
                query = data_factory.get_search_query(rating_key=item_id)

        if metadata:
            return serve_template(templatename="info.html", data=metadata, title="Info", config=config)
        else:
            logger.warn('Unable to retrieve data.')
            return serve_template(templatename="info.html", data=None, query=query, title="Info")

    @cherrypy.expose
    def get_user_recently_watched(self, user=None, user_id=None, limit='10', **kwargs):

        data_factory = datafactory.DataFactory()
        result = data_factory.get_recently_watched(user_id=user_id, user=user, limit=limit)

        if result:
            return serve_template(templatename="user_recently_watched.html", data=result,
                                  title="Recently Watched")
        else:
            logger.warn('Unable to retrieve data.')
            return serve_template(templatename="user_recently_watched.html", data=None,
                                  title="Recently Watched")

    @cherrypy.expose
    def get_user_watch_time_stats(self, user=None, user_id=None, **kwargs):

        user_data = users.Users()
        result = user_data.get_user_watch_time_stats(user_id=user_id, user=user)

        if result:
            return serve_template(templatename="user_watch_time_stats.html", data=result, title="Watch Stats")
        else:
            logger.warn('Unable to retrieve data.')
            return serve_template(templatename="user_watch_time_stats.html", data=None, title="Watch Stats")

    @cherrypy.expose
    def get_user_player_stats(self, user=None, user_id=None, **kwargs):

        user_data = users.Users()
        result = user_data.get_user_player_stats(user_id=user_id, user=user)

        if result:
            return serve_template(templatename="user_player_stats.html", data=result,
                                  title="Player Stats")
        else:
            logger.warn('Unable to retrieve data.')
            return serve_template(templatename="user_player_stats.html", data=None, title="Player Stats")

    @cherrypy.expose
    def get_item_children(self, rating_key='', **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_item_children(rating_key)

        if result:
            return serve_template(templatename="info_children_list.html", data=result, title="Children List")
        else:
            logger.warn('Unable to retrieve data.')
            return serve_template(templatename="info_children_list.html", data=None, title="Children List")

    @cherrypy.expose
    def get_metadata_json(self, rating_key='', **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_metadata(rating_key, 'json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_metadata_xml(self, rating_key='', **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_metadata(rating_key)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/xml'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_recently_added_json(self, count='0', **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_recently_added(count, 'json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_episode_list_json(self, rating_key='', **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_episode_list(rating_key, 'json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_user_ips(self, user_id=None, user=None, **kwargs):

        custom_where = []
        if user_id:
            custom_where = [['user_id', user_id]]
        elif user:
            custom_where = [['user', user]]

        user_data = users.Users()
        history = user_data.get_user_unique_ips(kwargs=kwargs,
                                                custom_where=custom_where)

        cherrypy.response.headers['Content-type'] = 'application/json'
        return json.dumps(history)

    @cherrypy.expose
    def get_plays_by_date(self, time_range='30', y_axis='plays', **kwargs):

        graph = graphs.Graphs()
        result = graph.get_total_plays_per_day(time_range=time_range, y_axis=y_axis)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_plays_by_dayofweek(self, time_range='30', y_axis='plays', **kwargs):

        graph = graphs.Graphs()
        result = graph.get_total_plays_per_dayofweek(time_range=time_range, y_axis=y_axis)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_plays_by_hourofday(self, time_range='30', y_axis='plays', **kwargs):

        graph = graphs.Graphs()
        result = graph.get_total_plays_per_hourofday(time_range=time_range, y_axis=y_axis)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_plays_per_month(self, y_axis='plays', **kwargs):

        graph = graphs.Graphs()
        result = graph.get_total_plays_per_month(y_axis=y_axis)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_plays_by_top_10_platforms(self, time_range='30', y_axis='plays', **kwargs):

        graph = graphs.Graphs()
        result = graph.get_total_plays_by_top_10_platforms(time_range=time_range, y_axis=y_axis)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_plays_by_top_10_users(self, time_range='30', y_axis='plays', **kwargs):

        graph = graphs.Graphs()
        result = graph.get_total_plays_by_top_10_users(time_range=time_range, y_axis=y_axis)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_plays_by_stream_type(self, time_range='30', y_axis='plays', **kwargs):

        graph = graphs.Graphs()
        result = graph.get_total_plays_per_stream_type(time_range=time_range, y_axis=y_axis)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_plays_by_source_resolution(self, time_range='30', y_axis='plays', **kwargs):

        graph = graphs.Graphs()
        result = graph.get_total_plays_by_source_resolution(time_range=time_range, y_axis=y_axis)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_plays_by_stream_resolution(self, time_range='30', y_axis='plays', **kwargs):

        graph = graphs.Graphs()
        result = graph.get_total_plays_by_stream_resolution(time_range=time_range, y_axis=y_axis)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_stream_type_by_top_10_users(self, time_range='30', y_axis='plays', **kwargs):

        graph = graphs.Graphs()
        result = graph.get_stream_type_by_top_10_users(time_range=time_range, y_axis=y_axis)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_stream_type_by_top_10_platforms(self, time_range='30', y_axis='plays', **kwargs):

        graph = graphs.Graphs()
        result = graph.get_stream_type_by_top_10_platforms(time_range=time_range, y_axis=y_axis)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_friends_list(self, **kwargs):

        plex_tv = plextv.PlexTV()
        result = plex_tv.get_plextv_friends('json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_user_details(self, **kwargs):

        plex_tv = plextv.PlexTV()
        result = plex_tv.get_plextv_user_details('json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_server_list(self, **kwargs):

        plex_tv = plextv.PlexTV()
        result = plex_tv.get_plextv_server_list('json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_sync_lists(self, machine_id='', **kwargs):

        plex_tv = plextv.PlexTV()
        result = plex_tv.get_plextv_sync_lists(machine_id=machine_id, output_format='json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_servers(self, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_server_list(output_format='json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_servers_info(self, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_servers_info()

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_server_prefs(self, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_server_prefs(output_format='json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_server_children(self, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_server_children()

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_activity(self, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_current_activity()

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_full_users_list(self, **kwargs):

        plex_tv = plextv.PlexTV()
        result = plex_tv.get_full_users_list()

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def refresh_users_list(self, **kwargs):
        threading.Thread(target=plextv.refresh_users).start()
        logger.info('Manual user list refresh requested.')

    @cherrypy.expose
    def get_sync(self, machine_id=None, user_id=None, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        server_id = pms_connect.get_server_identity()

        plex_tv = plextv.PlexTV()
        if not machine_id:
            result = plex_tv.get_synced_items(machine_id=server_id['machine_identifier'], user_id=user_id)
        else:
            result = plex_tv.get_synced_items(machine_id=machine_id, user_id=user_id)

        if result:
            output = {"data": result}
        else:
            logger.warn('Unable to retrieve sync data for user.')
            output = {"data": []}

        cherrypy.response.headers['Content-type'] = 'application/json'
        return json.dumps(output)

    @cherrypy.expose
    def get_sync_item(self, sync_id, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_sync_item(sync_id, output_format='json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_sync_transcode_queue(self, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_sync_transcode_queue(output_format='json')

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_server_pref(self, pref=None, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_server_pref(pref=pref)

        if result:
            return result
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_plexwatch_export_data(self, database_path=None, table_name=None, import_ignore_interval=0, **kwargs):
        from plexpy import plexwatch_import

        db_check_msg = plexwatch_import.validate_database(database=database_path,
                                                          table_name=table_name)
        if db_check_msg == 'success':
            threading.Thread(target=plexwatch_import.import_from_plexwatch,
                             kwargs={'database': database_path,
                                     'table_name': table_name,
                                     'import_ignore_interval': import_ignore_interval}).start()
            return 'Import has started. Check the PlexPy logs to monitor any problems.'
        else:
            return db_check_msg

    @cherrypy.expose
    def plexwatch_import(self, **kwargs):
        return serve_template(templatename="plexwatch_import.html", title="Import PlexWatch Database")

    @cherrypy.expose
    def get_server_id(self, hostname=None, port=None, **kwargs):
        from plexpy import http_handler

        if hostname and port:
            request_handler = http_handler.HTTPHandler(host=hostname,
                                                       port=port,
                                                       token=None)
            uri = '/identity'
            request = request_handler.make_request(uri=uri,
                                                   proto='http',
                                                   request_type='GET',
                                                   output_format='',
                                                   no_token=True)
            if request:
                cherrypy.response.headers['Content-type'] = 'application/xml'
                return request
            else:
                logger.warn('Unable to retrieve data.')
                return None
        else:
            return None

    @cherrypy.expose
    def random_arnold_quotes(self, **kwargs):
        from random import randint
        quote_list = ['To crush your enemies, see them driven before you, and to hear the lamentation of their women!',
                      'Your clothes, give them to me, now!',
                      'Do it!',
                      'If it bleeds, we can kill it',
                      'See you at the party Richter!',
                      'Let off some steam, Bennett',
                      'I\'ll be back',
                      'Get to the chopper!',
                      'Hasta La Vista, Baby!',
                      'It\'s not a tumor!',
                      'Dillon, you son of a bitch!',
                      'Benny!! Screw you!!',
                      'Stop whining! You kids are soft. You lack discipline.',
                      'Nice night for a walk.',
                      'Stick around!',
                      'I need your clothes, your boots and your motorcycle.',
                      'No, it\'s not a tumor. It\'s not a tumor!',
                      'I LIED!',
                      'See you at the party, Richter!',
                      'Are you Sarah Conner?',
                      'I\'m a cop you idiot!',
                      'Come with me if you want to live.',
                      'Who is your daddy and what does he do?'
                      ]

        random_number = randint(0, len(quote_list) - 1)
        return quote_list[int(random_number)]

    @cherrypy.expose
    def get_notification_agent_config(self, config_id, **kwargs):
        config = notifiers.get_notification_agent_config(config_id=config_id)

        checkboxes = {'email_tls': checked(plexpy.CONFIG.EMAIL_TLS)}

        return serve_template(templatename="notification_config.html", title="Notification Configuration",
                              config_id=config_id, data=config, checkboxes=checkboxes)

    @cherrypy.expose
    def get_notification_agent_triggers(self, config_id, **kwargs):
        if config_id.isdigit():
            agents = notifiers.available_notification_agents()
            for agent in agents:
                if int(config_id) == agent['id']:
                    this_agent = agent
                    break
                else:
                    this_agent = None
        else:
            return None

        return serve_template(templatename="notification_triggers_modal.html", title="Notification Triggers",
                              data=this_agent)

    @cherrypy.expose
    def delete_history_rows(self, row_id, **kwargs):
        data_factory = datafactory.DataFactory()

        if row_id:
            delete_row = data_factory.delete_session_history_rows(row_id=row_id)

            if delete_row:
                cherrypy.response.headers['Content-type'] = 'application/json'
                return json.dumps({'message': delete_row})
        else:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps({'message': 'no data received'})

    @cherrypy.expose
    def delete_all_user_history(self, user_id, **kwargs):
        data_factory = datafactory.DataFactory()

        if user_id:
            delete_row = data_factory.delete_all_user_history(user_id=user_id)

            if delete_row:
                cherrypy.response.headers['Content-type'] = 'application/json'
                return json.dumps({'message': delete_row})
        else:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps({'message': 'no data received'})

    @cherrypy.expose
    def search(self, query=''):

        return serve_template(templatename="search.html", title="Search", query=query)

    @cherrypy.expose
    def search_results(self, query, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_search_results(query)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_search_results_children(self, query, media_type=None, season_index=None, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_search_results(query)

        if media_type:
            result['results_list'] = {media_type: result['results_list'][media_type]}
        if media_type == 'season' and season_index:
            for season in result['results_list']['season']:
                if season['index'] == season_index:
                    result['results_list']['season'] = [season]
                    break

        if result:
            return serve_template(templatename="info_search_results_list.html", data=result, title="Search Result List")
        else:
            logger.warn('Unable to retrieve data.')
            return serve_template(templatename="info_search_results_list.html", data=None, title="Search Result List")

    @cherrypy.expose
    def update_history_rating_key(self, old_rating_key, new_rating_key, media_type, **kwargs):
        data_factory = datafactory.DataFactory()
        pms_connect = pmsconnect.PmsConnect()

        old_key_list = data_factory.get_rating_keys_list(rating_key=old_rating_key, media_type=media_type)
        new_key_list = pms_connect.get_rating_keys_list(rating_key=new_rating_key, media_type=media_type)

        update_db = data_factory.update_rating_key(old_key_list=old_key_list,
                                                   new_key_list=new_key_list,
                                                   media_type=media_type)

        if update_db:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps({'message': update_db})
        else:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps({'message': 'no data received'})

    # test code
    @cherrypy.expose
    def get_new_rating_keys(self, rating_key='', media_type='', **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_rating_keys_list(rating_key=rating_key, media_type=media_type)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_old_rating_keys(self, rating_key='', media_type='', **kwargs):

        data_factory = datafactory.DataFactory()
        result = data_factory.get_rating_keys_list(rating_key=rating_key, media_type=media_type)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def get_map_rating_keys(self, old_rating_key, new_rating_key, media_type, **kwargs):

        data_factory = datafactory.DataFactory()
        pms_connect = pmsconnect.PmsConnect()

        if new_rating_key:
            old_key_list = data_factory.get_rating_keys_list(rating_key=old_rating_key, media_type=media_type)
            new_key_list = pms_connect.get_rating_keys_list(rating_key=new_rating_key, media_type=media_type)

            result = data_factory.update_rating_key(old_key_list=old_key_list,
                                                    new_key_list=new_key_list,
                                                    media_type=media_type)

        if result:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return json.dumps(result)
        else:
            logger.warn('Unable to retrieve data.')

    @cherrypy.expose
    def discover(self, token=''):
        """
        Returns the servers that you own as a
        list of dicts (formatted for selectize)
        """
        # Need to set token so result dont return http 401
        plexpy.CONFIG.__setattr__('PMS_TOKEN', token)
        plexpy.CONFIG.write()

        result = plextv.PlexTV()
        servers = result.discover()
        if servers:
            cherrypy.response.headers['Content-type'] = 'application/json'
            return servers
