JUSTICE APPZ
Cloud Web Player
Server Portal
Dawwg Main Server
Username
Password
CONNECT
Justice Appz
12:00
LIVE TV
MOVIES
TV SERIES
SETTINGS
ACCOUNT INFO
CHANGE PLAYLIST
Expiration: Unlimited
Justice Appz
Justice Appz
12:00
Loading...
Categories
Select a channel
ENGINE:
Video.js (VHS)
Hls.js Engine
Native Direct
Unmute
TAP TO UNMUTE AUDIO
MOVIES
Categories
All Categories
{ loadPublicConfig(); setInterval(updateClock, 1000); }); function initVideoPlayer() { const el = document.getElementById('justice-vjs'); if (!el) return; if (!vjsPlayer && typeof videojs !== 'undefined') { try { vjsPlayer = videojs('justice-vjs', { autoplay: true, controls: true, fluid: false, html5: { vhs: { overrideNative: true, handleManifestRedirects: true, enableLowInitialPlaylist: true } } }); // Show unmute banner if player starts muted vjsPlayer.on('volumechange', () => { const banner = document.getElementById('unmute-banner'); if (banner) banner.style.display = vjsPlayer.muted() ? 'flex' : 'none'; }); } catch (e) {} } } function initVodPlayer() { const el = document.getElementById('vod-vjs'); if (!el) return; if (!vodPlayer && typeof videojs !== 'undefined') { try { vodPlayer = videojs('vod-vjs', { autoplay: true, controls: true, fluid: false }); } catch (e) {} } } async function loadPublicConfig() { try { const res = await fetch('/api/public-config'); const data = await res.json(); const select = document.getElementById('portal-select'); if (!select) return; const portals = data.portals || []; if (portals.length > 0) { select.innerHTML = ''; portals.forEach(portal => { const opt = document.createElement('option'); opt.value = portal.url; opt.innerText = portal.name; select.appendChild(opt); }); } } catch (e) {} } document.getElementById('login-form').addEventListener('submit', async (e) => { e.preventDefault(); const portal = document.getElementById('portal-select').value; const user = document.getElementById('iptv-user').value.trim(); const pass = document.getElementById('iptv-pass').value.trim(); if (!portal) return alert("Select a portal first"); const authUrl = `/api/proxy?url=${encodeURIComponent(`${portal}/player_api.php?username=${user}&password=${pass}`)}`; try { const res = await fetch(authUrl); const data = await res.json(); if (data.user_info && data.user_info.auth === 1) { currentSession = { user, pass, portal, info: data.user_info }; currentDns = portal; const exp = data.user_info.exp_date ? new Date(data.user_info.exp_date * 1000).toLocaleDateString() : 'Unlimited'; document.getElementById('account-expiry').innerText = `Expiration: ${exp}`; document.getElementById('header-username').innerText = user; document.getElementById('live-header-user').innerText = user; showView('dashboard-view'); } else { alert("Login Failed: Please check your credentials."); } } catch (err) { alert("Unable to reach Xtream Codes server."); } }); function showView(viewId) { document.querySelectorAll('.view').forEach(v => v.classList.remove('active')); const target = document.getElementById(viewId); if (target) target.classList.add('active'); } // ---------------- LIVE TV ---------------- async function openLiveView() { showView('live-view'); initVideoPlayer(); loadLiveCategories(); } async function loadLiveCategories() { const target = `${currentDns}/player_api.php?username=${currentSession.user}&password=${currentSession.pass}&action=get_live_categories`; const res = await fetch(`/api/proxy?url=${encodeURIComponent(target)}`); const categories = await res.json(); const modalList = document.getElementById('cat-modal-list'); modalList.innerHTML = ''; categories.forEach((cat) => { const item = document.createElement('div'); item.className = 'cat-modal-item'; item.innerText = cat.category_name; item.onclick = () => { document.getElementById('active-cat-name').innerText = cat.category_name; loadLiveStreams(cat.category_id); closeCategoriesModal(); }; modalList.appendChild(item); }); if (categories.length > 0) { document.getElementById('active-cat-name').innerText = categories[0].category_name; loadLiveStreams(categories[0].category_id); } } async function loadLiveStreams(categoryId) { const list = document.getElementById('channel-rows'); list.innerHTML = `
Loading channels...
`; const target = `${currentDns}/player_api.php?username=${currentSession.user}&password=${currentSession.pass}&action=get_live_streams&category_id=${categoryId}`; const res = await fetch(`/api/proxy?url=${encodeURIComponent(target)}`); liveStreams = await res.json(); renderChannelRows(liveStreams); if (liveStreams.length > 0 && !currentPlayingStream) { selectAndPlayLive(liveStreams[0]); } } const DEFAULT_ICON = 'data:image/svg+xml;utf8,
'; function renderChannelRows(streams) { const container = document.getElementById('channel-rows'); container.innerHTML = ''; if (!Array.isArray(streams) || streams.length === 0) { container.innerHTML = `
No channels found.
`; return; } streams.forEach(s => { const row = document.createElement('div'); row.className = `channel-row ${currentPlayingStream && currentPlayingStream.stream_id === s.stream_id ? 'active' : ''}`; row.id = `row-${s.stream_id}`; const rawIcon = s.stream_icon; const iconSrc = (rawIcon && rawIcon.startsWith('http')) ? rawIcon : DEFAULT_ICON; const img = document.createElement('img'); img.src = iconSrc; img.loading = "lazy"; img.onerror = function() { this.onerror = null; this.src = DEFAULT_ICON; }; const infoDiv = document.createElement('div'); infoDiv.className = 'channel-info'; const nameDiv = document.createElement('div'); nameDiv.className = 'channel-name'; nameDiv.innerText = s.name; const epgDiv = document.createElement('div'); epgDiv.className = 'channel-epg-now'; epgDiv.id = `epg-${s.stream_id}`; epgDiv.innerText = 'Loading schedule...'; infoDiv.appendChild(nameDiv); infoDiv.appendChild(epgDiv); const playIcon = document.createElement('i'); playIcon.className = 'fa-solid fa-play'; playIcon.style.color = '#64748b'; playIcon.style.fontSize = '0.85rem'; row.appendChild(img); row.appendChild(infoDiv); row.appendChild(playIcon); row.onclick = () => selectAndPlayLive(s); container.appendChild(row); fetchRowEPG(s); }); } async function fetchRowEPG(stream) { try { const epgUrl = `${currentDns}/player_api.php?username=${currentSession.user}&password=${currentSession.pass}&action=get_simple_data_table&stream_id=${stream.stream_id}`; const res = await fetch(`/api/proxy?url=${encodeURIComponent(epgUrl)}`); const data = await res.json(); const epgLabel = document.getElementById(`epg-${stream.stream_id}`); if (epgLabel && data.epg_listings && data.epg_listings.length > 0) { const prog = data.epg_listings[0]; const start = prog.start ? prog.start.split(' ')[1].slice(0, 5) : ''; let title = prog.title || 'Live Broadcast'; try { title = atob(prog.title); } catch (err) {} epgLabel.innerText = `${start} - ${title}`; } else if (epgLabel) { epgLabel.innerText = "No Guide Data Available"; epgLabel.style.color = "#64748b"; } } catch (e) {} } function selectAndPlayLive(stream) { currentPlayingStream = stream; document.querySelectorAll('.channel-row').forEach(r => r.classList.remove('active')); const activeRow = document.getElementById(`row-${stream.stream_id}`); if (activeRow) activeRow.classList.add('active'); document.getElementById('now-playing-title').innerText = stream.name; const pIcon = document.getElementById('now-playing-icon'); if (stream.stream_icon && stream.stream_icon.startsWith('http')) { pIcon.src = stream.stream_icon; pIcon.style.display = 'block'; } else { pIcon.style.display = 'none'; } playWithEngine(selectedEngine, stream); } function playWithEngine(engine, stream) { const rawVideo = document.querySelector('#justice-vjs video') || document.getElementById('justice-vjs'); const banner = document.getElementById('unmute-banner'); if (hlsInstance) { hlsInstance.destroy(); hlsInstance = null; } const streamUrl = `${currentDns}/live/${currentSession.user}/${currentSession.pass}/${stream.stream_id}.m3u8`; const proxied = `/api/proxy?url=${encodeURIComponent(streamUrl)}`; if (engine === 'hlsjs' && Hls.isSupported()) { if (vjsPlayer) vjsPlayer.pause(); hlsInstance = new Hls({ enableWorker: true, lowLatencyMode: true }); hlsInstance.loadSource(proxied); hlsInstance.attachMedia(rawVideo); hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => { rawVideo.play().catch(() => { rawVideo.muted = true; rawVideo.play(); if (banner) banner.style.display = 'flex'; }); }); return; } if (engine === 'native') { if (vjsPlayer) vjsPlayer.pause(); const tsUrl = `${currentDns}/live/${currentSession.user}/${currentSession.pass}/${stream.stream_id}.ts`; rawVideo.src = `/api/proxy?url=${encodeURIComponent(tsUrl)}`; rawVideo.play().catch(() => { rawVideo.muted = true; rawVideo.play(); if (banner) banner.style.display = 'flex'; }); return; } // Default: Video.js (VHS Engine) initVideoPlayer(); if (vjsPlayer) { vjsPlayer.pause(); vjsPlayer.src({ src: proxied, type: 'application/x-mpegURL' }); // Attempt unmuted play first vjsPlayer.muted(false); const p = vjsPlayer.play(); if (p !== undefined) { p.catch(() => { // If browser blocks unmuted sound, mute and show the Unmute banner vjsPlayer.muted(true); vjsPlayer.play().catch(() => {}); if (banner) banner.style.display = 'flex'; }); } } } function toggleAudioMute() { const banner = document.getElementById('unmute-banner'); const rawVideo = document.querySelector('#justice-vjs video') || document.getElementById('justice-vjs'); if (vjsPlayer) { vjsPlayer.muted(false); vjsPlayer.volume(1.0); } if (rawVideo) { rawVideo.muted = false; rawVideo.volume = 1.0; } if (banner) banner.style.display = 'none'; } function changePlayerEngine(eng) { selectedEngine = eng; if (currentPlayingStream) playWithEngine(selectedEngine, currentPlayingStream); } function retryCurrentStream() { if (currentPlayingStream) selectAndPlayLive(currentPlayingStream); } function filterLiveChannels() { const q = document.getElementById('live-search').value.toLowerCase(); const filtered = liveStreams.filter(s => (s.name || '').toLowerCase().includes(q)); renderChannelRows(filtered); } // ---------------- VOD MOVIES & SERIES ---------------- async function openVodView(type) { currentVodType = type; document.getElementById('vod-section-title').innerText = type === 'movies' ? 'MOVIES' : 'TV SERIES'; showView('vod-view'); loadVodCategories(type); } async function loadVodCategories(type) { const action = type === 'movies' ? 'get_vod_categories' : 'get_series_categories'; const target = `${currentDns}/player_api.php?username=${currentSession.user}&password=${currentSession.pass}&action=${action}`; const res = await fetch(`/api/proxy?url=${encodeURIComponent(target)}`); const categories = await res.json(); const modalList = document.getElementById('cat-modal-list'); modalList.innerHTML = ''; categories.forEach(cat => { const item = document.createElement('div'); item.className = 'cat-modal-item'; item.innerText = cat.category_name; item.onclick = () => { document.getElementById('active-vod-cat-name').innerText = cat.category_name; loadVodStreams(cat.category_id); closeCategoriesModal(); }; modalList.appendChild(item); }); if (categories.length > 0) { document.getElementById('active-vod-cat-name').innerText = categories[0].category_name; loadVodStreams(categories[0].category_id); } } async function loadVodStreams(categoryId) { const grid = document.getElementById('vod-grid'); grid.innerHTML = `
Loading catalog...
`; const action = currentVodType === 'movies' ? 'get_vod_streams' : 'get_series'; const target = `${currentDns}/player_api.php?username=${currentSession.user}&password=${currentSession.pass}&action=${action}&category_id=${categoryId}`; const res = await fetch(`/api/proxy?url=${encodeURIComponent(target)}`); vodStreams = await res.json(); renderVodGrid(vodStreams); } function renderVodGrid(items) { const grid = document.getElementById('vod-grid'); grid.innerHTML = ''; if (!Array.isArray(items) || items.length === 0) { grid.innerHTML = `
No titles found in this category.
`; return; } items.forEach(item => { const card = document.createElement('div'); card.className = 'vod-card'; const posterSrc = item.stream_icon || item.cover || DEFAULT_ICON; const img = document.createElement('img'); img.className = 'vod-poster'; img.src = posterSrc; img.loading = "lazy"; img.onerror = function() { this.onerror = null; this.src = DEFAULT_ICON; }; const titleDiv = document.createElement('div'); titleDiv.className = 'vod-title'; titleDiv.innerText = item.name || item.title || 'Untitled'; card.appendChild(img); card.appendChild(titleDiv); card.onclick = () => playVodItem(item); grid.appendChild(card); }); } function filterVodItems() { const q = document.getElementById('vod-search').value.toLowerCase(); const filtered = vodStreams.filter(s => (s.name || s.title || '').toLowerCase().includes(q)); renderVodGrid(filtered); } async function playVodItem(item) { initVodPlayer(); if (currentVodType === 'movies') { const ext = item.container_extension || 'mp4'; const streamUrl = `${currentDns}/movie/${currentSession.user}/${currentSession.pass}/${item.stream_id}.${ext}`; const proxied = `/api/proxy?url=${encodeURIComponent(streamUrl)}`; document.getElementById('vod-player-modal').style.display = 'flex'; if (vodPlayer) { vodPlayer.pause(); vodPlayer.src({ src: proxied, type: ext === 'm3u8' ? 'application/x-mpegURL' : 'video/mp4' }); vodPlayer.play().catch(() => {}); } } else { const target = `${currentDns}/player_api.php?username=${currentSession.user}&password=${currentSession.pass}&action=get_series_info&series_id=${item.series_id}`; const res = await fetch(`/api/proxy?url=${encodeURIComponent(target)}`); const seriesData = await res.json(); const episodes = seriesData.episodes || {}; const seasons = Object.keys(episodes); if (seasons.length > 0 && episodes[seasons[0]].length > 0) { const firstEp = episodes[seasons[0]][0]; const ext = firstEp.container_extension || 'mp4'; const streamUrl = `${currentDns}/series/${currentSession.user}/${currentSession.pass}/${firstEp.id}.${ext}`; const proxied = `/api/proxy?url=${encodeURIComponent(streamUrl)}`; document.getElementById('vod-player-modal').style.display = 'flex'; if (vodPlayer) { vodPlayer.pause(); vodPlayer.src({ src: proxied, type: 'video/mp4' }); vodPlayer.play().catch(() => {}); } } else { alert("No available episodes found for this series."); } } } function closeVodPlayer() { if (vodPlayer) vodPlayer.pause(); document.getElementById('vod-player-modal').style.display = 'none'; } function openCategoriesModal(type) { activeCatType = type; if (type === 'live') loadLiveCategories(); else loadVodCategories(currentVodType); document.getElementById('cat-modal').style.display = 'flex'; } function closeCategoriesModal() { document.getElementById('cat-modal').style.display = 'none'; } function updateClock() { const now = new Date(); const str = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`; const dClock = document.getElementById('current-clock'); const lClock = document.getElementById('live-clock'); if (dClock) dClock.innerText = str; if (lClock) lClock.innerText = str; } function logout() { if (vjsPlayer) vjsPlayer.pause(); if (vodPlayer) vodPlayer.pause(); currentPlayingStream = null; currentSession = null; showView('login-view'); }