poke/html/download.ejs
2025-08-20 02:20:29 +02:00

350 lines
19 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
This Source Code Form is subject to the terms of the GNU General Public License:
Copyright (C) 2021-2025 POKETUBE (https://codeberg.org/Ashley/poketube)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see https://www.gnu.org/licenses/.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Poke | Download Video</title>
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no,viewport-fit=cover" />
<meta name="theme-color" content="<%= color %>"/>
<meta property="og:type" content="website" />
<meta property="og:title" content="PokeTube - Video Downloader" />
<meta property="og:image" content="https://i.ytimg.com/vi/<%= v %>/maxresdefault.jpg" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@PoketaleBot" />
<meta name="twitter:creator" content="@PoketaleBot" />
<meta name="twitter:description" content="You wouldn't download a car... Right? welp i would — Download this video on PokeTube for 0$!" />
<link rel="icon" href="/css/yt-ukraine.svg?v=7" />
<link rel="manifest" href="/manifest.json" />
<link rel="preconnect" href="https://fonts.poketube.fun" crossorigin />
<link href="https://fonts.poketube.fun/css/fonts.css" rel="stylesheet" />
<link href="https://p.poketube.fun/https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css" rel="stylesheet" />
<style>
:root{
--bg:#0f0f10;--bg-elev:#17171a;--bg-chip:#1e1e22;--text:#e8e8ee;--muted:#a8a8b3;--link:#3ea6ff;
--ring:<%= color %>;--radius:14px;--pad:16px;--gap:12px;
--shadow:0 10px 30px rgba(0,0,0,.25),0 1px 0 rgba(255,255,255,.04) inset
}
@media (prefers-color-scheme: light){
:root{--bg:#f7f7fb;--bg-elev:#fff;--bg-chip:#f1f2f6;--text:#151518;--muted:#5a5a66;--shadow:0 12px 30px rgba(0,0,0,.08),0 1px 0 rgba(0,0,0,.04) inset}
}
html,body{height:100%}
body{
margin:0;
background:
radial-gradient(1200px 600px at 10% -10%, rgba(255,255,255,.06), transparent 50%),
radial-gradient(1000px 600px at 110% 10%, rgba(255,255,255,.04), transparent 40%),
linear-gradient(135deg,#820622 10%,#4e2e82 60%,#725965 100%);
color:var(--text);
font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,"PokeTube Flex","Inter",sans-serif;
-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;overflow-x:hidden
}
.topbar{
position:sticky;top:0;z-index:50;display:flex;align-items:center;justify-content:space-between;gap:var(--gap);
padding:12px clamp(12px,2vw,24px);
background:linear-gradient(to bottom,rgba(0,0,0,.45),rgba(0,0,0,.25));
backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
border-bottom:1px solid rgba(255,255,255,.06)
}
.brand{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--text)}
.brand img{height:28px}
.actions{display:flex;align-items:center;gap:10px}
.icon-btn{
display:inline-grid;place-items:center;width:36px;height:36px;border-radius:999px;background:rgba(255,255,255,.08);
box-shadow:var(--shadow);color:var(--text);text-decoration:none;transition:transform .15s ease,background .2s ease
}
.icon-btn:focus-visible{outline:2px solid var(--ring);outline-offset:3px}
.icon-btn:hover{transform:translateY(-1px);background:rgba(255,255,255,.12)}
.container{max-width:1100px;margin:clamp(16px,4vw,40px) auto;padding:0 clamp(12px,2vw,20px)}
.panel{background:var(--bg-elev);border:1px solid rgba(255,255,255,.08);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden}
.panel-header{display:grid;grid-template-columns:1.2fr .8fr;gap:clamp(12px,3vw,20px);padding:clamp(16px,3vw,24px)}
@media (max-width:840px){.panel-header{grid-template-columns:1fr}}
.title{display:flex;flex-direction:column;gap:10px}
.title h1{margin:0;font-size:clamp(20px,3.2vw,28px);letter-spacing:.2px}
.subtitle{color:var(--muted);font-size:14px}
.subtitle a{color:var(--link);text-decoration:none}
.subtitle a:hover{text-decoration:underline}
.thumb-wrap{position:relative;border-radius:12px;overflow:hidden;box-shadow:0 0 4.4em <%=color%>}
.thumb{width:100%;height:auto;display:block;aspect-ratio:16/9;object-fit:cover;transform:translateZ(0)}
.formats{display:flex;flex-direction:column;gap:8px;padding:0 clamp(16px,3vw,24px) clamp(16px,3vw,24px)}
.section{margin-top:8px;padding-top:8px}
.section h2{font-size:clamp(16px,2.5vw,18px);font-weight:800;margin:10px 0 8px;letter-spacing:.3px}
.section p.helper{margin:4px 0 12px;color:var(--muted);font-size:13px}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:12px}
.chip{
display:flex;align-items:center;gap:12px;background:var(--bg-chip);
border:1px solid rgba(255,255,255,.08);border-radius:12px;padding:12px;min-height:56px;box-shadow:var(--shadow)
}
.chip .meta{display:flex;flex-direction:column;gap:4px;min-width:0;flex:1 1 auto}
.chip .label{font-weight:800;letter-spacing:.2px;white-space:nowrap;overflow:hidden;text-overflow:clip}
.chip .hint{font-size:12px;color:var(--muted)}
.chip .actions{display:flex;align-items:center;gap:8px;margin-left:auto;flex:0 0 auto}
.btn{
--h:36px;display:inline-flex;align-items:center;gap:8px;height:var(--h);padding:0 14px;border-radius:999px;
border:1px solid rgba(255,255,255,.12);background:linear-gradient(180deg,rgba(255,255,255,.08),rgba(255,255,255,.04));
color:var(--text);text-decoration:none;font-weight:700;letter-spacing:.2px;box-shadow:var(--shadow);
transition:transform .15s ease,background .2s ease,border-color .2s ease;flex:0 0 auto
}
.btn:hover{transform:translateY(-1px);background:linear-gradient(180deg,rgba(255,255,255,.12),rgba(255,255,255,.06))}
.btn:focus-visible{outline:2px solid var(--ring);outline-offset:3px}
.btn .fa-download{font-size:14px}
.alert{margin:16px 0 0;padding:12px 14px;font-size:13px;color:#fff;background:#2196F3;border-radius:10px;box-shadow:var(--shadow)}
.sr-only{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
a{color:inherit}
.chip a.btn{color:var(--text)}
.mux-card{
margin-top:16px;background:var(--bg-elev);border:1px solid rgba(255,255,255,.08);
border-radius:12px;box-shadow:var(--shadow);padding:16px
}
.mux-rows{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;margin-top:12px}
.mux-item{background:var(--bg-chip);border:1px solid rgba(255,255,255,.08);border-radius:10px;padding:12px}
.kbd{font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);border-radius:8px;padding:10px;display:block;
overflow:auto;line-height:1.45;font-size:13px}
.copy{
display:inline-flex;align-items:center;gap:6px;margin-top:8px;padding:6px 10px;border-radius:8px;border:1px solid rgba(255,255,255,.12);
background:rgba(255,255,255,.06);cursor:pointer
}
.copy:focus-visible{outline:2px solid var(--ring);outline-offset:3px}
</style>
</head>
<body>
<header class="topbar" role="banner">
<a class="brand" href="/143" aria-label="Poke home">
<img src="/css/logo-poke.svg?v=5" alt="Poke logo" />
</a>
<div class="actions" role="group" aria-label="Quick actions">
<a class="icon-btn" href="/privacy" aria-label="Privacy"><i class="fa-light fa-shield"></i></a>
<a class="icon-btn" href="/video/upload?from=" aria-label="Upload video"><i class="fa-light fa-video"></i></a>
<a class="icon-btn" href="https://codeberg.org/Ashley/poketube/" aria-label="Source code"><i class="fab fa-git-alt"></i></a>
<a class="icon-btn" href="https://codeberg.org/Ashley/poketube/issues" aria-label="Report a bug"><i class="fa-light fa-bug"></i></a>
</div>
</header>
<main class="container">
<section class="panel" aria-labelledby="download-title">
<div class="panel-header">
<div class="title">
<h1 id="download-title">Download Video / Music</h1>
<div class="subtitle">ID: <code><%= v %></code> — <a href="/watch?v=<%= v %>">Open the watch page</a></div>
<p class="helper" style="margin:6px 0 0;color:var(--muted)">Select a format to download. Muxed = audio+video. “Video-only” needs a separate audio track if you plan to mux.</p>
</div>
<figure class="thumb-wrap">
<img class="thumb" src="/vi/<%= v %>/maxresdefault.jpg" alt="Thumbnail preview for the requested video" loading="eager" decoding="async"/>
</figure>
</div>
<div class="formats">
<section class="section" aria-labelledby="muxed">
<h2 id="muxed">Muxed formats</h2>
<div class="grid">
<article class="chip">
<div class="meta">
<span class="label">MP4 — 480p</span>
<span class="hint">Audio + Video • Good compatibility</span>
</div>
<div class="actions">
<a class="btn" href="/api/video/download?v=<%= v %>&q=18"><i class="fa-light fa-download" aria-hidden="true"></i><span>Download</span></a>
</div>
</article>
</div>
</section>
<section class="section" aria-labelledby="audio">
<h2 id="audio">Audio-only formats</h2>
<div class="grid">
<article class="chip">
<div class="meta"><span class="label">m4a — low</span><span class="hint">Small size</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=139&f=webm" aria-label="Download m4a low"><i class="fa-light fa-download" aria-hidden="true"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">m4a — high</span><span class="hint">Better quality</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=140&f=webm" aria-label="Download m4a high"><i class="fa-light fa-download" aria-hidden="true"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">webm — low</span><span class="hint">Opus • Small size</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=249&f=webm" aria-label="Download webm low"><i class="fa-light fa-download" aria-hidden="true"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">webm — high</span><span class="hint">Opus • Higher bitrate</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=251&f=webm" aria-label="Download webm high"><i class="fa-light fa-download" aria-hidden="true"></i><span>Download</span></a>
</article>
</div>
</section>
<section class="section" aria-labelledby="videoonly">
<h2 id="videoonly">Video-only formats</h2>
<p class="helper">No audio. You may need to mux with an audio track.</p>
<div class="grid">
<article class="chip">
<div class="meta"><span class="label">144p — MP4</span><span class="hint">Very small</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=160&f=mp4"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">144p — WEBM</span><span class="hint">Very small</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=278&f=webm"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">240p — MP4</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=133&f=mp4"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">240p — WEBM</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=242&f=webm"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">360p — MP4</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=134&f=mp4"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">360p — WEBM</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=243&f=webm"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">480p — MP4</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=135&f=mp4"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">480p — WEBM</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=244&f=webm"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">720p — MP4</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=136&f=mp4"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">720p — WEBM</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=247&f=webm"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">1080p — MP4</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=137&f=mp4"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
<article class="chip">
<div class="meta"><span class="label">1080p — WEBM</span></div>
<a class="btn" href="/api/video/download?v=<%= v %>&q=248&f=webm"><i class="fa-light fa-download"></i><span>Download</span></a>
</article>
</div>
</section>
<div class="alert" role="status" aria-live="polite">Tip: if a download fails, try another container (MP4 vs WEBM) or a different quality.</div>
<!-- Combine (mux) section -->
<section class="mux-card" aria-labelledby="combine">
<h2 id="combine" style="margin:0 0 6px">Combine video-only + audio-only (mux)</h2>
<p class="helper">Pick one of these quick methods. They dont re-encode, so quality stays the same.</p>
<div class="mux-rows">
<div class="mux-item">
<strong>FFmpeg — MP4 output</strong>
<pre class="kbd" data-code="ffmpeg -i video.mp4 -i audio.m4a -c copy -map 0:v:0 -map 1:a:0 output.mp4">ffmpeg -i video.mp4 -i audio.m4a -c copy -map 0:v:0 -map 1:a:0 output.mp4</pre>
<button class="copy" data-target="prev"><i class="fa-light fa-copy"></i> Copy</button>
<p class="helper" style="margin-top:8px">Use when your video is MP4 (H.264) and audio is M4A/AAC.</p>
</div>
<div class="mux-item">
<strong>FFmpeg — WEBM output</strong>
<pre class="kbd" data-code="ffmpeg -i video.webm -i audio.webm -c copy -map 0:v:0 -map 1:a:0 output.webm">ffmpeg -i video.webm -i audio.webm -c copy -map 0:v:0 -map 1:a:0 output.webm</pre>
<button class="copy" data-target="prev"><i class="fa-light fa-copy"></i> Copy</button>
<p class="helper" style="margin-top:8px">Use when both tracks are WEBM (VP9/AV1 + Opus).</p>
</div>
<div class="mux-item">
<strong>FFmpeg — mixed containers</strong>
<pre class="kbd" data-code="ffmpeg -i video.webm -i audio.m4a -c copy -map 0:v:0 -map 1:a:0 output.mkv">ffmpeg -i video.webm -i audio.m4a -c copy -map 0:v:0 -map 1:a:0 output.mkv</pre>
<button class="copy" data-target="prev"><i class="fa-light fa-copy"></i> Copy</button>
<p class="helper" style="margin-top:8px">If containers differ, mux to <code>mkv</code> for best compatibility.</p>
</div>
<div class="mux-item">
<strong>Windows (drag-and-drop)</strong>
<pre class="kbd">1) Install FFmpeg (add to PATH)
2) Put video and audio next to each other
3) Open Terminal in that folder and run one of the commands above</pre>
</div>
<div class="mux-item">
<strong>GNU/Linux / macOS</strong>
<pre class="kbd"># Example
ffmpeg -i video.mp4 -i audio.m4a -c copy -map 0:v:0 -map 1:a:0 output.mp4</pre>
</div>
<div class="mux-item">
<strong>Android (Termux)</strong>
<pre class="kbd">pkg install ffmpeg
cd /sdcard/Download
ffmpeg -i video.webm -i audio.webm -c copy -map 0:v:0 -map 1:a:0 output.webm</pre>
</div>
</div>
</section>
</div>
</section>
</main>
<script src="/css/custom-css.js?v=54"></script>
<script src="/css/data-mobile.js?v=549"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.chip').forEach(chip => {
const btn = chip.querySelector('.btn');
chip.setAttribute('tabindex','0');
chip.addEventListener('keydown', e => {
if ((e.key === 'Enter' || e.key === ' ') && btn) { e.preventDefault(); btn.click(); }
});
chip.addEventListener('focus', () => chip.style.outline = `2px solid <%= color %>`);
chip.addEventListener('blur', () => chip.style.outline = 'none');
});
const toClipboard = async (text, el) => {
try {
await navigator.clipboard.writeText(text);
el.dataset.copied = "1";
const old = el.innerHTML;
el.innerHTML = '<i class="fa-light fa-check"></i> Copied';
setTimeout(() => { el.innerHTML = old; el.dataset.copied=""; }, 1200);
} catch (_) {}
};
document.querySelectorAll('.copy').forEach(btn => {
btn.addEventListener('click', () => {
const target = btn.dataset.target === 'prev' ? btn.previousElementSibling : document.querySelector(btn.dataset.target);
const code = target?.dataset.code || target?.innerText || '';
if (code.trim()) toClipboard(code.trim(), btn);
});
});
});
</script>
</body>
</html>