feat: add 'New Download' button in admin video library
- ➕ New Download toggle in search bar
- Inline URL input form with Enter key support
- Calls POST /api/download with format_id=best
- Shows success/error feedback, auto-closes on success
- Refreshes video list and stats after submission
This commit is contained in:
@@ -19,6 +19,17 @@
|
|||||||
<option value="error">Error</option>
|
<option value="error">Error</option>
|
||||||
<option value="pending">Pending</option>
|
<option value="pending">Pending</option>
|
||||||
</select>
|
</select>
|
||||||
|
<button class="btn-add-dl" @click="showAddForm = !showAddForm">➕ New Download</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Inline download form -->
|
||||||
|
<div v-if="showAddForm" class="add-dl-form">
|
||||||
|
<input v-model="addUrl" placeholder="Paste YouTube / Pornhub / X URL…"
|
||||||
|
@keyup.enter="addDownload" class="add-dl-input" />
|
||||||
|
<button @click="addDownload" :disabled="addLoading" class="btn-add-submit">
|
||||||
|
{{ addLoading ? 'Starting…' : '▶ Download' }}
|
||||||
|
</button>
|
||||||
|
<span v-if="addMsg" :class="addMsg.ok ? 'add-msg-ok' : 'add-msg-err'">{{ addMsg.text }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="video-list">
|
<div class="video-list">
|
||||||
@@ -237,6 +248,11 @@ const videos = ref([])
|
|||||||
const stats = ref(null)
|
const stats = ref(null)
|
||||||
const search = ref('')
|
const search = ref('')
|
||||||
const filterStatus = 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 page = ref(1)
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const pageSize = 20
|
const pageSize = 20
|
||||||
@@ -436,6 +452,24 @@ async function downloadAuth(v) {
|
|||||||
URL.revokeObjectURL(url)
|
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) {
|
async function deleteVideo(v) {
|
||||||
const shortTitle = v.title && v.title.length > 20 ? v.title.slice(0, 20) + '…' : (v.title || 'Untitled')
|
const shortTitle = v.title && v.title.length > 20 ? v.title.slice(0, 20) + '…' : (v.title || 'Untitled')
|
||||||
if (!confirm(`Delete #${v.id} "${shortTitle}"?`)) return
|
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-wrap { position: relative; max-width: 90vw; max-height: 90vh; }
|
||||||
.player { max-width: 90vw; max-height: 85vh; border-radius: 8px; }
|
.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; }
|
.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; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user