docker: alpine, autodownload
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
app/__pycache__
|
app/__pycache__
|
||||||
|
downloads/
|
||||||
16
Dockerfile
16
Dockerfile
@@ -1,9 +1,7 @@
|
|||||||
FROM python:3.12-slim
|
FROM python:3.12-alpine
|
||||||
|
|
||||||
# Install ffmpeg (required for merging video+audio streams and MP3 conversion)
|
# Install ffmpeg and dcron (lightweight cron daemon for Alpine)
|
||||||
RUN apt-get update && \
|
RUN apk add --no-cache ffmpeg dcron
|
||||||
apt-get install -y --no-install-recommends ffmpeg && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@@ -17,10 +15,10 @@ COPY app/ .
|
|||||||
# Downloads are stored here (mount a host volume to persist them)
|
# Downloads are stored here (mount a host volume to persist them)
|
||||||
RUN mkdir -p /downloads
|
RUN mkdir -p /downloads
|
||||||
|
|
||||||
# Setup Cronjob
|
# Setup Cronjob (fails)
|
||||||
RUN apt-get update && apt-get install -y cron && \
|
RUN echo "0 4 * * * pip install --upgrade yt-dlp >> /var/log/yt-dlp-upgrade.log 2>&1" | crontab -
|
||||||
echo "0 4 * * * pip install --upgrade yt-dlp >> /var/log/yt-dlp-upgrade.log 2>&1" | crontab -
|
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
|
# Launch
|
||||||
|
CMD uvicorn main:app --host 0.0.0.0 --port 8080
|
||||||
@@ -96,6 +96,7 @@
|
|||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 11px 20px;
|
padding: 11px 20px;
|
||||||
|
margin: 1px;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -290,15 +291,7 @@
|
|||||||
<body>
|
<body>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<div class="logo">
|
<h1>hier könnte ihre werbung stehen</h1>
|
||||||
<svg aria-label="yt-dlp downloader" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect width="36" height="36" rx="8" fill="#e53935"/>
|
|
||||||
<path d="M10 13l8 5-8 5V13z" fill="white"/>
|
|
||||||
<path d="M20 13h6M20 18h6M20 23h6" stroke="white" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
<h1>yt-dlp Downloader</h1>
|
|
||||||
</div>
|
|
||||||
<p class="subtitle">Download videos & audio from YouTube and 1000+ sites</p>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -315,28 +308,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="options-row">
|
<div style="margin-top: 16px; display: flex;">
|
||||||
<div class="option-group">
|
<button class="btn" id="dl-btn-video" onclick="startDownload(true)" style="width:50%">Download Video</button>
|
||||||
<label class="opt-label" for="format-select">Format</label>
|
|
||||||
<select id="format-select" onchange="onFormatChange()">
|
|
||||||
<option value="video">Video (MP4)</option>
|
|
||||||
<option value="audio">Audio (MP3)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="option-group" id="quality-group">
|
|
||||||
<label class="opt-label" for="quality-select">Quality</label>
|
|
||||||
<select id="quality-select">
|
|
||||||
<option value="best">Best available</option>
|
|
||||||
<option value="1080">1080p</option>
|
|
||||||
<option value="720">720p</option>
|
|
||||||
<option value="480">480p</option>
|
|
||||||
<option value="360">360p</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin-top:16px;">
|
<button class="btn" id="dl-btn-audio" onclick="startDownload(false)" style="width:50%">Download Audio</button>
|
||||||
<button class="btn" id="dl-btn" onclick="startDownload()" style="width:100%">Download</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -359,12 +334,6 @@
|
|||||||
return n + ' views';
|
return n + ' views';
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFormatChange() {
|
|
||||||
const fmt = document.getElementById('format-select').value;
|
|
||||||
document.getElementById('quality-group').style.opacity = fmt === 'audio' ? '0.4' : '1';
|
|
||||||
document.getElementById('quality-select').disabled = fmt === 'audio';
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchInfo() {
|
async function fetchInfo() {
|
||||||
const url = document.getElementById('url-input').value.trim();
|
const url = document.getElementById('url-input').value.trim();
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
@@ -390,15 +359,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startDownload() {
|
async function startDownload(isVideo) {
|
||||||
const url = document.getElementById('url-input').value.trim();
|
const url = document.getElementById('url-input').value.trim();
|
||||||
if (!url) { alert('Please enter a URL first.'); return; }
|
if (!url) { alert('Please enter a URL first.'); return; }
|
||||||
const format = document.getElementById('format-select').value;
|
const format = isVideo ? "video" : "audio";
|
||||||
const quality = document.getElementById('quality-select').value;
|
const quality = "best";
|
||||||
|
|
||||||
const btn = document.getElementById('dl-btn');
|
const btnv = document.getElementById('dl-btn-video');
|
||||||
btn.disabled = true;
|
btnv.disabled = true;
|
||||||
btn.innerHTML = '<span class="spinner"></span>Starting…';
|
btnv.innerHTML = '<span class="spinner"></span>Starting…';
|
||||||
|
|
||||||
|
const btna = document.getElementById('dl-btn-audio');
|
||||||
|
btna.disabled = true;
|
||||||
|
btna.innerHTML = '<span class="spinner"></span>Starting…';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/download', {
|
const res = await fetch('/api/download', {
|
||||||
@@ -413,8 +386,11 @@
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert('Failed to start download: ' + e.message);
|
alert('Failed to start download: ' + e.message);
|
||||||
} finally {
|
} finally {
|
||||||
btn.disabled = false;
|
btna.disabled = false;
|
||||||
btn.textContent = 'Download';
|
btna.textContent = 'Download Audio';
|
||||||
|
|
||||||
|
btnv.disabled = false;
|
||||||
|
btnv.textContent = 'Download Video';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,7 +458,8 @@
|
|||||||
} else if (job.status === 'processing') {
|
} else if (job.status === 'processing') {
|
||||||
meta.textContent = 'Post-processing (merging / converting)…';
|
meta.textContent = 'Post-processing (merging / converting)…';
|
||||||
} else if (job.status === 'done') {
|
} else if (job.status === 'done') {
|
||||||
meta.textContent = 'Complete · ' + job.filename.split('_').slice(1).join('_');
|
const goodname = job.filename.split('_').slice(1).join('_');
|
||||||
|
meta.textContent = 'Complete · ' + goodname;
|
||||||
if (!actions.querySelector('a')) {
|
if (!actions.querySelector('a')) {
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = '/api/file/' + job.id;
|
a.href = '/api/file/' + job.id;
|
||||||
@@ -490,8 +467,9 @@
|
|||||||
a.style.fontSize = '0.82rem';
|
a.style.fontSize = '0.82rem';
|
||||||
a.style.padding = '7px 14px';
|
a.style.padding = '7px 14px';
|
||||||
a.textContent = '⬇ Download File';
|
a.textContent = '⬇ Download File';
|
||||||
a.download = '';
|
a.download = goodname;
|
||||||
actions.appendChild(a);
|
actions.appendChild(a);
|
||||||
|
window.location.href = '/api/file/' + job.id;
|
||||||
}
|
}
|
||||||
} else if (job.status === 'error') {
|
} else if (job.status === 'error') {
|
||||||
meta.textContent = '';
|
meta.textContent = '';
|
||||||
@@ -502,7 +480,7 @@
|
|||||||
|
|
||||||
// Allow pressing Enter in the URL field
|
// Allow pressing Enter in the URL field
|
||||||
document.getElementById('url-input').addEventListener('keydown', (e) => {
|
document.getElementById('url-input').addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Enter') startDownload();
|
if (e.key === 'Enter') startDownload(true);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user