Check-in [e0e28edba2]
Overview
Comment: | Experiment with a few more target types. Works with a few text editors on STRING. Most just want a uri-list, but can't handle it. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
e0e28edba26bf906967648092b554b80 |
User & Date: | mario on 2015-04-19 19:35:57 |
Other Links: | manifest | tags |
Context
2015-04-19
| ||
22:17 | Implement in-application row copying per JSON (info=51, mime=json/vnd.streamtuner2.station). Fixed set_text() bug by using set("STRING",..) atom instead. check-in: 38812e4bbf user: mario tags: trunk | |
19:35 | Experiment with a few more target types. Works with a few text editors on STRING. Most just want a uri-list, but can't handle it. check-in: e0e28edba2 user: mario tags: trunk | |
19:35 | Add plugin defaults. check-in: 6a17061df0 user: mario tags: trunk | |
Changes
Modified action.py from [820487cd33] to [ceb9b4a4e2].
︙ | ︙ | |||
387 388 389 390 391 392 393 | row["url"] = url new_rows.append(row) # Or just allow one stream per station in a playlist entry if not self.multiply: break rows = new_rows | | | 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 | row["url"] = url new_rows.append(row) # Or just allow one stream per station in a playlist entry if not self.multiply: break rows = new_rows debug(dbg.DATA, "conversion to:", dest, " with rows=", rows) # call conversion schemes converter = getattr(self, dest) or self.pls return converter(rows) # save directly def file(self, rows, dest, fn): |
︙ | ︙ | |||
458 459 460 461 462 463 464 | # SMIL def smil(self, rows): txt = """<smil>\n<head>\n\t<meta name="title" content="%s"/>\n</head>\n<body>\n\t<seq>\n""" % (rows[0]["title"]) for row in rows: if row.get("url"): | | | 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | # SMIL def smil(self, rows): txt = """<smil>\n<head>\n\t<meta name="title" content="%s"/>\n</head>\n<body>\n\t<seq>\n""" % (rows[0]["title"]) for row in rows: if row.get("url"): txt += """\t\t<{} src="{}"/>\n""".format(row.get("format", "audio").split("/")[0], row["url"]) txt += """\t</seq>\n</body>\n</smil>\n""" return txt # Generate filename for temporary .m3u, if possible with unique id |
︙ | ︙ |
Modified channels/dnd.py from [2bdcd36106] to [3ecc3ca6e1].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # encoding: UTF-8 # api: streamtuner2 # title: Drag and Drop # description: Move streams/stations from and to other applications. # depends: uikit # version: 0.1 # type: interface # category: ui # # Implements Gtk/X11 drag and drop support for station lists. # Should allow to export either just stream URLs, or complete # PLS, XSPF collections. # # Also used by the bookmarks tab to move favourites around. import copy | > > > > > > > > > > | > > > | > > > > > | > > > > > > > > > > | | < > < | > > > > | | | < > | < < < | | > | > > > > > | | | | | < > > | > | > | 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 185 | # encoding: UTF-8 # api: streamtuner2 # title: Drag and Drop # description: Move streams/stations from and to other applications. # depends: uikit # version: 0.1 # type: interface # config: # { name: dnd_format, type: select, value: pls, select: "pls|m3u|xspf|jspf|asx|smil", description: "Default temporary file format for copying a station entry." } # category: ui # priority: experimental # # Implements Gtk/X11 drag and drop support for station lists. # Should allow to export either just stream URLs, or complete # PLS, XSPF collections. # # Also used by the bookmarks tab to move favourites around. # mousepad == ['GTK_TEXT_BUFFER_CONTENTS', 'application/x-gtk-text-buffer-rich-text', # 'UTF8_STRING', 'COMPOUND_TEXT', 'TEXT', 'STRING', # 'text/plain;charset=utf-8', 'text/plain'] # libreoffice ==# ['text/plain;charset=utf-8', 'UTF8_STRING', 'application/x-openoffice-embed-source-xml;windows_formatname="Star Embed# Source (XML)"', 'text/richtext', 'text/html', # 'application/x-openoffice-objectdescriptor-xml;windows_formatname="Star Object Descriptor (XML)";classname="8BC6B165-B1B2-4EDD-aa47-dae2ee689dd6";typename="LibreOffice 4.4 Textdokument";viewaspect="1";width="16999";height="2995";posx="5347";posy="5347"'] import copy from config import conf, __print__, dbg, json from uikit import * import action # Drag and Drop support class dnd(object): module = "dnd" meta = plugin_meta() # Keeps selected row on starting DND event row = None # Buffer converted types buf = {} # Supported type map drag_types = [ # internal ("json/vnd.streamtuner2.station", 0, 51), # literal exports ("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), # direct srv urls ("text/url", 0, 15), #@TODO: support in action.save_/convert_ ("message/external-body", 0, 15), ("url/direct", 0, 15), # url+comments ("TEXT", 0, 5), ("STRING", 0, 5), ("UTF8_STRING", 0, 5), ("text/plain", 0, 5), # filename, file:// IRL ("FILE_NAME", 0, 3), ("text/uri-list", 0, 4), ] cnv_types = { 20: "m3u", 21: "pls", 22: "xspf", 23: "smil", 25: "jspf", 15: "srv", 4: "temp", 5: "srv", 51: "json", } # Hook to main, and extend channel tabs def __init__(self, parent): self.parent = parent parent.hooks["init"].append(self.add_dnd) conf.add_plugin_defaults(self.meta, self.module) # Attach drag and drop handlers to each channelsĀ“ station TreeView def add_dnd(self, parent): # visit each module for cn,module in parent.channels.items(): w = module.gtk_list # bind SOURCE events w.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, self.drag_types, gtk.gdk.ACTION_DEFAULT|gtk.gdk.ACTION_COPY|gtk.gdk.ACTION_MOVE) w.connect('drag-begin', self.begin) w.connect('drag-data-get', self.data_get) # bind DESTINATION events w.enable_model_drag_dest(self.drag_types, gtk.gdk.ACTION_DEFAULT|gtk.gdk.ACTION_COPY) w.connect('drag-drop', self.drop) w.connect('drag-data-received', self.data_received) # -- SOURCE, drag'n'drop from ST2 to elsewhere -- # Starting to drag a row def begin(self, widget, context): __print__(dbg.UI, "dndāsource: begin-drag, store current row") self.row = self.treelist_row() self.buf = {} uikit.do(context.set_icon_stock, gtk.STOCK_ADD, 16, 16) return "url" in self.row # Keep currently selected row when source dragging starts def treelist_row(self): cn = self.parent.channel() row = copy.copy(cn.row()) row.setdefault("format", cn.audioformat) row.setdefault("listformat", cn.listformat) row.setdefault("url", row.get("homepage")) return row # Target window/app requests data for offered drop def data_get(self, widget, context, selection, info, time): __print__(dbg.UI, "dndāsource: data-get, send and convert to requested target type", info, selection.get_target()) # Start new converter if not buffered (because `data_get` gets called mercilessly along the dragging path) if not info in self.buf: r = self.row cnv = action.save_playlist(source=r["listformat"], multiply=False) # internal JSON row info = 5 if info >= 51: buf = 'text', json.dumps(r) # Pass M3U/PLS/XSPF as literal payload elif info >= 20: buf = 'text', cnv.export(urls=[r["url"]], row=r, dest=self.cnv_types[info]) # Direct server URL elif info >= 10: urls = action.convert_playlist(r["url"], r["listformat"], "srv", False, r) #buf = 'uris', urls buf = 'text', urls[0] # Text sources are assumed to understand the literal URL or expect a description block elif info >= 5: buf = 'text', "{url}\n# Title: {title}\n# Homepage: {homepage}\n\n".format(**r) # Create temporary PLS file, because "text/uri-list" is widely misunderstood and just implemented for file:// IRLs else: tmpfn = "{}/{}.{}".format(conf.tmp, re.sub("[^\w-]+", " ", r["title"]), conf.dnd_format) cnv.file(rows=[r], dest=conf.dnd_format, fn=tmpfn) buf = 'uris', ["file://{}".format(tmpfn)] if (info==4) else tmpfn # Keep in type request buffer self.buf[info] = buf # Return prepared data func, data = self.buf[info] if func.find("text") >= 0: selection.set_text(data, len(data)) if func.find("uris") >= 0: selection.set_uris(data) return True # -- DESTINATION, when playlist/url gets dragged in from other app -- # Just a notification for incoming drop def drop(self, widget, context, x, y, time): __print__(dbg.UI, "dndādest: drop-probing", context.targets, x, y, time, context.drag_get_selection()) widget.drag_get_data(context, "STRING", time)#context.targets[0], time) context.drop_reply(True, time) return True # Actual data is being passed, # now has to be converted and patched into stream rows and channel liststore def data_received(self, widget, context, x, y, selection, info, time): __print__(dbg.UI, "dndādest: data-receival", info, selection.get_target(), selection.get_uris(), selection.get_text()) context.drop_finish(True, time) context.finish(True, False, time) return True |