first commit
This commit is contained in:
commit
b7934e09cc
6 changed files with 193 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
.vscode
|
||||||
|
mediathek.code-workspace
|
||||||
16
Dockerfile
Normal file
16
Dockerfile
Normal file
|
|
@ -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"]
|
||||||
19
config.ini
Normal file
19
config.ini
Normal file
|
|
@ -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<titel>.*)\s+\(S(?P<season>\d+)/E(?P<episod>\d+)\)$
|
||||||
|
|
||||||
|
[Hubert ohne Staller]
|
||||||
|
rss = https://mediathekviewweb.de/feed?query=%23Hubert%2Bohne%2BStaller%20!ARD
|
||||||
|
languages = ger
|
||||||
|
ger = ^(?P<titel>.*)\s+\(S(?P<season>\d+)/E(?P<episod>\d+)\)$
|
||||||
|
|
||||||
|
[Doctor Who]
|
||||||
|
rss = https://mediathekviewweb.de/feed?query=%23Doctor%20!ARD
|
||||||
|
languages = ger,eng
|
||||||
|
ger = ^(?P<titel>.*)\s+\(S(?P<season>\d+)/E?(?P<episod>\d+|Weihnachtsspecial)\)$
|
||||||
|
eng = ^(?P<titel>.*)\s+\(S(?P<season>\d+)/E?(?P<episod>\d+|Weihnachtsspecial)\).*Originalversion.*$
|
||||||
10
docker-compose.yaml
Normal file
10
docker-compose.yaml
Normal file
|
|
@ -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"
|
||||||
143
main.py
Executable file
143
main.py
Executable file
|
|
@ -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()
|
||||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
feedparser==5.2.1
|
||||||
|
requests==2.24.0
|
||||||
|
pymkv==1.0.5
|
||||||
Loading…
Add table
Add a link
Reference in a new issue