Загрузить файлы в «templates»
This commit is contained in:
178
templates/Home.html
Normal file
178
templates/Home.html
Normal file
@@ -0,0 +1,178 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>YT Stream Player</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
background: #0f0f0f; color: #eee; font-family: Arial, sans-serif;
|
||||
min-height: 100vh; display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.card {
|
||||
background: #1a1a1a; border-radius: 14px; padding: 40px;
|
||||
width: 100%; max-width: 540px; box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
||||
}
|
||||
.logo { font-size: 36px; text-align: center; margin-bottom: 8px; }
|
||||
h1 { text-align: center; font-size: 22px; margin-bottom: 6px; }
|
||||
.subtitle { text-align: center; color: #888; font-size: 13px; margin-bottom: 28px; }
|
||||
label { font-size: 13px; color: #aaa; display: block; margin-bottom: 6px; }
|
||||
input[type="text"] {
|
||||
width: 100%; background: #111; border: 1px solid #333; border-radius: 8px;
|
||||
color: #fff; font-size: 14px; padding: 12px 14px; outline: none;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
input[type="text"]:focus { border-color: #ff4444; }
|
||||
input[type="text"]::placeholder { color: #555; }
|
||||
.btn {
|
||||
width: 100%; margin-top: 16px; background: #ff4444; color: #fff;
|
||||
border: none; border-radius: 8px; padding: 13px; font-size: 15px;
|
||||
font-weight: bold; cursor: pointer; transition: background 0.2s;
|
||||
}
|
||||
.btn:hover { background: #cc0000; }
|
||||
.btn:disabled { background: #555; cursor: not-allowed; }
|
||||
.error {
|
||||
margin-top: 14px; background: #3a1a1a; border: 1px solid #ff4444;
|
||||
border-radius: 8px; padding: 10px 14px; font-size: 13px;
|
||||
color: #ff8888; display: none;
|
||||
}
|
||||
.loading { display: none; text-align: center; margin-top: 16px; color: #aaa; font-size: 13px; }
|
||||
.spinner {
|
||||
display: inline-block; width: 16px; height: 16px; border: 2px solid #555;
|
||||
border-top-color: #ff4444; border-radius: 50%;
|
||||
animation: spin 0.7s linear infinite; vertical-align: middle; margin-right: 8px;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.examples { margin-top: 22px; border-top: 1px solid #2a2a2a; padding-top: 16px; }
|
||||
.examples p { font-size: 12px; color: #666; margin-bottom: 10px; }
|
||||
.example-links { display: flex; flex-direction: column; gap: 6px; }
|
||||
.example-link {
|
||||
background: #222; border: 1px solid #333; border-radius: 6px;
|
||||
padding: 8px 12px; font-size: 12px; color: #888; cursor: pointer;
|
||||
transition: border-color 0.2s, color 0.2s; text-align: left;
|
||||
}
|
||||
.example-link:hover { border-color: #ff4444; color: #eee; }
|
||||
.supported { margin-top: 16px; text-align: center; font-size: 11px; color: #555; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="logo">▶️</div>
|
||||
<h1>YT Stream Player</h1>
|
||||
<p class="subtitle">Paste any YouTube link to stream or download</p>
|
||||
|
||||
<label for="url-input">YouTube URL</label>
|
||||
<input type="text" id="url-input"
|
||||
placeholder="https://www.youtube.com/watch?v=..." autofocus>
|
||||
|
||||
<button class="btn" id="watch-btn" onclick="goWatch()">▶ Watch Now</button>
|
||||
|
||||
<div class="error" id="error-box"></div>
|
||||
<div class="loading" id="loading">
|
||||
<span class="spinner"></span> Fetching video info, please wait...
|
||||
</div>
|
||||
|
||||
<div class="examples">
|
||||
<p>Supported URL formats:</p>
|
||||
<div class="example-links">
|
||||
<button class="example-link"
|
||||
onclick="fillUrl('https://www.youtube.com/watch?v=jNQXAC9IVRw')">
|
||||
📺 youtube.com/watch?v=...
|
||||
</button>
|
||||
<button class="example-link"
|
||||
onclick="fillUrl('https://youtu.be/jNQXAC9IVRw')">
|
||||
🔗 youtu.be/...
|
||||
</button>
|
||||
<button class="example-link"
|
||||
onclick="fillUrl('https://www.youtube.com/shorts/dQw4w9WgXcQ')">
|
||||
📱 youtube.com/shorts/...
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="supported">Supports streaming and direct download</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const input = document.getElementById('url-input');
|
||||
const btn = document.getElementById('watch-btn');
|
||||
const errBox = document.getElementById('error-box');
|
||||
const loader = document.getElementById('loading');
|
||||
|
||||
input.addEventListener('keydown', e => { if (e.key === 'Enter') goWatch(); });
|
||||
input.addEventListener('input', () => { errBox.style.display = 'none'; });
|
||||
|
||||
function fillUrl(url) { input.value = url; input.focus(); errBox.style.display = 'none'; }
|
||||
|
||||
function isValidYoutubeUrl(url) {
|
||||
return /^https?:\/\/(www\.)?(youtube\.com\/(watch\?v=|shorts\/)|youtu\.be\/)/.test(url);
|
||||
}
|
||||
|
||||
async function goWatch() {
|
||||
const url = input.value.trim();
|
||||
if (!url) { showError('Please paste a YouTube URL first.'); return; }
|
||||
if (!isValidYoutubeUrl(url)) { showError("That doesn't look like a valid YouTube URL."); return; }
|
||||
|
||||
btn.disabled = true;
|
||||
loader.style.display = 'block';
|
||||
errBox.style.display = 'none';
|
||||
|
||||
try {
|
||||
const res = await fetch('/validate', {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({ url })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.ok) {
|
||||
window.location.href = '/watch?url=' + encodeURIComponent(url);
|
||||
} else {
|
||||
showError(data.error || 'Could not load video.');
|
||||
}
|
||||
} catch (e) {
|
||||
showError('Server error. Is Flask running?');
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
loader.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function showError(msg) {
|
||||
errBox.textContent = '⚠ ' + msg;
|
||||
errBox.style.display = 'block';
|
||||
}
|
||||
async function goWatch() {
|
||||
const url = input.value.trim();
|
||||
if (!url) { showError('Please paste a YouTube URL.'); return; }
|
||||
|
||||
btn.disabled = true;
|
||||
loader.style.display = 'block';
|
||||
errBox.style.display = 'none';
|
||||
|
||||
try {
|
||||
const res = await fetch(`/info?url=${encodeURIComponent(url)}`);
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
showError(data.detail || 'Could not load URL.');
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ Route to playlist or video player based on type
|
||||
if (data.type === 'playlist') {
|
||||
window.location.href =
|
||||
`/playlist/watch?url=${encodeURIComponent(url)}`;
|
||||
} else {
|
||||
window.location.href =
|
||||
`/watch?url=${encodeURIComponent(url)}`;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
showError('Server error.');
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
loader.style.display = 'none';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user