def transcodeOneVideo(self, video):
   if video.already_transcoded or video in self.transcoded_files:
     logging.info("Already transcoded %s" % video.url)
     return False
   logging.info("Transcoding %s" % video.url)
   self._status["current_video"] = str(video)
   (result, transcoded_filename) = self.transcodeFile(video.local_filename)
   self._status["current_video"] = None
   del self._status["progress"]
   if self.must_die_event.isSet():
     return False
   if result:
     video.local_transcoded_filename = transcoded_filename
     video.already_transcoded = True
     # Inutile, mais on ne sait jamais...
     video.already_downloaded = True
     # On supprime le fichier flv, il ne sert plus a rien...
     os.remove(video.local_filename)
     # Si on est sur Darwin (OS X), signale le succes avec Growl
     if platform.system() == "Darwin":
       subprocess.call([findSettings()["growlnotify"],
                        "-n", "PodcastCanalPlus",
                        "-t", "Encodage Fini",
                        "-m", video.name])
     return True
   else:
     return False
 def transcodeFile(self, video_filename):
   waiting_videos_count = self.transcode_queue.qsize()
   handbrake_options = (
     "--preset '%s'" % findSettings()["transcode_preset"])
   handbrake_cli_path = findSettings()["handbrake"]
   command_template = "%s %s --input '%s' --output '%s'"
   transcoded_filename = ""
   try:
     transcoded_filename = os.path.basename(video_filename)
     transcoded_filename = transcoded_filename.replace(' ', '_')
     try:
       extension_index = transcoded_filename.rindex(".")
       transcoded_filename = transcoded_filename[:extension_index] + ".m4v"
     except ValueError, e:
       transcoded_filename = transcoded_filename[:-4] + ".m4v"
   except TypeError, e:
     pprint.pprint(video_filename)
     return (False, None)
  def getJsonString(self, ip_address):
    """Renvoie un flux JSON correspondant a une adresse IP du serveur donnee.

    L'adresse IP du serveur n'est pas fixe, car :
      - Le serveur peut en changer
      - Les differents clients peuvent acceder au serveur par une adresse
        differente. ex: acces par le reseau local et internet par NAT.

    Args:
      ip_address: L'adresse IP a preciser dans le flux, sous forme de string.
    """
    self.addAllAvailableVideos()
    podcast_address = findSettings()["podcast_address_template"] % ip_address
    base_url = "%s:%d/" % (podcast_address, findSettings()["podcast_port"])
    podcast_dict = {
      "info":
        {"title": self.title,
         "link": base_url + self.pipeline_name + ".xml",
         "description": self.description,
         "lastBuildDate": int(time.time()),
         },
      "items": [],
      }
    for (video, size, change_time) in self.podcasted_videos:
      filename = video.local_transcoded_filename
      url = base_url + filename.split('/')[-1]
      if video.description is not None:
        description = video.description
      else:
        description = filename
      change_datetime = datetime.datetime.fromtimestamp(change_time)
      if video.name is not None:
        name = video.name
      else:
        name = filename
      video_enclosure = RSS2.Enclosure(url, size, "video/x-m4v")
      item = {"title": name,
              "description": description,
              "video_url": url,
              "video_size": size,
              "mime": "video/x-m4v",
              "date": change_time,}
      podcast_dict["items"].append(item)
    return json.dumps(podcast_dict)
예제 #4
0
 def createPipelines(self):
   self.threads = []
   logging.info("Demarrage des pipelines...")
   # Recupere les infos sur ce qui a deja ete fait depuis le disque dur
   self.downloaded_files = SynchronizedSet('downloaded_videos')
   self.transcoded_files = SynchronizedSet('transcoded_videos')
   if self.options.verbose:
     # Affichage de ce qu'on a deja fait
     print "Downloaded Videos:"
     for video in self.downloaded_files:
       print video
       print "Transcoded Videos:"
     for video in self.transcoded_files:
       print video
   # Serveur HTTP - un seul pour tous les pipelines
   self.http_server = PodcastHttpServer(findSettings()["podcast_port"])
   self.httpd_thread = self.http_server.start()
   self.threads.append(self.httpd_thread)
   # Transcodeur - un seul pour tous les pipelines
   transcode_queue = Queue.Queue()
   for video in self.downloaded_files:
     if not video in self.transcoded_files:
       transcode_queue.put(video)
   self.transcode_thread = VideoTranscoder(transcode_queue,
                                           self.transcoded_files)
   self.transcode_thread.start()
   self.threads.append(self.transcode_thread)
   self.rss_feedserver = RssFeedHttpServer(findSettings()["podcast_port"] + 1)
   # Tous les pipelines
   for pipeline_name in self.pipeline_specs:
     logging.info("Pipeline " + pipeline_name)
     pipeline = PipelineFactory.createPipeline(
       pipeline_name, transcode_queue,
       self.pipeline_specs[pipeline_name]["input_spec"],
       self.pipeline_specs[pipeline_name]["podcast_spec"],
       self.downloaded_files, self.transcoded_files)
     pipeline_threads = pipeline.createThreads()
     self.threads += pipeline_threads
   # On cree enfin le thread sentinelle, qui va permettre de quitter
   # proprement en cas d'erreur.
   self.sentinel_thread = SentinelThread(self.threads)
  def getRssFeedString(self, ip_address):
    """Renvoie le Flux RSS correspondant a une adresse IP du serveur donnee.

    L'adresse IP du serveur n'est pas fixe, car :
      - Le serveur peut en changer
      - Les differents clients peuvent acceder au serveur par une adresse
        differente. ex: acces par le reseau local et internet par NAT.

    Args:
      ip_address: L'adresse IP a preciser dans le flux, sous forme de string.
    """
    self.addAllAvailableVideos()
    podcast_address = findSettings()["podcast_address_template"] % ip_address
    base_url = "%s:%d/" % (podcast_address, findSettings()["podcast_port"])
    rss_feed = RSS2.RSS2(
      title=self.title,
      link=base_url + self.pipeline_name + ".xml",
      description=self.description,
      lastBuildDate=datetime.datetime.now())
    for (video, size, change_time) in self.podcasted_videos:
      filename = video.local_transcoded_filename
      url = base_url + filename.split('/')[-1]
      if video.description is not None:
        description = video.description
      else:
        description = filename
      change_datetime = datetime.datetime.fromtimestamp(change_time)
      if video.name is not None:
        name = video.name
      else:
        name = filename
      video_enclosure = RSS2.Enclosure(url, size, "video/x-m4v")
      rss_item = RSS2.RSSItem(
        title=name,
        description=description,
        enclosure=video_enclosure,
        guid=RSS2.Guid(url),
        pubDate=change_datetime)
      rss_feed.items.append(rss_item)
    return rss_feed.to_xml()
예제 #6
0
def main():
  option_parser = NiceOptionParser()
  option_parser.add_option("--config", help="Fichier de configuration",
                           action="store", type="string", dest="config_file",
                           default=findSettings()["config_file"])
  option_parser.add_option("--verbose", help="Mode verbeux.",
                           action="store_true", dest="verbose", default=False)
  (options, args) = option_parser.parse_args()

  if options.config_file is None or options.config_file == "":
    option_parser.print_usage()
    sys.exit(1)
  # On ne peut pas importer les modules avant car ils importent des
  # modules dependant de MyThread, dont la definition depend de la
  # valeur de with_gui, et qui est defini par initThreading().
  from user_config import PipelineFactory, ConfigParser, iTunesConfig
  config_parser = ConfigParser(options.config_file)
  pipeline_specs = config_parser.parse()
  # On fait le chdir apres avoir lu les parametres
  logging.info("chdir " + findSettings()["output_dir"])
  os.chdir(findSettings()["output_dir"])

  # On fait le menage
  downloaded_files = SynchronizedSet('downloaded_videos')
  transcoded_files = SynchronizedSet('transcoded_videos')
  deleteOldVideos(downloaded_files, transcoded_files)

  # Si on est sur Darwin (OS X), on dit poliment bonjour
  if platform.system() == "Darwin":
    subprocess.call([findSettings()["growlnotify"],
                     "-n", "PodcastCanalPlus",
                     "-t", "PodcastCanalPlus",
                     "-m", "Bonjour"])
  from cli import CliInterface
  cli_interface = CliInterface(options)
  os._exit(cli_interface.run())
 def downloadVideo(self, video):
   logging.info("Downloading Video: " + video.url)
   output_filename = findSettings()["output_dir"] +  video.url.split('/')[-1]
   command = self.__COMMAND_TEMPLATE % {"url": video.url,
                                        "output_filename": output_filename}
   waiting_videos_count = self.download_queue.qsize()
   return_code = 1
   retries = 0
   while return_code != 0 and retries < 5:
     logging.info("Launching flvstreamer")
     logging.info(command)
     p = subprocess.Popen(command, shell=True, stderr=subprocess.PIPE)
     standard_error = utils.UnbufferedReadline(p.stderr)
     for line in standard_error.readlines():
       # Si on doit mourrir, on le fait proprement
       if self.must_die_event.isSet():
         # Popen.terminate() n'existe pas avant python 2.6.
         # On recupere donc le pid pour lui envoyer un signal
         pid = p.pid
         import signal
         os.kill(pid, signal.SIGTERM)
         p.wait()
         return False
       # On cherche si le nombre de videos en attente a varie. Si c'est le cas
       # on notifie l'interface graphique du changement.
       if self.download_queue.qsize() != waiting_videos_count:
         waiting_videos_count = self.download_queue.qsize()
       m = re.search("^\d+\.\d+ kB / \d+.\d+ sec \((.*)\%\)", line)
       if m:
         value = float(m.group(1))
         self._status["percentage"] = value
     return_code = p.wait()
     retries += 1
     if return_code != 0:
       logging.info("flvstreamer: code de retour != 0 (%d). essai %d"
                    % (return_code, retries))
   if return_code == 0:
     video.already_downloaded = True
     video.local_filename = output_filename
     return True
   else:
     logging.warning("Impossible de telecharger %s" % video)
     return False
예제 #8
0
def iTunesConfig(pipeline_specs):
  """Utilise AppleScript pour configurer iTunes de facon automatique.
  """
  logging.info("Configuration d'iTunes.")
  script_template = """tell application "iTunes"
    subscribe "%s"
end
"""
  # Dans tous les cas, si on utilise le programme pour ajouter les podcasts
  # a iTunes, alors on est dans le cas ou le serveur est accessible par
  # localhost.
  base_address = "http://localhost:%d/" % findSettings()["podcast_port"]
  for pipeline_name in pipeline_specs:
    logging.info("\tPodcast: " + pipeline_name)
    pipeline_spec = pipeline_specs[pipeline_name]
    podcast_address = base_address + pipeline_spec["podcast_spec"]["filename"]
    script = script_template % podcast_address
    process = subprocess.Popen("osascript", shell=True, stdin=subprocess.PIPE)
    process.stdin.write(script)
    process.stdin.close()
    if process.wait() != 0:
      logging.warning("\tCode de retour != 0")