diff --git a/frontend/src/views/Admin.vue b/frontend/src/views/Admin.vue index 986b329..38f740b 100644 --- a/frontend/src/views/Admin.vue +++ b/frontend/src/views/Admin.vue @@ -19,6 +19,17 @@ + + + + +
+ + + {{ addMsg.text }}
@@ -237,6 +248,11 @@ const videos = ref([]) const stats = ref(null) const search = ref('') const filterStatus = ref('') +// ── Add download ── +const showAddForm = ref(false) +const addUrl = ref('') +const addLoading = ref(false) +const addMsg = ref(null) const page = ref(1) const total = ref(0) const pageSize = 20 @@ -436,6 +452,24 @@ async function downloadAuth(v) { URL.revokeObjectURL(url) } +async function addDownload() { + const url = addUrl.value.trim() + if (!url) return + addLoading.value = true + addMsg.value = null + try { + await axios.post('/api/download', { url, format_id: 'best', quality: '' }) + addMsg.value = { ok: true, text: '✅ Download started' } + addUrl.value = '' + setTimeout(() => { addMsg.value = null; showAddForm.value = false }, 2000) + fetchVideos(); fetchStats() + } catch (e) { + addMsg.value = { ok: false, text: '❌ ' + (e.response?.data?.detail || 'Failed') } + } finally { + addLoading.value = false + } +} + async function deleteVideo(v) { const shortTitle = v.title && v.title.length > 20 ? v.title.slice(0, 20) + '…' : (v.title || 'Untitled') if (!confirm(`Delete #${v.id} "${shortTitle}"?`)) return @@ -661,4 +695,16 @@ onUnmounted(() => { if (pollTimer) clearInterval(pollTimer) }) .player-wrap { position: relative; max-width: 90vw; max-height: 90vh; } .player { max-width: 90vw; max-height: 85vh; border-radius: 8px; } .close-btn { position: absolute; top: -40px; right: 0; background: none; border: none; color: #fff; font-size: 1.5rem; cursor: pointer; } + +/* Add download */ +.btn-add-dl { margin-left: auto; background: #1da1f2; color: #fff; border: none; border-radius: 6px; padding: 6px 14px; font-size: 0.85rem; cursor: pointer; white-space: nowrap; } +.btn-add-dl:hover { background: #1a8fd1; } +.add-dl-form { display: flex; align-items: center; gap: 8px; padding: 10px 0 4px; flex-wrap: wrap; } +.add-dl-input { flex: 1; min-width: 240px; background: #1a1a2e; border: 1px solid #333; border-radius: 6px; color: #eee; padding: 8px 12px; font-size: 0.88rem; } +.add-dl-input:focus { outline: none; border-color: #1da1f2; } +.btn-add-submit { background: #27ae60; color: #fff; border: none; border-radius: 6px; padding: 8px 16px; font-size: 0.85rem; cursor: pointer; white-space: nowrap; } +.btn-add-submit:disabled { opacity: 0.6; cursor: default; } +.btn-add-submit:not(:disabled):hover { background: #219a52; } +.add-msg-ok { color: #2ecc71; font-size: 0.85rem; } +.add-msg-err { color: #e74c3c; font-size: 0.85rem; }