Initial commit: StreamFlow IPTV platform

This commit is contained in:
aiulian25 2025-12-17 00:42:43 +00:00
commit 73a8ae9ffd
1240 changed files with 278451 additions and 0 deletions

View file

@ -0,0 +1,10 @@
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#a855f7;stop-opacity:1" />
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="32" height="32" rx="7" fill="url(#grad1)"/>
<path d="M 12 9 L 12 23 L 23 16 Z" fill="#ffffff" opacity="0.95"/>
</svg>

After

Width:  |  Height:  |  Size: 468 B

View file

@ -0,0 +1,292 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>StreamFlow Icons Preview</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 40px 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
h1 {
color: white;
text-align: center;
margin-bottom: 10px;
font-size: 2.5rem;
}
.subtitle {
color: rgba(255, 255, 255, 0.9);
text-align: center;
margin-bottom: 40px;
font-size: 1.1rem;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 30px;
margin-bottom: 60px;
}
.icon-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 30px;
text-align: center;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.icon-card:hover {
transform: translateY(-5px);
box-shadow: 0 12px 48px rgba(0, 0, 0, 0.15);
}
.icon-card img {
width: 100%;
max-width: 150px;
height: auto;
margin-bottom: 15px;
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.1));
}
.icon-size {
font-weight: 700;
color: #a855f7;
font-size: 1.1rem;
margin-bottom: 5px;
}
.icon-type {
color: #666;
font-size: 0.875rem;
}
.color-palette {
background: white;
border-radius: 20px;
padding: 40px;
margin-bottom: 40px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.color-palette h2 {
color: #333;
margin-bottom: 25px;
font-size: 1.8rem;
}
.colors {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.color-swatch {
flex: 1;
min-width: 150px;
text-align: center;
}
.color-box {
height: 100px;
border-radius: 12px;
margin-bottom: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.color-name {
font-weight: 600;
color: #333;
margin-bottom: 5px;
}
.color-hex {
font-family: 'Courier New', monospace;
color: #666;
font-size: 0.875rem;
}
.usage-section {
background: white;
border-radius: 20px;
padding: 40px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.usage-section h2 {
color: #333;
margin-bottom: 20px;
font-size: 1.8rem;
}
.usage-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.usage-card {
border: 2px solid #e5e7eb;
border-radius: 12px;
padding: 20px;
display: flex;
align-items: center;
gap: 15px;
}
.usage-card img {
width: 48px;
height: 48px;
}
.usage-info h3 {
color: #333;
font-size: 1rem;
margin-bottom: 5px;
}
.usage-info p {
color: #666;
font-size: 0.875rem;
}
.gradient-demo {
background: linear-gradient(135deg, #a855f7 0%, #3b82f6 100%);
height: 120px;
border-radius: 12px;
margin: 20px 0;
box-shadow: 0 8px 24px rgba(168, 85, 247, 0.4);
}
</style>
</head>
<body>
<div class="container">
<h1>🎬 StreamFlow Logo & Icons</h1>
<p class="subtitle">Professional IPTV Application Branding</p>
<!-- Color Palette -->
<div class="color-palette">
<h2>🎨 Brand Colors</h2>
<div class="colors">
<div class="color-swatch">
<div class="color-box" style="background: #a855f7;"></div>
<div class="color-name">Purple Primary</div>
<div class="color-hex">#a855f7</div>
</div>
<div class="color-swatch">
<div class="color-box" style="background: #3b82f6;"></div>
<div class="color-name">Blue Secondary</div>
<div class="color-hex">#3b82f6</div>
</div>
<div class="color-swatch">
<div class="color-box" style="background: #c084fc;"></div>
<div class="color-name">Purple Light</div>
<div class="color-hex">#c084fc</div>
</div>
<div class="color-swatch">
<div class="color-box" style="background: #9333ea;"></div>
<div class="color-name">Purple Dark</div>
<div class="color-hex">#9333ea</div>
</div>
</div>
<div class="gradient-demo"></div>
<p style="text-align: center; color: #666; margin-top: 10px;">
<strong>Gradient:</strong> linear-gradient(135deg, #a855f7 0%, #3b82f6 100%)
</p>
</div>
<!-- Icon Sizes -->
<h2 style="color: white; text-align: center; margin-bottom: 30px; font-size: 2rem;">📱 PWA Icons (Rounded)</h2>
<div class="grid">
<div class="icon-card">
<img src="icons/icon-72x72.svg" alt="72x72">
<div class="icon-size">72×72</div>
<div class="icon-type">Mobile Small</div>
</div>
<div class="icon-card">
<img src="icons/icon-96x96.svg" alt="96x96">
<div class="icon-size">96×96</div>
<div class="icon-type">Mobile Medium</div>
</div>
<div class="icon-card">
<img src="icons/icon-128x128.svg" alt="128x128">
<div class="icon-size">128×128</div>
<div class="icon-type">Tablet</div>
</div>
<div class="icon-card">
<img src="icons/icon-144x144.svg" alt="144x144">
<div class="icon-size">144×144</div>
<div class="icon-type">Mobile High-Res</div>
</div>
<div class="icon-card">
<img src="icons/icon-152x152.svg" alt="152x152">
<div class="icon-size">152×152</div>
<div class="icon-type">iPad Touch</div>
</div>
<div class="icon-card">
<img src="icons/icon-192x192.svg" alt="192x192">
<div class="icon-size">192×192</div>
<div class="icon-type">Standard PWA</div>
</div>
<div class="icon-card">
<img src="icons/icon-384x384.svg" alt="384x384">
<div class="icon-size">384×384</div>
<div class="icon-type">High-DPI</div>
</div>
<div class="icon-card">
<img src="icons/icon-512x512.svg" alt="512x512">
<div class="icon-size">512×512</div>
<div class="icon-type">Desktop / Splash</div>
</div>
</div>
<!-- Usage Examples -->
<div class="usage-section">
<h2>🚀 Platform Usage</h2>
<div class="usage-grid">
<div class="usage-card">
<img src="favicon.svg" alt="Favicon">
<div class="usage-info">
<h3>Browser Tab</h3>
<p>favicon.svg (32×32)</p>
</div>
</div>
<div class="usage-card">
<img src="icons/icon-192x192.svg" alt="Android">
<div class="usage-info">
<h3>Android Home Screen</h3>
<p>192×192 maskable icon</p>
</div>
</div>
<div class="usage-card">
<img src="icons/icon-192x192.svg" alt="iOS">
<div class="usage-info">
<h3>iOS Home Screen</h3>
<p>Apple touch icon</p>
</div>
</div>
<div class="usage-card">
<img src="icons/icon-512x512.svg" alt="Desktop">
<div class="usage-info">
<h3>Desktop PWA</h3>
<p>512×512 for shortcuts</p>
</div>
</div>
<div class="usage-card">
<img src="icons/icon-512x512.svg" alt="Linux">
<div class="usage-info">
<h3>Linux AppImage</h3>
<p>512×512 app icon</p>
</div>
</div>
<div class="usage-card">
<img src="icons/icon-192x192.svg" alt="APK">
<div class="usage-info">
<h3>Android APK</h3>
<p>Adaptive icon resource</p>
</div>
</div>
</div>
<div style="margin-top: 30px; padding: 20px; background: #f9fafb; border-radius: 12px; border-left: 4px solid #a855f7;">
<h3 style="color: #333; margin-bottom: 10px;">✅ All icons feature:</h3>
<ul style="color: #666; line-height: 1.8; margin-left: 20px;">
<li><strong>Rounded corners</strong> for modern aesthetics (~22% radius)</li>
<li><strong>Purple-blue gradient</strong> matching brand identity</li>
<li><strong>SVG format</strong> for crisp rendering at any scale</li>
<li><strong>Maskable safe zone</strong> for adaptive launcher icons</li>
<li><strong>Flow lines</strong> suggesting streaming motion</li>
</ul>
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,12 @@
<svg width="128" height="128" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#a855f7;stop-opacity:1" />
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="128" height="128" rx="28" fill="url(#grad1)"/>
<path d="M 48 35 L 48 93 L 93 64 Z" fill="#ffffff" opacity="0.95"/>
<path d="M 64 64 C 64 64, 54 56, 48 50 C 42 44, 38 38, 38 36 L 96 64 C 96 64, 94 68, 88 74 C 82 80, 74 88, 66 93"
fill="none" stroke="#ffffff" stroke-width="3" opacity="0.4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 686 B

View file

@ -0,0 +1,12 @@
<svg width="144" height="144" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#a855f7;stop-opacity:1" />
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="144" height="144" rx="32" fill="url(#grad1)"/>
<path d="M 54 40 L 54 104 L 104 72 Z" fill="#ffffff" opacity="0.95"/>
<path d="M 72 72 C 72 72, 60 62, 54 56 C 48 50, 42 42, 42 40 L 108 72 C 108 72, 106 76, 99 83 C 92 90, 83 99, 74 104"
fill="none" stroke="#ffffff" stroke-width="3.5" opacity="0.4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 694 B

View file

@ -0,0 +1,12 @@
<svg width="152" height="152" viewBox="0 0 152 152" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#a855f7;stop-opacity:1" />
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="152" height="152" rx="34" fill="url(#grad1)"/>
<path d="M 57 42 L 57 110 L 110 76 Z" fill="#ffffff" opacity="0.95"/>
<path d="M 76 76 C 76 76, 64 66, 57 59 C 50 52, 44 44, 44 42 L 114 76 C 114 76, 112 80, 105 87 C 98 94, 88 104, 78 110"
fill="none" stroke="#ffffff" stroke-width="3.5" opacity="0.4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 696 B

View file

@ -0,0 +1,12 @@
<svg width="192" height="192" viewBox="0 0 192 192" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#a855f7;stop-opacity:1" />
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="192" height="192" rx="42" fill="url(#grad1)"/>
<path d="M 72 53 L 72 139 L 139 96 Z" fill="#ffffff" opacity="0.95"/>
<path d="M 96 96 C 96 96, 80 83, 72 74 C 64 65, 56 55, 56 53 L 144 96 C 144 96, 141 101, 132 110 C 123 119, 111 131, 98 139"
fill="none" stroke="#ffffff" stroke-width="4" opacity="0.4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 699 B

View file

@ -0,0 +1,12 @@
<svg width="384" height="384" viewBox="0 0 384 384" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#a855f7;stop-opacity:1" />
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="384" height="384" rx="84" fill="url(#grad1)"/>
<path d="M 144 106 L 144 278 L 278 192 Z" fill="#ffffff" opacity="0.95"/>
<path d="M 192 192 C 192 192, 160 166, 144 148 C 128 130, 112 110, 112 106 L 288 192 C 288 192, 282 202, 264 220 C 246 238, 222 262, 196 278"
fill="none" stroke="#ffffff" stroke-width="6" opacity="0.4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 720 B

View file

@ -0,0 +1,12 @@
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#a855f7;stop-opacity:1" />
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="512" height="512" rx="112" fill="url(#grad1)"/>
<path d="M 192 142 L 192 370 L 370 256 Z" fill="#ffffff" opacity="0.95"/>
<path d="M 256 256 C 256 256, 214 222, 192 198 C 170 174, 150 146, 150 142 L 384 256 C 384 256, 376 270, 352 294 C 328 318, 296 350, 262 370"
fill="none" stroke="#ffffff" stroke-width="8" opacity="0.4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 721 B

View file

@ -0,0 +1,12 @@
<svg width="72" height="72" viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#a855f7;stop-opacity:1" />
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="72" height="72" rx="16" fill="url(#grad1)"/>
<path d="M 28 20 L 28 52 L 52 36 Z" fill="#ffffff" opacity="0.95"/>
<path d="M 36 36 C 36 36, 30 32, 28 30 C 26 28, 24 26, 24 24 L 54 36 C 54 36, 52 38, 50 40 C 48 42, 44 46, 40 48"
fill="none" stroke="#ffffff" stroke-width="2" opacity="0.4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 680 B

View file

@ -0,0 +1,12 @@
<svg width="96" height="96" viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#a855f7;stop-opacity:1" />
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="96" height="96" rx="21" fill="url(#grad1)"/>
<path d="M 36 26 L 36 70 L 70 48 Z" fill="#ffffff" opacity="0.95"/>
<path d="M 48 48 C 48 48, 40 42, 36 38 C 32 34, 30 30, 30 28 L 72 48 C 72 48, 70 52, 66 56 C 62 60, 56 66, 50 70"
fill="none" stroke="#ffffff" stroke-width="2.5" opacity="0.4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 682 B

View file

@ -0,0 +1,78 @@
{
"name": "StreamFlow IPTV",
"short_name": "StreamFlow",
"description": "Professional IPTV Streaming with Multi-Screen & Picture-in-Picture",
"start_url": "/",
"display": "standalone",
"background_color": "#121212",
"theme_color": "#a855f7",
"orientation": "any",
"icons": [
{
"src": "/icons/icon-72x72.svg",
"sizes": "72x72",
"type": "image/svg+xml",
"purpose": "any maskable"
},
{
"src": "/icons/icon-96x96.svg",
"sizes": "96x96",
"type": "image/svg+xml",
"purpose": "any maskable"
},
{
"src": "/icons/icon-128x128.svg",
"sizes": "128x128",
"type": "image/svg+xml",
"purpose": "any maskable"
},
{
"src": "/icons/icon-144x144.svg",
"sizes": "144x144",
"type": "image/svg+xml",
"purpose": "any maskable"
},
{
"src": "/icons/icon-152x152.svg",
"sizes": "152x152",
"type": "image/svg+xml",
"purpose": "any maskable"
},
{
"src": "/icons/icon-192x192.svg",
"sizes": "192x192",
"type": "image/svg+xml",
"purpose": "any maskable"
},
{
"src": "/icons/icon-384x384.svg",
"sizes": "384x384",
"type": "image/svg+xml",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512x512.svg",
"sizes": "512x512",
"type": "image/svg+xml",
"purpose": "any maskable"
}
],
"categories": ["entertainment", "multimedia", "video"],
"screenshots": [],
"shortcuts": [
{
"name": "Live TV",
"short_name": "Live",
"description": "Watch Live TV channels",
"url": "/live-tv",
"icons": [{ "src": "/icons/icon-96x96.svg", "sizes": "96x96" }]
},
{
"name": "Movies",
"short_name": "Movies",
"description": "Browse movies",
"url": "/movies",
"icons": [{ "src": "/icons/icon-96x96.svg", "sizes": "96x96" }]
}
]
}

View file

@ -0,0 +1,6 @@
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<rect width="200" height="200" fill="#f5f5f5"/>
<text x="50%" y="50%" font-family="Arial" font-size="16" fill="#999" text-anchor="middle" dy=".3em">
Channel Logo
</text>
</svg>

After

Width:  |  Height:  |  Size: 253 B

View file

@ -0,0 +1,6 @@
<svg width="120" height="80" xmlns="http://www.w3.org/2000/svg">
<rect width="120" height="80" fill="#e0e0e0"/>
<text x="50%" y="50%" font-family="Arial" font-size="12" fill="#666" text-anchor="middle" dy=".3em">
Show
</text>
</svg>

After

Width:  |  Height:  |  Size: 243 B

View file

@ -0,0 +1,178 @@
const CACHE_NAME = 'streamflow-v1';
const urlsToCache = [
'/',
'/index.html',
'/manifest.json'
];
// Global error handler for service worker (CWE-391 protection)
self.addEventListener('error', (event) => {
// Log error but don't expose to user
console.error('Service Worker error:', event.error);
event.preventDefault();
});
// Handle unhandled promise rejections in service worker
self.addEventListener('unhandledrejection', (event) => {
// Log error but don't expose to user
console.error('Service Worker unhandled rejection:', event.reason);
event.preventDefault();
});
// Install event - cache static assets
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(urlsToCache))
.then(() => self.skipWaiting())
.catch((error) => {
// Handle cache errors gracefully
console.error('Cache installation error:', error);
// Still skip waiting even if caching fails
return self.skipWaiting();
})
);
});
// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) {
return caches.delete(cacheName);
}
})
);
}).then(() => self.clients.claim())
);
});
// Fetch event - serve from cache, fallback to network
self.addEventListener('fetch', (event) => {
// Skip non-GET requests - let them pass through to network
if (event.request.method !== 'GET') {
event.respondWith(fetch(event.request));
return;
}
// Skip API requests and streaming URLs
if (event.request.url.includes('/api/') ||
event.request.url.includes('m3u8') ||
event.request.url.includes('ts')) {
event.respondWith(fetch(event.request));
return;
}
event.respondWith(
caches.match(event.request)
.then((response) => {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request).then((response) => {
// Check if valid response
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// Clone the response
const responseToCache = response.clone();
caches.open(CACHE_NAME)
.then((cache) => {
cache.put(event.request, responseToCache);
})
.catch((error) => {
// Silently handle cache write errors
console.error('Cache write error:', error);
});
return response;
}).catch((error) => {
// Network error - return generic offline response
console.error('Fetch error:', error);
// Return a basic error response instead of throwing
return new Response('Offline', {
status: 503,
statusText: 'Service Unavailable'
});
});
})
.catch((error) => {
// Handle any cache read errors
console.error('Cache read error:', error);
// Try to fetch from network as fallback
return fetch(event.request).catch(() => {
return new Response('Offline', {
status: 503,
statusText: 'Service Unavailable'
});
});
})
);
});
// Handle background sync
self.addEventListener('sync', (event) => {
if (event.tag === 'sync-playlists') {
event.waitUntil(syncPlaylists());
}
});
// Handle push notifications
self.addEventListener('push', (event) => {
const data = event.data ? event.data.json() : {};
const options = {
body: data.body || 'New notification from StreamFlow',
icon: '/icons/icon-192x192.svg',
badge: '/icons/icon-72x72.svg',
vibrate: [200, 100, 200],
tag: data.tag || 'streamflow-notification',
renotify: true,
data: data.data || {},
actions: data.actions || [
{
action: 'open',
title: 'Open'
}
]
};
event.waitUntil(
self.registration.showNotification(data.title || 'StreamFlow', options)
);
});
// Handle notification clicks
self.addEventListener('notificationclick', (event) => {
event.notification.close();
const urlToOpen = event.notification.data.url || '/';
event.waitUntil(
clients.matchAll({ type: 'window', includeUncontrolled: true })
.then((clientList) => {
// Check if there's already a window open
for (let i = 0; i < clientList.length; i++) {
const client = clientList[i];
if (client.url.includes(urlToOpen) && 'focus' in client) {
return client.focus();
}
}
// If no window is open, open a new one
if (clients.openWindow) {
return clients.openWindow(urlToOpen);
}
})
);
});
// Sync function
async function syncPlaylists() {
// Implementation for syncing playlists
console.log('Syncing playlists...');
}

View file

@ -0,0 +1,8 @@
// Fix for mobile viewport height with address bar
function setVh() {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}
setVh();
window.addEventListener('resize', setVh);
window.addEventListener('orientationchange', setVh);