Index: channels/bookmarks.py ================================================================== --- channels/bookmarks.py +++ channels/bookmarks.py @@ -40,10 +40,11 @@ listformat = "any" categories = ["favourite", ] # timer, links, search, and links show up as needed finder_song = { "genre": "Youtube ", "format": "video/youtube", "playing": "current_", "title": "The Finder song", "url": "http://youtube.com/v/omyZy4H8y9M", "homepage": "http://youtu.be/omyZy4H8y9M" } streams = {"favourite":[finder_song], "search":[], "scripts":[], "timer":[], "history":[], } default = "favourite" + fixed_size = [32,24] # cache list, to determine if a PLS url is bookmarked urls = [] ADDED contrib/scripts.py Index: contrib/scripts.py ================================================================== --- contrib/scripts.py +++ contrib/scripts.py @@ -0,0 +1,128 @@ +# encoding: UTF-8 +# api: streamtuner2 +# title: Script stations +# description: User scripts for individual stations +# type: feature +# category: bookmark +# version: 0.1 +# priority: theoretical +# +# This plugin provides for a simpler alternative to channel plugins. +# It reads the ~./config/streamtuner2/script/ directory for script +# files, and shows them in the bookmarks channel. Each script may +# scan/uncover a station url at runtime. +# Which obviously isn't meant for easily parseable stations, but for +# the more difficult cases. +# +# There's support for python scripts obviously, but any executable +# file (scripting language) can be run and queried for urls. +# +# Each is supposed to contain a meta comment block (much like this +# plugin), except it's using more stream-oriented descriptors: +# +# #!/bin/sh +# # title: Station title +# # description: Fetching a live stream +# # genre: jazz +# # homepage: http://example.org/ +# # +# # Now normally, it would not just print something static. +# +# echo "http://example.org/.mp3" +# +# Obviously the purpose is more complicated extractions. Which is +# why a .py script had acces to all ST2s parsing tools already. +# +# This is implemented using the action.handler hooks for urn: +# modules. But ensures the resolver script is run each time - by +# not caching the final stream url. +# Conversly this plugin prevents editing of script station entries. +# + + +import os, shutil, copy, subprocess, sys, StringIO +import csv, zipfile +import re, json, pq +import ahttp +import config +from config import * +import uikit +from compat2and3 import * +import action +from channels import * + + +# dynamic station extractors from ~/.config/streamtuner2/scripts/ +class scripts (object): + + # plugin info + module = "scripts" + meta = plugin_meta() + parent = None + dir = conf.dir + "/scripts" + + # register hooks + def __init__(self, parent): + if not os.path.exists(self.dir): + os.mkdir(self.dir) + self.parent = parent + self.bm = parent.bookmarks + action.handler["urn:script"] = self.urn_resolve + self.bm.add_category("scripts") + self.bm.category_plugins["scripts"] = self + + # find script files and compile station dicts + def update_streams(self, cat): + r = [] + for fn in os.listdir(self.dir): + meta = config.plugin_meta(fn=self.dir+"/"+fn) + r.append(dict( + genre = meta.get("genre", meta.get("type", "script")), + title = meta.get("title", fn), + playing = meta.get("description", meta.get("playing", "")), + homepage = meta.get("homepage", ""), + listeners = to_int(meta.get("listeners", "1")), + bitrate = to_int(meta.get("bitrate", "64")), + format = meta.get("format", "audio/mp3"), + listformat = "href", + file = fn, + url = "urn:script:" + fn + )) + return r + + # run'em + def urn_resolve(self, row, *x): + if not row.get("file"): + return + + # prepare + fn = "%s/%s" % (self.dir, row["file"]) + row = copy.copy(row) + row["url"] = "" + output = None + + # executable + if os.path.isfile(fn) and os.access(fn, os.X_OK): + f = subprocess.Popen([fn], stdout=subprocess.PIPE) + output, err = f.communicate() + # plain python script + elif re.match("^[\w+-]\.py$"): + real_stdout, sys.stdout = sys.stdout, StringIO.StringIO() + execfile(fn) + output, sys.stdout = sys.stdout.getvalue(), real_stdout + # none + else: + return + + # extract urls + if output: + urls = re.findall("(\w+://\S+)", output) + if urls: + row["url"] = urls[0] # action module does not currently support multi-urls + return row + # Unlike other .resolve_urn() handlers this one returns a copy, + # does not modify the passed dict. And this is only handled in + # action.run_fmt_url(), but not by GenericChannel.play(). Why + # OTOH this incudes a *double script invocation*. + +