{"id":18,"date":"2023-12-15T16:51:00","date_gmt":"2023-12-15T16:51:00","guid":{"rendered":"https:\/\/divinewisdomministries.com\/?page_id=18"},"modified":"2025-10-23T07:14:23","modified_gmt":"2025-10-23T07:14:23","slug":"tt-tv","status":"publish","type":"page","link":"https:\/\/divinewisdomministries.com\/ta\/tt-tv\/","title":{"rendered":"ThiruThoothar TV"},"content":{"rendered":"<style>\r\n    .custom-tv-wrapper {\r\n        position: relative;\r\n        width: 100%;\r\n        max-width: 960px;\r\n        margin: 0 auto;\r\n        background: #000;\r\n        border-radius: 12px;\r\n        box-shadow: 0 10px 30px rgba(0,0,0,0.45);\r\n        overflow: hidden;\r\n    }\r\n    .custom-tv-wrapper video, .custom-tv-embed { display:block; width:100%; height:auto; background:black; border-radius:12px; }\r\n    .tv-logo-overlay { position:absolute; top:12px; right:12px; z-index:1002; pointer-events:none; opacity:0; transform:translateY(-4px); transition:opacity .3s, transform .3s; }\r\n    .tv-logo-overlay.show { opacity:0.95; transform:translateY(0); }\r\n    .tv-info-bar { display:flex; align-items:center; justify-content:space-between; padding:8px 12px; color:#e9eef5; font-size:13px; background:linear-gradient(to bottom, rgba(0,0,0,0.35), rgba(0,0,0,0.25)); }\r\n    .tv-title { font-weight:600; color:#ffa14f; }\r\n    @media (max-width:600px) { .tv-logo-overlay img { max-width:72px; } }\r\n    \/* embed blocked banner inside wrapper *\/\r\n    .ttv-embed-banner { position:absolute; left:0; right:0; top:0; bottom:0; display:flex; align-items:center; justify-content:center; background:rgba(0,0,0,0.6); z-index:9999; color:#fff; flex-direction:column; gap:10px; padding:16px; }\r\n    .ttv-embed-banner a { background:#ff0000; color:#fff; padding:10px 16px; border-radius:6px; text-decoration:none; font-weight:600; }\r\n    <\/style>\r\n\r\n    <div class=\"custom-tv-wrapper\" id=\"tvWrapper\">\r\n        <video id=\"custom-tv-player\" controls autoplay playsinline muted preload=\"metadata\">\r\n            <source id=\"video-source\" src=\"\" type=\"video\/mp4\">\r\n            Your browser does not support the video tag.\r\n        <\/video>\r\n\r\n        <div id=\"embedContainer\" style=\"display:none;\"><\/div>\r\n\r\n        <div class=\"tv-logo-overlay\" id=\"tvLogo\" aria-hidden=\"true\">\r\n            <img decoding=\"async\" src=\"https:\/\/divinewisdomministries.com\/wp-content\/themes\/oceanwp\/assets\/img\/logo.png\" alt=\"ThiruThoothar TV\" style=\"max-width:110px; filter:drop-shadow(0 4px 10px rgba(0,0,0,0.45));\">\r\n        <\/div>\r\n\r\n        <div class=\"tv-info-bar\" id=\"tvInfoBar\">\r\n            <div class=\"tv-info-left\">\r\n                <div class=\"tv-title\">ThiruThoothar TV<\/div>\r\n                <div class=\"tv-sub\" id=\"tvNowPlaying\" style=\"margin-left:10px; color:#bcd6ff; font-size:12px;\">Loading...<\/div>\r\n            <\/div>\r\n            <div class=\"tv-hint\" style=\"font-size:12px; color:rgba(255,255,255,0.8);\">Space = Play\/Pause \u2022 F = Fullscreen \u2022 \u2192 = Next<\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <script>\r\n    (function(){\r\n        const pollMs = 8000;\r\n        const adminAjax = 'https:\/\/divinewisdomministries.com\/wp-admin\/admin-ajax.php';\r\n\r\n        const wrapper = document.getElementById('tvWrapper');\r\n        const player = document.getElementById('custom-tv-player');\r\n        const videoSource = document.getElementById('video-source');\r\n        const embedContainer = document.getElementById('embedContainer');\r\n        const logo = document.getElementById('tvLogo');\r\n        const nowPlayingEl = document.getElementById('tvNowPlaying');\r\n\r\n        \/\/ program display element\r\n        let programEl = document.getElementById('ttv_program_display');\r\n        if (!programEl) {\r\n            programEl = document.createElement('div');\r\n            programEl.id = 'ttv_program_display';\r\n            programEl.style.position = 'absolute';\r\n            programEl.style.left = '12px';\r\n            programEl.style.bottom = '60px';\r\n            programEl.style.zIndex = '1003';\r\n            programEl.style.color = '#fff';\r\n            programEl.style.fontSize = '14px';\r\n            programEl.style.background = 'rgba(0,0,0,0.45)';\r\n            programEl.style.padding = '6px 10px';\r\n            programEl.style.borderRadius = '6px';\r\n            programEl.style.maxWidth = '70%';\r\n            programEl.style.overflow = 'hidden';\r\n            programEl.style.whiteSpace = 'nowrap';\r\n            programEl.style.textOverflow = 'ellipsis';\r\n            wrapper.appendChild(programEl);\r\n        }\r\n\r\n        \/\/ LocalStorage keys\r\n        const LS_STATE = 'ttv_playlist_state_v1';\r\n        const LS_LAST_PROGRAM = 'ttv_last_program';\r\n\r\n        let originalVideos = [], playlist = [], currentIndex = 0, currentlyLive = false, logoTimeout = null;\r\n\r\n        function shuffleArray(array) {\r\n            const a = array.slice();\r\n            for (let i = a.length - 1; i > 0; i--) {\r\n                const j = Math.floor(Math.random() * (i + 1));\r\n                [a[i], a[j]] = [a[j], a[i]];\r\n            }\r\n            return a;\r\n        }\r\n        function filenameFromUrl(url) {\r\n            try { return decodeURIComponent(url.split('\/').pop().split('?')[0]); } catch(e) { return url; }\r\n        }\r\n\r\n        function savePlaylistState() {\r\n            try {\r\n                const state = {\r\n                    playlist: playlist,\r\n                    currentIndex: currentIndex,\r\n                    currentTime: Math.max(0, Math.floor(player.currentTime || 0)),\r\n                    updated_at: Date.now()\r\n                };\r\n                localStorage.setItem(LS_STATE, JSON.stringify(state));\r\n            } catch(e){}\r\n        }\r\n        function loadPlaylistState() {\r\n            try {\r\n                const raw = localStorage.getItem(LS_STATE);\r\n                if (!raw) return null;\r\n                const state = JSON.parse(raw);\r\n                if (!state || !Array.isArray(state.playlist)) return null;\r\n                return state;\r\n            } catch(e){ return null; }\r\n        }\r\n\r\n        function showProgramInfo(djName, programTitle) {\r\n            let text = '';\r\n            if (programTitle) text += programTitle;\r\n            if (djName) text += (text ? ' \u2022 Presented By ' : 'Presented By ') + djName;\r\n            if (!text) {\r\n                try {\r\n                    const last = JSON.parse(localStorage.getItem(LS_LAST_PROGRAM) || 'null');\r\n                    if (last && (last.program_title || last.dj_name)) {\r\n                        text = (last.program_title || '') + (last.program_title && last.dj_name ? ' \u2022 Presented By ' : '') + (last.dj_name || '');\r\n                    }\r\n                } catch(e){}\r\n            }\r\n            programEl.textContent = text;\r\n            programEl.style.display = text ? 'block' : 'none';\r\n        }\r\n\r\n        function initPlaylist(videos) {\r\n            if (!Array.isArray(videos) || videos.length === 0) {\r\n                nowPlayingEl.textContent = 'No videos available';\r\n                return;\r\n            }\r\n            originalVideos = videos.slice();\r\n\r\n            const saved = loadPlaylistState();\r\n            if ( saved && Array.isArray(saved.playlist) && saved.playlist.length > 0 ) {\r\n                const serverSet = new Set(originalVideos);\r\n                const ok = saved.playlist.every(u => serverSet.has(u));\r\n                if ( ok ) {\r\n                    playlist = saved.playlist.slice();\r\n                    currentIndex = Math.min(saved.currentIndex || 0, playlist.length - 1);\r\n                    const src = playlist[currentIndex];\r\n                    videoSource.src = src;\r\n                    player.load();\r\n                    player.addEventListener('canplay', function __seekOnce(){\r\n                        try {\r\n                            const t = Math.max(0, Math.floor(saved.currentTime || 0));\r\n                            if ( t > 1 && player.duration && t < player.duration - 1 ) player.currentTime = t;\r\n                        } catch(e){}\r\n                        player.play().catch(()=>{});\r\n                        player.removeEventListener('canplay', __seekOnce);\r\n                    });\r\n                    updateNowPlaying();\r\n                    startPeriodicSave();\r\n                    return;\r\n                }\r\n            }\r\n\r\n            playlist = shuffleArray(originalVideos);\r\n            currentIndex = 0;\r\n            updateNowPlaying();\r\n            playCurrent();\r\n            startPeriodicSave();\r\n        }\r\n\r\n        function updateNowPlaying() {\r\n            const name = playlist[currentIndex] ? filenameFromUrl(playlist[currentIndex]) : '\u2014';\r\n            nowPlayingEl.textContent = name;\r\n        }\r\n\r\n        function playCurrent() {\r\n            if (!playlist || playlist.length === 0) return;\r\n            const src = playlist[currentIndex];\r\n            videoSource.src = src;\r\n            player.load();\r\n            const p = player.play();\r\n            if (p && typeof p.catch === 'function') p.catch(()=>{});\r\n            updateNowPlaying();\r\n            revealLogoTemporarily();\r\n        }\r\n\r\n        player.addEventListener('ended', function(){\r\n            currentIndex++;\r\n            if (currentIndex >= playlist.length) {\r\n                playlist = shuffleArray(originalVideos);\r\n                currentIndex = 0;\r\n            }\r\n            playCurrent();\r\n        });\r\n\r\n        player.addEventListener('error', function(e){\r\n            console.warn('Video error; skipping to next.', e);\r\n            currentIndex++;\r\n            if (currentIndex >= playlist.length) {\r\n                playlist = shuffleArray(originalVideos);\r\n                currentIndex = 0;\r\n            }\r\n            setTimeout(playCurrent, 600);\r\n        });\r\n\r\n        function revealLogoTemporarily() {\r\n            logo.classList.add('show');\r\n            if (logoTimeout) clearTimeout(logoTimeout);\r\n            logoTimeout = setTimeout(()=>{ logo.classList.remove('show'); }, 6000);\r\n        }\r\n        wrapper.addEventListener('mouseenter', ()=>{ if (logoTimeout) clearTimeout(logoTimeout); logo.classList.add('show'); });\r\n        wrapper.addEventListener('mouseleave', ()=>{ if (logoTimeout) clearTimeout(logoTimeout); logoTimeout = setTimeout(()=>logo.classList.remove('show'),900); });\r\n\r\n        let periodicSave = null;\r\n        function startPeriodicSave() {\r\n            if ( periodicSave ) return;\r\n            periodicSave = setInterval(savePlaylistState, 5000);\r\n            window.addEventListener('beforeunload', savePlaylistState);\r\n        }\r\n\r\n        function activateLive(embedUrl, djName, programTitle, embedBlocked) {\r\n            if (currentlyLive) return;\r\n            currentlyLive = true;\r\n            try { player.pause(); } catch(e){}\r\n            player.style.display = 'none';\r\n\r\n            showProgramInfo(djName, programTitle);\r\n\r\n            embedContainer.innerHTML = '';\r\n            const div = document.createElement('div');\r\n            div.className = 'custom-tv-embed';\r\n            div.style.position = 'relative';\r\n            div.style.paddingTop = '56.25%';\r\n            div.style.width = '100%';\r\n            div.style.height = '0';\r\n            div.style.overflow = 'hidden';\r\n\r\n            const iframe = document.createElement('iframe');\r\n            iframe.setAttribute('src', embedUrl + (embedUrl.includes('?') ? '&autoplay=1&mute=1' : '?autoplay=1&mute=1'));\r\n            iframe.setAttribute('allow','autoplay; encrypted-media; picture-in-picture; fullscreen');\r\n            iframe.setAttribute('frameborder','0');\r\n            iframe.style.position = 'absolute';\r\n            iframe.style.top = '0';\r\n            iframe.style.left = '0';\r\n            iframe.style.width = '100%';\r\n            iframe.style.height = '100%';\r\n            iframe.style.border = '0';\r\n            div.appendChild(iframe);\r\n            embedContainer.appendChild(div);\r\n            embedContainer.style.display = 'block';\r\n            logo.classList.add('show');\r\n\r\n            \/\/ if embed_blocked flag is set, show banner overlay\r\n            if ( embedBlocked ) {\r\n                let banner = document.getElementById('ttv_embed_banner');\r\n                if (!banner) {\r\n                    banner = document.createElement('div');\r\n                    banner.id = 'ttv_embed_banner';\r\n                    banner.className = 'ttv-embed-banner';\r\n                    banner.innerHTML = '<div style=\"font-size:18px;font-weight:600;\">Embedding blocked by video owner<\/div><div style=\"max-width:480px;text-align:center;\">This stream cannot be played inside other websites \u2014 open it on YouTube to watch.<\/div>';\r\n                    const btn = document.createElement('a');\r\n                    \/\/ derive watch URL\r\n                    let watchUrl = embedUrl;\r\n                    if ( embedUrl.indexOf('\/embed\/') !== -1 ) {\r\n                        const vid = embedUrl.split('\/embed\/').pop().split('?')[0];\r\n                        watchUrl = 'https:\/\/www.youtube.com\/watch?v=' + vid;\r\n                    }\r\n                    btn.href = watchUrl;\r\n                    btn.target = '_blank';\r\n                    btn.textContent = 'Watch on YouTube';\r\n                    banner.appendChild(btn);\r\n                    const close = document.createElement('button');\r\n                    close.textContent = 'Close';\r\n                    close.style.marginTop = '6px';\r\n                    close.onclick = function(){ banner.remove(); };\r\n                    banner.appendChild(close);\r\n                    wrapper.appendChild(banner);\r\n                }\r\n            } else {\r\n                const b = document.getElementById('ttv_embed_banner');\r\n                if ( b ) b.remove();\r\n            }\r\n        }\r\n\r\n        function deactivateLive() {\r\n            if (!currentlyLive) return;\r\n            currentlyLive = false;\r\n            embedContainer.innerHTML = '';\r\n            embedContainer.style.display = 'none';\r\n            player.style.display = '';\r\n\r\n            \/\/ restore program info from last saved program\r\n            try {\r\n                const last = JSON.parse(localStorage.getItem(LS_LAST_PROGRAM) || 'null');\r\n                if ( last && (last.program_title || last.dj_name) ) showProgramInfo(last.dj_name, last.program_title);\r\n                else showProgramInfo('', '');\r\n            } catch(e){ showProgramInfo('',''); }\r\n\r\n            \/\/ resume playlist from saved state if valid\r\n            const saved = loadPlaylistState();\r\n            if ( saved && Array.isArray(saved.playlist) && saved.playlist.length > 0 ) {\r\n                const serverSet = new Set(originalVideos);\r\n                const ok = saved.playlist.every(u => serverSet.has(u));\r\n                if ( ok ) {\r\n                    playlist = saved.playlist.slice();\r\n                    currentIndex = Math.min(saved.currentIndex || 0, playlist.length - 1);\r\n                    const src = playlist[currentIndex];\r\n                    videoSource.src = src;\r\n                    player.load();\r\n                    player.addEventListener('canplay', function __seekOnce(){\r\n                        try {\r\n                            const t = Math.max(0, Math.floor(saved.currentTime || 0));\r\n                            if ( t > 1 && player.duration && t < player.duration - 1 ) player.currentTime = t;\r\n                        } catch(e){}\r\n                        player.play().catch(()=>{});\r\n                        player.removeEventListener('canplay', __seekOnce);\r\n                    });\r\n                    updateNowPlaying();\r\n                    return;\r\n                }\r\n            }\r\n\r\n            \/\/ fallback: just play current index\r\n            playCurrent();\r\n        }\r\n\r\n        async function checkLiveStatus() {\r\n            try {\r\n                const resp = await fetch(adminAjax + '?action=ttv_get_live_status', {cache:'no-store'});\r\n                const json = await resp.json();\r\n                if (!json || !json.success) return;\r\n                const p = json.data || { live:0, embed_url:'', embed_blocked:0, dj_name:'', program_title:'' };\r\n                const isLive = p.live === 1 || p.live === '1' || p.live === true;\r\n                const embed = p.embed_url || '';\r\n                const djName = p.dj_name || '';\r\n                const programTitle = p.program_title || '';\r\n                const blocked = p.embed_blocked === 1 || p.embed_blocked === '1';\r\n\r\n                if ( isLive && !currentlyLive ) activateLive(embed, djName, programTitle, blocked);\r\n                else if ( !isLive && currentlyLive ) deactivateLive();\r\n                else if ( isLive && currentlyLive ) showProgramInfo(djName, programTitle);\r\n            } catch(e) {\r\n                console.warn('Error polling live status:', e);\r\n            }\r\n        }\r\n\r\n        setInterval(checkLiveStatus, pollMs);\r\n        checkLiveStatus();\r\n\r\n        \/\/ load playlist\r\n        fetch(adminAjax + '?action=get_videos', {cache:'no-store'})\r\n            .then(r => r.json())\r\n            .then(videos => initPlaylist(videos))\r\n            .catch(e => { console.error('Error loading videos:', e); nowPlayingEl.textContent = 'Error loading videos'; });\r\n\r\n        \/\/ interaction to trigger play\r\n        function tryPlayOnInteraction() {\r\n            const p = player.play();\r\n            if (p && typeof p.catch === 'function') p.catch(()=>{});\r\n            document.removeEventListener('click', tryPlayOnInteraction);\r\n            document.removeEventListener('touchstart', tryPlayOnInteraction);\r\n        }\r\n        document.addEventListener('click', tryPlayOnInteraction);\r\n        document.addEventListener('touchstart', tryPlayOnInteraction);\r\n\r\n        \/\/ keep a lightweight updater of last_program in localStorage while live\r\n        setInterval(async function() {\r\n            try {\r\n                const resp = await fetch(adminAjax + '?action=ttv_get_live_status', {cache:'no-store'});\r\n                const j = await resp.json();\r\n                if ( j && j.success && j.data && j.data.live ) {\r\n                    const p = j.data;\r\n                    try { localStorage.setItem(LS_LAST_PROGRAM, JSON.stringify({ dj_name: p.dj_name || '', program_title: p.program_title || '', updated_at: Date.now() })); } catch(e){}\r\n                }\r\n            } catch(e){}\r\n        }, 15000);\r\n\r\n    })();\r\n    <\/script>","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-18","page","type-page","status-publish","hentry","entry"],"_links":{"self":[{"href":"https:\/\/divinewisdomministries.com\/ta\/wp-json\/wp\/v2\/pages\/18","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/divinewisdomministries.com\/ta\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/divinewisdomministries.com\/ta\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/divinewisdomministries.com\/ta\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/divinewisdomministries.com\/ta\/wp-json\/wp\/v2\/comments?post=18"}],"version-history":[{"count":15,"href":"https:\/\/divinewisdomministries.com\/ta\/wp-json\/wp\/v2\/pages\/18\/revisions"}],"predecessor-version":[{"id":480,"href":"https:\/\/divinewisdomministries.com\/ta\/wp-json\/wp\/v2\/pages\/18\/revisions\/480"}],"wp:attachment":[{"href":"https:\/\/divinewisdomministries.com\/ta\/wp-json\/wp\/v2\/media?parent=18"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}