Pregunta

Estoy viviendo al otro lado del mundo desde mi casa (GMT + 1 ahora, GMT + 13 es mi hogar), y extraño mi vieja estación de radio terrestre. Tiene una transmisión Shoutcast, y me gustaría simplemente retrasarla 12 horas para que siempre esté disponible cuando quiera escucharla, de manera que su zona horaria esté sincronizada con mi zona horaria.

Imagino esto como un script que se ejecuta en el host de mi servidor.

Un enfoque ingenuo sería simplemente asignar suficiente carnero en un ringbuffer para almacenar todo el retraso de 12 horas, y conectar la salida del streamripper. Pero la transmisión es un mp3 de 128 kbps, lo que significaría (128/8) * 60 * 60 = ~ 56 MB por hora, o 675 MB para todo el búfer de 12 horas, lo que no es realmente tan práctico. Además, podría tener que lidiar con el host de mi servidor simplemente matando el proceso después de un cierto tiempo de espera.

Entonces, ¿cuáles son algunas estrategias que podrían ser prácticas?

¿Fue útil?

Solución

Un destripador de flujo sería la manera Fácil, y probablemente la manera Correcta, pero si quieres hacerlo de la manera Programadora ...

  • La mayoría de las máquinas de desarrollo tienen bastante RAM. ¿Estás SEGURO de que no puedes ahorrar 675 MB?
  • En lugar de almacenar el resultado en un búfer, ¿no puede almacenarlo en un archivo o archivos, por ejemplo, una hora a la vez? (esencialmente, estaría escribiendo su propio extractor de secuencias)
  • Convierta la transmisión a una tasa de bits más baja, si puede tolerar la pérdida de calidad

Otros consejos

¿Por qué no lo descarga con un destripador de flujo como Ripshout o algo así?

para responder mi propia pregunta, aquí hay un script que se inicia como un trabajo cron cada 30 minutos. descarga el flujo entrante en fragmentos de 5 minutos (o establecido por FILE _ SECONDS) en un directorio particular. los bordes de bloque se sincronizan con el reloj, y no comienza a escribir hasta el fin del fragmento de tiempo actual, por lo que los cronjobs en ejecución pueden superponerse sin duplicar datos o dejar espacios vacíos. los archivos se denominan como (tiempo de época% número de segundos en 24 horas) .str.

todavía no he creado un reproductor, pero el plan era establecer el directorio de salida en un lugar accesible desde la web, y escribir un script que se ejecute localmente que use el mismo código de cálculo de marca de tiempo que aquí para acceder secuencialmente (marca de tiempo Hace 12 horas) .str, vuélvalos a juntar nuevamente y luego configúrelos como un servidor de difusión local. entonces podría apuntar mi reproductor de música a http: // localhost: port y obtenerlo.

edit: Nueva versión con tiempos de espera y mejor comprobación de condiciones de error, además de un buen archivo de registro. actualmente se ejecuta sin problemas en mi webhost compartido (barato), sin problemas.

#!/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()
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top