feat(soundboard): download modal with filename input + fix yt-dlp binary
- Add download modal: filename input, progress phases (input/downloading/done/error) - Refactor backend: shared handleUrlDownload() with optional custom filename + rename - Fix Dockerfile: use yt-dlp_linux standalone binary (no Python dependency) - Modal shows URL type badge (YouTube/Instagram/MP3), spinner, retry on error Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0b2ba0ef86
commit
9ff8a38547
4 changed files with 332 additions and 76 deletions
|
|
@ -2032,6 +2032,141 @@
|
|||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────
|
||||
Download Modal
|
||||
──────────────────────────────────────────── */
|
||||
.dl-modal-overlay {
|
||||
position: fixed; inset: 0;
|
||||
background: rgba(0, 0, 0, .55);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
z-index: 300;
|
||||
animation: fade-in 150ms ease;
|
||||
}
|
||||
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
|
||||
|
||||
.dl-modal {
|
||||
width: 420px; max-width: 92vw;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid rgba(255, 255, 255, .1);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 12px 60px rgba(0, 0, 0, .5);
|
||||
animation: scale-in 200ms cubic-bezier(.16, 1, .3, 1);
|
||||
}
|
||||
@keyframes scale-in { from { opacity: 0; transform: scale(.95); } to { opacity: 1; transform: scale(1); } }
|
||||
|
||||
.dl-modal-header {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, .06);
|
||||
font-size: 14px; font-weight: 700;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
.dl-modal-header .material-icons { color: var(--accent); }
|
||||
|
||||
.dl-modal-close {
|
||||
margin-left: auto;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
width: 26px; height: 26px; border-radius: 50%;
|
||||
border: none; background: rgba(255,255,255,.06);
|
||||
color: var(--text-muted); cursor: pointer;
|
||||
transition: background var(--transition);
|
||||
}
|
||||
.dl-modal-close:hover { background: rgba(255,255,255,.14); color: var(--text-normal); }
|
||||
|
||||
.dl-modal-body { padding: 16px; display: flex; flex-direction: column; gap: 14px; }
|
||||
|
||||
/* URL display */
|
||||
.dl-modal-url {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 8px 10px; border-radius: 8px;
|
||||
background: rgba(0, 0, 0, .2);
|
||||
overflow: hidden;
|
||||
}
|
||||
.dl-modal-tag {
|
||||
flex-shrink: 0; padding: 2px 8px; border-radius: 6px;
|
||||
font-size: 10px; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
|
||||
}
|
||||
.dl-modal-tag.youtube { background: rgba(255, 0, 0, .18); color: #ff4444; }
|
||||
.dl-modal-tag.instagram { background: rgba(225, 48, 108, .18); color: #e1306c; }
|
||||
.dl-modal-tag.mp3 { background: rgba(46, 204, 113, .18); color: #2ecc71; }
|
||||
.dl-modal-url-text {
|
||||
font-size: 11px; color: var(--text-faint);
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Filename field */
|
||||
.dl-modal-field { display: flex; flex-direction: column; gap: 5px; }
|
||||
.dl-modal-label { font-size: 11px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .5px; }
|
||||
.dl-modal-input-wrap {
|
||||
display: flex; align-items: center;
|
||||
border: 1px solid rgba(255, 255, 255, .1); border-radius: 8px;
|
||||
background: rgba(0, 0, 0, .15);
|
||||
overflow: hidden;
|
||||
transition: border-color var(--transition);
|
||||
}
|
||||
.dl-modal-input-wrap:focus-within { border-color: var(--accent); }
|
||||
.dl-modal-input {
|
||||
flex: 1; border: none; background: transparent;
|
||||
padding: 8px 10px; color: var(--text-normal);
|
||||
font-size: 13px; font-family: var(--font); outline: none;
|
||||
}
|
||||
.dl-modal-input::placeholder { color: var(--text-faint); }
|
||||
.dl-modal-ext {
|
||||
padding: 0 10px; font-size: 12px; font-weight: 600;
|
||||
color: var(--text-faint); background: rgba(255, 255, 255, .04);
|
||||
align-self: stretch; display: flex; align-items: center;
|
||||
}
|
||||
.dl-modal-hint { font-size: 10px; color: var(--text-faint); }
|
||||
|
||||
/* Progress spinner */
|
||||
.dl-modal-progress {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 20px 0; justify-content: center;
|
||||
font-size: 13px; color: var(--text-muted);
|
||||
}
|
||||
.dl-modal-spinner {
|
||||
width: 24px; height: 24px; border-radius: 50%;
|
||||
border: 3px solid rgba(var(--accent-rgb), .2);
|
||||
border-top-color: var(--accent);
|
||||
animation: spin 800ms linear infinite;
|
||||
}
|
||||
|
||||
/* Success */
|
||||
.dl-modal-success {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 16px 0; justify-content: center;
|
||||
font-size: 13px; color: var(--text-normal);
|
||||
}
|
||||
.dl-modal-check { color: #2ecc71; font-size: 28px; }
|
||||
|
||||
/* Error */
|
||||
.dl-modal-error {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 12px 0; justify-content: center;
|
||||
font-size: 13px; color: #e74c3c;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
.dl-modal-actions {
|
||||
display: flex; justify-content: flex-end; gap: 8px;
|
||||
padding: 0 16px 14px;
|
||||
}
|
||||
.dl-modal-cancel {
|
||||
padding: 7px 14px; border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, .1); background: transparent;
|
||||
color: var(--text-muted); font-size: 12px; font-weight: 600;
|
||||
cursor: pointer; transition: all var(--transition);
|
||||
}
|
||||
.dl-modal-cancel:hover { background: rgba(255,255,255,.06); color: var(--text-normal); }
|
||||
.dl-modal-submit {
|
||||
display: flex; align-items: center; gap: 5px;
|
||||
padding: 7px 16px; border-radius: 8px;
|
||||
border: none; background: var(--accent);
|
||||
color: #fff; font-size: 12px; font-weight: 700;
|
||||
cursor: pointer; transition: filter var(--transition);
|
||||
}
|
||||
.dl-modal-submit:hover { filter: brightness(1.15); }
|
||||
|
||||
/* ────────────────────────────────────────────
|
||||
Utility
|
||||
──────────────────────────────────────────── */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue