changeset 792:351cac9512c1

no more use for the mpd version of ascoltami Ignore-this: 22417ff34602c86db64713e4978f73cd
author drewp@bigasterisk.com
date Sat, 30 Jun 2012 05:58:27 +0000
parents 44cad4444def
children cbfed4e684ef
files bin/ascoltami
diffstat 1 files changed, 0 insertions(+), 588 deletions(-) [+]
line wrap: on
line diff
--- a/bin/ascoltami	Mon Jun 18 04:51:25 2012 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,588 +0,0 @@
-#!bin/python
-
-# this is a fork from semprini/ascotalmi to use mpd
-
-""" a separate player program from Semprini.py. name means 'listen to
-me' in italian.
-
-features and limitations:
-
-  xmlrpc interface for:
-    getting the current time in the playing song
-    requesting what song is playing
-    saying what song should play
-
-
-setup:
-
-get mpd root path correct
-
-run mpd that's been modified to give precise times:
-  dash(pts/21):/my/dl/modified/mpd% src/mpd --no-daemon
-
-tell it to scan
-  mpc update
-  (mpc listall   to check)
-  
-run ascoltami
-
-
-todo:
-
-
-"""
-
-from __future__ import division,nested_scopes
-
-from optparse import OptionParser
-import sys, os,math,time, jsonlib
-from rdflib import URIRef
-import Tkinter as tk
-import logging
-log = logging.getLogger()
-log.setLevel(logging.INFO)
-
-from twisted.internet import reactor,tksupport
-from twisted.internet.error import CannotListenError
-from twisted.web import xmlrpc, server, resource
-
-import run_local
-from light9 import networking, showconfig, wavelength
-from light9.namespaces import L9, MUS
-from light9.uihelpers import toplevelat
-
-sys.path.append("/home/drewp/projects/pympd_0")
-sys.path.append("/my/proj/pympd_0")
-from pympd import Mpd
-
-appstyle={'fg':'white','bg':'black'}
-
-def shortSongPath(song, all):
-    prefixlen = len(os.path.commonprefix(all))
-    # include to the last os.sep- dont crop path elements in the middle
-    prefixlen = all[0].rfind(os.sep)+1 
-
-    return os.path.splitext(song[prefixlen:])[0]
-
-class RestApi(resource.Resource):
-    isLeaf = True
-    def __init__(self, player):
-        resource.Resource.__init__(self)
-        self.player = player
-    def render_GET(self, request):
-        if request.postpath == ['position']:
-            return jsonlib.write({"song" : self.player.song_uri,
-                                  "t" : self.player.smoothCurrentTime(),
-                                  "started" : self.player.playStartTime})
-
-class XMLRPCServe(xmlrpc.XMLRPC):
-    def __init__(self,player):
-        xmlrpc.XMLRPC.__init__(self)
-        self.player=player
-
-    def xmlrpc_echo(self,x):
-        return x
-
-    def xmlrpc_playfile(self,musicfilename):
-        self.player.play(musicfilename)
-        return 'ok'
-    def xmlrpc_stop(self):
-        self.player.state.set('stop')
-        return 'ok'
-    def xmlrpc_seek_to(self,t):
-        self.player.seek_to(t)
-        return 'ok'
-    def xmlrpc_seekplay_or_pause(self,t):
-        """either seek to t and play; or pause. this is the
-        curvecalc click-play interface"""
-        if self.player.state.get() == "play":
-            self.player.pause()
-            return 'paused'
-        else:
-            self.player.seek_to(t)
-            self.player.play()
-            return 'playing'
-    def xmlrpc_gettime(self):
-        """returns seconds from start of song"""
-        return float(self.player.smoothCurrentTime())
-    def xmlrpc_songlength(self):
-        """song length, in seconds"""
-        return float(self.player.total_time.get())
-
-    def xmlrpc_song_uri(self):
-        """song URI, or None"""
-        return self.player.song_uri.encode('utf8') or "No song"
-
-class Player:
-    """semprini-style access to mpd. in here is where we add the
-    padding"""
-
-    song_pad_time = 10
-    
-    def __init__(self):
-
-        self.mpd = Mpd()
-        args = (networking.mpdServer()+(self.mpd,))
-        log.info("connecting to %r", args)
-        reactor.connectTCP(*args)
-
-        self.state = tk.StringVar()
-        self.state.set("stop") # 'stop' 'pause' 'play'
-
-        self.current_time = tk.DoubleVar()
-        self.total_time = tk.DoubleVar()
-        self.filename_var = tk.StringVar()
-        self.song_uri = None
-        self.playStartTime = 0
-
-        self.pre_post_names = showconfig.prePostSong()
-
-        self.last_poll_time = None
-        self.last_autopause_time = None
-
-        self.pollStatus()
-
-        self.mpd_is_lying = False # mpd reports bad times in certain intervals
-
-    def smoothCurrentTime(self):
-        """like self.current_time.get, but more accurate"""
-
-        if self.last_poll_time and self.state.get() == 'play':
-            dt = time.time() - self.last_poll_time
-        else:
-            dt = 0
-        return self.current_time.get() + dt
-
-    def pollStatus(self):
-        if self.state.get() == 'stop':
-            self.current_time.set(-4)
-            
-        self.mpd.status().addCallback(self.pollStatus2)
-        
-    def pollStatus2(self, stat):
-        try:
-            if self.state.get() != stat.state:
-                self.state.set(stat.state)
-
-            if hasattr(stat, 'time_elapsed'):
-                elapsed = stat.time_elapsed
-                songnum = stat.song
-                total = stat.time_total
-                if self.mpd_is_lying and elapsed < 3:
-                    self.mpd_is_lying = False
-
-                # mpd lies about elapsed, song, and total during the last
-                # .5sec of each song. so we coast through that part
-                if elapsed > total - .75 or self.mpd_is_lying:
-                    if not self.mpd_is_lying:
-                        self.mpd_is_lying = True
-                        self.true_song_total = songnum, total
-                        self.marked_time = time.time()
-                        self.marked_val = elapsed
-                    elapsed = self.marked_val + (time.time() - self.marked_time)
-                    songnum, total = self.true_song_total
-
-                t = -1
-                if songnum == 1:
-                    t = elapsed
-                elif songnum == 0:
-                    t = elapsed - total
-                elif songnum == 2:
-                    t = self.total_time.get() + elapsed
-
-                self.current_time.set(t)
-
-                self.last_poll_time = time.time()
-
-            self.check_autopause()
-        finally:
-            reactor.callLater(.05, self.pollStatus)
-
-    def set_total_time(self, song):
-        # currently only good for .wav
-        p = showconfig.songOnDisk(song)
-        self.total_time.set(wavelength.wavelength(p))
-
-    def mpdPlay(self):
-        """vidref wants to know when this play started"""
-        self.playStartTime = time.time()
-        self.mpd.play()
-
-    def play(self, song=None):
-        if song is None:
-            self.mpdPlay()
-            return
-    
-        self.mpd.clear()
-        self.mpd.add(showconfig.songInMpd(MUS['preSong']))
-        self.mpd.add(showconfig.songInMpd(song))
-        self.mpd.add(showconfig.songInMpd(MUS['postSong']))
-
-        self.fillCache(song)
-        
-        self.filename_var.set(graph.value(song, L9['showPath']))
-        self.song_uri = song
-
-        self.set_total_time(song)
-        self.seek_to(-4)
-
-    def fillCache(self, song):
-        """read the song's entire wav file into memory just before
-        playing, so that mpd should never hit the disk during
-        playback. On score in 2007, we had some mpd stutters that were
-        always timed with a disk read."""
-        p = showconfig.songOnDisk(song)
-        print "reading %s to prime the cache" % p
-        open(p).read()
-
-    def check_autopause(self):
-        pause_time = self.total_time.get() + self.song_pad_time
-        t = self.current_time.get()
-        if (self.state.get() == "play" and
-            self.last_autopause_time < pause_time < t):
-            self.mpd.pause()
-        self.last_autopause_time = t
-
-    def stop(self):
-        self.mpd.seek(seconds=0, song=0)
-        self.mpd.stop()
-        
-    def seek_to(self, t):
-        if t < 0:
-            # seeking to anything within my 4-sec silence ogg goes
-            # right to zero. maybe ogg seeking is too coarse?
-            self.mpd.seek(seconds=t - (-4), song=0)
-        elif t > self.total_time.get():
-            self.mpd.seek(seconds=t - self.total_time.get(), song=2)
-        else:
-            self.mpd.seek(seconds=t, song=1)
-        self.last_autopause_time = t
-        self.playStartTime = time.time()
-
-    def skip_intro(self):
-        self.seek_to(0)
-
-    def in_post(self):
-        return (self.current_time.get() > self.total_time.get() +
-                self.song_pad_time)
-
-    def play_pause_toggle(self):
-        def finish(status):
-            if status.state == 'play':
-                self.mpd.pause()
-            else:
-                self.mpdPlay()
-        self.mpd.status().addCallback(finish)
-
-    def pause(self):
-        self.mpd.pause()
-
-    def skip_to_post(self):
-        self.seek_to(self.total_time.get() + self.song_pad_time)
-        self.play()
-
-
-class GoButton:
-    def __init__(self, player, statusLabel, songURIs):
-        self.player = player
-        self.statusLabel = statusLabel
-        self.songURIs = songURIs
-
-        self.player.current_time.trace("w", self.updateStatus)
-
-    def _nextAction(self):
-        state = self.player.state.get() 
-        if state == 'stop':
-            currentPath = self.player.song_uri
-            try:
-                nextPath = self.songURIs[self.songURIs.index(currentPath) + 1]
-            except (ValueError, IndexError):
-                nextPath = self.songURIs[0]
-                
-            return ("next song %s" % shortSongPath(nextPath, self.songURIs),
-                    lambda: self.player.play(nextPath))
-
-        if state == 'pause':
-            return "play", self.player.play
-
-        if state == 'play':
-            if self.player.current_time.get() < 0:
-                return "skip intro", self.player.skip_intro
-            if self.player.in_post():
-                return "<nothing>", lambda: None
-            return "skip to post", self.player.skip_to_post
-        
-    def action(self):
-        desc, func = self._nextAction()
-        func()
-        
-    def updateStatus(self, *args):
-        desc, func = self._nextAction()
-        self.statusLabel.config(text=desc)
-        
-
-
-def buildsonglist(root, graph, songs, player):
-    songlist=tk.Frame(root,bd=2,relief='raised',bg='black')
-
-    maxsfwidth=max([len(graph.label(song)) for song in songs])
-
-    for i,song in enumerate(songs):
-        b=tk.Button(songlist,text=graph.label(song),width=maxsfwidth,
-                    anchor='w',pady=0,bd=0,relief='flat',
-                    font="arial 14 bold")
-        b.bind("<Configure>",lambda ev,b=b:
-               b.config(font="arial %d bold" % min(12,int((ev.height-3)*.8))))
-        try:
-            # rainbow colors
-            frac=i/len(songs)
-            b.config(bg='black',
-                     fg="#%02x%02x%02x" % tuple([int(255*(.7+.3*
-                                                          math.sin(frac*4+x))
-                                                     ) for x in 1,2,3]))
-        except Exception,e:
-            print "rainbow failed: %s"%e
-        
-        b.config(command=lambda song=song: player.play(song))
-        b.pack(side='top',fill='both',exp=1,padx=0,pady=0,ipadx=0,ipady=0)
-
-
-        def color_buttons(x, y, z, song=song, b=b):
-            name = player.filename_var.get()
-            if name == graph.value(song, L9['showPath']):
-                b['bg'] = 'grey50'
-            else:
-                b['bg'] = 'black'
-        player.filename_var.trace("w", color_buttons)
-    return songlist
- 
-
-class TimeScale(tk.Scale):
-    def __init__(self,master,player):
-        tk.Scale.__init__(self, master, orient="horiz",
-                          from_=-4,to_=100,
-                          sliderlen=20,width=20,
-                          res=0.001,
-                          showvalue=0,
-                          troughcolor='black',
-                          bg='lightblue3',
-                          )
-
-        self.player = player
-
-        self.dragging = False
-        self.button_down = False
-        self.drag_start_player_state = None
-
-        self.bind("<ButtonPress-1>", self.b1down)
-        self.config(command=self.scale_changed)
-        self.bind("<ButtonRelease-1>", self.b1up)
-
-        self.player.current_time.trace('w', self.current_time_changed)
-
-    def current_time_changed(self, *args):
-        """attach player.current_time to scale (one-way)"""
-        if not self.dragging:
-            self.set(self.player.current_time.get())
-
-    def b1down(self,evt):
-        self.button_down = True
-        self.dragging = False
-
-    def scale_changed(self, time):
-        if not self.button_down:
-            return
-        
-        if not self.dragging:
-            self.dragging = True
-            self.drag_start_player_state = self.player.state.get()
-            if self.drag_start_player_state == "play":
-                # due to mpd having to seemingly play a whole block at
-                # every new seek point, it is better to pause playback
-                # while the mouse is down
-                self.player.pause()
-
-        # ok to seek around when paused. this keeps the displayed time 
-        # up to date, which is how the user knows where he is
-        self.player.seek_to(float(time))
-
-    def b1up(self,evt):
-        self.button_down = False
-        self.dragging = False
-            
-        if self.drag_start_player_state == "play":
-            self.player.play()
-
-class Seeker(tk.Frame):
-    """includes scale AND labels below it"""
-    def __init__(self,master,player):
-        tk.Frame.__init__(self,master,bg='black')
-
-        self.scl = TimeScale(self,player)
-        self.scl.pack(fill='x',side='top')
-
-        self.buildstatus(player)
-
-
-    def buildstatus(self,player):
-        left_var=tk.DoubleVar()
-        for txt,var,fmt in (('Current',player.current_time,"%.2f"),
-                            ('Song len',player.total_time,"%.2f",),
-                            ('Left',left_var, "%.2f"),
-                            ('Song',player.filename_var, "%s"),
-                            ('State', player.state, "%s")):
-            tk.Label(self,text=txt,
-                     relief='raised',bd=1,font='arial 9',
-                     **appstyle).pack(side='left',fill='y')
-            l = tk.Label(self,width=7, anchor='w', text=var.get(),
-                         relief='ridge',bd=1,font='arial 12 bold',
-                         padx=2,pady=2,
-                         bg='#800000',fg='white')
-            l.pack(side='left',expand=1, fill='x')
-
-            var.trace_variable('w',
-                               lambda a,b,c,l=l,fmt=fmt,var=var:
-                               l.config(text=fmt % var.get()))
-
-        # update end time as the song changes
-        player.total_time.trace("w",lambda *args: (
-            self.scl.config(to_=player.total_time.get()+15)))
-
-        def fixleft(*args):
-            # update time-left variable
-            left_var.set(player.total_time.get()-player.current_time.get())
-
-            if player.current_time.get() < 0 or left_var.get() < 0:
-                self.scl['troughcolor'] = 'blue'
-            else:
-                self.scl['troughcolor'] = 'black'
-
-        player.total_time.trace("w",fixleft)
-        player.current_time.trace("w",fixleft)
-
-class ControlButtons(tk.Frame):
-    def __init__(self, master, goButton, player, root):
-        tk.Frame.__init__(self,master,bg='black')
-        self.statebuttons = {} # lowercased name : Button
-        self.goButton = goButton
-        self.player = player
-        for tag, txt,cmd,key in [
-            ('stop',
-             'Stop\nC-s', player.stop, "<Control-s>"),
-            ('pause',
-             'Pause\nC-p', player.play_pause_toggle, "<Control-p>"),
-            ('skip intro',
-             'Skip Intro\nC-i', player.skip_intro, "<Control-i>"),
-            ('skip to post',
-             'Skip to Post\nC-t', player.skip_to_post, "<Control-t>"),
-            ('go', 'Go\nspace', self.goButton.action, "<Key-space>"),
-            ]:
-            b = tk.Button(self, text=txt, command=cmd,
-                          font='arial 16 bold',
-                          height=3,**appstyle)
-            b.pack(side='left', fill='both', expand=True)
-            # keyboard bindings
-            root.bind(key, lambda evt, cmd=cmd: cmd())
-            self.statebuttons[tag] = b
-
-    def update_state_buttons(self,*args):
-        state = self.player.state.get()
-
-        if state in ('stop', 'pause'):
-            self.statebuttons['pause']['text'] = 'Play\nC-p'
-        else:
-            self.statebuttons['pause']['text'] = 'Pause\nC-p' 
-
-        colors = {'stop' : 'red',
-                  'play' : 'blue',
-                  'pause' : 'green'} # very confusing -- play and pause supply colors
-                                     # for each other!
-        for name, button in self.statebuttons.items():
-            if name == 'pause' and state not in ('stop', 'pause'):
-                name = 'play'
-
-            if state == name: # name gets changed sometimes for 'pause' -- see right above
-                button['bg'] = colors.get(name, 'black')
-            else:
-                button['bg'] = 'black'
-        self.goButton.updateStatus()
-
-############################
-
-
-
-def main():
-    global graph
-    parser = OptionParser()
-    parser.add_option('--show',
-        help='show URI, like http://light9.bigasterisk.com/show/dance2008')
-    parser.add_option("-v", "--verbose", action="store_true",
-                      help="logging.DEBUG")
-    graph = showconfig.getGraph()
-    (options, songfiles) = parser.parse_args()
-
-    if options.verbose:
-        log.setLevel(logging.DEBUG)
-        
-    if len(songfiles)<1:
-        graph = showconfig.getGraph()
-        if not options.show:
-            raise ValueError("missing --show http://...")
-        playList = graph.value(URIRef(options.show), L9['playList'])
-        if not playList:
-            raise ValueError("%r has no l9:playList" % options.show)
-        songs = list(graph.items(playList))
-    else:
-        raise NotImplementedError("don't know how to make rdf song nodes from cmdline song paths")
-
-
-    root=tk.Tk()
-    root.wm_title("ascoltami")
-    toplevelat("ascoltami", root)
-    root.config(bg="black")
-    player=Player()
-
-    songlist = buildsonglist(root, graph, songs, player)
-    songlist.pack(fill='both',exp=1)
-
-    seeker = Seeker(root, player)
-    
-    goRow = tk.Frame(root)
-    tk.Label(goRow, text="Go button action",
-             font='arial 9', **appstyle).pack(side='left', fill='both')
-    goStatus = tk.Label(goRow, font='arial 12 bold', **appstyle)
-    goStatus.config(bg='#800000', fg='white', relief='ridge')
-    goStatus.pack(side='left', expand=True, fill='x')
-
-    go = GoButton(player, goStatus, songs)
-    
-    buts = ControlButtons(root, go, player, root)
-
-    songlist.pack(fill='both', expand=True)
-    buts.pack(side='top', fill='x')
-    seeker.pack(side='top', fill='x')
-    goRow.pack(side='top', fill='x')
-
-    player.state.trace_variable('w', buts.update_state_buttons)
-    buts.update_state_buttons()
-
-    tksupport.install(root,ms=10)
-
-    rootResource = resource.Resource()
-    site = server.Site(rootResource)
-    rootResource.putChild("api", RestApi(player))
-    rootResource.putChild("RPC2", XMLRPCServe(player))
-    
-    try:
-        reactor.listenTCP(networking.musicPort(), site)
-        print "started server on %s" % networking.musicPort()
-    except CannotListenError:
-        print "no server started- %s is in use" % networking.musicPort()
-
-    root.bind("<Destroy>",lambda ev: reactor.stop)
-    root.protocol('WM_DELETE_WINDOW', reactor.stop)
-
-    reactor.run()
-    
-if __name__ == '__main__':
-    main()