+----------------------------------------------------------+
| [Add URLs] [Start] [Stop] | Parallel: [4] Retries: [3] |
+----------------------------------------------------------+
| ████████████ video1.mp4 45% 2.1 MB/s ETA: 00:01:23 |
| ████████ video2.webm 12% 0.8 MB/s ETA: 00:04:12 |
| ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ ☐ |
| (drag‑and‑drop a .txt file here) |
+----------------------------------------------------------+
| Log output … |
+----------------------------------------------------------+
| ✅ | Item |
|----|------|
| ☐ | Add --list <file> and drag‑and‑drop handling to UI. |
| ☐ | Implement DownloadManager with configurable semaphore for parallelism. |
| ☐ | Hook up a thread‑safe progress_t struct and UI callback. |
| ☐ | Add exponential back‑off retry wrapper around the low‑level HTTP client. |
| ☐ | Integrate optional ffprobe (bundle a small static binary for cross‑platform use). |
| ☐ | Write JSON config loader that merges CLI flags > config file > defaults. |
| ☐ | Create failed.log with timestamped entries. |
| ☐ | Add unit tests for: queue ordering, retry counts, metadata generation. |
| ☐ | Provide a simple “‑‑help” output describing all flags. |
| ☐ | Update documentation (README, changelog) with new feature description and usage examples. |
def main(args):
cfg = load_config(args.config_path) # defaults + overrides
url_list = read_urls(args.url_file) # one URL per line
manager = DownloadManager(cfg)
for url in url_list:
manager.enqueue(url)
manager.run() # blocks until queue empty
class DownloadManager:
def __init__(self, cfg):
self.cfg = cfg
self.queue = Queue()
self.active = 0
self.lock = threading.Lock()
self.semaphore = threading.Semaphore(cfg.parallel)
def enqueue(self, url):
self.queue.put(Job(url))
def run(self):
while not self.queue.empty() or self.active > 0:
self.semaphore.acquire()
job = self.queue.get()
threading.Thread(target=self._worker, args=(job,)).start()
def _worker(self, job):
with self.lock: self.active += 1
try:
download_with_retries(job, self.cfg)
if self.cfg.metadata:
extract_metadata(job.output_path)
finally:
with self.lock: self.active -= 1
self.semaphore.release()
download_with_retries implements exponential back‑off and updates a thread‑safe progress object that the UI reads. xfantazy video downloader upd