| import streamlit as st |
| import streamlit.components.v1 as components |
|
|
| |
| st.set_page_config( |
| page_title="TiviStream Pro", |
| page_icon="📺", |
| layout="wide", |
| initial_sidebar_state="collapsed" |
| ) |
|
|
| |
| st.markdown(""" |
| <style> |
| /* Hide Streamlit header, footer, and hamburger menu */ |
| #MainMenu {visibility: hidden;} |
| footer {visibility: hidden;} |
| header {visibility: hidden;} |
| |
| /* Remove padding to allow full-screen iframe */ |
| .block-container { |
| padding-top: 0rem; |
| padding-bottom: 0rem; |
| padding-left: 0rem; |
| padding-right: 0rem; |
| } |
| |
| /* Ensure the iframe container takes full height */ |
| iframe { |
| height: 100vh !important; |
| } |
| </style> |
| """, unsafe_allow_html=True) |
|
|
| |
| st.markdown( |
| """ |
| <div style="position: absolute; top: 10px; right: 10px; z-index: 9999;"> |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" style="color: rgba(255,255,255,0.5); text-decoration: none; font-family: sans-serif; font-size: 12px; background: rgba(0,0,0,0.5); padding: 5px 10px; border-radius: 15px;">Built with anycoder</a> |
| </div> |
| """, |
| unsafe_allow_html=True |
| ) |
|
|
| |
| |
| |
|
|
| html_code = """ |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>TiviStream Pro</title> |
| |
| <!-- Fonts & Icons --> |
| <link rel="preconnect" href="https://fonts.googleapis.com"> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| |
| <style> |
| :root { |
| --bg-dark: #0b0d12; |
| --bg-glass: rgba(15, 23, 42, 0.85); |
| --bg-panel: rgba(30, 41, 59, 0.7); |
| --primary: #3b82f6; |
| --primary-hover: #2563eb; |
| --accent: #f59e0b; |
| --text-main: #f8fafc; |
| --text-muted: #94a3b8; |
| --border: rgba(255, 255, 255, 0.08); |
| --focus-bg: rgba(255, 255, 255, 0.1); |
| --transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); |
| } |
| |
| * { |
| box-sizing: border-box; |
| margin: 0; |
| padding: 0; |
| user-select: none; |
| -webkit-font-smoothing: antialiased; |
| } |
| |
| body { |
| font-family: 'Inter', sans-serif; |
| background-color: #000; |
| color: var(--text-main); |
| height: 100vh; |
| width: 100vw; |
| overflow: hidden; |
| } |
| |
| /* --- Layers --- */ |
| #video-layer { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| z-index: 0; |
| } |
| |
| video { |
| width: 100%; |
| height: 100%; |
| object-fit: cover; |
| transition: transform 0.3s ease; |
| } |
| |
| #ui-layer { |
| position: relative; |
| z-index: 10; |
| height: 100%; |
| display: flex; |
| flex-direction: column; |
| background: linear-gradient(to right, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.7) 40%, rgba(0,0,0,0) 100%); |
| transition: opacity 0.3s ease; |
| } |
| |
| /* --- Login Modal --- */ |
| #login-modal { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| z-index: 100; |
| background: rgba(0,0,0,0.85); |
| backdrop-filter: blur(15px); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| transition: opacity 0.4s ease; |
| } |
| |
| #login-modal.hidden { |
| opacity: 0; |
| pointer-events: none; |
| } |
| |
| .login-container { |
| background: #1e293b; |
| width: 600px; |
| border-radius: 16px; |
| border: 1px solid var(--border); |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); |
| overflow: hidden; |
| display: flex; |
| flex-direction: column; |
| } |
| |
| .login-header { |
| padding: 20px 30px; |
| background: rgba(0,0,0,0.2); |
| border-bottom: 1px solid var(--border); |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| } |
| |
| .login-tabs { |
| display: flex; |
| background: rgba(0,0,0,0.3); |
| padding: 5px; |
| } |
| |
| .tab-btn { |
| flex: 1; |
| padding: 15px; |
| background: transparent; |
| border: none; |
| color: var(--text-muted); |
| cursor: pointer; |
| font-weight: 600; |
| transition: var(--transition); |
| border-bottom: 2px solid transparent; |
| } |
| |
| .tab-btn.active { |
| color: var(--primary); |
| border-bottom-color: var(--primary); |
| background: rgba(59, 130, 246, 0.05); |
| } |
| |
| .login-body { |
| padding: 30px; |
| } |
| |
| .input-group { |
| margin-bottom: 20px; |
| } |
| |
| .input-group label { |
| display: block; |
| margin-bottom: 8px; |
| color: var(--text-muted); |
| font-size: 0.9rem; |
| } |
| |
| .form-input { |
| width: 100%; |
| padding: 12px 15px; |
| background: rgba(0,0,0,0.3); |
| border: 1px solid var(--border); |
| border-radius: 8px; |
| color: white; |
| font-size: 1rem; |
| outline: none; |
| transition: var(--transition); |
| } |
| |
| .form-input:focus { |
| border-color: var(--primary); |
| box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2); |
| } |
| |
| .btn-primary { |
| width: 100%; |
| padding: 14px; |
| background: var(--primary); |
| color: white; |
| border: none; |
| border-radius: 8px; |
| font-weight: 600; |
| font-size: 1rem; |
| cursor: pointer; |
| transition: var(--transition); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| gap: 10px; |
| } |
| |
| .btn-primary:hover { |
| background: var(--primary-hover); |
| transform: translateY(-1px); |
| } |
| |
| /* --- Main UI --- */ |
| header { |
| padding: 1.5rem 2rem; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| } |
| |
| .brand { |
| font-size: 1.5rem; |
| font-weight: 700; |
| color: white; |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| } |
| |
| .brand span { |
| background: linear-gradient(135deg, #fff 0%, #94a3b8 100%); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| } |
| |
| .top-meta { |
| display: flex; |
| gap: 20px; |
| align-items: center; |
| font-size: 0.9rem; |
| color: var(--text-muted); |
| } |
| |
| .connection-badge { |
| background: rgba(16, 185, 129, 0.1); |
| color: #10b981; |
| padding: 4px 10px; |
| border-radius: 20px; |
| border: 1px solid rgba(16, 185, 129, 0.2); |
| font-size: 0.75rem; |
| font-weight: 600; |
| display: flex; |
| align-items: center; |
| gap: 6px; |
| } |
| |
| .content-area { |
| display: flex; |
| flex: 1; |
| overflow: hidden; |
| padding-bottom: 20px; |
| } |
| |
| /* Sidebar */ |
| .sidebar { |
| width: 280px; |
| padding: 10px 0; |
| display: flex; |
| flex-direction: column; |
| border-right: 1px solid var(--border); |
| background: rgba(0,0,0,0.2); |
| } |
| |
| .group-item { |
| padding: 12px 25px; |
| color: var(--text-muted); |
| cursor: pointer; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| transition: var(--transition); |
| border-left: 3px solid transparent; |
| } |
| |
| .group-item:hover { |
| background: var(--focus-bg); |
| color: white; |
| } |
| |
| .group-item.active { |
| background: linear-gradient(90deg, rgba(59, 130, 246, 0.1), transparent); |
| color: white; |
| border-left-color: var(--primary); |
| font-weight: 600; |
| } |
| |
| /* EPG List */ |
| .epg-container { |
| flex: 1; |
| display: flex; |
| flex-direction: column; |
| padding-left: 20px; |
| position: relative; |
| } |
| |
| .channel-list { |
| overflow-y: auto; |
| padding-right: 20px; |
| mask-image: linear-gradient(to bottom, black 90%, transparent 100%); |
| } |
| |
| .channel-row { |
| display: grid; |
| grid-template-columns: 60px 60px 200px 1fr 80px; |
| align-items: center; |
| padding: 12px 15px; |
| border-radius: 8px; |
| margin-bottom: 4px; |
| cursor: pointer; |
| border: 1px solid transparent; |
| transition: var(--transition); |
| } |
| |
| .channel-row:hover { |
| background: var(--focus-bg); |
| } |
| |
| .channel-row.active { |
| background: var(--primary); |
| border-color: rgba(255,255,255,0.2); |
| box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3); |
| transform: scale(1.01); |
| } |
| |
| .ch-logo { |
| width: 40px; |
| height: 30px; |
| background: white; |
| border-radius: 4px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| color: #333; |
| font-size: 0.8rem; |
| } |
| |
| .program-bar-container { |
| height: 4px; |
| background: rgba(255,255,255,0.15); |
| border-radius: 2px; |
| margin-top: 6px; |
| overflow: hidden; |
| } |
| |
| .program-bar-fill { |
| height: 100%; |
| background: var(--text-muted); |
| width: 0%; |
| } |
| |
| .channel-row.active .program-bar-fill { |
| background: white; |
| } |
| |
| /* Info Panel (Bottom) */ |
| .info-panel { |
| position: absolute; |
| bottom: 0; |
| left: 0; |
| width: 100%; |
| padding: 30px 40px; |
| background: linear-gradient(to top, #000 10%, rgba(0,0,0,0.8) 60%, transparent 100%); |
| display: flex; |
| align-items: flex-end; |
| justify-content: space-between; |
| } |
| |
| .info-content h1 { |
| font-size: 2.5rem; |
| margin-bottom: 10px; |
| font-weight: 700; |
| text-shadow: 0 2px 4px rgba(0,0,0,0.5); |
| } |
| |
| .info-meta { |
| display: flex; |
| gap: 15px; |
| margin-bottom: 15px; |
| } |
| |
| .tag { |
| background: rgba(255,255,255,0.15); |
| padding: 4px 10px; |
| border-radius: 6px; |
| font-size: 0.8rem; |
| font-weight: 600; |
| backdrop-filter: blur(4px); |
| } |
| |
| .tag.hd { background: #f59e0b; color: #000; } |
| |
| .description { |
| max-width: 600px; |
| color: var(--text-muted); |
| line-height: 1.5; |
| font-size: 1rem; |
| text-shadow: 0 1px 2px rgba(0,0,0,0.8); |
| } |
| |
| /* Utils */ |
| ::-webkit-scrollbar { width: 6px; } |
| ::-webkit-scrollbar-track { background: transparent; } |
| ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; } |
| |
| .loader { |
| border: 3px solid rgba(255,255,255,0.1); |
| border-top: 3px solid var(--primary); |
| border-radius: 50%; |
| width: 24px; |
| height: 24px; |
| animation: spin 1s linear infinite; |
| display: none; |
| } |
| |
| @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } |
| |
| .hidden { display: none !important; } |
| |
| </style> |
| </head> |
| <body> |
| |
| <!-- Video Layer --> |
| <div id="video-layer"> |
| <video id="main-player" loop muted playsinline poster="https://images.unsplash.com/photo-1593784991095-a205069470b6?q=80&w=2070&auto=format&fit=crop"> |
| <!-- Placeholder video --> |
| <source src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4" type="video/mp4"> |
| </video> |
| </div> |
| |
| <!-- Login / Connection Modal --> |
| <div id="login-modal"> |
| <div class="login-container"> |
| <div class="login-header"> |
| <div class="brand" style="font-size: 1.2rem;"> |
| <i class="fa-solid fa-tv"></i> <span>TiviStream Pro</span> |
| </div> |
| <div style="color: var(--text-muted); font-size: 0.9rem;">Connection Setup</div> |
| </div> |
| |
| <div class="login-tabs"> |
| <button class="tab-btn active" onclick="switchTab('m3u')">M3U Playlist</button> |
| <button class="tab-btn" onclick="switchTab('xtream')">Xtream Codes</button> |
| <button class="tab-btn" onclick="switchTab('mac')">Stalker MAC</button> |
| </div> |
| |
| <div class="login-body"> |
| <!-- M3U Form --> |
| <div id="form-m3u" class="auth-form"> |
| <div class="input-group"> |
| <label>Playlist Name</label> |
| <input type="text" class="form-input" placeholder="My TV List" value="Demo Playlist"> |
| </div> |
| <div class="input-group"> |
| <label>M3U URL</label> |
| <input type="text" class="form-input" placeholder="http://example.com/playlist.m3u" value="https://iptv-org.github.io/iptv/index.m3u"> |
| </div> |
| </div> |
| |
| <!-- Xtream Form --> |
| <div id="form-xtream" class="auth-form hidden"> |
| <div class="input-group"> |
| <label>Server URL</label> |
| <input type="text" class="form-input" placeholder="http://server-url.com:8080"> |
| </div> |
| <div style="display: flex; gap: 15px;"> |
| <div class="input-group" style="flex:1"> |
| <label>Username</label> |
| <input type="text" class="form-input" placeholder="user"> |
| </div> |
| <div class="input-group" style="flex:1"> |
| <label>Password</label> |
| <input type="password" class="form-input" placeholder="pass"> |
| </div> |
| </div> |
| </div> |
| |
| <!-- MAC Form --> |
| <div id="form-mac" class="auth-form hidden"> |
| <div class="input-group"> |
| <label>Portal URL</label> |
| <input type="text" class="form-input" placeholder="http://mag.portal.com"> |
| </div> |
| <div class="input-group"> |
| <label>MAC Address</label> |
| <input type="text" class="form-input" placeholder="00:1A:79:XX:XX:XX" value="00:1A:79:00:00:01"> |
| </div> |
| </div> |
| |
| <button class="btn-primary" onclick="connectService()"> |
| <span class="loader" id="btn-loader"></span> |
| <span id="btn-text">Connect Service</span> |
| </button> |
| |
| <div style="margin-top: 15px; font-size: 0.8rem; color: var(--text-muted); text-align: center;"> |
| <i class="fa-solid fa-lock"></i> Secure Connection • |
| <span style="color: var(--primary); cursor: pointer;" onclick="loadDemo()">Load Demo Mode</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <!-- Main UI Layer --> |
| <div id="ui-layer" class="hidden"> |
| <header> |
| <div class="brand"> |
| <i class="fa-solid fa-tv" style="color: var(--primary)"></i> |
| <span>TiviStream Pro</span> |
| </div> |
| <div class="top-meta"> |
| <div class="connection-badge" id="conn-status"> |
| <i class="fa-solid fa-circle" style="font-size: 6px;"></i> Live |
| </div> |
| <div id="clock">12:00 PM</div> |
| <i class="fa-solid fa-gear" style="cursor: pointer;" onclick="showSettings()"></i> |
| </div> |
| </header> |
| |
| <div class="content-area"> |
| <!-- Groups Sidebar --> |
| <div class="sidebar" id="group-list"> |
| <!-- Groups injected via JS --> |
| </div> |
| |
| <!-- EPG List --> |
| <div class="epg-container"> |
| <div style="padding: 10px 15px; color: var(--text-muted); font-size: 0.85rem; border-bottom: 1px solid rgba(255,255,255,0.05); margin-bottom: 10px; display: flex; justify-content: space-between;"> |
| <span>CHANNEL LIST</span> |
| <span id="date-display">Mon, Jan 1</span> |
| </div> |
| <div class="channel-list" id="channel-list"> |
| <!-- Channels injected via JS --> |
| </div> |
| </div> |
| </div> |
| |
| <!-- Bottom Info Panel --> |
| <div class="info-panel"> |
| <div class="info-content"> |
| <div class="info-meta"> |
| <span class="tag hd">HD</span> |
| <span class="tag">LIVE</span> |
| <span class="tag" id="cat-tag">Sports</span> |
| </div> |
| <h1 id="info-title">Select Channel</h1> |
| <div style="color:white; font-weight:600; margin-bottom:8px; font-size: 1.1rem;"> |
| <span id="prog-title">Current Program</span> |
| <span style="color: var(--text-muted); font-weight: 400; font-size: 0.9rem; margin-left: 10px;" id="prog-time">12:00 - 13:00</span> |
| </div> |
| <p class="description" id="info-desc"> |
| Welcome to TiviStream Pro. Select a connection method to start, or use Demo Mode to preview the interface. |
| </p> |
| </div> |
| </div> |
| </div> |
| |
| <script> |
| // --- Data Simulation --- |
| const categories = ['All Channels', 'Favorites', 'Sports', 'Movies', 'News', 'Kids', 'Documentary', 'Music', 'UHD 4K']; |
| |
| const channelDB = [ |
| { id: 1, num: 101, name: "Sky Sports Main", cat: "Sports", icon: "fa-futbol", prog: "Live: Man City vs Liverpool", desc: "Exclusive live coverage of the Premier League match." }, |
| { id: 2, num: 102, name: "ESPN", cat: "Sports", icon: "fa-basketball", prog: "NBA: Lakers at Warriors", desc: "Live basketball action from the Crypto.com Arena." }, |
| { id: 3, num: 103, name: "HBO", cat: "Movies", icon: "fa-film", prog: "Dune: Part Two", desc: "Paul Atreides unites with Chani and the Fremen while on a warpath of revenge." }, |
| { id: 4, num: 104, name: "CNN International", cat: "News", icon: "fa-newspaper", prog: "Global News Hour", desc: "Breaking news and analysis from around the world." }, |
| { id: 5, num: 105, name: "Nat Geo Wild", cat: "Documentary", icon: "fa-leaf", prog: "Savage Kingdom", desc: "The struggle for survival in the African savanna." }, |
| { id: 6, num: 106, name: "Disney Channel", cat: "Kids", icon: "fa-shapes", prog: "Bluey", desc: "Bluey and Bingo play a game of magic statues." }, |
| { id: 7, num: 107, name: "MTV Live", cat: "Music", icon: "fa-music", prog: "Top 20 Hits", desc: "Countdown of the hottest tracks right now." }, |
| { id: 8, num: 108, name: "BBC One", cat: "News", icon: "fa-tv", prog: "BBC News at Six", desc: "The latest national and international news stories." }, |
| { id: 9, num: 109, name: "Cinema Action", cat: "Movies", icon: "fa-video", prog: "John Wick 4", desc: "John Wick uncovers a path to defeating The High Table." }, |
| { id: 10, num: 110, name: "Discovery Science", cat: "Documentary", icon: "fa-flask", prog: "How It's Made", desc: "Exploring how everyday objects are manufactured." } |
| ]; |
| |
| // Generate more dummy channels |
| for(let i=11; i<=50; i++) { |
| channelDB.push({ |
| id: i, |
| num: 100 + i, |
| name: `Channel ${100+i}`, |
| cat: categories[Math.floor(Math.random() * (categories.length - 2)) + 2], |
| icon: "fa-tv", |
| prog: "Regular Programming", |
| desc: "High definition broadcast stream." |
| }); |
| } |
| |
| // --- State --- |
| let activeCategory = 'All Channels'; |
| let activeChannelId = null; |
| let connectionType = 'm3u'; // m3u, xtream, mac |
| |
| // --- DOM Elements --- |
| const modal = document.getElementById('login-modal'); |
| const uiLayer = document.getElementById('ui-layer'); |
| const videoEl = document.getElementById('main-player'); |
| |
| // --- Functions --- |
| |
| function switchTab(type) { |
| connectionType = type; |
| document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); |
| event.target.classList.add('active'); |
| |
| document.querySelectorAll('.auth-form').forEach(f => f.classList.add('hidden')); |
| document.getElementById(`form-${type}`).classList.remove('hidden'); |
| } |
| |
| function connectService() { |
| const btnText = document.getElementById('btn-text'); |
| const loader = document.getElementById('btn-loader'); |
| |
| btnText.innerText = "Authenticating..."; |
| loader.style.display = "inline-block"; |
| |
| // Simulate API Latency |
| setTimeout(() => { |
| loadInterface(); |
| }, 1500); |
| } |
| |
| function loadDemo() { |
| loadInterface(); |
| } |
| |
| function loadInterface() { |
| modal.classList.add('hidden'); |
| uiLayer.classList.remove('hidden'); |
| |
| // Init UI |
| renderGroups(); |
| renderChannels('All Channels'); |
| startClock(); |
| |
| // Select first channel automatically |
| selectChannel(channelDB[0]); |
| |
| // Update connection badge |
| const statusText = connectionType |