Khaneja commited on
Commit
1cc8a00
·
1 Parent(s): 27820e7

feat: implement mp3 quality selection and fix uuid routes

Browse files
Files changed (2) hide show
  1. app.py +23 -1
  2. templates/index.html +15 -4
app.py CHANGED
@@ -20,6 +20,8 @@ def run_download(job_id, url, format_choice, format_id):
20
  cmd = ["yt-dlp", "--no-playlist", "-o", out_template]
21
 
22
  if format_choice == "audio":
 
 
23
  cmd += ["-x", "--audio-format", "mp3"]
24
  elif format_id:
25
  cmd += ["-f", f"{format_id}+bestaudio/best", "--merge-output-format", "mp4"]
@@ -95,7 +97,16 @@ def get_info():
95
 
96
  # Build quality options — keep best format per resolution
97
  best_by_height = {}
 
98
  for f in info.get("formats", []):
 
 
 
 
 
 
 
 
99
  height = f.get("height")
100
  if height and f.get("vcodec", "none") != "none":
101
  tbr = f.get("tbr") or 0
@@ -111,12 +122,22 @@ def get_info():
111
  })
112
  formats.sort(key=lambda x: x["height"], reverse=True)
113
 
 
 
 
 
 
 
 
 
 
114
  return jsonify({
115
  "title": info.get("title", ""),
116
  "thumbnail": info.get("thumbnail", ""),
117
  "duration": info.get("duration"),
118
  "uploader": info.get("uploader", ""),
119
  "formats": formats,
 
120
  })
121
  except subprocess.TimeoutExpired:
122
  return jsonify({"error": "Timed out fetching video info"}), 400
@@ -158,7 +179,8 @@ def check_status(job_id):
158
 
159
 
160
  @app.route("/api/file/<job_id>")
161
- def download_file(job_id):
 
162
  job = jobs.get(job_id)
163
  if not job or job["status"] != "done":
164
  return jsonify({"error": "File not ready"}), 404
 
20
  cmd = ["yt-dlp", "--no-playlist", "-o", out_template]
21
 
22
  if format_choice == "audio":
23
+ if format_id:
24
+ cmd += ["-f", format_id]
25
  cmd += ["-x", "--audio-format", "mp3"]
26
  elif format_id:
27
  cmd += ["-f", f"{format_id}+bestaudio/best", "--merge-output-format", "mp4"]
 
97
 
98
  # Build quality options — keep best format per resolution
99
  best_by_height = {}
100
+ audio_by_abr = {}
101
  for f in info.get("formats", []):
102
+ if f.get("vcodec") == "none":
103
+ abr = f.get("abr")
104
+ if abr:
105
+ abr = int(abr)
106
+ if abr not in audio_by_abr or (f.get("asr", 0) > audio_by_abr[abr].get("asr", 0)):
107
+ audio_by_abr[abr] = f
108
+ continue
109
+
110
  height = f.get("height")
111
  if height and f.get("vcodec", "none") != "none":
112
  tbr = f.get("tbr") or 0
 
122
  })
123
  formats.sort(key=lambda x: x["height"], reverse=True)
124
 
125
+ audio_formats = []
126
+ for abr, f in audio_by_abr.items():
127
+ audio_formats.append({
128
+ "id": f["format_id"],
129
+ "label": f"{abr}kbps",
130
+ "abr": abr,
131
+ })
132
+ audio_formats.sort(key=lambda x: x["abr"], reverse=True)
133
+
134
  return jsonify({
135
  "title": info.get("title", ""),
136
  "thumbnail": info.get("thumbnail", ""),
137
  "duration": info.get("duration"),
138
  "uploader": info.get("uploader", ""),
139
  "formats": formats,
140
+ "audioFormats": audio_formats,
141
  })
142
  except subprocess.TimeoutExpired:
143
  return jsonify({"error": "Timed out fetching video info"}), 400
 
179
 
180
 
181
  @app.route("/api/file/<job_id>")
182
+ @app.route("/api/file/<job_id>/<path:filename>")
183
+ def download_file(job_id, filename=None):
184
  job = jobs.get(job_id)
185
  if not job or job["status"] != "done":
186
  return jsonify({"error": "File not ready"}), 404
templates/index.html CHANGED
@@ -593,6 +593,8 @@
593
  uploader: data.uploader || '',
594
  formats: data.formats || [],
595
  selectedFormatId: data.formats?.[0]?.id || null,
 
 
596
  };
597
  }
598
  } catch (err) {
@@ -664,7 +666,11 @@
664
  }
665
 
666
  let qualityChips = '';
667
- if (!isAudio && c.formats && c.formats.length > 1) {
 
 
 
 
668
  qualityChips = c.formats.map(f =>
669
  `<button class="q-chip${f.id === c.selectedFormatId ? ' active' : ''}" onclick="pickFormat(${idx}, '${f.id}')">${f.label}</button>`
670
  ).join('');
@@ -705,7 +711,11 @@
705
  }
706
 
707
  function pickFormat(idx, formatId) {
708
- cardData[idx].selectedFormatId = formatId;
 
 
 
 
709
  renderCard(idx);
710
  }
711
 
@@ -716,13 +726,14 @@
716
  renderCard(idx);
717
 
718
  try {
 
719
  const res = await fetch('/api/download', {
720
  method: 'POST',
721
  headers: { 'Content-Type': 'application/json' },
722
  body: JSON.stringify({
723
  url: c.url,
724
  format: currentFormat,
725
- format_id: c.selectedFormatId,
726
  title: c.title || '',
727
  }),
728
  });
@@ -773,7 +784,7 @@
773
  const c = cardData[idx];
774
  if (!c.jobId) return;
775
  const a = document.createElement('a');
776
- a.href = `/api/file/${c.jobId}`;
777
  a.download = c.filename || '';
778
  a.click();
779
  }
 
593
  uploader: data.uploader || '',
594
  formats: data.formats || [],
595
  selectedFormatId: data.formats?.[0]?.id || null,
596
+ audioFormats: data.audioFormats || [],
597
+ selectedAudioFormatId: data.audioFormats?.[0]?.id || null,
598
  };
599
  }
600
  } catch (err) {
 
666
  }
667
 
668
  let qualityChips = '';
669
+ if (isAudio && c.audioFormats && c.audioFormats.length > 1) {
670
+ qualityChips = c.audioFormats.map(f =>
671
+ `<button class="q-chip${f.id === c.selectedAudioFormatId ? ' active' : ''}" onclick="pickFormat(${idx}, '${f.id}')">${f.label}</button>`
672
+ ).join('');
673
+ } else if (!isAudio && c.formats && c.formats.length > 1) {
674
  qualityChips = c.formats.map(f =>
675
  `<button class="q-chip${f.id === c.selectedFormatId ? ' active' : ''}" onclick="pickFormat(${idx}, '${f.id}')">${f.label}</button>`
676
  ).join('');
 
711
  }
712
 
713
  function pickFormat(idx, formatId) {
714
+ if (currentFormat === 'audio') {
715
+ cardData[idx].selectedAudioFormatId = formatId;
716
+ } else {
717
+ cardData[idx].selectedFormatId = formatId;
718
+ }
719
  renderCard(idx);
720
  }
721
 
 
726
  renderCard(idx);
727
 
728
  try {
729
+ const payloadFormatId = currentFormat === 'audio' ? c.selectedAudioFormatId : c.selectedFormatId;
730
  const res = await fetch('/api/download', {
731
  method: 'POST',
732
  headers: { 'Content-Type': 'application/json' },
733
  body: JSON.stringify({
734
  url: c.url,
735
  format: currentFormat,
736
+ format_id: payloadFormatId,
737
  title: c.title || '',
738
  }),
739
  });
 
784
  const c = cardData[idx];
785
  if (!c.jobId) return;
786
  const a = document.createElement('a');
787
+ a.href = `/api/file/${c.jobId}/${encodeURIComponent(c.filename || 'download.mp4')}`;
788
  a.download = c.filename || '';
789
  a.click();
790
  }