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

⌈⌋ ⎇ branch:  streamtuner2


Update of "api-action"

Overview

Artifact ID: 1cd9434ef4c2ec9343ab976c57a0e32db713fe84
Page Name:api-action
Date: 2017-02-20 15:14:05
Original User: mario
Parent: ada8ea7ce243e93cc9e16c4186a30ea5cd4ce410 (diff)
Next 2bd8d79419c353d7eafa845972268ad0c01ca492
Content

Python: module action

 
 
action

index
streamtuner2/action.py

# encoding: UTF-8
# api: streamtuner2
# type: functions
# category: io
# title: play/record actions
# description: Starts audio applications, guesses MIME types for URLs
# version: 1.2.1
# priority: core
#
# Multimedia interface for starting audio players, recording app,
# or web browser (listed as "url/http" association in players).
# It maps audio MIME types, and extracts/converts playlist types
# (PLS, M3U, XSPF, SMIL, JSPF, ASX, raw urls).
#
# Each channel plugin has a .listtype which defines the linked
# audio playlist format. It's "pls", seldomly "m3u", or "xspf".
# Some channels list raw "srv" addresses, while Youtube "href"
# entries point to Flash videos.
#
# As fallback the playlist URL is retrieved and its MIME type
# checked, then its content regexped to guess the list format.
# Lastly a playlist format suitable for audio players recreated.
# Which is somewhat of a security feature; playlists get cleaned
# up this way. The conversion is not strictly necessary, because
# baseline PLS/M3U is understood by most players.
#
# And finally this module is also used by exporting and playlist
# importing features (e.g. by the drag'n'drop module).
#
# Still needs some rewrites to transition off the url lists,
# and work with full rows primarily. (And perhaps it should be
# renamed to "playlist" module now).

 
Modules

      

 
ahttp
copy
json
os
pipes
platform
re
subprocess
sys

 
Classes

      

 
__builtin__.object
extract_playlist
save_playlist

 
class extract_playlist(__builtin__.object)

   

# Extract URLs and meta infos (titles) from playlist formats
# â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾
# It's mostly regex-based at the moment, because that's more
# resilient against mailformed XSPF or JSON. But specialized
# import helpers can be added as needed.
 
  Methods defined here:
__init__(self, text=None, fn=None)
decode(self, val, unesc)
# String decoding
field(self, name, rules, src_part)
# Single field
jamj(self)
# Jamendo JAMJAMJSON playlists
mime_guess(self, url)
# Probe url "extensions" for common media types
# (only care about the common audio formats, don't need an exact match or pre-probing in practice)
mkrow(self, row, title=None)
# Add placeholder fields to extracted row
pls(self)
# More exact PLS extraction (for the unlikely case entries were misordered)
probe_ext(self, url)
# Test URL/path "extension" for ".pls" / ".m3u" etc.
probe_fmt(self)
# Probe MIME type and content per regex
rows(self, fmt=None)
# Extract only URLs from given source type
uniq(self, rows)
# Filter out duplicate urls
urls(self, fmt)
# Return just URL list from extracted playlist

Data descriptors defined here:

__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

Data and other attributes defined here:
extr_urls = {'asf': {'unesc': 'xml', 'url': r' (?m) ^ \s*Ref\d+ = (\w+://^\s+) '}, 'asx': {'split': ' (?ix) <entry^>*> ', 'title': ' (?ix) <title> (^<>+) ', 'unesc': 'xml', 'url': ' (?ix) <ref \\b^>+\\b href \\s*=\\s* \\\'\\" (\\w+://^\\s\\"\\\'+) \\\'\\" '}, 'desktop': {'genre': '(?m) ^Categories=(.+)', 'playing': '(?m) ^Comment=(.+)', 'title': '(?m) ^Name=(.+)', 'url': r'(?m) ^URL=(\w+://.+)'}, 'jamj': {'unesc': 'json', 'url': r' (?x) \"audio\" \s*:\s* \"(\w+:\\?/\\?/^\"\s+)\" '}, 'json': {'genre': r' (?x) \"(?:genre|keywords|category)\" \s*:\s* \"(^\"+)\" ', 'homepage': r' (?x) \"(?:homepage|website|info)\" \s*:\s* \"(^\"+)\" ', 'playing': r' (?x) \"(?:playing|current|description)\" \s*:\s* \"(^\"+)\" ', 'title': r' (?x) \"(?:title|name|station)\" \s*:\s* \"(^\"+)\" ', 'unesc': 'json', 'url': r' (?x) \"(?:url|audio|stream)\" \s*:\s* \"(\w+:\\?/\\?/^\"\s+)\" '}, 'jspf': {'split': r'(?s) \"track\":\s*\{ >', 'unesc': 'json', 'url': r'(?s) \"location\" \s*:\s* \"(\w+://^\"\s+)\" '}, 'm3u': {'split': r'(?m) (?=^\#)', 'title': r'(?m) ^ \#EXTINF -:\d,* (.+)', 'url': r'(?m) ^( \w+:// ^#\n+ )'}, 'pls': {'title': r'(?m) ^Title\d* \s*=\s*(.+)', 'url': r'(?m) ^File\d* \s*=\s* (\w+://^\s+) '}, 'qtl': {'unesc': 'xml', 'url': ' <embed\\s+src=\\"\\\'(^\\"\\\'+)\\"\\\'\\s*/>'}, 'raw': {'title': r'(?i)Title\W+(.+)', 'unesc': '*', 'url': ' (?i) ( \\w++:// [^<font color="#c040c0">\\</font>s<font color="#c040c0">\\</font>"<font color="#c040c0">\\\'\\</font>&gt;<font color="#c040c0">\\</font>#]^\\s\\"\\\'\\>\\#+ ) '}, ...}
fn = ''
src = ''

 
class save_playlist(__builtin__.object)

   

# Save rows[] in one of the export formats
# â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾
#  â†’ The export() version uses urls[] and a template row{} as input,
# converts it into a list of complete rows{} beforehand. It's mostly
# utilized to expand a source playlist, merge in alternative streaming
# server addresses.
#
#  â†’ With store() a full set of rows[] is required to begin with, as
# it performs a complete serialization.  Can save directly to a file.
# Which is often used directly by export functions, when no internal
# .pls/.m3u urls should be expanded or converted.
#
# Note that this can chain to convert_playlist() itself. So there's
# some danger for neverending loops in here. Never happened, but some
# careful source= and dest= parameter use is advised. Use source="asis"
# or "srv" to leave addresses alone, or "href" for input probing.
 
  Methods defined here:
__init__(self, source='asis', multiply=False)
# constructor
asx(self, rows)
# ASX
desktop(self, rows)
# .DESKTOP links
export(self, urls=[], row={}, dest='pls', title=None)
# Used by playlist_convert(), to transform a list of extracted URLs
# into a local .pls/.m3u collection again. Therefore injects the
# `title` back into each of the URL rows / or uses row{} template.
file(self, rows, dest, fn)
# save directly
json(self, rows)
# JSON (native lists of streamtuner2)
jspf(self, rows)
# JSPF
m3u(self, rows)
# M3U
pls(self, rows)
# PLS
qtl(self, rows)
# QTL
smil(self, rows)
# SMIL
store(self, rows=None, dest='pls')
# Export a playlist from rows{}
url(self, rows)
# .URL shortcuts
xspf(self, rows)
# XSPF
xspf_row(self, row, map)
# individual tracks

Data descriptors defined here:

__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

Data and other attributes defined here:
multiply = True
source = 'pls'
xspf_map = {'description': 'info', 'homepage': 'info', 'playing': 'annotation', 'title': 'title', 'url': 'location'}

 
Functions

      

 
browser(url)
# Start web browser
cleanup_tmp_files()
# Callback from main / after gtk_main_quit
convert_playlist(url, source, dest, local_file=True, row={})
# Substitute streaming address with desired playlist format
# â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾
# Converts input rows/urls, probes for playlist format, fetches them
# and possibly converts remote .pls to local .m3u/.xpsf filename or
# just returns direct "srv" urls.
#
#  Â· Takes a single input `url` (and original row{} as template).
#  Â· But returns a list of urls after playlist extraction.
#  Â· If repackaging as .m3u/.pls/.xspf, returns the local fn.
help(*args)
# Open help browser, chm, or streamtuner2 pages
http_probe_get(url)
# Tries to fetch a resource, aborts on ICY responses.
interpol(cmd, source='pls', row={}, add_default=True)
# Replaces instances of %m3u, %pls, %srv in a command string
# â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾â€¾
#  Â· Also understands short aliases %l, %f, %d.
#  Â· And can embed %title or $genre placeholders (may use either % or $).
#  Â· Replace .pls URL with local .m3u file depending on map.
listfmt(t='pls')
# Convert e.g. "text/x-scpls" MIME types to just "pls" monikers
mime_app(fmt, cmd_list)
# Convert MIME type into list of "audio/xyz", "audio/*", "*/*"
# for comparison against configured record/play association.
play(row={}, audioformat='audio/mpeg', source='pls')
# Calls player for stream url and format
quote(ins)
# OS shell command escaping
record(row={}, audioformat='audio/mpeg', source='href', append=None)
# Call streamripper / youtube-dl / wget
resolve_urn(row)
# Is called upon rows containing an url starting with "urn:service:#id",
# calls the handler from the channel plugin to look up the page and find
# the actual streaming url
run(cmd)
# Exec wrapper
run_fmt_url(row={}, audioformat='audio/mpeg', source='pls', assoc={}, append=None, cmd=None, add_default=True)
# Invokes player/recorder for stream url and format
tmp_fn(row, ext='pls')
# Generate filename for temporary .pls/m3u, with unique id

 
Data

      

  conf = {u'filter_walledgardens': True, u'radionomy_page...e/mario/.config/streamtuner2', u'history': u'20'}
handler = {}
listfmt_t = {'*/*': 'href', 'application/json': 'json', 'application/smil': 'smil', 'application/vnd.apple.mpegurl': 'm3u', 'application/vnd.ms-wpl': 'smil', 'application/x-shockwave-flash': 'href', 'application/xspf+xml': 'xspf', 'audio/mpegurl': 'm3u', 'audio/x-mpegurl': 'm3u', 'audio/x-ms-wax': 'asx', ...}
log = <config.log_printer object>
main = None
mediafmt_t = {'audio/aac': 'aac', 'audio/aacp': 'aac', 'audio/it+zip': 'mod', 'audio/midi': 'midi', 'audio/mod': 'mod', 'audio/mpeg': 'mp3', 'audio/ogg': 'ogg', 'audio/s3+zip': 'mod', 'audio/xm+zip': 'mod'}
placeholder_map = {'asx': '(%asx)', 'jspf': '(%jspf | %j)', 'm3u': '(%m3u | %f | %g | %m)', 'pls': '(%url | %pls | %u | %l | %r)', 'smil': '(%smil)', 'srv': '(%srv | %d | %s)', 'xspf': '(%xspf | %xpsf | %x)'}
playlist_content_map = ('pls', r' (?i)\[playlist\.*NumberOfEntries '), ('xspf', r' <\?xml .* <playlist .* ((?i)http://xspf\.org)/ns/0/ '), ('m3u', r' ^ \s* \#(EXT)?M3U '), ('asx', r' (?i) <asx\b '), ('smil', ' <smil^>*> .* <seq> '), ('html', ' (?i)<(audio|video)\\b^>+\\bsrc\\s*=\\s*"\'?https?:// '), ('wpl', r' <\?wpl \s+ version="1\.0" \s* \?> '), ('b4s', ' <WinampXML> '), ('qtl', r' <?quicktime\d+type="application/x-quicktime-media-link"\d*?> '), ('jspf', r' ^ \s* \{ \s* "playlist": \s* \{ '), ('asf', r' ^ \Reference\ .*? ^Ref\d+= '), ('url', r' ^ \InternetShortcut\ .*? ^URL= '), ('desktop', r' ^ \Desktop Entry\ .*? ^Link= '), ('json', r' "url": \s* "\w+:\\?/\\?/ '), ('jamj', r' "audio": \s* "\w+:\\?/\\?/ '), ('gvp', r' ^gvp_version:1\.\d+$ '), ('href', ' .* ')]
playlist_fmt_prio = 'pls', 'xspf', 'asx', 'smil', 'jamj', 'json', 'm3u', 'asf', 'raw'
tmp_files = []