(function () {
    "use strict";

    var KEY_CODES = {
        ENTER: 13,
        LEFT: 37,
        UP: 38,
        RIGHT: 39,
        DOWN: 40,
        BACK: 10009,
        RETURN: 461,
        PLAY: 415,
        PAUSE: 19,
        PLAY_PAUSE: 10252,
        MEDIA_PLAY_PAUSE: 179
    };

    var APP_SERVER_URL = "https://rivobu.com/backend";
    var IPTV_PLAYLIST_URL = "";
    var IPTV_MAX_ITEMS_PER_SECTION = 0;
    var VOD_RENDER_LIMIT = 180;
    var VOD_RENDER_STEP = 120;
    var LIVE_RENDER_LIMIT = 160;
    var LIVE_RENDER_STEP = 120;
    var XTREAM_LIBRARY_TIMEOUT = 35000;

    var SECTIONS = [
        { id: "live", title: "Ù…Ø¨Ø§Ø´Ø±", subtitle: "Live TV", icon: "TV" },
        { id: "movies", title: "Ø£ÙÙ„Ø§Ù…", subtitle: "Movies", icon: "Play" },
        { id: "series", title: "Ù…Ø³Ù„Ø³Ù„Ø§Øª", subtitle: "Series", icon: "List" }
    ];

    var state = {
        screen: "home",
        homePane: "sections",
        homeActionIndex: 0,
        latestIndex: 0,
        managementFocusIndex: 0,
        sectionIndex: 0,
        activeSection: "live",
        searchQuery: "",
        librarySortMode: "az",
        categories: [],
        categoryIndex: 0,
        itemIndex: 0,
        libraryPane: "categories",
        libraryActionIndex: 0,
        virtualKeyboardMode: "ar",
        virtualKeyboardTextMode: "ar",
        virtualKeyboardIndex: 0,
        pinDialogOpen: false,
        pinDialogValue: "",
        pinDialogIndex: 0,
        pinDialogCallback: null,
        detailActionIndex: 0,
        detailPane: "actions",
        deviceMac: "",
        activation: null,
        language: "ar",
        playerMode: "auto",
        seasonIndex: 0,
        episodeIndex: 0,
        castIndex: 0,
        actorWorkIndex: 0,
        focusedControl: "play",
        playerToolOptionIndex: -1,
        subtitleSelectionLocked: false,
        subtitleCueTracks: {},
        activeSubtitleCueKey: "",
        activeSubtitleCues: [],
        lastSubtitleText: "",
        subtitleOverlayTimer: null,
        subtitleDelay: 0,
        overlayTimer: null,
        returnScreen: "library",
        detailReturnScreen: "library",
        detailItem: null,
        playingItem: null,
        pendingResume: null,
        lastProgressSaveAt: 0,
        vodRenderLimit: VOD_RENDER_LIMIT,
        liveRenderLimit: LIVE_RENDER_LIMIT,
        liveOkItemKey: "",
        liveOkPressCount: 0,
        splashVisible: true,
        splashStartedAt: Date.now(),
        splashHideTimer: null,
        playlistFormVisible: false
    };

    var elements = {};
    var player;
    var allItems = [];
    var visibleItems = [];
    var renderedItems = [];
    var latestItems = [];
    var detailMetadataCache = {};
    var tmdbCastCache = {};
    var tmdbPersonImageCache = {};
    var tmdbResolveCache = {};
    var imdbResolveCache = {};
    var detailSeasons = [];
    var detailEpisodes = [];
    var actorWorks = [];
    var actorSearchToken = 0;
    var subtitleCueKeyCounter = 0;
    var livePreviewPlayer = null;
    var livePreviewTimer = null;
    var livePreviewItemKey = "";
    var liveUnlockedChannels = {};

    function init() {
        cacheElements();
        registerRemoteKeys();
        createPlayer();
        loadAppSettings();
        bindDomEvents();
        renderVirtualKeyboard();
        renderHome();
        updateHomeClock();
        setInterval(updateHomeClock, 30000);
        initializeActivation();
    }

    function cacheElements() {
        elements.homeScreen = document.getElementById("homeScreen");
        elements.splashScreen = document.getElementById("splashScreen");
        elements.splashStatus = document.getElementById("splashStatus");
        elements.playlistScreen = document.getElementById("playlistScreen");
        elements.accountScreen = document.getElementById("accountScreen");
        elements.historyScreen = document.getElementById("historyScreen");
        elements.settingsScreen = document.getElementById("settingsScreen");
        elements.libraryScreen = document.getElementById("libraryScreen");
        elements.detailScreen = document.getElementById("detailScreen");
        elements.playerScreen = document.getElementById("playerScreen");
        elements.playlistStatus = document.getElementById("playlistStatus");
        elements.sectionList = document.getElementById("sectionList");
        elements.homeActionList = document.getElementById("homeActionList");
        elements.latestList = document.getElementById("latestList");
        elements.latestCount = document.getElementById("latestCount");
        elements.activationSummary = document.getElementById("activationSummary");
        elements.playlistBackButton = document.getElementById("playlistBackButton");
        elements.playlistDeviceInfo = document.getElementById("playlistDeviceInfo");
        elements.playlistCurrentInfo = document.getElementById("playlistCurrentInfo");
        elements.playlistForm = document.getElementById("playlistForm");
        elements.playlistAddButton = document.getElementById("playlistAddButton");
        elements.playlistList = document.getElementById("playlistList");
        elements.playlistNameInput = document.getElementById("playlistNameInput");
        elements.playlistType = document.getElementById("playlistType");
        elements.playlistUrlInput = document.getElementById("playlistUrlInput");
        elements.playlistServerInput = document.getElementById("playlistServerInput");
        elements.playlistUsernameInput = document.getElementById("playlistUsernameInput");
        elements.playlistPasswordInput = document.getElementById("playlistPasswordInput");
        elements.playlistDeleteButton = document.getElementById("playlistDeleteButton");
        elements.playlistSaveButton = document.getElementById("playlistSaveButton");
        elements.playlistFormMessage = document.getElementById("playlistFormMessage");
        elements.accountBackButton = document.getElementById("accountBackButton");
        elements.accountInfo = document.getElementById("accountInfo");
        elements.accountPayButton = document.getElementById("accountPayButton");
        elements.accountDashboardButton = document.getElementById("accountDashboardButton");
        elements.historyBackButton = document.getElementById("historyBackButton");
        elements.historyHeading = document.getElementById("historyHeading");
        elements.recentHeading = document.getElementById("recentHeading");
        elements.favoriteHeading = document.getElementById("favoriteHeading");
        elements.recentList = document.getElementById("recentList");
        elements.favoriteList = document.getElementById("favoriteList");
        elements.settingsBackButton = document.getElementById("settingsBackButton");
        elements.settingsForm = document.getElementById("settingsForm");
        elements.appLanguageSelect = document.getElementById("appLanguageSelect");
        elements.playerModeSelect = document.getElementById("playerModeSelect");
        elements.settingsMessage = document.getElementById("settingsMessage");
        elements.settingsNav = document.querySelector(".settings-nav");
        elements.homeTime = document.getElementById("homeTime");
        elements.homeDate = document.getElementById("homeDate");
        elements.powerButton = document.getElementById("powerButton");
        elements.libraryBackButton = document.getElementById("libraryBackButton");
        elements.libraryEyebrow = document.getElementById("libraryEyebrow");
        elements.libraryHeading = document.getElementById("libraryHeading");
        elements.categoryHeading = document.getElementById("categoryHeading");
        elements.librarySearchInput = document.getElementById("librarySearchInput");
        elements.virtualKeyboard = document.getElementById("virtualKeyboard");
        elements.categoryList = document.getElementById("categoryList");
        elements.itemList = document.getElementById("itemList");
        elements.itemCount = document.getElementById("itemCount");
        elements.liveTopMenu = document.getElementById("liveTopMenu");
        elements.libraryActionBar = document.getElementById("libraryActionBar");
        elements.liveFavoriteButton = document.getElementById("liveFavoriteButton");
        elements.liveLockButton = document.getElementById("liveLockButton");
        elements.liveReloadButton = document.getElementById("liveReloadButton");
        elements.librarySortSelect = document.getElementById("librarySortSelect");
        elements.liveInfoPanel = document.getElementById("liveInfoPanel");
        elements.livePreviewVideo = document.getElementById("livePreviewVideo");
        elements.livePreviewLoading = document.getElementById("livePreviewLoading");
        elements.livePreviewError = document.getElementById("livePreviewError");
        elements.liveNowNumber = document.getElementById("liveNowNumber");
        elements.liveNowLogo = document.getElementById("liveNowLogo");
        elements.liveNowTitle = document.getElementById("liveNowTitle");
        elements.livePreviewPlayButton = document.getElementById("livePreviewPlayButton");
        elements.livePreviewProgress = document.getElementById("livePreviewProgress");
        elements.livePreviewCurrentTime = document.getElementById("livePreviewCurrentTime");
        elements.livePreviewDuration = document.getElementById("livePreviewDuration");
        elements.livePreviewTitle = document.getElementById("livePreviewTitle");
        elements.detailBackdrop = document.getElementById("detailBackdrop");
        elements.detailBackButton = document.getElementById("detailBackButton");
        elements.detailEyebrow = document.getElementById("detailEyebrow");
        elements.detailTitle = document.getElementById("detailTitle");
        elements.detailMeta = document.getElementById("detailMeta");
        elements.detailDescription = document.getElementById("detailDescription");
        elements.detailPlayButton = document.getElementById("detailPlayButton");
        elements.detailTrailerButton = document.getElementById("detailTrailerButton");
        elements.detailFavoriteButton = document.getElementById("detailFavoriteButton");
        elements.detailWatchlistButton = document.getElementById("detailWatchlistButton");
        elements.detailActionMessage = document.getElementById("detailActionMessage");
        elements.detailPoster = document.getElementById("detailPoster");
        elements.seasonSection = document.getElementById("seasonSection");
        elements.seasonList = document.getElementById("seasonList");
        elements.episodeList = document.getElementById("episodeList");
        elements.castList = document.getElementById("castList");
        elements.actorWorksSection = document.getElementById("actorWorksSection");
        elements.actorWorksTitle = document.getElementById("actorWorksTitle");
        elements.actorWorksList = document.getElementById("actorWorksList");
        elements.video = document.getElementById("videoPlayer");
        elements.subtitleOverlay = document.getElementById("subtitleOverlay");
        elements.videoTitle = document.getElementById("videoTitle");
        elements.loading = document.getElementById("loadingIndicator");
        elements.error = document.getElementById("errorMessage");
        elements.overlay = document.getElementById("controlsOverlay");
        elements.playPauseButton = document.getElementById("playPauseButton");
        elements.backButton = document.getElementById("backButton");
        elements.progressBar = document.getElementById("progressBar");
        elements.currentTime = document.getElementById("currentTime");
        elements.duration = document.getElementById("duration");
        elements.audioLanguageSelect = document.getElementById("audioLanguageSelect");
        elements.subtitleSelect = document.getElementById("subtitleSelect");
        elements.subtitleStyleSelect = document.getElementById("subtitleStyleSelect");
        elements.screenModeSelect = document.getElementById("screenModeSelect");
        elements.playerToolButtons = document.getElementById("playerToolButtons");
        elements.favoriteToolButton = document.getElementById("favoriteToolButton");
        elements.subtitleToolButton = document.getElementById("subtitleToolButton");
        elements.subtitleSettingsToolButton = document.getElementById("subtitleSettingsToolButton");
        elements.screenToolButton = document.getElementById("screenToolButton");
        elements.playerToolPanel = document.getElementById("playerToolPanel");
        elements.playerToolTitle = document.getElementById("playerToolTitle");
        elements.playerToolOptions = document.getElementById("playerToolOptions");
        elements.resumeDialog = document.getElementById("resumeDialog");
        elements.resumeDialogText = document.getElementById("resumeDialogText");
        elements.resumeContinueButton = document.getElementById("resumeContinueButton");
        elements.resumeRestartButton = document.getElementById("resumeRestartButton");
        elements.pinDialog = document.getElementById("pinDialog");
        elements.pinDialogTitle = document.getElementById("pinDialogTitle");
        elements.pinDialogValue = document.getElementById("pinDialogValue");
        elements.pinDialogMessage = document.getElementById("pinDialogMessage");
        elements.pinKeyboard = document.getElementById("pinKeyboard");
    }

    function registerRemoteKeys() {
        if (!window.tizen || !tizen.tvinputdevice) {
            return;
        }

        ["MediaPlay", "MediaPause", "MediaPlayPause"].forEach(function (keyName) {
            try {
                tizen.tvinputdevice.registerKey(keyName);
            } catch (error) {
                console.log("Could not register key:", keyName, error.message);
            }
        });
    }

    function createPlayer() {
        window.NEXO_APP_SERVER_URL = APP_SERVER_URL;
        player = new window.TvVideoPlayer({
            video: elements.video,
            titleElement: elements.videoTitle,
            loadingElement: elements.loading,
            errorElement: elements.error,
            playPauseButton: elements.playPauseButton,
            progressBar: elements.progressBar,
            currentTimeElement: elements.currentTime,
            durationElement: elements.duration,
            serverUrl: APP_SERVER_URL,
            onTracksChanged: refreshPlayerOptions
        });

        if (elements.livePreviewVideo) {
            livePreviewPlayer = new window.TvVideoPlayer({
                video: elements.livePreviewVideo,
                titleElement: elements.livePreviewTitle,
                loadingElement: elements.livePreviewLoading,
                errorElement: elements.livePreviewError,
                playPauseButton: elements.livePreviewPlayButton,
                progressBar: elements.livePreviewProgress,
                currentTimeElement: elements.livePreviewCurrentTime,
                durationElement: elements.livePreviewDuration,
                serverUrl: APP_SERVER_URL
            });
        }
    }

    function loadAppSettings() {
        try {
            state.language = window.localStorage.getItem("tizenPlayerLanguage") || "ar";
            state.playerMode = window.localStorage.getItem("tizenPlayerMode") || "auto";
            state.librarySortMode = window.localStorage.getItem("nexoLibrarySortMode") || "az";
        } catch (error) {
            state.language = "ar";
            state.playerMode = "auto";
            state.librarySortMode = "az";
        }

        player.setPlaybackMode(state.playerMode);
        if (livePreviewPlayer) {
            livePreviewPlayer.setPlaybackMode(state.playerMode);
        }
        if (elements.appLanguageSelect) {
            elements.appLanguageSelect.value = state.language;
        }
        if (elements.playerModeSelect) {
            elements.playerModeSelect.value = state.playerMode;
        }
        if (elements.librarySortSelect) {
            elements.librarySortSelect.value = state.librarySortMode || "az";
        }
        applyLanguage();
        applyScreenMode();
        applySubtitleStyle();
    }

    function saveSettings(event) {
        event.preventDefault();
        state.language = elements.appLanguageSelect.value;
        state.playerMode = elements.playerModeSelect.value;

        try {
            window.localStorage.setItem("tizenPlayerLanguage", state.language);
            window.localStorage.setItem("tizenPlayerMode", state.playerMode);
        } catch (error) {
            console.log("Could not save settings:", error.message);
        }

        player.setPlaybackMode(state.playerMode);
        if (livePreviewPlayer) {
            livePreviewPlayer.setPlaybackMode(state.playerMode);
        }
        applyLanguage();
        elements.settingsMessage.hidden = false;
        elements.settingsMessage.textContent = "ØªÙ… Ø­ÙØ¸ Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª";
        renderHome();
    }

    function handleSettingsClick(event) {
        var panelButton = event.target.closest("[data-settings-panel]");
        var languageButton = event.target.closest("[data-language]");
        var playerButton = event.target.closest("[data-player-mode]");

        if (panelButton && panelButton.classList.contains("settings-nav-button")) {
            setSettingsPanel(panelButton.getAttribute("data-settings-panel"));
            return;
        }

        if (languageButton) {
            elements.appLanguageSelect.value = languageButton.getAttribute("data-language");
            syncSettingsButtons();
            return;
        }

        if (playerButton) {
            elements.playerModeSelect.value = playerButton.getAttribute("data-player-mode");
            syncSettingsButtons();
        }
    }

    function setSettingsPanel(panelName) {
        Array.prototype.slice.call(elements.settingsForm.querySelectorAll(".settings-nav-button")).forEach(function (button) {
            button.classList.toggle("is-active", button.getAttribute("data-settings-panel") === panelName);
        });
        Array.prototype.slice.call(elements.settingsForm.querySelectorAll(".settings-panel")).forEach(function (panel) {
            panel.classList.toggle("is-active", panel.getAttribute("data-settings-panel") === panelName);
        });
    }

    function syncSettingsButtons() {
        Array.prototype.slice.call(elements.settingsForm.querySelectorAll("[data-language]")).forEach(function (button) {
            button.classList.toggle("is-selected", button.getAttribute("data-language") === elements.appLanguageSelect.value);
        });
        Array.prototype.slice.call(elements.settingsForm.querySelectorAll("[data-player-mode]")).forEach(function (button) {
            button.classList.toggle("is-selected", button.getAttribute("data-player-mode") === elements.playerModeSelect.value);
        });
    }

    function applyLanguage() {
        var labels = getLanguageLabels(state.language);
        var actionButtons = elements.homeActionList ? elements.homeActionList.querySelectorAll(".home-action-button") : [];

        SECTIONS[0].title = labels.live;
        SECTIONS[0].subtitle = "Live TV";
        SECTIONS[1].title = labels.movies;
        SECTIONS[1].subtitle = "Movies";
        SECTIONS[2].title = labels.series;
        SECTIONS[2].subtitle = "Series";

        if (actionButtons[0]) {
            actionButtons[0].textContent = labels.account;
        }
        if (actionButtons[1]) {
            actionButtons[1].textContent = labels.settings;
        }
        if (actionButtons[2]) {
            actionButtons[2].textContent = labels.historyFavorites;
        }
        if (actionButtons[3]) {
            actionButtons[3].textContent = labels.playlists;
        }
    }

    function getLanguageLabels(language) {
        var labels = {
            ar: { live: "Ù…Ø¨Ø§Ø´Ø±", movies: "Ø£ÙÙ„Ø§Ù…", series: "Ù…Ø³Ù„Ø³Ù„Ø§Øª", account: "Ø­Ø³Ø§Ø¨ÙŠ", settings: "Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª", playlists: "Ù‚ÙˆØ§Ø¦Ù… Ø§Ù„ØªØ´ØºÙŠÙ„" },
            en: { live: "Live TV", movies: "Movies", series: "Series", account: "My Account", settings: "Settings", playlists: "List Playlists" },
            tr: { live: "Canli TV", movies: "Filmler", series: "Diziler", account: "Hesabim", settings: "Ayarlar", playlists: "Listeler" },
            de: { live: "Live-TV", movies: "Filme", series: "Serien", account: "Mein Konto", settings: "Einstellungen", playlists: "Playlists" },
            fr: { live: "TV en direct", movies: "Films", series: "Series", account: "Mon compte", settings: "Parametres", playlists: "Listes" },
            ru: { live: "Live TV", movies: "Filmy", series: "Serialy", account: "Akkount", settings: "Nastroiki", playlists: "Pleilisty" },
            zh: { live: "Live TV", movies: "Movies", series: "Series", account: "Account", settings: "Settings", playlists: "Playlists" },
            ja: { live: "Live TV", movies: "Movies", series: "Series", account: "Account", settings: "Settings", playlists: "Playlists" },
            es: { live: "TV en vivo", movies: "Peliculas", series: "Series", account: "Mi cuenta", settings: "Ajustes", playlists: "Listas" },
            it: { live: "TV live", movies: "Film", series: "Serie", account: "Account", settings: "Impostazioni", playlists: "Playlist" },
            hi: { live: "Live TV", movies: "Movies", series: "Series", account: "Account", settings: "Settings", playlists: "Playlists" },
            "pt-BR": { live: "TV ao vivo", movies: "Filmes", series: "Series", account: "Minha conta", settings: "Configuracoes", playlists: "Playlists" }
        };

        return labels[language] || labels.ar;
    }

    function applyLanguage() {
        var labels = getLanguageLabels(state.language);
        var actionButtons = elements.homeActionList ? elements.homeActionList.querySelectorAll(".home-action-button") : [];

        SECTIONS[0].title = labels.live;
        SECTIONS[0].subtitle = "Live TV";
        SECTIONS[1].title = labels.movies;
        SECTIONS[1].subtitle = "Movies";
        SECTIONS[2].title = labels.series;
        SECTIONS[2].subtitle = "Series";

        if (actionButtons[0]) {
            actionButtons[0].textContent = labels.account;
        }
        if (actionButtons[1]) {
            actionButtons[1].textContent = labels.settings;
        }
        if (actionButtons[2]) {
            actionButtons[2].textContent = labels.historyFavorites;
        }
        if (actionButtons[3]) {
            actionButtons[3].textContent = labels.playlists;
        }

        applyStaticLanguage(labels);
    }

    function getLanguageLabels(language) {
        var ar = {
            live: "\u0645\u0628\u0627\u0634\u0631",
            movies: "\u0623\u0641\u0644\u0627\u0645",
            series: "\u0645\u0633\u0644\u0633\u0644\u0627\u062a",
            account: "\u062d\u0633\u0627\u0628\u064a",
            settings: "\u0627\u0644\u0625\u0639\u062f\u0627\u062f\u0627\u062a",
            playlists: "\u0642\u0648\u0627\u0626\u0645 \u0627\u0644\u062a\u0634\u063a\u064a\u0644",
            historyFavorites: "\u0622\u062e\u0631 \u0627\u0644\u0645\u0634\u0627\u0647\u062f\u0627\u062a \u0648\u0627\u0644\u0645\u0641\u0636\u0644\u0629",
            recentlyWatched: "\u0622\u062e\u0631 \u0627\u0644\u0645\u0634\u0627\u0647\u062f\u0627\u062a",
            favorites: "\u0627\u0644\u0645\u0641\u0636\u0644\u0629",
            noRecent: "\u0644\u0627 \u062a\u0648\u062c\u062f \u0645\u0634\u0627\u0647\u062f\u0627\u062a \u062d\u062f\u064a\u062b\u0629.",
            noFavorites: "\u0644\u0627 \u062a\u0648\u062c\u062f \u0639\u0646\u0627\u0635\u0631 \u0645\u0641\u0636\u0644\u0629.",
            back: "\u0631\u062c\u0648\u0639",
            latest: "\u0622\u062e\u0631 \u0627\u0644\u0623\u0641\u0644\u0627\u0645 \u0648\u0627\u0644\u0645\u0633\u0644\u0633\u0644\u0627\u062a \u0627\u0644\u0645\u0636\u0627\u0641\u0629",
            categories: "\u0627\u0644\u062a\u0635\u0646\u064a\u0641\u0627\u062a",
            search: "\u0628\u062d\u062b",
            all: "\u0627\u0644\u0643\u0644",
            items: "\u0639\u0646\u0627\u0635\u0631",
            unavailable: "\u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631",
            unknown: "\u063a\u064a\u0631 \u0645\u0639\u0631\u0648\u0641",
            status: "\u0627\u0644\u062d\u0627\u0644\u0629",
            start: "\u0627\u0644\u0628\u062f\u0627\u064a\u0629",
            end: "\u0627\u0644\u0646\u0647\u0627\u064a\u0629",
            siteUrl: "\u0631\u0627\u0628\u0637 \u0627\u0644\u0645\u0648\u0642\u0639",
            playlistType: "\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u062a\u0634\u063a\u064a\u0644",
            notConfigured: "\u063a\u064a\u0631 \u0645\u0636\u0628\u0648\u0637",
            notAdded: "\u063a\u064a\u0631 \u0645\u0636\u0627\u0641\u0629",
            openDashboard: "\u0641\u062a\u062d \u0644\u0648\u062d\u0629 \u0627\u0644\u0645\u0648\u0642\u0639",
            playlistNameLabel: "\u0627\u0633\u0645 \u0627\u0644\u0642\u0627\u0626\u0645\u0629",
            addPlaylist: "+ \u0623\u0636\u0641 \u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u062a\u0634\u063a\u064a\u0644",
            activePlaylist: "\u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0646\u0634\u0637\u0629",
            playlistActiveBadge: "\u0646\u0634\u0637",
            playlistChannels: "\u0627\u0644\u0642\u0646\u0648\u0627\u062a",
            playlistMovies: "\u0623\u0641\u0644\u0627\u0645",
            playlistSeries: "\u0645\u0633\u0644\u0633\u0644",
            playlistUpdated: "\u0622\u062e\u0631 \u062a\u062d\u062f\u064a\u062b",
            playlistAdded: "\u0625\u0636\u0627\u0641\u0629",
            playlistSwitching: "\u062c\u0627\u0631\u064a \u062a\u0628\u062f\u064a\u0644 \u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u062a\u0634\u063a\u064a\u0644...",
            playlistTypeLabel: "\u0646\u0648\u0639 \u0627\u0644\u0642\u0627\u0626\u0645\u0629",
            currentPlaylistSaved: "\u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u062d\u0627\u0644\u064a\u0629 \u0645\u062d\u0641\u0648\u0638\u0629 \u0641\u064a \u0627\u0644\u0645\u0648\u0642\u0639",
            noSavedPlaylist: "\u0644\u0627 \u062a\u0648\u062c\u062f \u0642\u0627\u0626\u0645\u0629 \u0645\u062d\u0641\u0648\u0638\u0629 \u0644\u0647\u0630\u0627 \u0627\u0644\u062c\u0647\u0627\u0632 \u062d\u062a\u0649 \u0627\u0644\u0622\u0646.",
            saveOnSite: "\u062d\u0641\u0638 \u0627\u0644\u0642\u0627\u0626\u0645\u0629",
            deleteFromSite: "\u062d\u0630\u0641 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0646\u0634\u0637\u0629",
            enterM3u: "\u0623\u062f\u062e\u0644 \u0631\u0627\u0628\u0637 M3U.",
            enterXtream: "\u0623\u062f\u062e\u0644 \u0631\u0627\u0628\u0637 \u0627\u0644\u0633\u064a\u0631\u0641\u0631 \u0648\u0627\u0633\u0645 \u0627\u0644\u0645\u0633\u062a\u062e\u062f\u0645 \u0648\u0643\u0644\u0645\u0629 \u0627\u0644\u0645\u0631\u0648\u0631.",
            savingPlaylist: "\u062c\u0627\u0631\u064a \u0627\u0644\u062d\u0641\u0638 \u0641\u064a \u0627\u0644\u0645\u0648\u0642\u0639...",
            playlistSaved: "\u062a\u0645 \u062d\u0641\u0638 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0641\u064a \u0627\u0644\u0645\u0648\u0642\u0639 \u0644\u0647\u0630\u0627 \u0627\u0644\u062c\u0647\u0627\u0632.",
            playlistSaveFailed: "\u062a\u0639\u0630\u0631 \u062d\u0641\u0638 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0641\u064a \u0627\u0644\u0645\u0648\u0642\u0639.",
            deletingPlaylist: "\u062c\u0627\u0631\u064a \u062d\u0630\u0641 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0645\u0646 \u0627\u0644\u0645\u0648\u0642\u0639...",
            playlistDeleted: "\u062a\u0645 \u062d\u0630\u0641 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0645\u0646 \u0627\u0644\u0645\u0648\u0642\u0639.",
            playlistDeleteFailed: "\u062a\u0639\u0630\u0631 \u062d\u0630\u0641 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0645\u0646 \u0627\u0644\u0645\u0648\u0642\u0639.",
            serverRequired: "\u064a\u062c\u0628 \u0636\u0628\u0637 \u0631\u0627\u0628\u0637 \u0627\u0644\u0645\u0648\u0642\u0639 \u062f\u0627\u062e\u0644 \u0627\u0644\u062a\u0637\u0628\u064a\u0642 \u0623\u0648\u0644\u0627.",
            play: "\u062a\u0634\u063a\u064a\u0644",
            pause: "\u0625\u064a\u0642\u0627\u0641",
            trailer: "\u0645\u0634\u0627\u0647\u062f\u0629 \u0627\u0644\u0645\u0642\u0637\u0639 \u0627\u0644\u0625\u0639\u0644\u0627\u0646\u064a",
            removeWatchlist: "\u0625\u0632\u0627\u0644\u0629 \u0645\u0646 \u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0645\u0634\u0627\u0647\u062f\u0629",
            story: "\u0642\u0635\u0629 \u0627\u0644\u0641\u064a\u0644\u0645",
            seasons: "\u0645\u0648\u0627\u0633\u0645",
            season: "\u0645\u0648\u0633\u0645",
            cast: "\u0627\u0644\u0646\u062c\u0648\u0645",
            actorWorks: "\u0623\u0639\u0645\u0627\u0644 \u0627\u0644\u0645\u0645\u062b\u0644",
            actorWorksOf: "\u0623\u0639\u0645\u0627\u0644 ",
            noActorWorks: "\u0644\u0627 \u062a\u0648\u062c\u062f \u0623\u0639\u0645\u0627\u0644 \u0645\u0631\u062a\u0628\u0637\u0629 \u0628\u0647\u0630\u0627 \u0627\u0644\u0645\u0645\u062b\u0644 \u0641\u064a \u0627\u0644\u0645\u0643\u062a\u0628\u0629.",
            loadingActorWorks: "\u062c\u0627\u0631\u064a \u0627\u0644\u0628\u062d\u062b \u0639\u0646 \u0623\u0639\u0645\u0627\u0644 ",
            audio: "\u0627\u0644\u0635\u0648\u062a",
            subtitle: "\u0627\u0644\u062a\u0631\u062c\u0645\u0629",
            subtitleStyle: "\u0634\u0643\u0644 \u0627\u0644\u062a\u0631\u062c\u0645\u0629",
            screen: "\u0627\u0644\u0634\u0627\u0634\u0629",
            defaultAudio: "\u0627\u0644\u0635\u0648\u062a \u0627\u0644\u0627\u0641\u062a\u0631\u0627\u0636\u064a",
            noSubtitle: "\u0628\u062f\u0648\u0646 \u062a\u0631\u062c\u0645\u0629",
            saved: "\u062a\u0645 \u062d\u0641\u0638 \u0627\u0644\u0625\u0639\u062f\u0627\u062f\u0627\u062a.",
            saveSettings: "\u062d\u0641\u0638 \u0627\u0644\u0625\u0639\u062f\u0627\u062f\u0627\u062a",
            accountTitle: "\u0644\u0648\u062d\u0629 \u062a\u062d\u0643\u0645 \u0627\u0644\u0639\u0645\u064a\u0644",
            playlistTitle: "\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u062a\u0634\u063a\u064a\u0644 \u0627\u0644\u062e\u0627\u0635\u0629 \u0628\u064a",
            settingsLanguages: "\u0627\u0644\u0644\u063a\u0627\u062a",
            settingsPlayer: "\u0627\u0644\u0645\u0634\u063a\u0644",
            settingsSubtitles: "\u0627\u0644\u062a\u0631\u062c\u0645\u0629",
            subtitleHelp: "\u062a\u0638\u0647\u0631 \u062e\u064a\u0627\u0631\u0627\u062a \u0627\u0644\u062a\u0631\u062c\u0645\u0629 \u0648\u0627\u0644\u0634\u0643\u0644 \u062f\u0627\u062e\u0644 \u0645\u0634\u063a\u0644 \u0627\u0644\u0641\u064a\u062f\u064a\u0648."
            ,
            subtitleTitle: "\u0627\u0644\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0641\u0631\u0639\u064a",
            subtitleSettingsTitle: "\u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0627\u0644\u062a\u0631\u062c\u0645\u0629",
            screenSettingsTitle: "\u0646\u0633\u0628\u0629 \u0627\u0644\u0639\u0631\u0636 \u0625\u0644\u0649 \u0627\u0644\u0627\u0631\u062a\u0641\u0627\u0639",
            subtitleSample: "\u0633\u064a\u0628\u062f\u0648 \u062a\u0631\u062c\u0645\u0627\u062a\u0643 \u0645\u062b\u0644 \u0647\u0630\u0627",
            small: "\u0635\u063a\u064a\u0631",
            normal: "\u0639\u0627\u062f\u064a",
            large: "\u0643\u0628\u064a\u0631",
            shadow: "\u0625\u0633\u0642\u0627\u0637 \u0627\u0644\u0638\u0644",
            boxed: "\u062e\u0644\u0641\u064a\u0629 \u062f\u0627\u0643\u0646\u0629",
            noBox: "\u0628\u062f\u0648\u0646 \u062e\u0644\u0641\u064a\u0629",
            delay: "\u062a\u0623\u062e\u064a\u0631",
            fit: "\u0627\u0641\u062a\u0631\u0627\u0636\u064a",
            fullscreen: "\u0645\u0644\u0621 \u0627\u0644\u0634\u0627\u0634\u0629"
        };
        var en = {
            live: "Live TV",
            movies: "Movies",
            series: "Series",
            account: "My Account",
            settings: "Settings",
            playlists: "List Playlists",
            historyFavorites: "Recent & Favorites",
            recentlyWatched: "Recently watched",
            favorites: "Favorites",
            noRecent: "No recently watched items.",
            noFavorites: "No favorite items.",
            back: "Back",
            latest: "Latest movies and series",
            categories: "Categories",
            search: "Search",
            all: "All",
            items: "items",
            unavailable: "Unavailable",
            unknown: "unknown",
            status: "Status",
            start: "Start",
            end: "End",
            siteUrl: "Site URL",
            playlistType: "Playlist",
            notConfigured: "Not configured",
            notAdded: "Not added",
            openDashboard: "Open dashboard",
            playlistNameLabel: "Playlist name",
            addPlaylist: "+ Add playlist",
            activePlaylist: "Active playlist",
            playlistActiveBadge: "active",
            playlistChannels: "channels",
            playlistMovies: "movies",
            playlistSeries: "series",
            playlistUpdated: "Updated",
            playlistAdded: "Added",
            playlistSwitching: "Switching playlist...",
            playlistTypeLabel: "Playlist type",
            currentPlaylistSaved: "Current playlist saved on the site",
            noSavedPlaylist: "No saved playlist exists for this device yet.",
            saveOnSite: "Save playlist",
            deleteFromSite: "Delete active playlist",
            enterM3u: "Enter an M3U URL.",
            enterXtream: "Enter server URL, username, and password.",
            savingPlaylist: "Saving playlist on site...",
            playlistSaved: "Playlist saved on site for this device.",
            playlistSaveFailed: "Could not save playlist on site.",
            deletingPlaylist: "Deleting playlist from site...",
            playlistDeleted: "Playlist deleted from site.",
            playlistDeleteFailed: "Could not delete playlist from site.",
            serverRequired: "Configure the site URL inside the app first.",
            play: "Play",
            pause: "Pause",
            trailer: "Watch trailer",
            removeWatchlist: "Remove from watchlist",
            story: "Story",
            seasons: "Seasons",
            season: "Season",
            cast: "Cast",
            actorWorks: "Actor works",
            actorWorksOf: "Works of ",
            noActorWorks: "No matching movies or series were found for this actor in your library.",
            loadingActorWorks: "Searching works for ",
            audio: "Audio",
            subtitle: "Subtitles",
            subtitleStyle: "Subtitle style",
            screen: "Screen",
            defaultAudio: "Default audio",
            noSubtitle: "No subtitles",
            saved: "Settings saved.",
            saveSettings: "Save settings",
            accountTitle: "Customer dashboard",
            playlistTitle: "My playlists",
            settingsLanguages: "Languages",
            settingsPlayer: "Player",
            settingsSubtitles: "Subtitles",
            subtitleHelp: "Subtitle track and style controls appear inside the video player."
            ,
            subtitleTitle: "Subtitles",
            subtitleSettingsTitle: "Subtitle settings",
            screenSettingsTitle: "Aspect ratio",
            subtitleSample: "Your subtitles will look like this",
            small: "Small",
            normal: "Normal",
            large: "Large",
            shadow: "Drop shadow",
            boxed: "Dark box",
            noBox: "No box",
            delay: "Delay",
            fit: "Fit",
            fullscreen: "Full screen"
        };
        var labels = {
            ar: ar,
            en: en,
            tr: { live: "Canli TV", movies: "Filmler", series: "Diziler", account: "Hesabim", settings: "Ayarlar", playlists: "Listeler" },
            de: { live: "Live-TV", movies: "Filme", series: "Serien", account: "Mein Konto", settings: "Einstellungen", playlists: "Playlists" },
            fr: { live: "TV en direct", movies: "Films", series: "Series", account: "Mon compte", settings: "Parametres", playlists: "Listes" },
            ru: { live: "Live TV", movies: "Filmy", series: "Serialy", account: "Akkount", settings: "Nastroiki", playlists: "Pleilisty" },
            zh: { live: "Live TV", movies: "Movies", series: "Series", account: "Account", settings: "Settings", playlists: "Playlists" },
            ja: { live: "Live TV", movies: "Movies", series: "Series", account: "Account", settings: "Settings", playlists: "Playlists" },
            es: { live: "TV en vivo", movies: "Peliculas", series: "Series", account: "Mi cuenta", settings: "Ajustes", playlists: "Listas" },
            it: { live: "TV live", movies: "Film", series: "Serie", account: "Account", settings: "Impostazioni", playlists: "Playlist" },
            hi: { live: "Live TV", movies: "Movies", series: "Series", account: "Account", settings: "Settings", playlists: "Playlists" },
            "pt-BR": { live: "TV ao vivo", movies: "Filmes", series: "Series", account: "Minha conta", settings: "Configuracoes", playlists: "Playlists" }
        };

        return Object.assign({}, en, labels[language] || ar);
    }

    function applyStaticLanguage(labels) {
        setText(elements.playlistBackButton, labels.back);
        setText(elements.accountBackButton, labels.back);
        setText(elements.settingsBackButton, labels.back);
        setText(elements.libraryBackButton, labels.back);
        setText(document.querySelector("#playlistScreen .playlist-header .eyebrow"), labels.playlists);
        setText(document.querySelector("#accountScreen .playlist-header .eyebrow"), labels.account);
        setText(document.querySelector("#playlistScreen h1"), labels.playlistTitle);
        setText(document.querySelector("#accountScreen h1"), labels.accountTitle);
        setText(document.querySelector("#settingsScreen h1"), labels.settings);
        setText(document.querySelector("#settingsSaveButton"), labels.saveSettings);
        setText(elements.accountDashboardButton, labels.openDashboard);
        setText(elements.historyBackButton, labels.back);
        setText(elements.historyHeading, labels.historyFavorites);
        setText(elements.recentHeading, labels.recentlyWatched);
        setText(elements.favoriteHeading, labels.favorites);
        updatePlaylistLanguage(labels);
        setText(document.querySelector(".latest-header h2"), labels.latest);
        setText(elements.libraryHeading, labels.categories);
        if (elements.categoryHeading && normalizeSearchText(elements.categoryHeading.textContent) === normalizeSearchText(labels.all === "All" ? "\u0627\u0644\u0643\u0644" : "All")) {
            elements.categoryHeading.textContent = labels.all;
        }
        setText(document.querySelector(".library-search span"), labels.search);
        if (elements.librarySearchInput) {
            elements.librarySearchInput.placeholder = labels.search;
        }
        setText(elements.detailPlayButton, labels.play);
        setText(elements.detailTrailerButton, labels.trailer);
        setText(elements.detailWatchlistButton, labels.removeWatchlist);
        setText(document.querySelector(".detail-story h2"), labels.story);
        setText(document.querySelector(".season-section h2"), labels.seasons);
        setText(document.querySelector(".cast-section h2"), labels.cast);
        if (elements.actorWorksSection && elements.actorWorksSection.hidden) {
            setText(elements.actorWorksTitle, labels.actorWorks);
        }
        setSelectLabel(elements.audioLanguageSelect, labels.audio);
        setSelectLabel(elements.subtitleSelect, labels.subtitle);
        setSelectLabel(elements.subtitleStyleSelect, labels.subtitleStyle);
        setSelectLabel(elements.screenModeSelect, labels.screen);
        updateSettingsLanguage(labels);
        populateAudioTracks();
        populateSubtitleTracks();
    }

    function setText(element, text) {
        if (element) {
            element.textContent = text;
        }
    }

    function setSelectLabel(selectElement, text) {
        var label = selectElement && selectElement.closest("label");
        if (!label || !label.firstChild) {
            return;
        }
        label.firstChild.nodeValue = text;
    }

    function updateSettingsLanguage(labels) {
        var nav = elements.settingsForm ? elements.settingsForm.querySelectorAll(".settings-nav-button") : [];
        var panels = elements.settingsForm ? elements.settingsForm.querySelectorAll(".settings-panel") : [];
        if (nav[0]) { nav[0].textContent = labels.settingsLanguages; }
        if (nav[1]) { nav[1].textContent = labels.settingsPlayer; }
        if (nav[2]) { nav[2].textContent = labels.settingsSubtitles; }
        if (panels[0]) { setText(panels[0].querySelector("h2"), labels.settingsLanguages); }
        if (panels[1]) { setText(panels[1].querySelector("h2"), labels.settingsPlayer); }
        if (panels[2]) {
            setText(panels[2].querySelector("h2"), labels.settingsSubtitles);
            setText(panels[2].querySelector(".settings-note"), labels.subtitleHelp);
        }
    }

    function updatePlaylistLanguage(labels) {
        var formLabels = elements.playlistForm ? elements.playlistForm.querySelectorAll("label > span") : [];
        setText(elements.playlistAddButton, labels.addPlaylist);
        setText(formLabels[0], labels.playlistNameLabel);
        setText(formLabels[1], labels.playlistTypeLabel);
        setText(formLabels[2], "M3U URL");
        setText(formLabels[3], "Server URL");
        setText(formLabels[4], "Username");
        setText(formLabels[5], "Password");
        setText(elements.playlistSaveButton, labels.saveOnSite);
        setText(elements.playlistDeleteButton, labels.deleteFromSite);
    }

    function saveSettings(event) {
        event.preventDefault();
        state.language = elements.appLanguageSelect.value;
        state.playerMode = elements.playerModeSelect.value;

        try {
            window.localStorage.setItem("tizenPlayerLanguage", state.language);
            window.localStorage.setItem("tizenPlayerMode", state.playerMode);
        } catch (error) {
            console.log("Could not save settings:", error.message);
        }

        player.setPlaybackMode(state.playerMode);
        if (livePreviewPlayer) {
            livePreviewPlayer.setPlaybackMode(state.playerMode);
        }
        applyLanguage();
        syncSettingsButtons();
        elements.settingsMessage.hidden = false;
        elements.settingsMessage.textContent = getLanguageLabels(state.language).saved;
        renderHome();
    }

    function bindDomEvents() {
        document.addEventListener("keydown", handleKeyDown);

        elements.sectionList.addEventListener("click", function (event) {
            var card = event.target.closest(".section-card");
            if (card) {
                openLibrary(card.getAttribute("data-section"));
            }
        });

        elements.homeActionList.addEventListener("click", function (event) {
            var action = event.target.closest(".home-action-button");
            if (action) {
                runHomeAction(action.getAttribute("data-action"));
            }
        });

        elements.latestList.addEventListener("click", function (event) {
            var item = event.target.closest(".latest-card");
            if (item) {
                openLatestItem(parseInt(item.getAttribute("data-index"), 10));
            }
        });

        elements.powerButton.addEventListener("click", exitApp);
        elements.playlistBackButton.addEventListener("click", goHome);
        elements.playlistAddButton.addEventListener("click", openAddPlaylistForm);
        elements.playlistList.addEventListener("click", handlePlaylistListClick);
        elements.playlistType.addEventListener("change", renderPlaylistTypeFields);
        elements.playlistForm.addEventListener("submit", savePlaylistFromForm);
        elements.playlistDeleteButton.addEventListener("click", deletePlaylistFromServer);
        elements.accountBackButton.addEventListener("click", goHome);
        elements.accountPayButton.addEventListener("click", openAccountAction);
        elements.accountDashboardButton.addEventListener("click", openCustomerDashboard);
        elements.historyBackButton.addEventListener("click", goHome);
        elements.recentList.addEventListener("click", handleHistoryItemClick);
        elements.favoriteList.addEventListener("click", handleHistoryItemClick);
        elements.settingsBackButton.addEventListener("click", goHome);
        elements.settingsForm.addEventListener("submit", saveSettings);
        elements.settingsForm.addEventListener("click", handleSettingsClick);
        elements.audioLanguageSelect.addEventListener("change", applyAudioTrackSelection);
        elements.subtitleSelect.addEventListener("change", applySubtitleSelection);
        elements.subtitleStyleSelect.addEventListener("change", applySubtitleStyle);
        elements.screenModeSelect.addEventListener("change", applyScreenMode);
        elements.playerToolButtons.addEventListener("click", function (event) {
            var button = event.target.closest(".player-tool-button");
            if (button) {
                openPlayerTool(button.getAttribute("data-tool"));
            }
        });
        elements.playerToolOptions.addEventListener("click", function (event) {
            var option = event.target.closest(".player-tool-option");
            if (option) {
                applyPlayerToolOption(option);
            }
        });

        elements.categoryList.addEventListener("click", function (event) {
            var category = event.target.closest(".category-row");
            if (category) {
                setCategory(parseInt(category.getAttribute("data-index"), 10));
                setLibraryPane("items");
            }
        });

        elements.librarySearchInput.addEventListener("input", function () {
            state.searchQuery = elements.librarySearchInput.value;
            state.categoryIndex = 0;
            state.itemIndex = 0;
            state.vodRenderLimit = VOD_RENDER_LIMIT;
            state.liveRenderLimit = LIVE_RENDER_LIMIT;
            renderLibrary();
        });

        elements.librarySearchInput.addEventListener("click", function (event) {
            event.preventDefault();
            openVirtualKeyboard();
        });

        if (elements.virtualKeyboard) {
            elements.virtualKeyboard.addEventListener("click", function (event) {
                var button = event.target.closest(".virtual-key");
                if (button) {
                    state.virtualKeyboardIndex = parseInt(button.getAttribute("data-index"), 10) || 0;
                    applyVirtualKeyboardKey(button.getAttribute("data-key-type"), button.getAttribute("data-value") || "");
                }
            });
        }

        elements.itemList.addEventListener("click", function (event) {
            var item = event.target.closest(".item-row, .poster-card");
            if (item) {
                openItem(parseInt(item.getAttribute("data-index"), 10));
            }
        });

        if (elements.liveFavoriteButton) {
            elements.liveFavoriteButton.addEventListener("click", toggleCurrentLiveFavorite);
        }
        if (elements.liveLockButton) {
            elements.liveLockButton.addEventListener("click", toggleCurrentLiveLock);
        }
        if (elements.liveReloadButton) {
            elements.liveReloadButton.addEventListener("click", reloadCurrentLiveChannel);
        }
        if (elements.librarySortSelect) {
            elements.librarySortSelect.addEventListener("change", function () {
                applyLibrarySortMode(elements.librarySortSelect.value || "az");
            });
        }
        if (elements.pinKeyboard) {
            elements.pinKeyboard.addEventListener("click", function (event) {
                var button = event.target.closest(".pin-key");

                if (!button) {
                    return;
                }

                state.pinDialogIndex = parseInt(button.getAttribute("data-index"), 10) || 0;
                applyPinKeyboardKey(button.getAttribute("data-pin-key-type"), button.getAttribute("data-value") || "");
            });
        }

        elements.itemList.addEventListener("mouseover", function (event) {
            var item = event.target.closest(".item-row");
            var index;

            if (!item || state.activeSection !== "live") {
                return;
            }

            index = parseInt(item.getAttribute("data-index"), 10);
            if (!isFinite(index)) {
                return;
            }

            state.itemIndex = Math.max(0, Math.min(renderedItems.length - 1, index));
            state.libraryPane = "items";
            syncLibraryFocus(false);
        });

        elements.libraryBackButton.addEventListener("click", goHome);
        elements.detailBackButton.addEventListener("click", goBackFromDetail);
        elements.detailPlayButton.addEventListener("click", playDetailItem);
        elements.detailTrailerButton.addEventListener("click", playTrailer);
        elements.detailFavoriteButton.addEventListener("click", addDetailFavorite);
        elements.detailWatchlistButton.addEventListener("click", removeFromWatchlist);
        elements.seasonList.addEventListener("click", function (event) {
            var season = event.target.closest(".season-button");
            if (season) {
                setDetailSeason(parseInt(season.getAttribute("data-index"), 10));
            }
        });
        elements.episodeList.addEventListener("click", function (event) {
            var episode = event.target.closest(".episode-card");
            if (episode) {
                playEpisode(parseInt(episode.getAttribute("data-index"), 10));
            }
        });
        elements.castList.addEventListener("click", function (event) {
            var cast = event.target.closest(".cast-card");
            if (cast) {
                openActorWorks(parseInt(cast.getAttribute("data-index"), 10));
            }
        });
        elements.actorWorksList.addEventListener("click", function (event) {
            var work = event.target.closest(".actor-work-card");
            if (work) {
                openActorWork(parseInt(work.getAttribute("data-index"), 10));
            }
        });
        elements.playPauseButton.addEventListener("click", function () {
            player.togglePlay();
            showOverlay();
        });
        elements.backButton.addEventListener("click", goBackFromPlayer);
        elements.video.addEventListener("timeupdate", savePlaybackProgress);
        elements.video.addEventListener("pause", savePlaybackProgress);
        elements.resumeContinueButton.addEventListener("click", function () {
            runPendingResume(true);
        });
        elements.resumeRestartButton.addEventListener("click", function () {
            runPendingResume(false);
        });
    }

    function renderHome() {
        elements.sectionList.innerHTML = "";

        SECTIONS.forEach(function (section, index) {
            var count = getItemsBySection(section.id).length;
            var card = document.createElement("button");
            card.className = "section-card";
            card.type = "button";
            card.setAttribute("data-section", section.id);
            card.setAttribute("data-index", index);
            card.innerHTML =
                '<span class="section-icon">' + getSectionIconHtml(section.id) + '</span>' +
                '<span class="section-title">' + escapeHtml(section.title) + '</span>' +
                '<span class="section-subtitle">' + escapeHtml(section.subtitle) + '</span>' +
                '<span class="section-count">' + count + '</span>';
            elements.sectionList.appendChild(card);
        });

        renderLatestItems();
        renderActivationSummary();
        syncHomeFocus();
    }

    function getSectionIconHtml(sectionId) {
        if (sectionId === "live") {
            return '<svg viewBox="0 0 96 96" aria-hidden="true" focusable="false">' +
                '<path d="M30 18 43 32M66 18 53 32" />' +
                '<rect x="16" y="32" width="64" height="42" rx="8" />' +
                '<path d="M36 80h24M48 74v6" />' +
                '<path d="M28 44h40v18H28z" />' +
            '</svg>';
        }

        if (sectionId === "movies") {
            return '<svg viewBox="0 0 96 96" aria-hidden="true" focusable="false">' +
                '<rect x="18" y="34" width="56" height="34" rx="7" />' +
                '<path d="M74 45 86 38v26l-12-7" />' +
                '<circle cx="31" cy="26" r="9" />' +
                '<circle cx="53" cy="24" r="11" />' +
                '<path d="M30 51h24" />' +
            '</svg>';
        }

        return '<svg viewBox="0 0 96 96" aria-hidden="true" focusable="false">' +
            '<g transform="rotate(-7 48 48)">' +
                '<path d="M12 28h72a7 7 0 0 1 7 7v26a7 7 0 0 1-7 7H12a7 7 0 0 1-7-7V35a7 7 0 0 1 7-7z" />' +
                '<path d="M25 31v34M48 31v34M71 31v34" />' +
                '<path d="M31 39h12v18H31zM54 39h12v18H54z" />' +
                '<path d="M12 36h6M12 48h6M12 60h6M78 36h6M78 48h6M78 60h6" />' +
                '<path d="M27 34h14M50 34h14M27 62h14M50 62h14" />' +
            '</g>' +
        '</svg>';
    }

    function renderActivationSummary() {
        var data = state.activation || {};
        var access = data.access || {};
        var status = access.reason || "offline";
        var planEnd = data.active_until || data.trial_ends_at || "";

        if (!elements.activationSummary) {
            return;
        }

        elements.activationSummary.innerHTML =
            '<span><strong>MAC</strong> ' + escapeHtml(state.deviceMac || data.mac || "ØºÙŠØ± Ù…ØªÙˆÙØ±") + '</span>' +
            '<span><strong>Ø§Ù„Ø­Ø§Ù„Ø©</strong> ' + escapeHtml(status) + '</span>' +
            '<span><strong>Ø§Ù„Ø¨Ø¯Ø§ÙŠØ©</strong> ' + escapeHtml(formatServerDate(data.trial_started_at || "")) + '</span>' +
            '<span><strong>Ø§Ù„Ù†Ù‡Ø§ÙŠØ©</strong> ' + escapeHtml(formatServerDate(planEnd || "")) + '</span>';
    }

    function renderActivationSummary() {
        var data = state.activation || {};
        var access = data.access || {};
        var status = access.reason || "offline";
        var planEnd = data.active_until || data.trial_ends_at || "";
        var labels = getLanguageLabels(state.language);

        if (!elements.activationSummary) {
            return;
        }

        elements.activationSummary.innerHTML =
            '<span><strong>MAC</strong> ' + escapeHtml(state.deviceMac || data.mac || labels.unavailable) + '</span>' +
            '<span><strong>' + escapeHtml(labels.status) + '</strong> ' + escapeHtml(formatStatusLabel(status)) + '</span>' +
            '<span><strong>' + escapeHtml(labels.start) + '</strong> ' + escapeHtml(formatOptionalDate(data.trial_started_at, labels)) + '</span>' +
            '<span><strong>' + escapeHtml(labels.end) + '</strong> ' + escapeHtml(formatOptionalDate(planEnd, labels)) + '</span>';
    }

    function formatStatusLabel(status) {
        var value = String(status || "offline").toLowerCase();

        if (state.language !== "ar") {
            return status || "offline";
        }

        if (value === "trial") {
            return "\u062a\u062c\u0631\u064a\u0628\u064a";
        }
        if (value === "active" || value === "allowed") {
            return "\u0645\u0641\u0639\u0644";
        }
        if (value === "expired") {
            return "\u0645\u0646\u062a\u0647\u064a";
        }
        if (value === "offline") {
            return "\u063a\u064a\u0631 \u0645\u062a\u0635\u0644";
        }

        return status;
    }

    function formatOptionalDate(value, labels) {
        if (!value) {
            return labels.unknown;
        }

        return formatServerDate(value);
    }

    function renderLatestItems() {
        latestItems = getLatestVodItems(18);

        elements.latestCount.textContent = latestItems.length + " " + getLanguageLabels(state.language).items;
        elements.latestList.innerHTML = latestItems.map(function (item, index) {
            return '<button class="latest-card home-control" type="button" data-index="' + index + '">' +
                '<span class="latest-poster" style="' + getPosterStyle(item) + '">' + getPosterFallbackHtml(item) + '</span>' +
                '<span class="latest-title">' + escapeHtml(item.title) + '</span>' +
                '<span class="latest-meta">' + escapeHtml(getSection(item.section).subtitle) + '</span>' +
            '</button>';
        }).join("");

        if (state.latestIndex >= latestItems.length) {
            state.latestIndex = 0;
        }
    }

    function getLatestVodItems(limit) {
        var map = {};
        var latest = [];
        var vodItems = allItems.filter(function (item) {
            return item.section === "movies" || item.section === "series";
        });

        vodItems.forEach(function (item, index) {
            var key = getLibraryWorkKey(item);
            var current = map[key];

            item.libraryOrder = typeof item.libraryOrder === "number" ? item.libraryOrder : index;
            if (!current || isBetterLatestRepresentative(item, current)) {
                map[key] = item;
            }
        });

        latest = Object.keys(map).map(function (key) {
            return map[key];
        }).sort(function (a, b) {
            return getItemSortTime(b) - getItemSortTime(a);
        });

        return latest.slice(0, limit);
    }

    function isBetterLatestRepresentative(candidate, current) {
        if (candidate.section === "series" && current.section === "series") {
            if (!candidate.episodeNumber && current.episodeNumber) {
                return true;
            }

            if (candidate.episodeNumber && current.episodeNumber) {
                if ((candidate.seasonNumber || 1) !== (current.seasonNumber || 1)) {
                    return (candidate.seasonNumber || 1) < (current.seasonNumber || 1);
                }

                return candidate.episodeNumber < current.episodeNumber;
            }
        }

        return getItemSortTime(candidate) > getItemSortTime(current);
    }

    function getItemSortTime(item) {
        var value = parseInt(item.addedAt || item.libraryOrder || 0, 10);

        return isFinite(value) ? value : 0;
    }

    function updateHomeClock() {
        var now = new Date();
        var time = now.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
        var date = now.toLocaleDateString([], { month: "short", day: "numeric", year: "numeric" });

        elements.homeTime.textContent = time;
        elements.homeDate.textContent = date;
    }

    function renderLibrary() {
        var section = getSection(state.activeSection);
        var sectionItems = getLibraryItemsBySection(state.activeSection);
        var sortMode = getValidSortModeForSection(state.activeSection, state.librarySortMode);

        sectionItems = filterItemsBySearch(sectionItems, state.searchQuery);
        state.librarySortMode = sortMode;
        syncLibrarySortOptions();
        sectionItems = sortLibraryItems(sectionItems, sortMode);
        state.categories = buildCategories(sectionItems);
        if (state.categoryIndex >= state.categories.length) {
            state.categoryIndex = 0;
        }

        elements.libraryEyebrow.textContent = section.subtitle;
        elements.libraryHeading.textContent = section.title;
        if (elements.librarySearchInput && elements.librarySearchInput.value !== state.searchQuery) {
            elements.librarySearchInput.value = state.searchQuery;
        }
        elements.libraryScreen.classList.toggle("is-live-library", state.activeSection === "live");
        if (elements.liveTopMenu) {
            elements.liveTopMenu.hidden = true;
        }
        if (elements.libraryActionBar) {
            elements.libraryActionBar.hidden = false;
        }
        if (elements.librarySortSelect && elements.librarySortSelect.value !== state.librarySortMode) {
            elements.librarySortSelect.value = state.librarySortMode;
        }
        renderCategories();
        renderItems();
        setLibraryPane(state.libraryPane);
    }

    function applyLibrarySortMode(mode) {
        state.librarySortMode = getValidSortModeForSection(state.activeSection, mode);
        if (elements.librarySortSelect && elements.librarySortSelect.value !== state.librarySortMode) {
            elements.librarySortSelect.value = state.librarySortMode;
        }

        try {
            window.localStorage.setItem("nexoLibrarySortMode", state.librarySortMode);
        } catch (error) {
            console.log("Could not save sort mode:", error.message);
        }

        state.categoryIndex = 0;
        state.itemIndex = 0;
        state.vodRenderLimit = VOD_RENDER_LIMIT;
        state.liveRenderLimit = LIVE_RENDER_LIMIT;
        renderLibrary();
    }

    function cycleLibrarySortMode() {
        var modes = getLibrarySortOptions().map(function (option) {
            return option.value;
        });
        var index = modes.indexOf(state.librarySortMode || "az");

        applyLibrarySortMode(modes[(index + 1 + modes.length) % modes.length]);
        state.libraryPane = "actions";
        syncLibraryFocus(false);
    }

    function getLibrarySortOptions() {
        var isArabic = state.language === "ar";

        if (state.activeSection === "live") {
            return [
                { value: "number", label: isArabic ? "\u0631\u0642\u0645 \u0627\u0644\u0642\u0646\u0627\u0629" : "Channel #" },
                { value: "az", label: isArabic ? "\u0623 - \u064a" : "A - Z" },
                { value: "za", label: isArabic ? "\u064a - \u0623" : "Z - A" },
                { value: "recent", label: isArabic ? "\u0627\u0644\u0623\u062d\u062f\u062b" : "Newest" }
            ];
        }

        return [
            { value: "az", label: isArabic ? "\u0623 - \u064a" : "A - Z" },
            { value: "za", label: isArabic ? "\u064a - \u0623" : "Z - A" },
            { value: "rating", label: isArabic ? "\u0627\u0644\u062a\u0642\u064a\u064a\u0645" : "Rating" },
            { value: "year", label: isArabic ? "\u0627\u0644\u0633\u0646\u0629" : "Year" }
        ];
    }

    function getValidSortModeForSection(sectionId, mode) {
        var options = getLibrarySortOptions();
        var values = options.map(function (option) {
            return option.value;
        });

        if (sectionId !== state.activeSection) {
            if (sectionId === "live") {
                values = ["number", "az", "za", "recent"];
            } else {
                values = ["az", "za", "rating", "year"];
            }
        }

        return values.indexOf(mode) !== -1 ? mode : (sectionId === "live" ? "number" : "az");
    }

    function syncLibrarySortOptions() {
        var options;
        var optionSet;

        if (!elements.librarySortSelect) {
            return;
        }

        options = getLibrarySortOptions();
        optionSet = (state.activeSection === "live" ? "live" : "vod") + ":" + state.language;
        if (elements.librarySortSelect.getAttribute("data-sort-set") !== optionSet) {
            elements.librarySortSelect.innerHTML = options.map(function (option) {
                return '<option value="' + escapeHtml(option.value) + '">' + escapeHtml(option.label) + '</option>';
            }).join("");
            elements.librarySortSelect.setAttribute("data-sort-set", optionSet);
        }
        elements.librarySortSelect.value = state.librarySortMode;
    }

    function renderCategories() {
        elements.categoryList.innerHTML = "";

        state.categories.forEach(function (category, index) {
            var row = document.createElement("button");
            row.className = "category-row";
            row.type = "button";
            row.setAttribute("data-index", index);
            row.innerHTML =
                '<span>' + escapeHtml(category.name) + '</span>' +
                '<span class="row-count">' + category.items.length + '</span>';
            elements.categoryList.appendChild(row);
        });
    }

    function renderItems() {
        var labels = getLanguageLabels(state.language);
        var category = state.categories[state.categoryIndex];
        visibleItems = category ? category.items : [];
        if (state.itemIndex >= visibleItems.length) {
            state.itemIndex = 0;
        }
        if (isVodSection() && VOD_RENDER_LIMIT > 0) {
            renderedItems = visibleItems.slice(0, Math.max(VOD_RENDER_LIMIT, state.vodRenderLimit || VOD_RENDER_LIMIT, state.itemIndex + 1));
        } else if (state.activeSection === "live" && LIVE_RENDER_LIMIT > 0) {
            renderedItems = visibleItems.slice(0, Math.max(LIVE_RENDER_LIMIT, state.liveRenderLimit || LIVE_RENDER_LIMIT, state.itemIndex + 1));
        } else {
            renderedItems = visibleItems;
        }
        elements.itemList.innerHTML = "";
        elements.categoryHeading.textContent = category ? category.name : labels.all;
        elements.itemCount.textContent = renderedItems.length < visibleItems.length ?
            renderedItems.length + " / " + visibleItems.length + " " + labels.items :
            visibleItems.length + " " + labels.items;
        elements.itemList.classList.toggle("poster-grid", isVodSection());
        elements.itemList.classList.toggle("live-channel-list", state.activeSection === "live");
        state.itemIndex = Math.max(0, Math.min(renderedItems.length - 1, state.itemIndex));

        renderedItems.forEach(function (item, index) {
            var row = document.createElement("button");
            row.className = "item-row";
            row.type = "button";
            row.setAttribute("data-index", index);

            if (isVodSection()) {
                row.className = "poster-card";
                row.innerHTML =
                    '<span class="poster-art" style="' + getPosterStyle(item) + '">' + getPosterFallbackHtml(item) + '</span>' +
                    '<span class="poster-title">' + escapeHtml(item.title) + '</span>' +
                    '<span class="poster-meta">' + escapeHtml(item.group || getSection(item.section).subtitle) + '</span>';
            } else {
                row.className = "item-row live-channel-row";
                row.innerHTML =
                    '<span class="item-number">' + escapeHtml(getChannelNumber(item, index)) + '</span>' +
                    '<span class="live-channel-logo" style="' + getChannelLogoStyle(item) + '">' + getChannelLogoFallbackHtml(item) + '</span>' +
                    '<span class="item-main">' +
                        '<span class="item-title">' + escapeHtml(item.title) + '</span>' +
                        '<span class="item-meta">' + escapeHtml(item.group || item.description || item.type.toUpperCase()) + '</span>' +
                    '</span>' +
                    '<span class="item-type">' + escapeHtml(item.type.toUpperCase()) + '</span>';
            }

            elements.itemList.appendChild(row);
        });

        renderLiveInfoPanel();
        setLibraryPane(state.libraryPane);
    }

    function renderLiveInfoPanel() {
        var item = visibleItems[state.itemIndex] || visibleItems[0];

        if (!elements.liveInfoPanel) {
            return;
        }

        if (state.activeSection !== "live") {
            elements.liveInfoPanel.hidden = true;
            stopLivePreview();
            return;
        }

        elements.liveInfoPanel.hidden = false;

        if (!item) {
            updateLivePreviewDetails(null);
            updateLiveActionButtons(null);
            stopLivePreview();
            return;
        }

        updateLivePreviewDetails(item);
        updateLiveActionButtons(item);
        syncLiveOkTarget(item);
        if (isChannelLocked(item) && !isLiveChannelUnlocked(item)) {
            stopLivePreview();
            showLivePreviewMessage("القناة مقفلة. اضغط قفل وأدخل الرقم السري.");
            return;
        }
        if (livePreviewItemKey && livePreviewItemKey !== getLivePreviewKey(item)) {
            stopLivePreview();
            showLivePreviewMessage("");
        }
    }

    function updateLivePreviewDetails(item) {
        if (!elements.liveNowNumber || !elements.liveNowLogo || !elements.liveNowTitle) {
            return;
        }

        if (!item) {
            elements.liveNowNumber.textContent = "";
            elements.liveNowLogo.removeAttribute("style");
            elements.liveNowLogo.innerHTML = "";
            elements.liveNowTitle.textContent = "لا توجد قنوات في هذه الفئة";
            return;
        }

        elements.liveNowNumber.textContent = getChannelNumber(item, state.itemIndex);
        elements.liveNowLogo.setAttribute("style", getChannelLogoStyle(item));
        elements.liveNowLogo.innerHTML = getChannelLogoFallbackHtml(item);
        elements.liveNowTitle.textContent = item.title;
    }

    function updateLiveActionButtons(item) {
        if (elements.liveFavoriteButton) {
            elements.liveFavoriteButton.textContent = item && isItemFavorite(item) ? "في المفضلة" : "أضف للمفضلة";
            elements.liveFavoriteButton.disabled = !item;
        }
        if (elements.liveLockButton) {
            elements.liveLockButton.textContent = item && isChannelLocked(item) ? "فتح القفل" : "قفل";
            elements.liveLockButton.disabled = !item;
        }
        if (elements.liveReloadButton) {
            elements.liveReloadButton.disabled = !item;
        }
    }

    function syncLiveOkTarget(item) {
        var key = getLivePreviewKey(item);

        if (key !== state.liveOkItemKey) {
            state.liveOkItemKey = key;
            state.liveOkPressCount = 0;
        }
    }

    function getLivePreviewKey(item) {
        return item && item.url ? item.url : "";
    }

    function scheduleLivePreview(item) {
        var key = getLivePreviewKey(item);

        if (!livePreviewPlayer || !item || !key) {
            return;
        }

        if (livePreviewItemKey === key && livePreviewPlayer.currentItem) {
            return;
        }

        if (livePreviewTimer) {
            window.clearTimeout(livePreviewTimer);
            livePreviewTimer = null;
        }

        livePreviewTimer = window.setTimeout(function () {
            livePreviewTimer = null;
            startLivePreview(item);
        }, 650);
    }

    function startLivePreview(item, force) {
        var key = getLivePreviewKey(item);

        if (!livePreviewPlayer || !item || !key || state.screen !== "library" || state.activeSection !== "live") {
            return;
        }

        if (isChannelLocked(item) && !isLiveChannelUnlocked(item)) {
            stopLivePreview();
            showLivePreviewMessage("القناة مقفلة. أدخل الرقم السري للتشغيل.");
            return;
        }

        if (force) {
            livePreviewItemKey = "";
            livePreviewPlayer.stop();
        }

        livePreviewItemKey = key;
        livePreviewPlayer.setPlaybackMode(state.playerMode);
        livePreviewPlayer.load(item);
    }

    function stopLivePreview() {
        if (livePreviewTimer) {
            window.clearTimeout(livePreviewTimer);
            livePreviewTimer = null;
        }

        livePreviewItemKey = "";
        if (livePreviewPlayer) {
            livePreviewPlayer.stop();
        }
    }

    function showLivePreviewMessage(message) {
        if (elements.livePreviewLoading) {
            elements.livePreviewLoading.hidden = true;
        }
        if (elements.livePreviewError) {
            elements.livePreviewError.textContent = message || "";
            elements.livePreviewError.hidden = !message;
        }
    }

    function getCurrentLiveItem() {
        return state.activeSection === "live" ? (visibleItems[state.itemIndex] || visibleItems[0]) : null;
    }

    function getItemStorageKey(prefix, item) {
        if (!item) {
            return "";
        }

        return prefix + getLibraryWorkKey(item);
    }

    function isItemFavorite(item) {
        var key = getItemStorageKey("nexoWishlist:", item);

        if (!key) {
            return false;
        }

        try {
            return !!window.localStorage.getItem(key);
        } catch (error) {
            return false;
        }
    }

    function setItemFavorite(item, favorite) {
        var key = getItemStorageKey("nexoWishlist:", item);

        if (!key) {
            return;
        }

        try {
            if (favorite) {
                window.localStorage.setItem(key, JSON.stringify({
                    title: item.title,
                    section: item.section,
                    addedAt: Date.now()
                }));
            } else {
                window.localStorage.removeItem(key);
            }
        } catch (error) {
            console.log("Could not update favorite:", error.message);
        }
    }

    function toggleCurrentLiveFavorite() {
        var item = getCurrentLiveItem();

        if (!item) {
            return;
        }

        setItemFavorite(item, !isItemFavorite(item));
        updateLiveActionButtons(item);
        if (state.activeSection === "live") {
            state.liveRenderLimit = LIVE_RENDER_LIMIT;
            renderLibrary();
        }
    }

    function getChannelLockKey(item) {
        return getItemStorageKey("nexoChannelLock:", item);
    }

    function getChannelLock(item) {
        var key = getChannelLockKey(item);

        if (!key) {
            return null;
        }

        try {
            return JSON.parse(window.localStorage.getItem(key) || "null");
        } catch (error) {
            return null;
        }
    }

    function isChannelLocked(item) {
        return !!getChannelLock(item);
    }

    function isLiveChannelUnlocked(item) {
        var key = getChannelLockKey(item);

        return !!(key && liveUnlockedChannels[key]);
    }

    function getPinKeyboardRows() {
        return [
            ["1", "2", "3"],
            ["4", "5", "6"],
            ["7", "8", "9"],
            [
                { label: "حذف", type: "backspace" },
                { label: "0", type: "digit", value: "0" },
                { label: "تم", type: "done" }
            ]
        ];
    }

    function openPinDialog(title, callback) {
        if (!elements.pinDialog || !elements.pinKeyboard) {
            if (callback) {
                callback("");
            }
            return;
        }

        state.pinDialogOpen = true;
        state.pinDialogValue = "";
        state.pinDialogIndex = 0;
        state.pinDialogCallback = callback || null;
        elements.pinDialogTitle.textContent = title || "الرقم السري";
        setPinDialogMessage("");
        updatePinDialogValue();
        renderPinKeyboard();
        elements.pinDialog.hidden = false;
        focusPinKeyboardButton();
    }

    function closePinDialog(value) {
        var callback = state.pinDialogCallback;

        state.pinDialogOpen = false;
        state.pinDialogValue = "";
        state.pinDialogIndex = 0;
        state.pinDialogCallback = null;
        if (elements.pinDialog) {
            elements.pinDialog.hidden = true;
        }
        if (callback) {
            callback(value || "");
        }
    }

    function setPinDialogMessage(message) {
        if (!elements.pinDialogMessage) {
            return;
        }

        elements.pinDialogMessage.textContent = message || "";
        elements.pinDialogMessage.hidden = !message;
    }

    function updatePinDialogValue() {
        var length = (state.pinDialogValue || "").length;

        if (elements.pinDialogValue) {
            elements.pinDialogValue.textContent = length ? Array(length + 1).join("•") : "----";
        }
    }

    function renderPinKeyboard() {
        var index = 0;
        var html = getPinKeyboardRows().map(function (row, rowIndex) {
            var rowHtml = row.map(function (entry, colIndex) {
                var key = typeof entry === "string" ?
                    { label: entry, type: "digit", value: entry } :
                    entry;
                var className = "pin-key" + (index === state.pinDialogIndex ? " is-focused" : "") +
                    (key.type === "digit" ? " is-digit" : " is-action");
                var button = '<button class="' + className + '" type="button" data-index="' + index +
                    '" data-row="' + rowIndex + '" data-col="' + colIndex +
                    '" data-pin-key-type="' + escapeHtml(key.type || "digit") +
                    '" data-value="' + escapeHtml(key.value || "") + '">' +
                    escapeHtml(key.label || key.value || "") + '</button>';

                index += 1;
                return button;
            }).join("");

            return '<div class="pin-keyboard-row">' + rowHtml + '</div>';
        }).join("");

        elements.pinKeyboard.innerHTML = html;
    }

    function focusPinKeyboardButton() {
        var buttons = elements.pinKeyboard ? elements.pinKeyboard.querySelectorAll(".pin-key") : [];

        if (!buttons.length) {
            return;
        }

        state.pinDialogIndex = Math.max(0, Math.min(buttons.length - 1, state.pinDialogIndex || 0));
        buttons.forEach(function (button, index) {
            button.classList.toggle("is-focused", index === state.pinDialogIndex);
        });
        buttons[state.pinDialogIndex].focus();
    }

    function movePinKeyboardFocus(delta) {
        var buttons = elements.pinKeyboard ? elements.pinKeyboard.querySelectorAll(".pin-key") : [];

        if (!buttons.length) {
            return;
        }

        state.pinDialogIndex = Math.max(0, Math.min(buttons.length - 1, (state.pinDialogIndex || 0) + delta));
        focusPinKeyboardButton();
    }

    function applyPinKeyboardKey(type, value) {
        if (type === "digit") {
            if ((state.pinDialogValue || "").length < 12) {
                state.pinDialogValue = (state.pinDialogValue || "") + value;
                setPinDialogMessage("");
                updatePinDialogValue();
            }
            return;
        }

        if (type === "backspace") {
            state.pinDialogValue = Array.from(state.pinDialogValue || "").slice(0, -1).join("");
            setPinDialogMessage("");
            updatePinDialogValue();
            return;
        }

        if (type === "done") {
            if (!state.pinDialogValue) {
                setPinDialogMessage("أدخل الرقم السري");
                return;
            }
            closePinDialog(state.pinDialogValue);
        }
    }

    function handlePinDialogKeys(keyCode, event) {
        switch (keyCode) {
            case KEY_CODES.LEFT:
                event.preventDefault();
                movePinKeyboardFocus(-1);
                return true;
            case KEY_CODES.RIGHT:
                event.preventDefault();
                movePinKeyboardFocus(1);
                return true;
            case KEY_CODES.UP:
                event.preventDefault();
                movePinKeyboardFocus(-3);
                return true;
            case KEY_CODES.DOWN:
                event.preventDefault();
                movePinKeyboardFocus(3);
                return true;
            case KEY_CODES.ENTER:
                event.preventDefault();
                activatePinKeyboardFocus();
                return true;
            case KEY_CODES.BACK:
            case KEY_CODES.RETURN:
                event.preventDefault();
                closePinDialog("");
                return true;
        }

        return false;
    }

    function activatePinKeyboardFocus() {
        var buttons = elements.pinKeyboard ? elements.pinKeyboard.querySelectorAll(".pin-key") : [];
        var button = buttons[state.pinDialogIndex || 0];

        if (button) {
            applyPinKeyboardKey(button.getAttribute("data-pin-key-type"), button.getAttribute("data-value") || "");
        }
    }

    function verifyChannelUnlock(item, callback) {
        var lock = getChannelLock(item);
        var key = getChannelLockKey(item);
        var done = callback || function () {};

        if (!lock || !key || liveUnlockedChannels[key]) {
            done(true);
            return true;
        }

        openPinDialog("أدخل الرقم السري للقناة", function (pin) {
            if (pin === lock.pin) {
                liveUnlockedChannels[key] = true;
                showLivePreviewMessage("");
                updateLiveActionButtons(item);
                renderLiveInfoPanel();
                done(true);
                return;
            }

            if (pin) {
                showLivePreviewMessage("الرقم السري غير صحيح.");
            }
            done(false);
        });
        return false;
    }

    function toggleCurrentLiveLock() {
        var item = getCurrentLiveItem();
        var key = getChannelLockKey(item);
        var lock;

        if (!item || !key) {
            return;
        }

        lock = getChannelLock(item);
        if (lock) {
            verifyChannelUnlock(item, function (unlocked) {
                if (!unlocked) {
                    return;
                }
                try {
                    window.localStorage.removeItem(key);
                    delete liveUnlockedChannels[key];
                } catch (error) {
                    console.log("Could not unlock channel:", error.message);
                }
                updateLiveActionButtons(item);
                renderLiveInfoPanel();
            });
            return;
        }

        openPinDialog("اكتب رقم سري للقناة", function (pin) {
            if (!pin) {
                return;
            }

            openPinDialog("أعد كتابة الرقم السري", function (confirmPin) {
                if (!confirmPin) {
                    return;
                }
                if (pin !== confirmPin) {
                    showLivePreviewMessage("الرقم السري غير متطابق.");
                    return;
                }

                try {
                    window.localStorage.setItem(key, JSON.stringify({
                        pin: pin,
                        title: item.title,
                        addedAt: Date.now()
                    }));
                    delete liveUnlockedChannels[key];
                } catch (error) {
                    console.log("Could not lock channel:", error.message);
                }
                updateLiveActionButtons(item);
                renderLiveInfoPanel();
            });
        });
    }

    function reloadCurrentLiveChannel() {
        var item = getCurrentLiveItem();

        if (!item) {
            return;
        }

        verifyChannelUnlock(item, function (unlocked) {
            if (!unlocked) {
                return;
            }
            showLivePreviewMessage("");
            startLivePreview(item, true);
        });
    }

    function openLivePreviewFullscreen() {
        var target = elements.livePreviewVideo || elements.liveInfoPanel;

        if (!target || state.activeSection !== "live") {
            return;
        }

        elements.libraryScreen.classList.add("is-live-preview-fullscreen");
        if (target.requestFullscreen) {
            target.requestFullscreen().catch(function () {});
        } else if (target.webkitRequestFullscreen) {
            target.webkitRequestFullscreen();
        }
    }

    function closeLivePreviewFullscreen() {
        if (elements.libraryScreen) {
            elements.libraryScreen.classList.remove("is-live-preview-fullscreen");
        }
        if (document.fullscreenElement && document.exitFullscreen) {
            document.exitFullscreen().catch(function () {});
        } else if (document.webkitFullscreenElement && document.webkitExitFullscreen) {
            document.webkitExitFullscreen();
        }
    }

    function maybeExtendVodRenderedItems(targetIndex, force) {
        var previousScrollTop;
        var nextLimit;

        if (!isVodSection() || VOD_RENDER_LIMIT <= 0 || renderedItems.length >= visibleItems.length) {
            return false;
        }

        if (!force && targetIndex < renderedItems.length - 24) {
            return false;
        }

        previousScrollTop = elements.itemList.scrollTop;
        nextLimit = Math.min(visibleItems.length, Math.max(targetIndex + 1, renderedItems.length + VOD_RENDER_STEP));
        if (nextLimit <= renderedItems.length) {
            return false;
        }

        state.vodRenderLimit = nextLimit;
        renderItems();
        elements.itemList.scrollTop = previousScrollTop;
        syncLibraryFocus(false);
        return true;
    }

    function maybeExtendLiveRenderedItems(targetIndex, force) {
        var previousScrollTop;
        var nextLimit;

        if (state.activeSection !== "live" || LIVE_RENDER_LIMIT <= 0 || renderedItems.length >= visibleItems.length) {
            return false;
        }

        if (!force && targetIndex < renderedItems.length - 24) {
            return false;
        }

        previousScrollTop = elements.itemList.scrollTop;
        nextLimit = Math.min(visibleItems.length, Math.max(targetIndex + 1, renderedItems.length + LIVE_RENDER_STEP));
        if (nextLimit <= renderedItems.length) {
            return false;
        }

        state.liveRenderLimit = nextLimit;
        renderItems();
        elements.itemList.scrollTop = previousScrollTop;
        syncLibraryFocus(false);
        return true;
    }

    function isVodListNearEnd() {
        var scroller = elements.itemList;

        return !!(scroller && scroller.scrollTop + scroller.clientHeight >= scroller.scrollHeight - 360);
    }

    function getVirtualKeyboardRows() {
        if (state.virtualKeyboardMode === "numbers") {
            return [
                ["1", "2", "3", "4", "5", "6", "7"],
                ["8", "9", "0", "-", "_", ".", "/"],
                ["@", "#", "&", ":", "?", "!", "+"],
                [
                    { label: "AR", type: "mode", value: "ar" },
                    { label: "ABC", type: "mode", value: "en" },
                    { label: "ÙØ±Ø§Øº", type: "space", value: " " },
                    { label: "Ø­Ø°Ù", type: "backspace" },
                    { label: "Ù…Ø³Ø­", type: "clear" },
                    { label: "ØªÙ…", type: "done" }
                ]
            ];
        }

        if (state.virtualKeyboardMode === "en") {
            return [
                ["q", "w", "e", "r", "t", "y", "u"],
                ["i", "o", "p", "a", "s", "d", "f"],
                ["g", "h", "j", "k", "l", "z", "x"],
                ["c", "v", "b", "n", "m"],
                [
                    { label: "Ø¹Ø±Ø¨ÙŠ", type: "mode", value: "ar" },
                    { label: "123", type: "numbers" },
                    { label: "Spc", type: "space", value: " " },
                    { label: "Del", type: "backspace" },
                    { label: "Clr", type: "clear" },
                    { label: "OK", type: "done" }
                ]
            ];
        }

        return [
            ["Ø¶", "Øµ", "Ø«", "Ù‚", "Ù", "Øº", "Ø¹"],
            ["Ù‡", "Ø®", "Ø­", "Ø¬", "Ø¯", "Ø´", "Ø³"],
            ["ÙŠ", "Ø¨", "Ù„", "Ø§", "Øª", "Ù†", "Ù…"],
            ["Ùƒ", "Ø·", "Ø¦", "Ø¡", "Ø¤", "Ø±", "Ù„Ø§"],
            ["Ù‰", "Ø©", "Ùˆ", "Ø²", "Ø¸"],
            [
                { label: "ABC", type: "mode", value: "en" },
                { label: "123", type: "numbers" },
                { label: "ÙØ±Ø§Øº", type: "space", value: " " },
                { label: "Ø­Ø°Ù", type: "backspace" },
                { label: "Ù…Ø³Ø­", type: "clear" },
                { label: "ØªÙ…", type: "done" }
            ]
        ];
    }

    function getVirtualKeyboardKeys() {
        var keys = [];

        getVirtualKeyboardRows().forEach(function (row) {
            row.forEach(function (entry) {
                if (typeof entry === "string") {
                    keys.push({ label: entry, value: entry, type: "char" });
                } else {
                    keys.push(entry);
                }
            });
        });

        return keys;
    }

    function renderVirtualKeyboard() {
        var keys;

        if (!elements.virtualKeyboard) {
            return;
        }

        keys = getVirtualKeyboardKeys();
        elements.virtualKeyboard.innerHTML = '<div class="virtual-keyboard-grid">' + keys.map(function (key, index) {
            return '<button class="virtual-key' + (index === state.virtualKeyboardIndex ? " is-focused" : "") + '" type="button" data-index="' + index + '" data-key-type="' + escapeHtml(key.type || "char") + '" data-value="' + escapeHtml(key.value || "") + '">' + escapeHtml(key.label || key.value || "") + '</button>';
        }).join("") + '</div>';
    }

    function openVirtualKeyboard() {
        if (!elements.virtualKeyboard) {
            return;
        }

        elements.librarySearchInput.setAttribute("readonly", "readonly");
        elements.virtualKeyboard.hidden = false;
        state.libraryPane = "keyboard";
        state.virtualKeyboardIndex = Math.max(0, Math.min(getVirtualKeyboardKeys().length - 1, state.virtualKeyboardIndex || 0));
        renderVirtualKeyboard();
        syncLibraryFocus();
    }

    function closeVirtualKeyboard(nextPane) {
        if (elements.virtualKeyboard) {
            elements.virtualKeyboard.hidden = true;
        }

        state.libraryPane = nextPane || "categories";
        syncLibraryFocus();
    }

    function focusVirtualKeyboardButton() {
        var buttons;

        if (!elements.virtualKeyboard || elements.virtualKeyboard.hidden) {
            return;
        }

        buttons = elements.virtualKeyboard.querySelectorAll(".virtual-key");
        if (!buttons.length) {
            return;
        }

        state.virtualKeyboardIndex = Math.max(0, Math.min(buttons.length - 1, state.virtualKeyboardIndex || 0));
        buttons.forEach(function (button, index) {
            button.classList.toggle("is-focused", index === state.virtualKeyboardIndex);
        });
        buttons[state.virtualKeyboardIndex].focus();
    }

    function handleVirtualKeyboardKeys(keyCode, event) {
        switch (keyCode) {
            case KEY_CODES.LEFT:
                event.preventDefault();
                moveVirtualKeyboardFocus(-1);
                return true;
            case KEY_CODES.RIGHT:
                event.preventDefault();
                moveVirtualKeyboardFocus(1);
                return true;
            case KEY_CODES.UP:
                event.preventDefault();
                moveVirtualKeyboardFocus(-7);
                return true;
            case KEY_CODES.DOWN:
                event.preventDefault();
                moveVirtualKeyboardFocus(7);
                return true;
            case KEY_CODES.ENTER:
                event.preventDefault();
                activateVirtualKeyboardFocus();
                return true;
            case KEY_CODES.BACK:
            case KEY_CODES.RETURN:
                event.preventDefault();
                closeVirtualKeyboard("search");
                return true;
        }

        return false;
    }

    function moveVirtualKeyboardFocus(delta) {
        var buttons = elements.virtualKeyboard ? elements.virtualKeyboard.querySelectorAll(".virtual-key") : [];

        if (!buttons.length) {
            return;
        }

        state.virtualKeyboardIndex = Math.max(0, Math.min(buttons.length - 1, (state.virtualKeyboardIndex || 0) + delta));
        focusVirtualKeyboardButton();
    }

    function activateVirtualKeyboardFocus() {
        var buttons = elements.virtualKeyboard ? elements.virtualKeyboard.querySelectorAll(".virtual-key") : [];
        var button = buttons[state.virtualKeyboardIndex || 0];

        if (!button) {
            return;
        }

        applyVirtualKeyboardKey(button.getAttribute("data-key-type"), button.getAttribute("data-value") || "");
    }

    function applyVirtualKeyboardKey(type, value) {
        var current = elements.librarySearchInput.value || "";

        if (type === "mode") {
            state.virtualKeyboardMode = value || "ar";
            if (state.virtualKeyboardMode !== "numbers") {
                state.virtualKeyboardTextMode = state.virtualKeyboardMode;
            }
            state.virtualKeyboardIndex = 0;
            renderVirtualKeyboard();
            focusVirtualKeyboardButton();
            return;
        }

        if (type === "numbers") {
            if (state.virtualKeyboardMode === "numbers") {
                state.virtualKeyboardMode = state.virtualKeyboardTextMode || "ar";
            } else {
                state.virtualKeyboardTextMode = state.virtualKeyboardMode || "ar";
                state.virtualKeyboardMode = "numbers";
            }
            state.virtualKeyboardIndex = 0;
            renderVirtualKeyboard();
            focusVirtualKeyboardButton();
            return;
        }

        if (type === "backspace") {
            setSearchQueryFromKeyboard(Array.from(current).slice(0, -1).join(""));
            return;
        }

        if (type === "clear") {
            setSearchQueryFromKeyboard("");
            return;
        }

        if (type === "done") {
            closeVirtualKeyboard(visibleItems.length ? "items" : "categories");
            return;
        }

        setSearchQueryFromKeyboard(current + (type === "space" ? " " : value));
    }

    function setSearchQueryFromKeyboard(value) {
        state.searchQuery = value;
        elements.librarySearchInput.value = value;
        state.categoryIndex = 0;
        state.itemIndex = 0;
        state.vodRenderLimit = VOD_RENDER_LIMIT;
        renderLibrary();
        if (elements.virtualKeyboard) {
            elements.virtualKeyboard.hidden = false;
        }
        state.libraryPane = "keyboard";
        renderVirtualKeyboard();
        syncLibraryFocus();
    }

    function makeVirtualKey(label, type, value, span, className, ariaLabel) {
        return {
            label: label,
            type: type || "char",
            value: value || "",
            span: span || 1,
            className: className || "",
            ariaLabel: ariaLabel || label
        };
    }

    function getVirtualKeyboardRows() {
        var modeLabel = state.virtualKeyboardMode === "numbers" ?
            (state.virtualKeyboardTextMode === "en" ? "ABC" : "\u0623\u0628\u062c") :
            "123";
        var letterRows;

        if (state.virtualKeyboardMode === "numbers") {
            return [
                ["1", "2", "3", "4", "5", "6"],
                ["7", "8", "9", "0", "-", "_"],
                [".", "/", "@", ":", "#", "&"],
                ["?", "!", "+", "=", "(", ")"],
                [
                    makeVirtualKey("\u232b", "backspace", "", 2, "is-icon", "حذف"),
                    makeVirtualKey("\u0645\u0633\u062d", "clear", "", 2, "is-action"),
                    makeVirtualKey(modeLabel, "numbers", "", 2, "is-action")
                ],
                [
                    makeVirtualKey("\ud83c\udf10", "toggle-language", "", 1, "is-icon", "تبديل اللغة"),
                    makeVirtualKey("\u0645\u0633\u0627\u0641\u0629", "space", " ", 3, "is-space"),
                    makeVirtualKey("\u0628\u062d\u062b", "done", "", 2, "is-search")
                ]
            ];
        }

        if (state.virtualKeyboardMode === "en") {
            letterRows = [
                ["q", "w", "e", "r", "t", "y"],
                ["u", "i", "o", "p", "a", "s"],
                ["d", "f", "g", "h", "j", "k"],
                ["l", "z", "x", "c", "v", "b"],
                ["n", "m", ".", "-", "_", "'"]
            ];
        } else {
            letterRows = [
                ["\u0636", "\u0635", "\u062b", "\u0642", "\u0641", "\u063a"],
                ["\u0639", "\u0647", "\u062e", "\u062d", "\u062c", "\u0634"],
                ["\u0633", "\u064a", "\u0628", "\u0644", "\u0627", "\u062a"],
                ["\u0646", "\u0645", "\u0643", "\u0637", "\u0626", "\u0621"],
                ["\u0624", "\u0631", "\u0644\u0627", "\u0649", "\u0629", "\u0648"],
                ["\u0632", "\u0638", "\u0630", "\u062f", "\u0623", "\u0625"]
            ];
        }

        return letterRows.concat([
            [
                makeVirtualKey("\u232b", "backspace", "", 2, "is-icon", "حذف"),
                makeVirtualKey("\u0645\u0633\u062d", "clear", "", 2, "is-action"),
                makeVirtualKey(modeLabel, "numbers", "", 2, "is-action")
            ],
            [
                makeVirtualKey("\ud83c\udf10", "toggle-language", "", 1, "is-icon", "تبديل اللغة"),
                makeVirtualKey("\u0645\u0633\u0627\u0641\u0629", "space", " ", 3, "is-space"),
                makeVirtualKey("\u0628\u062d\u062b", "done", "", 2, "is-search")
            ]
        ]);
    }

    function getVirtualKeyboardKeys() {
        var keys = [];

        getVirtualKeyboardRows().forEach(function (row) {
            row.forEach(function (entry) {
                if (typeof entry === "string") {
                    keys.push({ label: entry, value: entry, type: "char", span: 1, className: "" });
                } else {
                    keys.push(entry);
                }
            });
        });

        return keys;
    }

    function renderVirtualKeyboard() {
        var index = 0;
        var html;

        if (!elements.virtualKeyboard) {
            return;
        }

        html = getVirtualKeyboardRows().map(function (row, rowIndex) {
            var col = 0;
            var rowHtml = row.map(function (entry) {
                var key = typeof entry === "string" ?
                    { label: entry, value: entry, type: "char", span: 1, className: "", ariaLabel: entry } :
                    entry;
                var span = Math.max(1, Math.min(6, key.span || 1));
                var className = "virtual-key" + (index === state.virtualKeyboardIndex ? " is-focused" : "") +
                    (key.className ? " " + key.className : "");
                var style = span > 1 ? ' style="grid-column: span ' + span + '"' : "";
                var button = '<button class="' + className + '" type="button" data-index="' + index +
                    '" data-row="' + rowIndex + '" data-col="' + col + '" data-span="' + span +
                    '" data-key-type="' + escapeHtml(key.type || "char") +
                    '" data-value="' + escapeHtml(key.value || "") +
                    '" aria-label="' + escapeHtml(key.ariaLabel || key.label || key.value || "") + '"' + style + '>' +
                    escapeHtml(key.label || key.value || "") + '</button>';

                index += 1;
                col += span;
                return button;
            }).join("");

            return '<div class="virtual-keyboard-row">' + rowHtml + '</div>';
        }).join("");

        elements.virtualKeyboard.innerHTML = '<div class="virtual-keyboard-grid">' + html + '</div>';
    }

    function handleVirtualKeyboardKeys(keyCode, event) {
        switch (keyCode) {
            case KEY_CODES.LEFT:
                event.preventDefault();
                moveVirtualKeyboardFocus(-1);
                return true;
            case KEY_CODES.RIGHT:
                event.preventDefault();
                moveVirtualKeyboardFocus(1);
                return true;
            case KEY_CODES.UP:
                event.preventDefault();
                moveVirtualKeyboardVertical(-1);
                return true;
            case KEY_CODES.DOWN:
                event.preventDefault();
                moveVirtualKeyboardVertical(1);
                return true;
            case KEY_CODES.ENTER:
                event.preventDefault();
                activateVirtualKeyboardFocus();
                return true;
            case KEY_CODES.BACK:
            case KEY_CODES.RETURN:
                event.preventDefault();
                closeVirtualKeyboard("search");
                return true;
        }

        return false;
    }

    function moveVirtualKeyboardFocus(delta) {
        var buttons = elements.virtualKeyboard ? elements.virtualKeyboard.querySelectorAll(".virtual-key") : [];

        if (!buttons.length) {
            return;
        }

        state.virtualKeyboardIndex = Math.max(0, Math.min(buttons.length - 1, (state.virtualKeyboardIndex || 0) + delta));
        focusVirtualKeyboardButton();
    }

    function moveVirtualKeyboardVertical(direction) {
        var buttons = elements.virtualKeyboard ? Array.prototype.slice.call(elements.virtualKeyboard.querySelectorAll(".virtual-key")) : [];
        var current = buttons[state.virtualKeyboardIndex || 0];
        var currentRow;
        var currentCenter;
        var targetRow;
        var maxRow = 0;
        var candidates;
        var best = null;
        var bestDistance = Infinity;

        if (!current || !buttons.length) {
            return;
        }

        currentRow = parseInt(current.getAttribute("data-row"), 10) || 0;
        currentCenter = (parseInt(current.getAttribute("data-col"), 10) || 0) +
            ((parseInt(current.getAttribute("data-span"), 10) || 1) / 2);
        buttons.forEach(function (button) {
            maxRow = Math.max(maxRow, parseInt(button.getAttribute("data-row"), 10) || 0);
        });
        targetRow = currentRow + direction;

        while (targetRow >= 0 && targetRow <= maxRow) {
            candidates = buttons.filter(function (button) {
                return (parseInt(button.getAttribute("data-row"), 10) || 0) === targetRow;
            });

            if (candidates.length) {
                candidates.forEach(function (button) {
                    var center = (parseInt(button.getAttribute("data-col"), 10) || 0) +
                        ((parseInt(button.getAttribute("data-span"), 10) || 1) / 2);
                    var distance = Math.abs(center - currentCenter);

                    if (distance < bestDistance) {
                        bestDistance = distance;
                        best = button;
                    }
                });
                break;
            }

            targetRow += direction;
        }

        if (best) {
            state.virtualKeyboardIndex = parseInt(best.getAttribute("data-index"), 10) || 0;
            focusVirtualKeyboardButton();
        }
    }

    function applyVirtualKeyboardKey(type, value) {
        var current = elements.librarySearchInput.value || "";

        if (type === "toggle-language") {
            state.virtualKeyboardMode = state.virtualKeyboardMode === "en" ? "ar" : "en";
            state.virtualKeyboardTextMode = state.virtualKeyboardMode;
            state.virtualKeyboardIndex = 0;
            renderVirtualKeyboard();
            focusVirtualKeyboardButton();
            return;
        }

        if (type === "mode") {
            state.virtualKeyboardMode = value || "ar";
            if (state.virtualKeyboardMode !== "numbers") {
                state.virtualKeyboardTextMode = state.virtualKeyboardMode;
            }
            state.virtualKeyboardIndex = 0;
            renderVirtualKeyboard();
            focusVirtualKeyboardButton();
            return;
        }

        if (type === "numbers") {
            if (state.virtualKeyboardMode === "numbers") {
                state.virtualKeyboardMode = state.virtualKeyboardTextMode || "ar";
            } else {
                state.virtualKeyboardTextMode = state.virtualKeyboardMode || "ar";
                state.virtualKeyboardMode = "numbers";
            }
            state.virtualKeyboardIndex = 0;
            renderVirtualKeyboard();
            focusVirtualKeyboardButton();
            return;
        }

        if (type === "backspace") {
            setSearchQueryFromKeyboard(Array.from(current).slice(0, -1).join(""));
            return;
        }

        if (type === "clear") {
            setSearchQueryFromKeyboard("");
            return;
        }

        if (type === "done") {
            closeVirtualKeyboard(visibleItems.length ? "items" : "categories");
            return;
        }

        setSearchQueryFromKeyboard(current + (type === "space" ? " " : value));
    }

    function renderItemsLegacy() {
        var category = state.categories[state.categoryIndex];
        visibleItems = category ? category.items : [];
        renderedItems = isVodSection() && VOD_RENDER_LIMIT > 0 ? visibleItems.slice(0, VOD_RENDER_LIMIT) : visibleItems;
        elements.itemList.innerHTML = "";
        elements.categoryHeading.textContent = category ? category.name : "All";
        elements.itemCount.textContent = renderedItems.length < visibleItems.length ?
            renderedItems.length + " / " + visibleItems.length + " items" :
            visibleItems.length + " items";
        elements.itemList.classList.toggle("poster-grid", isVodSection());
        if (state.itemIndex >= renderedItems.length) {
            state.itemIndex = 0;
        }

        renderedItems.forEach(function (item, index) {
            var row = document.createElement("button");
            row.className = "item-row";
            row.type = "button";
            row.setAttribute("data-index", index);

            if (isVodSection()) {
                row.className = "poster-card";
                row.innerHTML =
                    '<span class="poster-art" style="' + getPosterStyle(item) + '">' +
                        getPosterFallbackHtml(item) +
                    '</span>' +
                    '<span class="poster-title">' + escapeHtml(item.title) + '</span>' +
                    '<span class="poster-meta">' + escapeHtml(item.group || getSection(item.section).subtitle) + '</span>';
            } else {
                row.innerHTML =
                    '<span class="item-number">' + (index + 1) + '</span>' +
                    '<span class="item-main">' +
                        '<span class="item-title">' + escapeHtml(item.title) + '</span>' +
                        '<span class="item-meta">' + escapeHtml(item.group || item.description || item.type.toUpperCase()) + '</span>' +
                    '</span>' +
                    '<span class="item-type">' + escapeHtml(item.type.toUpperCase()) + '</span>';
            }
            elements.itemList.appendChild(row);
        });

        syncLibraryFocus();
    }

    function initializeActivation() {
        setPlaylistLoading(true, "Ø¬Ø§Ø±ÙŠ Ø§Ù„ØªØ­Ù‚Ù‚ Ù…Ù† Ø§Ù„ØªÙØ¹ÙŠÙ„ ÙˆØ¬Ù„Ø¨ Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„Ø¬Ù‡Ø§Ø²...");
        getDeviceMac(function (mac) {
            state.deviceMac = mac;

            if (!APP_SERVER_URL) {
                elements.playlistStatus.textContent = "MAC: " + mac + " - local playlist mode";
                renderActivationSummary();
                loadIptvPlaylist();
                return;
            }

            requestJsonWithTimeout(normalizeServerUrl(APP_SERVER_URL) + "/api/device.php?mac=" + encodeURIComponent(mac), 18000, function (error, data) {
                if (error || !data || !data.ok) {
                    console.log("Activation server unavailable:", error ? error.message : data);
                    setPlaylistLoading(false);
                    elements.playlistStatus.textContent = "Activation server unavailable";
                    renderActivationSummary();
                    return;
                }

                state.activation = data;
                renderActivationSummary();

                if (!data.access || !data.access.allowed) {
                    setPlaylistLoading(false);
                    elements.playlistStatus.textContent = "Trial expired. Open My Account to activate with Stripe. MAC: " + mac;
                    return;
                }

                if (data.playlist && data.playlist.generated_url) {
                    IPTV_PLAYLIST_URL = data.playlist.generated_url;
                } else {
                    IPTV_PLAYLIST_URL = "";
                }

                elements.playlistStatus.textContent = getActivationStatusText(data);
                loadIptvPlaylist();
            });
        });
    }

    function getDeviceMac(callback) {
        var fallback = getStoredBrowserDeviceId();

        try {
            if (window.webapis && window.webapis.network && typeof window.webapis.network.getMac === "function") {
                callback(normalizeMac(window.webapis.network.getMac()));
                return;
            }
        } catch (error) {
            console.log("Samsung MAC lookup failed:", error.message);
        }

        callback(fallback);
    }

    function getStoredBrowserDeviceId() {
        var key = "tizenPlayerDeviceId";
        var value;

        try {
            value = window.localStorage.getItem(key);
            if (!value) {
                value = "WEB-" + Math.random().toString(16).slice(2, 8).toUpperCase() + "-" + Math.random().toString(16).slice(2, 8).toUpperCase();
                window.localStorage.setItem(key, value);
            }
            return value;
        } catch (error) {
            return "WEB-DEMO";
        }
    }

    function normalizeMac(mac) {
        return String(mac || "").trim().toUpperCase();
    }

    function normalizeServerUrl(url) {
        return String(url || "").replace(/\/+$/, "");
    }

    function getActivationStatusText(data) {
        if (data.access && data.access.reason === "paid") {
            return "Activated until " + formatServerDate(data.active_until) + " - MAC: " + data.mac;
        }

        return "Trial active until " + formatServerDate(data.trial_ends_at) + " - MAC: " + data.mac;
    }

    function formatServerDate(value) {
        if (!value) {
            return "unknown";
        }

        return String(value).replace("T", " ").replace(/\+.*/, "");
    }

    function resetLibraryForReload() {
        allItems = [];
        visibleItems = [];
        renderedItems = [];
        latestItems = [];
        detailMetadataCache = {};
        imdbResolveCache = {};
        detailSeasons = [];
        detailEpisodes = [];
        actorWorks = [];
        state.liveRenderLimit = LIVE_RENDER_LIMIT;
        state.vodRenderLimit = VOD_RENDER_LIMIT;
        renderHome();
    }

    function loadIptvPlaylist() {
        if (!IPTV_PLAYLIST_URL) {
            elements.playlistStatus.textContent = "Add playlist from List Playlists";
            hideStartupSplash();
            return;
        }

        setPlaylistLoading(true, "Loading channels, movies and series...");
        if (getXtreamConfig()) {
            loadXtreamLibrary({
                replaceExisting: true,
                fallbackToM3u: true
            });
            return;
        }

        loadM3uPlaylist(false);
    }

    function loadM3uPlaylist(refreshXtreamAfterLoad) {
        requestTextWithRetry(IPTV_PLAYLIST_URL, 2, function (error, playlistText) {
            var iptvItems;

            if (error) {
                setPlaylistLoading(false);
                elements.playlistStatus.textContent = "Playlist failed";
                console.log("Could not load IPTV playlist:", error.message);
                return;
            }

            iptvItems = limitItemsPerSection(parseM3uPlaylist(playlistText), IPTV_MAX_ITEMS_PER_SECTION);

            if (!iptvItems.length) {
                setPlaylistLoading(false);
                elements.playlistStatus.textContent = "No IPTV items found";
                return;
            }

            allItems = assignLibraryOrder(iptvItems);
            storeActivePlaylistCounts();
            renderHome();
            if (refreshXtreamAfterLoad && getXtreamConfig()) {
                loadXtreamLibrary();
            } else {
                setPlaylistLoading(false);
                elements.playlistStatus.textContent = iptvItems.length + " IPTV items loaded";
            }

            if (state.screen === "library") {
                renderLibrary();
            }
        });
    }

    function setPlaylistLoading(isLoading, message) {
        if (isLoading && state.splashHideTimer) {
            clearTimeout(state.splashHideTimer);
            state.splashHideTimer = null;
        }

        elements.playlistStatus.classList.toggle("is-loading", !!isLoading && !state.splashVisible);
        if (message) {
            if (state.splashVisible && isLoading) {
                elements.splashStatus.textContent = "Loading ...";
                elements.playlistStatus.textContent = "";
            } else {
                elements.playlistStatus.textContent = message;
            }
        }

        if (!isLoading && state.splashVisible) {
            hideStartupSplash();
        }
    }

    function hideStartupSplash() {
        var elapsed;
        var delay;

        if (!state.splashVisible || state.splashHideTimer) {
            return;
        }

        elapsed = Date.now() - state.splashStartedAt;
        delay = Math.max(0, 1600 - elapsed);
        if (delay > 0) {
            state.splashHideTimer = setTimeout(hideStartupSplashNow, delay);
            return;
        }

        hideStartupSplashNow();
    }

    function hideStartupSplashNow() {
        state.splashVisible = false;
        state.splashHideTimer = null;
        if (elements.splashScreen) {
            elements.splashScreen.classList.add("is-hidden");
        }
    }

    function requestTextWithRetry(url, retries, callback) {
        requestText(url, function (error, text) {
            if (!error) {
                callback(null, text);
                return;
            }

            if (retries > 0) {
                console.log("Retrying playlist request:", error.message);
                setTimeout(function () {
                    requestTextWithRetry(url, retries - 1, callback);
                }, 1200);
                return;
            }

            callback(error);
        });
    }

    function loadXtreamLibrary(options) {
        options = options || {};

        var config = getXtreamConfig();
        var pending = 6;
        var rawLiveItems = [];
        var rawMovieItems = [];
        var rawSeriesItems = [];
        var liveCategories = {};
        var movieCategories = {};
        var seriesCategories = {};

        if (!config) {
            return;
        }

        setPlaylistLoading(true, "Ø¬Ø§Ø±ÙŠ ØªØ­Ø¯ÙŠØ« Ù…ÙƒØªØ¨Ø© Ø§Ù„Ø£ÙÙ„Ø§Ù… ÙˆØ§Ù„Ù…Ø³Ù„Ø³Ù„Ø§Øª...");
        setPlaylistLoading(true, "Loading Xtream library...");
        requestJsonWithTimeout(getXtreamActionUrl("get_live_categories"), XTREAM_LIBRARY_TIMEOUT, function (error, data) {
            if (error) {
                console.log("Could not load Xtream live categories:", error.message);
            } else {
                liveCategories = normalizeXtreamCategoryMap(Array.isArray(data) ? data : []);
            }
            finish();
        });

        requestJsonWithTimeout(getXtreamActionUrl("get_live_streams"), XTREAM_LIBRARY_TIMEOUT, function (error, data) {
            if (error) {
                console.log("Could not load Xtream live library:", error.message);
            } else {
                rawLiveItems = Array.isArray(data) ? data : [];
            }
            finish();
        });

        requestJsonWithTimeout(getXtreamActionUrl("get_vod_categories"), XTREAM_LIBRARY_TIMEOUT, function (error, data) {
            if (error) {
                console.log("Could not load Xtream movie categories:", error.message);
            } else {
                movieCategories = normalizeXtreamCategoryMap(Array.isArray(data) ? data : []);
            }
            finish();
        });

        requestJsonWithTimeout(getXtreamActionUrl("get_series_categories"), XTREAM_LIBRARY_TIMEOUT, function (error, data) {
            if (error) {
                console.log("Could not load Xtream series categories:", error.message);
            } else {
                seriesCategories = normalizeXtreamCategoryMap(Array.isArray(data) ? data : []);
            }
            finish();
        });

        requestJsonWithTimeout(getXtreamActionUrl("get_vod_streams"), XTREAM_LIBRARY_TIMEOUT, function (error, data) {
            if (error) {
                console.log("Could not load Xtream movies library:", error.message);
            } else {
                rawMovieItems = Array.isArray(data) ? data : [];
            }
            finish();
        });

        requestJsonWithTimeout(getXtreamActionUrl("get_series"), XTREAM_LIBRARY_TIMEOUT, function (error, data) {
            if (error) {
                console.log("Could not load Xtream series library:", error.message);
            } else {
                rawSeriesItems = Array.isArray(data) ? data : [];
            }
            finish();
        });

        function finish() {
            var normalizedItems;

            pending -= 1;
            if (pending > 0) {
                return;
            }

            normalizedItems = normalizeXtreamLiveList(rawLiveItems, config, liveCategories).concat(
                normalizeXtreamMovieList(rawMovieItems, config, movieCategories),
                normalizeXtreamSeriesList(rawSeriesItems, seriesCategories)
            );

            if (!normalizedItems.length && options.fallbackToM3u) {
                console.log("Xtream API library was empty; falling back to M3U playlist.");
                loadM3uPlaylist(false);
                return;
            }

            if (options.replaceExisting) {
                allItems = [];
            }

            mergeXtreamLibrary(normalizedItems);
            assignLibraryOrder(allItems);
            storeActivePlaylistCounts();
            setPlaylistLoading(false);
            elements.playlistStatus.textContent = getItemsBySection("live").length + " Live / " +
                getItemsBySection("movies").length + " Movies / " +
                getItemsBySection("series").length + " Series";
            renderHome();

            if (state.screen === "library") {
                renderLibrary();
            } else if (state.screen === "detail" && state.detailItem) {
                renderDetail(state.detailItem);
            }
        }
    }

    function getXtreamActionUrl(action) {
        var config = getXtreamConfig();

        if (!config) {
            return "";
        }

        return config.baseUrl + "/player_api.php?username=" + encodeURIComponent(config.username) +
            "&password=" + encodeURIComponent(config.password) +
            "&action=" + encodeURIComponent(action);
    }

    function requestJsonWithTimeout(url, timeout, callback) {
        requestTextWithIptvFallback(url, timeout, function (error, text) {
            if (error) {
                callback(error);
                return;
            }

            try {
                callback(null, JSON.parse(text));
            } catch (parseError) {
                callback(parseError);
            }
        });
    }

    function requestTextWithTimeout(url, timeout, callback) {
        var request = new XMLHttpRequest();

        request.open("GET", url, true);
        request.timeout = timeout;

        request.onreadystatechange = function () {
            if (request.readyState !== 4) {
                return;
            }

            if (request.status >= 200 && request.status < 300) {
                callback(null, request.responseText);
                return;
            }

            callback(new Error("HTTP " + request.status));
        };

        request.onerror = function () {
            callback(new Error("Network error"));
        };

        request.ontimeout = function () {
            callback(new Error("Request timed out"));
        };

        request.send();
    }

    function requestExternalText(url, callback) {
        var proxyUrl = getIptvProxyUrl(url);

        requestTextWithTimeout(url, 12000, function (error, text) {
            var readerUrl;

            if (!error) {
                callback(null, text);
                return;
            }

            if (proxyUrl) {
                requestTextWithTimeout(proxyUrl, 22000, function (proxyError, proxyText) {
                    if (!proxyError) {
                        callback(null, proxyText);
                        return;
                    }
                    requestExternalTextViaReader(url, callback);
                });
                return;
            }

            requestExternalTextViaReader(url, callback);
        });
    }

    function requestExternalTextViaReader(url, callback) {
        var readerUrl = "https://r.jina.ai/http://" + String(url || "").replace(/^https?:\/\//i, "");

        requestTextWithTimeout(readerUrl, 18000, function (proxyError, proxyText) {
            if (!proxyError) {
                callback(null, proxyText);
                return;
            }

            requestTextWithTimeout(String(url || "").replace(/^https:\/\//i, "http://"), 18000, callback);
        });
    }

    function normalizeXtreamCategoryMap(items) {
        var map = {};

        items.forEach(function (category) {
            var id = category.category_id || category.id;
            var name = category.category_name || category.name || category.title;

            if (id && name) {
                map[String(id)] = String(name);
            }
        });

        return map;
    }

    function normalizeXtreamContainerExtension(value) {
        var extension = String(value || "").trim().toLowerCase().replace(/^\./, "");

        if (!extension || extension === "undefined" || extension === "null") {
            return "mp4";
        }

        return extension;
    }

    function getPreferredXtreamLiveExtension(streamExtension, config) {
        var output = normalizeXtreamContainerExtension(config && config.output);
        var extension = normalizeXtreamContainerExtension(streamExtension || output || "ts");

        if (output === "m3u8" || extension === "ts" || extension === "mpegts") {
            return "m3u8";
        }

        return extension;
    }

    function getMediaTypeFromExtension(extension) {
        var normalized = normalizeXtreamContainerExtension(extension);

        if (normalized === "m3u8") {
            return "hls";
        }

        if (normalized === "ts") {
            return "mpegts";
        }

        return normalized;
    }

    function normalizeXtreamLiveList(items, config, categoryMap) {
        return items.map(function (stream) {
            var extension = getPreferredXtreamLiveExtension(stream.container_extension || stream.containerExtension || "ts", config);
            var titleParts = parseTitleDetails(stream.name || stream.title || "Live TV");
            var categoryId = stream.category_id || stream.categoryId || "";
            var group = categoryMap[String(categoryId)] || stream.category_name || stream.genre || stream.category || "Live TV";
            var streamId = stream.stream_id || stream.streamId || stream.id;
            var directSource = stream.direct_source || stream.directSource || "";

            return {
                title: titleParts.title,
                group: group,
                section: "live",
                description: group ? "IPTV - " + group : "IPTV",
                logo: stream.stream_icon || stream.logo || "",
                number: stream.num || stream.number || "",
                year: titleParts.year,
                quality: titleParts.quality,
                duration: "",
                addedAt: stream.added || "",
                rating: "",
                plot: "",
                cast: [],
                xtreamId: streamId,
                containerExtension: extension,
                type: getMediaTypeFromExtension(extension),
                url: directSource || (config.baseUrl + "/live/" + encodeURIComponent(config.username) + "/" + encodeURIComponent(config.password) + "/" + streamId + "." + extension)
            };
        }).filter(function (item) {
            return item.xtreamId && item.url;
        });
    }

    function normalizeXtreamMovieList(items, config, categoryMap) {
        return items.map(function (movie) {
            var extension = normalizeXtreamContainerExtension(movie.container_extension);
            var titleParts = parseTitleDetails(movie.name || movie.title || "Movie");
            var categoryId = movie.category_id || movie.categoryId || "";
            var group = categoryMap[String(categoryId)] || movie.category_name || movie.genre || movie.category || "Movies";

            return {
                title: titleParts.title,
                group: group,
                section: "movies",
                description: "",
                logo: movie.stream_icon || "",
                year: titleParts.year,
                quality: titleParts.quality,
                duration: "",
                addedAt: movie.added || "",
                rating: movie.rating || movie.rating_5based || "",
                plot: "",
                cast: [],
                xtreamId: movie.stream_id,
                containerExtension: extension,
                type: getMediaTypeFromExtension(extension),
                url: config.baseUrl + "/movie/" + encodeURIComponent(config.username) + "/" + encodeURIComponent(config.password) + "/" + movie.stream_id + "." + extension
            };
        }).filter(function (item) {
            return item.xtreamId;
        });
    }

    function normalizeXtreamSeriesList(items, categoryMap) {
        return items.map(function (series) {
            var titleParts = parseTitleDetails(series.name || "Series");
            var castPeople = parseCastPeople(series.cast || "");
            var backdrop = Array.isArray(series.backdrop_path) ? series.backdrop_path[0] : series.backdrop_path;
            var categoryId = series.category_id || series.categoryId || "";
            var group = categoryMap[String(categoryId)] || series.category_name || series.genre || "Series";

            return {
                title: titleParts.title,
                group: group,
                section: "series",
                description: series.plot || "",
                logo: series.cover || "",
                backdrop: backdrop || "",
                year: series.releaseDate ? String(series.releaseDate).slice(0, 4) : titleParts.year,
                quality: titleParts.quality,
                duration: series.episode_run_time || "",
                addedAt: series.last_modified || "",
                rating: series.rating || series.rating_5based || "",
                plot: series.plot || "",
                cast: castPeople.map(function (person) {
                    return person.name;
                }),
                castPeople: castPeople,
                seriesTitle: titleParts.title,
                xtreamId: series.series_id,
                isSeriesContainer: true,
                type: "mp4",
                url: ""
            };
        }).filter(function (item) {
            return item.xtreamId;
        });
    }

    function mergeXtreamLibrary(items) {
        var existing = {};
        var liveItems = [];
        var richSections = {};

        items.forEach(function (item) {
            richSections[item.section] = true;
        });

        allItems.forEach(function (item) {
            if (item.section === "live") {
                liveItems.push(item);
                return;
            }

            if (richSections[item.section]) {
                return;
            }

            existing[getXtreamMergeKey(item)] = item;
        });

        items.forEach(function (item) {
            existing[getXtreamMergeKey(item)] = mergeLibraryItem(existing[getXtreamMergeKey(item)], item);
        });

        allItems = liveItems.concat(Object.keys(existing).map(function (key) {
            return existing[key];
        }));
    }

    function getXtreamMergeKey(item) {
        if (item.xtreamId) {
            return item.section + ":id:" + item.xtreamId;
        }

        return getLibraryWorkKey(item);
    }

    function mergeLibraryItem(oldItem, newItem) {
        if (!oldItem) {
            return newItem;
        }

        Object.keys(newItem).forEach(function (key) {
            if (newItem[key] !== "" && newItem[key] !== null && typeof newItem[key] !== "undefined") {
                oldItem[key] = newItem[key];
            }
        });

        return oldItem;
    }

    function assignLibraryOrder(items) {
        (items || []).forEach(function (item, index) {
            item.libraryOrder = index;
        });

        return items || [];
    }

    function limitItemsPerSection(items, maxPerSection) {
        if (!maxPerSection || maxPerSection <= 0) {
            return items;
        }

        var counts = {
            live: 0,
            movies: 0,
            series: 0
        };

        return items.filter(function (item) {
            if (counts[item.section] >= maxPerSection) {
                return false;
            }

            counts[item.section] += 1;
            return true;
        });
    }

    function requestText(url, callback) {
        var request = new XMLHttpRequest();

        request.open("GET", url, true);
        request.timeout = 20000;

        request.onreadystatechange = function () {
            if (request.readyState !== 4) {
                return;
            }

            if (request.status >= 200 && request.status < 300) {
                callback(null, request.responseText);
                return;
            }

            callback(new Error("HTTP " + request.status));
        };

        request.onerror = function () {
            callback(new Error("Network error"));
        };

        request.ontimeout = function () {
            callback(new Error("Request timed out"));
        };

        request.send();
    }

    function postJson(url, payload, callback) {
        var request = new XMLHttpRequest();

        request.open("POST", url, true);
        request.timeout = 20000;
        request.setRequestHeader("Content-Type", "application/json");

        request.onreadystatechange = function () {
            if (request.readyState !== 4) {
                return;
            }

            if (request.status >= 200 && request.status < 300) {
                try {
                    callback(null, JSON.parse(request.responseText));
                } catch (error) {
                    callback(error);
                }
                return;
            }

            callback(new Error("HTTP " + request.status));
        };

        request.onerror = function () {
            callback(new Error("Network error"));
        };

        request.ontimeout = function () {
            callback(new Error("Request timed out"));
        };

        request.send(JSON.stringify(payload || {}));
    }

    function parseM3uPlaylist(text) {
        var lines = text.split(/\r?\n/);
        var items = [];
        var currentInfo = null;

        lines.forEach(function (line) {
            var trimmed = line.trim();

            if (!trimmed) {
                return;
            }

            if (trimmed.indexOf("#EXTINF") === 0) {
                currentInfo = parseExtInf(trimmed);
                return;
            }

            if (trimmed.charAt(0) === "#") {
                return;
            }

            if (currentInfo) {
                trimmed = normalizeIptvStreamUrl(trimmed);
                items.push({
                    title: currentInfo.title || "IPTV",
                    group: currentInfo.group || "Other",
                    section: detectSection(trimmed, currentInfo),
                    description: currentInfo.group ? "IPTV - " + currentInfo.group : "IPTV",
                    logo: currentInfo.logo,
                    year: currentInfo.year,
                    quality: currentInfo.quality,
                    duration: currentInfo.duration,
                    addedAt: currentInfo.addedAt,
                    rating: currentInfo.rating,
                    plot: currentInfo.plot,
                    cast: currentInfo.cast,
                    seriesTitle: currentInfo.seriesTitle,
                    seasonNumber: currentInfo.seasonNumber,
                    episodeNumber: currentInfo.episodeNumber,
                    xtreamId: getXtreamStreamId(trimmed),
                    type: getStreamType(trimmed),
                    url: trimmed
                });
                currentInfo = null;
            }
        });

        return items;
    }

    function parseExtInf(line) {
        var commaIndex = line.lastIndexOf(",");
        var title = commaIndex !== -1 ? line.slice(commaIndex + 1).trim() : "IPTV";
        var tvgName = getAttributeValue(line, "tvg-name");
        var group = getAttributeValue(line, "group-title");
        var logo = getAttributeValue(line, "tvg-logo");
        var titleParts = parseTitleDetails(tvgName || title);
        var seriesInfo = parseSeriesEpisodeInfo(titleParts.title);

        return {
            title: seriesInfo.title,
            group: group,
            logo: logo,
            year: titleParts.year,
            quality: titleParts.quality,
            duration: titleParts.duration,
            addedAt: getAttributeValue(line, "added") || getAttributeValue(line, "added-at") || getAttributeValue(line, "date") || "",
            rating: getAttributeValue(line, "rating") || "",
            plot: getAttributeValue(line, "plot") || getAttributeValue(line, "description") || "",
            cast: parseCast(getAttributeValue(line, "cast") || getAttributeValue(line, "actors")),
            seriesTitle: seriesInfo.seriesTitle,
            seasonNumber: seriesInfo.seasonNumber,
            episodeNumber: seriesInfo.episodeNumber
        };
    }

    function getAttributeValue(text, attributeName) {
        var pattern = new RegExp(attributeName + '="([^"]*)"', "i");
        var match = text.match(pattern);

        return match ? match[1] : "";
    }

    function parseTitleDetails(title) {
        var cleanTitle = String(title || "IPTV").replace(/\s+/g, " ").trim();
        var yearMatch = cleanTitle.match(/\b(19|20)\d{2}\b/);
        var durationMatch = cleanTitle.match(/\b(\d{1,2}:\d{2}(?::\d{2})?)\b/);
        var qualityMatch = cleanTitle.match(/\b(4K|UHD|FHD|HD|SD|WEB-DL|BLURAY|CAM|MP4|HEVC)\b/i);

        cleanTitle = cleanTitle
            .replace(/\b(19|20)\d{2}\b/g, "")
            .replace(/\b(\d{1,2}:\d{2}(?::\d{2})?)\b/g, "")
            .replace(/\b(4K|UHD|FHD|HD|SD|WEB-DL|BLURAY|CAM|MP4|HEVC)\b/ig, "")
            .replace(/\(\s*\)/g, "")
            .replace(/\[\s*\]/g, "")
            .replace(/\s*[-|]\s*$/g, "")
            .replace(/\s+/g, " ")
            .trim();

        return {
            title: cleanTitle || title || "IPTV",
            year: yearMatch ? yearMatch[0] : "",
            duration: durationMatch ? durationMatch[0] : "",
            quality: qualityMatch ? qualityMatch[0].toUpperCase() : ""
        };
    }

    function parseCast(value) {
        return parseCastPeople(value).map(function (person) {
            return person.name;
        });
    }

    function parseCastPeople(value) {
        var people;

        if (!value) {
            return [];
        }

        if (Array.isArray(value)) {
            people = value.map(function (person) {
                if (typeof person === "string") {
                    return { name: person.trim(), image: "" };
                }

                var name = String(person.name || person.original_name || person.title || "").trim();
                return {
                    name: name,
                    image: normalizePersonImageUrl(person.profile_path || person.image || person.photo || person.avatar || ""),
                    tmdbPersonId: person.id || person.tmdb_id || person.person_id || ""
                };
            });
        } else {
            people = String(value).split(/[,|ØŒ]/).map(function (name) {
                var cleanName = name.trim();
                return { name: cleanName, image: "" };
            });
        }

        return people.filter(function (person) {
            return person.name;
        }).slice(0, 12);
    }

    function parseSeriesEpisodeInfoLegacy(title) {
        var text = String(title || "IPTV").replace(/\s+/g, " ").trim();
        var match = text.match(/\bS(?:eason)?\s*(\d{1,2})\s*E(?:p(?:isode)?)?\s*(\d{1,3})\b/i) ||
            text.match(/\bS\s*(\d{1,2})\s*(?:Ep|Episode|E)\s*(\d{1,3})\b/i) ||
            text.match(/\b(\d{1,2})x(\d{1,3})\b/i) ||
            text.match(/Ù…ÙˆØ³Ù…\s*(\d{1,2}).*Ø­Ù„Ù‚Ø©\s*(\d{1,3})/i);
        var cleanTitle = match ? text.replace(match[0], "") : text;

        cleanTitle = cleanTitle
            .replace(/\bS(?:eason)?\s*\d{1,2}\b/ig, "")
            .replace(/\bE(?:p(?:isode)?)?\s*\d{1,3}\b/ig, "")
            .replace(/\b(?:Ep|Episode)\s*\d{1,3}\b/ig, "")
            .replace(/\bS\d{1,2}\b/ig, "")
            .replace(/\(\s*\)/g, "")
            .replace(/\[\s*\]/g, "")
            .replace(/\s*[-|]\s*$/g, "")
            .replace(/\s+/g, " ")
            .trim();

        return {
            title: text,
            seriesTitle: cleanTitle || text,
            seasonNumber: match ? parseInt(match[1], 10) : 1,
            episodeNumber: match ? parseInt(match[2], 10) : 1
        };
    }

    function parseSeriesEpisodeInfo(title) {
        var text = String(title || "IPTV").replace(/\s+/g, " ").trim();
        var match = text.match(/\bS(?:eason)?\s*(\d{1,2})\s*E(?:p(?:isode)?)?\s*(\d{1,3})\b/i) ||
            text.match(/\bS\s*(\d{1,2})\s*[._-]\s*E\s*(\d{1,3})\b/i) ||
            text.match(/\bS(\d{1,2})E(\d{1,3})\b/i) ||
            text.match(/\bS\s*(\d{1,2})\s*(?:Ep|Episode|E)\s*(\d{1,3})\b/i) ||
            text.match(/\b(\d{1,2})x(\d{1,3})\b/i) ||
            text.match(/(?:season|saison|temporada|sezon)\s*(\d{1,2}).*(?:episode|ep|episodio|bolum)\s*(\d{1,3})/i) ||
            text.match(/Ã™â€¦Ã™Ë†Ã˜Â³Ã™â€¦\s*(\d{1,2}).*Ã˜Â­Ã™â€žÃ™â€šÃ˜Â©\s*(\d{1,3})/i);
        var cleanTitle = match ? text.replace(match[0], "") : text;

        cleanTitle = cleanTitle
            .replace(/\bS\s*\d{1,2}\s*[._-]\s*E\s*\d{1,3}\b/ig, "")
            .replace(/\bS\d{1,2}E\d{1,3}\b/ig, "")
            .replace(/\bS(?:eason)?\s*\d{1,2}\b/ig, "")
            .replace(/\bE(?:p(?:isode)?)?\s*\d{1,3}\b/ig, "")
            .replace(/(?:\b(?:Ep|Episode|Episodio|Bolum)|.pisode)\s*\d{1,3}\b/ig, "")
            .replace(/\bS\d{1,2}\b/ig, "")
            .replace(/\(\s*\)/g, "")
            .replace(/\[\s*\]/g, "")
            .replace(/\s*[-|]\s*[-|]?\s*/g, " - ")
            .replace(/\s*[-|]\s*$/g, "")
            .replace(/^\s*[-|]\s*/g, "")
            .replace(/\s+/g, " ")
            .trim();

        return {
            title: text,
            seriesTitle: cleanTitle || text,
            seasonNumber: match ? parseInt(match[1], 10) : 1,
            episodeNumber: match ? parseInt(match[2], 10) : 1
        };
    }

    function preferXtreamLiveHlsUrl(url) {
        var trimmed = String(url || "").trim();

        if (!/\/live\//i.test(trimmed)) {
            return trimmed;
        }

        return trimmed.replace(/\.(ts|mpegts)([?#].*)?$/i, ".m3u8$2");
    }

    function forceXtreamPlaylistHlsOutput(url) {
        var trimmed = String(url || "").trim();
        var hashIndex;
        var hash = "";
        var base;

        if (!/\/get\.php\?/i.test(trimmed) || !/[?&]username=/i.test(trimmed) || !/[?&]password=/i.test(trimmed)) {
            return trimmed;
        }

        hashIndex = trimmed.indexOf("#");
        if (hashIndex !== -1) {
            hash = trimmed.slice(hashIndex);
            trimmed = trimmed.slice(0, hashIndex);
        }

        if (/[?&]output=/i.test(trimmed)) {
            return trimmed.replace(/([?&]output=)[^&#]*/i, "$1m3u8") + hash;
        }

        base = trimmed + (trimmed.indexOf("?") === -1 ? "?" : "&") + "output=m3u8";
        return base + hash;
    }

    function normalizeIptvStreamUrl(url) {
        return preferXtreamLiveHlsUrl(url);
    }

    function getXtreamInfoUrl(item) {
        var config = getXtreamConfigForItem(item);
        var action;

        if (!config || !item.xtreamId || (item.section !== "movies" && item.section !== "series")) {
            return "";
        }

        if (item.section === "series" && item.url && !item.seriesXtreamId && !item.isSeriesContainer) {
            return "";
        }

        action = item.section === "series" ? "get_series_info&series_id=" : "get_vod_info&vod_id=";
        return config.baseUrl + "/player_api.php?username=" + encodeURIComponent(config.username) +
            "&password=" + encodeURIComponent(config.password) +
            "&action=" + action + encodeURIComponent(item.seriesXtreamId || item.xtreamId);
    }

    function getXtreamConfigForItem(item) {
        var config = getXtreamConfig();
        var match;

        if (item && item.url) {
            match = String(item.url).match(/^(https?:\/\/[^/]+)\/(?:movie|series|live)\/([^/]+)\/([^/]+)\//i);
            if (match) {
                return {
                    baseUrl: normalizeIptvBaseUrl(match[1]),
                    username: decodeURIComponent(match[2]),
                    password: decodeURIComponent(match[3])
                };
            }
        }

        return config;
    }

    function normalizeIptvBaseUrl(url) {
        var normalized = String(url || "").replace(/\/+$/, "");
        var parts = normalized.match(/^(https?:\/\/[^/:]+)(?::80)?$/i);

        if (parts) {
            return parts[1];
        }

        return normalized.replace(/:80(\/|$)/i, "$1");
    }

    function getXtreamConfig() {
        var match = IPTV_PLAYLIST_URL.match(/^(https?:\/\/[^/]+)\/get\.php\?([^#]+)/i);
        var params;
        var playlist;

        if (match) {
            params = parseQueryString(match[2]);
            if (params.username && params.password) {
                return {
                    baseUrl: normalizeIptvBaseUrl(match[1]),
                    username: params.username,
                    password: params.password,
                    output: normalizeXtreamContainerExtension(params.output || "m3u8")
                };
            }
        }

        playlist = state.activation && state.activation.playlist;
        if (playlist && playlist.type === "xtream" && playlist.server_url && playlist.username && playlist.password) {
            return {
                baseUrl: normalizeIptvBaseUrl(playlist.server_url),
                username: playlist.username,
                password: playlist.password,
                output: normalizeXtreamContainerExtension(playlist.output || "m3u8")
            };
        }

        return null;
    }

    function parseQueryString(query) {
        var params = {};

        query.split("&").forEach(function (part) {
            var pair = part.split("=");
            var key = decodeURIComponent(pair[0] || "");
            var value = decodeURIComponent(pair.slice(1).join("=") || "");

            if (key) {
                params[key] = value;
            }
        });

        return params;
    }

    function getXtreamStreamId(url) {
        var cleanUrl = url.split("?")[0];
        var fileName = cleanUrl.slice(cleanUrl.lastIndexOf("/") + 1);
        var id = fileName.replace(/\.[^.]+$/, "");

        return /^\d+$/.test(id) ? id : "";
    }

    function detectSection(url, info) {
        var text = String(info.group || "").toLowerCase();
        var cleanUrl = url.split("?")[0].toLowerCase();

        if (cleanUrl.indexOf("/live/") !== -1) {
            return "live";
        }

        if (cleanUrl.indexOf("/series/") !== -1) {
            return "series";
        }

        if (cleanUrl.indexOf("/movie/") !== -1) {
            return "movies";
        }

        if (text.indexOf("/series/") !== -1 || text.indexOf("series") !== -1 || text.indexOf("Ù…Ø³Ù„Ø³Ù„") !== -1) {
            return "series";
        }

        if (text.indexOf("/movie/") !== -1 || text.indexOf("movie") !== -1 || text.indexOf("film") !== -1 || text.indexOf("vod") !== -1 || text.indexOf("Ø§ÙÙ„Ø§Ù…") !== -1 || text.indexOf("Ø£ÙÙ„Ø§Ù…") !== -1) {
            return "movies";
        }

        return "live";
    }

    function getStreamType(url) {
        var cleanUrl = url.split("?")[0].toLowerCase();

        if (cleanUrl.indexOf(".m3u8") !== -1) {
            return "hls";
        }

        if (cleanUrl.indexOf(".mp4") !== -1) {
            return "mp4";
        }

        if (/\.(mkv|avi|mov|m4v|webm)(?:$|\?)/i.test(cleanUrl)) {
            return cleanUrl.slice(cleanUrl.lastIndexOf(".") + 1).split("?")[0];
        }

        if (cleanUrl.indexOf(".ts") !== -1) {
            return "mpegts";
        }

        return "iptv";
    }

    function handleKeyDown(event) {
        var keyCode = event.keyCode || event.which;

        if (state.pinDialogOpen) {
            handlePinDialogKeys(keyCode, event);
            return;
        }

        if (state.pendingResume) {
            handleResumeKeys(keyCode, event);
            return;
        }

        if (isTextEditingTarget(event.target) && !isNavigationKey(keyCode)) {
            return;
        }

        if (state.screen === "home") {
            handleHomeKeys(keyCode, event);
        } else if (state.screen === "playlist" || state.screen === "account" || state.screen === "settings" || state.screen === "history") {
            handleManagementKeys(keyCode, event);
        } else if (state.screen === "library") {
            handleLibraryKeys(keyCode, event);
        } else if (state.screen === "detail") {
            handleDetailKeys(keyCode, event);
        } else {
            handlePlayerKeys(keyCode, event);
        }
    }

    function handleResumeKeys(keyCode, event) {
        switch (keyCode) {
            case KEY_CODES.LEFT:
            case KEY_CODES.RIGHT:
                event.preventDefault();
                if (document.activeElement === elements.resumeContinueButton) {
                    elements.resumeRestartButton.focus();
                } else {
                    elements.resumeContinueButton.focus();
                }
                break;
            case KEY_CODES.ENTER:
                event.preventDefault();
                runPendingResume(document.activeElement !== elements.resumeRestartButton);
                break;
            case KEY_CODES.BACK:
            case KEY_CODES.RETURN:
                event.preventDefault();
                elements.resumeDialog.hidden = true;
                state.pendingResume = null;
                break;
        }
    }

    function isTextEditingTarget(target) {
        return target && (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT");
    }

    function isNavigationKey(keyCode) {
        return keyCode === KEY_CODES.LEFT ||
            keyCode === KEY_CODES.RIGHT ||
            keyCode === KEY_CODES.UP ||
            keyCode === KEY_CODES.DOWN ||
            keyCode === KEY_CODES.ENTER ||
            keyCode === KEY_CODES.BACK ||
            keyCode === KEY_CODES.RETURN;
    }

    function handleHomeKeys(keyCode, event) {
        switch (keyCode) {
            case KEY_CODES.LEFT:
                event.preventDefault();
                moveHomeFocus(-1, 0);
                break;
            case KEY_CODES.RIGHT:
                event.preventDefault();
                moveHomeFocus(1, 0);
                break;
            case KEY_CODES.UP:
                event.preventDefault();
                moveHomeFocus(0, -1);
                break;
            case KEY_CODES.DOWN:
                event.preventDefault();
                moveHomeFocus(0, 1);
                break;
            case KEY_CODES.ENTER:
                event.preventDefault();
                activateHomeFocus();
                break;
            case KEY_CODES.BACK:
            case KEY_CODES.RETURN:
                event.preventDefault();
                exitApp();
                break;
        }
    }

    function handleLibraryKeys(keyCode, event) {
        if (state.libraryPane === "keyboard" && handleVirtualKeyboardKeys(keyCode, event)) {
            return;
        }

        if (state.activeSection === "live" &&
                elements.libraryScreen &&
                elements.libraryScreen.classList.contains("is-live-preview-fullscreen") &&
                (keyCode === KEY_CODES.BACK || keyCode === KEY_CODES.RETURN)) {
            event.preventDefault();
            closeLivePreviewFullscreen();
            return;
        }

        switch (keyCode) {
            case KEY_CODES.LEFT:
                event.preventDefault();
                if (state.libraryPane === "actions") {
                    moveLibraryActionFocus(-1);
                    break;
                }
                if (isVodSection()) {
                    moveVodLibraryHorizontal(-1);
                    break;
                }
                if (state.libraryPane === "search") {
                    setLibraryPane("categories");
                } else {
                    setLibraryPane("categories");
                }
                break;
            case KEY_CODES.RIGHT:
                event.preventDefault();
                if (state.libraryPane === "actions") {
                    moveLibraryActionFocus(1);
                    break;
                }
                if (isVodSection()) {
                    moveVodLibraryHorizontal(1);
                    break;
                }
                if (state.libraryPane === "search") {
                    setLibraryPane("items");
                } else if (visibleItems.length) {
                    setLibraryPane("items");
                }
                break;
            case KEY_CODES.UP:
                event.preventDefault();
                if (isVodSection() && state.libraryPane === "items") {
                    if (state.itemIndex <= 0 && getLibraryActionControls().length) {
                        setLibraryPane("actions");
                        break;
                    }
                    scrollVodLibraryItems(-1);
                    break;
                }
                moveLibraryFocus(-1);
                break;
            case KEY_CODES.DOWN:
                event.preventDefault();
                if (isVodSection() && state.libraryPane === "items") {
                    scrollVodLibraryItems(1);
                    break;
                }
                moveLibraryFocus(1);
                break;
            case KEY_CODES.ENTER:
                event.preventDefault();
                if (state.libraryPane === "back") {
                    goHome();
                } else if (state.libraryPane === "actions") {
                    activateLibraryAction();
                } else if (state.libraryPane === "search") {
                    openVirtualKeyboard();
                } else if (state.libraryPane === "categories") {
                    setCategory(state.categoryIndex);
                    if (visibleItems.length) {
                        setLibraryPane("items");
                    }
                } else if (state.activeSection === "live") {
                    handleLiveItemOk();
                } else {
                    openItem(state.itemIndex);
                }
                break;
            case KEY_CODES.BACK:
            case KEY_CODES.RETURN:
                event.preventDefault();
                goHome();
                break;
        }
    }

    function handleLiveItemOk() {
        var item = getCurrentLiveItem();
        var key = getLivePreviewKey(item);

        if (!item || !key) {
            return;
        }

        verifyChannelUnlock(item, function (unlocked) {
            if (!unlocked) {
                return;
            }

            if (key !== state.liveOkItemKey) {
                state.liveOkItemKey = key;
                state.liveOkPressCount = 0;
            }

            if (state.liveOkPressCount < 1) {
                state.liveOkPressCount = 1;
                showLivePreviewMessage("");
                startLivePreview(item, true);
                return;
            }

            openLivePreviewFullscreen();
        });
    }

    function handleManagementKeys(keyCode, event) {
        if (state.screen === "settings" && handleSettingsKeys(keyCode, event)) {
            return;
        }

        if (state.screen === "playlist" && handlePlaylistManagementKeys(keyCode, event)) {
            return;
        }

        switch (keyCode) {
            case KEY_CODES.LEFT:
            case KEY_CODES.UP:
                event.preventDefault();
                moveManagementFocus(-1);
                break;
            case KEY_CODES.RIGHT:
            case KEY_CODES.DOWN:
                event.preventDefault();
                moveManagementFocus(1);
                break;
            case KEY_CODES.ENTER:
                if (activateManagementFocus()) {
                    event.preventDefault();
                }
                break;
            case KEY_CODES.BACK:
            case KEY_CODES.RETURN:
                event.preventDefault();
                goHome();
                break;
        }
    }

    function handleSettingsKeys(keyCode, event) {
        var active = document.activeElement;
        var navButtons = getSettingsNavButtons();
        var navIndex = navButtons.indexOf(active);

        if (navIndex >= 0) {
            switch (keyCode) {
                case KEY_CODES.UP:
                    event.preventDefault();
                    focusSettingsNavButton(navIndex - 1);
                    return true;
                case KEY_CODES.DOWN:
                    event.preventDefault();
                    focusSettingsNavButton(navIndex + 1);
                    return true;
                case KEY_CODES.RIGHT:
                    event.preventDefault();
                    focusFirstActiveSettingsOption();
                    return true;
                case KEY_CODES.ENTER:
                    event.preventDefault();
                    setSettingsPanel(active.getAttribute("data-settings-panel"));
                    syncSettingsButtons();
                    syncManagementFocus();
                    return true;
                case KEY_CODES.BACK:
                case KEY_CODES.RETURN:
                    event.preventDefault();
                    goHome();
                    return true;
            }
        }

        if (keyCode === KEY_CODES.LEFT) {
            event.preventDefault();
            focusActiveSettingsNavButton();
            return true;
        }

        return false;
    }

    function handlePlaylistManagementKeys(keyCode, event) {
        if (document.activeElement === elements.playlistType &&
                (keyCode === KEY_CODES.ENTER || keyCode === KEY_CODES.LEFT || keyCode === KEY_CODES.RIGHT)) {
            event.preventDefault();
            cyclePlaylistType(keyCode === KEY_CODES.LEFT ? -1 : 1);
            return true;
        }

        return false;
    }

    function getSettingsNavButtons() {
        return Array.prototype.slice.call(elements.settingsForm.querySelectorAll(".settings-nav-button"));
    }

    function focusSettingsNavButton(index) {
        var navButtons = getSettingsNavButtons();
        var controls = getManagementFocusableControls();
        var target;

        if (!navButtons.length) {
            return;
        }

        index = Math.max(0, Math.min(navButtons.length - 1, index));
        target = navButtons[index];
        focusManagementControl(controls.indexOf(target), controls);
    }

    function focusActiveSettingsNavButton() {
        var navButtons = getSettingsNavButtons();
        var activeIndex = 0;

        navButtons.forEach(function (button, index) {
            if (button.classList.contains("is-active")) {
                activeIndex = index;
            }
        });

        focusSettingsNavButton(activeIndex);
    }

    function focusFirstActiveSettingsOption() {
        var panel = elements.settingsForm.querySelector(".settings-panel.is-active");
        var target = panel && panel.querySelector("button");
        var controls = getManagementFocusableControls();

        if (!target) {
            target = elements.settingsForm.querySelector(".settings-save-button");
        }

        if (target) {
            focusManagementControl(controls.indexOf(target), controls);
        }
    }

    function cyclePlaylistType(direction) {
        var options = Array.prototype.slice.call(elements.playlistType.options);
        var index = Math.max(0, elements.playlistType.selectedIndex);

        if (!options.length) {
            return;
        }

        index = (index + direction + options.length) % options.length;
        elements.playlistType.selectedIndex = index;
        renderPlaylistTypeFields();
        focusFirstManagementControl("#playlistType");
    }

    function getManagementScreenElement() {
        if (state.screen === "playlist") {
            return elements.playlistScreen;
        }
        if (state.screen === "account") {
            return elements.accountScreen;
        }
        if (state.screen === "settings") {
            return elements.settingsScreen;
        }
        if (state.screen === "history") {
            return elements.historyScreen;
        }

        return null;
    }

    function getManagementFocusableControls() {
        var screen = getManagementScreenElement();
        var selector = "button, input, select, textarea, [tabindex]:not([tabindex='-1'])";

        if (!screen) {
            return [];
        }

        return Array.prototype.slice.call(screen.querySelectorAll(selector)).filter(function (control) {
            return !!(control &&
                !control.disabled &&
                !control.hidden &&
                control.getClientRects().length);
        });
    }

    function getManagementFocusIndex(controls) {
        var active = document.activeElement;
        var index = controls.indexOf(active);

        if (index >= 0) {
            return index;
        }

        return Math.max(0, Math.min(controls.length - 1, state.managementFocusIndex || 0));
    }

    function moveManagementFocus(delta) {
        var controls = getManagementFocusableControls();
        var index;

        if (!controls.length) {
            return;
        }

        index = getManagementFocusIndex(controls);
        focusManagementControl(index + delta, controls);
    }

    function focusFirstManagementControl(preferredSelector) {
        var controls = getManagementFocusableControls();
        var preferred = preferredSelector ? getManagementScreenElement().querySelector(preferredSelector) : null;
        var preferredIndex = preferred ? controls.indexOf(preferred) : -1;

        if (!controls.length) {
            return;
        }

        focusManagementControl(preferredIndex >= 0 ? preferredIndex : 0, controls);
    }

    function focusManagementControl(index, controls) {
        var target;

        controls = controls || getManagementFocusableControls();
        if (!controls.length) {
            return;
        }

        index = Math.max(0, Math.min(controls.length - 1, index));
        state.managementFocusIndex = index;
        target = controls[index];

        controls.forEach(function (control) {
            control.classList.remove("is-focused");
        });
        target.classList.add("is-focused");
        target.focus();
        target.scrollIntoView({ block: "nearest", inline: "nearest" });
    }

    function syncManagementFocus() {
        var controls = getManagementFocusableControls();

        if (!controls.length) {
            return;
        }

        focusManagementControl(getManagementFocusIndex(controls), controls);
    }

    function activateManagementFocus() {
        var controls = getManagementFocusableControls();
        var index = getManagementFocusIndex(controls);
        var control = controls[index];
        var tagName;

        if (!control) {
            return false;
        }

        tagName = control.tagName;
        if (tagName === "INPUT" || tagName === "TEXTAREA" || tagName === "SELECT") {
            control.focus();
            return false;
        }

        if (typeof control.click === "function") {
            control.click();
            window.setTimeout(syncManagementFocus, 0);
            return true;
        }

        return false;
    }

    function handlePlayerKeys(keyCode, event) {
        showOverlay();

        switch (keyCode) {
            case KEY_CODES.LEFT:
                event.preventDefault();
                if (isPlayerToolPanelOpen()) {
                    movePlayerToolOptionByGrid(-1, 0);
                    break;
                }
                if (isPlayerToolFocused()) {
                    movePlayerToolFocus(-1);
                } else {
                    player.seekBy(-10);
                }
                break;
            case KEY_CODES.RIGHT:
                event.preventDefault();
                if (isPlayerToolPanelOpen()) {
                    movePlayerToolOptionByGrid(1, 0);
                    break;
                }
                if (isPlayerToolFocused()) {
                    movePlayerToolFocus(1);
                } else {
                    player.seekBy(10);
                }
                break;
            case KEY_CODES.UP:
                event.preventDefault();
                if (isPlayerToolPanelOpen()) {
                    movePlayerToolOptionByGrid(0, -1);
                    break;
                }
                movePlayerControlFocus(-1);
                break;
            case KEY_CODES.DOWN:
                event.preventDefault();
                if (isPlayerToolPanelOpen()) {
                    movePlayerToolOptionByGrid(0, 1);
                    break;
                }
                movePlayerControlFocus(1);
                break;
            case KEY_CODES.ENTER:
            case KEY_CODES.PLAY:
            case KEY_CODES.PAUSE:
            case KEY_CODES.PLAY_PAUSE:
            case KEY_CODES.MEDIA_PLAY_PAUSE:
                event.preventDefault();
                if (state.focusedControl === "back" && keyCode === KEY_CODES.ENTER) {
                    goBackFromPlayer();
                } else if (isPlayerToolPanelOpen() && keyCode === KEY_CODES.ENTER) {
                    applyFocusedPlayerToolOption();
                } else if (isPlayerToolFocused() && keyCode === KEY_CODES.ENTER) {
                    openPlayerTool(state.focusedControl);
                } else {
                    player.togglePlay();
                }
                break;
            case KEY_CODES.BACK:
            case KEY_CODES.RETURN:
                event.preventDefault();
                goBackFromPlayer();
                break;
        }
    }

    function handleDetailKeys(keyCode, event) {
        switch (keyCode) {
            case KEY_CODES.LEFT:
                event.preventDefault();
                moveDetailHorizontal(-1);
                break;
            case KEY_CODES.RIGHT:
                event.preventDefault();
                moveDetailHorizontal(1);
                break;
            case KEY_CODES.UP:
                event.preventDefault();
                moveDetailVertical(-1);
                break;
            case KEY_CODES.DOWN:
                event.preventDefault();
                moveDetailVertical(1);
                break;
            case KEY_CODES.ENTER:
                event.preventDefault();
                activateDetailFocus();
                break;
            case KEY_CODES.PLAY:
            case KEY_CODES.PLAY_PAUSE:
            case KEY_CODES.MEDIA_PLAY_PAUSE:
                event.preventDefault();
                playDetailItem();
                break;
            case KEY_CODES.BACK:
            case KEY_CODES.RETURN:
                event.preventDefault();
                goBackFromDetail();
                break;
        }
    }

    function openLibrary(sectionId) {
        state.screen = "library";
        state.activeSection = sectionId;
        state.categoryIndex = 0;
        state.itemIndex = 0;
        state.libraryPane = "categories";
        state.vodRenderLimit = VOD_RENDER_LIMIT;
        state.liveRenderLimit = LIVE_RENDER_LIMIT;
        showScreen("library");
        state.searchQuery = "";
        if (elements.librarySearchInput) {
            elements.librarySearchInput.value = "";
        }
        renderLibrary();
    }

    function openItem(index) {
        if (!visibleItems[index]) {
            return;
        }

        if (isVodSection()) {
            openDetail(visibleItems[index], "library");
            return;
        }

        stopLivePreview();
        playItem(visibleItems[index], "library");
    }

    function openDetail(item, returnScreen) {
        state.screen = "detail";
        state.detailReturnScreen = returnScreen || "library";
        state.detailItem = item;
        state.detailPane = "actions";
        state.detailActionIndex = 1;
        state.seasonIndex = 0;
        state.episodeIndex = 0;
        state.castIndex = 0;
        state.actorWorkIndex = 0;
        showScreen("detail");
        elements.detailScreen.scrollTop = 0;
        renderDetail(item);
        setDetailAction(state.detailActionIndex);
    }

    function renderDetail(item) {
        var meta;
        var cast;

        if (!item) {
            return;
        }

        meta = getRichDetailMeta(item);
        cast = getCastPeople(item);

        elements.detailBackdrop.style.cssText = getBackdropStyle(item);
        elements.detailPoster.style.cssText = getPosterStyle(item);
        elements.detailPoster.innerHTML = getPosterFallbackHtml(item);
        elements.detailEyebrow.textContent = getSection(item.section).subtitle;
        elements.detailTitle.textContent = item.title;
        elements.detailDescription.textContent = getReadableStory(item);
        elements.detailMeta.innerHTML = meta.map(function (value) {
            return '<span class="' + escapeHtml(value.className || "") + '">' + escapeHtml(value.text) + '</span>';
        }).join("");
        elements.castList.innerHTML = cast.map(function (person, index) {
            var name = person.name || String(person || "");
            var initials = person.image ? "" : escapeHtml(getInitials(name));
            var selectedClass = state.detailPane === "actorWorks" && index === state.castIndex ? " is-selected" : "";
            return '<button class="cast-card' + selectedClass + '" type="button" data-index="' + index + '">' +
                '<div class="cast-photo cast-color-' + (index % 6) + '" style="' + getCastPhotoStyle(item, person, index) + '">' + initials + '</div>' +
                '<span>' + escapeHtml(name) + '</span>' +
            '</button>';
        }).join("");

        renderSeriesRows(item);
        actorSearchToken += 1;
        renderActorWorks([]);
        loadDetailMetadata(item);
    }

    function playDetailItem() {
        if (!state.detailItem) {
            return;
        }

        if (state.detailItem.section === "series") {
            if (detailEpisodes.length) {
                playEpisode(state.episodeIndex || 0);
            } else {
                showDetailActionMessage("Ø¬Ø§Ø±ÙŠ ØªØ­Ù…ÙŠÙ„ Ø§Ù„Ø­Ù„Ù‚Ø§Øª. Ø§Ø®ØªØ± Ø§Ù„Ø­Ù„Ù‚Ø© Ø¨Ø¹Ø¯ Ø¸Ù‡ÙˆØ± Ø§Ù„Ù…ÙˆØ§Ø³Ù….");
            }
            return;
        }

        playItem(state.detailItem, "detail");
    }

    function playTrailer() {
        showDetailActionMessage("Ø§Ù„Ù…Ù‚Ø·Ø¹ Ø§Ù„Ø§Ø¹Ù„Ø§Ù†ÙŠ ØºÙŠØ± Ù…ØªÙˆÙØ± Ù„Ù‡Ø°Ø§ Ø§Ù„Ø¹Ù…Ù„.");
    }

    function addDetailFavorite() {
        var key;

        if (!state.detailItem) {
            return;
        }

        key = getLibraryWorkKey(state.detailItem);
        try {
            window.localStorage.setItem("nexoWishlist:" + key, JSON.stringify({
                title: state.detailItem.title,
                section: state.detailItem.section,
                addedAt: Date.now()
            }));
        } catch (error) {
            console.log("Could not save wishlist item:", error.message);
        }

        showDetailActionMessage("\u062a\u0645\u062a \u0627\u0644\u0625\u0636\u0627\u0641\u0629 \u0625\u0644\u0649 \u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0631\u063a\u0628\u0627\u062a \u2665");
    }

    function removeFromWatchlist() {
        showDetailActionMessage("ØªÙ…Øª Ø§Ù„Ø¥Ø²Ø§Ù„Ø© Ù…Ù† Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ù…Ø´Ø§Ù‡Ø¯Ø©.");
    }

    function showDetailActionMessage(message) {
        elements.detailActionMessage.textContent = message;
        elements.detailActionMessage.hidden = false;
    }

    function renderSeriesRows(item) {
        detailSeasons = item && item.section === "series" ? getSeriesSeasons(item) : [];
        elements.seasonSection.hidden = !detailSeasons.length;

        if (!detailSeasons.length) {
            elements.seasonList.innerHTML = "";
            elements.episodeList.innerHTML = "";
            detailEpisodes = [];
            return;
        }

        if (state.seasonIndex >= detailSeasons.length) {
            state.seasonIndex = 0;
        }

        elements.seasonList.innerHTML = detailSeasons.map(function (season, index) {
            return '<button class="season-button' + (index === state.seasonIndex ? " is-focused" : "") + '" type="button" data-index="' + index + '">Ù…ÙˆØ³Ù… ' + escapeHtml(season.number) + '</button>';
        }).join("");

        renderEpisodes();
    }

    function renderSeriesRows(item) {
        detailSeasons = item && item.section === "series" ? getSeriesSeasons(item) : [];
        elements.seasonSection.hidden = !detailSeasons.length;

        if (!detailSeasons.length) {
            elements.seasonList.innerHTML = "";
            elements.episodeList.innerHTML = "";
            detailEpisodes = [];
            return;
        }

        if (state.seasonIndex >= detailSeasons.length) {
            state.seasonIndex = 0;
        }

        elements.seasonList.innerHTML = detailSeasons.map(function (season, index) {
            return '<button class="season-button' + (index === state.seasonIndex ? " is-focused" : "") + '" type="button" data-index="' + index + '">' + escapeHtml(getLanguageLabels(state.language).season) + ' ' + escapeHtml(season.number) + '</button>';
        }).join("");

        renderEpisodes();
    }

    function renderEpisodes() {
        var season = detailSeasons[state.seasonIndex];

        detailEpisodes = season ? season.episodes : [];
        elements.episodeList.innerHTML = detailEpisodes.map(function (episode, index) {
            return '<button class="episode-card" type="button" data-index="' + index + '">' +
                '<span class="episode-thumb" style="' + getPosterStyle(episode) + '">' + getPosterFallbackHtml(episode) + '</span>' +
                '<span class="episode-title">S' + escapeHtml(episode.seasonNumber || season.number) + ' E' + escapeHtml(episode.episodeNumber || (index + 1)) + '</span>' +
            '</button>';
        }).join("");
    }

    function setDetailSeason(index) {
        state.seasonIndex = Math.max(0, Math.min(detailSeasons.length - 1, index));
        state.episodeIndex = 0;
        state.detailPane = "seasons";
        renderSeriesRows(state.detailItem);
        focusDetailPane();
    }

    function playEpisode(index) {
        var episode = detailEpisodes[index];

        if (episode) {
            state.episodeIndex = index;
            ensureSeriesPlaybackContext(episode, state.detailItem);
            playItem(episode, "detail");
        }
    }

    function ensureSeriesPlaybackContext(item, parentItem) {
        if (!item || item.section !== "series" || item.seriesXtreamId) {
            return;
        }

        if (parentItem && parentItem.xtreamId && parentItem.isSeriesContainer) {
            item.seriesXtreamId = parentItem.xtreamId;
        }
    }

    function getSeriesSeasons(item) {
        if (item.seriesSeasons && item.seriesSeasons.length) {
            return item.seriesSeasons;
        }

        return buildLocalSeriesSeasons(item);
    }

    function buildLocalSeriesSeasons(item) {
        var key = normalizeSeriesKey(item.seriesTitle || item.title);
        var map = {};

        allItems.forEach(function (candidate) {
            var candidateKey;
            var seasonNumber;
            var episodeNumber;

            if (candidate.section !== "series") {
                return;
            }

            candidateKey = normalizeSeriesKey(candidate.seriesTitle || candidate.title);
            if (candidateKey !== key) {
                return;
            }

            seasonNumber = candidate.seasonNumber || 1;
            episodeNumber = candidate.episodeNumber || 1;
            if (!map[seasonNumber]) {
                map[seasonNumber] = [];
            }
            candidate.seasonNumber = seasonNumber;
            candidate.episodeNumber = episodeNumber;
            if (item.xtreamId && item.isSeriesContainer) {
                candidate.seriesXtreamId = item.xtreamId;
            }
            map[seasonNumber].push(candidate);
        });

        return Object.keys(map).sort(numberSort).map(function (seasonNumber) {
            return {
                number: seasonNumber,
                episodes: map[seasonNumber].sort(function (a, b) {
                    return (a.episodeNumber || 0) - (b.episodeNumber || 0);
                })
            };
        });
    }

    function normalizeSeriesKey(value) {
        return String(value || "").toLowerCase().replace(/\bS\d+\s*E\d+\b/ig, "").replace(/\s+/g, " ").trim();
    }

    function numberSort(a, b) {
        return parseInt(a, 10) - parseInt(b, 10);
    }

    function getUniqueVodItems() {
        var seen = {};
        var items = [];

        allItems.forEach(function (item) {
            var key;

            if (item.section !== "movies" && item.section !== "series") {
                return;
            }

            key = getLibraryWorkKey(item);
            if (!seen[key]) {
                seen[key] = true;
                items.push(item);
            }
        });

        return items;
    }

    function getLibraryWorkKey(item) {
        var title = item.section === "series" ? (item.seriesTitle || item.title) : item.title;
        return item.section + ":" + normalizeWorkTitle(title || item.title || item.url);
    }

    function normalizeWorkTitle(value) {
        return normalizeSearchText(value)
            .replace(/\bs\d{1,2}\s*e\d{1,3}\b/g, "")
            .replace(/\b\d{1,2}x\d{1,3}\b/g, "")
            .replace(/\b(19|20)\d{2}\b/g, "")
            .replace(/\([^)]*\)/g, "")
            .replace(/Ø¬ÙˆØ¯Ù‡ Ø§Ù‚Ù„|Ø¬ÙˆØ¯Ù‡ Ø¹Ø§Ù„ÙŠÙ‡|Ù…Ø¯Ø¨Ù„Ø¬|Ù…ØªØ±Ø¬Ù…/g, "")
            .replace(/\b(4k|uhd|fhd|hd|sd|mp4|hevc|web dl|bluray|cam)\b/g, "")
            .replace(/\s+/g, " ")
            .trim();
    }

    function itemHasActor(item, normalizedActor) {
        return getReadableCastItems(item).some(function (name) {
            var normalizedName = normalizeSearchText(name);
            return normalizedName === normalizedActor;
        });
    }

    function normalizeSearchText(value) {
        return String(value || "")
            .toLowerCase()
            .replace(/[\u064b-\u065f\u0670]/g, "")
            .replace(/[Ø¥Ø£Ø¢Ø§]/g, "Ø§")
            .replace(/[Ù‰ÙŠ]/g, "ÙŠ")
            .replace(/Ø©/g, "Ù‡")
            .replace(/[^\w\u0600-\u06ff]+/g, " ")
            .replace(/\s+/g, " ")
            .trim();
    }

    function normalizeWorkTitle(value) {
        return normalizeSearchText(value)
            .replace(/\bs\d{1,2}\s*e\d{1,3}\b/g, "")
            .replace(/\b\d{1,2}x\d{1,3}\b/g, "")
            .replace(/\b(19|20)\d{2}\b/g, "")
            .replace(/\([^)]*\)/g, "")
            .replace(/Ø¬ÙˆØ¯Ù‡ Ø§Ù‚Ù„|Ø¬ÙˆØ¯Ù‡ Ø¹Ø§Ù„ÙŠÙ‡|Ù…Ø¯Ø¨Ù„Ø¬|Ù…ØªØ±Ø¬Ù…/g, "")
            .replace(/\b(4k|uhd|fhd|hd|sd|mp4|hevc|web dl|bluray|cam)\b/g, "")
            .replace(/\s+/g, " ")
            .trim();
    }

    function normalizeSearchText(value) {
        return String(value || "")
            .toLowerCase()
            .replace(/[\u064b-\u065f\u0670]/g, "")
            .replace(/[\u0625\u0623\u0622\u0627]/g, "\u0627")
            .replace(/[\u0649\u064a]/g, "\u064a")
            .replace(/\u0629/g, "\u0647")
            .replace(/[^\w\u0600-\u06ff]+/g, " ")
            .replace(/\s+/g, " ")
            .trim();
    }

    function renderActorWorks(items) {
        actorWorks = items || [];
        elements.actorWorksSection.hidden = !actorWorks.length;
        elements.actorWorksList.innerHTML = actorWorks.map(function (item, index) {
            return '<button class="actor-work-card" type="button" data-index="' + index + '">' +
                '<span class="actor-work-poster" style="' + getPosterStyle(item) + '">' + getPosterFallbackHtml(item) + '</span>' +
                '<span>' + escapeHtml(item.title) + '</span>' +
            '</button>';
        }).join("");
    }

    function renderActorWorksMessage(message) {
        actorWorks = [];
        elements.actorWorksSection.hidden = false;
        elements.actorWorksList.innerHTML = '<div class="actor-work-empty">' + escapeHtml(message) + '</div>';
    }

    function openActorWorks(index) {
        var cast = getCastPeople(state.detailItem);
        var actorPerson = cast[index];
        var actor = actorPerson && actorPerson.name;

        if (!actor || actor.indexOf("ØºÙŠØ± Ù…ØªÙˆÙØ±") !== -1) {
            showDetailActionMessage("Ù„Ø§ ØªÙˆØ¬Ø¯ Ø£Ø¹Ù…Ø§Ù„ Ù…Ø±ØªØ¨Ø·Ø© Ø¨Ù‡Ø°Ø§ Ø§Ù„Ù…Ù…Ø«Ù„ ÙÙŠ Ø§Ù„Ù…ÙƒØªØ¨Ø©.");
            return;
        }

        elements.actorWorksTitle.textContent = "Ø£Ø¹Ù…Ø§Ù„ " + actor;
        state.castIndex = index;
        state.actorWorkIndex = 0;
        state.detailPane = "actorWorks";
        renderActorWorksMessage("Ø¬Ø§Ø±ÙŠ Ø§Ù„Ø¨Ø­Ø« Ø¹Ù† Ø£Ø¹Ù…Ø§Ù„ " + actor + "...");
        focusDetailPane();
        findActorWorks(actor, actorPerson && actorPerson.tmdbPersonId);
    }

    function openActorWorks(index) {
        var cast = getCastPeople(state.detailItem);
        var actorPerson = cast[index];
        var actor = actorPerson && actorPerson.name;
        var labels = getLanguageLabels(state.language);

        if (!actor || actor.indexOf("\u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631") !== -1) {
            showDetailActionMessage(labels.noActorWorks);
            return;
        }

        elements.actorWorksTitle.textContent = labels.actorWorksOf + actor;
        state.castIndex = index;
        state.actorWorkIndex = 0;
        state.detailPane = "actorWorks";
        renderActorWorksMessage(labels.loadingActorWorks + actor + "...");
        focusDetailPane();
        findActorWorks(actor, actorPerson && actorPerson.tmdbPersonId);
    }

    function findActorWorks(actor, tmdbPersonId) {
        var token = actorSearchToken + 1;
        var normalizedActor = normalizeSearchText(actor);
        var candidates = getUniqueVodItems();
        var matches = [];
        var seen = {};
        var cursor = 0;
        var active = 0;
        var maxActive = 5;

        actorSearchToken = token;

        function addMatch(item) {
            var key = getLibraryWorkKey(item);

            if (seen[key] || !itemHasActor(item, normalizedActor)) {
                return;
            }

            seen[key] = true;
            matches.push(item);
            renderActorWorks(matches.slice(0, 96));
        }

        candidates.forEach(addMatch);
        if (tmdbPersonId) {
            loadTmdbActorWorks(tmdbPersonId, token, function (titles) {
                if (token !== actorSearchToken) {
                    return;
                }
                addTmdbActorTitleMatches(titles, candidates, seen, matches);
                if (matches.length) {
                    renderActorWorks(matches.slice(0, 96));
                }
            });
        }

        function finishIfDone() {
            if (token !== actorSearchToken || active || cursor < candidates.length) {
                return;
            }

            if (!matches.length) {
                renderActorWorksMessage("Ù„Ø§ ØªÙˆØ¬Ø¯ Ø£Ø¹Ù…Ø§Ù„ Ø£Ø®Ø±Ù‰ Ù„Ù‡Ø°Ø§ Ø§Ù„Ù…Ù…Ø«Ù„ ÙÙŠ Ù…ÙƒØªØ¨Ø© Ø§Ù„Ø£ÙÙ„Ø§Ù… Ø£Ùˆ Ø§Ù„Ù…Ø³Ù„Ø³Ù„Ø§Øª.");
            }
        }

        function pump() {
            if (token !== actorSearchToken) {
                return;
            }

            while (active < maxActive && cursor < candidates.length) {
                var item = candidates[cursor];
                cursor += 1;

                if (item.metadataLoaded || !getXtreamInfoUrl(item)) {
                    continue;
                }

                fetchActorCandidate(item);
            }

            finishIfDone();
        }

        function fetchActorCandidate(candidate) {
            active += 1;
            fetchItemMetadata(candidate, function () {
                active -= 1;
                addMatch(candidate);
                pump();
                finishIfDone();
            });
        }

        pump();
    }

    function addTmdbActorTitleMatches(titles, candidates, seen, matches) {
        var normalizedTitles = (titles || []).map(function (title) {
            return normalizeWorkTitle(title);
        }).filter(Boolean);

        candidates.forEach(function (item) {
            var key = getLibraryWorkKey(item);
            var itemTitle = normalizeWorkTitle(item.seriesTitle || item.title);

            if (seen[key] || !itemTitle) {
                return;
            }

            normalizedTitles.some(function (title) {
                if (title === itemTitle) {
                    seen[key] = true;
                    matches.push(item);
                    return true;
                }
                return false;
            });
        });
    }

    function openActorWork(index) {
        if (actorWorks[index]) {
            openDetail(actorWorks[index]);
        }
    }

    function loadDetailMetadata(item) {
        var url = getXtreamInfoUrl(item);
        var cacheKey = item.section + ":" + (item.xtreamId || item.url);

        if (!url || item.metadataLoaded) {
            loadTmdbCastImages(item);
            return;
        }

        if (detailMetadataCache[cacheKey]) {
            applyDetailMetadata(item, detailMetadataCache[cacheKey]);
            return;
        }

        item.metadataLoading = true;
        requestIptvJson(url, function (error, data) {
            item.metadataLoading = false;
            if (error || state.detailItem !== item) {
                if (error) {
                    console.log("Could not load detail metadata:", error.message);
                    loadTmdbCastImages(item);
                }
                return;
            }

            detailMetadataCache[cacheKey] = data;
            applyDetailMetadata(item, data);
        });
    }

    function fetchItemMetadata(item, callback) {
        var url = getXtreamInfoUrl(item);
        var cacheKey = item.section + ":" + (item.xtreamId || item.url);
        var wantsSubtitles = !item.subtitles || !item.subtitles.length;

        if (!url) {
            callback();
            return;
        }

        if (item.metadataLoaded && !wantsSubtitles) {
            callback();
            return;
        }

        if (detailMetadataCache[cacheKey]) {
            mergeItemMetadata(item, detailMetadataCache[cacheKey]);
            if (!wantsSubtitles || (item.subtitles && item.subtitles.length)) {
                callback();
                return;
            }
        }

        requestIptvJson(url, function (error, data) {
            if (!error && data) {
                detailMetadataCache[cacheKey] = data;
                mergeItemMetadata(item, data);
            } else {
                if (error) {
                    console.log("Could not load playback metadata:", item.title, error.message);
                }
                item.metadataLoaded = true;
            }

            callback();
        });
    }

    function needsSubtitleMetadata(item) {
        return !!(getXtreamInfoUrl(item) && (!item.metadataLoaded || !item.subtitles || !item.subtitles.length));
    }

    function needsRemoteSubtitles(item) {
        return !!(item && (item.section === "movies" || item.section === "series") &&
            (!item.subtitles || !item.subtitles.length));
    }

    function ensurePlaybackSubtitles(item, callback) {
        function afterMetadata() {
            if (!needsRemoteSubtitles(item)) {
                callback();
                return;
            }

            fetchRemoteSubtitles(item, callback);
        }

        if (needsSubtitleMetadata(item)) {
            fetchItemMetadata(item, afterMetadata);
            return;
        }

        afterMetadata();
    }

    function pickPreferredSubtitle(subtitles) {
        var list = subtitles || [];
        var i;
        var subtitle;

        for (i = 0; i < list.length; i += 1) {
            subtitle = list[i];
            if (subtitle.language === "ar" || /arab/i.test(subtitle.label || "")) {
                return subtitle;
            }
        }

        return list[0] || null;
    }

    function applyLoadedSubtitlesToPlayer(item) {
        var preferred;

        if (!item || state.playingItem !== item || !item.subtitles || !item.subtitles.length) {
            return;
        }

        preferred = pickPreferredSubtitle(item.subtitles);
        console.log("[subtitles] Applying", item.subtitles.length, "track(s) to player for", item.title);

        if (player.loadMode === "avplay" && player.attachAvplayExternalSubtitle) {
            prepareSubtitleTracks(item);
            player.attachAvplayExternalSubtitle(preferred, item.subtitles, function (success) {
                if (!success) {
                    console.log("[subtitles] AVPlay subtitle attach failed; keeping AVPlay playback active:", item.title);
                }
                refreshPlayerOptions();
                autoSelectFirstSubtitleTrack();
            });
            return;
        }

        prepareSubtitleTracks(item);
        refreshPlayerOptions();
        autoSelectFirstSubtitleTrack();
    }

    function fetchRemoteSubtitles(item, callback) {
        var mediaType = item.section === "series" ? "tv" : "movie";
        var openSubtitlesType = item.section === "series" ? "series" : "movie";

        if (!needsRemoteSubtitles(item)) {
            callback();
            return;
        }

        function loadFromImdb(imdbId) {
            if (!imdbId) {
                searchOpenSubtitlesByTitle(item, openSubtitlesType, callback);
                return;
            }

            requestExternalText("https://opensubtitles-v3.strem.io/subtitles/" + openSubtitlesType + "/" + encodeURIComponent(buildOpenSubtitlesId(item, imdbId)) + ".json", function (error, text) {
                var parsed;
                var subtitles;

                if (error) {
                    console.log("[subtitles] OpenSubtitles lookup failed:", item.title, error.message);
                    searchOpenSubtitlesByTitle(item, openSubtitlesType, callback);
                    return;
                }

                try {
                    parsed = JSON.parse(text);
                } catch (parseError) {
                    searchOpenSubtitlesByTitle(item, openSubtitlesType, callback);
                    return;
                }

                subtitles = parseOpenSubtitlesResponse(parsed);
                if (subtitles.length) {
                    item.subtitles = mergeSubtitleLists(item.subtitles, subtitles);
                    console.log("[subtitles] OpenSubtitles tracks:", item.title, subtitles.length);
                    callback();
                    return;
                }

                searchOpenSubtitlesByTitle(item, openSubtitlesType, callback);
            });
        }

        if (!item.tmdbId) {
            resolveTmdbIdForItem(item, function (resolvedId) {
                if (resolvedId) {
                    item.tmdbId = resolvedId;
                }
                if (!item.tmdbId) {
                    searchOpenSubtitlesByTitle(item, openSubtitlesType, callback);
                    return;
                }
                resolveImdbIdForItem(item, mediaType, loadFromImdb);
            });
            return;
        }

        resolveImdbIdForItem(item, mediaType, loadFromImdb);
    }

    function buildOpenSubtitlesId(item, imdbId) {
        var season;
        var episode;

        if (!item || item.section !== "series") {
            return imdbId || "";
        }

        season = parseInt(item.seasonNumber || item.season || 1, 10);
        episode = parseInt(item.episodeNumber || item.episode || 1, 10);

        if (!season || season < 1) {
            season = 1;
        }
        if (!episode || episode < 1) {
            episode = 1;
        }

        return (imdbId || "") + ":" + season + ":" + episode;
    }

    function resolveImdbIdFromTitle(item, callback) {
        var query = normalizeDisplayTitle(item.seriesTitle || item.title).replace(/\(\d{4}\)\s*$/, "").trim();
        var yearMatch = String(item.title || "").match(/\((\d{4})\)/);
        var year = item.year || (yearMatch ? yearMatch[1] : "");
        var searchQuery;

        if (!query) {
            callback("");
            return;
        }

        resolveImdbIdFromCinemetaSearch(item, query, year, function (cinemetaImdbId) {
            if (cinemetaImdbId) {
                callback(cinemetaImdbId);
                return;
            }

            searchQuery = encodeURIComponent(query + (year ? " " + year : ""));
            requestExternalText("https://www.imdb.com/find?q=" + searchQuery, function (error, html) {
                var match = !error && html ? html.match(/\/title\/(tt\d+)/i) : null;
                callback(match ? match[1] : "");
            });
        });
    }

    function resolveImdbIdFromCinemetaSearch(item, query, year, callback) {
        var type = item && item.section === "series" ? "series" : "movie";
        var url = "https://v3-cinemeta.strem.io/catalog/" + type + "/top/search=" + encodeURIComponent(query + (year ? " " + year : "")) + ".json";
        var wantedTitle = normalizeWorkTitle(query);
        var wantedYear = String(year || "");

        requestExternalText(url, function (error, text) {
            var data;
            var metas;
            var best = null;
            var i;

            if (error || !text) {
                callback("");
                return;
            }

            try {
                data = JSON.parse(text);
            } catch (parseError) {
                callback("");
                return;
            }

            metas = data && data.metas ? data.metas : [];
            for (i = 0; i < metas.length; i += 1) {
                var meta = metas[i];
                var resultTitle = normalizeWorkTitle(meta.name || "");
                var resultYear = String(meta.year || meta.releaseInfo || "").match(/\b(19|20)\d{2}\b/);
                var titleMatches = resultTitle === wantedTitle ||
                    resultTitle.indexOf(wantedTitle) !== -1 ||
                    wantedTitle.indexOf(resultTitle) !== -1;
                var yearMatches = !wantedYear || (resultYear && resultYear[0] === wantedYear);

                if (meta && (meta.imdb_id || meta.id) && titleMatches && yearMatches) {
                    best = meta;
                    break;
                }
            }

            callback(best ? (best.imdb_id || best.id || "") : "");
        });
    }

    function searchOpenSubtitlesByTitle(item, openSubtitlesType, callback) {
        resolveImdbIdFromTitle(item, function (imdbId) {
            var parsed;
            var subtitles;

            if (!imdbId) {
                console.log("[subtitles] No IMDB id found for title search:", item.title);
                callback();
                return;
            }

            item.imdbId = imdbId;
            requestExternalText("https://opensubtitles-v3.strem.io/subtitles/" + openSubtitlesType + "/" + encodeURIComponent(buildOpenSubtitlesId(item, imdbId)) + ".json", function (error, text) {
                if (error) {
                    console.log("[subtitles] OpenSubtitles IMDB search failed:", item.title, error.message);
                    callback();
                    return;
                }

                try {
                    parsed = JSON.parse(text);
                } catch (parseError) {
                    callback();
                    return;
                }

                subtitles = parseOpenSubtitlesResponse(parsed);
                if (subtitles.length) {
                    item.subtitles = mergeSubtitleLists(item.subtitles, subtitles);
                    console.log("[subtitles] OpenSubtitles via IMDB title search:", item.title, subtitles.length, imdbId);
                }

                callback();
            });
        });
    }

    function resolveImdbIdForItem(item, mediaType, callback) {
        var cacheKey = mediaType + ":" + item.tmdbId;
        var cinemetaUrl = "https://cinemeta-live.strem.io/meta/" + (mediaType === "tv" ? "series" : "movie") + "/tmdb:" + encodeURIComponent(item.tmdbId) + ".json";

        if (item.imdbId) {
            callback(item.imdbId);
            return;
        }

        if (imdbResolveCache[cacheKey]) {
            item.imdbId = imdbResolveCache[cacheKey];
            callback(item.imdbId);
            return;
        }

        requestExternalText(cinemetaUrl, function (metaError, metaText) {
            var meta;

            if (!metaError && metaText) {
                try {
                    meta = JSON.parse(metaText);
                    if (meta && meta.meta && meta.meta.imdb_id) {
                        item.imdbId = meta.meta.imdb_id;
                        imdbResolveCache[cacheKey] = item.imdbId;
                        callback(item.imdbId);
                        return;
                    }
                } catch (parseError) {
                    // Fall through to TMDB page.
                }
            }

            requestExternalText("https://www.themoviedb.org/" + mediaType + "/" + encodeURIComponent(item.tmdbId), function (error, html) {
                var imdbMatch = !error && html ? html.match(/href="https?:\/\/(?:www\.)?imdb\.com\/title\/(tt\d+)/i) : null;

                if (imdbMatch) {
                    item.imdbId = imdbMatch[1];
                    imdbResolveCache[cacheKey] = item.imdbId;
                    callback(item.imdbId);
                    return;
                }

                resolveImdbIdFromTitle(item, function (imdbFromTitle) {
                    if (imdbFromTitle) {
                        item.imdbId = imdbFromTitle;
                        imdbResolveCache[cacheKey] = item.imdbId;
                    }
                    callback(item.imdbId || "");
                });
            });
        });
    }

    function parseOpenSubtitlesResponse(data) {
        var langPriority = { arb: 1, ara: 1, ar: 1, eng: 2, en: 2, fre: 3, fra: 3, fr: 3, spa: 4, es: 4, tur: 5, tr: 5, por: 6, pob: 6 };
        var langLabels = {
            arb: "Arabic",
            ara: "Arabic",
            ar: "Arabic",
            "ar-eg": "Arabic",
            "ar-sa": "Arabic",
            eng: "English",
            en: "English",
            fre: "French",
            fra: "French",
            fr: "French",
            spa: "Spanish",
            es: "Spanish",
            tur: "Turkish",
            tr: "Turkish",
            ger: "German",
            de: "German",
            ita: "Italian",
            it: "Italian",
            por: "Portuguese",
            pob: "Portuguese (BR)"
        };
        var seen = {};
        var list = [];

        (data && data.subtitles ? data.subtitles : []).forEach(function (entry) {
            var lang = String(entry.lang || entry.language || "").toLowerCase();
            var url = entry.url || "";

            if (!url || seen[url]) {
                return;
            }

            seen[url] = true;
            list.push({
                url: url,
                label: langLabels[lang] || lang.toUpperCase() || "Subtitle",
                language: guessSubtitleLanguage(lang),
                priority: langPriority[lang] || 99
            });
        });

        list.sort(function (a, b) {
            return (a.priority || 99) - (b.priority || 99);
        });

        return list.slice(0, 16).map(function (entry) {
            return {
                url: entry.url,
                label: entry.label,
                language: entry.language
            };
        });
    }

    function applyDetailMetadata(item, data) {
        if (!mergeItemMetadata(item, data) || state.detailItem !== item) {
            return;
        }

        renderDetail(item);
        loadTmdbCastImages(item);
    }

    function mergeItemMetadata(item, data) {
        var info = data && (data.info || data.movie_data || data);
        var movieProperties;
        var castPeople;
        var subtitleSources;

        if (!info) {
            return false;
        }

        item.metadataLoaded = true;
        item.plot = info.plot || info.description || item.plot;
        item.rating = info.rating || item.rating;
        item.year = info.releasedate ? String(info.releasedate).slice(0, 4) : (info.year || item.year);
        item.addedAt = info.added || info.added_at || item.addedAt;
        item.duration = info.duration || info.duration_secs || item.duration;
        item.quality = info.quality || item.quality;
        item.tmdbId = info.tmdb_id || getTmdbIdFromUrl(info.tmdb_url) || item.tmdbId;
        movieProperties = parseMovieProperties(info.movie_properties || info.movie_propeties || data.movie_properties || data.movie_propeties);
        subtitleSources = [
            info.subtitles,
            info.subtitle,
            info.subtitles_url,
            info.subtitles_urls,
            info.subtitle_url,
            info.subtitle_urls,
            info.external_subtitles,
            info.subtitles_all,
            info.subtitle_tracks,
            info.caption,
            info.caption_url,
            info.vtt,
            info.srt,
            info.cc,
            info.captions,
            movieProperties && movieProperties.subtitles,
            movieProperties && movieProperties.subtitle,
            movieProperties && movieProperties.subtitles_url,
            movieProperties && movieProperties.subtitle_url,
            data.subtitles,
            data.subtitle,
            data.subtitles_url,
            data.subtitles_urls,
            data.subtitle_url,
            data.subtitle_urls,
            data.external_subtitles,
            data.subtitles_all,
            data.vtt,
            data.srt,
            data.movie_data && data.movie_data.subtitles,
            data.movie_data && data.movie_data.subtitle,
            data.movie_data && data.movie_data.subtitles_url,
            data.movie_data && data.movie_data.subtitles_urls,
            data.movie_data && data.movie_data.subtitle_url,
            data.movie_data && data.movie_data.subtitle_urls,
            data.movie_data && data.movie_data.external_subtitles,
            data.movie_data && data.movie_data.vtt,
            data.movie_data && data.movie_data.srt
        ];
        if (data.info && data.movie_data) {
            collectSubtitleSources(data.movie_data, subtitleSources);
        }
        collectSubtitleSources(data, subtitleSources);
        item.subtitles = mergeSubtitleLists(item.subtitles, normalizeSubtitleList(subtitleSources, item));
        item.seriesSeasons = normalizeXtreamEpisodes(data.episodes, item);
        mergeEpisodeMetadataFromSeriesInfo(item);
        console.log("[subtitles] Metadata merged for", item.title, "tracks:", (item.subtitles || []).length);

        castPeople = parseCastPeople(
            info.cast ||
            info.actors ||
            info.cast_members ||
            info.people ||
            info.credits ||
            data.cast ||
            data.actors ||
            data.cast_members ||
            data.movie_data && (data.movie_data.cast || data.movie_data.actors || data.movie_data.cast_members) ||
            movieProperties && (movieProperties.cast || movieProperties.actors || movieProperties.cast_members) ||
            ""
        );
        if (castPeople.length) {
            item.castPeople = castPeople;
            item.cast = castPeople.map(function (person) {
                return person.name;
            });
        }

        return true;
    }

    function requestJson(url, callback) {
        requestText(url, function (error, text) {
            if (error) {
                callback(error);
                return;
            }

            try {
                callback(null, JSON.parse(text));
            } catch (parseError) {
                callback(parseError);
            }
        });
    }

    function getIptvProxyUrl(url) {
        if (!APP_SERVER_URL || !url || !/^https?:\/\//i.test(url)) {
            return "";
        }

        return normalizeServerUrl(APP_SERVER_URL) + "/api/iptv_proxy.php?url=" + encodeURIComponent(url);
    }

    function requestTextWithIptvFallback(url, timeout, callback) {
        var proxyUrl = getIptvProxyUrl(url);

        if (!proxyUrl) {
            requestTextWithTimeout(url, timeout, callback);
            return;
        }

        requestTextWithTimeout(url, timeout, function (error, text) {
            if (!error) {
                callback(null, text);
                return;
            }

            requestTextWithTimeout(proxyUrl, timeout, callback);
        });
    }

    function requestIptvJson(url, callback) {
        requestTextWithIptvFallback(url, 25000, function (error, text) {
            if (error) {
                callback(error);
                return;
            }

            try {
                callback(null, JSON.parse(text));
            } catch (parseError) {
                callback(parseError);
            }
        });
    }

    function loadTmdbCastImages(item) {
        var tmdbId = item && item.tmdbId;
        var mediaType = item && item.section === "series" ? "tv" : "movie";
        var cacheKey = mediaType + ":" + tmdbId;

        if (!item) {
            return;
        }

        if (!tmdbId) {
            resolveTmdbIdForItem(item, function (resolvedId) {
                if (resolvedId) {
                    item.tmdbId = resolvedId;
                    item.tmdbImagesLoaded = false;
                    loadTmdbCastImages(item);
                } else {
                    console.log("No TMDB id found for cast images:", item.title);
                    loadMissingCastPersonImages(item);
                }
            });
            return;
        }

        cacheKey = mediaType + ":" + tmdbId;

        if (item.tmdbImagesLoaded) {
            return;
        }

        if (tmdbCastCache[cacheKey]) {
            applyTmdbCastImages(item, tmdbCastCache[cacheKey]);
            return;
        }

        console.log("Loading TMDB cast images:", cacheKey, item.title);
        requestExternalText("https://www.themoviedb.org/" + mediaType + "/" + encodeURIComponent(tmdbId) + "/cast", function (error, html) {
            var people;

            if (error) {
                console.log("Could not load TMDB cast images:", error.message);
                loadCinemetaCastImages(item, mediaType, tmdbId, cacheKey);
                return;
            }

            people = parseTmdbCastHtml(html);
            console.log("TMDB cast images loaded:", cacheKey, people.length);
            tmdbCastCache[cacheKey] = people;
            applyTmdbCastImages(item, people);
            if (!people.length) {
                loadCinemetaCastImages(item, mediaType, tmdbId, cacheKey);
            }
        });
    }

    function loadCinemetaCastImages(item, mediaType, tmdbId, cacheKey) {
        var cinemetaType = mediaType === "tv" ? "series" : "movie";
        var url = "https://cinemeta-live.strem.io/meta/" + cinemetaType + "/tmdb:" + encodeURIComponent(tmdbId) + ".json";

        requestExternalText(url, function (error, text) {
            var parsed;
            var people;

            if (error) {
                console.log("Could not load Cinemeta cast:", error.message);
                loadMissingCastPersonImages(item);
                return;
            }

            try {
                parsed = JSON.parse(text);
            } catch (parseError) {
                loadMissingCastPersonImages(item);
                return;
            }

            people = parseCinemetaCastPeople(parsed);
            if (people.length) {
                tmdbCastCache[cacheKey] = people;
                applyTmdbCastImages(item, people);
                return;
            }

            loadMissingCastPersonImages(item);
        });
    }

    function parseCinemetaCastPeople(data) {
        var meta = data && (data.meta || data);
        var cast = meta && (meta.cast || meta.actors || meta.cast_members || []);

        if (typeof cast === "string") {
            cast = cast.split(/[,|ØŒ]/);
        }

        if (!Array.isArray(cast)) {
            return [];
        }

        return cast.map(function (person) {
            if (typeof person === "string") {
                return { name: person.trim(), image: "", tmdbPersonId: "" };
            }

            return {
                name: String(person.name || person.original_name || person.title || "").trim(),
                image: normalizePersonImageUrl(person.profile_path || person.image || person.photo || person.avatar || ""),
                tmdbPersonId: person.id || person.tmdb_id || person.person_id || ""
            };
        }).filter(function (person) {
            return person.name;
        }).slice(0, 24);
    }

    function parseTmdbCastHtml(html) {
        var people = [];
        var pattern = /<li[^>]*data-credit-id=[\s\S]*?<a href="\/person\/(\d+)[^"]*"[\s\S]*?<img[^>]+src="([^"]+)"[^>]+alt="([^"]*)"[\s\S]*?<\/li>/g;
        var match;

        while ((match = pattern.exec(html)) && people.length < 60) {
            people.push({
                name: decodeHtml(match[3]),
                image: normalizePersonImageUrl(match[2].replace(/w66_and_h66_face/g, "w300_and_h450_bestv2")),
                tmdbPersonId: match[1]
            });
        }

        if (!people.length) {
            people = parseTmdbCastHtmlFallback(html);
        }

        if (!people.length) {
            people = parseTmdbCastMarkdown(html);
        }

        return people;
    }

    function parseTmdbCastMarkdown(markdown) {
        var people = [];
        var pattern = /!\[Image\s+\d+:\s*([^\]]+)\]\((https:\/\/media\.themoviedb\.org\/t\/p\/w66_and_h66_face\/[^)]+)\)\]\(https:\/\/www\.themoviedb\.org\/person\/(\d+)[^)]*\)/g;
        var match;

        while ((match = pattern.exec(markdown)) && people.length < 60) {
            people.push({
                name: decodeHtml(match[1]),
                image: normalizePersonImageUrl(match[2].replace(/w66_and_h66_face/g, "w300_and_h450_bestv2")),
                tmdbPersonId: match[3]
            });
        }

        return people;
    }

    function resolveTmdbIdForItem(item, callback) {
        var mediaType = item.section === "series" ? "tv" : "movie";
        var cleanTitle = normalizeDisplayTitle(item.seriesTitle || item.title);
        var cacheKey = mediaType + ":" + cleanTitle;
        var searchUrl;

        if (!cleanTitle) {
            callback("");
            return;
        }

        if (tmdbResolveCache[cacheKey]) {
            callback(tmdbResolveCache[cacheKey]);
            return;
        }

        if (item.tmdbResolving) {
            callback("");
            return;
        }

        item.tmdbResolving = true;
        searchUrl = "https://www.themoviedb.org/search?query=" + encodeURIComponent(cleanTitle);
        console.log("Resolving TMDB id:", mediaType, cleanTitle);
        requestExternalText(searchUrl, function (error, markdown) {
            var tmdbId = "";

            item.tmdbResolving = false;
            if (error) {
                console.log("Could not resolve TMDB id:", item.title, error.message);
                callback("");
                return;
            }

            tmdbId = parseTmdbSearchId(markdown, mediaType, cleanTitle);
            if (tmdbId) {
                tmdbResolveCache[cacheKey] = tmdbId;
                console.log("Resolved TMDB id:", item.title, tmdbId);
            }

            callback(tmdbId);
        });
    }

    function parseTmdbSearchId(markdown, mediaType, cleanTitle) {
        var pattern = new RegExp("\\[\\!\\[Image\\s+\\d+:\\s*([^\\]]+)\\]\\([^)]*\\)\\]\\(https:\\/\\/www\\.themoviedb\\.org\\/" + mediaType + "\\/(\\d+)[^)]*\\)", "g");
        var normalizedTitle = normalizeWorkTitle(cleanTitle);
        var firstId = "";
        var match;

        while ((match = pattern.exec(markdown))) {
            var resultTitle = normalizeWorkTitle(match[1]);

            if (!firstId) {
                firstId = match[2];
            }

            if (resultTitle === normalizedTitle ||
                    resultTitle.indexOf(normalizedTitle) !== -1 ||
                    normalizedTitle.indexOf(resultTitle) !== -1) {
                return match[2];
            }
        }

        return firstId;
    }

    function normalizeDisplayTitle(value) {
        return String(value || "")
            .replace(/\([^)]*\)/g, "")
            .replace(/\[\s*\]/g, "")
            .replace(/\bS\s*\d{1,2}\s*[._-]\s*E\s*\d{1,3}\b/ig, "")
            .replace(/\bS\d{1,2}\s*E\d{1,3}\b/ig, "")
            .replace(/\bS\d{1,2}E\d{1,3}\b/ig, "")
            .replace(/\b\d{1,2}x\d{1,3}\b/ig, "")
            .replace(/(?:\b(?:Ep|Episode|Episodio|Bolum)|.pisode)\s*\d{1,3}\b/ig, "")
            .replace(/Ù…Ø¯Ø¨Ù„Ø¬|Ù…ØªØ±Ø¬Ù…|Ø¬ÙˆØ¯Ø© Ø§Ù‚Ù„|Ø¬ÙˆØ¯Ø© Ø¹Ø§Ù„ÙŠØ©/g, "")
            .replace(/\s*[-|]\s*[-|]?\s*/g, " - ")
            .replace(/\s*[-|]\s*$/g, "")
            .replace(/^\s*[-|]\s*/g, "")
            .replace(/\s+/g, " ")
            .trim();
    }

    function parseTmdbCastHtmlFallback(html) {
        var people = [];
        var liPattern = /<li[^>]*data-credit-id[\s\S]*?<\/li>/g;
        var personPattern = /\/person\/(\d+)[^"]*/;
        var imagePattern = /<img[^>]+(?:src|data-src)="([^"]+)"/;
        var altPattern = /alt="([^"]*)"/;
        var match;

        while ((match = liPattern.exec(html)) && people.length < 60) {
            var block = match[0];
            var personMatch = block.match(personPattern);
            var imageMatch = block.match(imagePattern);
            var altMatch = block.match(altPattern);

            if (personMatch && imageMatch && altMatch) {
                people.push({
                    name: decodeHtml(altMatch[1]),
                    image: normalizePersonImageUrl(imageMatch[1].replace(/w66_and_h66_face/g, "w300_and_h450_bestv2")),
                    tmdbPersonId: personMatch[1]
                });
            }
        }

        return people;
    }

    function applyTmdbCastImages(item, tmdbPeople) {
        var sourceCast;
        var castPeople;

        if (!tmdbPeople || !tmdbPeople.length) {
            return;
        }

        item.tmdbImagesLoaded = true;
        sourceCast = item.cast && item.cast.length ? item.cast : tmdbPeople.map(function (person) {
            return person.name;
        });

        castPeople = sourceCast.map(function (name, index) {
            var current = item.castPeople && item.castPeople[index];
            var tmdbPerson = findTmdbPersonByName(name, tmdbPeople) || tmdbPeople[index];

            return {
                name: name || (tmdbPerson && tmdbPerson.name) || "",
                image: (current && current.image) || (tmdbPerson && tmdbPerson.image) || "",
                tmdbPersonId: (current && current.tmdbPersonId) || (tmdbPerson && tmdbPerson.tmdbPersonId) || ""
            };
        }).filter(function (person) {
            return person.name;
        });

        item.castPeople = castPeople;
        item.cast = castPeople.map(function (person) {
            return person.name;
        });
        loadMissingCastPersonImages(item);
        if (state.detailItem === item) {
            renderDetail(item);
        }
    }

    function loadMissingCastPersonImages(item) {
        var missing;
        var active = 0;
        var cursor = 0;
        var maxActive = 3;

        if (!item || !item.castPeople || !item.castPeople.length) {
            if (item && item.cast && item.cast.length) {
                item.castPeople = item.cast.map(function (name) {
                    return { name: name, image: "" };
                });
            }
        }

        if (!item || !item.castPeople || !item.castPeople.length) {
            return;
        }

        missing = item.castPeople.map(function (person, index) {
            return { person: person, index: index };
        }).filter(function (entry) {
            return entry.person && entry.person.name && !entry.person.image && entry.person.name.indexOf("\u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631") === -1;
        }).slice(0, 18);

        function pump() {
            while (active < maxActive && cursor < missing.length) {
                (function (entry) {
                    active += 1;
                    cursor += 1;
                    resolveTmdbPersonImage(entry.person.name, function (personData) {
                        active -= 1;
                        if (personData && personData.image && item.castPeople && item.castPeople[entry.index]) {
                            item.castPeople[entry.index].image = personData.image;
                            item.castPeople[entry.index].tmdbPersonId = item.castPeople[entry.index].tmdbPersonId || personData.tmdbPersonId;
                            if (state.detailItem === item) {
                                renderDetail(item);
                            }
                        }
                        pump();
                    });
                }(missing[cursor]));
            }
        }

        pump();
    }

    function resolveTmdbPersonImage(name, callback) {
        var cleanName = String(name || "").trim();
        var cacheKey = normalizeSearchText(cleanName);

        if (!cacheKey) {
            callback(null);
            return;
        }

        if (Object.prototype.hasOwnProperty.call(tmdbPersonImageCache, cacheKey)) {
            callback(tmdbPersonImageCache[cacheKey]);
            return;
        }

        requestExternalText("https://www.themoviedb.org/search/person?query=" + encodeURIComponent(cleanName), function (error, html) {
            var personData = null;

            if (!error) {
                personData = parseTmdbPersonSearch(html, cleanName);
            }

            tmdbPersonImageCache[cacheKey] = personData;
            callback(personData);
        });
    }

    function parseTmdbPersonSearch(html, cleanName) {
        var patterns = [
            /!\[Image\s+\d+:\s*([^\]]+)\]\((https:\/\/media\.themoviedb\.org\/t\/p\/[^)]+)\)\]\(https:\/\/www\.themoviedb\.org\/person\/(\d+)[^)]*\)/g,
            /<a[^>]+href="\/person\/(\d+)[^"]*"[\s\S]{0,800}?<img[^>]+(?:src|data-src)="([^"]+)"[^>]+alt="([^"]*)"/g
        ];
        var normalizedName = normalizeSearchText(cleanName);
        var first = null;
        var i;

        for (i = 0; i < patterns.length; i += 1) {
            var match;
            while ((match = patterns[i].exec(html))) {
                var isMarkdown = i === 0;
                var name = decodeHtml(isMarkdown ? match[1] : match[3]);
                var image = normalizePersonImageUrl((isMarkdown ? match[2] : match[2]).replace(/w66_and_h66_face|w90_and_h90_face/g, "w300_and_h450_bestv2"));
                var id = isMarkdown ? match[3] : match[1];
                var data = { name: name, image: image, tmdbPersonId: id };

                if (!first) {
                    first = data;
                }
                if (normalizeSearchText(name) === normalizedName) {
                    return data;
                }
            }
        }

        return first;
    }

    function findTmdbPersonByName(name, people) {
        var normalizedName = normalizeSearchText(name);

        for (var i = 0; i < people.length; i += 1) {
            var normalizedPerson = normalizeSearchText(people[i].name);
            if (normalizedPerson === normalizedName ||
                    normalizedPerson.indexOf(normalizedName) !== -1 ||
                    normalizedName.indexOf(normalizedPerson) !== -1) {
                return people[i];
            }
        }

        return null;
    }

    function loadTmdbActorWorks(personId, token, callback) {
        var url = "https://www.themoviedb.org/person/" + encodeURIComponent(personId) + "?language=ar";

        requestExternalText(url, function (error, html) {
            if (token !== actorSearchToken) {
                return;
            }

            if (error) {
                console.log("Could not load TMDB actor works:", error.message);
                callback([]);
                return;
            }

            callback(parseTmdbActorTitles(html));
        });
    }

    function parseTmdbActorTitles(html) {
        var titles = [];
        var seen = {};
        var pattern = /<a\s+class="tooltip"[^>]+href="\/(?:movie|tv)\/\d+[^"]*"[^>]*>([\s\S]*?)<\/a>/g;
        var markdownPattern = /\]\(https:\/\/www\.themoviedb\.org\/(?:movie|tv)\/\d+[^)]*\)\s*\n+\s*([^\n]+)/g;
        var match;

        while ((match = pattern.exec(html)) && titles.length < 240) {
            var title = decodeHtml(match[1].replace(/<[^>]+>/g, "")).replace(/\s+/g, " ").trim();

            if (title && !seen[title]) {
                seen[title] = true;
                titles.push(title);
            }
        }

        while ((match = markdownPattern.exec(html)) && titles.length < 240) {
            var markdownTitle = decodeHtml(match[1].replace(/\s+/g, " ").trim());

            if (markdownTitle && !seen[markdownTitle] && markdownTitle.length < 120) {
                seen[markdownTitle] = true;
                titles.push(markdownTitle);
            }
        }

        return titles;
    }

    function normalizeXtreamEpisodes(episodes, parentItem) {
        var config = getXtreamConfigForItem(parentItem) || getXtreamConfig();
        var seasons = [];

        if (!episodes || !config) {
            return [];
        }

        Object.keys(episodes).sort(numberSort).forEach(function (seasonNumber) {
            var seasonEpisodes = episodes[seasonNumber] || [];

            seasons.push({
                number: seasonNumber,
                episodes: seasonEpisodes.map(function (episode, index) {
                    var episodeInfo = episode.info || {};
                    var extension = normalizeXtreamContainerExtension(
                        episode.container_extension ||
                        episode.containerExtension ||
                        episodeInfo.container_extension ||
                        episodeInfo.containerExtension ||
                        parentItem.containerExtension
                    );
                    return {
                        title: episode.title || parentItem.title + " S" + seasonNumber + " E" + (episode.episode_num || index + 1),
                        group: parentItem.group,
                        section: "series",
                        description: parentItem.description,
                        logo: episodeInfo.movie_image || parentItem.logo,
                        year: parentItem.year,
                        quality: parentItem.quality,
                        duration: episodeInfo.duration || parentItem.duration,
                        addedAt: parentItem.addedAt,
                        rating: parentItem.rating,
                        plot: episodeInfo.plot || parentItem.plot,
                        cast: parentItem.cast,
                        subtitles: normalizeSubtitleList([
                            episode.subtitles,
                            episode.subtitle,
                            episode.subtitle_url,
                            episode.subtitles_url,
                            episode.subtitles_urls,
                            episode.external_subtitles,
                            episode.vtt,
                            episode.srt,
                            episodeInfo.subtitles,
                            episodeInfo.subtitle,
                            episodeInfo.subtitle_url,
                            episodeInfo.subtitles_url,
                            episodeInfo.subtitles_urls,
                            episodeInfo.external_subtitles,
                            episodeInfo.vtt,
                            episodeInfo.srt
                        ], parentItem),
                        seriesTitle: parentItem.seriesTitle || parentItem.title,
                        seasonNumber: parseInt(seasonNumber, 10),
                        episodeNumber: parseInt(episode.episode_num || index + 1, 10),
                        seriesXtreamId: parentItem.xtreamId,
                        xtreamId: episode.id,
                        containerExtension: extension,
                        type: getMediaTypeFromExtension(extension),
                        url: config.baseUrl + "/series/" + encodeURIComponent(config.username) + "/" + encodeURIComponent(config.password) + "/" + episode.id + "." + extension
                    };
                })
            });
        });

        return seasons;
    }

    function mergeEpisodeMetadataFromSeriesInfo(item) {
        var matchedEpisode = null;

        if (!item || !item.seriesXtreamId || !item.seriesSeasons || !item.seriesSeasons.length) {
            return;
        }

        item.seriesSeasons.some(function (season) {
            return season.episodes.some(function (episode) {
                if (String(episode.xtreamId) === String(item.xtreamId)) {
                    matchedEpisode = episode;
                    return true;
                }
                return false;
            });
        });

        if (!matchedEpisode) {
            return;
        }

        item.subtitles = mergeSubtitleLists(item.subtitles, matchedEpisode.subtitles);
        item.plot = matchedEpisode.plot || item.plot;
        item.logo = matchedEpisode.logo || item.logo;
        item.duration = matchedEpisode.duration || item.duration;
    }

    function parseMovieProperties(value) {
        var parsed = value;

        if (!parsed) {
            return null;
        }

        if (typeof parsed === "string") {
            try {
                return JSON.parse(parsed);
            } catch (error) {
                return null;
            }
        }

        if (typeof parsed === "object") {
            return parsed;
        }

        return null;
    }

    function isSubtitleUrl(value) {
        var text = String(value || "").trim();

        if (!text) {
            return false;
        }

        return /^https?:\/\//i.test(text) ||
            /^\/[^\s]+$/.test(text) ||
            /\.(srt|vtt|ass|ssa|sub)(?:$|\?)/i.test(text);
    }

    function buildXtreamSubtitleProbeList(item) {
        var config = getXtreamConfigForItem(item) || getXtreamConfig();
        var streamId = item && (item.xtreamId || getXtreamStreamId(item.url || ""));
        var section = item && item.section;
        var user;
        var pass;
        var base;
        var probes = [];
        var langSuffixes = ["", "ar", "en"];

        if (!config || !streamId || (section !== "movies" && section !== "series")) {
            return [];
        }

        user = encodeURIComponent(config.username);
        pass = encodeURIComponent(config.password);
        base = String(config.baseUrl || "").replace(/\/+$/, "");

        function addProbe(folder, labelPrefix) {
            langSuffixes.forEach(function (suffix) {
                var nameSuffix = suffix ? "." + suffix : "";
                probes.push({
                    url: base + "/" + folder + "/" + user + "/" + pass + "/" + streamId + nameSuffix + ".srt",
                    label: (labelPrefix || "SRT") + (suffix ? " " + suffix.toUpperCase() : ""),
                    language: guessSubtitleLanguage(suffix)
                });
                probes.push({
                    url: base + "/" + folder + "/" + user + "/" + pass + "/" + streamId + nameSuffix + ".vtt",
                    label: (labelPrefix || "VTT") + (suffix ? " " + suffix.toUpperCase() : ""),
                    language: guessSubtitleLanguage(suffix)
                });
            });
            probes.push({
                url: base + "/subtitles/" + user + "/" + pass + "/" + streamId + ".srt",
                label: "Server SRT",
                language: ""
            });
            probes.push({
                url: base + "/subtitles/" + user + "/" + pass + "/" + streamId + ".vtt",
                label: "Server VTT",
                language: ""
            });
        }

        if (section === "movies") {
            addProbe("movie", "Movie");
        } else {
            addProbe("series", "Episode");
        }

        return probes;
    }

    function collectSubtitleSources(value, output, depth) {
        var keys;

        if (!value || !output || depth > 6) {
            return;
        }

        if (Array.isArray(value)) {
            value.forEach(function (entry) {
                collectSubtitleSources(entry, output, (depth || 0) + 1);
            });
            return;
        }

        if (typeof value !== "object") {
            return;
        }

        keys = Object.keys(value);
        keys.forEach(function (key) {
            var entry = value[key];
            var normalizedKey = String(key || "").toLowerCase();
            var isSubtitleKey = /sub|caption|vtt|srt|webvtt|track|cc/.test(normalizedKey);

            if (isSubtitleKey) {
                output.push(entry);
                return;
            }

            if (entry && typeof entry === "object") {
                collectSubtitleSources(entry, output, (depth || 0) + 1);
            }
        });
    }

    function normalizeSubtitleList(value, item) {
        var list = [];
        var parsed = [];

        if (!parsed) {
            return [];
        }

        collectSubtitleEntries(value, parsed);

        parsed.forEach(function (entry, index) {
            var url = typeof entry === "string" ? entry : (entry.url || entry.file || entry.src || entry.link || entry.path ||
                entry.subtitle || entry.subtitle_url || entry.subtitles_url || entry.subtitle_path || entry.file_path ||
                entry.download_url || entry.external_link || entry.stream || "");
            var label = typeof entry === "string" ? "" : (entry.label || entry.name || entry.title || entry.lang || entry.language || entry.code || "");

            if (typeof entry === "object" && entry && !url && entry.server && entry.path) {
                url = String(entry.server).replace(/\/+$/, "") + "/" + String(entry.path).replace(/^\/+/, "");
            }

            url = resolveSubtitleUrl(url, item);
            if (!url) {
                return;
            }

            list.push({
                url: url,
                label: label || guessSubtitleLabel(url, index),
                language: guessSubtitleLanguage(label || url, item)
            });
        });

        return mergeSubtitleLists([], list);
    }

    function collectSubtitleEntries(value, output) {
        var parsed = value;

        if (!parsed) {
            return;
        }

        if (Array.isArray(parsed)) {
            parsed.forEach(function (entry) {
                collectSubtitleEntries(entry, output);
            });
            return;
        }

        if (typeof parsed === "string") {
            parsed = parsed.trim();
            if (!parsed) {
                return;
            }
            if (parsed.charAt(0) === "[" || parsed.charAt(0) === "{") {
                try {
                    collectSubtitleEntries(JSON.parse(parsed), output);
                    return;
                } catch (error) {
                    // Treat malformed JSON as a plain URL list below.
                }
            }
            parsed.split(/[\n,|]+/).forEach(function (entry) {
                if (entry.trim()) {
                    output.push(entry.trim());
                }
            });
            return;
        }

        Object.keys(parsed).forEach(function (key) {
            var entry = parsed[key];
            if (typeof entry === "string") {
                if (isSubtitleUrl(entry)) {
                    output.push({ label: key, url: entry });
                }
                return;
            }
            if (entry && typeof entry === "object") {
                if (Array.isArray(entry) || (!entry.url && !entry.file && !entry.src && !entry.link && !entry.path &&
                        !entry.subtitle && !entry.subtitle_url)) {
                    collectSubtitleEntries(entry, output);
                    return;
                }
                entry.label = entry.label || entry.name || key;
                output.push(entry);
            }
        });
    }

    function resolveSubtitleUrl(url, item) {
        var cleanUrl = String(url || "").trim();
        var config;

        if (!cleanUrl) {
            return "";
        }

        cleanUrl = cleanUrl.replace(/\\\//g, "/").replace(/\s/g, "%20");
        if (/^https?:\/\//i.test(cleanUrl)) {
            return cleanUrl;
        }

        config = getXtreamConfigForItem(item) || getXtreamConfig();
        if (cleanUrl.charAt(0) === "/" && config) {
            return config.baseUrl + cleanUrl;
        }

        if (item && item.url && /^https?:\/\//i.test(item.url)) {
            return item.url.replace(/\/[^/?#]*(?:[?#].*)?$/, "/") + cleanUrl.replace(/^\/+/, "");
        }

        if (config && config.baseUrl) {
            return config.baseUrl + "/" + cleanUrl.replace(/^\/+/, "");
        }

        return "";
    }

    function mergeSubtitleLists(existing, incoming) {
        var seen = {};
        var merged = [];

        (existing || []).concat(incoming || []).forEach(function (subtitle) {
            var key = getSubtitleMergeKey(subtitle);
            if (!key || seen[key]) {
                return;
            }
            seen[key] = true;
            merged.push(subtitle);
        });

        return merged;
    }

    function getSubtitleMergeKey(subtitle) {
        var language;
        var label;

        if (!subtitle || !subtitle.url) {
            return "";
        }

        language = subtitle.language || guessSubtitleLanguage(subtitle.label || subtitle.url);
        label = String(subtitle.label || "").toLowerCase().replace(/\s+/g, " ").trim();

        if (language) {
            return "lang:" + language;
        }

        if (label && !/^subtitle\s+\d+$/i.test(label)) {
            return "label:" + label;
        }

        return "url:" + subtitle.url;
    }

    function guessSubtitleLabel(url, index) {
        var file = decodeURIComponent(String(url).split("?")[0].split("/").pop() || "");
        return file || ("Subtitle " + (index + 1));
    }

    function guessSubtitleLanguage(value) {
        var text = String(value || "").toLowerCase();
        if (text.indexOf("arab") !== -1 || text.indexOf(".ar.") !== -1 || text.indexOf("_ar") !== -1) {
            return "ar";
        }
        if (text.indexOf("eng") !== -1 || text.indexOf(".en.") !== -1 || text.indexOf("_en") !== -1) {
            return "en";
        }
        if (text.indexOf("tur") !== -1 || text.indexOf(".tr.") !== -1 || text.indexOf("_tr") !== -1) {
            return "tr";
        }
        return "";
    }

    function prepareSubtitleTracks(item) {
        clearSubtitleTrackElements();

        if (!item) {
            return;
        }

        if (!item.subtitles || !item.subtitles.length) {
            console.log("No external subtitles found for item:", item.title);
            discoverSidecarSubtitles(item);
            discoverHlsManifestSubtitles(item);
            scheduleEmbeddedSubtitleScans();
            return;
        }

        console.log("External subtitles found:", item.title, item.subtitles.length);
        item.subtitles.forEach(function (subtitle, index) {
            addSubtitleTrack(subtitle, index);
        });
        discoverHlsManifestSubtitles(item);
        scheduleEmbeddedSubtitleScans();
    }

    function scheduleEmbeddedSubtitleScans() {
        var delays = [400, 1200, 2500, 5000, 8000];

        delays.forEach(function (delay) {
            setTimeout(refreshPlayerOptions, delay);
        });

        if (elements.video.textTracks) {
            elements.video.textTracks.addEventListener("addtrack", refreshPlayerOptions);
        }
    }

    function discoverHlsManifestSubtitles(item) {
        var url = item && item.url ? String(item.url) : "";

        if (!item || item.hlsSubtitleDiscoveryStarted || url.toLowerCase().split("?")[0].indexOf(".m3u8") === -1) {
            return;
        }

        item.hlsSubtitleDiscoveryStarted = true;
        requestExternalText(url, function (error, manifest) {
            var entries;
            var pending;
            var found = [];

            if (error || !manifest) {
                return;
            }

            entries = parseHlsSubtitleEntries(manifest, url);
            pending = entries.length;
            if (!pending) {
                return;
            }

            entries.forEach(function (entry, index) {
                loadHlsSubtitleEntry(entry, index, function (subtitle) {
                    pending -= 1;
                    if (subtitle) {
                        found.push(subtitle);
                        appendSubtitleTrack(subtitle.url, subtitle, found.length - 1, subtitle.blobUrl || "", subtitle.cueText || "");
                    }
                    if (!pending && found.length) {
                        item.subtitles = mergeSubtitleLists(item.subtitles, found);
                        refreshPlayerOptions();
                    }
                });
            });
        });
    }

    function parseHlsSubtitleEntries(manifest, baseUrl) {
        var entries = [];
        var lines = String(manifest || "").split(/\r?\n/);

        lines.forEach(function (line, index) {
            var attrs;
            var uri;

            if (line.indexOf("#EXT-X-MEDIA:") !== 0 || line.indexOf("TYPE=SUBTITLES") === -1) {
                return;
            }

            attrs = parseHlsAttributeList(line.slice(line.indexOf(":") + 1));
            uri = attrs.URI || attrs.Uri || attrs.uri;
            if (!uri) {
                return;
            }

            entries.push({
                url: resolveRelativeUrl(uri, baseUrl),
                label: attrs.NAME || attrs.LANGUAGE || ("Subtitle " + (index + 1)),
                language: attrs.LANGUAGE || guessSubtitleLanguage(attrs.NAME || uri)
            });
        });

        return entries;
    }

    function parseHlsAttributeList(value) {
        var attrs = {};
        var pattern = /([A-Z0-9-]+)=("(?:[^"\\]|\\.)*"|[^,]*)/gi;
        var match;

        while ((match = pattern.exec(value))) {
            attrs[match[1]] = String(match[2] || "").replace(/^"|"$/g, "");
        }

        return attrs;
    }

    function loadHlsSubtitleEntry(entry, index, callback) {
        var lowerUrl = entry.url.toLowerCase().split("?")[0];

        if (lowerUrl.lastIndexOf(".m3u8") === -1) {
            requestExternalText(entry.url, function (error, text) {
                var cueText;
                var blobUrl;

                if (error || !looksLikeSubtitleText(text)) {
                    callback(entry);
                    return;
                }

                cueText = lowerUrl.lastIndexOf(".srt") !== -1 ? convertSrtToVtt(text) : normalizeVttText(text);
                blobUrl = URL.createObjectURL(new Blob([cueText], { type: "text/vtt" }));
                callback({
                    url: blobUrl,
                    blobUrl: blobUrl,
                    cueText: cueText,
                    label: entry.label || ("Subtitle " + (index + 1)),
                    language: entry.language || guessSubtitleLanguage(entry.label || entry.url)
                });
            });
            return;
        }

        requestExternalText(entry.url, function (error, playlist) {
            var segments;

            if (error || !playlist) {
                callback(null);
                return;
            }

            segments = parseHlsSubtitleSegments(playlist, entry.url);
            if (!segments.length) {
                callback(null);
                return;
            }

            fetchSubtitleSegmentsAsVtt(segments, function (cueText, blobUrl) {
                if (!blobUrl) {
                    callback(null);
                    return;
                }

                callback({
                    url: blobUrl,
                    blobUrl: blobUrl,
                    cueText: cueText,
                    label: entry.label || ("Subtitle " + (index + 1)),
                    language: entry.language || guessSubtitleLanguage(entry.label || entry.url)
                });
            });
        });
    }

    function parseHlsSubtitleSegments(playlist, baseUrl) {
        var segments = [];

        String(playlist || "").split(/\r?\n/).forEach(function (line) {
            var clean = line.trim();
            if (!clean || clean.charAt(0) === "#") {
                return;
            }
            if (/\.(vtt|webvtt|srt)(?:\?|$)/i.test(clean)) {
                segments.push(resolveRelativeUrl(clean, baseUrl));
            }
        });

        return segments.slice(0, 400);
    }

    function fetchSubtitleSegmentsAsVtt(segments, callback) {
        var pending = segments.length;
        var parts = [];

        if (!pending) {
            callback("");
            return;
        }

        segments.forEach(function (url, index) {
            requestExternalText(url, function (error, text) {
                pending -= 1;
                if (!error && looksLikeSubtitleText(text)) {
                    parts[index] = normalizeSubtitleSegmentText(text);
                }
                if (!pending) {
                    parts = parts.filter(Boolean);
                    var cueText = parts.length ? "WEBVTT\n\n" + parts.join("\n\n") : "";
                    callback(cueText, cueText ? URL.createObjectURL(new Blob([cueText], { type: "text/vtt" })) : "");
                }
            });
        });
    }

    function normalizeSubtitleSegmentText(text) {
        return String(text || "")
            .replace(/^\uFEFF/, "")
            .replace(/^WEBVTT[^\n]*(?:\n|$)/i, "")
            .replace(/^X-TIMESTAMP-MAP=.*(?:\n|$)/gim, "")
            .replace(/\r+/g, "")
            .trim();
    }

    function resolveRelativeUrl(url, baseUrl) {
        try {
            return new URL(url, baseUrl).toString();
        } catch (error) {
            return url;
        }
    }

    function discoverSidecarSubtitles(item) {
        var candidates = getSidecarSubtitleCandidates(item).concat(buildXtreamSubtitleProbeList(item));
        var found = [];
        var pending = candidates.length;
        var seen = {};

        if (!item || !candidates.length) {
            return;
        }

        candidates = candidates.filter(function (subtitle) {
            var key = subtitle && subtitle.url;
            if (!key || seen[key]) {
                return false;
            }
            seen[key] = true;
            return true;
        });
        pending = candidates.length;

        candidates.forEach(function (subtitle) {
            requestExternalText(subtitle.url, function (error, text) {
                pending -= 1;
                if (!error && looksLikeSubtitleText(text)) {
                    var blobUrl;
                    var vttText;
                    found.push(subtitle);
                    if (subtitle.url.toLowerCase().split("?")[0].lastIndexOf(".srt") !== -1) {
                        vttText = convertSrtToVtt(text);
                    } else {
                        vttText = normalizeVttText(text);
                    }
                    blobUrl = URL.createObjectURL(new Blob([vttText], { type: "text/vtt" }));
                    appendSubtitleTrack(blobUrl, subtitle, found.length - 1, blobUrl, vttText);
                }
                if (!pending && found.length) {
                    item.subtitles = mergeSubtitleLists(item.subtitles, found);
                    refreshPlayerOptions();
                }
            });
        });
    }

    function getSidecarSubtitleCandidates(item) {
        var url = item && item.url ? String(item.url) : "";
        var clean;
        var base;

        if (!/^https?:\/\//i.test(url) || url.indexOf(".m3u8") !== -1) {
            return [];
        }

        clean = url.split("?")[0];
        base = clean.replace(/\.[^/.]+$/, "");
        if (base === clean) {
            return [];
        }

        return [
            { url: base + ".vtt", label: "VTT", language: "" },
            { url: base + ".srt", label: "SRT", language: "" },
            { url: base + ".ar.vtt", label: "Arabic VTT", language: "ar" },
            { url: base + ".ar.srt", label: "Arabic SRT", language: "ar" }
        ];
    }

    function looksLikeSubtitleText(text) {
        var value = String(text || "").slice(0, 2000);
        return value.indexOf("WEBVTT") !== -1 || /\d{1,2}:\d{2}:\d{2}[,.]\d{3}\s+-->\s+\d{1,2}:\d{2}:\d{2}[,.]\d{3}/.test(value);
    }

    function parseSubtitleTime(value) {
        var parts = String(value || "").trim().replace(",", ".").split(":");
        var seconds;
        var minutes;
        var hours;

        if (parts.length < 2) {
            return NaN;
        }

        seconds = parseFloat(parts.pop());
        minutes = parseInt(parts.pop(), 10);
        hours = parts.length ? parseInt(parts.pop(), 10) : 0;

        if (!isFinite(seconds) || !isFinite(minutes) || !isFinite(hours)) {
            return NaN;
        }

        return (hours * 3600) + (minutes * 60) + seconds;
    }

    function cleanSubtitleCueText(text) {
        return String(text || "")
            .replace(/<br\s*\/?>/gi, "\n")
            .replace(/<\/?[^>]+>/g, "")
            .replace(/\{\\[^}]*\}/g, "")
            .replace(/\{[^}]*\}/g, "")
            .replace(/\n{3,}/g, "\n\n")
            .trim();
    }

    function parseSubtitleCues(text) {
        var blocks = String(text || "")
            .replace(/^\uFEFF/, "")
            .replace(/\r+/g, "")
            .split(/\n{2,}/);
        var cues = [];

        blocks.forEach(function (block) {
            var lines = block.split("\n").filter(function (line) {
                return line.trim();
            });
            var timingIndex = -1;
            var timing;
            var parts;
            var start;
            var end;
            var cueText;
            var i;

            if (!lines.length || /^WEBVTT/i.test(lines[0]) || /^NOTE\b/i.test(lines[0]) ||
                    /^STYLE\b/i.test(lines[0]) || /^REGION\b/i.test(lines[0])) {
                return;
            }

            for (i = 0; i < lines.length; i += 1) {
                if (lines[i].indexOf("-->") !== -1) {
                    timingIndex = i;
                    break;
                }
            }

            if (timingIndex === -1) {
                return;
            }

            timing = lines[timingIndex];
            parts = timing.split("-->");
            if (parts.length < 2) {
                return;
            }

            start = parseSubtitleTime(parts[0]);
            end = parseSubtitleTime(String(parts[1]).trim().split(/\s+/)[0]);
            cueText = cleanSubtitleCueText(lines.slice(timingIndex + 1).join("\n"));

            if (!isFinite(start) || !isFinite(end) || end <= start || !cueText) {
                return;
            }

            cues.push({
                start: start,
                end: end,
                text: cueText
            });
        });

        cues.sort(function (a, b) {
            return a.start - b.start;
        });

        return cues;
    }

    function createSubtitleCueTrack(subtitle, cueText) {
        var cueKey = "subtitle-" + (++subtitleCueKeyCounter);
        var cues = parseSubtitleCues(cueText);

        if (!cues.length) {
            return "";
        }

        state.subtitleCueTracks[cueKey] = {
            cues: cues,
            label: subtitle && subtitle.label ? subtitle.label : "",
            url: subtitle && subtitle.url ? subtitle.url : ""
        };

        return cueKey;
    }

    function clearManualSubtitleOverlay() {
        state.activeSubtitleCueKey = "";
        state.activeSubtitleCues = [];
        state.lastSubtitleText = "";
        if (elements.subtitleOverlay) {
            elements.subtitleOverlay.textContent = "";
            elements.subtitleOverlay.hidden = true;
        }
    }

    function getTrackElementForTextTrackIndex(index) {
        var textTrack = elements.video && elements.video.textTracks ? elements.video.textTracks[index] : null;
        var tracks;
        var i;

        if (!textTrack) {
            return null;
        }

        tracks = elements.video.querySelectorAll("track");
        for (i = 0; i < tracks.length; i += 1) {
            if (tracks[i].track === textTrack) {
                return tracks[i];
            }
        }

        return null;
    }

    function getCueKeyForSubtitleSelection(selected) {
        var tracks = player.getSubtitleTracks();
        var selectedText = String(selected);
        var i;
        var track;
        var trackElement;

        for (i = 0; i < tracks.length; i += 1) {
            track = tracks[i];
            if (String(track.id) !== selectedText) {
                continue;
            }

            if (track.cueKey) {
                return track.cueKey;
            }

            if (typeof track.textTrackIndex === "number") {
                trackElement = getTrackElementForTextTrackIndex(track.textTrackIndex);
                return trackElement && trackElement.dataset ? (trackElement.dataset.cueKey || "") : "";
            }
        }

        return "";
    }

    function activateManualSubtitleForSelection(selected) {
        var cueKey;
        var cueTrack;

        if (selected < 0 || !state.playingItem) {
            clearManualSubtitleOverlay();
            return;
        }

        cueKey = getCueKeyForSubtitleSelection(selected);
        cueTrack = cueKey ? state.subtitleCueTracks[cueKey] : null;

        if (!cueTrack || !cueTrack.cues || !cueTrack.cues.length) {
            clearManualSubtitleOverlay();
            return;
        }

        state.activeSubtitleCueKey = cueKey;
        state.activeSubtitleCues = cueTrack.cues;
        state.lastSubtitleText = "";
        updateSubtitleOverlay();
    }

    function findActiveSubtitleText(cues, time) {
        var text = [];
        var i;

        for (i = 0; i < cues.length; i += 1) {
            if (cues[i].end < time) {
                continue;
            }
            if (cues[i].start > time) {
                break;
            }
            text.push(cues[i].text);
        }

        return text.join("\n");
    }

    function updateSubtitleOverlay() {
        var cues = state.activeSubtitleCues || [];
        var time;
        var text;

        if (!elements.subtitleOverlay || state.screen !== "player" || !cues.length) {
            if (elements.subtitleOverlay) {
                elements.subtitleOverlay.hidden = true;
            }
            return;
        }

        time = player && player.getCurrentTime ? player.getCurrentTime() : (elements.video.currentTime || 0);
        time -= state.subtitleDelay || 0;
        text = findActiveSubtitleText(cues, Math.max(0, time));

        if (text === state.lastSubtitleText) {
            return;
        }

        state.lastSubtitleText = text;
        elements.subtitleOverlay.textContent = text;
        elements.subtitleOverlay.hidden = !text;
    }

    function startSubtitleOverlayTimer() {
        stopSubtitleOverlayTimer();
        state.subtitleOverlayTimer = setInterval(updateSubtitleOverlay, 250);
    }

    function stopSubtitleOverlayTimer() {
        if (state.subtitleOverlayTimer) {
            clearInterval(state.subtitleOverlayTimer);
            state.subtitleOverlayTimer = null;
        }
    }

    function clearSubtitleTrackElements() {
        Array.prototype.slice.call(elements.video.querySelectorAll("track[data-app-subtitle='1']")).forEach(function (track) {
            if (track.dataset.cueKey) {
                delete state.subtitleCueTracks[track.dataset.cueKey];
            }
            if (track.dataset.blobUrl) {
                URL.revokeObjectURL(track.dataset.blobUrl);
            }
            track.parentNode.removeChild(track);
        });
        clearManualSubtitleOverlay();
    }

    function addSubtitleTrack(subtitle, index) {
        var lowerUrl = subtitle.url.split("?")[0].toLowerCase();
        var isSrt = lowerUrl.indexOf(".srt") !== -1;

        function attachLoadedText(text) {
            var vttText = isSrt ? convertSrtToVtt(text) : normalizeVttText(text);
            var blobUrl = URL.createObjectURL(new Blob([vttText], { type: "text/vtt" }));
            appendSubtitleTrack(blobUrl, subtitle, index, blobUrl, vttText);
        }

        requestTextWithTimeout(subtitle.url, 12000, function (error, text) {
            if (!error && looksLikeSubtitleText(text)) {
                attachLoadedText(text);
                return;
            }

            requestExternalText(subtitle.url, function (proxyError, proxyText) {
                if (proxyError || !looksLikeSubtitleText(proxyText)) {
                    console.log("Could not load subtitle track:", subtitle.url, proxyError ? proxyError.message : "invalid subtitle text");
                    return;
                }
                attachLoadedText(proxyText);
            });
        });
    }

    function normalizeVttText(text) {
        var value = String(text || "").replace(/^\uFEFF/, "");
        if (value.indexOf("WEBVTT") === 0) {
            return value;
        }
        return "WEBVTT\n\n" + value;
    }

    function appendSubtitleTrack(src, subtitle, index, blobUrl, cueText) {
        var track = document.createElement("track");
        var cueKey = cueText ? createSubtitleCueTrack(subtitle, cueText) : "";

        track.kind = "subtitles";
        track.label = subtitle.label || ("Subtitle " + (index + 1));
        track.srclang = subtitle.language || "und";
        track.src = src;
        track.dataset.appSubtitle = "1";
        if (cueKey) {
            track.dataset.cueKey = cueKey;
        }
        if (blobUrl) {
            track.dataset.blobUrl = blobUrl;
        }
        track.addEventListener("load", refreshPlayerOptions);
        track.addEventListener("error", function () {
            console.log("Subtitle track failed to load:", src);
        });
        elements.video.appendChild(track);
        try {
            track.track.mode = "hidden";
        } catch (error) {}
        setTimeout(refreshPlayerOptions, 250);
    }

    function convertSrtToVtt(text) {
        return "WEBVTT\n\n" + String(text || "")
            .replace(/\r+/g, "")
            .replace(/^\uFEFF/, "")
            .replace(/(\d{2}:\d{2}:\d{2}),(\d{3})/g, "$1.$2");
    }

    function playItem(item, returnScreen, resumeChoice) {
        var progress = getSavedPlaybackProgress(item);

        if (resumeChoice !== "continue" && resumeChoice !== "restart" && progress && progress.time > 30) {
            showResumeDialog(item, returnScreen, progress);
            return;
        }

        state.screen = "player";
        state.returnScreen = returnScreen;
        state.playingItem = item;
        state.subtitleSelectionLocked = false;
        state.subtitleDelay = 0;
        showScreen("player");
        setFocusedControl("play");
        showOverlay();
        startSubtitleOverlayTimer();

        ensureSeriesPlaybackContext(item, state.detailItem);
        startPlaybackWithSubtitles(item, resumeChoice === "continue" ? progress.time : 0);
        loadPlaybackSubtitlesInBackground(item);
    }

    function loadPlaybackSubtitlesInBackground(item) {
        if (!needsSubtitleMetadata(item) && !needsRemoteSubtitles(item)) {
            return;
        }

        ensurePlaybackSubtitles(item, function () {
            applyLoadedSubtitlesToPlayer(item);
        });
    }

    function showResumeDialog(item, returnScreen, progress) {
        state.pendingResume = { item: item, returnScreen: returnScreen, progress: progress };
        elements.resumeDialogText.textContent = "\u062a\u0648\u0642\u0641\u062a \u0639\u0646\u062f " + formatTimeLabel(progress.time) + ". \u0647\u0644 \u062a\u0631\u064a\u062f \u0627\u0644\u0645\u062a\u0627\u0628\u0639\u0629\u061f";
        elements.resumeDialog.hidden = false;
        elements.resumeContinueButton.focus();
    }

    function runPendingResume(shouldContinue) {
        var pending = state.pendingResume;

        if (!pending) {
            return;
        }

        elements.resumeDialog.hidden = true;
        state.pendingResume = null;
        playItem(pending.item, pending.returnScreen, shouldContinue ? "continue" : "restart");
    }

    function startPlaybackWithSubtitles(item, resumeTime) {
        player.load(item);
        prepareSubtitleTracks(item);
        if (resumeTime > 0) {
            seekAfterMetadata(resumeTime);
        } else {
            clearPlaybackProgress(item);
        }
        setTimeout(refreshPlayerOptions, 250);
        setTimeout(refreshPlayerOptions, 900);
        setTimeout(refreshPlayerOptions, 1800);
        setTimeout(refreshPlayerOptions, 3500);
        setTimeout(refreshPlayerOptions, 6000);
    }

    function seekAfterMetadata(seconds) {
        var applied = false;
        function applySeek() {
            var duration = player.getDuration ? player.getDuration() : elements.video.duration;
            if (applied || !isFinite(duration) || duration <= 0) {
                return;
            }
            applied = true;
            if (player.seekTo) {
                player.seekTo(Math.min(seconds, Math.max(0, duration - 10)));
            } else {
                elements.video.currentTime = Math.min(seconds, Math.max(0, duration - 10));
            }
        }
        elements.video.addEventListener("loadedmetadata", applySeek, { once: true });
        setTimeout(applySeek, 1200);
        setTimeout(applySeek, 2500);
    }

    function getPlaybackProgressKey(item) {
        return "nexoProgress:" + getLibraryWorkKey(item);
    }

    function getSavedPlaybackProgress(item) {
        try {
            return JSON.parse(window.localStorage.getItem(getPlaybackProgressKey(item)) || "null");
        } catch (error) {
            return null;
        }
    }

    function clearPlaybackProgress(item) {
        try {
            window.localStorage.removeItem(getPlaybackProgressKey(item));
        } catch (error) {
            console.log("Could not clear playback progress:", error.message);
        }
    }

    function savePlaybackProgress() {
        var item = state.playingItem;
        var current = player.getCurrentTime ? player.getCurrentTime() : (elements.video.currentTime || 0);
        var duration = player.getDuration ? player.getDuration() : (elements.video.duration || 0);
        var now = Date.now();

        if (!item || now - state.lastProgressSaveAt < 4000) {
            return;
        }

        state.lastProgressSaveAt = now;
        if (duration && isFinite(duration) && current > duration - 20) {
            clearPlaybackProgress(item);
            return;
        }

        if (current < 15) {
            return;
        }

        try {
            window.localStorage.setItem(getPlaybackProgressKey(item), JSON.stringify({
                time: Math.floor(current),
                duration: isFinite(duration) ? Math.floor(duration) : 0,
                savedAt: now
            }));
        } catch (error) {
            console.log("Could not save playback progress:", error.message);
        }
    }

    function formatTimeLabel(seconds) {
        var total = Math.max(0, Math.floor(seconds || 0));
        var hours = Math.floor(total / 3600);
        var minutes = Math.floor((total % 3600) / 60);
        var secs = total % 60;

        if (hours) {
            return hours + ":" + pad2(minutes) + ":" + pad2(secs);
        }
        return minutes + ":" + pad2(secs);
    }

    function pad2(value) {
        return value < 10 ? "0" + value : String(value);
    }

    function refreshPlayerOptions() {
        populateAudioTracks();
        populateSubtitleTracks();
        refreshOpenPlayerTool();
        autoSelectFirstSubtitleTrack();
        applySubtitleStyle();
        applyScreenMode();
    }

    function populateAudioTracks() {
        var tracks = player.getAudioTracks();
        var options = '<option value="-1">Ø§Ù„ØµÙˆØª Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠ</option>';

        for (var i = 0; i < tracks.length; i += 1) {
            options += '<option value="' + tracks[i].id + '"' + (tracks[i].selected ? " selected" : "") + '>' + escapeHtml(tracks[i].label) + '</option>';
        }

        elements.audioLanguageSelect.innerHTML = options;
    }

    function populateSubtitleTracks() {
        var tracks = player.getSubtitleTracks();
        var options = '<option value="-1">Ø¨Ø¯ÙˆÙ† ØªØ±Ø¬Ù…Ø©</option>';

        for (var i = 0; i < tracks.length; i += 1) {
            options += '<option value="' + tracks[i].id + '"' + (tracks[i].selected ? " selected" : "") + '>' + escapeHtml(tracks[i].label) + '</option>';
        }

        elements.subtitleSelect.innerHTML = options;
    }

    function populateAudioTracks() {
        var tracks = player.getAudioTracks();
        var labels = getLanguageLabels(state.language);
        var options = '<option value="-1">' + escapeHtml(labels.defaultAudio) + '</option>';

        for (var i = 0; i < tracks.length; i += 1) {
            options += '<option value="' + tracks[i].id + '"' + (tracks[i].selected ? " selected" : "") + '>' + escapeHtml(tracks[i].label) + '</option>';
        }

        elements.audioLanguageSelect.innerHTML = options;
    }

    function populateSubtitleTracks() {
        var tracks = player.getSubtitleTracks();
        var labels = getLanguageLabels(state.language);
        var options = '<option value="-1">' + escapeHtml(labels.noSubtitle) + '</option>';
        var hasTracks = tracks.length > 0;

        for (var i = 0; i < tracks.length; i += 1) {
            options += '<option value="' + tracks[i].id + '"' + (tracks[i].selected ? " selected" : "") + '>' + escapeHtml(tracks[i].label) + '</option>';
        }

        elements.subtitleSelect.innerHTML = options;
        elements.subtitleSelect.disabled = !hasTracks;
        elements.subtitleToolButton.hidden = !hasTracks;
        if (!hasTracks && state.activePlayerTool === "subtitles") {
            closePlayerToolPanel();
        }
    }

    function openPlayerTool(toolName) {
        if (toolName === "favorite") {
            togglePlayingFavorite();
            setFocusedControl("favorite");
            return;
        }
        state.playerToolOptionIndex = -1;
        state.activePlayerTool = state.activePlayerTool === toolName && !elements.playerToolPanel.hidden ? "" : toolName;
        renderPlayerToolPanel();
        setFocusedControl(toolName || "play");
    }

    function refreshOpenPlayerTool() {
        if (state.activePlayerTool && !elements.playerToolPanel.hidden) {
            renderPlayerToolPanel();
        }
    }

    function renderPlayerToolPanel() {
        var labels = getLanguageLabels(state.language);
        var tool = state.activePlayerTool;

        elements.playerToolPanel.hidden = !tool;
        elements.subtitleToolButton.classList.toggle("is-open", tool === "subtitles");
        elements.subtitleSettingsToolButton.classList.toggle("is-open", tool === "subtitle-settings");
        elements.screenToolButton.classList.toggle("is-open", tool === "screen");
        elements.favoriteToolButton.classList.remove("is-open");

        if (!tool) {
            elements.playerToolOptions.innerHTML = "";
            return;
        }

        if (tool === "subtitles") {
            renderSubtitleTool(labels);
        } else if (tool === "subtitle-settings") {
            renderSubtitleSettingsTool(labels);
        } else {
            renderScreenTool(labels);
        }
        syncPlayerToolOptionFocus();
    }

    function renderSubtitleTool(labels) {
        populateSubtitleTracks();
        var tracks = player.getSubtitleTracks();
        var current = elements.subtitleSelect.value || "-1";
        var options = [{ id: "-1", label: labels.noSubtitle, type: "subtitle" }].concat(tracks.map(function (track) {
            return { id: String(track.id), label: track.label, type: "subtitle" };
        }));

        elements.playerToolTitle.textContent = labels.subtitleTitle;
        elements.playerToolOptions.className = "player-tool-options";
        elements.playerToolOptions.innerHTML = options.map(function (option) {
            return '<button class="player-tool-option' + (String(option.id) === String(current) ? " is-selected" : "") + (option.type === "subtitle-missing" ? " is-muted" : "") + '" type="button" data-option-type="' + escapeHtml(option.type) + '" data-value="' + escapeHtml(option.id) + '">' + escapeHtml(option.label) + '</button>';
        }).join("");
    }

    function isPlayerToolPanelOpen() {
        return !!state.activePlayerTool && elements.playerToolPanel && !elements.playerToolPanel.hidden;
    }

    function getPlayerToolOptions() {
        return Array.prototype.slice.call(elements.playerToolOptions.querySelectorAll(".player-tool-option"));
    }

    function movePlayerToolOption(direction) {
        var options = getPlayerToolOptions();

        if (!options.length) {
            return;
        }

        if (state.playerToolOptionIndex < 0) {
            state.playerToolOptionIndex = 0;
        } else {
            state.playerToolOptionIndex = Math.max(0, Math.min(options.length - 1, state.playerToolOptionIndex + direction));
        }
        syncPlayerToolOptionFocus();
    }

    function getPlayerToolColumnCount() {
        if (elements.playerToolOptions.classList.contains("subtitle-settings")) {
            return 3;
        }

        return 2;
    }

    function movePlayerToolOptionByGrid(horizontal, vertical) {
        var options = getPlayerToolOptions();
        var columns = getPlayerToolColumnCount();
        var index = state.playerToolOptionIndex;
        var next;

        if (!options.length) {
            return;
        }

        if (index < 0) {
            index = 0;
        }

        if (horizontal) {
            if (horizontal < 0 && index % columns === 0) {
                closePlayerToolPanel();
                return;
            }
            next = index + horizontal;
            if (next >= 0 && next < options.length && Math.floor(next / columns) === Math.floor(index / columns)) {
                state.playerToolOptionIndex = next;
            }
        } else if (vertical) {
            next = index + (vertical * columns);
            if (next >= 0 && next < options.length) {
                state.playerToolOptionIndex = next;
            }
        }

        syncPlayerToolOptionFocus();
    }

    function closePlayerToolPanel() {
        var tool = state.activePlayerTool;

        state.activePlayerTool = null;
        state.playerToolOptionIndex = -1;
        renderPlayerToolPanel();
        setFocusedControl(tool || "play");
    }

    function syncPlayerToolOptionFocus() {
        var options = getPlayerToolOptions();
        var selectedIndex = -1;

        if (!options.length) {
            state.playerToolOptionIndex = -1;
            return;
        }

        options.forEach(function (option, index) {
            if (selectedIndex === -1 && option.classList.contains("is-selected")) {
                selectedIndex = index;
            }
        });

        if (state.playerToolOptionIndex < 0) {
            state.playerToolOptionIndex = selectedIndex >= 0 ? selectedIndex : 0;
        }
        if (state.playerToolOptionIndex >= options.length) {
            state.playerToolOptionIndex = options.length - 1;
        }

        options.forEach(function (option, index) {
            option.classList.toggle("is-focused", index === state.playerToolOptionIndex);
        });
        options[state.playerToolOptionIndex].focus();
    }

    function applyFocusedPlayerToolOption() {
        var options = getPlayerToolOptions();
        var option = options[state.playerToolOptionIndex];

        if (option) {
            applyPlayerToolOption(option);
        }
    }

    function getFallbackSubtitleLanguages(labels) {
        return [
            { id: "missing-ar", label: state.language === "ar" ? "\u0627\u0644\u0639\u0631\u0628\u064a\u0629" : "Arabic", type: "subtitle-missing" },
            { id: "missing-en", label: "English", type: "subtitle-missing" },
            { id: "missing-tr", label: state.language === "ar" ? "\u0627\u0644\u062a\u0631\u0643\u064a\u0629" : "Turkish", type: "subtitle-missing" },
            { id: "missing-fr", label: state.language === "ar" ? "\u0627\u0644\u0641\u0631\u0646\u0633\u064a\u0629" : "French", type: "subtitle-missing" },
            { id: "missing-de", label: state.language === "ar" ? "\u0627\u0644\u0623\u0644\u0645\u0627\u0646\u064a\u0629" : "German", type: "subtitle-missing" },
            { id: "missing-es", label: state.language === "ar" ? "\u0627\u0644\u0625\u0633\u0628\u0627\u0646\u064a\u0629" : "Spanish", type: "subtitle-missing" }
        ];
    }

    function renderSubtitleSettingsTool(labels) {
        var style = elements.subtitleStyleSelect.value || "normal";
        var size = style === "large" || style === "large-boxed" ? "large" : "normal";
        var box = style === "boxed" || style === "large-boxed";

        elements.playerToolTitle.textContent = labels.subtitleSettingsTitle;
        elements.playerToolOptions.className = "player-tool-options subtitle-settings";
        elements.playerToolOptions.innerHTML =
            '<div class="subtitle-preview">' + escapeHtml(labels.subtitleSample) + '</div>' +
            '<button class="player-tool-option' + (size === "normal" ? " is-selected" : "") + '" type="button" data-option-type="subtitle-size" data-value="normal">Aa ' + escapeHtml(labels.normal) + '</button>' +
            '<button class="player-tool-option' + (size === "large" ? " is-selected" : "") + '" type="button" data-option-type="subtitle-size" data-value="large">Aa ' + escapeHtml(labels.large) + '</button>' +
            '<button class="player-tool-option' + (!box ? " is-selected" : "") + '" type="button" data-option-type="subtitle-box" data-value="0">' + escapeHtml(labels.noBox) + '</button>' +
            '<button class="player-tool-option' + (box ? " is-selected" : "") + '" type="button" data-option-type="subtitle-box" data-value="1">' + escapeHtml(labels.boxed) + '</button>' +
            '<button class="player-tool-option" type="button" data-option-type="subtitle-delay" data-value="-1">-1s</button>' +
            '<button class="player-tool-option" type="button" data-option-type="subtitle-delay" data-value="1">+1s</button>';
    }

    function renderScreenTool(labels) {
        var current = elements.screenModeSelect.value || "contain";
        var options = [
            { id: "contain", label: labels.fit },
            { id: "cover", label: "Zoom" },
            { id: "fill", label: labels.fullscreen },
            { id: "wide", label: "Wide" }
        ];

        elements.playerToolTitle.textContent = labels.screenSettingsTitle;
        elements.playerToolOptions.className = "player-tool-options";
        elements.playerToolOptions.innerHTML = options.map(function (option) {
            return '<button class="player-tool-option' + (option.id === current ? " is-selected" : "") + '" type="button" data-option-type="screen" data-value="' + escapeHtml(option.id) + '">' + escapeHtml(option.label) + '</button>';
        }).join("");
    }

    function applyPlayerToolOption(option) {
        var type = option.getAttribute("data-option-type");
        var value = option.getAttribute("data-value");

        if (type === "subtitle") {
            elements.subtitleSelect.value = value;
            applySubtitleSelection();
        } else if (type === "subtitle-missing") {
            elements.subtitleSelect.value = "-1";
            applySubtitleSelection();
        } else if (type === "screen") {
            elements.screenModeSelect.value = value;
            applyScreenMode();
        } else if (type === "subtitle-size") {
            setSubtitleStyleValue(value, null);
        } else if (type === "subtitle-box") {
            setSubtitleStyleValue(null, value === "1");
        } else if (type === "subtitle-delay") {
            state.subtitleDelay = (state.subtitleDelay || 0) + parseInt(value, 10);
            updateSubtitleOverlay();
        }

        renderPlayerToolPanel();
    }

    function togglePlayingFavorite() {
        var item = state.playingItem;
        var key;

        if (!item) {
            return;
        }

        key = getLibraryWorkKey(item);
        try {
            window.localStorage.setItem("nexoWishlist:" + key, JSON.stringify({
                title: item.title,
                section: item.section,
                addedAt: Date.now()
            }));
        } catch (error) {
            console.log("Could not save favorite from player:", error.message);
        }
    }

    function setSubtitleStyleValue(size, boxed) {
        var current = elements.subtitleStyleSelect.value || "normal";
        var isLarge = size ? size === "large" : (current === "large" || current === "large-boxed");
        var hasBox = boxed === null ? (current === "boxed" || current === "large-boxed") : boxed;

        elements.subtitleStyleSelect.value = isLarge && hasBox ? "large-boxed" : (isLarge ? "large" : (hasBox ? "boxed" : "normal"));
        applySubtitleStyle();
    }

    function applyAudioTrackSelection() {
        var selected = parseInt(elements.audioLanguageSelect.value, 10);
        player.setAudioTrack(selected);
    }

    function applySubtitleSelection() {
        var selected = parseInt(elements.subtitleSelect.value, 10);
        state.subtitleSelectionLocked = true;
        player.setSubtitleTrack(selected);
        activateManualSubtitleForSelection(isNaN(selected) ? -1 : selected);
    }

    function autoSelectFirstSubtitleTrack() {
        var tracks;
        var selected;

        if (state.screen !== "player" || state.subtitleSelectionLocked) {
            return;
        }

        tracks = player.getSubtitleTracks();
        if (!tracks.length) {
            return;
        }

        selected = tracks[0].id;
        state.subtitleSelectionLocked = true;
        elements.subtitleSelect.value = String(selected);
        player.setSubtitleTrack(parseInt(selected, 10));
        activateManualSubtitleForSelection(parseInt(selected, 10));
        populateSubtitleTracks();
        refreshOpenPlayerTool();
        console.log("[subtitles] Auto-selected first subtitle:", tracks[0].label);
    }

    function applySubtitleStyle() {
        var style = elements.subtitleStyleSelect ? elements.subtitleStyleSelect.value : "normal";
        elements.video.classList.toggle("subtitle-large", style === "large" || style === "large-boxed");
        elements.video.classList.toggle("subtitle-boxed", style === "boxed" || style === "large-boxed");
    }

    function applyScreenMode() {
        var mode = elements.screenModeSelect ? elements.screenModeSelect.value : "contain";
        ["contain", "cover", "fill", "wide"].forEach(function (name) {
            elements.video.classList.toggle("screen-" + name, mode === name);
        });
    }

    function goHome() {
        state.screen = "home";
        showScreen("home");
        renderHome();
    }

    function goBackFromPlayer() {
        state.screen = state.returnScreen;
        clearTimeout(state.overlayTimer);
        stopSubtitleOverlayTimer();
        clearManualSubtitleOverlay();
        state.lastProgressSaveAt = 0;
        savePlaybackProgress();
        player.stop();
        state.playingItem = null;
        showScreen(state.returnScreen);

        if (state.returnScreen === "library") {
            renderLibrary();
        } else if (state.returnScreen === "detail") {
            renderDetail(state.detailItem);
            setDetailAction(state.detailActionIndex);
        } else {
            renderHome();
        }
    }

    function goBackFromDetail() {
        state.screen = state.detailReturnScreen;
        showScreen(state.detailReturnScreen);

        if (state.detailReturnScreen === "home") {
            renderHome();
        } else if (state.detailReturnScreen === "history") {
            renderHistoryScreen();
        } else {
            renderLibrary();
        }
    }

    function showScreen(screenName) {
        if (screenName !== "library" && elements.virtualKeyboard) {
            elements.virtualKeyboard.hidden = true;
        }

        if (screenName !== "library") {
            stopLivePreview();
        }

        elements.homeScreen.classList.toggle("is-active", screenName === "home");
        elements.playlistScreen.classList.toggle("is-active", screenName === "playlist");
        elements.accountScreen.classList.toggle("is-active", screenName === "account");
        elements.historyScreen.classList.toggle("is-active", screenName === "history");
        elements.settingsScreen.classList.toggle("is-active", screenName === "settings");
        elements.libraryScreen.classList.toggle("is-active", screenName === "library");
        elements.detailScreen.classList.toggle("is-active", screenName === "detail");
        elements.playerScreen.classList.toggle("is-active", screenName === "player");
    }

    function setFocusedSection(index) {
        var cards = elements.sectionList.querySelectorAll(".section-card");

        if (!cards.length) {
            return;
        }

        state.sectionIndex = index;
        cards.forEach(function (card, cardIndex) {
            card.classList.toggle("is-focused", cardIndex === index);
        });
        cards[index].focus();
    }

    function moveHomeFocus(horizontal, vertical) {
        if (vertical < 0) {
            if (state.homePane === "latest") {
                state.homePane = "actions";
            } else if (state.homePane === "actions") {
                state.homePane = "sections";
            }
        } else if (vertical > 0) {
            if (state.homePane === "sections") {
                state.homePane = "actions";
            } else if (state.homePane === "actions") {
                state.homePane = "latest";
            }
        } else if (horizontal) {
            if (state.homePane === "sections") {
                state.sectionIndex = Math.max(0, Math.min(SECTIONS.length - 1, state.sectionIndex + horizontal));
            } else if (state.homePane === "actions") {
                state.homeActionIndex = Math.max(0, Math.min(elements.homeActionList.querySelectorAll(".home-action-button").length - 1, state.homeActionIndex + horizontal));
            } else if (state.homePane === "latest") {
                state.latestIndex = Math.max(0, Math.min(latestItems.length - 1, state.latestIndex + horizontal));
            }
        }

        syncHomeFocus();
    }

    function syncHomeFocus() {
        var cards = elements.sectionList.querySelectorAll(".section-card");
        var actions = elements.homeActionList.querySelectorAll(".home-action-button");
        var latest = elements.latestList.querySelectorAll(".latest-card");
        var target = null;

        cards.forEach(function (card, index) {
            card.classList.toggle("is-focused", state.homePane === "sections" && index === state.sectionIndex);
        });
        actions.forEach(function (button, index) {
            button.classList.toggle("is-focused", state.homePane === "actions" && index === state.homeActionIndex);
        });
        latest.forEach(function (button, index) {
            button.classList.toggle("is-focused", state.homePane === "latest" && index === state.latestIndex);
        });

        if (state.homePane === "sections") {
            target = cards[state.sectionIndex];
        } else if (state.homePane === "actions") {
            target = actions[state.homeActionIndex];
        } else {
            target = latest[state.latestIndex];
        }

        if (target) {
            target.focus();
            target.scrollIntoView({ block: "center", inline: "nearest" });
        }
    }

    function activateHomeFocus() {
        var actionButtons = elements.homeActionList.querySelectorAll(".home-action-button");

        if (state.homePane === "sections") {
            openLibrary(SECTIONS[state.sectionIndex].id);
        } else if (state.homePane === "actions" && actionButtons[state.homeActionIndex]) {
            runHomeAction(actionButtons[state.homeActionIndex].getAttribute("data-action"));
        } else if (state.homePane === "latest") {
            openLatestItem(state.latestIndex);
        }
    }

    function runHomeAction(action) {
        if (action === "account") {
            openAccountScreen();
        } else if (action === "settings") {
            openSettingsScreen();
        } else if (action === "history") {
            openHistoryScreen();
        } else {
            openPlaylistScreen();
        }
    }

    function openHistoryScreen() {
        state.screen = "history";
        showScreen("history");
        renderHistoryScreen();
        focusFirstManagementControl(".poster-card");
    }

    function renderHistoryScreen() {
        var labels = getLanguageLabels(state.language);
        setText(elements.historyHeading, labels.historyFavorites);
        setText(elements.recentHeading, labels.recentlyWatched);
        setText(elements.favoriteHeading, labels.favorites);
        renderHistoryList(elements.recentList, getRecentItems(), labels.noRecent);
        renderHistoryList(elements.favoriteList, getFavoriteItems(), labels.noFavorites);
    }

    function renderHistoryList(container, items, emptyText) {
        container.innerHTML = items.length ? items.map(function (item, index) {
            return '<button class="poster-card" type="button" data-index="' + index + '" data-key="' + escapeHtml(getLibraryWorkKey(item)) + '">' +
                '<span class="poster-art" style="' + getPosterStyle(item) + '">' + getPosterFallbackHtml(item) + '</span>' +
                '<span class="poster-title">' + escapeHtml(item.title) + '</span>' +
                '<span class="poster-meta">' + escapeHtml(getSection(item.section).subtitle) + '</span>' +
            '</button>';
        }).join("") : '<div class="history-empty">' + escapeHtml(emptyText) + '</div>';
    }

    function handleHistoryItemClick(event) {
        var card = event.target.closest(".poster-card");
        var item;

        if (!card) {
            return;
        }

        item = findItemByLibraryKey(card.getAttribute("data-key"));
        if (item) {
            state.activeSection = item.section;
            openDetail(item, "history");
        }
    }

    function getRecentItems() {
        return getStoredItemKeys("nexoProgress:").map(function (entry) {
            return {
                item: findItemByLibraryKey(entry.key.replace("nexoProgress:", "")),
                savedAt: entry.value && entry.value.savedAt ? entry.value.savedAt : 0
            };
        }).filter(function (entry) {
            return entry.item;
        }).sort(function (a, b) {
            return b.savedAt - a.savedAt;
        }).slice(0, 12).map(function (entry) {
            return entry.item;
        });
    }

    function getFavoriteItems() {
        return getStoredItemKeys("nexoWishlist:").map(function (entry) {
            return findItemByLibraryKey(entry.key.replace("nexoWishlist:", ""));
        }).filter(Boolean).slice(0, 12);
    }

    function getStoredItemKeys(prefix) {
        var entries = [];
        var i;

        try {
            for (i = 0; i < window.localStorage.length; i += 1) {
                var key = window.localStorage.key(i);
                if (key && key.indexOf(prefix) === 0) {
                    entries.push({ key: key, value: JSON.parse(window.localStorage.getItem(key) || "null") });
                }
            }
        } catch (error) {
            console.log("Could not read stored items:", error.message);
        }

        return entries;
    }

    function findItemByLibraryKey(key) {
        for (var i = 0; i < allItems.length; i += 1) {
            if (getLibraryWorkKey(allItems[i]) === key) {
                return allItems[i];
            }
        }
        return null;
    }

    function openAccountScreen() {
        state.screen = "account";
        showScreen("account");
        renderAccountScreen();
        focusFirstManagementControl("#accountDashboardButton");
    }

    function renderAccountScreen() {
        var data = state.activation || {};
        var access = data.access || {};
        var endDate = data.active_until || data.trial_ends_at || "";

        elements.accountInfo.innerHTML =
            '<span><strong>MAC</strong>' + escapeHtml(state.deviceMac || data.mac || "ØºÙŠØ± Ù…ØªÙˆÙØ±") + '</span>' +
            '<span><strong>Ø§Ù„Ø­Ø§Ù„Ø©</strong>' + escapeHtml(access.reason || "offline") + '</span>' +
            '<span><strong>Ø¨Ø¯Ø§ÙŠØ© Ø§Ù„ØªØ¬Ø±Ø¨Ø©</strong>' + escapeHtml(formatServerDate(data.trial_started_at || "")) + '</span>' +
            '<span><strong>Ù†Ù‡Ø§ÙŠØ© Ø§Ù„ØªØ¬Ø±Ø¨Ø© / Ø§Ù„Ø®Ø·Ø©</strong>' + escapeHtml(formatServerDate(endDate || "")) + '</span>' +
            '<span><strong>Ø±Ø§Ø¨Ø· Ø§Ù„Ù…ÙˆÙ‚Ø¹</strong>' + escapeHtml(APP_SERVER_URL || "ØºÙŠØ± Ù…Ø¶Ø¨ÙˆØ·") + '</span>' +
            '<span><strong>Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„ØªØ´ØºÙŠÙ„</strong>' + escapeHtml(data.playlist && data.playlist.type ? data.playlist.type : "ØºÙŠØ± Ù…Ø¶Ø§ÙØ©") + '</span>';
    }

    function renderAccountScreen() {
        var data = state.activation || {};
        var access = data.access || {};
        var endDate = data.active_until || data.trial_ends_at || "";
        var labels = getLanguageLabels(state.language);

        elements.accountInfo.innerHTML =
            '<span><strong>MAC</strong>' + escapeHtml(state.deviceMac || data.mac || labels.unavailable) + '</span>' +
            '<span><strong>' + escapeHtml(labels.status) + '</strong>' + escapeHtml(formatStatusLabel(access.reason || "offline")) + '</span>' +
            '<span><strong>' + escapeHtml(labels.start) + '</strong>' + escapeHtml(formatOptionalDate(data.trial_started_at, labels)) + '</span>' +
            '<span><strong>' + escapeHtml(labels.end) + '</strong>' + escapeHtml(formatOptionalDate(endDate, labels)) + '</span>' +
            '<span><strong>' + escapeHtml(labels.siteUrl) + '</strong>' + escapeHtml(APP_SERVER_URL || labels.notConfigured) + '</span>' +
            '<span><strong>' + escapeHtml(labels.playlistType) + '</strong>' + escapeHtml(data.playlist && data.playlist.type ? data.playlist.type : labels.notAdded) + '</span>';
    }

    function openSettingsScreen() {
        state.screen = "settings";
        showScreen("settings");
        elements.appLanguageSelect.value = state.language;
        elements.playerModeSelect.value = state.playerMode;
        elements.settingsMessage.hidden = true;
        setSettingsPanel("language");
        syncSettingsButtons();
        focusFirstManagementControl(".settings-nav-button");
    }

    function openCustomerDashboard() {
        if (!APP_SERVER_URL) {
            return;
        }

        window.location.href = normalizeServerUrl(APP_SERVER_URL) + "/dashboard.php?mac=" + encodeURIComponent(state.deviceMac || "");
    }

    function openAccountAction() {
        var checkoutUrl;

        if (!APP_SERVER_URL) {
            elements.playlistStatus.textContent = "MAC: " + (state.deviceMac || "unknown") + " - set APP_SERVER_URL to enable online activation";
            return;
        }

        checkoutUrl = normalizeServerUrl(APP_SERVER_URL) + "/api/create_checkout.php";
        setPlaylistLoading(true, "Ø¬Ø§Ø±ÙŠ ØªØ¬Ù‡ÙŠØ² ØµÙØ­Ø© Ø§Ù„Ø¯ÙØ¹...");
        postJson(checkoutUrl, { mac: state.deviceMac }, function (error, data) {
            setPlaylistLoading(false);
            if (error || !data || !data.ok || !data.checkout_url) {
                elements.playlistStatus.textContent = "Could not create Stripe checkout. Check server config.";
                console.log("Stripe checkout failed:", error ? error.message : data);
                return;
            }

            elements.playlistStatus.textContent = "Opening Stripe checkout...";
            window.location.href = data.checkout_url;
        });
    }

    function openPlaylistSetupFlow() {
        openPlaylistScreen();
        return;
        var type = null;
        var payload = { mac: state.deviceMac || getStoredBrowserDeviceId() };
        var serverUrl;
        var username;
        var password;
        var playlistUrl;

        if (type === null) {
            return;
        }

        if (String(type).trim() === "2") {
            serverUrl = "";
            username = "";
            password = "";

            if (!serverUrl || !username || !password) {
                elements.playlistStatus.textContent = "Playlist was not saved";
                return;
            }

            payload.type = "xtream";
            payload.server_url = serverUrl;
            payload.username = username;
            payload.password = password;
            payload.output = "m3u8";
            playlistUrl = buildXtreamPlaylistUrl(serverUrl, username, password, payload.output);
        } else {
            playlistUrl = "";
            if (!playlistUrl) {
                elements.playlistStatus.textContent = "Playlist was not saved";
                return;
            }

            payload.type = "m3u";
            payload.playlist_url = playlistUrl;
        }

        IPTV_PLAYLIST_URL = playlistUrl;
        // Legacy prompt flow is disabled; playlists are saved only on the website.
        resetLibraryForReload();

        if (!APP_SERVER_URL) {
            elements.playlistStatus.textContent = "Playlist must be saved on the website";
            loadIptvPlaylist();
            return;
        }

        setPlaylistLoading(true, "Ø¬Ø§Ø±ÙŠ Ø­ÙØ¸ Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„ØªØ´ØºÙŠÙ„ Ø¹Ù„Ù‰ Ø§Ù„Ø³ÙŠØ±ÙØ±...");
        postJson(normalizeServerUrl(APP_SERVER_URL) + "/api/playlist.php", payload, function (error, data) {
            setPlaylistLoading(false);
            if (error || !data || !data.ok) {
                console.log("Could not save playlist on server:", error ? error.message : data);
                elements.playlistStatus.textContent = "Playlist saved on this device only";
            } else {
                elements.playlistStatus.textContent = "Playlist saved for MAC: " + payload.mac;
            }
            loadIptvPlaylist();
        });
    }

    function buildXtreamPlaylistUrl(serverUrl, username, password, output) {
        return String(serverUrl || "").replace(/\/+$/, "") +
            "/get.php?username=" + encodeURIComponent(username || "") +
            "&password=" + encodeURIComponent(password || "") +
            "&type=m3u_plus&output=" + encodeURIComponent(output || "m3u8");
    }

    function normalizePlaylistFormUrl(value) {
        var url = String(value || "").trim();

        if (!url) {
            return "";
        }

        if (!/^https?:\/\//i.test(url)) {
            url = "http://" + url;
        }

        return forceXtreamPlaylistHlsOutput(url);
    }

    function openPlaylistScreen() {
        state.screen = "playlist";
        showScreen("playlist");
        renderPlaylistScreen();
        focusFirstManagementControl("#playlistType");
    }

    function renderPlaylistScreen() {
        var playlist = (state.activation && state.activation.playlist) || {};
        var type = playlist.type || "m3u";

        elements.playlistDeviceInfo.textContent = "MAC: " + (state.deviceMac || "unknown") + " | Server: " + (APP_SERVER_URL || "not configured");
        elements.playlistType.value = type;
        elements.playlistUrlInput.value = playlist.playlist_url || (type === "m3u" ? IPTV_PLAYLIST_URL : "");
        elements.playlistServerInput.value = playlist.server_url || "";
        elements.playlistUsernameInput.value = playlist.username || "";
        elements.playlistPasswordInput.value = playlist.password || "";
        elements.playlistFormMessage.hidden = true;
        elements.playlistCurrentInfo.innerHTML = playlist.generated_url ?
            '<strong>Ø§Ù„Ù‚Ø§Ø¦Ù…Ø© Ø§Ù„Ø­Ø§Ù„ÙŠØ© Ù…Ø­ÙÙˆØ¸Ø© ÙÙŠ Ø§Ù„Ù…ÙˆÙ‚Ø¹</strong><br><span class="ltr">' + escapeHtml(playlist.generated_url) + '</span>' :
            "Ù„Ø§ ØªÙˆØ¬Ø¯ Ù‚Ø§Ø¦Ù…Ø© Ù…Ø­ÙÙˆØ¸Ø© Ù„Ù‡Ø°Ø§ Ø§Ù„Ø¬Ù‡Ø§Ø² Ø­ØªÙ‰ Ø§Ù„Ø¢Ù†.";
        renderPlaylistTypeFields();
        elements.playlistType.focus();
    }

    function renderPlaylistTypeFields() {
        var isM3u = elements.playlistType.value === "m3u";
        elements.playlistUrlInput.closest("label").hidden = !isM3u;
        elements.playlistServerInput.closest("label").hidden = isM3u;
        elements.playlistUsernameInput.closest("label").hidden = isM3u;
        elements.playlistPasswordInput.closest("label").hidden = isM3u;
    }

    function savePlaylistFromForm(event) {
        var type = elements.playlistType.value;
        var payload = { mac: state.deviceMac || getStoredBrowserDeviceId(), type: type };

        event.preventDefault();

        if (!APP_SERVER_URL) {
            showPlaylistFormMessage("ÙŠØ¬Ø¨ Ø¶Ø¨Ø· Ø±Ø§Ø¨Ø· Ø§Ù„Ù…ÙˆÙ‚Ø¹ Ø¯Ø§Ø®Ù„ Ø§Ù„ØªØ·Ø¨ÙŠÙ‚ Ø£ÙˆÙ„Ø§.", true);
            return;
        }

        if (type === "m3u") {
            payload.playlist_url = elements.playlistUrlInput.value.trim();
            if (!payload.playlist_url) {
                showPlaylistFormMessage("Ø£Ø¯Ø®Ù„ Ø±Ø§Ø¨Ø· M3U.", true);
                return;
            }
        } else {
            payload.server_url = elements.playlistServerInput.value.trim();
            payload.username = elements.playlistUsernameInput.value.trim();
            payload.password = elements.playlistPasswordInput.value.trim();
            payload.output = "m3u8";
            if (!payload.server_url || !payload.username || !payload.password) {
                showPlaylistFormMessage("Ø£Ø¯Ø®Ù„ Ø±Ø§Ø¨Ø· Ø§Ù„Ø³ÙŠØ±ÙØ± ÙˆØ§Ø³Ù… Ø§Ù„Ù…Ø³ØªØ®Ø¯Ù… ÙˆÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ±.", true);
                return;
            }
        }

        showPlaylistFormMessage("Ø¬Ø§Ø±ÙŠ Ø§Ù„Ø­ÙØ¸ ÙÙŠ Ø§Ù„Ù…ÙˆÙ‚Ø¹...", false);
        postJson(normalizeServerUrl(APP_SERVER_URL) + "/api/playlist.php", payload, function (error, data) {
            if (error || !data || !data.ok) {
                console.log("Could not save playlist on server:", error ? error.message : data);
                showPlaylistFormMessage("ØªØ¹Ø°Ø± Ø­ÙØ¸ Ø§Ù„Ù‚Ø§Ø¦Ù…Ø© ÙÙŠ Ø§Ù„Ù…ÙˆÙ‚Ø¹.", true);
                return;
            }

            state.activation = state.activation || {};
            state.activation.playlist = data.playlist;
            IPTV_PLAYLIST_URL = data.playlist && data.playlist.generated_url ? data.playlist.generated_url : "";
            resetLibraryForReload();
            renderPlaylistScreen();
            showPlaylistFormMessage("ØªÙ… Ø­ÙØ¸ Ø§Ù„Ù‚Ø§Ø¦Ù…Ø© ÙÙŠ Ø§Ù„Ù…ÙˆÙ‚Ø¹ Ù„Ù‡Ø°Ø§ Ø§Ù„Ø¬Ù‡Ø§Ø².", false);
            loadIptvPlaylist();
        });
    }

    function deletePlaylistFromServer() {
        if (!APP_SERVER_URL) {
            showPlaylistFormMessage("ÙŠØ¬Ø¨ Ø¶Ø¨Ø· Ø±Ø§Ø¨Ø· Ø§Ù„Ù…ÙˆÙ‚Ø¹ Ø¯Ø§Ø®Ù„ Ø§Ù„ØªØ·Ø¨ÙŠÙ‚ Ø£ÙˆÙ„Ø§.", true);
            return;
        }

        showPlaylistFormMessage("Ø¬Ø§Ø±ÙŠ Ø­Ø°Ù Ø§Ù„Ù‚Ø§Ø¦Ù…Ø© Ù…Ù† Ø§Ù„Ù…ÙˆÙ‚Ø¹...", false);
        postJson(normalizeServerUrl(APP_SERVER_URL) + "/api/playlist.php", {
            mac: state.deviceMac || getStoredBrowserDeviceId(),
            action: "delete"
        }, function (error, data) {
            if (error || !data || !data.ok) {
                console.log("Could not delete playlist on server:", error ? error.message : data);
                showPlaylistFormMessage("ØªØ¹Ø°Ø± Ø­Ø°Ù Ø§Ù„Ù‚Ø§Ø¦Ù…Ø© Ù…Ù† Ø§Ù„Ù…ÙˆÙ‚Ø¹.", true);
                return;
            }

            state.activation = state.activation || {};
            state.activation.playlist = null;
            IPTV_PLAYLIST_URL = "";
            resetLibraryForReload();
            renderPlaylistScreen();
            showPlaylistFormMessage("ØªÙ… Ø­Ø°Ù Ø§Ù„Ù‚Ø§Ø¦Ù…Ø© Ù…Ù† Ø§Ù„Ù…ÙˆÙ‚Ø¹.", false);
        });
    }

    function showPlaylistFormMessage(message, isError) {
        elements.playlistFormMessage.hidden = false;
        elements.playlistFormMessage.textContent = message;
        elements.playlistFormMessage.classList.toggle("is-error", !!isError);
    }

    function renderPlaylistScreen() {
        var playlist = (state.activation && state.activation.playlist) || {};
        var type = playlist.type || "m3u";
        var labels = getLanguageLabels(state.language);

        applyStaticLanguage(labels);
        elements.playlistDeviceInfo.textContent = "MAC: " + (state.deviceMac || labels.unknown) + " | Server: " + (APP_SERVER_URL || labels.notConfigured);
        elements.playlistType.value = type;
        elements.playlistUrlInput.value = playlist.playlist_url || (type === "m3u" ? IPTV_PLAYLIST_URL : "");
        elements.playlistServerInput.value = playlist.server_url || "";
        elements.playlistUsernameInput.value = playlist.username || "";
        elements.playlistPasswordInput.value = playlist.password || "";
        elements.playlistFormMessage.hidden = true;
        elements.playlistCurrentInfo.innerHTML = playlist.generated_url ?
            '<strong>' + escapeHtml(labels.currentPlaylistSaved) + '</strong><br><span class="ltr">' + escapeHtml(playlist.generated_url) + '</span>' :
            escapeHtml(labels.noSavedPlaylist);
        renderPlaylistTypeFields();
        syncManagementFocus();
    }

    function savePlaylistFromForm(event) {
        var type = elements.playlistType.value;
        var payload = { mac: state.deviceMac || getStoredBrowserDeviceId(), type: type };
        var labels = getLanguageLabels(state.language);

        event.preventDefault();

        if (!APP_SERVER_URL) {
            showPlaylistFormMessage(labels.serverRequired, true);
            return;
        }

        if (type === "m3u") {
            payload.playlist_url = normalizePlaylistFormUrl(elements.playlistUrlInput.value);
            elements.playlistUrlInput.value = payload.playlist_url;
            if (!payload.playlist_url) {
                showPlaylistFormMessage(labels.enterM3u, true);
                return;
            }
        } else {
            payload.server_url = normalizePlaylistFormUrl(elements.playlistServerInput.value);
            elements.playlistServerInput.value = payload.server_url;
            payload.username = elements.playlistUsernameInput.value.trim();
            payload.password = elements.playlistPasswordInput.value.trim();
            payload.output = "m3u8";
            if (!payload.server_url || !payload.username || !payload.password) {
                showPlaylistFormMessage(labels.enterXtream, true);
                return;
            }
        }

        showPlaylistFormMessage(labels.savingPlaylist, false);
        postJson(normalizeServerUrl(APP_SERVER_URL) + "/api/playlist.php", payload, function (error, data) {
            if (error || !data || !data.ok) {
                console.log("Could not save playlist on server:", error ? error.message : data);
                showPlaylistFormMessage(labels.playlistSaveFailed, true);
                return;
            }

            state.activation = state.activation || {};
            state.activation.playlist = data.playlist;
            IPTV_PLAYLIST_URL = data.playlist && data.playlist.generated_url ? data.playlist.generated_url : "";
            resetLibraryForReload();
            renderPlaylistScreen();
            showPlaylistFormMessage(labels.playlistSaved, false);
            loadIptvPlaylist();
        });
    }

    function deletePlaylistFromServer() {
        var labels = getLanguageLabels(state.language);

        if (!APP_SERVER_URL) {
            showPlaylistFormMessage(labels.serverRequired, true);
            return;
        }

        showPlaylistFormMessage(labels.deletingPlaylist, false);
        postJson(normalizeServerUrl(APP_SERVER_URL) + "/api/playlist.php", {
            mac: state.deviceMac || getStoredBrowserDeviceId(),
            action: "delete"
        }, function (error, data) {
            if (error || !data || !data.ok) {
                console.log("Could not delete playlist on server:", error ? error.message : data);
                showPlaylistFormMessage(labels.playlistDeleteFailed, true);
                return;
            }

            state.activation = state.activation || {};
            state.activation.playlist = null;
            IPTV_PLAYLIST_URL = "";
            resetLibraryForReload();
            renderPlaylistScreen();
            showPlaylistFormMessage(labels.playlistDeleted, false);
        });
    }

    function getSavedPlaylists() {
        var activation = state.activation || {};

        if (Array.isArray(activation.playlists) && activation.playlists.length) {
            return activation.playlists;
        }

        return activation.playlist ? [activation.playlist] : [];
    }

    function getActivePlaylist() {
        var playlists = getSavedPlaylists();
        var playlist = state.activation && state.activation.playlist;
        var i;

        if (playlist) {
            return playlist;
        }

        for (i = 0; i < playlists.length; i += 1) {
            if (playlists[i] && playlists[i].enabled) {
                return playlists[i];
            }
        }

        return playlists[0] || null;
    }

    function getActivePlaylistId() {
        var playlist = getActivePlaylist();
        return playlist && playlist.id ? String(playlist.id) : "";
    }

    function getPlaylistKey(playlist) {
        return String((playlist && (playlist.id || playlist.generated_url || playlist.name)) || "playlist");
    }

    function getPlaylistCountsStorageKey(playlist) {
        return "nexoPlaylistCounts:" + encodeURIComponent(state.deviceMac || getStoredBrowserDeviceId()) + ":" +
            encodeURIComponent(getPlaylistKey(playlist));
    }

    function getCurrentLibraryCounts() {
        return {
            live: getItemsBySection("live").length,
            movies: getItemsBySection("movies").length,
            series: getItemsBySection("series").length,
            savedAt: Date.now()
        };
    }

    function storeActivePlaylistCounts() {
        var playlist = getActivePlaylist();

        if (!playlist || !window.localStorage) {
            return;
        }

        try {
            window.localStorage.setItem(getPlaylistCountsStorageKey(playlist), JSON.stringify(getCurrentLibraryCounts()));
        } catch (error) {
            console.log("Could not save playlist counts:", error.message);
        }

        if (state.screen === "playlist") {
            renderPlaylistList();
        }
    }

    function getPlaylistCounts(playlist) {
        var active = getActivePlaylist();
        var counts;

        if (active && playlist && String(active.id || "") === String(playlist.id || "") && allItems.length) {
            return getCurrentLibraryCounts();
        }

        if (playlist && playlist.counts) {
            return playlist.counts;
        }

        try {
            counts = JSON.parse(window.localStorage.getItem(getPlaylistCountsStorageKey(playlist)) || "null");
        } catch (error) {
            counts = null;
        }

        return counts || { live: "--", movies: "--", series: "--" };
    }

    function formatPlaylistDate(value) {
        if (!value) {
            return "--";
        }

        return String(value).replace("T", " ").split(" ")[0] || "--";
    }

    function getPlaylistName(playlist, index) {
        return String((playlist && (playlist.name || playlist.playlist_name || playlist.title)) || ("Playlist " + (index + 1))).trim();
    }

    function derivePlaylistName(payload) {
        var source = payload.server_url || payload.playlist_url || "";
        var match = String(source).match(/^https?:\/\/([^/:?#]+)/i);

        if (payload.username) {
            return payload.username;
        }

        return match ? match[1] : "Playlist";
    }

    function setActivationFromPlaylistResponse(data) {
        var playlists;
        var active;

        state.activation = state.activation || {};

        if (data && Array.isArray(data.playlists)) {
            state.activation.playlists = data.playlists;
        }

        if (data && Object.prototype.hasOwnProperty.call(data, "playlist")) {
            state.activation.playlist = data.playlist;
        }

        playlists = getSavedPlaylists();
        active = state.activation.playlist || null;

        if (!active && playlists.length) {
            active = playlists.filter(function (playlist) {
                return playlist && playlist.enabled;
            })[0] || null;
            state.activation.playlist = active;
        }

        IPTV_PLAYLIST_URL = active && active.generated_url ? active.generated_url : "";
        renderActivationSummary();
    }

    function setActivePlaylistLocally(playlist) {
        var playlists = getSavedPlaylists();

        state.activation = state.activation || {};
        state.activation.playlist = Object.assign({}, playlist, { enabled: true });
        state.activation.playlists = playlists.map(function (item) {
            return Object.assign({}, item, {
                enabled: String(item.id || "") === String(playlist.id || "")
            });
        });
        IPTV_PLAYLIST_URL = state.activation.playlist.generated_url || "";
        renderActivationSummary();
    }

    function getPlaylistPayloadForSave(playlist, enabled) {
        var type = playlist && playlist.type === "xtream" ? "xtream" : "m3u";
        var payload = {
            mac: state.deviceMac || getStoredBrowserDeviceId(),
            id: playlist && playlist.id ? playlist.id : undefined,
            name: getPlaylistName(playlist || {}, 0),
            type: type,
            enabled: enabled ? 1 : 0
        };

        if (type === "xtream") {
            payload.server_url = normalizePlaylistFormUrl(playlist.server_url || "");
            payload.username = String(playlist.username || "").trim();
            payload.password = String(playlist.password || "").trim();
            payload.output = "m3u8";
        } else {
            payload.playlist_url = normalizePlaylistFormUrl(playlist.playlist_url || playlist.generated_url || "");
        }

        return payload;
    }

    function renderPlaylistList() {
        var playlists = getSavedPlaylists();

        if (!elements.playlistList) {
            return;
        }

        if (!playlists.length) {
            elements.playlistList.innerHTML = '<div class="playlist-card is-empty">' +
                escapeHtml(getLanguageLabels(state.language).noSavedPlaylist) + '</div>';
            return;
        }

        elements.playlistList.innerHTML = playlists.map(renderPlaylistCard).join("");
    }

    function renderPlaylistCard(playlist, index) {
        var labels = getLanguageLabels(state.language);
        var active = getActivePlaylist();
        var isActive = active && String(active.id || "") === String(playlist.id || "");
        var counts = getPlaylistCounts(playlist);
        var name = getPlaylistName(playlist, index);
        var updated = formatPlaylistDate(playlist.updated_at);
        var added = formatPlaylistDate(playlist.created_at || playlist.added_at || playlist.updated_at);

        return '<div class="playlist-card-wrap">' +
            '<button class="playlist-card' + (isActive ? " is-active" : "") + '" type="button" data-playlist-id="' + escapeHtml(playlist.id || "") + '">' +
                '<span class="playlist-card-title">' +
                    '<span class="playlist-card-icon" aria-hidden="true">&#9776;&#9655;</span>' +
                    '<span>' + escapeHtml(name) + '</span>' +
                    (isActive ? '<span class="playlist-active-pill">' + escapeHtml(labels.playlistActiveBadge) + '</span>' : "") +
                '</span>' +
                '<span class="playlist-card-stats">' +
                    '<span>' + escapeHtml(counts.live) + '<small>' + escapeHtml(labels.playlistChannels) + '</small></span>' +
                    '<span>' + escapeHtml(counts.movies) + '<small>' + escapeHtml(labels.playlistMovies) + '</small></span>' +
                    '<span>' + escapeHtml(counts.series) + '<small>' + escapeHtml(labels.playlistSeries) + '</small></span>' +
                '</span>' +
                '<span class="playlist-card-meta">' +
                    '<span>' + escapeHtml(labels.playlistUpdated) + ': ' + escapeHtml(updated) + '</span>' +
                    '<span>' + escapeHtml(labels.playlistAdded) + ': ' + escapeHtml(added) + '</span>' +
                '</span>' +
            '</button>' +
            '<button class="playlist-delete-card" type="button" data-delete-playlist-id="' + escapeHtml(playlist.id || "") + '" aria-label="Delete playlist">&#128465;</button>' +
        '</div>';
    }

    function clearPlaylistForm() {
        elements.playlistNameInput.value = "";
        elements.playlistType.value = "m3u";
        elements.playlistUrlInput.value = "";
        elements.playlistServerInput.value = "";
        elements.playlistUsernameInput.value = "";
        elements.playlistPasswordInput.value = "";
        renderPlaylistTypeFields();
    }

    function openAddPlaylistForm() {
        state.playlistFormVisible = true;
        clearPlaylistForm();
        renderPlaylistScreen();
        focusFirstManagementControl("#playlistNameInput");
    }

    function handlePlaylistListClick(event) {
        var deleteButton = event.target.closest(".playlist-delete-card");
        var card;
        var id;

        if (deleteButton) {
            id = deleteButton.getAttribute("data-delete-playlist-id");
            deletePlaylistById(id);
            return;
        }

        card = event.target.closest(".playlist-card[data-playlist-id]");
        if (card) {
            activatePlaylistById(card.getAttribute("data-playlist-id"));
        }
    }

    function findPlaylistById(id) {
        return getSavedPlaylists().filter(function (playlist) {
            return playlist && String(playlist.id || "") === String(id || "");
        })[0] || null;
    }

    function activatePlaylistById(id, quiet) {
        var playlist = findPlaylistById(id);
        var labels = getLanguageLabels(state.language);
        var payload;

        if (!playlist) {
            return;
        }

        if (!APP_SERVER_URL) {
            setActivePlaylistLocally(playlist);
            resetLibraryForReload();
            renderPlaylistScreen();
            loadIptvPlaylist();
            return;
        }

        payload = getPlaylistPayloadForSave(playlist, true);
        if (!payload.playlist_url && playlist.type !== "xtream") {
            showPlaylistFormMessage(labels.enterM3u, true);
            return;
        }
        if (playlist.type === "xtream" && (!payload.server_url || !payload.username || !payload.password)) {
            showPlaylistFormMessage(labels.enterXtream, true);
            return;
        }

        if (!quiet) {
            showPlaylistFormMessage(labels.playlistSwitching, false);
        }

        postJson(normalizeServerUrl(APP_SERVER_URL) + "/api/playlist.php", payload, function (error, data) {
            if (error || !data || !data.ok) {
                console.log("Could not switch playlist:", error ? error.message : data);
                showPlaylistFormMessage(labels.playlistSaveFailed, true);
                return;
            }

            setActivationFromPlaylistResponse(data);
            state.playlistFormVisible = false;
            resetLibraryForReload();
            renderPlaylistScreen();
            if (!quiet) {
                showPlaylistFormMessage(labels.currentPlaylistSaved, false);
            }
            loadIptvPlaylist();
        });
    }

    function deletePlaylistById(id) {
        var labels = getLanguageLabels(state.language);
        var wasActive = String(id || "") === getActivePlaylistId();

        if (!id) {
            return;
        }

        if (!APP_SERVER_URL) {
            showPlaylistFormMessage(labels.serverRequired, true);
            return;
        }

        showPlaylistFormMessage(labels.deletingPlaylist, false);
        postJson(normalizeServerUrl(APP_SERVER_URL) + "/api/playlist.php", {
            mac: state.deviceMac || getStoredBrowserDeviceId(),
            action: "delete",
            id: id
        }, function (error, data) {
            var nextPlaylist;

            if (error || !data || !data.ok) {
                console.log("Could not delete playlist on server:", error ? error.message : data);
                showPlaylistFormMessage(labels.playlistDeleteFailed, true);
                return;
            }

            setActivationFromPlaylistResponse(data);
            state.playlistFormVisible = !getSavedPlaylists().length;

            if (wasActive && !getActivePlaylist() && getSavedPlaylists().length) {
                nextPlaylist = getSavedPlaylists()[0];
                renderPlaylistScreen();
                activatePlaylistById(nextPlaylist.id, true);
                return;
            }

            if (wasActive) {
                resetLibraryForReload();
                loadIptvPlaylist();
            }

            renderPlaylistScreen();
            showPlaylistFormMessage(labels.playlistDeleted, false);
        });
    }

    function openPlaylistScreen() {
        var hasPlaylists = getSavedPlaylists().length > 0;

        state.screen = "playlist";
        state.playlistFormVisible = !hasPlaylists;
        state.managementFocusIndex = 0;
        showScreen("playlist");
        renderPlaylistScreen();
        focusFirstManagementControl(hasPlaylists ? ".playlist-card" : "#playlistAddButton");
    }

    function renderPlaylistScreen() {
        var playlists = getSavedPlaylists();
        var playlist = getActivePlaylist() || {};
        var type = playlist.type || "m3u";
        var labels = getLanguageLabels(state.language);

        if (!playlists.length) {
            state.playlistFormVisible = true;
        }

        applyStaticLanguage(labels);
        elements.playlistDeviceInfo.textContent = "MAC: " + (state.deviceMac || labels.unknown) + " | Server: " + (APP_SERVER_URL || labels.notConfigured);
        elements.playlistCurrentInfo.innerHTML = playlist.generated_url ?
            '<strong>' + escapeHtml(labels.activePlaylist) + ': ' + escapeHtml(getPlaylistName(playlist, 0)) + '</strong><br><span class="ltr">' + escapeHtml(playlist.generated_url) + '</span>' :
            escapeHtml(labels.noSavedPlaylist);
        renderPlaylistList();
        elements.playlistForm.hidden = !state.playlistFormVisible;
        elements.playlistDeleteButton.hidden = !getActivePlaylistId();

        if (!state.playlistFormVisible) {
            elements.playlistFormMessage.hidden = true;
        }

        if (!elements.playlistType.value) {
            elements.playlistType.value = type;
        }

        renderPlaylistTypeFields();
        syncManagementFocus();
    }

    function savePlaylistFromForm(event) {
        var type = elements.playlistType.value;
        var payload = { mac: state.deviceMac || getStoredBrowserDeviceId(), type: type, enabled: 1 };
        var labels = getLanguageLabels(state.language);

        event.preventDefault();

        if (!APP_SERVER_URL) {
            showPlaylistFormMessage(labels.serverRequired, true);
            return;
        }

        payload.name = elements.playlistNameInput.value.trim();

        if (type === "m3u") {
            payload.playlist_url = normalizePlaylistFormUrl(elements.playlistUrlInput.value);
            elements.playlistUrlInput.value = payload.playlist_url;
            if (!payload.playlist_url) {
                showPlaylistFormMessage(labels.enterM3u, true);
                return;
            }
        } else {
            payload.server_url = normalizePlaylistFormUrl(elements.playlistServerInput.value);
            elements.playlistServerInput.value = payload.server_url;
            payload.username = elements.playlistUsernameInput.value.trim();
            payload.password = elements.playlistPasswordInput.value.trim();
            payload.output = "m3u8";
            if (!payload.server_url || !payload.username || !payload.password) {
                showPlaylistFormMessage(labels.enterXtream, true);
                return;
            }
        }

        if (!payload.name) {
            payload.name = derivePlaylistName(payload);
            elements.playlistNameInput.value = payload.name;
        }

        showPlaylistFormMessage(labels.savingPlaylist, false);
        postJson(normalizeServerUrl(APP_SERVER_URL) + "/api/playlist.php", payload, function (error, data) {
            if (error || !data || !data.ok) {
                console.log("Could not save playlist on server:", error ? error.message : data);
                showPlaylistFormMessage(labels.playlistSaveFailed, true);
                return;
            }

            setActivationFromPlaylistResponse(data);
            state.playlistFormVisible = false;
            clearPlaylistForm();
            resetLibraryForReload();
            renderPlaylistScreen();
            showPlaylistFormMessage(labels.playlistSaved, false);
            loadIptvPlaylist();
        });
    }

    function deletePlaylistFromServer() {
        deletePlaylistById(getActivePlaylistId());
    }

    function openLatestItem(index) {
        if (!latestItems[index]) {
            return;
        }

        state.activeSection = latestItems[index].section;
            openDetail(latestItems[index], "home");
    }

    function setCategory(index) {
        state.categoryIndex = Math.max(0, Math.min(state.categories.length - 1, index));
        state.itemIndex = 0;
        state.vodRenderLimit = VOD_RENDER_LIMIT;
        state.liveRenderLimit = LIVE_RENDER_LIMIT;
        renderItems();
    }

    function getLibraryActionControls() {
        var controls = [];

        if (state.activeSection === "live") {
            controls.push(elements.liveFavoriteButton, elements.liveLockButton, elements.liveReloadButton);
        }
        controls.push(elements.librarySortSelect);

        return controls.filter(function (control) {
            return !!(control && !control.hidden && !control.disabled);
        });
    }

    function moveLibraryActionFocus(delta) {
        var controls = getLibraryActionControls();

        if (!controls.length) {
            return;
        }

        state.libraryActionIndex = Math.max(0, Math.min(controls.length - 1, (state.libraryActionIndex || 0) + delta));
        syncLibraryFocus(false);
    }

    function activateLibraryAction() {
        var controls = getLibraryActionControls();
        var control = controls[state.libraryActionIndex || 0];

        if (!control) {
            return;
        }

        if (control === elements.librarySortSelect) {
            cycleLibrarySortMode();
            return;
        }

        control.click();
    }

    function setLibraryPane(pane) {
        if (pane === "actions" && !getLibraryActionControls().length) {
            pane = "categories";
        }
        state.libraryPane = pane;
        if (elements.virtualKeyboard && pane !== "keyboard") {
            elements.virtualKeyboard.hidden = true;
        }
        syncLibraryFocus();
    }

    function moveLibraryFocus(delta) {
        if (state.libraryPane === "back") {
            if (delta > 0) {
                setLibraryPane("search");
            }
            return;
        }

        if (state.libraryPane === "search") {
            if (delta < 0) {
                setLibraryPane("back");
                return;
            }
            if (delta > 0) {
                setLibraryPane("categories");
            }
            return;
        }

        if (state.libraryPane === "actions") {
            if (delta < 0) {
                setLibraryPane("back");
                return;
            }
            if (delta > 0 && renderedItems.length) {
                setLibraryPane("items");
            }
            return;
        }

        if (state.libraryPane === "categories") {
            if (delta < 0 && state.categoryIndex === 0) {
                setLibraryPane("search");
                return;
            }
            setCategory(Math.max(0, Math.min(state.categories.length - 1, state.categoryIndex + delta)));
            return;
        }

        if (delta < 0 && state.itemIndex <= 0 && getLibraryActionControls().length) {
            setLibraryPane("actions");
            return;
        }

        if (state.activeSection === "live") {
            state.itemIndex = Math.max(0, Math.min(visibleItems.length - 1, state.itemIndex + delta));
            maybeExtendLiveRenderedItems(state.itemIndex, false);
        } else {
            state.itemIndex = Math.max(0, Math.min(renderedItems.length - 1, state.itemIndex + delta));
            maybeExtendVodRenderedItems(state.itemIndex, false);
        }
        syncLibraryFocus();
    }

    function moveVodLibraryHorizontal(delta) {
        if (!visibleItems.length) {
            return;
        }

        if (state.libraryPane === "search" || state.libraryPane === "categories") {
            setLibraryPane("items");
            return;
        }

        if (state.libraryPane !== "items") {
            setLibraryPane("items");
            return;
        }

        if (delta < 0 && state.itemIndex <= 0) {
            setLibraryPane("categories");
            return;
        }

        state.itemIndex = Math.max(0, Math.min(visibleItems.length - 1, state.itemIndex + delta));
        maybeExtendVodRenderedItems(state.itemIndex, false);
        syncLibraryFocus();
    }

    function scrollVodLibraryItems(delta) {
        var scroller = elements.itemList;
        var amount;

        if (!scroller) {
            return;
        }

        amount = Math.max(280, Math.floor(scroller.clientHeight * 0.72));
        scroller.scrollTop += delta * amount;
        syncVodItemIndexToVisibleCard(delta);
        if (isVodListNearEnd()) {
            maybeExtendVodRenderedItems(renderedItems.length, true);
        }
        syncLibraryFocus(false);
    }

    function syncVodItemIndexToVisibleCard(direction) {
        var scroller = elements.itemList;
        var items = elements.itemList.querySelectorAll(".poster-card");
        var scrollerRect;
        var bestIndex = -1;
        var bestDistance = Infinity;
        var targetEdge;

        if (!scroller || !items.length) {
            return;
        }

        scrollerRect = scroller.getBoundingClientRect();
        targetEdge = direction > 0 ? scrollerRect.top + 24 : scrollerRect.bottom - 24;

        items.forEach(function (item, index) {
            var rect = item.getBoundingClientRect();
            var visible = rect.bottom > scrollerRect.top + 8 && rect.top < scrollerRect.bottom - 8;
            var distance;

            if (!visible) {
                return;
            }

            distance = Math.abs((direction > 0 ? rect.top : rect.bottom) - targetEdge);
            if (distance < bestDistance) {
                bestDistance = distance;
                bestIndex = index;
            }
        });

        if (bestIndex >= 0) {
            state.itemIndex = bestIndex;
            renderLiveInfoPanel();
        }
    }

    function syncLibraryFocus(shouldScroll) {
        var categories = elements.categoryList.querySelectorAll(".category-row");
        var items = elements.itemList.querySelectorAll(".item-row, .poster-card");
        var actionControls;
        var allActionControls;
        var activeAction;

        if (shouldScroll === undefined) {
            shouldScroll = true;
        }

        if (state.libraryPane === "downloads") {
            state.libraryPane = "items";
        }

        categories.forEach(function (row, index) {
            row.classList.toggle("is-selected", index === state.categoryIndex);
            row.classList.toggle("is-focused", state.libraryPane === "categories" && index === state.categoryIndex);
        });

        items.forEach(function (row, index) {
            row.classList.toggle("is-focused", state.libraryPane === "items" && index === state.itemIndex);
        });
        renderLiveInfoPanel();

        actionControls = getLibraryActionControls();
        allActionControls = [
            elements.liveFavoriteButton,
            elements.liveLockButton,
            elements.liveReloadButton,
            elements.librarySortSelect
        ].filter(Boolean);
        if (state.libraryPane === "actions" && actionControls.length) {
            state.libraryActionIndex = Math.max(0, Math.min(actionControls.length - 1, state.libraryActionIndex || 0));
        }
        activeAction = actionControls[state.libraryActionIndex || 0];

        if (elements.libraryBackButton) {
            elements.libraryBackButton.classList.toggle("is-focused", state.libraryPane === "back");
        }
        allActionControls.forEach(function (control) {
            var focused = state.libraryPane === "actions" && control === activeAction;
            var sortControl = control.closest ? control.closest(".library-sort-control") : null;

            control.classList.toggle("is-focused", focused);
            if (sortControl) {
                sortControl.classList.toggle("is-focused", focused);
            }
        });

        elements.librarySearchInput.classList.toggle("is-focused", state.libraryPane === "search" || state.libraryPane === "keyboard");
        if (state.libraryPane === "keyboard") {
            focusVirtualKeyboardButton();
            return;
        }

        if (state.libraryPane === "back") {
            elements.libraryBackButton.focus();
            return;
        }

        if (state.libraryPane === "actions" && activeAction) {
            activeAction.focus();
            return;
        }

        if (state.libraryPane === "search") {
            elements.librarySearchInput.focus();
            return;
        }

        if (state.libraryPane === "categories" && categories[state.categoryIndex]) {
            categories[state.categoryIndex].focus();
            categories[state.categoryIndex].scrollIntoView({ block: "nearest" });
        }

        if (state.libraryPane === "items" && items[state.itemIndex]) {
            items[state.itemIndex].focus();
            if (shouldScroll) {
                items[state.itemIndex].scrollIntoView({ block: "nearest" });
            }
        }
    }

    function setDetailAction(index) {
        var controls = getDetailControls();

        state.detailPane = "actions";
        state.detailActionIndex = index;
        focusDetailPane();
    }

    function getDetailControls() {
        return [
            elements.detailBackButton,
            elements.detailPlayButton,
            elements.detailTrailerButton,
            elements.detailFavoriteButton,
            elements.detailWatchlistButton
        ];
    }

    function moveDetailHorizontal(delta) {
        if (state.detailPane === "actions") {
            state.detailActionIndex = Math.max(0, Math.min(getDetailControls().length - 1, state.detailActionIndex + delta));
        } else if (state.detailPane === "seasons") {
            state.seasonIndex = Math.max(0, Math.min(detailSeasons.length - 1, state.seasonIndex + delta));
            state.episodeIndex = 0;
            renderSeriesRows(state.detailItem);
        } else if (state.detailPane === "episodes") {
            state.episodeIndex = Math.max(0, Math.min(detailEpisodes.length - 1, state.episodeIndex + delta));
        } else if (state.detailPane === "cast") {
            state.castIndex = Math.max(0, Math.min(getCastPeople(state.detailItem).length - 1, state.castIndex + delta));
        } else if (state.detailPane === "actorWorks") {
            state.actorWorkIndex = Math.max(0, Math.min(actorWorks.length - 1, state.actorWorkIndex + delta));
        }

        focusDetailPane();
    }

    function moveDetailVertical(delta) {
        if (delta < 0) {
            if (state.detailPane === "actorWorks") {
                state.detailPane = "cast";
            } else if (state.detailPane === "cast") {
                state.detailPane = detailEpisodes.length ? "episodes" : "actions";
            } else if (state.detailPane === "episodes") {
                state.detailPane = detailSeasons.length ? "seasons" : "actions";
            } else if (state.detailPane === "seasons") {
                state.detailPane = "actions";
            }
        } else {
            if (state.detailPane === "actions") {
                state.detailPane = detailSeasons.length ? "seasons" : "cast";
            } else if (state.detailPane === "seasons") {
                state.detailPane = "episodes";
            } else if (state.detailPane === "episodes") {
                state.detailPane = "cast";
            } else if (state.detailPane === "cast" && actorWorks.length) {
                state.detailPane = "actorWorks";
            }
        }

        focusDetailPane();
    }

    function activateDetailFocus() {
        if (state.detailPane === "actions") {
            if (state.detailActionIndex === 0) {
                goBackFromDetail();
            } else if (state.detailActionIndex === 1) {
                playDetailItem();
            } else if (state.detailActionIndex === 2) {
                playTrailer();
            } else if (state.detailActionIndex === 3) {
                addDetailFavorite();
            } else {
                removeFromWatchlist();
            }
        } else if (state.detailPane === "episodes") {
            playEpisode(state.episodeIndex);
        } else if (state.detailPane === "cast") {
            openActorWorks(state.castIndex);
        } else if (state.detailPane === "actorWorks") {
            openActorWork(state.actorWorkIndex);
        }
    }

    function focusDetailPane() {
        var controls = getDetailControls();
        var seasons = elements.seasonList.querySelectorAll(".season-button");
        var episodes = elements.episodeList.querySelectorAll(".episode-card");
        var castCards = elements.castList.querySelectorAll(".cast-card");
        var works = elements.actorWorksList.querySelectorAll(".actor-work-card");

        controls.forEach(function (control, index) {
            control.classList.toggle("is-focused", state.detailPane === "actions" && index === state.detailActionIndex);
        });
        seasons.forEach(function (control, index) {
            control.classList.toggle("is-focused", state.detailPane === "seasons" && index === state.seasonIndex);
        });
        episodes.forEach(function (control, index) {
            control.classList.toggle("is-focused", state.detailPane === "episodes" && index === state.episodeIndex);
        });
        castCards.forEach(function (control, index) {
            control.classList.toggle("is-focused", state.detailPane === "cast" && index === state.castIndex);
            control.classList.toggle("is-selected", state.detailPane === "actorWorks" && index === state.castIndex);
        });
        works.forEach(function (control, index) {
            control.classList.toggle("is-focused", state.detailPane === "actorWorks" && index === state.actorWorkIndex);
        });

        focusActiveDetailElement(controls, seasons, episodes, castCards, works);
    }

    function focusActiveDetailElement(controls, seasons, episodes, castCards, works) {
        var target = null;

        if (state.detailPane === "actions") {
            target = controls[state.detailActionIndex];
        } else if (state.detailPane === "seasons") {
            target = seasons[state.seasonIndex];
        } else if (state.detailPane === "episodes") {
            target = episodes[state.episodeIndex];
        } else if (state.detailPane === "cast") {
            target = castCards[state.castIndex];
        } else if (state.detailPane === "actorWorks") {
            target = works[state.actorWorkIndex];
        }

        if (target) {
            target.focus();
            target.scrollIntoView({ block: "nearest", inline: "nearest" });
        }
    }

    function buildCategories(items) {
        var map = {};
        var categories = [{ name: getFavoritesLabel(), items: getFavoriteCategoryItems(items), isFavorites: true }];

        items.forEach(function (item) {
            var group = item.group || "Other";
            if (!map[group]) {
                map[group] = [];
            }
            map[group].push(item);
        });

        Object.keys(map).sort().forEach(function (group) {
            categories.push({ name: group, items: map[group] });
        });

        return categories;
    }

    function getFavoritesLabel() {
        return state.language === "ar" ? "\u0627\u0644\u0645\u0641\u0636\u0644\u0629" : "Favorites";
    }

    function getFavoriteCategoryItems(items) {
        var favoriteKeys = getFavoriteStorageKeyMap();

        return (items || []).filter(function (item) {
            return item.section === state.activeSection && !!favoriteKeys[getItemStorageKey("nexoWishlist:", item)];
        });
    }

    function getFavoriteStorageKeyMap() {
        var map = {};
        var i;

        try {
            for (i = 0; i < window.localStorage.length; i += 1) {
                var key = window.localStorage.key(i);
                if (key && key.indexOf("nexoWishlist:") === 0) {
                    map[key] = true;
                }
            }
        } catch (error) {
            console.log("Could not read favorite keys:", error.message);
        }

        return map;
    }

    function isVodSection() {
        return state.activeSection === "movies" || state.activeSection === "series";
    }

    function getRichDetailMeta(item) {
        var values = [];
        var year = item.year || getYearFromTitle(item.title);

        values.push({ text: getReadableRating(item), className: "rating" });
        values.push({ text: year ? "ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥Ù†ØªØ§Ø¬ " + year : "ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥Ù†ØªØ§Ø¬ ØºÙŠØ± Ù…ØªÙˆÙØ±", className: "" });
        values.push({ text: item.addedAt ? "ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥Ø¶Ø§ÙØ© " + formatDateText(item.addedAt) : "ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥Ø¶Ø§ÙØ© ØºÙŠØ± Ù…ØªÙˆÙØ±", className: "" });
        values.push({ text: item.duration ? "Ø§Ù„Ù…Ø¯Ø© " + formatDurationText(item.duration) : "Ø§Ù„Ù…Ø¯Ø© ØºÙŠØ± Ù…ØªÙˆÙØ±Ø©", className: "" });
        values.push({ text: item.group || "Ø§Ù„Ù†ÙˆØ¹ ØºÙŠØ± Ù…ØªÙˆÙØ±", className: "" });
        values.push({ text: item.quality || item.type.toUpperCase(), className: "" });

        return values;
    }

    function getDetailMeta(item) {
        var values = [];
        var year = item.year || getYearFromTitle(item.title);

        values.push({ text: getRatingText(item), className: "rating" });
        values.push({ text: year ? "ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥Ù†ØªØ§Ø¬ " + year : "ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥Ù†ØªØ§Ø¬ ØºÙŠØ± Ù…ØªÙˆÙØ±", className: "" });
        values.push({ text: item.addedAt ? "ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥Ø¶Ø§ÙØ© " + item.addedAt : "ØªØ§Ø±ÙŠØ® Ø§Ù„Ø¥Ø¶Ø§ÙØ© ØºÙŠØ± Ù…ØªÙˆÙØ±", className: "" });
        values.push({ text: item.duration ? "Ø§Ù„Ù…Ø¯Ø© " + formatDurationText(item.duration) : "Ø§Ù„Ù…Ø¯Ø© ØºÙŠØ± Ù…ØªÙˆÙØ±Ø©", className: "" });
        values.push({ text: item.group || "Ø§Ù„Ù†ÙˆØ¹ ØºÙŠØ± Ù…ØªÙˆÙØ±", className: "" });
        values.push({ text: item.quality || item.type.toUpperCase(), className: "" });

        return values;
    }

    function getStoryText(item) {
        if (item.plot) {
            return item.plot;
        }

        if (item.description && item.description.indexOf("IPTV - ") !== 0) {
            return item.description;
        }

        return "Ù‚ØµØ© Ø§Ù„ÙÙŠÙ„Ù… ØºÙŠØ± Ù…ØªÙˆÙØ±Ø© ÙÙŠ Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„Ø§Ø´ØªØ±Ø§Ùƒ. ÙŠÙ…ÙƒÙ† ØªØ´ØºÙŠÙ„ Ø§Ù„Ø¹Ù…Ù„ Ù…Ø¨Ø§Ø´Ø±Ø© Ù…Ù† Ø²Ø± ØªØ´ØºÙŠÙ„.";
    }

    function getReadableStory(item) {
        if (item.plot) {
            return item.plot;
        }

        if (item.description && item.description.indexOf("IPTV - ") !== 0) {
            return item.description;
        }

        return "Ù‚ØµØ© Ø§Ù„ÙÙŠÙ„Ù… ØºÙŠØ± Ù…ØªÙˆÙØ±Ø© ÙÙŠ Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„Ø§Ø´ØªØ±Ø§Ùƒ. ÙŠÙ…ÙƒÙ† ØªØ´ØºÙŠÙ„ Ø§Ù„Ø¹Ù…Ù„ Ù…Ø¨Ø§Ø´Ø±Ø© Ù…Ù† Ø²Ø± ØªØ´ØºÙŠÙ„.";
    }

    function getCastItems(item) {
        if (item.cast && item.cast.length) {
            return item.cast;
        }

        return [
            "ØºÙŠØ± Ù…ØªÙˆÙØ±",
            "ØºÙŠØ± Ù…ØªÙˆÙØ±",
            "ØºÙŠØ± Ù…ØªÙˆÙØ±",
            "ØºÙŠØ± Ù…ØªÙˆÙØ±",
            "ØºÙŠØ± Ù…ØªÙˆÙØ±",
            "ØºÙŠØ± Ù…ØªÙˆÙØ±"
        ];
    }

    function getRatingText(item) {
        var rating = parseFloat(item.rating);

        if (isFinite(rating) && rating > 0) {
            return "â˜… " + rating.toFixed(1);
        }

        return "â˜… Ø§Ù„ØªÙ‚ÙŠÙŠÙ… ØºÙŠØ± Ù…ØªÙˆÙØ±";
    }

    function getReadableRating(item) {
        var rating = parseFloat(item.rating);

        if (isFinite(rating) && rating > 0) {
            return "â˜… " + rating.toFixed(1);
        }

        return "â˜… Ø§Ù„ØªÙ‚ÙŠÙŠÙ… ØºÙŠØ± Ù…ØªÙˆÙØ±";
    }

    function formatDurationText(value) {
        var text = String(value || "");
        var parts = text.split(":");
        var seconds = parseInt(text, 10);

        if (/^\d+$/.test(text) && seconds > 60) {
            return formatSeconds(seconds);
        }

        if (parts.length === 3) {
            return parts[0] + ":" + parts[1] + ":" + parts[2];
        }

        if (parts.length === 2) {
            return "00:" + parts[0] + ":" + parts[1];
        }

        return text;
    }

    function formatSeconds(totalSeconds) {
        var hours = Math.floor(totalSeconds / 3600);
        var minutes = Math.floor((totalSeconds % 3600) / 60);
        var seconds = totalSeconds % 60;

        function pad(value) {
            return value < 10 ? "0" + value : String(value);
        }

        return pad(hours) + ":" + pad(minutes) + ":" + pad(seconds);
    }

    function formatDateText(value) {
        var text = String(value || "");
        var timestamp = parseInt(text, 10);
        var date;

        if (/^\d{10}$/.test(text)) {
            date = new Date(timestamp * 1000);
            return date.getFullYear() + "-" + padDate(date.getMonth() + 1) + "-" + padDate(date.getDate());
        }

        return text;
    }

    function padDate(value) {
        return value < 10 ? "0" + value : String(value);
    }

    function getReadableCastItems(item) {
        if (item.cast && item.cast.length) {
            return item.cast;
        }

        return [
            "ØºÙŠØ± Ù…ØªÙˆÙØ±",
            "ØºÙŠØ± Ù…ØªÙˆÙØ±",
            "ØºÙŠØ± Ù…ØªÙˆÙØ±",
            "ØºÙŠØ± Ù…ØªÙˆÙØ±",
            "ØºÙŠØ± Ù…ØªÙˆÙØ±",
            "ØºÙŠØ± Ù…ØªÙˆÙØ±"
        ];
    }

    function getRichDetailMeta(item) {
        var labels = getLanguageLabels(state.language);
        var values = [];
        var year = item.year || getYearFromTitle(item.title);
        var isArabic = state.language === "ar";

        values.push({ text: getReadableRating(item), className: "rating" });
        values.push({ text: year ? (isArabic ? "\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0625\u0646\u062a\u0627\u062c " : "Release year ") + year : (isArabic ? "\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0625\u0646\u062a\u0627\u062c \u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631" : "Release year unavailable"), className: "" });
        values.push({ text: item.addedAt ? (isArabic ? "\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0625\u0636\u0627\u0641\u0629 " : "Added ") + formatDateText(item.addedAt) : (isArabic ? "\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0625\u0636\u0627\u0641\u0629 \u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631" : "Added date unavailable"), className: "" });
        values.push({ text: item.duration ? (isArabic ? "\u0627\u0644\u0645\u062f\u0629 " : "Duration ") + formatDurationText(item.duration) : (isArabic ? "\u0627\u0644\u0645\u062f\u0629 \u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631\u0629" : "Duration unavailable"), className: "" });
        values.push({ text: item.group || (isArabic ? "\u0627\u0644\u0646\u0648\u0639 \u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631" : "Category unavailable"), className: "" });
        values.push({ text: item.quality || item.type.toUpperCase(), className: "" });

        return values;
    }

    function getReadableStory(item) {
        if (item.plot) {
            return item.plot;
        }

        if (item.description && item.description.indexOf("IPTV - ") !== 0) {
            return item.description;
        }

        return state.language === "ar" ?
            "\u0642\u0635\u0629 \u0627\u0644\u0641\u064a\u0644\u0645 \u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631\u0629 \u0641\u064a \u0628\u064a\u0627\u0646\u0627\u062a \u0627\u0644\u0627\u0634\u062a\u0631\u0627\u0643. \u064a\u0645\u0643\u0646 \u062a\u0634\u063a\u064a\u0644 \u0627\u0644\u0639\u0645\u0644 \u0645\u0628\u0627\u0634\u0631\u0629 \u0645\u0646 \u0632\u0631 \u062a\u0634\u063a\u064a\u0644." :
            "No story is available in the playlist data. You can play this item directly.";
    }

    function getReadableRating(item) {
        var rating = parseFloat(item.rating);

        if (isFinite(rating) && rating > 0) {
            return "\u2605 " + rating.toFixed(1);
        }

        return state.language === "ar" ? "\u2605 \u0627\u0644\u062a\u0642\u064a\u064a\u0645 \u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631" : "\u2605 Rating unavailable";
    }

    function getReadableCastItems(item) {
        if (item.cast && item.cast.length) {
            return item.cast;
        }

        if (item.tmdbResolving || item.tmdbId || item.metadataLoading) {
            return [state.language === "ar" ? "\u062c\u0627\u0631\u064a \u062c\u0644\u0628 \u0627\u0644\u0646\u062c\u0648\u0645..." : "Loading cast..."];
        }

        if (item.tmdbImagesLoaded || item.metadataLoaded) {
            return [];
        }

        return [
            state.language === "ar" ? "\u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631" : "Unavailable",
            state.language === "ar" ? "\u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631" : "Unavailable",
            state.language === "ar" ? "\u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631" : "Unavailable",
            state.language === "ar" ? "\u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631" : "Unavailable",
            state.language === "ar" ? "\u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631" : "Unavailable",
            state.language === "ar" ? "\u063a\u064a\u0631 \u0645\u062a\u0648\u0641\u0631" : "Unavailable"
        ];
    }

    function getCastPeople(item) {
        var names;

        if (!item) {
            return [];
        }

        if (item.castPeople && item.castPeople.length) {
            return item.castPeople;
        }

        names = getReadableCastItems(item);
        return names.map(function (name) {
            return { name: name, image: "" };
        });
    }

    function getCastPhotoStyle(item, person, index) {
        var image = person && person.image;

        if (image) {
            return "background-image: linear-gradient(180deg, rgba(0,0,0,0.02), rgba(0,0,0,0.22)), url('" + escapeCssUrl(image) + "');";
        }

        return "background-image: " + getGeneratedPosterGradient((person && person.name) || item.title || ("Cast " + index)) + ";";
    }

    function getPosterStyle(item) {
        if (item.logo) {
            return "background-image: linear-gradient(180deg, rgba(0,0,0,0.02), rgba(0,0,0,0.18)), url('" + escapeCssUrl(item.logo) + "');";
        }

        return "background-image: " + getGeneratedPosterGradient(item.title) + ";";
    }

    function getPosterFallbackHtml(item) {
        if (item.logo || item.backdrop) {
            return "";
        }

        return '<span class="poster-fallback">' + escapeHtml(getInitials(item.title)) + '</span>';
    }

    function getChannelNumber(item, index) {
        return item && item.number ? item.number : (index + 1);
    }

    function getChannelLogoStyle(item) {
        if (item && item.logo) {
            return "background-image: url('" + escapeCssUrl(item.logo) + "');";
        }

        return "";
    }

    function getChannelLogoFallbackHtml(item) {
        if (item && item.logo) {
            return "";
        }

        return '<span>' + escapeHtml(getInitials(item && item.title)) + '</span>';
    }

    function normalizePersonImageUrl(value) {
        var text = String(value || "").trim();

        if (!text) {
            return "";
        }

        if (text.indexOf("//") === 0) {
            return "https:" + text;
        }

        if (text.charAt(0) === "/") {
            return "https://image.tmdb.org/t/p/w300" + text;
        }

        return text;
    }

    function getTmdbIdFromUrl(value) {
        var match = String(value || "").match(/themoviedb\.org\/(?:movie|tv)\/(\d+)/i);

        return match ? match[1] : "";
    }

    function getBackdropStyle(item) {
        if (item.backdrop) {
            return "background-image: linear-gradient(90deg, rgba(5,8,12,0.94) 0%, rgba(5,8,12,0.66) 48%, rgba(5,8,12,0.78) 100%), url('" + escapeCssUrl(item.backdrop) + "');";
        }

        if (item.logo) {
            return "background-image: linear-gradient(90deg, rgba(5,8,12,0.94) 0%, rgba(5,8,12,0.66) 48%, rgba(5,8,12,0.78) 100%), url('" + escapeCssUrl(item.logo) + "');";
        }

        return "background-image: linear-gradient(90deg, rgba(5,8,12,0.96) 0%, rgba(5,8,12,0.70) 52%, rgba(5,8,12,0.86) 100%), " + getGeneratedPosterGradient(item.title) + ";";
    }

    function getGeneratedPosterGradient(value) {
        var hue = getStringHue(value);
        return "linear-gradient(145deg, hsl(" + hue + ", 72%, 38%) 0%, hsl(" + ((hue + 42) % 360) + ", 72%, 24%) 48%, hsl(" + ((hue + 92) % 360) + ", 70%, 18%) 100%)";
    }

    function getStringHue(value) {
        var text = String(value || "IPTV");
        var hash = 0;

        for (var i = 0; i < text.length; i += 1) {
            hash = ((hash << 5) - hash) + text.charCodeAt(i);
            hash |= 0;
        }

        return Math.abs(hash) % 360;
    }

    function getInitials(value) {
        var words = String(value || "IPTV").trim().split(/\s+/).filter(Boolean);

        if (!words.length) {
            return "IP";
        }

        return words.slice(0, 2).map(function (word) {
            return word.charAt(0).toUpperCase();
        }).join("");
    }

    function getYearFromTitle(title) {
        var match = String(title || "").match(/\b(19|20)\d{2}\b/);
        return match ? match[0] : "";
    }

    function escapeCssUrl(value) {
        return String(value || "").replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, "%22");
    }

    function getItemsBySection(sectionId) {
        return allItems.filter(function (item) {
            return item.section === sectionId;
        });
    }

    function getLibraryItemsBySection(sectionId) {
        var items = getItemsBySection(sectionId);

        if (sectionId === "series") {
            return getUniqueSeriesItems(items);
        }

        return items;
    }

    function sortLibraryItems(items, mode) {
        var sorted = (items || []).slice();
        var sortMode = mode || "az";

        sorted.sort(function (a, b) {
            if (state.activeSection === "live") {
                if (sortMode === "za") {
                    return compareItemTitles(b, a) || compareLiveChannelOrder(a, b);
                }
                if (sortMode === "recent") {
                    return getItemSortTime(b) - getItemSortTime(a) || compareLiveChannelOrder(a, b) || compareItemTitles(a, b);
                }
                if (sortMode === "az") {
                    return compareItemTitles(a, b) || compareLiveChannelOrder(a, b);
                }

                return compareLiveChannelOrder(a, b) || compareItemTitles(a, b);
            }
            if (sortMode === "za") {
                return compareItemTitles(b, a);
            }
            if (sortMode === "rating") {
                return getItemRatingValue(b) - getItemRatingValue(a) || compareItemTitles(a, b);
            }
            if (sortMode === "year") {
                return getItemYearValue(b) - getItemYearValue(a) || compareItemTitles(a, b);
            }

            return compareItemTitles(a, b);
        });

        return sorted;
    }

    function compareLiveChannelOrder(a, b) {
        var first = getLiveChannelOrderValue(a);
        var second = getLiveChannelOrderValue(b);

        return first - second;
    }

    function getLiveChannelOrderValue(item) {
        var number = parseInt(item && item.number, 10);
        var order = parseInt(item && item.libraryOrder, 10);

        if (isFinite(number)) {
            return number;
        }

        return isFinite(order) ? order : 9007199254740991;
    }

    function compareItemTitles(a, b) {
        var first = normalizeDisplayTitle((a && (a.seriesTitle || a.title)) || "");
        var second = normalizeDisplayTitle((b && (b.seriesTitle || b.title)) || "");

        return first.localeCompare(second, state.language === "ar" ? "ar" : "en", {
            numeric: true,
            sensitivity: "base"
        });
    }

    function getItemRatingValue(item) {
        var rating = parseFloat(item && item.rating);

        return isFinite(rating) ? rating : 0;
    }

    function getItemYearValue(item) {
        var value = parseInt((item && item.year) || getYearFromTitle(item && item.title), 10);

        return isFinite(value) ? value : 0;
    }

    function getUniqueSeriesItems(items) {
        var map = {};

        items.forEach(function (item) {
            var key = getLibraryWorkKey(item);
            var current = map[key];

            if (!current || isBetterSeriesRepresentative(item, current)) {
                map[key] = item;
            }
        });

        return Object.keys(map).map(function (key) {
            return map[key];
        }).sort(function (a, b) {
            return getItemSortTime(b) - getItemSortTime(a);
        });
    }

    function isBetterSeriesRepresentative(candidate, current) {
        if (!candidate.episodeNumber && current.episodeNumber) {
            return true;
        }

        if (candidate.episodeNumber && current.episodeNumber) {
            if ((candidate.seasonNumber || 1) !== (current.seasonNumber || 1)) {
                return (candidate.seasonNumber || 1) < (current.seasonNumber || 1);
            }

            return candidate.episodeNumber < current.episodeNumber;
        }

        return getItemSortTime(candidate) > getItemSortTime(current);
    }

    function getSearchableFields(item) {
        return [
            item.title,
            item.seriesTitle,
            item.group,
            item.description,
            item.quality,
            item.year,
            item.number
        ];
    }

    function getSearchTokens(value) {
        return normalizeSearchText(value).split(" ").filter(Boolean);
    }

    function getTokenMatchScore(queryToken, tokens, fullText) {
        var best = Infinity;

        tokens.forEach(function (token) {
            if (token === queryToken) {
                best = Math.min(best, 0);
            } else if (token.indexOf(queryToken) === 0) {
                best = Math.min(best, 8);
            } else if (queryToken.length > 1 && token.indexOf(queryToken) !== -1) {
                best = Math.min(best, 18);
            }
        });

        if (queryToken.length > 1 && fullText.indexOf(queryToken) !== -1) {
            best = Math.min(best, 32);
        }

        return best;
    }

    function scoreSearchItem(item, normalizedQuery, queryTokens) {
        var title = normalizeSearchText(item.title || "");
        var seriesTitle = normalizeSearchText(item.seriesTitle || "");
        var fullText = normalizeSearchText(getSearchableFields(item).join(" "));
        var compactText = fullText.replace(/\s+/g, "");
        var compactQuery = normalizedQuery.replace(/\s+/g, "");
        var tokens = getSearchTokens(fullText);
        var score = 0;
        var i;
        var tokenScore;

        if (!fullText) {
            return Infinity;
        }

        if (title === normalizedQuery || seriesTitle === normalizedQuery) {
            score -= 80;
        } else if (title.indexOf(normalizedQuery) === 0 || seriesTitle.indexOf(normalizedQuery) === 0) {
            score -= 55;
        } else if (title.indexOf(normalizedQuery) !== -1 || seriesTitle.indexOf(normalizedQuery) !== -1) {
            score -= 35;
        } else if (fullText.indexOf(normalizedQuery) !== -1) {
            score -= 10;
        } else if (compactQuery.length > 1 && compactText.indexOf(compactQuery) !== -1) {
            score -= 6;
        }

        for (i = 0; i < queryTokens.length; i += 1) {
            tokenScore = getTokenMatchScore(queryTokens[i], tokens, fullText);
            if (!isFinite(tokenScore)) {
                return Infinity;
            }
            score += tokenScore + (i * 2);
        }

        score += Math.min(title.length || 80, 80) / 100;
        return score;
    }

    function filterItemsBySearch(items, query) {
        var normalizedQuery = normalizeSearchText(query);
        var queryTokens = getSearchTokens(query);

        if (!normalizedQuery || !queryTokens.length) {
            return items;
        }

        return items.map(function (item, index) {
            return {
                item: item,
                index: index,
                score: scoreSearchItem(item, normalizedQuery, queryTokens)
            };
        }).filter(function (entry) {
            return isFinite(entry.score);
        }).sort(function (a, b) {
            if (a.score !== b.score) {
                return a.score - b.score;
            }
            return a.index - b.index;
        }).map(function (entry) {
            return entry.item;
        });
    }

    function getSection(sectionId) {
        for (var i = 0; i < SECTIONS.length; i += 1) {
            if (SECTIONS[i].id === sectionId) {
                return SECTIONS[i];
            }
        }

        return SECTIONS[0];
    }

    function setFocusedControl(control) {
        state.focusedControl = control;
        elements.backButton.classList.toggle("is-focused", control === "back");
        elements.playPauseButton.classList.toggle("is-focused", control === "play");
        elements.favoriteToolButton.classList.toggle("is-focused", control === "favorite");
        elements.subtitleToolButton.classList.toggle("is-focused", control === "subtitles");
        elements.subtitleSettingsToolButton.classList.toggle("is-focused", control === "subtitle-settings");
        elements.screenToolButton.classList.toggle("is-focused", control === "screen");

        if (control === "back") {
            elements.backButton.focus();
        } else if (control === "favorite") {
            elements.favoriteToolButton.focus();
        } else if (control === "subtitles") {
            elements.subtitleToolButton.focus();
        } else if (control === "subtitle-settings") {
            elements.subtitleSettingsToolButton.focus();
        } else if (control === "screen") {
            elements.screenToolButton.focus();
        } else {
            elements.playPauseButton.focus();
        }
    }

    function isPlayerToolFocused() {
        return state.focusedControl === "subtitles" ||
            state.focusedControl === "favorite" ||
            state.focusedControl === "subtitle-settings" ||
            state.focusedControl === "screen";
    }

    function movePlayerControlFocus(direction) {
        var controls = ["back", "favorite", "subtitles", "subtitle-settings", "screen", "play"];
        var index = controls.indexOf(state.focusedControl);

        if (index === -1) {
            index = controls.indexOf("play");
        }

        index = Math.max(0, Math.min(controls.length - 1, index + direction));
        setFocusedControl(controls[index]);
    }

    function movePlayerToolFocus(direction) {
        var tools = ["favorite", "subtitles", "subtitle-settings", "screen"];
        var index = tools.indexOf(state.focusedControl);

        if (index === -1) {
            setFocusedControl("subtitles");
            return;
        }

        index = Math.max(0, Math.min(tools.length - 1, index + direction));
        setFocusedControl(tools[index]);
    }

    function showOverlay() {
        clearTimeout(state.overlayTimer);
        elements.overlay.classList.remove("is-hidden");

        state.overlayTimer = setTimeout(function () {
            if (state.screen === "player" && !(player.isPaused ? player.isPaused() : elements.video.paused)) {
                elements.overlay.classList.add("is-hidden");
            }
        }, 4200);
    }

    function exitApp() {
        if (window.tizen && tizen.application) {
            try {
                tizen.application.getCurrentApplication().exit();
            } catch (error) {
                console.log("Unable to exit app:", error.message);
            }
        }
    }

    function escapeHtml(value) {
        return String(value)
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;")
            .replace(/'/g, "&#039;");
    }

    function decodeHtml(value) {
        var textarea = document.createElement("textarea");

        textarea.innerHTML = String(value || "");
        return textarea.value;
    }

    document.addEventListener("DOMContentLoaded", init);
})();
