Index: action.py ================================================================== --- action.py +++ action.py @@ -118,48 +118,49 @@ # Preferred probing order of known formats playlist_fmt_prio = [ "pls", "xspf", "asx", "smil", "jamj", "json", "m3u", "asf", "raw" ] +# custom stream domain handlers +handler = { + # "soundcloud": callback(), +} + # Exec wrapper -# def run(cmd): log.EXEC(cmd) try: os.system("start \"%s\"" % cmd if conf.windows else cmd + " &") except: log.ERR("Command not found:", cmd) - -# Start web browser -# -def browser(url): - bin = conf.play.get("url/http", "sensible-browser") - log.EXEC(bin) - run(bin + " " + quote(url)) - - # Open help browser, streamtuner2 pages -# def help(*args): run("yelp /usr/share/doc/streamtuner2/help/") +# Invokes player/recorder for stream url and format +def run_fmt_url(row={}, audioformat="audio/mpeg", source="pls", url=None, assoc={}): + if not url: + url = row["url"] + if audioformat in handler: + handler[audioformat](row, audioformat, source, url, assoc) + else: + cmd = mime_app(audioformat, assoc) + cmd = interpol(cmd, url, source, row) + run(cmd) + +# Start web browser +def browser(url): + run_fmt_url({}, "url/http", "srv", url, conf.play) # Calls player for stream url and format -# def play(row={}, audioformat="audio/mpeg", source="pls", url=None): - cmd = mime_app(audioformat, conf.play) - cmd = interpol(cmd, url or row["url"], source, row) - run(cmd) + run_fmt_url(row, audioformat, source, url, conf.play) - -# Call streamripper -# +# Call streamripper / youtube-dl / wget def record(row={}, audioformat="audio/mpeg", source="href", url=None): - cmd = mime_app(audioformat, conf.record) - cmd = interpol(cmd, url or row["url"], source, row) - run(cmd) + run_fmt_url(row, audioformat, source, url, conf.record) # OS shell command escaping # def quote(ins): DELETED contrib/cfg_soundcloud.py Index: contrib/cfg_soundcloud.py ================================================================== --- contrib/cfg_soundcloud.py +++ contrib/cfg_soundcloud.py @@ -1,27 +0,0 @@ -# api: streamtuner2 -# title: Soundcloud player -# description: Just sets a new configuration option for `soundcli` -# version: -1 -# url: http://elephly.net/soundcli.html -# priority: once -# type: config -# category: player -# -# You only need to run this plugin once. It just adds an -# entry for "audio/soundcloud" in the player config list. - -from config import * - -# just once -class cfg_soundcloud(object): - - module = "cfg_soundcloud" - fmt = "audio/soundcloud" - cmd = "xterm -e \"soundcli stream %srv\"" - - def __init__(self, *a, **kw): - conf.play.setdefault(self.fmt, self.cmd) - print self.module - conf.plugins[self.module] = False - - Index: contrib/reddit.py ================================================================== --- contrib/reddit.py +++ contrib/reddit.py @@ -6,11 +6,11 @@ # type: channel # url: http://reddit.com/r/Music # category: playlist # config: # { name: reddit_pages, type: int, value: 2, description: Number of pages to fetch. } -# { name: kill_soundcloud, type: boolean, value: 1, description: Filter soundcloud/spotify/etc if there's no player configured. } +# { name: filter_walledgardens, type: boolean, value: 1, description: Filter walled gardens (soundcloud/spotify/…) if there's no player. } # { name: reddit_keep_all, type: boolean, value: 0, description: Keep all web links (starts a browser for websites/news). } # png: # iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJ1BMVEUAAAAcICX/AABHSk1jZ299hYz/bmajq6//lY/d0M3C1+3T7P38+/iaLhuGAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF # HUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQffBRUXIyQbWArCAAAAh0lEQVQI12Pg3g0BDLtXrVq1eveq3Qy7gIxCU9dqEGO11/ZKbzBDenUIUM3u7cGi1UDFW0TE55wsdpZikAw/ # eebMnMmHGVxqDuUc0zzpynD4zIk5J3vOSDNsOQMG1gy7bI5HTq85Ws2wu/jM9PIzrkArdhmXlzuuXg00eVd5+epVqxmgrtgNAOWeS1KYtcY4AAAAAElFTkSuQmCC @@ -33,10 +33,11 @@ import json import re from config import * from channels import * +import action import ahttp # reddit.com # @@ -277,22 +278,22 @@ format = "video/youtube" # check for specific web links (Soundcloud etc.) else: listformat = "srv" format = None - for urltype in ("soundcloud", "spotify", "bandcamp", "mixcloud"): - if row["url"].find(urltype) > 0: - # is a specific player configured? - fmt = "audio/" + urltype - if fmt in conf.play: - state = "gtk-media-forward" - format = fmt - # retain it as web link? - elif not conf.kill_soundcloud: - state = "gtk-media-pause" - format = "url/http" - break + # look for walled gardens + urltype = re.findall("([\w-]+)\.\w+/", row["url"] + "/x-unknown.com/")[0] + if urltype in ("soundcloud", "spotify", "bandcamp", "mixcloud"): + # is a specific player configured? + fmt = "audio/" + urltype + if fmt in conf.play or fmt in action.handler: + state = "gtk-media-forward" + format = fmt + # retain it as web link? + elif not conf.filter_walledgardens: + state = "gtk-media-pause" + format = "url/http" # else skip entry completely if not format: if conf.reddit_keep_all: state = "gtk-page-setup" format = "url/http" ADDED contrib/url_soundcloud.py Index: contrib/url_soundcloud.py ================================================================== --- contrib/url_soundcloud.py +++ contrib/url_soundcloud.py @@ -0,0 +1,78 @@ +# api: streamtuner2 +# title: Soundcloud streams +# description: Convert soundcloud links from reddit to streamable tracks +# version: 0.1 +# type: filter +# category: audio +# depends: python:soundcloud, action >= 1.0, reddit >= 0.6 +# priority: rare +# +# Overrides action.play() function to convert soundcloud URLs +# to track/streaming address. Disables the reddit filter for +# walled gardens, and overrides any custom player configured +# for "audio/soundcloud" in settings. + + +import re +import soundcloud +from config import * +import ahttp +import action + +fmt = "audio/soundcloud" +rx_url = re.compile("^https?://(www\.)?soundcloud\.com/[\w-]+/[\w-]+$") +conn = None + + +# API connect +def client(): + global conn + if not conn: + conn = soundcloud.Client(client_id="f0aea6e0484043f6638cb5bf35d43312") + return conn + +# Capture play events for faux MIME type +def sndcl_convert(row={}, audioformat="audio/mpeg", source="pls", url=None, assoc={}): + if audioformat==fmt or rx_url.match(url): + + # find streaming address + try: + log.DATA_CONVERT_SOUNDCLOUD(url) + track = client().get('/resolve', url=url) + track_str = "/tracks/{}/stream".format(track.id) + url = client().get(track_str, allow_redirects=False).location + + # override attributes + row["url"] = url + source = "srv" + audioformat = "audio/mpeg" + + except Exception as e: + log.ERR_SOUNDCLOUD("URL resolving failed:", e) + + # let web browser run + audioformat = "url/http" + + # let primary handler take over + if audioformat != fmt: + return action.run_fmt_url(row, audioformat, source, url, assoc) + + +# Hook up custom action.handler for soundcloud URLs +# +# Still somewhat hodgepodge. The action module just lets .play() params +# rewrite by above handler. Should turn faux "audio/soundcloud" URL into +# plain/longwinded MP3 streaming address. +# +# Would need more generalized processing of custom URL schemes. But so +# far only the reddit module uses them anyway. +# +class url_soundcloud(object): + module = "url_soundcloud" + + # override action.play() with wrapper + def __init__(self, parent, *a, **kw): + conf.play[fmt] = "false / convert" + #conf.filter_walledgardens = False + action.handler[fmt] = sndcl_convert +