huuyfytryr commited on
Commit
075f520
·
1 Parent(s): 5de1239

Persist error alerts until close button is clicked and disable Tor download method

Browse files
Files changed (3) hide show
  1. app.py +1 -39
  2. static/css/style.css +31 -3
  3. static/js/app.js +24 -3
app.py CHANGED
@@ -666,48 +666,10 @@ def clip_youtube_video():
666
  stderr_text = str(e)
667
  continue
668
 
669
- # ── Method E: yt-dlp via Tor SOCKS5 proxy (exits from non-datacenter IPs) ──
670
- if not dl_success:
671
- print('[download] All direct methods failed, trying yt-dlp via Tor ...')
672
- ytdlp_bin_tor = get_pip_binary('yt-dlp')
673
- has_cookies_tor = os.path.exists(COOKIES_FILE_PATH) and os.path.getsize(COOKIES_FILE_PATH) > 0
674
- for path in [raw_download_path, raw_download_path + '.part']:
675
- if os.path.exists(path):
676
- try: os.remove(path)
677
- except Exception: pass
678
- tor_cmd = [
679
- ytdlp_bin_tor,
680
- '-f', 'bestvideo[height<=480][ext=mp4]+bestaudio/best[height<=480]/best',
681
- '--merge-output-format', 'mp4',
682
- '--no-check-certificates',
683
- '--proxy', 'socks5h://127.0.0.1:9050', # Tor SOCKS5 (with DNS resolved on proxy)
684
- '--socket-timeout', '20',
685
- '--retries', '1',
686
- '-o', raw_download_path
687
- ]
688
- if has_cookies_tor:
689
- tor_cmd.extend(['--cookies', COOKIES_FILE_PATH])
690
- tor_cmd.append(url)
691
- try:
692
- tor_res = subprocess.run(tor_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=120)
693
- tor_stderr = tor_res.stderr.decode('utf-8', errors='ignore')
694
- if tor_res.returncode == 0 and os.path.exists(raw_download_path) and os.path.getsize(raw_download_path) > 0:
695
- dl_success = True
696
- print('[yt-dlp/tor] Succeeded via Tor proxy!')
697
- else:
698
- stderr_text = tor_stderr
699
- print(f'[yt-dlp/tor] Failed: {tor_stderr[-200:]}')
700
- except subprocess.TimeoutExpired:
701
- stderr_text = 'Tor download timed out.'
702
- print('[yt-dlp/tor] Timed out.')
703
- except Exception as e:
704
- stderr_text = str(e)
705
- print(f'[yt-dlp/tor] Exception: {e}')
706
-
707
  if not dl_success:
708
  raw_err = stderr_text[-600:].strip() if stderr_text else 'All download methods failed'
709
  return jsonify({'success': False,
710
- 'error': f'Download failed — tried cobalt, pytubefix, invidious, yt-dlp+cookies, tor. Detail: {raw_err}'}), 500
711
 
712
 
713
 
 
666
  stderr_text = str(e)
667
  continue
668
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
669
  if not dl_success:
670
  raw_err = stderr_text[-600:].strip() if stderr_text else 'All download methods failed'
671
  return jsonify({'success': False,
672
+ 'error': f'Download failed — tried cobalt, pytubefix, invidious, yt-dlp+cookies. Detail: {raw_err}'}), 500
673
 
674
 
675
 
static/css/style.css CHANGED
@@ -1336,15 +1336,20 @@ body.light-theme .close-btn {
1336
  border: 1px solid var(--border-color);
1337
  box-shadow: var(--shadow-xl);
1338
  color: var(--text-primary);
1339
- padding: 12px 24px;
1340
- border-radius: var(--r-full);
1341
  z-index: 2000;
1342
  font-size: 0.85rem;
1343
  font-weight: 600;
1344
  opacity: 0;
1345
  pointer-events: none;
1346
  transition: all var(--t-normal) cubic-bezier(0.175, 0.885, 0.32, 1.275);
1347
- white-space: nowrap;
 
 
 
 
 
1348
  }
1349
  body.light-theme .toast {
1350
  background: #ffffff;
@@ -1353,6 +1358,29 @@ body.light-theme .toast {
1353
  .toast.show {
1354
  transform: translateX(-50%) translateY(0);
1355
  opacity: 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1356
  }
1357
 
1358
  .toast.success { border-left: 4px solid var(--success); }
 
1336
  border: 1px solid var(--border-color);
1337
  box-shadow: var(--shadow-xl);
1338
  color: var(--text-primary);
1339
+ padding: 12px 20px;
1340
+ border-radius: var(--r-md);
1341
  z-index: 2000;
1342
  font-size: 0.85rem;
1343
  font-weight: 600;
1344
  opacity: 0;
1345
  pointer-events: none;
1346
  transition: all var(--t-normal) cubic-bezier(0.175, 0.885, 0.32, 1.275);
1347
+ max-width: 90%;
1348
+ width: 480px;
1349
+ display: flex;
1350
+ align-items: center;
1351
+ justify-content: space-between;
1352
+ gap: 16px;
1353
  }
1354
  body.light-theme .toast {
1355
  background: #ffffff;
 
1358
  .toast.show {
1359
  transform: translateX(-50%) translateY(0);
1360
  opacity: 1;
1361
+ pointer-events: auto;
1362
+ }
1363
+
1364
+ .toast-content {
1365
+ flex: 1;
1366
+ white-space: normal;
1367
+ word-break: break-word;
1368
+ line-height: 1.4;
1369
+ }
1370
+
1371
+ .toast-close {
1372
+ background: transparent;
1373
+ border: none;
1374
+ color: var(--text-muted);
1375
+ cursor: pointer;
1376
+ font-size: 1.25rem;
1377
+ font-weight: 700;
1378
+ line-height: 1;
1379
+ padding: 0 4px;
1380
+ transition: color var(--t-fast);
1381
+ }
1382
+ .toast-close:hover {
1383
+ color: var(--text-primary);
1384
  }
1385
 
1386
  .toast.success { border-left: 4px solid var(--success); }
static/js/app.js CHANGED
@@ -11,12 +11,33 @@ const state = {
11
  function $(id) { return document.getElementById(id); }
12
  function $q(sel) { return document.querySelector(sel); }
13
 
14
- function showToast(msg, type = 'success', duration = 3500) {
 
 
15
  const t = $('toast');
16
  if (!t) return;
17
- t.textContent = msg;
 
 
 
 
 
 
 
 
 
18
  t.className = `toast ${type} show`;
19
- setTimeout(() => t.classList.remove('show'), duration);
 
 
 
 
 
 
 
 
 
 
20
  }
21
 
22
  function animateCount(el, target) {
 
11
  function $(id) { return document.getElementById(id); }
12
  function $q(sel) { return document.querySelector(sel); }
13
 
14
+ let toastTimeout = null;
15
+
16
+ function showToast(msg, type = 'success', duration = 4000) {
17
  const t = $('toast');
18
  if (!t) return;
19
+
20
+ if (toastTimeout) {
21
+ clearTimeout(toastTimeout);
22
+ toastTimeout = null;
23
+ }
24
+
25
+ t.innerHTML = `
26
+ <span class="toast-content">${msg}</span>
27
+ <button type="button" class="toast-close" id="toast-close-btn" title="Dismiss Alert">&times;</button>
28
+ `;
29
  t.className = `toast ${type} show`;
30
+
31
+ t.querySelector('#toast-close-btn')?.addEventListener('click', () => {
32
+ t.classList.remove('show');
33
+ });
34
+
35
+ // Keep error toasts visible until explicitly dismissed
36
+ if (type !== 'error') {
37
+ toastTimeout = setTimeout(() => {
38
+ t.classList.remove('show');
39
+ }, duration);
40
+ }
41
  }
42
 
43
  function animateCount(el, target) {