Skip to content

Instantly share code, notes, and snippets.

@nobkd
Last active January 21, 2026 21:40
Show Gist options
  • Select an option

  • Save nobkd/452a8dca9f982519c0c9d4c99ab3921f to your computer and use it in GitHub Desktop.

Select an option

Save nobkd/452a8dca9f982519c0c9d4c99ab3921f to your computer and use it in GitHub Desktop.
Download CCC Event Feeds

dl-ccc-feed

Quickly download all elements from a CCC event feed.

CLI Help

usage: dl-ccc-feed [-h] [-o OUTPUT] [-q {lq,hq}] [-f {mp4,webm,mp3,opus}] [-l] [-np] event

Download all elements from a CCC event feed

positional arguments:
  event                             e.g. '39c3'

options:
  -h, --help                        show this help message and exit
  -o, --output OUTPUT               output directory (default: .)
  -q, --quality {lq,hq}             only applicable for video (default: lq)
  -f, --format {mp4,webm,mp3,opus}  file format to download (default: mp4)
  -l, --list                        list file links in feed and exit (default: False)
  -np, --no-progress                don't show progress bars (default: False)
#!/usr/bin/env python
import argparse as ap
from os import makedirs, remove
import os.path as p
import signal
import feedparser as fp
import requests as r
from tqdm import tqdm
########## exit gracefully ##########
open_file = None
def graceful_exit(sig, frame):
if open_file:
remove(open_file)
exit()
signal.signal(signal.SIGINT, graceful_exit)
########## setup argparse ##########
video_formats = ["mp4", "webm"]
audio_formats = ["mp3", "opus"]
parser = ap.ArgumentParser(prog="dl-ccc-feed", description="Download all elements from a CCC event feed", formatter_class=lambda prog: ap.ArgumentDefaultsHelpFormatter(prog, max_help_position=50))
parser.add_argument("event", help="e.g. '39c3'")
parser.add_argument("-o", "--output", default=".", help="output directory")
parser.add_argument("-q", "--quality", default="lq", choices=["lq", "hq"], help="only applicable for video")
parser.add_argument("-f", "--format", default="mp4", choices=video_formats + audio_formats, help="file format to download")
parser.add_argument("-l", "--list", default=False, action='store_true', help="list file links in feed and exit")
parser.add_argument("-np", "--no-progress", default=False, action='store_true', help="don't show progress bars")
########## parse args ##########
args = parser.parse_args()
pth = args.output
event = args.event
file_format = args.format
quality = args.quality if file_format in video_formats else ""
no_progress = args.no_progress
show_list = args.list
########## setup ##########
url = f"https://media.ccc.de/c/{event}/podcast/{file_format}{('-' if quality else '') + quality}.xml"
# create dir, if not exists
makedirs(pth, exist_ok=True)
entries = fp.parse(url).entries
########## optional listing ##########
if show_list:
for entry in entries:
print(entry.id)
exit()
########## run download ##########
# for each element in feed
for entry in tqdm(entries, colour="green", position=1, disable=no_progress):
url = entry.id
title = entry.title
# get file name
fname = p.basename(url).split("?")[0]
fpth = p.join(pth, fname)
if p.exists(fpth):
continue
# download
res = r.get(url, stream=True)
size = int(res.headers.get("content-length", 0))
with tqdm(total=size, unit="B", unit_scale=True, colour="red", position=0, leave=False, desc="{:.30}".format(title), disable=no_progress) as bar:
with open(fpth, "wb") as f:
open_file = fpth
for chunk in res.iter_content(chunk_size=1024):
f.write(chunk)
bar.update(len(chunk))
open_file = None
feedparser>=6.0.12
Requests>=2.32.5
tqdm>=4.67.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment