1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
import gobject
import socket
import re
import urllib
from client import LiqClient
from widgets import View
ridformat = ("int/liquidsoap-rid", 0, 32)
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
import getopt
import sys
if __name__ == "__main__":
try:
opts, args = getopt.gnu_getopt(sys.argv[1:],"h:p:o:",
['host=','port=','operator='])
except:
# TODO real help
print 'Options are --operator=s --host=s --port=s and -ohp shortcuts'
sys.exit()
op='request'
host='localhost'
port=1234
for o , a in opts:
if o in ('-p', '--port'):
port=int(a)
if o in ('-o', '--operator'):
op=a
if o in ('-h', '--host'):
host=a
try:
win = gtk.Window()
win.set_border_width(10)
win.connect("delete_event", lambda w,e: False)
win.connect("destroy", lambda w: gtk.main_quit ())
win.set_title(host+':'+str(port)+' -- Request '+op)
win.resize(700,300)
win.add(LiqEditable(host,port,op))
win.show()
gtk.main()
except socket.error, x:
print "Couln't connect to "+host+':'+str(port)+'!'
|