Journal Bref, j'ai fait un script python.

Posté par  (site web personnel, Mastodon) . Licence CC By‑SA.
Étiquettes :
24
20
nov.
2011

Bref c'était un dimanche et je moulais sur mon PC.

Je moulais tranquillement sur mes sites habituels – xhamster.com linuxfr.org, slashdot.org, quand tout à coup est apparu un journal sur LinuxFR : Bref, j'ai fait un flux atom de bref.

Alors j'me suis dit : "Bref, j'ai bien cette série, il faut que je fais un script pour récupérer directement les épisodes et que je les mets sur ma freebox". Je sais, je suis mauvais en conjugaison, mais il fallait quand même que je les mets sur ma freebox. Bref.

J'ai donc récupéré le flux Atom et j'ai tenté de parser le flux pour récupérer les url des épisodes en HD. La HD, c'est mieux, parce que t'as plus de pixel à l'écran, et vu que j'ai un projecteur dans mon salon, bah les flux HD c'est mieux. Bref.

Le problème, c'est que mon colloc avait pas croqué la pomme depuis 6 mois et que là il voulait que je vais lui acheter des capotes à la pharmacie. Et puis en fait, parser le flux Atom, c'était plus sioux que de parser directement le flux XML original, qui est utilisé dans le script de Luke SKy. Bref, j'ai décidé de parser directement le flux XML original.

Bon. A ce moment-là, j'avais juste le flux des vidéos en HD, mais j'en faisais rien. Il était déjà tard. Et c'est là que je me suis demandé comment télécharger les flux RTMP parce que moi je voulais télécharger les épisodes, pas les regarder en direct. Alors j'ai cherché un peu et j'ai découvert l'outil rtmpdump. J'ai regardé l'aide et j'ai trouvé que ça serait facile de télécharger un épisode à partir de son url rtmp:// :

/usr/bin/rtmpdump -r '<url_rtmp_de_l_episode>' -o '<chemin_du_fichier_de_sortie>'

Là, j'avais les fichiers sur mon serveur local, une Debian qui va bien et qui tourne sur du matériel plutôt économe en énergie : une petite Eee Box. Une tâche cron pour lancer la vérification toutes les 6h, pour pas louper un épisode :

# Verifier et telecharger les episodes de "bref" chaque jour a 3h, 9h, 15h et 21h
0        3,9,15,21          *             *               *           python /opt/perso/bref/dl.bref.py 2>&1 1>>/var/log/dl.bref.log

Il restait plus qu'à les pousser sur la freebox, pour pouvoir les mater ensuite en grand écran. J'ai donc décidé d'utiliser le client ftp en ligne de commande ncftpput :

ncftpput -u freebox -p <mon_mot_de_passe> <ip_de_la_freebox> '/Disque dur/Enregistrements/Bref/' <chemin_du_fichier_cree_avec_rtmpdump>

Bon, c'était nickel, ça marchait bien, mais je savais pas quand un nouvel épisode était disponible. Je récupérais les épisodes automatiquement, mais sans le savoir. Je perdais l'intérêt de l'automatisation. Bref.

J'ai décidé de m'envoyer des alertes parce que comme ça je saurais quand un nouvel épisode était disponible. J'ai décidé d'utiliser XMPP, parce que XMPP, c'est bien. Et puis j'ai fait ça en python parce que python c'est bien. Et j'ai fait deux trois bricoles pour que ça marche mieux, pour que ça ne recharge pas systématiquement les vidéos déjà téléchargées, j'ai fait quelques requêtes Xpath, j'ai rendu les fichiers accessibles par http et ftp, j'ai créé un utilisateur Bender sur mon serveur XMPP et j'ai fait beaucoup d'essais.

Bref, j'ai fait un script python.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# * parser le xml pour récupérer les url HD
# * extraire le nom du fichier (après le dernier "/")
# * vérifier que le fichier n'existe pas
# * le sauvegarder

lsXmlFeedUrl = "http://www.canalplus.fr/rest/bootstrap.php?/bigplayer/search/bref"
lsSaveFolder = "/opt/perso/bref/episodes"

lsXmppSmsList = "{{mon_jid}}" # exemple : bender@monserveurxmpp.net
lsXmppSmsMessageTemplate = "Un nouvel épisode de 'Bref' est disponible : http://{{ip_du_serveur_download}}/perso/bref/%s"

import libxml2
import os.path
import subprocess
import xmpp

class Episode(object):

  def __init__(self, psUrl, psFilename, pbLocallyAvailable = False):
    self._sUrl      = psUrl
    self._sFilename = psFilename
    self._bLocallyAvailable = pbLocallyAvailable

  def getName(self):
    return self._sFilename

  def getFilename(self):
    return self._sFilename

  def getFilePath(self, psPathRoot = ""):
    lsFilePath = self._sFilename
    if psPathRoot!="":
      lsFilePath = "%s/%s" % (psPathRoot, lsFilePath)
    return lsFilePath

  def getUrl(self):
    return self._sUrl


class XmppShortMessageService(object):
  def __init__(self, psXmppServerName, psXmppServerPort, psXmppSenderUserName, psXmppSenderUserPass, psXmppSenderUserRsc):
    self._sXmppServerName = psXmppServerName
    self._sXmppServerPort = psXmppServerPort
    
    self._sXmppSenderUserName = psXmppSenderUserName
    self._sXmppSenderUserPass = psXmppSenderUserPass
    self._sXmppSenderUserRsc  = psXmppSenderUserRsc
    
    self._oXmppClient = None
    
  def connect(self):
    if self._oXmppClient==None:
      self._oXmppClient = xmpp.Client(self._sXmppServerName)
      self._oXmppClient.connect(server=(self._sXmppServerName, self._sXmppServerPort))
      self._oXmppClient.auth(self._sXmppSenderUserName, self._sXmppSenderUserPass, self._sXmppSenderUserRsc)
      self._oXmppClient.sendInitPresence()
    
  def send(self, psMsgText, psMsgTo):
    self.connect()
    lsXmppMessage = xmpp.Message(psMsgTo, psMsgText)
    lsXmppMessage.setAttr('type', 'chat')
    self._oXmppClient.send(lsXmppMessage)


if __name__ == '__main__':
  # 1. download XML
  # 2. search episode urls
  # 3. check if the episodes found are already downloaded
  # 4. download required files
  
  # STEP 1
  loXmlStreamDom  = libxml2.parseFile(lsXmlFeedUrl)
  loVideoElements = loXmlStreamDom.xpathEval('//VIDEO[./RUBRIQUAGE/RUBRIQUE="BREF"]')

  loEpisodeList = list()
  
  for loVideoElement in loVideoElements:
    loHdVideoFileUrl = loVideoElement.xpathEval('./MEDIA/VIDEOS/HD')[0] # there is only one HD

    lsHdVideoFileUrl  = loHdVideoFileUrl.content.strip()
    lsHdVideoFileName = lsHdVideoFileUrl.rsplit("/", 1)[1]
    
    loNewEpisode = Episode(lsHdVideoFileUrl, lsHdVideoFileName, False)
    loEpisodeList.append(loNewEpisode)
    print "Found Episode: %s" % (loNewEpisode.getName())
    
  for loEpisode in loEpisodeList:
    lsLocalFilePath = "%s/%s" % (lsSaveFolder, loEpisode.getFilename())
    if os.path.isfile(lsLocalFilePath)==True:
      print "Skipping %s [already downloaded]" % loEpisode.getName()
    else:
      lsCmd  = "/usr/bin/rtmpdump -r \"%s\" -o \"%s\"" % (loEpisode.getUrl(), loEpisode.getFilePath(lsSaveFolder))
      print "Download %s" % (loEpisode.getName())
      os.system(lsCmd)

      lsCmd  = "ncftpput -u freebox -p {{freebox_ftp_password}} {{freebox_ip}} '/Disque dur/Enregistrements/Bref/' %s" % (loEpisode.getFilePath(lsSaveFolder))
      print "Upload to freebox: %s" % (loEpisode.getName())
      os.system(lsCmd)


      # Send an XMPP notification including a link to see the episode
      loXmppSms = XmppShortMessageService('{{mon_server_xmpp}}', '5223', 'bender', 'mynameisbender', 'BrefDownloadBot')
      loXmppSms.send(lsXmppSmsMessageTemplate % (loEpisode.getFilename(), loEpisode.getFilename()), lsXmppSmsList)

  • # c'est long

    Posté par  . Évalué à 10.

    pour quelque chose de BREF, c'est un peu LONG :p

  • # Encodage

    Posté par  (site web personnel) . Évalué à 4.

    Sympa ;-)

    Après, ça serait bien de les réencoder automatiquement vers un format lisible par le navigateur. À l'époque j'avais fait un script pour ça (en parallélisant au maximum la récupération des épisodes et leur encodage), pour créer des OGV (Ogg Theora) :
    http://blog.rom1v.com/2010/04/aggreger-differentes-sources-de-vod-en-oggtheora/

    Il faudrait un tout petit peu l'adapter pour encoder en WEBM.

    blog.rom1v.com

    • [^] # Re: Encodage

      Posté par  (site web personnel, Mastodon) . Évalué à 2.

      Oui ça serait bien. Un autre truc qui serait bien, et pour lequel je n'ai pas trouvé les outils adéquats (mais je pense que je n'ai pas suffisamment cherché), c'est ajouter des méta-données à partir de l'information trouvée dans le flux XML.

      Par défaut les vidéos n'ont pas de titre, pas d'auteur, rien.

  • # XPath

    Posté par  (site web personnel) . Évalué à 3.

    T'as pas dit que XPath c'était bien.
    Doit-on en déduire que XPath, c'est mal, mais que t'as pas osé écrire un journal qui dénonce ?

  • # Thanks...

    Posté par  (site web personnel) . Évalué à 1.

    Merci pour ce petit script...

    Je vais l'adapter pour garder les épisodes en local (pas d'upload vers la FBX) et, tant qu'à faire, nommer les fichiers en fonction du titre de l'épisode (plutôt que BREF_EPISODES_110920_CAN_205901_video_HD.mp4 qui est plutôt moche).

    Ensuite on verra pour convertir ces fichiers dans un autre format :).

    • [^] # Re: Thanks...

      Posté par  (site web personnel, Mastodon) . Évalué à 1.

      J'y ai pensé, mais je me suis dit que potentiellement il y aurait des apostrophes, des virgules, etc dans les titres, et donc j'avais simplement cherché à mettre des métadonnées. Ceci dit, je suis preneur de ta modification...

      Peut-être devrais-je mettre mon script sur bitbucket ?

      • [^] # Re: Thanks...

        Posté par  (site web personnel) . Évalué à 1.

        Ben c'est un peu (beaucoup) gruik et le code n'est pas aussi clean que le tiens mais bon... Ça marche :).

        Après exécution ça me donne ça:

        $ ls -gGh *.mp4
        -rw-r--r-- 1 7.6M Nov 21 12:17 20110827-Bref.mp4
        -rw-r--r-- 1  19M Nov 21 12:17 20110829-Bref. J'ai dragué cette fille.mp4
        -rw-r--r-- 1  19M Nov 21 12:16 20110830-Bref. Je remets tout à demain.mp4
        -rw-r--r-- 1  19M Nov 21 12:16 20110901-Bref. Je me suis préparé pour un rendez-vous.mp4
        -rw-r--r-- 1  19M Nov 21 12:16 20110905-Bref. J'ai passé un entretien d'embauche.mp4
        -rw-r--r-- 1  18M Nov 21 12:16 20110906-Bref. J'ai fait un repas de famille.mp4
        -rw-r--r-- 1  18M Nov 21 12:15 20110908-Bref. J'ai traîné sur internet.mp4
        -rw-r--r-- 1  20M Nov 21 12:15 20110912-Bref. Je joue de la guitare.mp4
        -rw-r--r-- 1  18M Nov 21 12:15 20110913-Bref. J'ai recroisé cette fille.mp4
        -rw-r--r-- 1  19M Nov 21 12:15 20110913-Bref. J'ai vu un psy.mp4
        -rw-r--r-- 1  19M Nov 21 12:14 20110919-Bref. J'ai un plan cul régulier - version non censurée.mp4
        -rw-r--r-- 1  16M Nov 21 12:14 20110920-Bref. Je suis comme tout le monde.mp4
        -rw-r--r-- 1  20M Nov 21 12:14 20110922-Bref. J'ai eu un job.mp4
        -rw-r--r-- 1  19M Nov 21 12:14 20110926-Bref. J'étais coincé dans l'ascenseur.mp4
        -rw-r--r-- 1  19M Nov 21 12:13 20110927-Bref. Mes parents divorcent.mp4
        -rw-r--r-- 1  19M Nov 21 12:13 20110929-Bref. J'ai fait les courses avec mon frère.mp4
        -rw-r--r-- 1  20M Nov 21 12:13 20111003-Bref. Je me suis bourré la gueule.mp4
        -rw-r--r-- 1  18M Nov 21 12:13 20111004-Bref. Je suis allé à ce mariage.mp4
        -rw-r--r-- 1  14M Nov 21 12:12 20111005-Bref. J'ai couché avec une flic.mp4
        -rw-r--r-- 1  21M Nov 21 12:12 20111010-Bref. Mon pote s'est fait larguer.mp4
        -rw-r--r-- 1  18M Nov 21 12:11 20111012-Bref. J'ai eu 47 minutes de retard.mp4
        -rw-r--r-- 1  21M Nov 21 12:11 20111014-Bref, mon pote s'est fait larguer.mp4
        -rw-r--r-- 1  14M Nov 21 12:11 20111014-Bref. Je suis allé au supermarché.mp4
        -rw-r--r-- 1  20M Nov 21 12:11 20111020-Bref. On a enterré Croquette.mp4
        -rw-r--r-- 1  20M Nov 21 12:10 20111021-Bref. Mon coloc a fait l'amour.mp4
        -rw-r--r-- 1  22M Nov 21 12:10 20111031-Bref. J'ai fait un rêve.mp4
        -rw-r--r-- 1  20M Nov 21 12:10 20111103-Bref. J'ai fait un dépistage.mp4
        -rw-r--r-- 1  16M Nov 21 12:10 20111110-Bref. J'ai un pote à conditions générales.mp4
        -rw-r--r-- 1  18M Nov 21 12:09 20111114-Bref. J'ai recouché avec mon ex.mp4
        -rw-r--r-- 1  17M Nov 21 12:09 20111116-Bref. J'aime bien cette photo.mp4
        
        
        • [^] # Re: Thanks...

          Posté par  (site web personnel, Mastodon) . Évalué à 1.

          J'ai regardé ton script, et je me pose la question des noms de fichier. Je n'aime pas le fait de mettre des caractères "non-conventionnels" dans les noms de fichier : guillemets, apostrophes, etc, etc.

          Je me posais la question : existe-t-il une lib' ou une technique ou n'importe quoi pour convertir des chaînes quelconque en leur version "filename friendly" ? Par exemple qui convertirait Mon coloc a fait l'amour en Mon coloc a fait l-amour ; et qui marcherait pour tous les caractères un peu "sioux" ?

          Quelqu'un connait ça ?

          • [^] # Re: Thanks...

            Posté par  (site web personnel) . Évalué à 2.

            Ben, à part pour "/", je ne crois pas qu'il existe des caractères "non conventionnels" pour nommer un fichier sous Linux :).

            Personnellement, cela ne me dérange pas ainsi mais j'imagine qu'il doit être facile d'écrire une petite fonction qui ferait le boulot à ta place !

            • [^] # Re: Thanks...

              Posté par  (site web personnel, Mastodon) . Évalué à 1.

              Ben, à part pour "/", je ne crois pas qu'il existe des caractères "non conventionnels" pour nommer un fichier sous Linux :).

              Hm... en fait je suis excessivement partisan de l'interopérabilité.

              Personnellement, cela ne me dérange pas ainsi mais j'imagine qu'il doit être facile d'écrire une petite fonction qui ferait le boulot à ta place !

              Tout à fait ; je me disais que j'étais probablement pas le premier à me poser la question (et donc à chercher une réponse). En fait mon idée, c'est de convertir en un nom de fichier ascii avec les caractères qui vont bien uniquement (en gros, chiffres, lettres, - _ et hm... voilà quoi).

              Le hic, c'est les caractères avec accents, etc, etc. C'est pas tant qu'ils soient non supportés, d'ailleurs, mais qu'ils sont transformés en des signes cabalistiques, parfois, quand tu changes d'encodage d'un système de fichier à un autre.

              • [^] # Re: Thanks...

                Posté par  . Évalué à 1.

                echo -n $FILENAME|tr -cd '[:print:]'

                En python :
                > a = 'Téléchargements'
                > u = a.decode('utf-8')
                > u.encode('ascii', 'ignore')
                : 'Tlchargements'

                Après pour éliminer les accents, je crois qu’il n’y a pas le choix faut construire un table de correspondance.

  • # weboob

    Posté par  . Évalué à 6.

    Dommage, pour seulement 10 fois plus de code et un effort 20 fois plus important, tu aurais pu faire une nouvelle application weboob de notifications de disponibilité de nouveaux épisodes de séries quelconque de canalplus, en utilisant et éventuellement améliorant le backend canalplus existant. Cette application fonctionnerait au passage pour des canaux youtube ou sur d'autres plateformes de vidéo (moyennant quelques éventuelles modifications des backends concernés).

    Tu n'aurais alors eu plus qu'à coder une nouvelle application pour envoyer chaque épisode d'une liste de séries choisies parmi celles disponible directement sur un serveur ftp/sftp/loltp quand il est disponible. Avec une sélection de 'séries'/'canaux'/... commune avec l'application de notifications.

  • # tu peux faire plus générique

    Posté par  . Évalué à 3.

    généralement la freebox a son ip disponnible sous mafreebox.freebox.fr évidemment si tu utilises des serveurs dns externe ça peut poser problème ;)

    Il ne faut pas décorner les boeufs avant d'avoir semé le vent

    • [^] # Re: tu peux faire plus générique

      Posté par  . Évalué à 3.

      évidemment si tu utilises des serveurs dns externe ça peut poser problème

      Non, car quel que soit ton DNS, mafreebox.freebox.fr pointe sur 212.27.38.253. C’est cette IP qui est routée en interne par la freebox sur elle-même.

      • [^] # Re: tu peux faire plus générique

        Posté par  (site web personnel) . Évalué à 2.

        Sûr ?
        Car chez moi ça ne donne pas cette adresse IP (j'ai le même préfixe 212.27 mais le reste est différent).

        Python 3 - Apprendre à programmer dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

  • # Moi j'aime pas Bref

    Posté par  . Évalué à 2.

    J'aime pas Bref, par contre j'aime les Guignols, Groland ou encore Action Discrète. Et j'imagine que ce script peut être adapté à d'autres programmes. Donc c'est plutôt intéressant ! Par contre, le site de Canal+ change souvent de façon d'accéder à ses vidéos pour justement empêcher ce genre de petit hack, des logiciels un peu plus anciens en avaient déjà fait les frais...

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.