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

βŒˆβŒ‹ βŽ‡ branch:  streamtuner2


Check-in [1a034aeac0]

Overview
Comment:Allow for subcategories in bookmarks.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:1a034aeac0f9eebffa8bcc00a831d28c36857c7a
User & Date: mario on 2018-12-18 22:40:04
Other Links: manifest | tags
Context
2018-12-18
22:40
Optional toolbar button for search dialog. check-in: 4beef0ad4d user: mario tags: trunk
22:40
Allow for subcategories in bookmarks. check-in: 1a034aeac0 user: mario tags: trunk
2018-12-17
22:19
Added crude support for binding internal calls `object.func()` to buttons. check-in: bbb93d412c user: mario tags: trunk
Changes

Modified channels/bookmarks.py from [557d39309e] to [0b438ee149].

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
...
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
        "search": [],
        "scripts": [],
        "timer": [],
        "history": []
    }
    default = "favourite"
    fixed_size = [32,24]



    # cache list, to determine if a PLS url is bookmarked
    urls = []

    def gui(self, parent):
        parent.notebook_channels.set_menu_label_text(parent.v_bookmarks, "bookmarks")

        GenericChannel.gui(self, parent)
        uikit.tree_column(self.gtk_cat, "Group")

    # this channel does not actually retrieve/parse data from anywhere
    def update_categories(self):
        pass



        
    # but category sub-plugins might provide a hook
    category_plugins = {}
    def update_streams(self, cat):

        if cat in self.category_plugins:
            return self.category_plugins[cat].update_streams(cat) or []
        else:
            return self.streams.get(cat, [])

        
    # streams are already loaded at instantiation
................................................................................
        if (not self.urls):
            self.urls = [str(row.get("url","urn:x-streamtuner2:no")) for row in self.streams["favourite"]]
        return str(url) in self.urls


    # called from main window / menu / context menu,
    # when bookmark is to be added for a selected stream entry
    def add(self, row):

        # Add / copy some row attributes
        row["favourite"] = 1
        if not row.get("favicon"):
            pass#   row["favicon"] = favicon.file(row.get("homepage"))
        if not row.get("listformat"):
            row["listformat"] = self.parent.channel().listformat
        if not len(row.get("extra", "")):
            row["extra"] = self.parent.channel().module

        # append to storage
        self.streams["favourite"].append(row)
        self.save()
        self.load(self.default)
        self.urls.append(row["url"])


    # simplified gtk TreeStore display logic (just one category for the moment, always rebuilt)
    def load(self, category, force=False, y=None):







>







>



|

|
>
>
>




<







 







|











|







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
...
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
        "search": [],
        "scripts": [],
        "timer": [],
        "history": []
    }
    default = "favourite"
    fixed_size = [32,24]
    reserved_names = ["favourite", "radiotray", "scripts", "search", "timer", "history", "links", "themes"] #+ self.parent.features.keys()


    # cache list, to determine if a PLS url is bookmarked
    urls = []

    def gui(self, parent):
        parent.notebook_channels.set_menu_label_text(parent.v_bookmarks, "bookmarks")
        self.update_categories()
        GenericChannel.gui(self, parent)
        uikit.tree_column(self.gtk_cat, "Group")

    # custom categories are shown as subfolder below `favourite`
    def update_categories(self):
        cust_cats = list(set(self.streams.keys()) - set(self.reserved_names))
        if len(self.categories) < 2 or type(self.categories[1]) is not list:
            self.categories.insert(1, [])
        self.categories[1] = cust_cats
        
    # but category sub-plugins might provide a hook
    category_plugins = {}
    def update_streams(self, cat):

        if cat in self.category_plugins:
            return self.category_plugins[cat].update_streams(cat) or []
        else:
            return self.streams.get(cat, [])

        
    # streams are already loaded at instantiation
................................................................................
        if (not self.urls):
            self.urls = [str(row.get("url","urn:x-streamtuner2:no")) for row in self.streams["favourite"]]
        return str(url) in self.urls


    # called from main window / menu / context menu,
    # when bookmark is to be added for a selected stream entry
    def add(self, row, target="favourite"):

        # Add / copy some row attributes
        row["favourite"] = 1
        if not row.get("favicon"):
            pass#   row["favicon"] = favicon.file(row.get("homepage"))
        if not row.get("listformat"):
            row["listformat"] = self.parent.channel().listformat
        if not len(row.get("extra", "")):
            row["extra"] = self.parent.channel().module

        # append to storage
        self.streams[target].append(row)
        self.save()
        self.load(self.default)
        self.urls.append(row["url"])


    # simplified gtk TreeStore display logic (just one category for the moment, always rebuilt)
    def load(self, category, force=False, y=None):

Added contrib/new_favourite_cat.py version [f1bb6d76fa].

































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
# encoding: utf-8
# api: streamtuner2
# title: New favourite category
# description: Introduces new bookmarks categories
# version: 0.2
# type: feature
# category: ui
# config: -
# priority: optional
# 
# Adds a "New favourite category..." under Station > Extensions menu.
# New categories will show up in the bookmarks channel under favourite.
#

from uikit import *
from config import *

# 
class new_favourite_cat (object):
    plugin = "new_favourite_cat"
    meta = plugin_meta()
    parent = None
    w = None

    # hook up menu entry
    def __init__(self, parent):
        self.parent = parent
        uikit.add_menu([parent.extensions], "New favourite category…", self.win, insert=3)
        self.create_submenu(parent)
        self.update_submenu()
  
    # show input window
    def win(self, *w):
        w = self.w = gtk.Dialog(
            'New bookmark category',
            self.parent.win_streamtuner2, 
            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_APPLY)
        )
        input = gtk.Entry(35)
        w.vbox.pack_start(input)
        log.UI(w.vbox.get_children())
        w.show_all()
        self.add(self.w, w.run(), input.get_text())

    # add category
    def add(self, w, r, title):
        bm = self.parent.bookmarks
        have = bm.streams.has_key(title)
        w.destroy()
        if r == gtk.RESPONSE_APPLY:
            log.NEW(title)
            if not have:
                bm.streams[title] = {}
        if r == gtk.RESPONSE_DELETE_EVENT:
            if have:
                bm.streams.remove(title)
        self.update_submenu()
        bm.update_categories()
        bm.display_categories()

    # introduce MenuItem+Menu
    def create_submenu(self, parent):
        self.submenu = gtk.Menu()
        for title, target, i in [("Bookmark to", parent.streammenu, 1), ("Add bookmark to", parent.streamactions, 3)]:
            mi = gtk.ImageMenuItem(gtk.STOCK_INDENT)
            mi.set_submenu(self.submenu)
            mi.set_label(title)
            mi.show_all()
            target.insert(mi, i)

    # bookmark to > … submenu w/ custom categories
    def update_submenu(self):
        bmc = self.parent.bookmarks.categories
        [self.submenu.remove(w) for w in self.submenu.get_children()]
        if len(bmc) >= 2 and type(bmc[1]) is list:
            for label in bmc[1]:
                uikit.add_menu([self.submenu], label, lambda w,target=label: self.parent.bookmark(w, target))
        self.submenu.show_all()
    

Modified gtk3.xml.gz from [22b92b2dc9] to [1691d2599f].

cannot compute difference between binary files

Modified st2.py from [9e34a20f96] to [0aae622bbd].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#!/usr/bin/env python
# encoding: UTF-8
# api: python
# type: application
# title: streamtuner2
# description: Directory browser for internet radio, audio and video streams
# version: 2.2.1-dev20180820
# state: stable
# 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 }
................................................................................
    def on_category_clicked(self, widget, event, *more):
        category = self.channel().currentcat()
        log.UI("on_category_clicked", category, self.current_channel)
        self.on_reload_clicked(None, reload=0)
        pass

    # Add current selection to bookmark store
    def bookmark(self, widget):
        self.bookmarks.add(self.row())
        self.channel().row_icon(gtk.STOCK_ABOUT)
        # refresh bookmarks tab
        self.bookmarks.load(self.bookmarks.default)

    # Reload category tree
    def update_categories(self, widget):
        self.thread(self.channel().reload_categories)






|







 







|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#!/usr/bin/env python
# encoding: UTF-8
# api: python
# type: application
# title: streamtuner2
# description: Directory browser for internet radio, audio and video streams
# version: 2.2.1-dev20181218
# state: stable
# 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 }
................................................................................
    def on_category_clicked(self, widget, event, *more):
        category = self.channel().currentcat()
        log.UI("on_category_clicked", category, self.current_channel)
        self.on_reload_clicked(None, reload=0)
        pass

    # Add current selection to bookmark store
    def bookmark(self, widget, target="favourite"):
        self.bookmarks.add(self.row(), target)
        self.channel().row_icon(gtk.STOCK_ABOUT)
        # refresh bookmarks tab
        self.bookmarks.load(self.bookmarks.default)

    # Reload category tree
    def update_categories(self, widget):
        self.thread(self.channel().reload_categories)