Question

Je vis de chez moi à l'autre bout du monde (à l'heure GMT + 1, GMT + 13 est chez nous) et mon ancienne station de radio terrestre me manque. Il comporte un flux Shoutcast, et je voudrais simplement le retarder de 12 heures pour qu'il soit toujours disponible lorsque je souhaite l'écouter, de manière à ce que son fuseau horaire soit synchronisé avec mon fuseau horaire.

Je vois ceci comme un script exécuté sur l'hôte de mon serveur.

Une approche naïve consisterait simplement à allouer suffisamment de RAM dans un tampon annulaire pour stocker la totalité du délai de 12 heures et à canaliser la sortie de streamripper. Mais le flux est un fichier mp3 de 128 kbps, ce qui voudrait dire (128/8) * 60 * 60 = ~ 56 Mo par heure, ou 675 Mo pour la mémoire tampon de 12 heures, ce qui n’est pas vraiment pratique. De plus, je pourrais avoir à traiter avec mon serveur hôte juste en tuant le processus après un certain délai.

Alors, quelles sont les stratégies qui pourraient être réellement pratiques?

Était-ce utile?

La solution

Un ripper de flux serait le moyen le plus simple, et probablement le bon, mais si vous voulez le faire de la manière du programmeur ....

  • La plupart des machines de développement ont un peu de RAM. Êtes-vous sûr de ne pas pouvoir épargner 675 Mo?
  • Plutôt que de stocker la sortie dans une mémoire tampon, ne pouvez-vous pas la stocker dans un ou plusieurs fichiers, disons une heure à la fois? (essentiellement, vous écririez votre propre ripper de flux)
  • Convertissez le flux en un débit plus faible, si vous pouvez tolérer la perte de qualité

Autres conseils

Pourquoi ne le téléchargez-vous pas simplement avec un extracteur de flux tel que Ripshout ou quelque chose du genre?

Pour répondre à ma propre question, voici un script qui démarre sous forme de tâche cron toutes les 30 minutes. il vide le flux entrant par morceaux de 5 minutes (ou défini par FILE _ SECONDS) dans un répertoire particulier. Les bordures de bloc sont synchronisées sur l'horloge et l'écriture ne commence pas avant la fin du bloc d'heure en cours, de sorte que les tâches cron en cours peuvent se chevaucher sans doubler les données ni laisser d'espaces vides. les fichiers sont nommés comme (heure de la période% nombre de secondes sur 24 heures) .str.

Je n'ai pas encore créé de lecteur, mais l'objectif était de définir le répertoire de sortie sur un emplacement accessible sur le Web et d'écrire un script à exécuter localement, qui utilise le même code de calcul d'horodatage que celui utilisé ici pour un accès séquentiel (horodatage). Il y a 12 heures) .str, rassemblez-les à nouveau, puis configurez-les localement en tant que serveur shoutcast. alors je pourrais simplement pointer mon lecteur de musique sur http: // localhost: port et l'obtenir.

edit: Nouvelle version avec dépassement de délai et meilleure vérification des conditions d'erreur, ainsi qu'un fichier de journal sympa. cela fonctionne actuellement sans accroc sur mon hébergeur partagé (pas cher), sans aucun problème.

#!/usr/bin/python
import time
import urllib
import datetime
import os
import socket

# number of seconds for each file
FILE_SECONDS = 300

# run for 30 minutes
RUN_TIME = 60*30

# size in bytes of each read block
# 16384 = 1 second
BLOCK_SIZE = 16384

MAX_TIMEOUTS = 10

# where to save the files
OUTPUT_DIRECTORY = "dir/"
# URL for original stream
URL = "http://url/path:port"

debug = True
log = None
socket.setdefaulttimeout(10)

class DatestampedWriter:

    # output_path MUST have trailing '/'
    def __init__(self, output_path, run_seconds ):
        self.path = output_path
        self.file = None
        # needs to be -1 to avoid issue when 0 is a real timestamp
        self.curr_timestamp = -1
        self.running = False
        # don't start until the _end_ of the current time block
        # so calculate an initial timestamp as (now+FILE_SECONDS)
        self.initial_timestamp = self.CalcTimestamp( FILE_SECONDS )
        self.final_timestamp = self.CalcTimestamp( run_seconds )
        if debug:
            log = open(OUTPUT_DIRECTORY+"log_"+str(self.initial_timestamp)+".txt","w")
            log.write("initial timestamp "+str(self.initial_timestamp)+", final "+str(self.final_timestamp)+" (diff "+str(self.final_timestamp-self.initial_timestamp)+")\n")

        self.log = log

    def Shutdown(self):
        if self.file != None:
            self.file.close()

    # write out buf
    # returns True when we should stop
    def Write(self, buf):
        # check that we have the correct file open

        # get timestamp
        timestamp = self.CalcTimestamp()

        if not self.running :
            # should we start?
            if timestamp == self.initial_timestamp:
                if debug:
                    self.log.write( "starting running now\n" )
                    self.log.flush()
                self.running = True

        # should we open a new file?
        if self.running and timestamp != self.curr_timestamp:
            if debug:
                self.log.write( "new timestamp "+str(timestamp)+"\n" )
                self.log.flush()
            # close old file
            if ( self.file != None ):
                self.file.close()
            # time to stop?
            if ( self.curr_timestamp == self.final_timestamp ):
                if debug:
                    self.log.write( " -- time to stop\n" )
                    self.log.flush()
                self.running = False
                return True
            # open new file
            filename = self.path+str(timestamp)+".str"
            #if not os.path.exists(filename):
            self.file = open(filename, "w")
            self.curr_timestamp = int(timestamp)
            #else:
                # uh-oh
            #   if debug:
            #       self.log.write(" tried to open but failed, already there\n")
            #   self.running = False

        # now write bytes
        if self.running:
            #print("writing "+str(len(buf)))
            self.file.write( buf )

        return False

    def CalcTimestamp(self, seconds_offset=0):
        t = datetime.datetime.now()
        seconds = time.mktime(t.timetuple())+seconds_offset
        # FILE_SECONDS intervals, 24 hour days
        timestamp = seconds - ( seconds % FILE_SECONDS )
        timestamp = timestamp % 86400
        return int(timestamp)


writer = DatestampedWriter(OUTPUT_DIRECTORY, RUN_TIME)

writer_finished = False

# while been running for < (RUN_TIME + 5 minutes)
now = time.mktime(datetime.datetime.now().timetuple())
stop_time = now + RUN_TIME + 5*60
while not writer_finished and time.mktime(datetime.datetime.now().timetuple())<stop_time:

    now = time.mktime(datetime.datetime.now().timetuple())

    # open the stream
    if debug:
        writer.log.write("opening stream... "+str(now)+"/"+str(stop_time)+"\n")
        writer.log.flush()
    try:
        u = urllib.urlopen(URL)
    except socket.timeout:
        if debug:
            writer.log.write("timed out, sleeping 60 seconds\n")
            writer.log.flush()
        time.sleep(60)
        continue
    except IOError:
        if debug:
            writer.log.write("IOError, sleeping 60 seconds\n")
            writer.log.flush()
        time.sleep(60)
        continue
        # read 1 block of input
    buf = u.read(BLOCK_SIZE)

    timeouts = 0
    while len(buf) > 0 and not writer_finished and now<stop_time and timeouts<MAX_TIMEOUTS:
        # write to disc
        writer_finished = writer.Write(buf)

        # read 1 block of input
        try:
            buf = u.read(BLOCK_SIZE)
        except socket.timeout:
            # catch exception but do nothing about it
            if debug:
                writer.log.write("read timed out ("+str(timeouts)+")\n")
                writer.log.flush()
            timeouts = timeouts+1

        now = time.mktime(datetime.datetime.now().timetuple())
    # stream has closed,
    if debug:
        writer.log.write("read loop bailed out: timeouts "+str(timeouts)+", time "+str(now)+"\n")
        writer.log.flush()
    u.close();
    # sleep 1 second before trying to open the stream again
    time.sleep(1)

    now = time.mktime(datetime.datetime.now().timetuple())

writer.Shutdown()
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top