# coding=utf-8
import datetime
import StringIO
import glob
import os
import traceback
import urlparse

from zipfile import ZipFile, ZIP_DEFLATED

from subzero.language import Language

from subzero.lib.io import FileIO
from subzero.constants import PREFIX, PLUGIN_IDENTIFIER
from menu_helpers import SubFolderObjectContainer, debounce, set_refresh_menu_state, ZipObject, ObjectContainer, route
from main import fatality
from support.helpers import timestamp, pad_title
from support.config import config
from support.lib import Plex
from support.storage import reset_storage, log_storage, get_subtitle_storage
from support.scheduler import scheduler
from support.items import set_mods_for_part, get_item_kind_from_rating_key
from support.i18n import _


@route(PREFIX + '/advanced')
def AdvancedMenu(randomize=None, header=None, message=None):
    oc = SubFolderObjectContainer(
            header=header or _("Internal stuff, pay attention!"),
            message=message,
            no_cache=True,
            no_history=True,
            replace_parent=False,
            title2=_("Advanced"))

    if config.lock_advanced_menu and not config.pin_correct:
        oc.add(DirectoryObject(
            key=Callback(
                    PinMenu,
                    randomize=timestamp(),
                    success_go_to=_("advanced")),
            title=pad_title(_("Enter PIN")),
            summary=_("The owner has restricted the access to this menu. Please enter the correct pin"),
        ))
        return oc

    oc.add(DirectoryObject(
        key=Callback(TriggerRestart, randomize=timestamp()),
        title=pad_title(_("Restart the plugin")),
    ))
    oc.add(DirectoryObject(
        key=Callback(GetLogsLink),
        title=_("Get my logs (copy the appearing link and open it in your browser, please)"),
        summary=_("Copy the appearing link and open it in your browser, please"),
    ))
    oc.add(DirectoryObject(
        key=Callback(TriggerBetterSubtitles, randomize=timestamp()),
        title=pad_title(_("Trigger find better subtitles")),
    ))
    oc.add(DirectoryObject(
        key=Callback(SkipFindBetterSubtitles, randomize=timestamp()),
        title=pad_title(_("Skip next find better subtitles (sets last run to now)")),
    ))
    oc.add(DirectoryObject(
        key=Callback(SkipRecentlyAddedMissing, randomize=timestamp()),
        title=pad_title(_("Skip next find recently added with missing subtitles (sets last run to now)")),
    ))
    oc.add(DirectoryObject(
        key=Callback(TriggerStorageMaintenance, randomize=timestamp()),
        title=pad_title(_("Trigger subtitle storage maintenance")),
    ))
    oc.add(DirectoryObject(
        key=Callback(TriggerStorageMigration, randomize=timestamp()),
        title=pad_title(_("Trigger subtitle storage migration (expensive)")),
    ))
    oc.add(DirectoryObject(
        key=Callback(TriggerCacheMaintenance, randomize=timestamp()),
        title=pad_title(_("Trigger cache maintenance (refiners, providers and packs/archives)")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ApplyDefaultMods, randomize=timestamp()),
        title=pad_title(_("Apply configured default subtitle mods to all (active) stored subtitles")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ReApplyMods, randomize=timestamp()),
        title=pad_title(_("Re-Apply mods of all stored subtitles")),
    ))
    oc.add(DirectoryObject(
        key=Callback(LogStorage, key="tasks", randomize=timestamp()),
        title=pad_title(_("Log the plugin's scheduled tasks state storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(LogStorage, key="ignore", randomize=timestamp()),
        title=pad_title(_("Log the plugin's internal ignorelist storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(LogStorage, key=None, randomize=timestamp()),
        title=pad_title(_("Log the plugin's complete state storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ResetStorage, key="tasks", randomize=timestamp()),
        title=pad_title(_("Reset the plugin's scheduled tasks state storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ResetStorage, key="ignore", randomize=timestamp()),
        title=pad_title(_("Reset the plugin's internal ignorelist storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ResetStorage, key="menu_history", randomize=timestamp()),
        title=pad_title(_("Reset the plugin's menu history storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(InvalidateCache, randomize=timestamp()),
        title=pad_title(_("Invalidate Sub-Zero metadata caches (subliminal)")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ResetProviderThrottle, randomize=timestamp()),
        title=pad_title(_("Reset provider throttle states")),
    ))
    return oc


def DispatchRestart():
    Thread.CreateTimer(1.0, Restart)


@route(PREFIX + '/advanced/restart/trigger')
@debounce
def TriggerRestart(randomize=None):
    set_refresh_menu_state(_("Restarting the plugin"))
    DispatchRestart()
    return fatality(
            header=_("Restart triggered, please wait about 5 seconds"),
            force_title=" ",
            only_refresh=True,
            replace_parent=True,
            no_history=True,
            randomize=timestamp())


@route(PREFIX + '/advanced/restart/execute')
@debounce
def Restart(randomize=None):
    Plex[":/plugins"].restart(PLUGIN_IDENTIFIER)


@route(PREFIX + '/storage/reset', sure=bool)
@debounce
def ResetStorage(key, randomize=None, sure=False):
    if not sure:
        oc = SubFolderObjectContainer(
                no_history=True,
                title1=_("Reset subtitle storage"),
                title2=_("Are you sure?"))
        oc.add(DirectoryObject(
            key=Callback(
                    ResetStorage,
                    key=key,
                    sure=True,
                    randomize=timestamp()),
            title=pad_title(_("Are you really sure?")),

        ))
        return oc

    reset_storage(key)

    if key == "tasks":
        # reinitialize the scheduler
        scheduler.init_storage()
        scheduler.setup_tasks()

    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("Information Storage (%s) reset", key)
    )


@route(PREFIX + '/storage/log')
def LogStorage(key, randomize=None):
    log_storage(key)
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("Information Storage (%s) logged", key)
    )


@route(PREFIX + '/triggerbetter')
@debounce
def TriggerBetterSubtitles(randomize=None):
    scheduler.dispatch_task("FindBetterSubtitles")
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("FindBetterSubtitles triggered")
    )


@route(PREFIX + '/skipbetter')
@debounce
def SkipFindBetterSubtitles(randomize=None):
    task = scheduler.task("FindBetterSubtitles")
    task.last_run = datetime.datetime.now()

    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("FindBetterSubtitles skipped")
    )


@route(PREFIX + '/skipram')
@debounce
def SkipRecentlyAddedMissing(randomize=None):
    task = scheduler.task("SearchAllRecentlyAddedMissing")
    task.last_run = datetime.datetime.now()

    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("SearchAllRecentlyAddedMissing skipped")
    )


@route(PREFIX + '/triggermaintenance')
@debounce
def TriggerStorageMaintenance(randomize=None):
    scheduler.dispatch_task("SubtitleStorageMaintenance")
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("SubtitleStorageMaintenance triggered")
    )


@route(PREFIX + '/triggerstoragemigration')
@debounce
def TriggerStorageMigration(randomize=None):
    scheduler.dispatch_task("MigrateSubtitleStorage")
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("MigrateSubtitleStorage triggered")
    )


@route(PREFIX + '/triggercachemaintenance')
@debounce
def TriggerCacheMaintenance(randomize=None):
    scheduler.dispatch_task("CacheMaintenance")
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("TriggerCacheMaintenance triggered")
    )


def apply_default_mods(reapply_current=False, scandir_generic=False):
    storage = get_subtitle_storage()
    subs_applied = 0

    try:
        for fn in storage.get_all_files(scandir_generic=scandir_generic):
            data = storage.load(None, filename=fn)
            if data:
                video_id = data.video_id
                item_type = get_item_kind_from_rating_key(video_id)
                if not item_type:
                    continue

                for part_id, part in data.parts.iteritems():
                    for lang, subs in part.iteritems():
                        current_sub = subs.get("current")
                        if not current_sub:
                            continue
                        sub = subs[current_sub]

                        if not sub.content:
                            continue

                        current_mods = sub.mods or []
                        if not reapply_current:
                            add_mods = list(set(config.default_mods).difference(set(current_mods)))
                            if not add_mods:
                                continue
                        else:
                            if not current_mods:
                                continue
                            add_mods = []

                        try:
                            set_mods_for_part(video_id, part_id, Language.fromietf(lang), item_type, add_mods, mode="add")
                        except:
                            Log.Error("Couldn't set mods for %s:%s: %s", video_id, part_id, traceback.format_exc())
                            continue

                        subs_applied += 1
    except OSError:
        return apply_default_mods(reapply_current=reapply_current, scandir_generic=True)
    storage.destroy()
    Log.Debug("Applied mods to %i items" % subs_applied)


@route(PREFIX + '/applydefaultmods')
@debounce
def ApplyDefaultMods(randomize=None):
    Thread.CreateTimer(1.0, apply_default_mods)
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("This may take some time ...")
    )


@route(PREFIX + '/reapplyallmods')
@debounce
def ReApplyMods(randomize=None):
    Thread.CreateTimer(1.0, apply_default_mods, reapply_current=True)
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("This may take some time ...")
    )


@route(PREFIX + '/get_logs_link')
def GetLogsLink():
    if not config.plex_token:
        oc = ObjectContainer(
                title2=_("Download Logs"),
                no_cache=True,
                no_history=True,
                header=_("Sorry, feature unavailable"),
                message=_("Universal Plex token not available"))
        return oc

    # try getting the link base via the request in context, first, otherwise use the public ip
    req_headers = Core.sandbox.context.request.headers
    get_external_ip = True
    link_base = ""

    if "Origin" in req_headers:
        link_base = req_headers["Origin"]
        Log.Debug("Using origin-based link_base")
        get_external_ip = False

    elif "Referer" in req_headers:
        parsed = urlparse.urlparse(req_headers["Referer"])
        link_base = "%s://%s%s" % (parsed.scheme, parsed.hostname, (":%s" % parsed.port) if parsed.port else "")
        Log.Debug("Using referer-based link_base")
        get_external_ip = False

    if get_external_ip or "plex.tv" in link_base:
        ip = Core.networking.http_request("http://www.plexapp.com/ip.php", cacheTime=7200).content.strip()
        link_base = "https://%s:32400" % ip
        Log.Debug("Using ip-based fallback link_base")

    logs_link = "%s%s?X-Plex-Token=%s" % (link_base, PREFIX + '/logs', config.plex_token)
    oc = ObjectContainer(
            title2=logs_link,
            no_cache=True,
            no_history=True,
            header=_("Copy this link and open this in your browser, please"),
            message=logs_link)
    return oc


@route(PREFIX + '/logs')
def DownloadLogs():
    buff = StringIO.StringIO()
    zip_archive = ZipFile(buff, mode='w', compression=ZIP_DEFLATED)

    logs = sorted(glob.glob(config.plugin_log_path + '*')) + [config.server_log_path]
    for path in logs:
        data = StringIO.StringIO()
        data.write(FileIO.read(path))
        zip_archive.writestr(os.path.basename(path), data.getvalue())

    zip_archive.close()

    return ZipObject(buff.getvalue())


@route(PREFIX + '/invalidatecache')
@debounce
def InvalidateCache(randomize=None):
    from subliminal.cache import region
    if config.new_style_cache:
        region.backend.clear()
    else:
        region.invalidate()
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("Cache invalidated")
    )


@route(PREFIX + '/pin')
def PinMenu(pin="", randomize=None, success_go_to="channel"):
    oc = ObjectContainer(
            title2=_("Enter PIN number ") + str(len(pin) + 1),
            no_cache=True,
            no_history=True,
            skip_pin_lock=True)

    if pin == config.pin:
        Dict["pin_correct_time"] = datetime.datetime.now()
        config.locked = False
        if success_go_to == "channel":
            return fatality(
                force_title=_("PIN correct"),
                header=_("PIN correct"),
                no_history=True)
        elif success_go_to == "advanced":
            return AdvancedMenu(randomize=timestamp())

    for i in range(10):
        oc.add(DirectoryObject(
            key=Callback(
                    PinMenu,
                    randomize=timestamp(),
                    pin=pin + str(i),
                    success_go_to=success_go_to),
            title=pad_title(str(i)),
        ))
    oc.add(DirectoryObject(
        key=Callback(
                PinMenu,
                randomize=timestamp(),
                success_go_to=success_go_to),
        title=pad_title(_("Reset")),
    ))
    return oc


@route(PREFIX + '/pin_lock')
def ClearPin(randomize=None):
    Dict["pin_correct_time"] = None
    config.locked = True
    return fatality(force_title=_("Menu locked"), header=" ", no_history=True)


@route(PREFIX + '/reset_throttle')
def ResetProviderThrottle(randomize=None):
    Dict["provider_throttle"] = {}
    Dict.Save()
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("Provider throttles reset")
    )

# coding=utf-8
import datetime
import StringIO
import glob
import os
import traceback
import urlparse

from zipfile import ZipFile, ZIP_DEFLATED

from subzero.language import Language

from subzero.lib.io import FileIO
from subzero.constants import PREFIX, PLUGIN_IDENTIFIER
from menu_helpers import SubFolderObjectContainer, debounce, set_refresh_menu_state, ZipObject, ObjectContainer, route
from main import fatality
from support.helpers import timestamp, pad_title
from support.config import config
from support.lib import Plex
from support.storage import reset_storage, log_storage, get_subtitle_storage
from support.scheduler import scheduler
from support.items import set_mods_for_part, get_item_kind_from_rating_key
from support.i18n import _


@route(PREFIX + '/advanced')
def AdvancedMenu(randomize=None, header=None, message=None):
    oc = SubFolderObjectContainer(
            header=header or _("Internal stuff, pay attention!"),
            message=message,
            no_cache=True,
            no_history=True,
            replace_parent=False,
            title2=_("Advanced"))

    if config.lock_advanced_menu and not config.pin_correct:
        oc.add(DirectoryObject(
            key=Callback(
                    PinMenu,
                    randomize=timestamp(),
                    success_go_to=_("advanced")),
            title=pad_title(_("Enter PIN")),
            summary=_("The owner has restricted the access to this menu. Please enter the correct pin"),
        ))
        return oc

    oc.add(DirectoryObject(
        key=Callback(TriggerRestart, randomize=timestamp()),
        title=pad_title(_("Restart the plugin")),
    ))
    oc.add(DirectoryObject(
        key=Callback(GetLogsLink),
        title=_("Get my logs (copy the appearing link and open it in your browser, please)"),
        summary=_("Copy the appearing link and open it in your browser, please"),
    ))
    oc.add(DirectoryObject(
        key=Callback(TriggerBetterSubtitles, randomize=timestamp()),
        title=pad_title(_("Trigger find better subtitles")),
    ))
    oc.add(DirectoryObject(
        key=Callback(SkipFindBetterSubtitles, randomize=timestamp()),
        title=pad_title(_("Skip next find better subtitles (sets last run to now)")),
    ))
    oc.add(DirectoryObject(
        key=Callback(SkipRecentlyAddedMissing, randomize=timestamp()),
        title=pad_title(_("Skip next find recently added with missing subtitles (sets last run to now)")),
    ))
    oc.add(DirectoryObject(
        key=Callback(TriggerStorageMaintenance, randomize=timestamp()),
        title=pad_title(_("Trigger subtitle storage maintenance")),
    ))
    oc.add(DirectoryObject(
        key=Callback(TriggerStorageMigration, randomize=timestamp()),
        title=pad_title(_("Trigger subtitle storage migration (expensive)")),
    ))
    oc.add(DirectoryObject(
        key=Callback(TriggerCacheMaintenance, randomize=timestamp()),
        title=pad_title(_("Trigger cache maintenance (refiners, providers and packs/archives)")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ApplyDefaultMods, randomize=timestamp()),
        title=pad_title(_("Apply configured default subtitle mods to all (active) stored subtitles")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ReApplyMods, randomize=timestamp()),
        title=pad_title(_("Re-Apply mods of all stored subtitles")),
    ))
    oc.add(DirectoryObject(
        key=Callback(LogStorage, key="tasks", randomize=timestamp()),
        title=pad_title(_("Log the plugin's scheduled tasks state storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(LogStorage, key="ignore", randomize=timestamp()),
        title=pad_title(_("Log the plugin's internal ignorelist storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(LogStorage, key=None, randomize=timestamp()),
        title=pad_title(_("Log the plugin's complete state storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ResetStorage, key="tasks", randomize=timestamp()),
        title=pad_title(_("Reset the plugin's scheduled tasks state storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ResetStorage, key="ignore", randomize=timestamp()),
        title=pad_title(_("Reset the plugin's internal ignorelist storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ResetStorage, key="menu_history", randomize=timestamp()),
        title=pad_title(_("Reset the plugin's menu history storage")),
    ))
    oc.add(DirectoryObject(
        key=Callback(InvalidateCache, randomize=timestamp()),
        title=pad_title(_("Invalidate Sub-Zero metadata caches (subliminal)")),
    ))
    oc.add(DirectoryObject(
        key=Callback(ResetProviderThrottle, randomize=timestamp()),
        title=pad_title(_("Reset provider throttle states")),
    ))
    return oc


def DispatchRestart():
    Thread.CreateTimer(1.0, Restart)


@route(PREFIX + '/advanced/restart/trigger')
@debounce
def TriggerRestart(randomize=None):
    set_refresh_menu_state(_("Restarting the plugin"))
    DispatchRestart()
    return fatality(
            header=_("Restart triggered, please wait about 5 seconds"),
            force_title=" ",
            only_refresh=True,
            replace_parent=True,
            no_history=True,
            randomize=timestamp())


@route(PREFIX + '/advanced/restart/execute')
@debounce
def Restart(randomize=None):
    Plex[":/plugins"].restart(PLUGIN_IDENTIFIER)


@route(PREFIX + '/storage/reset', sure=bool)
@debounce
def ResetStorage(key, randomize=None, sure=False):
    if not sure:
        oc = SubFolderObjectContainer(
                no_history=True,
                title1=_("Reset subtitle storage"),
                title2=_("Are you sure?"))
        oc.add(DirectoryObject(
            key=Callback(
                    ResetStorage,
                    key=key,
                    sure=True,
                    randomize=timestamp()),
            title=pad_title(_("Are you really sure?")),

        ))
        return oc

    reset_storage(key)

    if key == "tasks":
        # reinitialize the scheduler
        scheduler.init_storage()
        scheduler.setup_tasks()

    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("Information Storage (%s) reset", key)
    )


@route(PREFIX + '/storage/log')
def LogStorage(key, randomize=None):
    log_storage(key)
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("Information Storage (%s) logged", key)
    )


@route(PREFIX + '/triggerbetter')
@debounce
def TriggerBetterSubtitles(randomize=None):
    scheduler.dispatch_task("FindBetterSubtitles")
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("FindBetterSubtitles triggered")
    )


@route(PREFIX + '/skipbetter')
@debounce
def SkipFindBetterSubtitles(randomize=None):
    task = scheduler.task("FindBetterSubtitles")
    task.last_run = datetime.datetime.now()

    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("FindBetterSubtitles skipped")
    )


@route(PREFIX + '/skipram')
@debounce
def SkipRecentlyAddedMissing(randomize=None):
    task = scheduler.task("SearchAllRecentlyAddedMissing")
    task.last_run = datetime.datetime.now()

    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("SearchAllRecentlyAddedMissing skipped")
    )


@route(PREFIX + '/triggermaintenance')
@debounce
def TriggerStorageMaintenance(randomize=None):
    scheduler.dispatch_task("SubtitleStorageMaintenance")
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("SubtitleStorageMaintenance triggered")
    )


@route(PREFIX + '/triggerstoragemigration')
@debounce
def TriggerStorageMigration(randomize=None):
    scheduler.dispatch_task("MigrateSubtitleStorage")
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("MigrateSubtitleStorage triggered")
    )


@route(PREFIX + '/triggercachemaintenance')
@debounce
def TriggerCacheMaintenance(randomize=None):
    scheduler.dispatch_task("CacheMaintenance")
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("TriggerCacheMaintenance triggered")
    )


def apply_default_mods(reapply_current=False, scandir_generic=False):
    storage = get_subtitle_storage()
    subs_applied = 0

    try:
        for fn in storage.get_all_files(scandir_generic=scandir_generic):
            data = storage.load(None, filename=fn)
            if data:
                video_id = data.video_id
                item_type = get_item_kind_from_rating_key(video_id)
                if not item_type:
                    continue

                for part_id, part in data.parts.iteritems():
                    for lang, subs in part.iteritems():
                        current_sub = subs.get("current")
                        if not current_sub:
                            continue
                        sub = subs[current_sub]

                        if not sub.content:
                            continue

                        current_mods = sub.mods or []
                        if not reapply_current:
                            add_mods = list(set(config.default_mods).difference(set(current_mods)))
                            if not add_mods:
                                continue
                        else:
                            if not current_mods:
                                continue
                            add_mods = []

                        try:
                            set_mods_for_part(video_id, part_id, Language.fromietf(lang), item_type, add_mods, mode="add")
                        except:
                            Log.Error("Couldn't set mods for %s:%s: %s", video_id, part_id, traceback.format_exc())
                            continue

                        subs_applied += 1
    except OSError:
        return apply_default_mods(reapply_current=reapply_current, scandir_generic=True)
    storage.destroy()
    Log.Debug("Applied mods to %i items" % subs_applied)


@route(PREFIX + '/applydefaultmods')
@debounce
def ApplyDefaultMods(randomize=None):
    Thread.CreateTimer(1.0, apply_default_mods)
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("This may take some time ...")
    )


@route(PREFIX + '/reapplyallmods')
@debounce
def ReApplyMods(randomize=None):
    Thread.CreateTimer(1.0, apply_default_mods, reapply_current=True)
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("This may take some time ...")
    )


@route(PREFIX + '/get_logs_link')
def GetLogsLink():
    if not config.plex_token:
        oc = ObjectContainer(
                title2=_("Download Logs"),
                no_cache=True,
                no_history=True,
                header=_("Sorry, feature unavailable"),
                message=_("Universal Plex token not available"))
        return oc

    # try getting the link base via the request in context, first, otherwise use the public ip
    req_headers = Core.sandbox.context.request.headers
    get_external_ip = True
    link_base = ""

    if "Origin" in req_headers:
        link_base = req_headers["Origin"]
        Log.Debug("Using origin-based link_base")
        get_external_ip = False

    elif "Referer" in req_headers:
        parsed = urlparse.urlparse(req_headers["Referer"])
        link_base = "%s://%s%s" % (parsed.scheme, parsed.hostname, (":%s" % parsed.port) if parsed.port else "")
        Log.Debug("Using referer-based link_base")
        get_external_ip = False

    if get_external_ip or "plex.tv" in link_base:
        ip = Core.networking.http_request("http://www.plexapp.com/ip.php", cacheTime=7200).content.strip()
        link_base = "https://%s:32400" % ip
        Log.Debug("Using ip-based fallback link_base")

    logs_link = "%s%s?X-Plex-Token=%s" % (link_base, PREFIX + '/logs', config.plex_token)
    oc = ObjectContainer(
            title2=logs_link,
            no_cache=True,
            no_history=True,
            header=_("Copy this link and open this in your browser, please"),
            message=logs_link)
    return oc


@route(PREFIX + '/logs')
def DownloadLogs():
    buff = StringIO.StringIO()
    zip_archive = ZipFile(buff, mode='w', compression=ZIP_DEFLATED)

    logs = sorted(glob.glob(config.plugin_log_path + '*')) + [config.server_log_path]
    for path in logs:
        data = StringIO.StringIO()
        data.write(FileIO.read(path))
        zip_archive.writestr(os.path.basename(path), data.getvalue())

    zip_archive.close()

    return ZipObject(buff.getvalue())


@route(PREFIX + '/invalidatecache')
@debounce
def InvalidateCache(randomize=None):
    from subliminal.cache import region
    if config.new_style_cache:
        region.backend.clear()
    else:
        region.invalidate()
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("Cache invalidated")
    )


@route(PREFIX + '/pin')
def PinMenu(pin="", randomize=None, success_go_to="channel"):
    oc = ObjectContainer(
            title2=_("Enter PIN number ") + str(len(pin) + 1),
            no_cache=True,
            no_history=True,
            skip_pin_lock=True)

    if pin == config.pin:
        Dict["pin_correct_time"] = datetime.datetime.now()
        config.locked = False
        if success_go_to == "channel":
            return fatality(
                force_title=_("PIN correct"),
                header=_("PIN correct"),
                no_history=True)
        elif success_go_to == "advanced":
            return AdvancedMenu(randomize=timestamp())

    for i in range(10):
        oc.add(DirectoryObject(
            key=Callback(
                    PinMenu,
                    randomize=timestamp(),
                    pin=pin + str(i),
                    success_go_to=success_go_to),
            title=pad_title(str(i)),
        ))
    oc.add(DirectoryObject(
        key=Callback(
                PinMenu,
                randomize=timestamp(),
                success_go_to=success_go_to),
        title=pad_title(_("Reset")),
    ))
    return oc


@route(PREFIX + '/pin_lock')
def ClearPin(randomize=None):
    Dict["pin_correct_time"] = None
    config.locked = True
    return fatality(force_title=_("Menu locked"), header=" ", no_history=True)


@route(PREFIX + '/reset_throttle')
def ResetProviderThrottle(randomize=None):
    Dict["provider_throttle"] = {}
    Dict.Save()
    return AdvancedMenu(
        randomize=timestamp(),
        header=_("Success"),
        message=_("Provider throttles reset")
    )

# coding=utf-8 # reinitialize the scheduler # try getting the link base via the request in context, first, otherwise use the public ip