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

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


Diff

Differences From Artifact [08769a579d]:

To Artifact [badcdb5ec3]:


1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16








-
+







# encoding: UTF-8
# api: streamtuner2
# title: Drag and Drop (experimental)
# description: Copy streams/stations from and to other applications.
# depends: uikit
# version: 0.5
# type: interface
# config:
#   { name: dnd_format, type: select, value: xspf, select: "pls|m3u|xspf|jspf|asx|smil", description: "Default temporary file format for copying a station." }
#   { name: dnd_format, type: select, value: xspf, select: "pls|m3u|xspf|jspf|asx|smil|desktop", description: "Default temporary file format for copying a station." }
# category: ui
# priority: default
# support: experimental
#
# Implements Gtk/X11 drag and drop support for station lists.
# Should allow to export either just stream URLs, or complete
# PLS, XSPF collections.
70
71
72
73
74
75
76

77
78
79
80
81
82
83
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84







+







      ("audio/x-mpegurl", 0, 20),
      ("application/x-scpls", 0, 21),
      ("application/xspf+xml", 0, 22),
      ("application/smil", 0, 23),
      ("text/html", 0, 23),
      ("text/richtext", 0, 23),
      ("application/jspf+json", 0, 25),
      ("application/x-desktop", 0, 26),
      # direct srv urls
      ("text/url", 0, 15),  #@TODO: support in action.save_/convert_
      ("message/external-body", 0, 15),
      ("url/direct", 0, 15),
      # filename, file:// IRL
      ("FILE_NAME", 0, 3),
      ("text/uri-list", 0, 4),
91
92
93
94
95
96
97

98
99
100
101
102
103
104
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106







+







    # Map target/`info` integers to action. module identifiers
    cnv_types = {
       20: "m3u",
       21: "pls",
       22: "xspf",
       23: "smil",
       25: "jspf",
       26: "desktop",
       15: "srv",
        4: "temp",
        5: "srv",
       51: "json",
    }


192
193
194
195
196
197
198
199

200
201
202
203
204
205
206
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208







-
+








        # Keep in type request buffer
        self.buf[info] = buf
        return buf



    # -- DESTINATION, when playlist/file gets dragged in from other app --
    # -- DESTINATION, when playlist/file gets dragged into ST2 from other app --

    # Just a notification for incoming drop
    def drop(self, widget, context, x, y, time):
        log.DND("dest←in: drop-probing, possible targets:", context.targets)
        # find a matching target
        accept = [type[0] for type in self.drag_types if type[0] in context.targets]
        context.drop_reply(len(accept) > 0, time)
227
228
229
230
231
232
233
234
235
236
237
238

239
240
241
242

243
244
245
246
247
248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263

264
265
266
267
268
269






270
271
272
273
274
229
230
231
232
233
234
235

236
237
238

239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261





262






263
264
265
266
267
268
269
270
271
272
273







-



-
+




+






+










-
-
-
-
-
+
-
-
-
-
-
-
+
+
+
+
+
+





        return True

    # Received files or payload has to be converted, copied into streams
    def import_row(self, info, urls, data, y=5000):
        # Internal target dicts
        cn = self.parent.channel()
        rows = []
        print info
                
        # Direct/internal row import
        if data and info >= 51:
            log.DND("Received row, append, reload")
            log.DND("Received row in internal format, append+reload")
            rows += [ json.loads(data) ]

        # Convertible formats as direct payload
        elif data and info >= 5:
            log.DND("Converting direct payload playlist")
            cnv = action.extract_playlist(data)
            add = cnv.rows(self.cnv_types[info] if info>=20 else cnv.probe_fmt() or "raw")
            rows += [ cnv.mkrow(row) for row in add ]

        # Extract from playlist files, either passed as text/uri-list or single FILE_NAME
        elif urls:
            log.DND("Importing from playlist file")
            for fn in urls or [data]:
                if not re.match("^(scp|file)://(localhost)?/|/", fn):
                    continue
                fn = compat2and3.urldecode(re.sub("^\w+://[^/]*", "", fn))
                cnv = action.extract_playlist(fn=fn)
                if cnv.src:
                    rows += [ cnv.mkrow(row) for row in cnv.rows() ]
        
        # Insert and update view
        if rows:
            # Inserting at correct row requires deducing index from dnd `y` position
            streams = cn.streams[cn.current]
            i_pos = (cn.gtk_list.get_path_at_pos(10, y) or [[len(streams) + 1]])[0][0]
            for row in rows:
                streams.insert(i_pos - 1, row)
            cn.insert_rows(rows, y)
                i_pos = i_pos + 1
            # Now appending to the liststore directly would be even nicer
            uikit.do(lambda *x: cn.load(cn.current))#, cn.gtk_list.scroll_to_point(0, y))
            if cn.module == "bookmarks":
                cn.save()
            #self.parent.streamedit()
            # if cn.module == "bookmarks":
            cn.save()
            # Show streamedit window if title is empty
            if not len(rows[0].get("title", "")):
                self.parent.configwin.load_config(rows[0], "streamedit_")
                self.parent.win_streamedit.show()
        else:
            self.parent.status("Unsupported station format. Not imported.")