Internet radio browser GUI for music/video streams from various directory services.

⌈⌋ ⎇ branch:  streamtuner2


Diff

Differences From Artifact [05a9ab14f0]:

To Artifact [d6067edad1]:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
#
# encoding: UTF-8
# api: python
# type: application
# title: streamtuner2
# description: Directory browser for internet radio / audio streams
# depends: pygtk | gi, threading, requests, pyquery, lxml, deb:python-pyquery, deb:python-requests, deb:python-gtk2
# version: 2.1.4
# author: Mario Salzer <milky@users.sf.net>
# license: public domain
# url: http://freshcode.club/projects/streamtuner2
# config:  
#   { type: env, name: http_proxy, description: proxy for HTTP access }
#   { type: env, name: XDG_CONFIG_HOME, description: relocates user .config subdirectory }
# category: sound
# id: streamtuner2









|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
#
# encoding: UTF-8
# api: python
# type: application
# title: streamtuner2
# description: Directory browser for internet radio / audio streams
# depends: pygtk | gi, threading, requests, pyquery, lxml, deb:python-pyquery, deb:python-requests, deb:python-gtk2
# version: 2.1.4
# author: Mario Salzer <mario@include-once.org>
# license: public domain
# url: http://freshcode.club/projects/streamtuner2
# config:  
#   { type: env, name: http_proxy, description: proxy for HTTP access }
#   { type: env, name: XDG_CONFIG_HOME, description: relocates user .config subdirectory }
# category: sound
# id: streamtuner2
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
sys.path.insert(0, ".")   # development module path

# gtk modules
from mygtk import pygtk, gtk, gobject, ui_file, mygtk, ver as GTK_VER, ComboBoxText, gui_startup

# custom modules
from config import conf   # initializes itself, so all conf.vars are available right away
from config import __print__, dbg
import ahttp
import action  # needs workaround... (action.main=main)
import channels
from channels import *
import favicon
import channels.bookmarks








|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
sys.path.insert(0, ".")   # development module path

# gtk modules
from mygtk import pygtk, gtk, gobject, ui_file, mygtk, ver as GTK_VER, ComboBoxText, gui_startup

# custom modules
from config import conf   # initializes itself, so all conf.vars are available right away
from config import __print__, dbg, plugin_meta
import ahttp
import action  # needs workaround... (action.main=main)
import channels
from channels import *
import favicon
import channels.bookmarks

401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
                mygtk.do(lambda:self.statusbar.push(sbar_cid, text))
            pass


        # load plugins from /usr/share/streamtuner2/channels/
        def load_plugin_channels(self):

            # find and order plugin files
            ls = channels.module_list()

            # step through
            for module in ls:
                gui_startup(2/10.0 + 7/10.0 * float(ls.index(module))/len(ls), "loading module "+module)
                                
                # skip module if disabled
                if conf.plugins.get(module, 1) == False:
                    __print__(dbg.STAT, "disabled plugin:", module)
                    continue







|

<
<







401
402
403
404
405
406
407
408
409


410
411
412
413
414
415
416
                mygtk.do(lambda:self.statusbar.push(sbar_cid, text))
            pass


        # load plugins from /usr/share/streamtuner2/channels/
        def load_plugin_channels(self):

            # initialize plugin modules (pre-ordered)
            ls = channels.module_list()


            for module in ls:
                gui_startup(2/10.0 + 7/10.0 * float(ls.index(module))/len(ls), "loading module "+module)
                                
                # skip module if disabled
                if conf.plugins.get(module, 1) == False:
                    __print__(dbg.STAT, "disabled plugin:", module)
                    continue
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
        current = None

        # show search dialog   
        def menu_search(self, w):
            self.search_dialog.show();
            if not self.current or main.current_channel != "bookmarks":
                self.current = main.current_channel
                self.search_dialog_current.set_label("just %s" % main.channels[self.current].title)


        # hide dialog box again
        def cancel(self, *args):
            self.search_dialog.hide()
            return True  # stop any other gtk handlers
            







|







542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
        current = None

        # show search dialog   
        def menu_search(self, w):
            self.search_dialog.show();
            if not self.current or main.current_channel != "bookmarks":
                self.current = main.current_channel
                self.search_dialog_current.set_label("just %s" % main.channels[self.current].meta["title"])


        # hide dialog box again
        def cancel(self, *args):
            self.search_dialog.hide()
            return True  # stop any other gtk handlers
            
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
            entries = []
            for i,cn in enumerate([main.channels[c] for c in self.targets]):
                if cn.has_search:  # "search" in cn.update_streams.func_code.co_varnames:
                    __print__(dbg.PROC, "has_search:", cn.module)
                    try:
                        add = cn.update_streams(cat=None, search=self.q)
                        for row in add:
                            row["genre"] = cn.title + " " + row.get("genre", "")
                        entries += add
                    except:
                        continue
                #main.status(main, 1.0 * i / 15)
            self.show_results(entries)









|







602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
            entries = []
            for i,cn in enumerate([main.channels[c] for c in self.targets]):
                if cn.has_search:  # "search" in cn.update_streams.func_code.co_varnames:
                    __print__(dbg.PROC, "has_search:", cn.module)
                    try:
                        add = cn.update_streams(cat=None, search=self.q)
                        for row in add:
                            row["genre"] = cn.meta["title"] + " " + row.get("genre", "")
                        entries += add
                    except:
                        continue
                #main.status(main, 1.0 * i / 15)
            self.show_results(entries)


795
796
797
798
799
800
801



802





803
804
805
806
807
808
809
810
811
812

813
814
815
816
817
818
819
        def apply_theme(self):
            conf.theme = self.theme.get_active_text()
            main.load_theme()


        # iterate over channel and feature plugins
        def add_plugins(self):



            for name,plugin in main.channels.iteritems():





                self.add_plg(name, plugin, plugin.meta)
            self.plugin_options.pack_start(mygtk.label("\n<b>Feature</b> plugins add categories, submenu entries, or other extensions.\n", 500, 1))
            for name,plugin in main.features.iteritems():
                self.add_plg(name, plugin, plugin.meta)

        # add configuration setting definitions from plugins
        def add_plg(self, name, c, meta):
            # add plugin load entry
            cb = gtk.CheckButton(name)
            cb.get_children()[0].set_markup("<b>%s</b> <i>(%s)</i> %s\n<small>%s</small>" % (meta["title"], meta["type"], meta.get("version", ""), meta["description"]))

            self.add_( "config_plugins_"+name, cb )

            # default values are already in conf[] dict (now done in conf.add_plugin_defaults)
            for opt in meta["config"]:
                color = opt.get("color", None)
                # display checkbox
                if opt["type"] == "boolean":







>
>
>
|
>
>
>
>
>
|
|
<
<


|


|
>







793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810


811
812
813
814
815
816
817
818
819
820
821
822
823
824
        def apply_theme(self):
            conf.theme = self.theme.get_active_text()
            main.load_theme()


        # iterate over channel and feature plugins
        def add_plugins(self):
            ls = {}
            for name in channels.module_list():
                if name in main.channels:
                    ls[name] = main.channels[name].meta
                elif name in main.features:
                    ls[name] = main.features[name].meta
                else:
                    ls[name] = plugin_meta(conf.share+"/channels/"+name+".py")
            for name,meta in sorted(ls.items(), key=lambda e: e[1]["type"]+e[1]["title"].lower(), reverse=False):
                self.add_plg(name, meta)
            #self.plugin_options.pack_start(mygtk.label("\n<b>Feature</b> plugins add categories, submenu entries, or other extensions.\n", 500, 1))



        # add configuration setting definitions from plugins
        def add_plg(self, name, meta):
            # add plugin load entry
            cb = gtk.CheckButton(name)
            cb.get_children()[0].set_markup("<b>%s</b> <i>(%s)</i> %s\n<small>%s</small>" % (meta.get("title", name), meta.get("type", "unknown"), meta.get("version", "./."), meta.get("description", "no description")))
            cb.set_tooltip_text(meta.get("doc", "").strip())
            self.add_( "config_plugins_"+name, cb )

            # default values are already in conf[] dict (now done in conf.add_plugin_defaults)
            for opt in meta["config"]:
                color = opt.get("color", None)
                # display checkbox
                if opt["type"] == "boolean":