# Copyright (c) 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.
#
# RealAudio is a registered trademark of RealNetworks, Inc.
import string, re, datetime, ST, gtk
from ST import _
### constants #################################################################
class FIELD:
DATE, DESCRIPTION, URL = range(3)
BASIC_CH_HOME = "http://www.basic.ch/"
BASIC_CH_ROOT = "http://basic.ch/"
re_dj = re.compile(r"javascript:openWindow\('(showtl.cfm\?showid=[0-9]+)'.*()?(.*?)()?")
re_genre = re.compile('(sans-serif">|)(.*?)<')
re_date = re.compile(r">([0-9][0-9])\.([0-9][0-9])\.([0-9][0-9])<")
re_stream = re.compile(r'a href="/(ram/.*?)".*?text-decoration:none">(.*)')
### helpers ###################################################################
class struct:
def __init__ (self, **entries):
self.__dict__.update(entries)
def parse_error (err):
handler.notice(_("parse error: %s") % (err))
# Convert a 2-digits year to a 4-digits year, conforming to the POSIX
# or X/Open standard -- nice y2k69 bug ;)
def yy2yyyy (yy):
if yy >= 69 and yy <= 99:
return 1900 + yy
else:
return 2000 + yy
### transfer callbacks ########################################################
def categories_line_cb (line, categories, info):
match = re_dj.search(line)
if match is not None:
if info.category is not None:
parse_error("incomplete category")
info.category = ST.Category()
info.category.name = match.group(1)
info.category.url_postfix = match.group(1)
info.category.label = string.capwords(ST.sgml_ref_expand(match.group(3)))
categories.append(info.category)
else:
match = re_genre.search(line)
if match is not None:
if info.category is None:
parse_error("misplaced genre")
else:
info.category.label = info.category.label + " (" + ST.sgml_ref_expand(match.group(2)) + ")"
info.category = None
def streams_line_cb (line, streams, info):
match = re_date.search(line)
if match is not None:
if info.stream is not None:
parse_error("incomplete stream")
date = datetime.date(yy2yyyy(int(match.group(3))),
int(match.group(2)),
int(match.group(1)))
info.stream = ST.Stream()
info.stream.fields[FIELD.DATE] = date.strftime("%x")
else:
match = re_stream.search(line)
if match is not None:
if info.stream is None:
parse_error("misplaced stream")
else:
info.stream.name = match.group(1)
info.stream.fields[FIELD.URL] = BASIC_CH_ROOT + info.stream.name
info.stream.fields[FIELD.DESCRIPTION] = ST.sgml_ref_expand(match.group(2))
streams.append(info.stream)
info.stream = None
### handler implementation ####################################################
class BasicChHandler (ST.Handler):
def __new__ (cls):
self = ST.Handler.__new__(cls, "basic.ch.py")
self.label = "basic.ch"
self.description = _("basic.ch Internet Radio - Live and Archived DJ Mixes")
self.home = BASIC_CH_HOME
self.icon = gtk.gdk.pixbuf_new_from_file(ST.find_icon("basic.ch.png"))
category = ST.Category()
category.name = "__main"
category.label = _("Live")
self.stock_categories = category,
self.add_field(ST.HandlerField(FIELD.DATE,
_("Date"),
str,
ST.HANDLER_FIELD_VISIBLE,
_("The mix recording date")))
self.add_field(ST.HandlerField(FIELD.DESCRIPTION,
_("Description"),
str,
ST.HANDLER_FIELD_VISIBLE,
_("The mix description")))
self.add_field(ST.HandlerField(FIELD.URL,
_("URL"),
str,
ST.HANDLER_FIELD_VISIBLE
| ST.HANDLER_FIELD_START_HIDDEN,
_("The mix listen URL")))
return self
def reload (self, category):
session = ST.TransferSession()
if not hasattr(self, "categories"):
categories = []
session.get_by_line(BASIC_CH_ROOT + "downtest.cfm",
flags = ST.TRANSFER_UTF8 | ST.TRANSFER_PARSE_HTTP_CHARSET | ST.TRANSFER_PARSE_HTML_CHARSET,
body_cb = categories_line_cb,
body_args = (categories, struct(category = None)))
self.categories = categories
streams = []
if category.url_postfix is None:
# main category
stream = ST.Stream()
stream.name = "basic.ram"
stream.fields[FIELD.DATE] = _("Now")
stream.fields[FIELD.DESCRIPTION] = _("Live stream")
stream.fields[FIELD.URL] = BASIC_CH_ROOT + stream.name
streams.append(stream)
else:
session.get_by_line(BASIC_CH_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.DATE]
elif stock_field == ST.HANDLER_STOCK_FIELD_DESCRIPTION:
return stream.fields[FIELD.DESCRIPTION]
elif stock_field == ST.HANDLER_STOCK_FIELD_URI_LIST:
return stream.fields[FIELD.URL],
def stream_tune_in (self, stream):
ST.action_run("play-ra", stream.fields[FIELD.URL])
def stream_record (self, stream):
ST.action_run("record-ra", stream.fields[FIELD.URL])
### initialization ############################################################
def init ():
global handler
if not ST.check_api_version(2, 0):
raise RuntimeError, _("API version mismatch")
ST.action_register("play-ra", _("Listen to a RealAudio%s stream") % ("\302\256"), "realplay %q")
ST.action_register("record-ra", _("Record a RealAudio%s stream") % ("\302\256"))
handler = BasicChHandler()
ST.handlers_add(handler)
init()