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 __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()
Beispiel #3
0
  def __init__(self,host='localhost',port=1234,op='root'):
    gtk.ScrolledWindow.__init__(self)
    self.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)

    # Connect to liquidsoap
    self.op = op
    self.tel = LiqClient(host,port)

    self.view = View([['status','50'],['uri','300']],[])
    self.update()
    self.add(self.view)
    self.view.show()
    self.show()

    gobject.timeout_add(1000,self.update)
Beispiel #4
0
class LiqPlaylist(gtk.ScrolledWindow):

  def __init__(self,host='localhost',port=1234,op='root'):
    gtk.ScrolledWindow.__init__(self)
    self.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)

    # Connect to liquidsoap
    self.op = op
    self.tel = LiqClient(host,port)

    self.view = View([['status','50'],['uri','300']],[])
    self.update()
    self.add(self.view)
    self.view.show()
    self.show()

    gobject.timeout_add(1000,self.update)

  def next(self):
    a = filter(lambda x: x!='',
               re.compile('\n').split(self.tel.command(self.op+".next")))
    def f(e):
      m = re.search('^\[(.*?)\] (.*)',e)
      if m:
        return { 'uri': m.group(2) , 'status': m.group(1) }
      else:
        return { 'uri': e }

    return [ f(e) for e in a ]

  def update(self):
    # TODO restore scroll position
    self.view.setModel(self.next())
    return True
Beispiel #5
0
    def __init__(self, host='localhost', port=1234):
        gtk.Notebook.__init__(self)
        self.set_show_tabs(False)

        self.host = host
        self.port = port
        self.tel = LiqClient(host, port)
        self.list = View([['type', '80'], ['name', '100']], [])
        self.list.connect('row_activated', self.row_activated)
        box = gtk.HBox()
        box.pack_start(self.list, False, False)
        lbl = gtk.Label()
        lbl.set_markup("""
<b>The Savonet Team is happy to welcome you in liguidsoap.</b>

On the left is a list of controllable nodes. Double-click a row to open the controller.

The <b>output</b> nodes are those which do something with your stream.
Some output directly to your speakers, some save your stream to a file, and 
finally, some output to an icecast server for webradio.

A <b>mixer</b> is a mixing table. It has some input sources, and allows you 
to select which one you want to play, tune the volume, etc.

<b>editable</b> and <b>queue</b> nodes are interactive playlists, in which
you can add files using drag-n-drop or builtin file browser. The editable is 
more powerful, allows you to delete, insert at any place, not only at the 
bottom.

Finally, a <b>playlist</b> is a non-interactive playlist, which list and 
behaviour is hard-coded in the liquidsoap script. You can just see what will be 
played next. (Actually, more could be coming soon, like changing random mode 
and playlist file...)

Liquidsoap and liguidsoap are copyright (c) 2003-2017 Savonet Team.
This is free software, released under the terms of GPL version 2 or higher.
""")
        box.pack_start(lbl)
        self.append_page(box, gtk.Label('list'))
        box.show()
        lbl.show()
        self.list.show()
        self.show()
        gobject.timeout_add(1000, self.update)
Beispiel #6
0
  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)
Beispiel #7
0
  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()
Beispiel #8
0
  def __init__(self,host='localhost',port=1234):
    gtk.Notebook.__init__(self)
    self.set_show_tabs(False)

    self.host=host
    self.port=port
    self.tel = LiqClient(host,port)
    self.list = View([['type','80'],['name','100']],[])
    self.list.connect('row_activated',self.row_activated)
    box = gtk.HBox()
    box.pack_start(self.list,False,False)
    lbl = gtk.Label()
    lbl.set_markup("""
<b>The Savonet Team is happy to welcome you in liguidsoap.</b>

On the left is a list of controllable nodes. Double-click a row to open the controller.

The <b>output</b> nodes are those which do something with your stream.
Some output directly to your speakers, some save your stream to a file, and 
finally, some output to an icecast server for webradio.

A <b>mixer</b> is a mixing table. It has some input sources, and allows you 
to select which one you want to play, tune the volume, etc.

<b>editable</b> and <b>queue</b> nodes are interactive playlists, in which
you can add files using drag-n-drop or builtin file browser. The editable is 
more powerful, allows you to delete, insert at any place, not only at the 
bottom.

Finally, a <b>playlist</b> is a non-interactive playlist, which list and 
behaviour is hard-coded in the liquidsoap script. You can just see what will be 
played next. (Actually, more could be coming soon, like changing random mode 
and playlist file...)

Liquidsoap and liguidsoap are copyright (c) 2003-2014 Savonet Team.
This is free software, released under the terms of GPL version 2 or higher.
""")
    box.pack_start(lbl)
    self.append_page(box,gtk.Label('list'))
    box.show() ; lbl.show()
    self.list.show()
    self.show()
    gobject.timeout_add(1000,self.update)
Beispiel #9
0
class LiqGui(gtk.Notebook):
  def __init__(self,host='localhost',port=1234):
    gtk.Notebook.__init__(self)
    self.set_show_tabs(False)

    self.host=host
    self.port=port
    self.tel = LiqClient(host,port)
    self.list = View([['type','80'],['name','100']],[])
    self.list.connect('row_activated',self.row_activated)
    box = gtk.HBox()
    box.pack_start(self.list,False,False)
    lbl = gtk.Label()
    lbl.set_markup("""
<b>The Savonet Team is happy to welcome you in liguidsoap.</b>

On the left is a list of controllable nodes. Double-click a row to open the controller.

The <b>output</b> nodes are those which do something with your stream.
Some output directly to your speakers, some save your stream to a file, and 
finally, some output to an icecast server for webradio.

A <b>mixer</b> is a mixing table. It has some input sources, and allows you 
to select which one you want to play, tune the volume, etc.

<b>editable</b> and <b>queue</b> nodes are interactive playlists, in which
you can add files using drag-n-drop or builtin file browser. The editable is 
more powerful, allows you to delete, insert at any place, not only at the 
bottom.

Finally, a <b>playlist</b> is a non-interactive playlist, which list and 
behaviour is hard-coded in the liquidsoap script. You can just see what will be 
played next. (Actually, more could be coming soon, like changing random mode 
and playlist file...)

Liquidsoap and liguidsoap are copyright (c) 2003-2014 Savonet Team.
This is free software, released under the terms of GPL version 2 or higher.
""")
    box.pack_start(lbl)
    self.append_page(box,gtk.Label('list'))
    box.show() ; lbl.show()
    self.list.show()
    self.show()
    gobject.timeout_add(1000,self.update)

  def update(self):
    def hashof(s):
      m = re.search('(\S+)\s:\s(\S+)',s)
      return { 'name' : m.group(1), 'type' : m.group(2) }
    list = [ hashof(s) for s in
             filter(lambda x: x!='',
                    re.compile('\n').split(self.tel.command('list'))) ]
    self.list.setModel(list)

  def row_activated(self,view,path,column):
    type = view.get_model().get_value(view.get_model().get_iter(path),0)
    name = view.get_model().get_value(view.get_model().get_iter(path),1)
    b = gtk.HBox()
    l = gtk.Label(name+' ')
    c = gtk.Button('x') # TODO stock close
    c.n = self.get_n_pages()
    b.pack_start(l)
    b.pack_start(c)
    c.show() ; l.show()
    def clicked(w):
      self.remove_page(w.n) # TODO cleanup contents (especially telnet)
    c.connect('clicked',clicked)
    self.set_show_tabs(True)
    # This is a dirty workaround, we should not show this item
    # in the first place!
    if type=='interactive':
      print("Unsupported item...")
    else:
      self.append_page(
          {'queue'   : lambda x: LiqQueue(self.host,self.port,x),
           'editable': lambda x: LiqEditable(self.host,self.port,x),
           'playlist': lambda x: LiqPlaylist(self.host,self.port,x),
           'mixer'   : lambda x: LiqMix(self.host,self.port,x),
           'output.icecast': lambda x: LiqOutput(self.host,self.port,x),
           'output.file'   : lambda x: LiqOutput(self.host,self.port,x),
           'output.oss'    : lambda x: LiqOutput(self.host,self.port,x),
           'output.ao'     : lambda x: LiqOutput(self.host,self.port,x),
           'output.alsa'   : lambda x: LiqOutput(self.host,self.port,x),
           'output.portaudio': lambda x: LiqOutput(self.host,self.port,x),
           'output.pulseaudio': lambda x: LiqOutput(self.host,self.port,x),
           'output.dummy'  : lambda x: LiqOutput(self.host,self.port,x),
          }[type](name),b)
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()
Beispiel #11
0
class LiqGui(gtk.Notebook):
  def __init__(self,host='localhost',port=1234):
    gtk.Notebook.__init__(self)
    self.set_show_tabs(False)

    self.host=host
    self.port=port
    self.tel = LiqClient(host,port)
    self.list = View([['type','80'],['name','100']],[])
    self.list.connect('row_activated',self.row_activated)
    box = gtk.HBox()
    box.pack_start(self.list,False,False)
    lbl = gtk.Label()
    lbl.set_markup("""
<b>The Savonet Team is happy to welcome you in liguidsoap.</b>

On the left is a list of controllable nodes. Double-click a row to open the controller.

The <b>output</b> nodes are those which do something with your stream.
Some output directly to your speakers, some save your stream to a file, and 
finally, some output to an icecast server for webradio.

A <b>mixer</b> is a mixing table. It has some input sources, and allows you 
to select which one you want to play, tune the volume, etc.

<b>editable</b> and <b>queue</b> nodes are interactive playlists, in which
you can add files using drag-n-drop or builtin file browser. The editable is 
more powerful, allows you to delete, insert at any place, not only at the 
bottom.

Finally, a <b>playlist</b> is a non-interactive playlist, which list and 
behaviour is hard-coded in the liquidsoap script. You can just see what will be 
played next. (Actually, more could be coming soon, like changing random mode 
and playlist file...)

Liquidsoap and liguidsoap are copyright (c) 2003-2021 Savonet Team.
This is free software, released under the terms of GPL version 2 or higher.
""")
    box.pack_start(lbl)
    self.append_page(box,gtk.Label('list'))
    box.show() ; lbl.show()
    self.list.show()
    self.show()
    gobject.timeout_add(1000,self.update)

  def update(self):
    def hashof(s):
      m = re.search('(\S+)\s:\s(\S+)',s)
      return { 'name' : m.group(1), 'type' : m.group(2) }
    list = [ hashof(s) for s in
             filter(lambda x: x!='',
                    re.compile('\n').split(self.tel.command('list'))) ]
    self.list.setModel(list)

  def row_activated(self,view,path,column):
    type = view.get_model().get_value(view.get_model().get_iter(path),0)
    name = view.get_model().get_value(view.get_model().get_iter(path),1)
    b = gtk.HBox()
    l = gtk.Label(name+' ')
    c = gtk.Button('x') # TODO stock close
    c.n = self.get_n_pages()
    b.pack_start(l)
    b.pack_start(c)
    c.show() ; l.show()
    def clicked(w):
      self.remove_page(w.n) # TODO cleanup contents (especially telnet)
    c.connect('clicked',clicked)
    self.set_show_tabs(True)
    # This is a dirty workaround, we should not show this item
    # in the first place!
    if type=='interactive':
      print("Unsupported item...")
    else:
      self.append_page(
          {'queue'   : lambda x: LiqQueue(self.host,self.port,x),
           'editable': lambda x: LiqEditable(self.host,self.port,x),
           'playlist': lambda x: LiqPlaylist(self.host,self.port,x),
           'mixer'   : lambda x: LiqMix(self.host,self.port,x),
           'output.icecast': lambda x: LiqOutput(self.host,self.port,x),
           'output.file'   : lambda x: LiqOutput(self.host,self.port,x),
           'output.oss'    : lambda x: LiqOutput(self.host,self.port,x),
           'output.ao'     : lambda x: LiqOutput(self.host,self.port,x),
           'output.alsa'   : lambda x: LiqOutput(self.host,self.port,x),
           'output.portaudio': lambda x: LiqOutput(self.host,self.port,x),
           'output.pulseaudio': lambda x: LiqOutput(self.host,self.port,x),
           'output.dummy'  : lambda x: LiqOutput(self.host,self.port,x),
          }[type](name),b)
Beispiel #12
0
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()
Beispiel #13
0
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
    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)
Beispiel #15
0
  def __init__(self,host='localhost',port=1234,op='mix'):
    gtk.HBox.__init__(self)
    scroll = gtk.ScrolledWindow()
    scroll.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_NEVER)
    self.add(scroll)
    scroll.show()

    self.box = gtk.HBox()
    scroll.add_with_viewport(self.box)
    self.box.show()
    fadebttn = gtk.Button("Fade")
    self.box.pack_start(fadebttn, False, False)
    fadebttn.show()
    # Connect to liquidsoap
    self.tel = LiqClient(host,port)

    # Get info about the mixing table
    self.inputs = filter(lambda x: x!='',
        re.compile('\s*(\S+)\s*').split(self.tel.command(op+".inputs")))
    self.n = len(self.inputs)
    self.status = [ {} for e in range(self.n) ]

    # Create widgets
    remaining,skip,ready,volume,single,fadein,fadeout,selected=[ [] for i in range(8) ]
    def play_stop(i):
      self.tel.command(op+".select "+str(i)+" "+
        (strbool(not self.status[i]['selected'])))
    for i in range(self.n):
      # Add a controller
      scroll = gtk.ScrolledWindow()
      scroll.set_policy(gtk.POLICY_NEVER,gtk.POLICY_AUTOMATIC)
      self.box.pack_start(scroll)
      vbox = gtk.VBox()
      scroll.add_with_viewport(vbox)
      scroll.show()
      vbox.show()

      # Status line
      ready.append(gtk.Label())
      vbox.pack_start(ready[i],False,False,10)
      ready[i].set_markup('<b>'+self.inputs[i]+'</b>')
      ready[i].set_justify(gtk.JUSTIFY_CENTER)
      ready[i].show()

      # Two next buttons will be packed horizontally
      hbox = gtk.HBox()
      vbox.pack_start(hbox,False,False)
      hbox.show()

      # Play/stop button
      selected.append(gtk.Button('play/pause'))
      hbox.pack_start(selected[i])
      selected[i].show()
      selected[i].connect("clicked",lambda b,i:play_stop(i),i)

      # Skip
      skip.append(gtk.Button("skip"))
      skip[i].connect("clicked",
                   lambda button,i:
                     self.tel.command(op+".skip "+str(i)), i)
      hbox.pack_start(skip[i])
      skip[i].show()

      # Single mode
      single.append(gtk.CheckButton("Stop at end of track"))
      vbox.pack_start(single[i],False,False)
      single[i].show()
      single[i].connect("clicked",
          lambda button,i:
            self.tel.command(op+".single "+str(i)+" "+
              (strbool(single[i].get_active()))),i)

      # Fade in check box
      fade = gtk.HBox()
      vbox.pack_start(fade,False,False)
      lbl = gtk.Label("Fade: ")
      fade.pack_start(lbl)

      fadein.append(gtk.CheckButton("in"))
      fadeout.append(gtk.CheckButton("out"))
      fade.pack_start(fadein[i])
      fade.pack_start(fadeout[i])
      
      lbl.show()
      fadein[i].show()
      fadeout[i].show()
      fade.show()
      
      # Volume
      volume.append(gtk.Adjustment())
      vol=gtk.VScale(volume[i])
      vbox.pack_start(vol,True,True)
      vol.set_inverted(True)
      vol.set_increments(1,10)
      vol.set_range(0,100)
      vol.connect("value-changed",
          lambda l,i: self.tel.command(op+".volume "+str(i)+" "+
            str(volume[i].get_value())),i)
      vol.set_update_policy(gtk.UPDATE_CONTINUOUS)
      vol.show()

      # Remaining time
      remaining.append(gtk.Label("..."))
      vbox.pack_start(remaining[i],False,False)
      remaining[i].show()

    # Fade out all the fadeout_Checkbutton and fade in all the fadein_CB
    # during duration
    def fade_in(i):
      volume[i].set_value(volume[i].get_value() + 1)
      if volume[i].get_value() == 100:
        return False #No need to redo
      else:
        return True #Need to redo

    def fade_out(i):
      volume[i].set_value(volume[i].get_value() - 1)
      if volume[i].get_value() == 0:
        return False
      else:
        return True
        
    def fade_in_out():
      duration = 10000 # in ms
      for i in range(self.n):
        if fadein[i].get_active() and not fadeout[i].get_active():
          sleep_in = duration/(101 - volume[i].get_value())
          # print sleep_in
          gobject.timeout_add(int(sleep_in), lambda i:fade_in(i),i)
        elif fadeout[i].get_active() and not fadein[i].get_active():
          sleep_out = duration/(volume[i].get_value()+1)
          # print sleep_out
          gobject.timeout_add(int(sleep_out), lambda i:fade_out(i),i)
        elif fadeout[i].get_active() and fadein[i].get_active():
          pass
      # TODO

    fadebttn.connect("clicked", lambda y:fade_in_out())

    # TODO charmap dependent
    keys = list('aqAQw'+'zsZSx'+'edEDc'+'rfRFv'+'tgTGb'+'yhYHn')
    def keypress(vol,ev):
      try:
        # print ev.string, ev.keyval, ev.hardware_keycode
        i = keys.index(ev.string)
        if i%5==4:
          play_stop(i/5)
        elif i%5==2:
          fadein[i/5].set_active(not fadein[i/5].get_active())
        elif i%5==3:
          fadeout[i/5].set_active(not fadeout[i/5].get_active())
        else:
          volume[i/5].set_value(volume[i/5].get_value()+((i%5==0 and 1) or -1))
      except:
        pass
    self.set_events(gtk.gdk.KEY_PRESS_MASK)
    self.connect("key_press_event",keypress)

    def update():
      for i in range(self.n):
        a=filter(lambda x: x!='',
            re.compile('(\S+)=(\S+)\s*').split(
              self.tel.command(op+".status "+str(i))))
        self.status[i]={}
        for j in range(len(a)/2):
          self.status[i][a[2*j]]=a[2*j+1]
        self.status[i]['selected'] = (self.status[i]['selected']=='true')
        self.status[i]['ready'] = (self.status[i]['ready']=='true')

        selected[i].set_label(
            (self.status[i]['selected'] and "pause") or 'start')
        remaining[i].set_label('End of track: '+self.status[i]['remaining'])
        volume[i].set_value(int(re.sub('%','',self.status[i]['volume'])))
        if self.status[i]['selected']:
          ready[i].set_markup('<b>'+self.inputs[i]+'</b>\n'+
            ((self.status[i]['ready'] and 'Currently playing') or
              'Waiting for source'))
        else:
          ready[i].set_label('<b>'+self.inputs[i]+'</b>\nStopped, '+
              ((self.status[i]['ready'] and 'ready') or 'not ready'))
      return True

    gobject.timeout_add(1000,update)
    self.show()
Beispiel #16
0
  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)
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