# Copyright (c) 2003, 2004 Jean-Yves Lefort
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of Jean-Yves Lefort nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import re, os, ST, gtk
from ST import _
### constants #################################################################
class FIELD:
TITLE, DESCRIPTION, HOMEPAGE = range(3)
GOOGLE_DIRECTORY_ROOT = "http://directory.google.com"
CATEGORIES_URL_POSTFIX = "/Top/Arts/Music/Sound_Files/MP3/Streaming/Stations/"
GOOGLE_STATIONS_HOME = GOOGLE_DIRECTORY_ROOT + CATEGORIES_URL_POSTFIX
re_category = re.compile('^()?(.*?)()? ')
re_stream = re.compile('^
(.*)')
re_description = re.compile('^ (.*?)')
### helpers ###################################################################
class struct:
def __init__ (self, **entries):
self.__dict__.update(entries)
def parse_error (err):
handler.notice(_("parse error: %s") % (err))
### transfer callbacks ########################################################
def categories_line_cb (line, categories):
match = re_category.match(line)
if match is not None:
category = ST.Category()
category.name = ST.sgml_ref_expand(match.group(3))
category.label = category.name
href = ST.sgml_ref_expand(match.group(1))
if href[0] == "/":
category.url_postfix = href
else:
category.url_postfix = CATEGORIES_URL_POSTFIX + href
categories.append(category)
def streams_line_cb (line, streams, info):
match = re_stream.match(line)
if match is not None:
if info.stream is not None:
parse_error("incomplete stream")
info.stream = ST.Stream()
info.stream.name = ST.sgml_ref_expand(match.group(1))
info.stream.fields[FIELD.HOMEPAGE] = info.stream.name
info.stream.fields[FIELD.TITLE] = ST.sgml_ref_expand(match.group(2))
else:
match = re_description.match(line)
if match is not None:
if info.stream is None:
parse_error("misplaced description")
else:
info.stream.fields[FIELD.DESCRIPTION] = ST.sgml_ref_expand(match.group(1))
streams.append(info.stream)
info.stream = None
### handler implementation ####################################################
class GoogleStationsHandler (ST.Handler):
def __new__ (cls):
self = ST.Handler.__new__(cls, "google-stations.py")
self.label = _("Google Stations")
self.home = GOOGLE_STATIONS_HOME
self.icon = gtk.gdk.pixbuf_new_from_file(ST.find_icon("google-stations.png"))
category = ST.Category()
category.name = "__main"
category.label = _("Internet radios")
category.url_postfix = "/Top/Arts/Radio/Internet/"
self.stock_categories = category,
self.add_field(ST.HandlerField(FIELD.TITLE,
_("Title"),
str,
ST.HANDLER_FIELD_VISIBLE,
_("The station title")))
self.add_field(ST.HandlerField(FIELD.DESCRIPTION,
_("Description"),
str,
ST.HANDLER_FIELD_VISIBLE,
_("The station description")))
self.add_field(ST.HandlerField(FIELD.HOMEPAGE,
_("Homepage"),
str,
ST.HANDLER_FIELD_VISIBLE
| ST.HANDLER_FIELD_START_HIDDEN,
_("The station homepage URL")))
return self
def reload (self, category):
session = ST.TransferSession()
if not hasattr(self, "categories"):
categories = []
session.get_by_line(GOOGLE_DIRECTORY_ROOT + CATEGORIES_URL_POSTFIX,
flags = ST.TRANSFER_UTF8 | ST.TRANSFER_PARSE_HTTP_CHARSET | ST.TRANSFER_PARSE_HTML_CHARSET,
body_cb = categories_line_cb,
body_args = (categories,))
self.categories = categories
streams = []
if category.url_postfix is not None:
session.get_by_line(GOOGLE_DIRECTORY_ROOT + category.url_postfix,
flags = ST.TRANSFER_UTF8 | ST.TRANSFER_PARSE_HTTP_CHARSET | ST.TRANSFER_PARSE_HTML_CHARSET,
body_cb = streams_line_cb,
body_args = (streams, struct(stream = None)))
return (self.categories, streams)
def stream_get_stock_field (self, stream, stock_field):
if stock_field == ST.HANDLER_STOCK_FIELD_NAME:
return stream.fields[FIELD.TITLE]
elif stock_field == ST.HANDLER_STOCK_FIELD_DESCRIPTION:
return stream.fields[FIELD.DESCRIPTION]
elif stock_field == ST.HANDLER_STOCK_FIELD_HOMEPAGE:
return stream.fields[FIELD.HOMEPAGE]
def stream_browse (self, stream):
ST.action_run("view-web", stream.fields[FIELD.HOMEPAGE])
### initialization ############################################################
def init ():
global handler
if not ST.check_api_version(2, 0):
raise RuntimeError, _("API version mismatch")
ST.action_register("view-web", _("Open a web page"), "epiphany %q")
handler = GoogleStationsHandler()
ST.handlers_add(handler)
init()
|