class LiqOutput(gtk.VBox): def delete_event(self, widget, event, data=None): return False def destroy(self, widget, data=None): gtk.main_quit() def __init__(self, host='localhost', port=1234, op='root'): gtk.VBox.__init__(self) # Connect to liquidsoap self.op = op self.tel = LiqClient(host, port) # Fast metadata view: short metadata, right click to see more (TODO) a = self.tel.metadata(self.op + ".metadata") self.list = View( [ ['rid', 40], ['artist', '120'], ['title', '120'], # Right-align URI because the end is more informative # than the beginning ['initial_uri', '300', { 'xalign': 1.0 }], ['on_air', '50'] ], a) scroll = gtk.ScrolledWindow() scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) scroll.add(self.list) scroll.show() self.pack_start(scroll, padding=10) # an hbox containing : remaining / start-stop / skip self.hbox = gtk.HBox() self.pack_start(self.hbox, False, False) self.remaining = gtk.Label("...") self.hbox.pack_start(self.remaining) gobject.timeout_add(1000, self.update) self.skip = gtk.Button("skip") self.skip.connect("clicked", self.do_skip, None) self.hbox.pack_start(self.skip, False, False) self.onoff = gtk.ToggleButton("on/off") self.onoff.set_active(self.tel.command(self.op + ".status") == "on") self.onoff.connect("clicked", self.do_onoff, None) self.hbox.pack_start(self.onoff, False, False) # Show everything... self.list.show() self.remaining.show() self.onoff.show() self.skip.show() self.show() self.hbox.show() def update(self): remaining = self.tel.command(self.op + ".remaining") if remaining != "(undef)": remaining = float(remaining) self.remaining.set_label("Remaining: %02d:%02d" % (remaining / 60, remaining % 60)) else: self.remaining.set_label("Remaining: (undef)") self.list.setModel(self.tel.metadata(self.op + ".metadata")) if self.tel.command(self.op + ".status") == "on": if not self.onoff.get_active(): self.onoff.set_active(True) else: if self.onoff.get_active(): self.onoff.set_active(False) return 1 def do_skip(self, widget, data=None): self.tel.command(self.op + ".skip") def do_onoff(self, widget, data=None): # The state we get is the one *after* the click if self.onoff.get_active(): self.tel.command(self.op + ".start") else: self.tel.command(self.op + ".stop") def main(self): gtk.main()
class LiqOutput(gtk.VBox): def delete_event(self, widget, event, data=None): return False def destroy(self, widget, data=None): gtk.main_quit() def __init__(self,host='localhost',port=1234,op='root'): gtk.VBox.__init__(self) # Connect to liquidsoap self.op = op self.tel = LiqClient(host,port) # Fast metadata view: short metadata, right click to see more (TODO) a=self.tel.metadata(self.op+".metadata") self.list = View([['rid' , 40], ['artist' ,'120'], ['title' ,'120'], # Right-align URI because the end is more informative # than the beginning ['initial_uri','300', {'xalign':1.0}], ['on_air','50']], a) scroll = gtk.ScrolledWindow() scroll.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) scroll.add(self.list) scroll.show() self.pack_start(scroll,padding=10) # an hbox containing : remaining / start-stop / skip self.hbox = gtk.HBox() self.pack_start(self.hbox,False,False) self.remaining = gtk.Label("...") self.hbox.pack_start(self.remaining) gobject.timeout_add(1000,self.update) self.skip = gtk.Button("skip") self.skip.connect("clicked", self.do_skip, None) self.hbox.pack_start(self.skip,False,False) self.onoff = gtk.ToggleButton("on/off") self.onoff.set_active(self.tel.command(self.op+".status")=="on") self.onoff.connect("clicked", self.do_onoff, None) self.hbox.pack_start(self.onoff,False,False) # Show everything... self.list.show() self.remaining.show() self.onoff.show() self.skip.show() self.show() self.hbox.show() def update(self): remaining = self.tel.command(self.op+".remaining") if remaining!="(undef)": remaining=float(remaining) self.remaining.set_label("Remaining: %02d:%02d" % (remaining/60,remaining%60)) else: self.remaining.set_label("Remaining: (undef)") self.list.setModel(self.tel.metadata(self.op+".metadata")) if self.tel.command(self.op+".status")=="on": if not self.onoff.get_active(): self.onoff.set_active(True) else: if self.onoff.get_active(): self.onoff.set_active(False) return 1 def do_skip(self,widget,data=None): self.tel.command(self.op+".skip") def do_onoff(self,widget,data=None): # The state we get is the one *after* the click if self.onoff.get_active(): self.tel.command(self.op+".start") else: self.tel.command(self.op+".stop") def main(self): gtk.main()
class LiqQueue(gtk.VPaned): def __init__(self,host='localhost',port=1234,op='request'): gtk.VPaned.__init__(self) self.show() # Connect to liquidsoap self.op = op self.tel = LiqClient(host,port) # Fast metadata view: short metadata, right click to see more (TODO) self.list = View([['rid' , 40], ['2nd_queue_pos', 80], ['skip' , False], ['artist' ,'120'], ['title' ,'120'], # Right-align URI because the end is more informative # than the beginning ['initial_uri','250', {'xalign':1.0}]], self.queue()) self.list.drag_dest_set(gtk.DEST_DEFAULT_ALL, [('text/uri-list',0,0)], gtk.gdk.ACTION_COPY) self.list.connect("drag_data_received",self.drag) self.list.connect("row_activated",self.row_activated) scroll = gtk.ScrolledWindow() scroll.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) scroll.add(self.list) # The list is ready, pack it to the top region # together with a help label box = gtk.VBox() lbl = gtk.Label() lbl.set_markup("\ <b>Enqueue</b> files by drag-n-dropping from your file manager or \ using the file chooser below.\n\ <b>Remove/restore</b> scheduled files by double-clicking.") box.pack_start(scroll,fill=True,expand=True) box.pack_start(lbl,fill=True,expand=False) self.pack1(box,resize=True) lbl.show() self.list.show() scroll.show() box.show() fsel = gtk.FileChooserWidget() fsel.connect("file_activated", lambda s: self.selected(s)) exp = gtk.Expander("File chooser") exp.add(fsel) self.pack2(exp,resize=False) fsel.show() exp.show() gobject.timeout_add(1000,self.update) def selected(self,file): self.tel.command(self.op+".push "+file.get_filename()) def drag(self,w,context,x,y,data,info,time): if data and data.format == 8: for e in data.data.split('\r\n')[:-1]: self.tel.command(self.op+".push "+urllib.unquote(e)) context.finish(gtk.TRUE, gtk.FALSE, time) def row_activated(self,view,path,column): rid = view.get_model().get_value(view.get_model().get_iter(path),0) skip = view.get_model().get_value(view.get_model().get_iter(path),2) if skip: self.tel.command(self.op+".consider "+str(rid)) else: self.tel.command(self.op+".ignore "+str(rid)) def queue(self): a = filter(lambda x: x!='', re.compile('(\d+)\s*').split(self.tel.command(self.op+".queue"))) a = [ self.tel.metadata('request.metadata '+e)[0] for e in a ] return a def update(self): self.list.setModel(self.queue()) return 1
class LiqQueue(gtk.VPaned): def __init__(self, host='localhost', port=1234, op='request'): gtk.VPaned.__init__(self) self.show() # Connect to liquidsoap self.op = op self.tel = LiqClient(host, port) # Fast metadata view: short metadata, right click to see more (TODO) self.list = View( [ ['rid', 40], ['2nd_queue_pos', 80], ['skip', False], ['artist', '120'], ['title', '120'], # Right-align URI because the end is more informative # than the beginning ['initial_uri', '250', { 'xalign': 1.0 }] ], self.queue()) self.list.drag_dest_set(gtk.DEST_DEFAULT_ALL, [('text/uri-list', 0, 0)], gtk.gdk.ACTION_COPY) self.list.connect("drag_data_received", self.drag) self.list.connect("row_activated", self.row_activated) scroll = gtk.ScrolledWindow() scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) scroll.add(self.list) # The list is ready, pack it to the top region # together with a help label box = gtk.VBox() lbl = gtk.Label() lbl.set_markup("\ <b>Enqueue</b> files by drag-n-dropping from your file manager or \ using the file chooser below.\n\ <b>Remove/restore</b> scheduled files by double-clicking.") box.pack_start(scroll, fill=True, expand=True) box.pack_start(lbl, fill=True, expand=False) self.pack1(box, resize=True) lbl.show() self.list.show() scroll.show() box.show() fsel = gtk.FileChooserWidget() fsel.connect("file_activated", lambda s: self.selected(s)) exp = gtk.Expander("File chooser") exp.add(fsel) self.pack2(exp, resize=False) fsel.show() exp.show() gobject.timeout_add(1000, self.update) def selected(self, file): self.tel.command(self.op + ".push " + file.get_filename()) def drag(self, w, context, x, y, data, info, time): if data and data.format == 8: for e in data.data.split('\r\n')[:-1]: self.tel.command(self.op + ".push " + urllib.unquote(e)) context.finish(gtk.TRUE, gtk.FALSE, time) def row_activated(self, view, path, column): rid = view.get_model().get_value(view.get_model().get_iter(path), 0) skip = view.get_model().get_value(view.get_model().get_iter(path), 2) if skip: self.tel.command(self.op + ".consider " + str(rid)) else: self.tel.command(self.op + ".ignore " + str(rid)) def queue(self): a = filter( lambda x: x != '', re.compile('(\d+)\s*').split(self.tel.command(self.op + ".queue"))) a = [self.tel.metadata('request.metadata ' + e)[0] for e in a] return a def update(self): self.list.setModel(self.queue()) return 1
class LiqEditable(gtk.VPaned): def __init__(self,host='localhost',port=1234,op='request'): gtk.VPaned.__init__(self) self.show() # Connect to liquidsoap self.op = op self.tel = LiqClient(host,port) self.list = View([['rid' , 40], ['status' ,'70'], ['artist' ,'120'], ['title' ,'120'], # Right-align URI because the end is more informative # than the beginning ['initial_uri','300', {'xalign':1.0}]], []) self.update() # Popup menu for requests menu = gtk.Menu() item = gtk.ImageMenuItem(gtk.STOCK_REMOVE) def remove_request(item): model,path = self.list.get_selection().get_selected() self.tel.command(self.op+'.remove '+str(model.get_value(path,0))) item.connect('activate',remove_request) item.show() menu.append(item) def popup(w,event): if event.button==3: menu.popup(None,None,None,event.button,event.time) self.list.connect('button_press_event',popup) # Receive drag-n-drops self.list.enable_model_drag_dest( [('text/uri-list',0,0),ridformat], gtk.gdk.ACTION_DEFAULT) def dnd_receive(w,context,x,y,data,info,time): if data and (data.format != 8 and data.format != ridformat[2]): print "DnD received: Unknown data format! (%d)" % data.format return row = w.get_dest_row_at_pos(x,y) if row: # This is an insertion path, pos = row # Remove the number of resolv(ed|ing) requests to get the pos # in the pending queue pos = path[0]-(len(w.get_model())-self.plen) if pos<0: print "Cannot move outside pending queue!" return if pos >= self.plen-1: pos = -1 if data and data.format == ridformat[2]: rid = int(data.data) self.tel.command('%s.move %d %d' % (self.op,rid,pos)) if data and data.format == 8: for e in data.data.split('\r\n')[:-1]: self.tel.command(self.op+'.insert '+str(pos)+' '+e) else: # This is a push if data and data.format == ridformat[2]: rid = int(data.data) self.tel.command('%s.move %d %d' % (self.op,rid,-1)) if data and data.format == 8: for e in data.data.split('\r\n')[:-1]: self.tel.command(self.op+".push "+urllib.unquote(e)) context.finish(True, False, time) self.list.connect("drag_data_received",dnd_receive) # Emit drag-n-drops self.list.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,[ridformat], gtk.DEST_DEFAULT_ALL) def dnd_emit(w,context,sel,info,time): # Check that format is RID if info==ridformat[2]: model,iter = w.get_selection().get_selected() if iter: sel.set(sel.target,info,str(model.get_value(iter,0))) self.list.connect("drag_data_get",dnd_emit) # Put the list in a scroll scroll = gtk.ScrolledWindow() scroll.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) scroll.add(self.list) scroll.show() self.list.show() # Pack this to self, with a doc label box = gtk.VBox() lbl = gtk.Label() lbl.set_markup("\ <b>Enqueue</b> files by drag-n-dropping from your file manager or \ using the file chooser below.\n\ <b>Re-order</b> the queue by drag-n-dropping.\n\ <b>Remove</b> scheduled requests by right-clicking.") box.pack_start(scroll,fill=True,expand=True) box.pack_start(lbl,fill=True,expand=False) self.pack1(box,resize=True) lbl.show() box.show() # A file selector in the other side of the pane fsel = gtk.FileChooserWidget() fsel.connect("file_activated", lambda s: self.selected(s)) exp = gtk.Expander("File chooser") exp.add(fsel) self.pack2(exp,resize=False) fsel.show() exp.show() # And the update callback gobject.timeout_add(1000,self.update) def selected(self,file): self.tel.command(self.op+".push "+file.get_filename()) def queue(self): a = filter(lambda x: x!='', re.compile('(\d+)\s*').split(self.tel.command(self.op+".queue"))) a = [ self.tel.metadata('request.metadata '+e)[0] for e in a ] return a def update(self): newq = self.queue() if newq != self.list.getModel(): # The test is useless for setModel but needed for plen update self.list.setModel(newq) plen = int(self.tel.command(self.op+'.pending_length')) self.plen = plen return 1
class LiqEditable(gtk.VPaned): def __init__(self, host='localhost', port=1234, op='request'): gtk.VPaned.__init__(self) self.show() # Connect to liquidsoap self.op = op self.tel = LiqClient(host, port) self.list = View( [ ['rid', 40], ['status', '70'], ['artist', '120'], ['title', '120'], # Right-align URI because the end is more informative # than the beginning ['initial_uri', '300', { 'xalign': 1.0 }] ], []) self.update() # Popup menu for requests menu = gtk.Menu() item = gtk.ImageMenuItem(gtk.STOCK_REMOVE) def remove_request(item): model, path = self.list.get_selection().get_selected() self.tel.command(self.op + '.remove ' + str(model.get_value(path, 0))) item.connect('activate', remove_request) item.show() menu.append(item) def popup(w, event): if event.button == 3: menu.popup(None, None, None, event.button, event.time) self.list.connect('button_press_event', popup) # Receive drag-n-drops self.list.enable_model_drag_dest([('text/uri-list', 0, 0), ridformat], gtk.gdk.ACTION_DEFAULT) def dnd_receive(w, context, x, y, data, info, time): if data and (data.format != 8 and data.format != ridformat[2]): print "DnD received: Unknown data format! (%d)" % data.format return row = w.get_dest_row_at_pos(x, y) if row: # This is an insertion path, pos = row # Remove the number of resolv(ed|ing) requests to get the pos # in the pending queue pos = path[0] - (len(w.get_model()) - self.plen) if pos < 0: print "Cannot move outside pending queue!" return if pos >= self.plen - 1: pos = -1 if data and data.format == ridformat[2]: rid = int(data.data) self.tel.command('%s.move %d %d' % (self.op, rid, pos)) if data and data.format == 8: for e in data.data.split('\r\n')[:-1]: self.tel.command(self.op + '.insert ' + str(pos) + ' ' + e) else: # This is a push if data and data.format == ridformat[2]: rid = int(data.data) self.tel.command('%s.move %d %d' % (self.op, rid, -1)) if data and data.format == 8: for e in data.data.split('\r\n')[:-1]: self.tel.command(self.op + ".push " + urllib.unquote(e)) context.finish(True, False, time) self.list.connect("drag_data_received", dnd_receive) # Emit drag-n-drops self.list.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, [ridformat], gtk.DEST_DEFAULT_ALL) def dnd_emit(w, context, sel, info, time): # Check that format is RID if info == ridformat[2]: model, iter = w.get_selection().get_selected() if iter: sel.set(sel.target, info, str(model.get_value(iter, 0))) self.list.connect("drag_data_get", dnd_emit) # Put the list in a scroll scroll = gtk.ScrolledWindow() scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) scroll.add(self.list) scroll.show() self.list.show() # Pack this to self, with a doc label box = gtk.VBox() lbl = gtk.Label() lbl.set_markup("\ <b>Enqueue</b> files by drag-n-dropping from your file manager or \ using the file chooser below.\n\ <b>Re-order</b> the queue by drag-n-dropping.\n\ <b>Remove</b> scheduled requests by right-clicking.") box.pack_start(scroll, fill=True, expand=True) box.pack_start(lbl, fill=True, expand=False) self.pack1(box, resize=True) lbl.show() box.show() # A file selector in the other side of the pane fsel = gtk.FileChooserWidget() fsel.connect("file_activated", lambda s: self.selected(s)) exp = gtk.Expander("File chooser") exp.add(fsel) self.pack2(exp, resize=False) fsel.show() exp.show() # And the update callback gobject.timeout_add(1000, self.update) def selected(self, file): self.tel.command(self.op + ".push " + file.get_filename()) def queue(self): a = filter( lambda x: x != '', re.compile('(\d+)\s*').split(self.tel.command(self.op + ".queue"))) a = [self.tel.metadata('request.metadata ' + e)[0] for e in a] return a def update(self): newq = self.queue() if newq != self.list.getModel(): # The test is useless for setModel but needed for plen update self.list.setModel(newq) plen = int(self.tel.command(self.op + '.pending_length')) self.plen = plen return 1