<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>M3U/M3U8 Oynatıcı - Retro TV</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Orbitron:wght@400;700;900&display=swap" rel="stylesheet">
<style>
:root {
--primary: #6366f1;
--primary-dark: #4f46e5;
--secondary: #8b5cf6;
--accent: #ec4899;
--bg-primary: #0a0a0a;
--bg-secondary: #111111;
--bg-card: #1a1a1a;
--bg-hover: #252525;
--text-primary: #ffffff;
--text-secondary: #a1a1aa;
--border: #27272a;
--success: #10b981;
--error: #ef4444;
--tv-beige: #f4e4c1;
--tv-brown: #8b6f47;
--tv-dark: #3e2723;
--tv-metal: #c0c0c0;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
overflow-x: hidden;
}
/* Header Styles */
.hero-section {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
padding: 40px 0;
position: relative;
overflow: hidden;
}
.hero-section::before {
content: '';
position: absolute;
top: -50%;
right: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
animation: pulse 15s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 0.5; }
50% { transform: scale(1.1); opacity: 0.3; }
}
.hero-content {
position: relative;
z-index: 1;
text-align: center;
}
.hero-title {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
background: linear-gradient(to right, #fff, #e0e7ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-subtitle {
font-size: 1.1rem;
opacity: 0.9;
font-weight: 300;
}
/* Main Container */
.main-container {
max-width: 1600px;
margin: 0 auto;
padding: 20px;
}
/* Upload Section */
.upload-container {
background: var(--bg-card);
border-radius: 20px;
padding: 30px;
margin: 20px auto 30px;
max-width: 800px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.upload-zone {
border: 2px dashed var(--primary);
border-radius: 16px;
padding: 40px 30px;
text-align: center;
transition: all 0.3s ease;
background: linear-gradient(135deg, rgba(99, 102, 241, 0.1) 0%, rgba(139, 92, 246, 0.1) 100%);
position: relative;
overflow: hidden;
}
.upload-zone:hover {
background: rgba(99, 102, 241, 0.15);
border-color: var(--secondary);
}
.upload-zone.dragover {
transform: scale(1.02);
border-color: var(--secondary);
}
.upload-icon {
font-size: 48px;
color: var(--primary);
margin-bottom: 15px;
}
.url-input-group {
margin-top: 20px;
}
.url-input {
background: var(--bg-secondary);
border: 2px solid transparent;
color: var(--text-primary);
border-radius: 12px;
padding: 12px 20px;
font-size: 1rem;
width: 100%;
transition: all 0.3s ease;
}
.url-input:focus {
outline: none;
border-color: var(--primary);
background: var(--bg-hover);
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.2);
}
.load-btn {
background: var(--primary);
border: none;
color: white;
padding: 12px 24px;
border-radius: 8px;
font-weight: 500;
transition: all 0.3s ease;
margin-top: 10px;
}
.load-btn:hover {
background: var(--primary-dark);
transform: translateY(-2px);
}
/* Content Layout */
.content-layout {
display: grid;
grid-template-columns: 400px 1fr;
gap: 30px;
align-items: start;
}
/* Channel List */
.channel-panel {
background: var(--bg-card);
border-radius: 20px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
height: calc(100vh - 200px);
position: sticky;
top: 20px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.panel-header {
margin-bottom: 20px;
}
.panel-title {
font-size: 1.3rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 10px;
}
.search-container {
position: relative;
margin-bottom: 20px;
}
.search-input {
width: 100%;
background: var(--bg-secondary);
border: 2px solid var(--border);
color: var(--text-primary);
border-radius: 12px;
padding: 12px 20px 12px 45px;
font-size: 0.9rem;
transition: all 0.3s ease;
}
.search-input:focus {
outline: none;
border-color: var(--primary);
background: var(--bg-hover);
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.2);
}
.search-icon {
position: absolute;
left: 15px;
top: 50%;
transform: translateY(-50%);
color: var(--text-secondary);
}
.channel-list {
flex: 1;
overflow-y: auto;
padding-right: 10px;
}
.channel-item {
background: var(--bg-secondary);
border-radius: 12px;
padding: 12px;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
transition: all 0.3s ease;
border: 1px solid transparent;
}
.channel-item:hover {
background: var(--bg-hover);
transform: translateX(5px);
border-color: var(--primary);
}
.channel-item.active {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
box-shadow: 0 5px 15px rgba(99, 102, 241, 0.4);
}
.channel-info {
flex: 1;
min-width: 0;
}
.channel-name {
font-weight: 500;
font-size: 0.95rem;
margin-bottom: 3px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.channel-meta {
font-size: 0.8rem;
color: var(--text-secondary);
}
.channel-item.active .channel-meta {
color: rgba(255, 255, 255, 0.8);
}
.play-indicator {
width: 36px;
height: 36px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.channel-item:hover .play-indicator {
background: var(--primary);
transform: scale(1.1);
}
.channel-item.active .play-indicator {
background: rgba(255, 255, 255, 0.2);
}
.hd-badge {
background: var(--accent);
color: white;
font-size: 0.65rem;
padding: 2px 6px;
border-radius: 4px;
margin-right: 6px;
font-weight: 500;
}
/* Stats */
.stats-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid var(--border);
}
.stat-card {
text-align: center;
padding: 12px;
background: var(--bg-secondary);
border-radius: 12px;
transition: all 0.3s ease;
}
.stat-card:hover {
transform: translateY(-2px);
background: var(--bg-hover);
}
.stat-value {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary);
margin-bottom: 3px;
}
.stat-label {
font-size: 0.8rem;
color: var(--text-secondary);
}
/* Retro TV - Full Size */
.tv-section {
position: relative;
display: flex;
justify-content: center;
align-items: center;
min-height: calc(100vh - 200px);
}
.retro-tv-setup {
position: relative;
width: 100%;
max-width: 900px;
}
/* TV Antenna */
.tv-antenna {
position: absolute;
top: -80px;
left: 50%;
transform: translateX(-50%);
z-index: 10;
}
.antenna-base {
width: 60px;
height: 20px;
background: var(--tv-metal);
border-radius: 10px;
margin: 0 auto;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
}
.antenna-rod {
position: absolute;
width: 4px;
background: var(--tv-metal);
transform-origin: bottom;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
}
.antenna-rod.left {
height: 60px;
left: 15px;
bottom: 20px;
transform: rotate(-20deg);
}
.antenna-rod.right {
height: 60px;
right: 15px;
bottom: 20px;
transform: rotate(20deg);
}
.antenna-tip {
position: absolute;
width: 8px;
height: 8px;
background: var(--tv-metal);
border-radius: 50%;
top: -4px;
left: 50%;
transform: translateX(-50%);
}
/* TV Main Body */
.tv-main {
background: linear-gradient(145deg, var(--tv-beige), #d4c4a1);
border-radius: 40px;
padding: 50px 40px 30px;
position: relative;
box-shadow:
0 30px 60px rgba(0, 0, 0, 0.6),
inset 0 2px 5px rgba(255, 255, 255, 0.3);
}
/* TV Brand */
.tv-brand {
position: absolute;
top: 15px;
left: 50%;
transform: translateX(-50%);
font-family: 'Orbitron', monospace;
font-size: 1.8rem;
font-weight: 900;
color: var(--tv-dark);
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
letter-spacing: 3px;
}
/* TV Screen Frame */
.tv-screen-frame {
background: var(--tv-dark);
border-radius: 30px;
padding: 25px;
position: relative;
box-shadow:
inset 0 8px 20px rgba(0, 0, 0, 0.8),
0 0 30px rgba(0, 0, 0, 0.5);
}
/* TV Screen */
.tv-screen {
position: relative;
background: #000;
border-radius: 20px;
overflow: hidden;
aspect-ratio: 4/3;
box-shadow: inset 0 0 80px rgba(0, 0, 0, 0.9);
}
/* CRT Effects */
.tv-screen::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(
rgba(18, 16, 16, 0) 50%,
rgba(0, 0, 0, 0.25) 50%
);
background-size: 100% 4px;
pointer-events: none;
z-index: 2;
}
.tv-screen::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(
ellipse at center,
rgba(255, 255, 255, 0.1) 0%,
rgba(255, 255, 255, 0) 60%
);
pointer-events: none;
z-index: 3;
}
.crt-curve {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 20px;
box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.9);
pointer-events: none;
z-index: 1;
}
#videoPlayer {
width: 100%;
height: 100%;
object-fit: contain;
position: relative;
z-index: 0;
}
.tv-placeholder {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: radial-gradient(circle at center, #1a1a1a 0%, #000 100%);
z-index: 0;
}
.static-noise {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0IiBoZWlnaHQ9IjQiPgo8cmVjdCB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBmaWxsPSIjMDAwIj48L3JlY3Q+CjxyZWN0IHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiMxMTEiPjwvcmVjdD4KPHJlY3QgeD0iMiIgeT0iMiIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0iIzExMSI+PC9yZWN0Pgo8L3N2Zz4=');
opacity: 0.05;
animation: static 0.2s infinite;
pointer-events: none;
}
@keyframes static {
0% { transform: translate(0, 0); }
10% { transform: translate(-1px, -1px); }
20% { transform: translate(1px, -1px); }
30% { transform: translate(-1px, 1px); }
40% { transform: translate(1px, 1px); }
50% { transform: translate(-1px, -1px); }
60% { transform: translate(1px, -1px); }
70% { transform: translate(-1px, 1px); }
80% { transform: translate(1px, 1px); }
90% { transform: translate(-1px, -1px); }
100% { transform: translate(0, 0); }
}
.no-channel-text {
font-family: 'Orbitron', monospace;
color: #444;
font-size: 2rem;
text-transform: uppercase;
letter-spacing: 4px;
animation: flicker 2s infinite;
text-shadow: 0 0 10px rgba(255, 255, 255, 0.1);
}
@keyframes flicker {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
/* TV Controls Panel */
.tv-controls-panel {
margin-top: 30px;
background: rgba(0, 0, 0, 0.3);
border-radius: 20px;
padding: 20px;
backdrop-filter: blur(5px);
}
.tv-controls {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.control-buttons {
display: flex;
gap: 15px;
}
.tv-button {
width: 50px;
height: 50px;
background: radial-gradient(circle at 30% 30%, #666, #333);
border: none;
border-radius: 50%;
color: #fff;
font-size: 1.2rem;
cursor: pointer;
transition: all 0.2s;
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.4),
inset 0 -2px 4px rgba(0, 0, 0, 0.5);
position: relative;
}
.tv-button:hover {
transform: translateY(-2px);
box-shadow:
0 5px 10px rgba(0, 0, 0, 0.5),
inset 0 -2px 4px rgba(0, 0, 0, 0.5);
}
.tv-button:active {
transform: translateY(0);
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.4),
inset 0 -1px 2px rgba(0, 0, 0, 0.5);
}
.tv-button.power {
background: radial-gradient(circle at 30% 30%, #d32f2f, #8b0000);
}
.tv-button.power.active {
background: radial-gradient(circle at 30% 30%, #4caf50, #2e7d32);
}
/* TV Info Display */
.tv-info {
background: rgba(0, 0, 0, 0.8);
border-radius: 15px;
padding: 20px;
font-family: 'Orbitron', monospace;
border: 1px solid #333;
}
.info-row {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
font-size: 1rem;
}
.info-row:last-child {
margin-bottom: 0;
}
.info-label {
color: #666;
font-weight: 500;
}
.info-value {
color: #0ff;
text-shadow: 0 0 8px #0ff;
font-weight: 700;
}
/* Volume Control */
.volume-control {
display: flex;
align-items: center;
gap: 15px;
margin-top: 15px;
}
.volume-slider {
flex: 1;
height: 8px;
background: #333;
border-radius: 4px;
outline: none;
-webkit-appearance: none;
}
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
background: #0ff;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 0 15px #0ff;
}
.volume-slider::-moz-range-thumb {
width: 20px;
height: 20px;
background: #0ff;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 0 15px #0ff;
border: none;
}
/* TV Stand */
.tv-stand {
width: 200px;
height: 40px;
background: linear-gradient(145deg, var(--tv-brown), #6b5637);
margin: 20px auto 0;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);
}
.tv-base {
width: 300px;
height: 20px;
background: linear-gradient(145deg, var(--tv-brown), #6b5637);
margin: 0 auto;
border-radius: 10px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);
}
/* Remote Control */
.tv-remote {
position: absolute;
right: -120px;
top: 50%;
transform: translateY(-50%);
background: linear-gradient(145deg, #333, #111);
border-radius: 15px;
padding: 20px 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}
.remote-brand {
text-align: center;
font-family: 'Orbitron', monospace;
font-size: 0.8rem;
color: #666;
margin-bottom: 15px;
}
.remote-buttons {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.remote-btn {
width: 30px;
height: 30px;
background: #222;
border: 1px solid #444;
border-radius: 5px;
color: #888;
font-size: 0.8rem;
cursor: pointer;
transition: all 0.2s;
}
.remote-btn:hover {
background: #333;
color: #fff;
transform: scale(1.1);
}
.remote-btn.power {
grid-column: span 3;
background: #d32f2f;
color: #fff;
font-weight: bold;
}
.remote-btn.power:hover {
background: #b71c1c;
}
/* Notification */
.notification {
position: fixed;
top: 20px;
right: 20px;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 16px 20px;
display: flex;
align-items: center;
gap: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
transform: translateX(400px);
transition: transform 0.3s ease;
z-index: 1000;
}
.notification.show {
transform: translateX(0);
}
.notification.success {
border-color: var(--success);
}
.notification.error {
border-color: var(--error);
}
.notification-icon {
font-size: 20px;
}
.notification.success .notification-icon {
color: var(--success);
}
.notification.error .notification-icon {
color: var(--error);
}
/* Scrollbar */
.channel-list::-webkit-scrollbar {
width: 6px;
}
.channel-list::-webkit-scrollbar-track {
background: var(--bg-secondary);
border-radius: 3px;
}
.channel-list::-webkit-scrollbar-thumb {
background: var(--primary);
border-radius: 3px;
}
.channel-list::-webkit-scrollbar-thumb:hover {
background: var(--secondary);
}
/* Loading Spinner */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
}
.spinner {
width: 50px;
height: 50px;
border: 3px solid var(--bg-secondary);
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-text {
margin-top: 20px;
color: var(--text-secondary);
}
/* Responsive */
@media (max-width: 1200px) {
.content-layout {
grid-template-columns: 350px 1fr;
}
.tv-remote {
display: none;
}
}
@media (max-width: 992px) {
.content-layout {
grid-template-columns: 1fr;
}
.channel-panel {
height: auto;
position: relative;
top: 0;
}
.tv-main {
padding: 40px 30px 20px;
}
}
@media (max-width: 576px) {
.hero-title {
font-size: 2rem;
}
.tv-main {
padding: 30px 20px 15px;
}
.tv-screen-frame {
padding: 15px;
}
.stats-container {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<!-- Hero Section -->
<section class="hero-section">
<div class="container">
<div class="hero-content">
<h1 class="hero-title">M3U/M3U8 Retro TV Oynatıcı</h1>
<p class="hero-subtitle">Playlist'lerinizi yükleyin ve nostaljik TV deneyimi yaşayın</p>
</div>
</div>
</section>
<!-- Main Container -->
<div class="main-container">
<!-- Upload Section -->
<div class="upload-container">
<div class="upload-zone" id="uploadArea">
<i class="bi bi-cloud-arrow-up upload-icon"></i>
<h3 class="mb-2">Playlist Yükle</h3>
<p class="text-secondary mb-3">M3U veya M3U8 dosyasını buraya sürükleyin</p>
<input type="file" id="fileInput" accept=".m3u,.m3u8" style="display: none;">
<button class="btn btn-light" onclick="document.getElementById('fileInput').click()">
<i class="bi bi-folder2-open me-2"></i> Dosya Seç
</button>
</div>
<div class="url-input-group">
<input type="url" class="url-input" id="urlInput" placeholder="http://example.com/playlist.m3u8">
<button class="load-btn" onclick="loadFromUrl()">
<i class="bi bi-download"></i> Yükle
</button>
</div>
</div>
<!-- Content Layout -->
<div class="content-layout">
<!-- Channel List -->
<div class="channel-panel">
<div class="panel-header">
<h2 class="panel-title">
<i class="bi bi-list-ul"></i> Kanallar
</h2>
</div>
<div class="search-container">
<i class="bi bi-search search-icon"></i>
<input type="text" class="search-input" id="searchInput" placeholder="Kanal ara...">
</div>
<div id="channelList" class="channel-list">
<div class="loading-container">
<div class="spinner"></div>
<p class="loading-text">Kanallar yükleniyor...</p>
</div>
</div>
<div class="stats-container" id="stats" style="display: none;">
<div class="stat-card">
<div class="stat-value" id="totalChannels">0</div>
<div class="stat-label">Toplam</div>
</div>
<div class="stat-card">
<div class="stat-value" id="hdChannels">0</div>
<div class="stat-label">HD</div>
</div>
<div class="stat-card">
<div class="stat-value" id="groupCount">0</div>
<div class="stat-label">Grup</div>
</div>
</div>
</div>
<!-- Retro TV Section -->
<div class="tv-section">
<div class="retro-tv-setup">
<!-- TV Antenna -->
<div class="tv-antenna">
<div class="antenna-base"></div>
<div class="antenna-rod left">
<div class="antenna-tip"></div>
</div>
<div class="antenna-rod right">
<div class="antenna-tip"></div>
</div>
</div>
<!-- TV Main Body -->
<div class="tv-main">
<div class="tv-brand">VINTAGE</div>
<!-- TV Screen -->
<div class="tv-screen-frame">
<div class="tv-screen">
<div class="crt-curve"></div>
<div id="playerPlaceholder" class="tv-placeholder">
<div class="static-noise"></div>
<div class="no-channel-text">No Signal</div>
</div>
<video id="videoPlayer" style="display: none;" controls></video>
</div>
</div>
<!-- TV Controls -->
<div class="tv-controls-panel">
<div class="tv-controls">
<div class="control-buttons">
<button class="tv-button power" id="powerBtn" onclick="togglePower()">
<i class="bi bi-power"></i>
</button>
<button class="tv-button" onclick="previousChannel()">
<i class="bi bi-chevron-left"></i>
</button>
<button class="tv-button" onclick="nextChannel()">
<i class="bi bi-chevron-right"></i>
</button>
</div>
</div>
<div id="tvInfo" class="tv-info" style="display: none;">
<div class="info-row">
<span class="info-label">KANAL:</span>
<span class="info-value" id="currentChannelName">---</span>
</div>
<div class="info-row">
<span class="info-label">GRUP:</span>
<span class="info-value" id="currentChannelGroup">---</span>
</div>
<div class="volume-control">
<span class="info-label">SES:</span>
<input type="range" class="volume-slider" id="volumeSlider" min="0" max="100" value="50">
<span class="info-value" id="volumeValue">50</span>
</div>
</div>
</div>
</div>
<!-- TV Stand -->
<div class="tv-stand"></div>
<div class="tv-base"></div>
<!-- Remote Control -->
<div class="tv-remote">
<div class="remote-brand">REMOTE</div>
<div class="remote-buttons">
<button class="remote-btn power" onclick="togglePower()">PWR</button>
<button class="remote-btn" onclick="previousChannel()">◀</button>
<button class="remote-btn" onclick="nextChannel()">▶</button>
<button class="remote-btn" onclick="volumeUp()">▲</button>
<button class="remote-btn" onclick="volumeDown()">▼</button>
<button class="remote-btn">M</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Notification Template -->
<div id="notification" class="notification">
<i class="notification-icon"></i>
<span class="notification-text"></span>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
let channels = [];
let currentChannelIndex = -1;
let hls = null;
let isTvOn = false;
// DOM Elements
const fileInput = document.getElementById('fileInput');
const uploadArea = document.getElementById('uploadArea');
const channelList = document.getElementById('channelList');
const searchInput = document.getElementById('searchInput');
const videoPlayer = document.getElementById('videoPlayer');
const playerPlaceholder = document.getElementById('playerPlaceholder');
const tvInfo = document.getElementById('tvInfo');
const currentChannelName = document.getElementById('currentChannelName');
const currentChannelGroup = document.getElementById('currentChannelGroup');
const powerBtn = document.getElementById('powerBtn');
const volumeSlider = document.getElementById('volumeSlider');
const volumeValue = document.getElementById('volumeValue');
// Drag and Drop
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
handleFile(files[0]);
}
});
fileInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
handleFile(e.target.files[0]);
}
});
// File Handler
function handleFile(file) {
const reader = new FileReader();
reader.onload = (e) => {
parseM3U(e.target.result);
};
reader.readAsText(file);
}
// Load from URL
function loadFromUrl() {
const url = document.getElementById('urlInput').value;
if (!url) {
showNotification('Lütfen bir URL girin', 'error');
return;
}
showNotification('Playlist yükleniyor...', 'info');
fetch(url)
.then(response => response.text())
.then(data => {
parseM3U(data);
showNotification('Playlist başarıyla yüklendi', 'success');
})
.catch(error => {
showNotification('URL yüklenemedi: ' + error, 'error');
});
}
// Parse M3U
function parseM3U(content) {
channels = [];
const lines = content.split('\n');
let currentChannel = null;
let groups = new Set();
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('#EXTINF:')) {
currentChannel = {
name: '',
url: '',
duration: 0,
group: '',
logo: '',
isHD: false
};
// Parse metadata
const nameMatch = line.match(/tvg-name="([^"]*)"/);
if (nameMatch) currentChannel.name = nameMatch[1];
const groupMatch = line.match(/group-title="([^"]*)"/);
if (groupMatch) {
currentChannel.group = groupMatch[1];
groups.add(groupMatch[1]);
}
const logoMatch = line.match(/tvg-logo="([^"]*)"/);
if (logoMatch) currentChannel.logo = logoMatch[1];
if (line.includes('HD') || line.includes('FHD') || line.includes('4K')) {
currentChannel.isHD = true;
}
// Get name from line if not found
if (!currentChannel.name) {
const nameMatch = line.match(/,(.+)$/);
if (nameMatch) currentChannel.name = nameMatch[1];
}
} else if (line && !line.startsWith('#') && currentChannel) {
currentChannel.url = line;
channels.push(currentChannel);
currentChannel = null;
}
}
if (channels.length > 0) {
displayChannels();
updateStats(groups.size);
showNotification(`${channels.length} kanal yüklendi`, 'success');
} else {
showNotification('Geçerli kanal bulunamadı', 'error');
channelList.innerHTML = '<div class="text-center p-4"><p class="text-secondary">Kanal bulunamadı</p></div>';
}
}
// Display Channels
function displayChannels(filteredChannels = null) {
const displayChannels = filteredChannels || channels;
if (displayChannels.length === 0) {
channelList.innerHTML = '<div class="text-center p-4"><p class="text-secondary">Kanal bulunamadı</p></div>';
return;
}
let html = '';
displayChannels.forEach((channel, index) => {
const originalIndex = channels.indexOf(channel);
html += `
<div class="channel-item ${originalIndex === currentChannelIndex ? 'active' : ''}" data-index="${originalIndex}" onclick="selectChannel(${originalIndex})">
<div class="channel-info">
<div class="channel-name">
${channel.isHD ? '<span class="hd-badge">HD</span>' : ''}
${channel.name || 'İsimsiz Kanal'}
</div>
<div class="channel-meta">
${channel.group || 'Grupsuz'}
</div>
</div>
<div class="play-indicator">
<i class="bi bi-play-fill"></i>
</div>
</div>
`;
});
channelList.innerHTML = html;
document.getElementById('stats').style.display = 'grid';
}
// Update Stats
function updateStats(groupCount) {
document.getElementById('totalChannels').textContent = channels.length;
document.getElementById('hdChannels').textContent = channels.filter(c => c.isHD).length;
document.getElementById('groupCount').textContent = groupCount;
}
// Search
searchInput.addEventListener('input', (e) => {
const searchTerm = e.target.value.toLowerCase();
const filtered = channels.filter(channel =>
channel.name.toLowerCase().includes(searchTerm) ||
(channel.group && channel.group.toLowerCase().includes(searchTerm))
);
displayChannels(filtered);
});
// TV Power Toggle
function togglePower() {
isTvOn = !isTvOn;
powerBtn.classList.toggle('active', isTvOn);
if (isTvOn && channels.length > 0 && currentChannelIndex === -1) {
selectChannel(0);
} else if (!isTvOn) {
stopPlayback();
}
}
// Select Channel
function selectChannel(index) {
if (!isTvOn) {
togglePower();
}
currentChannelIndex = index;
const channel = channels[index];
// Update active state
document.querySelectorAll('.channel-item').forEach(item => {
item.classList.remove('active');
});
document.querySelector(`[data-index="${index}"]`).classList.add('active');
// Show video player
playerPlaceholder.style.display = 'none';
videoPlayer.style.display = 'block';
tvInfo.style.display = 'block';
// Update channel info
currentChannelName.textContent = channel.name || 'İsimsiz Kanal';
currentChannelGroup.textContent = channel.group || 'Grupsuz';
// Handle HLS
if (channel.url.includes('.m3u8')) {
if (Hls.isSupported()) {
if (hls) hls.destroy();
hls = new Hls();
hls.loadSource(channel.url);
hls.attachMedia(videoPlayer);
hls.on(Hls.Events.MANIFEST_PARSED, function() {
videoPlayer.play();
});
} else if (videoPlayer.canPlayType('application/vnd.apple.mpegurl')) {
videoPlayer.src = channel.url;
videoPlayer.play();
}
} else {
videoPlayer.src = channel.url;
videoPlayer.play();
}
}
// Previous Channel
function previousChannel() {
if (!isTvOn || channels.length === 0) return;
currentChannelIndex = currentChannelIndex <= 0 ? channels.length - 1 : currentChannelIndex - 1;
selectChannel(currentChannelIndex);
}
// Next Channel
function nextChannel() {
if (!isTvOn || channels.length === 0) return;
currentChannelIndex = currentChannelIndex >= channels.length - 1 ? 0 : currentChannelIndex + 1;
selectChannel(currentChannelIndex);
}
// Volume Controls
function volumeUp() {
const currentVolume = parseInt(volumeSlider.value);
const newVolume = Math.min(100, currentVolume + 5);
volumeSlider.value = newVolume;
volumeSlider.dispatchEvent(new Event('input'));
}
function volumeDown() {
const currentVolume = parseInt(volumeSlider.value);
const newVolume = Math.max(0, currentVolume - 5);
volumeSlider.value = newVolume;
volumeSlider.dispatchEvent(new Event('input'));
}
// Stop Playback
function stopPlayback() {
if (hls) {
hls.destroy();
hls = null;
}
videoPlayer.pause();
videoPlayer.src = '';
playerPlaceholder.style.display = 'flex';
videoPlayer.style.display = 'none';
tvInfo.style.display = 'none';
currentChannelIndex = -1;
// Remove active state
document.querySelectorAll('.channel-item').forEach(item => {
item.classList.remove('active');
});
}
// Volume Control
volumeSlider.addEventListener('input', (e) => {
const volume = e.target.value;
volumeValue.textContent = volume;
videoPlayer.volume = volume / 100;
});
// Keyboard Controls
document.addEventListener('keydown', (e) => {
if (!isTvOn) return;
switch(e.key) {
case 'ArrowLeft':
previousChannel();
break;
case 'ArrowRight':
nextChannel();
break;
case 'ArrowUp':
volumeUp();
break;
case 'ArrowDown':
volumeDown();
break;
case ' ':
e.preventDefault();
if (videoPlayer.paused) {
videoPlayer.play();
} else {
videoPlayer.pause();
}
break;
}
});
// Show Notification
function showNotification(message, type = 'info') {
const notification = document.getElementById('notification');
const icon = notification.querySelector('.notification-icon');
const text = notification.querySelector('.notification-text');
// Set icon and class
notification.className = 'notification';
if (type === 'success') {
notification.classList.add('success');
icon.className = 'notification-icon bi bi-check-circle-fill';
} else if (type === 'error') {
notification.classList.add('error');
icon.className = 'notification-icon bi bi-exclamation-circle-fill';
} else {
icon.className = 'notification-icon bi bi-info-circle-fill';
}
text.textContent = message;
// Show notification
notification.classList.add('show');
// Hide after 3 seconds
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}
</script>
</body>
</html>

Selamlar masüstünde reklamsız tv izleyebilirsiniz üstelik kumandalı bir tv m3u dosyanızı veya urlsi ekleyin kanal listesine kanallar gelecektir tam ekran yapabilirsiniz kumanda ile tv kapatıp açabilirsiniz ses açıp kanal değiştirebilirsiniz tv deki tuşlarlada işlemleri yapabilirsiniz kodlar .html olarak masaüstüne kaydedip öyle açın iyi forumlar