commit b7934e09cc30833d76438d7c6e33b6e614f4aa65 Author: Philipp Rauch Date: Tue Jun 30 21:51:10 2020 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6711f11 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +mediathek.code-workspace \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cb66ce0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3-alpine + +WORKDIR /app + +COPY requirements.txt ./ +RUN apk update && apk add mkvtoolnix && apk add --no-cache tzdata +RUN pip install --no-cache-dir -r requirements.txt +ENV TZ Europe/Berlin + +COPY main.py ./ + +RUN crontab -l | { cat; echo "15 03 * * * cd /app && python -u /app/main.py"; } | crontab - + +VOLUME ["/download"] + +CMD ["/usr/sbin/crond", "-f", "-l", "0"] diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..1332016 --- /dev/null +++ b/config.ini @@ -0,0 +1,19 @@ +[DEFAULT] +save_path = /download +default_language = ger + +[Hubert und Staller] +rss = https://mediathekviewweb.de/feed?query=%23Hubert%2Bund%2BStaller%20!ARD +languages = ger +ger = ^(?P.*)\s+\(S(?P\d+)/E(?P\d+)\)$ + +[Hubert ohne Staller] +rss = https://mediathekviewweb.de/feed?query=%23Hubert%2Bohne%2BStaller%20!ARD +languages = ger +ger = ^(?P.*)\s+\(S(?P\d+)/E(?P\d+)\)$ + +[Doctor Who] +rss = https://mediathekviewweb.de/feed?query=%23Doctor%20!ARD +languages = ger,eng +ger = ^(?P.*)\s+\(S(?P\d+)/E?(?P\d+|Weihnachtsspecial)\)$ +eng = ^(?P.*)\s+\(S(?P\d+)/E?(?P\d+|Weihnachtsspecial)\).*Originalversion.*$ \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..9df2667 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,10 @@ +version: '3' +services: + app: + build: . + image: mediathekdownload:latest + volumes: + - "/home/smokephil/Videos:/download" + - "./config.ini:/app/config.ini" + environment: + - "TZ=Europe/Berlin" \ No newline at end of file diff --git a/main.py b/main.py new file mode 100755 index 0000000..1fc3a50 --- /dev/null +++ b/main.py @@ -0,0 +1,143 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +import logging +import os +import re +import shutil +import tempfile +import urllib.request +from configparser import ConfigParser +from pprint import pp + +import feedparser +import requests +from pymkv import MKVFile, MKVTrack + +logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG) + +config = ConfigParser(interpolation=None) +config.read('config.ini') + + +def build_filename(name, titel_dict): + if titel_dict == []: + return None + if titel_dict[0]['episod'].isdigit(): + filename = '{name} S{season:>02}E{episod:>02} {titel}'.format( + name=name, **titel_dict[0]) + else: + filename = '{name} S{season:>02}.{episod} {titel}'.format( + name=name, **titel_dict[0]) + + return filename.strip().replace(' ', '.') + + +def make_mkv(data): + series_name = data['series'] + default_lang = config[series_name]['default_language'] + lang_sort = config[series_name]['languages'].split(',') + + def add_video(lang, path): + logging.debug('[MKV] add video (%s, %s)', lang, path) + video = MKVTrack(path, track_id=0, language=lang, default_track=True) + mkv.add_track(video) + + def add_audio(lang, path): + logging.debug('[MKV] add audio (%s, %s)', lang, path) + audio = MKVTrack(path, + track_id=1, + language=lang, + default_track=lang == default_lang) + mkv.add_track(audio) + + mkv = MKVFile() + add_video(default_lang, data['lang'][default_lang]) + + for lang in lang_sort: + mp4_file = data['lang'].pop(lang, None) + if mp4_file is not None: + add_audio(lang, mp4_file) + + mkv.mux(data['mkvpath']) + + +def parse_feed(series_name): + d = feedparser.parse(config[series_name]['rss']) + + episodes = dict() + for item in d['entries']: + title = item['title'] + link = item['link'] + for lang in config[series_name]['languages'].split(','): + pattern = config[series_name][lang] + groups = [x.groupdict() for x in re.finditer(pattern, title, re.M)] + if len(groups) == 1: + break + + filename = build_filename(series_name, groups) + if filename is None: + logging.warning('skip %s', title) + continue + if filename not in episodes: + episodes[filename] = dict() + episodes[filename]['lang'] = dict() + episodes[filename]['lang'][lang] = link + season = '{season:>02}'.format(**groups[0]) + episod = '{episod:>02}'.format(**groups[0]) + basepath = os.path.join(config[series_name]['save_path'], series_name, + f'Season {season:>02}') + mkvpath = os.path.join(basepath, filename + '.mkv') + + episodes[filename]['season'] = season + episodes[filename]['episod'] = episod + episodes[filename]['series'] = series_name + episodes[filename]['basepath'] = basepath + episodes[filename]['mkvpath'] = mkvpath + + return episodes + + +def download_files(episodes): + for episod in sorted(episodes.keys()): + item = episodes[episod] + if os.path.exists(item['mkvpath']): + continue + + basepath = item['basepath'] + os.makedirs(basepath, exist_ok=True) + temp = tempfile.TemporaryDirectory(dir=basepath) + + for lang in item['lang']: + link = item['lang'][lang] + filepath = os.path.join(temp.name, os.path.basename(link)) + + logging.info('start downloading...') + logging.debug('source: %s', link) + logging.debug('destination: %s', filepath) + try: + response = urllib.request.urlopen(link) + with requests.get(link, stream=True) as r: + with open(filepath, 'wb') as f: + shutil.copyfileobj(r.raw, f) + + item['lang'][lang] = filepath + except: + logging.error("download") + pass + + make_mkv(item) + temp.cleanup() + + +def run(): + logging.info('====== START ======') + for series_name in config.sections(): + episodes = parse_feed(series_name) + download_files(episodes) + + logging.info('====== END ======') + + +if __name__ == "__main__": + run() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..fa38469 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +feedparser==5.2.1 +requests==2.24.0 +pymkv==1.0.5