vielen Dank für Deine Arbeit, läuft top, vor allem mit den Magenta Daten.
Muss man für Waipu noch irgendwas einstellen, damit auch Sendungsbilder geladen werden?
Bei mir überhaupt keine da über Waipu (über Magenta schon).
vielen Dank für Deine Arbeit, läuft top, vor allem mit den Magenta Daten.
Muss man für Waipu noch irgendwas einstellen, damit auch Sendungsbilder geladen werden?
Bei mir überhaupt keine da über Waipu (über Magenta schon).
Mir ist beim Sichten des Codes auch noch etwas aufgefallen, bitte die epg.db löschen und den Code updaten, dann sollten die fehlenden Daten nach einem Neustart des Grabbers auftauchen.
[…]
Jetzt sind die Bilder da, super, vielen Dank!
Jetzt hab ich die Qual der Wahl, ob ich Magenta- oder Waipu-Daten besser finde
Ich freue mich sehr, dass easyepg zumindest noch basale Infos von TMS beziehen kann (danke!). Wenn man Sender nutzt, für die es keine anderen Daten gibt, ist dies wirklich sehr nützlich. Ich habe noch zwei Anregungen hierzu:
Für den Eigenbedarf habe ich mir gerade eine Python-Lösung mit Hilfe von Bing Copilot gestrickt, die nachträglich die generierte xmltv-Datei durch Daten von TMDB anreichert. Voraussetzung ist, dass die Sendung mindestens 85 Minuten ist (sonst eher kein Spielfilm) und entweder eine Beschreibung oder ein Rating (Stars in TVHeadend) fehlt.
Unten der Code. Vielleicht will hilft es auch anderen oder jemand will es als Grundlage für etwas besseres nutzen:
import xml.etree.ElementTree as ET
import requests
import time
import json
import os
import sys
TMDB_API_KEY = 'DEIN_TMDB_API_KEY_HIER'
TMDB_SEARCH_URL = 'https://api.themoviedb.org/3/search/movie'
TMDB_DETAILS_URL = 'https://api.themoviedb.org/3/movie/{}'
TMDB_RELEASE_URL = 'https://api.themoviedb.org/3/movie/{}/release_dates'
CACHE_FILE = 'tmdb_cache.json'
if len(sys.argv) != 3:
print("Usage: python xmltv-aufwerten.py <input.xml> <output.xml>")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, 'r', encoding='utf-8') as f:
cache = json.load(f)
else:
cache = {}
MAX_REQUESTS_PER_SECOND = 25
request_timestamps = []
def rate_limit():
now = time.time()
request_timestamps[:] = [t for t in request_timestamps if now - t < 1]
if len(request_timestamps) >= MAX_REQUESTS_PER_SECOND:
time.sleep(1 - (now - request_timestamps[0]))
request_timestamps.append(time.time())
def get_tmdb_info(title):
if title in cache:
return cache[title]
rate_limit()
params = {'api_key': TMDB_API_KEY, 'query': title, 'language': 'de'}
response = requests.get(TMDB_SEARCH_URL, params=params)
results = response.json().get('results')
if not results:
cache[title] = {}
return {}
movie_id = results[0]['id']
rate_limit()
details = requests.get(TMDB_DETAILS_URL.format(movie_id), params={'api_key': TMDB_API_KEY, 'language': 'de'}).json()
rate_limit()
release_data = requests.get(TMDB_RELEASE_URL.format(movie_id), params={'api_key': TMDB_API_KEY}).json()
fsk = ''
for entry in release_data.get('results', []):
if entry.get('iso_3166_1') == 'DE':
for rd in entry.get('release_dates', []):
if rd.get('certification'):
fsk = rd['certification']
break
if fsk:
break
genres = details.get('genres', [])
genre_names = [g['name'] for g in genres]
info = {
'overview': details.get('overview', ''),
'rating': details.get('vote_average', ''),
'poster': f"https://image.tmdb.org/t/p/w500{details.get('poster_path')}" if details.get('poster_path') else '',
'genres': genre_names,
'fsk': fsk,
'type': 'Movie' if details.get('release_date') else 'TV',
'season': details.get('season_number', ''),
'episode': details.get('episode_count', '')
}
cache[title] = info
return info
# Genre → Content Type Mapping
GENRE_TO_TYPE = {
'Dokumentation': 'Documentary',
'Animation': 'Children',
'Krimi': 'Crime',
'Thriller': 'Crime',
'Komödie': 'Comedy',
'Drama': 'Drama',
'Science Fiction': 'Sci-Fi',
'Fantasy': 'Sci-Fi',
'Abenteuer': 'Adventure',
'Horror': 'Horror',
'Musik': 'Music',
'Romantik': 'Romance'
}
def enrich_xmltv(xmltv_path, output_path):
tree = ET.parse(xmltv_path)
root = tree.getroot()
for programme in root.findall('programme'):
title_elem = programme.find('title')
if title_elem is None or not title_elem.text:
continue
title = title_elem.text.strip()
start = programme.get('start')
stop = programme.get('stop')
if not start or not stop or len(start) < 12 or len(stop) < 12:
continue
try:
start_time = time.strptime(start[:14], "%Y%m%d%H%M%S")
stop_time = time.strptime(stop[:14], "%Y%m%d%H%M%S")
duration_minutes = (time.mktime(stop_time) - time.mktime(start_time)) / 60
except Exception:
continue
if duration_minutes <= 85:
continue
desc_elem = programme.find('desc')
rating_elem = programme.find('star-rating')
needs_desc = desc_elem is None or not desc_elem.text or len(desc_elem.text.strip()) < 20
needs_rating = rating_elem is None
if not (needs_desc or needs_rating):
continue
info = get_tmdb_info(title)
if not info:
continue
# Beschreibung
if needs_desc and info['overview']:
if desc_elem is None:
desc_elem = ET.SubElement(programme, 'desc')
desc_elem.text = info['overview']
# Bewertung
if needs_rating and info['rating']:
percent = round(float(info['rating']) * 10)
star_elem = ET.SubElement(programme, 'star-rating')
star_elem.set('system', 'IMDb')
value_elem = ET.SubElement(star_elem, 'value')
value_elem.text = f"{percent}/100"
# Altersfreigabe
if info.get('fsk'):
fsk_elem = ET.SubElement(programme, 'rating')
fsk_elem.set('system', 'FSK')
value_elem = ET.SubElement(fsk_elem, 'value')
value_elem.text = info['fsk']
# Poster
if programme.find('icon') is None and info['poster']:
icon_elem = ET.SubElement(programme, 'icon')
icon_elem.set('src', info['poster'])
# Genre & Content Type
existing_categories = [c.text for c in programme.findall('category') if c.text]
for genre in info.get('genres', []):
if genre not in existing_categories:
cat_elem = ET.SubElement(programme, 'category')
cat_elem.text = genre
# Content Type Simulation
mapped_type = GENRE_TO_TYPE.get(genre)
if mapped_type and mapped_type not in existing_categories:
cat_elem = ET.SubElement(programme, 'category')
cat_elem.text = mapped_type
# Episode
if info.get('season') and info.get('episode'):
try:
season = int(info['season']) - 1
episode = int(info['episode']) - 1
ep_elem = ET.SubElement(programme, 'episode-num')
ep_elem.set('system', 'xmltv_ns')
ep_elem.text = f"{season}.{episode}.0"
except:
pass
tree.write(output_path, encoding='utf-8', xml_declaration=True)
with open(CACHE_FILE, 'w', encoding='utf-8') as f:
json.dump(cache, f, ensure_ascii=False, indent=2)
enrich_xmltv(input_file, output_file)
Alles anzeigen
Habe dann Sender über die Web Option versucht hinzuzufügen, das geht extrem langsam ca. 15s für einen Sender.
Ja, das ist mir auch aufgefallen. Wenn man auf das + klickt, dauert es eine Weile bis angezeigt wird, das der Sender hinzugefügt wurde.
Bei mir war das bei Waipu.
Ich habe hier einen Raspberry Pi 3 mit 64bit als Testreferenz:
Linux raspberrypi 6.6.20+rpt-rpi-v8 #1 SMP PREEMPT Debian 1:6.6.20-1+rpt1 (2024-03-07) aarch64 GNU/Linux
Ich kann die genannte Verzögerung nicht nachvollziehen, zumindest nicht, wenn es um das Hinzufügen eines einzelnen Kanals geht. Der Browser zeigt mir eine Zeit von 400-500ms an - vom Klick auf das "+"-Symbol bis zum Erscheinen der Notification, dass der Kanal hinzugefügt wurde. Ich bin hier weit von den 15s entfernt.
Nur das Hinzufügen mehrerer (aller) Kanäle habe ich soeben optimiert, dort betrug die Ladezeit abhängig von der Listengröße ggf. mehrere Sekunden.
Geil wäre dann noch das EPG von Zattoo irgendwann wenn Zeit ist, dann stell ich alles auf WEB um und gut ist. TMS Lizenz mindestens 1200 Euro im Jahr
dass man auch neue hinzufügen kann
hier haste nen backup was ich vor ne weile mal angelegt habe DACH mit sender infos, GB nur callid'S
dupes: 22148
new: 0
channels: 4509
Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!