#!/usr/bin/python
import string,os,time,sys,ogg
import ogg.vorbis

global_datadir="/tmp/oggcast"
global_playlist=global_datadir+"/playlist"
global_current=global_datadir+"/current"

class radio:
    def __init__(self,name="radio",
                 datadir=None,playlist=None,current=None):
        if not datadir: datadir=global_datadir
        if not playlist: playlist=global_playlist+"-"+name
        if not current: current=global_current+"-"+name
        self.name=name
        self.datadir=datadir
        self.playlist=playlist
        self.current=current
    def loadcurrent(self):
        try:
            currentsong,currentposition,currentstart,currentlength= \
                map(string.strip,open(self.current).readlines())
            self.currentsong=currentsong
            self.currentposition=int(currentposition)
            self.currentstart=float(currentstart)
            self.currentlength=float(currentlength)
        except:
            print "Invalid <current> file!"
            raise
    def initcurrent(self):
        pass
    def waitendofsong(self):
        debug("startwait")
        while time.time()-self.currentstart < 0.95*self.currentlength:
            time.sleep(1)
        debug("endwait")
    def sendheader(self):
        sys.stdout.write( "200 OK HTTP/1.0\n\n" )
    def handleclient(self):
        httprequest = raw_input()
        self.sendheader()
        while 1:
            self.loadcurrent()
            # first look if the song is over or almost over
            if time.time()-self.currentstart > 0.90*self.currentlength:
                self.nextsong()
                continue
            # if we are near the beginning of the song, stream it fully
            if time.time()-self.currentstart < 10:
                self.sendfull()
            else:
                self.sendpartial()
            self.waitendofsong()
    def sendfull(self):
        input=open(self.currentsong,"rb")
        while 1:
            data=input.read(4096)
            if not data: break
            sys.stdout.write(data)
    def sendpartial(self):
        f=open(self.currentsong,"rb")
        f.seek(0,2)
        f_size=f.tell()
        f.seek(0)
        # three header packets
        n,p,os=copy_packets(f,sys.stdout,None,3)
        # seek
        f.seek(int((time.time()-self.currentstart)/self.currentlength*f_size))
        n,p,os=copy_packets(f,sys.stdout,os)
    def nextsong(self):
        filename=self.current+"+"
        f=open(filename,"w")
        songs=open(self.playlist).readlines()
        nextposition=self.currentposition+1
        nextsong=songs[nextposition][:-1]
        song=ogg.vorbis.VorbisFile(nextsong)
        length=song.time_total(0)
        f.write("%s\n"%nextsong)
        f.write("%d\n"%nextposition)
        f.write("%f\n"%time.time())
        f.write("%f\n"%length)
        f.close()
        os.rename(filename,self.current)

############ taken as-is from oggtail.py #############
_debug=None
def debug(s):
    if _debug:
        sys.stderr.write(s)
        sys.stderr.write('\n')

def copy_packets(from_, to_, outstream=None, count=None, start_pageno=0, start_granulepos=0):
    """
    Copy 'count' packets from file 'from_' to file 'to_'.  If 'count'
    is None, copy the rest of 'from_'.  Use 'start_granulepos' as the
    initial granulepos tag for pages.  Bytes will be skipped in
    'from_' to sync, as needed, and copying will stop if a chain
    boundary is encountered.
    """

    # XXX: warn on desync, except at beginning?

    copied = 0
    written = 0
    pageno = start_pageno
    granule_offset = None

    instream = None

    # get it?  insync?  get it?  i'm so funny you can't stand it!
    insync = ogg.OggSyncState()

    while count == None or copied < count:
        b = from_.read(65536)           # size doesn't really matter
        if not b:
            break
        insync.bytesin(b)

        skipped = 1
        while skipped != 0:
            skipped, page = insync.pageseek()
            if skipped > 0:
                if instream and page.serialno() != serialno:
                    # we hit a chain boundary
                    break
                if not instream:
                    serialno = page.serialno()
                    instream = ogg.OggStreamState(serialno)
                    outstream = ogg.OggStreamState(serialno)
                page.pageno = pageno
                debug('*** %s' % page.pageno())
                pageno = pageno + 1
                instream.pagein(page)
                while count == None or copied < count:
                    p = instream.packetout()
                    if not p:
                        break
                    if p.granulepos != -1:
                        if granule_offset == None:
                            granule_offset = p.granulepos
                        p.granulepos = p.granulepos - granule_offset
                    debug('copied %s' % p)
                    outstream.packetin(p)
                    copied = copied + 1
                while 1:
                    pg = outstream.pageout()
                    if not pg:
                        break
                    debug('writing %s' % pg)
                    # FIX: should check success of write
                    written = written + pg.writeout(to_)
            elif skipped < 0:
                print 'skipped', -skipped, 'bytes'

    pg = outstream.flush()
    if pg:
        written = written + pg.writeout(to_)

    return (written, pageno - start_pageno, outstream)
    

        

                                                                    
#
# playlist = 1 filename per line
# current = line 1 = filename being played
#           line 2 = linenumber of the file in playlist (1st = 0)
#           line 3 = unix timestamp of start of song, eg 1342879769.432
#           line 4 = length of song in seconds (can be a float)

myRadio=radio()
myRadio.handleclient()
