Shell 将Shoutcast流延迟12小时(Linux/bash)

Shell 将Shoutcast流延迟12小时(Linux/bash),shell,audio-recording,shoutcast,Shell,Audio Recording,Shoutcast,我住在世界的另一边,远离我的家(现在是GMT+1,GMT+13是家),我想念我的老地面电台。它有一个Shoutcast流,我只想把它延迟12个小时,这样当我想听它的时候,它总是可用的,这样它的时区就会与我的时区同步 我设想这是一个在我的服务器主机上运行的脚本 一个简单的方法是在一个ringbuffer中分配足够的ram来存储整个12小时的延迟,并通过管道输入streamripper的输出。但是这个流是128kbps的mp3,这意味着(128/8)*60*60=~56MB/小时,或者整个12小时的

我住在世界的另一边,远离我的家(现在是GMT+1,GMT+13是家),我想念我的老地面电台。它有一个Shoutcast流,我只想把它延迟12个小时,这样当我想听它的时候,它总是可用的,这样它的时区就会与我的时区同步

我设想这是一个在我的服务器主机上运行的脚本

一个简单的方法是在一个ringbuffer中分配足够的ram来存储整个12小时的延迟,并通过管道输入streamripper的输出。但是这个流是128kbps的mp3,这意味着(128/8)*60*60=~56MB/小时,或者整个12小时的缓冲区是675MB,这实际上并不实用。另外,我可能不得不处理服务器主机在某个超时后停止进程的问题


那么,哪些策略可能是实际可行的呢?

为什么不使用Ripshout之类的流裂土器下载呢?

流裂土器是一种简单的方法,可能是正确的方法,但是如果你想用程序员的方法来做

  • 大多数开发机器都有相当多的RAM。你确定不能腾出675 MB吗
  • 与其将输出存储在缓冲区中,不如将其存储在一个或多个文件中,比如一次存储一个小时?(基本上,您将编写自己的流裂土器)
  • 如果可以容忍质量损失,请将流转换为较低的比特率

    • 为了回答我自己的问题,这里有一个脚本,它每30分钟作为cron作业启动一次。它将传入流以5分钟的块(或按文件uu秒设置)转储到特定目录。块边框与时钟同步,直到当前时间块结束时才开始写入,因此运行的cronjobs可以重叠,而不会使数据加倍或留下间隙。文件名为(历元时间%24小时内的秒数).str

      我还没有制作播放器,但计划是将输出目录设置为web可访问的位置,并编写一个本地运行的脚本,该脚本使用与此处相同的时间戳计算代码来顺序访问(12小时前的时间戳)。str,再次将它们固定在一起,然后在本地设置为shoutcast服务器。然后我就可以把我的音乐播放器指向它了

      编辑:具有超时和更好的错误条件检查的新版本,加上漂亮的日志文件。这是目前在我的(便宜的)共享网络主机上运行无故障,没有任何问题

      #/usr/bin/python
      导入时间
      导入URL库
      导入日期时间
      导入操作系统
      导入套接字
      #每个文件的秒数
      文件\u秒=300
      #跑30分钟
      运行时间=60*30
      #每个读取块的大小(字节)
      #16384=1秒
      块大小=16384
      最大超时=10
      #保存文件的位置
      OUTPUT_DIRECTORY=“dir/”
      #原始流的URL
      URL=”http://url/path:port"
      调试=真
      日志=无
      socket.setdefaulttimeout(10)
      类DatestampedWriter:
      #输出路径必须有尾随“/”
      定义初始化(自身、输出路径、运行秒):
      self.path=输出路径
      self.file=None
      #需要为-1以避免在0是真实时间戳时出现问题
      self.curr\u timestamp=-1
      self.running=False
      #在当前时间段的_结束_之前不要启动
      #因此,将初始时间戳计算为(现在+文件秒)
      self.initial\u timestamp=self.CalcTimestamp(文件\u秒)
      self.final_timestamp=self.CalcTimestamp(运行秒)
      如果调试:
      log=open(输出目录+“log”+str(self.initial\u时间戳)+“.txt”,“w”)
      log.write(“初始时间戳”+str(self.initial\u timestamp)+”,final“+str(self.final\u timestamp)+”(diff“+str(self.final\u timestamp-self.initial\u timestamp)+”)\n)
      self.log=log
      def关闭(自):
      如果self.file!=无:
      self.file.close()文件
      #写出buf
      #当我们应该停止时返回True
      def写入(自身,buf):
      #检查是否打开了正确的文件
      #获取时间戳
      timestamp=self.CalcTimestamp()
      如果不是自动运行:
      #我们应该开始吗?
      如果时间戳==self.initial\u时间戳:
      如果调试:
      self.log.write(“现在开始运行\n”)
      self.log.flush()
      self.running=True
      #我们应该打开一个新文件吗?
      如果自运行和时间戳!=self.curr\u时间戳:
      如果调试:
      self.log.write(“新时间戳”+str(时间戳)+“\n”)
      self.log.flush()
      #关闭旧文件
      如果(self.file!=无):
      self.file.close()文件
      #是时候停下来了?
      如果(self.curr\u timestamp==self.final\u timestamp):
      如果调试:
      self.log.write(“--停止时间\n”)
      self.log.flush()
      self.running=False
      返回真值
      #打开新文件
      filename=self.path+str(时间戳)+“.str”
      #如果不存在os.path.exists(文件名):
      self.file=打开(文件名为“w”)
      self.curr\u timestamp=int(时间戳)
      #其他:
      #哦
      #如果调试:
      #self.log.write(“尝试打开但失败,已存在\n”)
      #self.running=False
      #现在写入字节
      如果自动运行:
      #打印(“写入”+str(len(buf)))
      self.file.write(buf)
      返回错误
      def CalcTimestamp(自身,秒\u偏移=0):
      t=datetime.datetime.now()
      秒=time.mktime(t.timetuple())+秒偏移量
      #文件\u秒间隔,24小时天
      时间戳=秒-(秒%FILE\u秒)
      时间戳=时间戳%86400
      返回int(时间戳)
      writer=DatestampedWriter(输出目录,运行时间)
      writer_finished=False
      #在运行期间<(运行_
      
      #!/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()