Enregistreur de télévision numérique en ligne de commande

24 janvier 2010 - Mots-clés : Arch Linux TVnum

Pourquoi écrire un enregistreur de télévision numérique, même rudimentaire ?

Abonné chez Free depuis de janvier 2009 à octobre 2014, je programmais des enregistements pour les regarder plus tard. Pour graver les émission enregistrées sur DVD, je transférais par FTP sur mon PC et je me suis rendu compte que les enregistrements de certaines chaînes gratuites étaient chiffrés. Comme par hasard, les chaînes en question ne sont pas des chaînes du service public. Comme je n'ai pas envie d'être pris en otage pour d'obscures raisons (le signal n'est pas embrouillé alors pourquoi les enregistrements sont ils chiffrés ?), j'ai décidé d'enregistrer la TNT à partir d'un bête PC équipé d'une clé USB de réception de la TNT.

Installation des packages

# pacman -S linuxtv-dvb-apps

Paramètrage de l'enregistreur

  • fichier de tuning initial

A la suite du passage au tout numérique de la TNT en France, la plupart des fichiers de tuning initiaux (les fichiers fr-*) sont devenus obsolètes et ont été retirés du package dvb-apps. Je vous propose ma méthode pour reconstituer celui de votre région.

Tout d'abord, il faut connaître les nouveaux numéros de canaux applicables à votre région. Un canal est nombre qui représente de manière simple la fréquence d'un multiplex. Pour Paris, ce sont les canaux 22, 25, 28, 30, 32, 33, 35, 42 et 58. A partir du numéro de canal, vous calculez la fréquence à l'aide de cette formule :

Fréquence = (306 + Numéro de canal * 8) * 1000000

Par exemple pour le canal 22, cela donne 482000000 Hz (soit 482 Mhz). Si vous n'avez pas de calculette sous la main, ce document vous donnera les correspondances numéro de canal <-> fréquence.

Ensuite, il n'y a plus qu'à insérer une ligne par canal dans le fichier de tuning initial. Comme ceci :

# Paris - France - various DVB-T transmitters
# contributed by Franck Barbenoire <fbarbenoire@yahoo.fr>
# Canaux Paris - Tour Eiffel (post 8 mars 2011) : 22 25 28 30 32 33 35 42 58
# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy
T 482000000 8MHz AUTO AUTO QAM64 8k 1/32 NONE
T 482000000 8MHz AUTO AUTO QAM64 8k 1/32 NONE
T 506000000 8MHz AUTO AUTO QAM64 8k 1/32 NONE
T 530000000 8MHz AUTO AUTO QAM64 8k 1/32 NONE
T 546000000 8MHz AUTO AUTO QAM64 8k 1/32 NONE
T 562000000 8MHz AUTO AUTO QAM64 8k 1/32 NONE
T 570000000 8MHz AUTO AUTO QAM64 8k 1/32 NONE
T 586000000 8MHz AUTO AUTO QAM64 8k 1/32 NONE
T 642000000 8MHz AUTO AUTO QAM64 8k 1/32 NONE
T 770000000 8MHz AUTO AUTO QAM64 8k 1/32 NONE

Le répertoire dédié à ces fichiers est /usr/share/dvb/dvb-t. Je sauvegarde le mien en tant que /usr/share/dvb/dvb-t/fr-Paris.

  • chaînes

Pour déterminer les chaînes disponibles, faites ces manipulations en tant que root :

# cd /etc
# scan /usr/share/dvb/dvb-t/fr-Paris > channels.conf

Choisissez le fichier de tuning initial en fonction de votre région (fr-Paris ici). La première colonne du fichier channels.conf contient la listes des chaînes détectées.

Pour la Paris - Tour Eiffel cela donne ceci après toilettage et suppression des chaînes payantes :

CANAL+:482000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:170:120:769
D8:506000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:120:130:513
BFM TV:506000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:320:330:515
i>TELE:506000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:420:430:516
D17:506000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:520:530:517
Gulli:506000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:620:630:518
France 4:506000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:720:730:519
M6:546000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:120:130:1025
W9:546000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:220:230:1026
NT1:546000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:320:330:1027
TF1:562000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:120:130:1537
NRJ12:562000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:220:230:1538
TMC:562000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:620:630:1542
ARTE:562000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:720:730:1543
Canal 31:570000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:3521:3641:2050
IDF1:570000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:3522:3642:2051
NRJ Paris:570000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:3523:3643:2052
BFM Business Paris:570000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:3524:3644:2053
France 2:586000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:120:130:257
France 5:586000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:320:330:260
France Ô:586000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:520:530:261
LCP:586000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:620:630:262
France 3:586000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:220:230:273
HD1:642000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:120:0:2561
L'Equipe 21:642000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:220:0:2562
Chérie 25:642000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:320:0:2563
6ter:770000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:120:0:2817
NUMERO 23:770000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_AUTO:FEC_AUTO:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_32:HIERARCHY_NONE:220:0:2818
  • extinction

Vous allez vous autoriser à lancer la commande shutdown. Il faut modifier le fichier /etc/sudoers par la commande visudo:

# visudo

Insérez les lignes suivantes à la fin du fichier :

user machine=NOPASSWD:/usr/bin/shutdown

Vous devez substituer user et machine avec vos valeurs. user est votre nom d'utilisateur. machine est le nom de votre machine, il peut être obtenu par la commande :

# uname -n
  • démarrage automatique de la machine

Pour que la machine démarre peu avant le début d'un enregistrement, suivez les instructions de cet article.

Script

Voici le script magneto.py :

#!/usr/bin/python2
# -*- coding: utf-8 -*-

import argparse
from dateutil.parser import parse
import os
from subprocess import Popen, PIPE
import sys
import time
# import pdb; pdb.set_trace()


def verifications():
    if not os.path.exists('/etc/channels.conf'):
        print('Fichier /etc/channels.conf non trouvé')
        return False
    for p in ['linuxtv-dvb-apps']:
        process = Popen(['pacman', '-Qi', p], stdout=PIPE)
        stdout, stderr = process.communicate()
        if len(stdout.splitlines()) == 1:
            print('package %s non trouvé' % p)
            return False
    return True


def lire_chaines():
    fichier = open('/etc/channels.conf', 'r')
    lignes = fichier.readlines()
    chaines = [c.split(':')[0] for c in lignes]
    fichier.close()
    return chaines


def convert_date(str):
    try:
        date = parse(str)
    except:
        print('date invalide %s' % str)
        sys.exit(1)
    return time.mktime(date.timetuple())

if __name__ == "__main__":
    if verifications():
        parser = argparse.ArgumentParser()
        parser.add_argument("-i", "--immediat", help="enregistrement immédiat",
                            action="store_true")
        args = parser.parse_args()

        chaines = lire_chaines()
        if not args.immediat:
            debut = raw_input( 'Heure de début : ')
            date_debut = convert_date(debut)
            if date_debut <= time.time():
                print('Trop tard !')
                sys.exit(1)
        fin = raw_input( 'Heure de fin : ')
        date_fin = convert_date(fin)
        chaine = raw_input( 'Chaîne : ')
        if chaine not in chaines:
            print('Chaîne inconnue')
            sys.exit(1)
        emission = raw_input( 'Emission : ').replace(' ', '_')
        extinction = raw_input( 'Extinction à la fin de l\'enregistrement (o/n) : ')
        if extinction.upper() not in ['O', 'N']:
            print('Mauvais choix')
            sys.exit(1)
        extinction = extinction.upper() == 'O'

        if args.immediat:
            date_debut = time.time()
        duree = date_fin - date_debut
        if duree <= 0:
            print('L\'heure de fin doit être postérieure à l\'heure de début')
            sys.exit(1)

        if not args.immediat:
            while time.time() - date_debut < 0:
                time.sleep(2.0)

        process = Popen(['gnutv', '-timeout', str(duree), '-out', 'file',
                         emission + '.ts', chaine], stdout=PIPE)
        stdout, stderr = process.communicate()

        if extinction:
            process = Popen(['sudo', 'shutdown'], stdout=PIPE)
            stdout, stderr = process.communicate()

        print('Fin')

Session d'utilisation :

$ magneto.py
Heure de début : 23:33
Heure de fin : 23:37
Chaîne : TF1
Emission : test
Using frontend "DiBcom 7000PC", type DVB-T
status SCVYL | signal 865b | snr 00d8 | ber 001fffff | unc 00000000 | FE_HAS_LOCK
Fin

Conclusion

Je conviens que ce n'est pas très « user friendly » mais c'est efficace et fonctionne parfaitement bien. La principale difficulté consiste à faire démarrer la machine à une heure donnée.


Comments