Domanda

Vivo dall'altra parte del mondo da casa mia (GMT + 1 ora, GMT + 13 è casa) e mi manca la mia vecchia stazione radio terrestre. Ha un flusso Shoutcast e vorrei semplicemente ritardarlo di 12 ore in modo che sia sempre disponibile quando voglio ascoltarlo, in modo da rendere il suo fuso orario sincronizzato con il mio fuso orario.

Immagino che questo sia uno script in esecuzione sul mio host server.

Un approccio ingenuo sarebbe semplicemente quello di allocare abbastanza RAM in un Ringbuffer per memorizzare l'intero ritardo di 12 ore e convogliare l'output dallo streamripper. Ma lo stream è un mp3 da 128kbps, il che significherebbe (128/8) * 60 * 60 = ~ 56 MB all'ora, o 675 MB per l'intero buffer di 12 ore, il che non è poi così pratico. Inoltre, potrei avere a che fare con il mio host del server semplicemente uccidendo il processo dopo un certo timeout.

Quindi, quali sono alcune strategie che potrebbero essere effettivamente pratiche?

È stato utile?

Soluzione

Uno stream ripper sarebbe il modo semplice, e probabilmente il modo giusto, ma se vuoi farlo nel modo programmatore ....

  • La maggior parte delle macchine di sviluppo ha abbastanza RAM. Sei sicuro di non poter risparmiare 675 MB?
  • Invece di archiviare l'output in un buffer, non è possibile memorizzarlo in uno o più file, ad esempio un'ora alla volta? (in sostanza, dovresti scrivere il tuo stream ripper)
  • Converti lo stream in un bitrate più basso, se puoi tollerare la perdita di qualità

Altri suggerimenti

Perché non lo scarichi semplicemente con uno stream ripper come Ripshout o qualcosa del genere?

per rispondere alla mia domanda, ecco uno script che si avvia come cron job ogni 30 minuti. scarica il flusso in entrata in blocchi di 5 minuti (o impostato da FILE _ SECONDS) in una directory specifica. i bordi dei blocchi sono sincronizzati con l'orologio e non inizia a scrivere fino alla fine del fuso orario corrente, quindi i cronjob in esecuzione possono sovrapporsi senza raddoppiare i dati o lasciare spazi vuoti. i file sono denominati come (tempo% numero di secondi in 24 ore) .str.

Non ho ancora creato un lettore, ma il piano era di impostare la directory di output su un sito Web accessibile e scrivere uno script da eseguire localmente che utilizza lo stesso codice di calcolo data / ora qui per accedere in sequenza (data / ora 12 ore fa) .str, incollali di nuovo insieme, quindi configurali come server shoutcast a livello locale. quindi potrei semplicemente puntare il mio lettore musicale su http: // localhost: port e ottenerlo.

modifica: nuova versione con timeout e migliore controllo delle condizioni di errore, oltre a un bel file di registro. questo è attualmente in esecuzione senza intoppi sul mio (economico) webhost condiviso, senza problemi.

#!/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()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top