Commit 8c4259c0 authored by Jonas's avatar Jonas

Ajout xmltv mafreebox.fr

parent ef7cd7fa
......@@ -62,6 +62,20 @@ $ ./autoremove_older.py --records-dir PATH
```
#### xmltv mafreebox.fr
Récupère le guide sur la freebox pour la liste des chaînes disponibles
dans hts.
- [Basé sur le travail de tr4ck3ur](https://mythtv-fr.org/forums/viewtopic.php?pid=25265#p25265)
- [Topic dédié](https://mythtv-fr.org/forums/viewtopic.php?pid=25270#p25270)
Exemple d'utilisation (voir --help pour plus d'options)
```
$ ./xmltv.mafreebox.fr.py > guide.xml
```
## Support
Gestionnaire de ticket gitlab ou irc (SnouF sur freenode, régulièrement
......
#!/usr/bin/env python
# -*- coding: utf8 -*-
# Copyright (c) 2016
# Jonas Fourquier (http://jonas.cloud) based on tr4ck3ur work
# https://mythtv-fr.org/forums/viewtopic.php?pid=25265#p25265
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
import sys, requests, argparse, time, logging
import xml.etree.ElementTree as ET
from datetime import datetime
from tzlocal import get_localzone
from xml.dom import minidom
from slugify import slugify
reload(sys)
sys.setdefaultencoding('utf8')
parser = argparse.ArgumentParser(description = "Création d'un xmltv pour hts tvheadend à partire de l'api mafreebox.fr")
parser.add_argument('--hts-url', default = 'http://localhost:9981/', help = 'url vers le serveur hts (http://localhost:9981 par défaut)')
parser.add_argument('--hts-user', default = 'admin', help = 'nom d\'utilisateur hts (admin par défaut)')
parser.add_argument('--hts-password', default = 'admin', help = 'mot de passe hts (admin par défaut)')
parser.add_argument("-v", "--verbose", dest="verbose", default="warning", help="Verbose (debug,info,warning,error) [info par defaut]")
args = parser.parse_args()
TZ = get_localzone()
B_URL = "http://mafreebox.freebox.fr"
BASE_URL = "http://mafreebox.freebox.fr/api/v3/"
CHANNEL_URL = BASE_URL + "tv/channels/"
ICON_URL = BASE_URL + "tv/img/channels/logos68x60/"
EPG_URL = BASE_URL + "tv/epg/by_channel/"
PROG_URL = BASE_URL + "tv/epg/programs/"
if args.verbose == 'debug' :
logLevel = logging.DEBUG
elif args.verbose == 'info' :
logLevel = logging.INFO
elif args.verbose == 'error' :
logLevel = logging.ERROR
else :
logLevel = logging.WARNING
logging.basicConfig(
format = "%(asctime)s %(levelname)s - %(message)s",
level = logLevel
)
categories_en = {
# Selon https://tvheadend.org/projects/tvheadend/repository/entry/src/epg.c
1: "Movie / Drama", # Film
2: "Movie / Drama", # Téléfilm
3: "Movie / Drama", # Série /Feuilleton
5: "Documentary",# Documentaire
10: "Magazines / Reports / Documentary", # Magazine
11: "Children's / Youth programmes", # Jeunesse
12: "Game show / Quiz / Contes", # Jeu
13: "Music / Ballet / Dance", # Musique
14: "Variety show", # Divertissement
16: "Cartoons / Puppets", # Dessins animés
19: "Sports", # Sport
20: "News / Current affairs", # Journal
22: "Discussion / Interview / Debate", # Débats
24: "Performing arts" # Spectacle
} #todo à compléter
def list_existing_chan():
list_chan = []
reponse = requests.get(
'%s/api/channel/list'%args.hts_url,
auth=(args.hts_user,args.hts_password)
)
if not reponse.ok :
logging.error ("Erreur de connection au serveur hts (erreur http code %i)"%reponse.status_code)
sys.exit(1)
for entrie in reponse.json()['entries'] :
list_chan.append(slugify(entrie['val']))
#~ list_chan = ['france-4'] # pour debug
return list_chan
def do_request(my_url):
logging.debug(my_url)
time.sleep(1)
response = requests.get(my_url)
out_dic = response.json()
while "msg" in out_dic and out_dic["msg"]=="Too many requests":
logging.info("Oups : trop que requètes, nouvelle tentative dans 60''")
time.sleep(60)
logging.info("Nouvelle tentative ..")
response = requests.get(my_url)
out_dic = response.json()
if "success" in out_dic and out_dic["success"]==False:
logging.error("Oups : %s"%response.text)
return out_dic.get('result',{})
#~ def prettify(elem):
#~ """Return a pretty-printed XML string for the Element.
#~ """
#~ rough_string = ET.tostring(elem, 'utf-8')
#~ reparsed = minidom.parseString(rough_string)
#~ return reparsed.toprettyxml(indent=" ", encoding='utf-8')
def start_time():
now=int(time.time())
return now-now%7200
def format_time(dt):
return datetime.strftime(
datetime.fromtimestamp(dt, tz=TZ),
"%Y%m%d%H%M%S %z")
def get_epg_data(root, channel_uid, channel_slug, start_time):
# Scan EPG to get program data
epg_progs = {}
for time_slot in range(10):
epgurl = EPG_URL + "%s/%s/" % (channel_uid, start_time+time_slot*7200)
dic_epg = do_request(epgurl)
logging.info("%d programes (%d au total)"%(len(dic_epg), len(epg_progs)))
# don't handle fake results
if len(dic_epg)==1:
for k,v in dic_epg.items():
if 'fake' in v:
return False
epg_progs.update(dic_epg)
for key, obj in epg_progs.iteritems():
cur_prog = ET.Element('programme')
cur_prog.attrib['start'] = format_time( obj.get('date'))
cur_prog.attrib['stop'] = format_time( obj.get('date') + obj.get('duration') )
cur_prog.attrib['channel'] = channel_slug
# Add Title
if 'title' in obj:
title = ET.Element('title')
title.text = obj.get('title')
cur_prog.append(title)
# Add Sub-Title
if 'sub_title' in obj:
subtitle = ET.Element('sub-title')
subtitle.text = obj.get('sub_title')
cur_prog.append(subtitle)
# Add Description
# desc = ET.Element('desc')
# desc.text = str( obj.get('desc') )
# Add category
if 'picture' in obj:
icon = ET.Element('icon')
icon.attrib['src'] = B_URL+obj.get('picture')
cur_prog.append(icon)
if 'episode_number' in obj and 'season_number' in obj:
episode = ET.Element('episode-num')
episode.attrib['system'] = "xmltv_ns"
episode.text="%d.%d.0" % (obj.get('season_number')-1,
obj.get('episode_number')-1)
cur_prog.append(episode)
if 'category_name' in obj:
if obj.get('category') in categories_en :
#traduction de la catégorie en anglais pour le filtre de hts
category_en = ET.Element('category')
category_en.attrib['lang'] = "en"
category_en.text = categories_en[obj.get('category')]
cur_prog.append(category_en)
else :
logging.warning("Traduction de la catégorie inconnue merci de rapporter le problème %d:\"%s\""%(obj.get('category'), obj.get('category_name')))
category_fr = ET.Element('category')
category_fr.attrib['lang'] = "fr"
category_fr.text = obj.get('category_name')
cur_prog.append(category_fr)
# Add length
if 'duration' in obj:
length = ET.Element('length')
length.attrib['units'] = "seconds"
length.text = str(obj.get('duration'))
cur_prog.append(length)
# check if need description depending category
if 'category' in obj and obj.get('category') not in [0, 5, 6, 9, 10, 12, 13, 14, 19, 20, 22, 31]:
# Ok let's check if we have a description
epg_id = obj.get('id')
prog_url = PROG_URL + "%s" % (epg_id)
dic_prog = do_request(prog_url)
if 'desc' in dic_prog:
desc = ET.Element('desc')
desc.attrib['lang'] = "fr"
desc.text = dic_prog.get('desc')
cur_prog.append(desc)
else:
logging.info("Pas de description trouvée")
#~ print dic_prog
root.append(cur_prog)
return True
def build_xml():
root = ET.Element('tv')
root.set('generator-info-name', 'snouf XMLTV generated')
root.set('generator-info-url', 'https://gitlab.com/snouf/tvheadend_tools')
dic_channels = do_request(CHANNEL_URL)
logging.info("%d chaines trouvée"%len(dic_channels))
list_existing = list_existing_chan()
start = start_time()
i=1
for chan_name, channel in dic_channels.iteritems():
if slugify(channel.get('name')) not in list_existing:
logging.info("%s pas dans la liste, saut"%channel.get('name'))
continue
if channel.get('available')==False:
continue
if channel.get('pub_service')==False:
continue
if channel.get('uuid').startswith('dvb'):
continue
if channel.get('name').startswith('C+'):
continue
channel_slug = slugify(channel.get('name')).lower()+'.mafreebox.fr'
cur_channel = ET.Element('tv')
cur_channel.attrib['id'] = channel_slug
icon_channel = ET.Element('icon')
icon_channel.attrib['src'] = B_URL + channel.get('logo_url')
disp_channel = ET.Element('display-name')
disp_channel.text = channel.get('name')
cur_channel.append(disp_channel)
cur_channel.append(icon_channel)
logging.debug("Récupération epg pour %s"%channel.get('name'))
res = get_epg_data(root,
channel.get('uuid'),
channel_slug,
start_time()+72000)
if res:
root.insert(1, cur_channel)
return ET.tostring(root)
if __name__ == '__main__':
sys.stdout.write(build_xml())
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment