Index: _package.epm
==================================================================
--- _package.epm
+++ _package.epm
@@ -35,11 +35,11 @@
f 644 root root /usr/share/streamtuner2/gtk3.xml ./gtk3.xml
f 644 root root /usr/share/streamtuner2/pson.py ./pson.py
#f 644 root root /usr/share/streamtuner2/processing.py ./processing.py
f 644 root root /usr/share/streamtuner2/action.py ./action.py
f 644 root root /usr/share/streamtuner2/config.py ./config.py
-f 644 root root /usr/share/streamtuner2/http.py ./http.py
+f 644 root root /usr/share/streamtuner2/ahttp.py ./ahttp.py
f 644 root root /usr/share/streamtuner2/cli.py ./cli.py
f 644 root root /usr/share/streamtuner2/mygtk.py ./mygtk.py
f 644 root root /usr/share/streamtuner2/favicon.py ./favicon.py
f 644 root root /usr/share/streamtuner2/kronos.py ./kronos.py
f 644 root root /usr/share/streamtuner2/pq.py ./pq.py
Index: action.py
==================================================================
--- action.py
+++ action.py
@@ -21,11 +21,11 @@
#
import re
import os
-import http
+import ahttp as http
from config import conf, __print__, dbg
import platform
main = None
@@ -79,12 +79,12 @@
# exec wrapper
@staticmethod
def run(cmd):
if conf.windows:
- os.system("start \"%s\"")
- else:
+ os.system("start \"%s\"")
+ else:
os.system(cmd + " &")
# streamripper
@staticmethod
ADDED ahttp.py
Index: ahttp.py
==================================================================
--- ahttp.py
+++ ahttp.py
@@ -0,0 +1,213 @@
+#
+# encoding: UTF-8
+# api: streamtuner2
+# type: functions
+# title: http download / methods
+# description: http utility
+# version: 1.4
+#
+# Provides a http GET method with gtk.statusbar() callback.
+# And a function to add trailings slashes on http URLs.
+#
+#
+
+
+from compat2and3 import urllib2, urlencode, urlparse, cookielib, StringIO, xrange
+from gzip import GzipFile
+from config import conf, __print__, dbg
+
+
+#-- url download ---------------------------------------------
+
+
+
+#-- chains to progress meter and status bar in main window
+feedback = None
+
+# sets either text or percentage, so may take two parameters
+def progress_feedback(*args):
+
+ # use reset values if none given
+ if not args:
+ args = ["", 1.0]
+
+ # send to main win
+ if feedback:
+ try: [feedback(d) for d in args]
+ except: pass
+
+
+
+
+#-- GET
+def get(url, maxsize=1<<19, feedback="old"):
+ __print__("GET", url)
+
+ # statusbar info
+ progress_feedback(url, 0.0)
+
+ # read
+ content = ""
+ f = urllib2.urlopen(url)
+ max = 222000 # mostly it's 200K, but we don't get any real information
+ read_size = 1
+
+ # multiple steps
+ while (read_size and len(content) < maxsize):
+
+ # partial read
+ add = f.read(8192)
+ content = content + add
+ read_size = len(add)
+
+ # set progress meter
+ progress_feedback(float(len(content)) / float(max))
+
+ # done
+
+ # clean statusbar
+ progress_feedback()
+
+ # fin
+ __print__(len(content))
+ return content
+
+
+
+
+
+#-- fix invalid URLs
+def fix_url(url):
+ if url is None:
+ url = ""
+ if len(url):
+ # remove whitespace
+ url = url.strip()
+ # add scheme
+ if (url.find("://") < 0):
+ url = "http://" + url
+ # add mandatory path
+ if (url.find("/", 10) < 0):
+ url = url + "/"
+ return url
+
+
+
+
+# default HTTP headers for AJAX/POST request
+default_headers = {
+ "User-Agent": "streamtuner2/2.1 (X11; U; Linux AMD64; en; rv:1.5.0.1) like WinAmp/2.1 but not like Googlebot/2.1", #"Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6",
+ "Accept": "*/*;q=0.5, audio/*, url/*",
+ "Accept-Language": "en-US,en,de,es,fr,it,*;q=0.1",
+ "Accept-Encoding": "gzip,deflate",
+ "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.1",
+ "Keep-Alive": "115",
+ "Connection": "keep-alive",
+ #"Content-Length", "56",
+ #"Cookie": "s_pers=%20s_getnr%3D1278607170446-Repeat%7C1341679170446%3B%20s_nrgvo%3DRepeat%7C1341679170447%3B; s_sess=%20s_cc%3Dtrue%3B%20s_sq%3Daolshtcst%252Caolsvc%253D%252526pid%25253Dsht%25252520%2525253A%25252520SHOUTcast%25252520Radio%25252520%2525257C%25252520Search%25252520Results%252526pidt%25253D1%252526oid%25253Dfunctiononclick%25252528event%25252529%2525257BshowMoreGenre%25252528%25252529%2525253B%2525257D%252526oidt%25253D2%252526ot%25253DDIV%3B; aolDemoChecked=1.849061",
+ "Pragma": "no-cache",
+ "Cache-Control": "no-cache",
+}
+
+
+
+# simulate ajax calls
+def ajax(url, post, referer=""):
+
+ # request
+ headers = default_headers
+ headers.update({
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
+ "X-Requested-With": "XMLHttpRequest",
+ "Referer": (referer if referer else url),
+ })
+ if type(post) == dict:
+ post = urlencode(post)
+ request = urllib2.Request(url, post, headers)
+
+ # open url
+ __print__( vars(request) )
+ progress_feedback(url, 0.2)
+ r = urllib2.urlopen(request)
+
+ # get data
+ __print__( r.info() )
+ progress_feedback(0.5)
+ data = r.read()
+ progress_feedback()
+ return data
+
+
+
+# http://techknack.net/python-urllib2-handlers/
+class ContentEncodingProcessor(urllib2.BaseHandler):
+ """A handler to add gzip capabilities to urllib2 requests """
+
+ # add headers to requests
+ def http_request(self, req):
+ req.add_header("Accept-Encoding", "gzip, deflate")
+ return req
+
+ # decode
+ def http_response(self, req, resp):
+ old_resp = resp
+ # gzip
+ if resp.headers.get("content-encoding") == "gzip":
+ gz = GzipFile(
+ fileobj=StringIO(resp.read()),
+ mode="r"
+ )
+ resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code)
+ resp.msg = old_resp.msg
+ # deflate
+ if resp.headers.get("content-encoding") == "deflate":
+ gz = StringIO( deflate(resp.read()) )
+ resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code) # 'class to add info() and geturl() methods to an open file.'
+ resp.msg = old_resp.msg
+ return resp
+
+# deflate support
+import zlib
+def deflate(data): # zlib only provides the zlib compress format, not the deflate format;
+ try: # so on top of all there's this workaround:
+ return zlib.decompress(data, -zlib.MAX_WBITS)
+ except zlib.error:
+ return zlib.decompress(data)
+
+
+
+
+
+
+
+#-- init for later use
+if urllib2:
+
+ # config 1
+ handlers = [None, None, None]
+
+ # base
+ handlers[0] = urllib2.HTTPHandler()
+ if conf.debug:
+ handlers[0].set_http_debuglevel(3)
+
+ # content-encoding
+ handlers[1] = ContentEncodingProcessor()
+
+ # store cookies at runtime
+ cj = cookielib.CookieJar()
+ handlers[2] = urllib2.HTTPCookieProcessor( cj )
+
+ # inject into urllib2
+ urllib2.install_opener( urllib2.build_opener(*handlers) )
+
+
+
+
+# alternative function names
+AJAX=ajax
+POST=ajax
+GET=get
+URL=fix_url
+
+
Index: channels/_generic.py
==================================================================
--- channels/_generic.py
+++ channels/_generic.py
@@ -21,11 +21,11 @@
import gtk
from mygtk import mygtk
from config import conf, __print__, dbg
-import http
+import ahttp as http
import action
import favicon
import os.path
import xml.sax.saxutils
import re
@@ -147,17 +147,17 @@
# category tree
self.display_categories()
#mygtk.tree(self.gtk_cat, self.categories, title="Category", icon=gtk.STOCK_OPEN);
# update column names
- for field,title in self.titles.iteritems():
+ for field,title in list(self.titles.items()):
self.update_datamap(field, title=title)
# prepare stream list
if (not self.rowmap):
for row in self.datamap:
- for x in xrange(2, len(row)):
+ for x in range(2, len(row)):
self.rowmap.append(row[x][0])
# load default category
if (self.current):
self.load(self.current)
@@ -181,11 +181,11 @@
# load data,
# update treeview content
def load(self, category, force=False):
# get data from cache or download
- if (force or not self.streams.has_key(category)):
+ if (force or not category in self.streams):
new_streams = self.update_streams(category)
if new_streams:
# modify
@@ -235,11 +235,11 @@
# finds differences in new/old streamlist, marks deleted with flag
def deleted_streams(self, new, old):
diff = []
new = [row.get("url","http://example.com/") for row in new]
for row in old:
- if (row.has_key("url") and (row.get("url") not in new)):
+ if (url in row and (row.get("url") not in new)):
row["deleted"] = 1
diff.append(row)
return diff
@@ -249,11 +249,11 @@
# oh my, at least it's working
# at start the bookmarks module isn't fully registered at instantiation in parent.channels{} - might want to do that step by step rather
# then display() is called too early to take effect - load() & co should actually be postponed to when a notebook tab gets selected first
# => might be fixed now, 1.9.8
# state icon: bookmark star
- if (conf.show_bookmarks and self.parent.channels.has_key("bookmarks") and self.parent.bookmarks.is_in(streams[i].get("url", "file:///tmp/none"))):
+ if (conf.show_bookmarks and "bookmarks" in self.parent.channels and self.parent.bookmarks.is_in(streams[i].get("url", "file:///tmp/none"))):
streams[i]["favourite"] = 1
# state icon: INFO or DELETE
if (not row.get("state")):
if row.get("favourite"):
@@ -309,15 +309,14 @@
# if category tree is empty, initialize it
if not self.categories:
__print__(dbg.PROC, "first_show: reload_categories");
#self.parent.thread(self.reload_categories)
- print("reload categories");
self.reload_categories()
self.display_categories()
self.current = self.categories.keys()[0]
- print self.current
+ __print__(dbg.STAT, self.current)
self.load(self.current)
# load current category
else:
__print__(dbg.STAT, "first_show: load current category");
Index: channels/basicch.py
==================================================================
--- channels/basicch.py
+++ channels/basicch.py
@@ -10,11 +10,11 @@
# Needs manual initialisation of categories first.
#
import re
-import http
+import ahttp as http
from config import conf
from channels import *
from xml.sax.saxutils import unescape
Index: channels/google.py
==================================================================
--- channels/google.py
+++ channels/google.py
@@ -49,11 +49,11 @@
import re, os, gtk
from channels import *
from xml.sax.saxutils import unescape as entity_decode, escape as xmlentities
-import http
+import ahttp as http
### constants #################################################################
Index: channels/internet_radio_org_uk.py
==================================================================
--- channels/internet_radio_org_uk.py
+++ channels/internet_radio_org_uk.py
@@ -13,11 +13,11 @@
from channels import *
import re
from config import conf, __print__, dbg
-import http
+import ahttp as http
from pq import pq
Index: channels/jamendo.py
==================================================================
--- channels/jamendo.py
+++ channels/jamendo.py
@@ -6,11 +6,11 @@
# Requires more rework of streamtuner2 list display to show album covers.
#
import re
-import http
+import ahttp as http
from config import conf
from channels import *
from xml.sax.saxutils import unescape
Index: channels/links.py
==================================================================
--- channels/links.py
+++ channels/links.py
@@ -56,23 +56,23 @@
bookmarks.streams[self.module] = []
bookmarks.add_category(self.module)
# collect links from channel plugins
- for name,channel in parent.channels.iteritems():
+ for name,channel in parent.channels.items():
try:
self.streams.append({
"favourite": 1,
"title": channel.title,
"homepage": channel.homepage,
})
except: pass
- for title,homepage in self.default.iteritems():
+ for title,homepage in self.default.items():
self.streams.append({
"title": title,
"homepage": homepage,
})
# add to bookmarks
bookmarks.streams[self.module] = self.streams
Index: channels/live365.py
==================================================================
--- channels/live365.py
+++ channels/live365.py
@@ -8,11 +8,11 @@
# streamtuner2 modules
from config import conf
from mygtk import mygtk
-import http
+import ahttp as http
from channels import *
from config import __print__, dbg
# python modules
import re
Index: channels/modarchive.py
==================================================================
--- channels/modarchive.py
+++ channels/modarchive.py
@@ -10,11 +10,11 @@
# VLC in */* seems to work fine however.
#
import re
-import http
+import ahttp as http
from config import conf
from channels import *
from config import __print__, dbg
from xml.sax.saxutils import unescape
Index: channels/musicgoal.py
==================================================================
--- channels/musicgoal.py
+++ channels/musicgoal.py
@@ -14,11 +14,11 @@
# st2 modules
from config import conf
from mygtk import mygtk
-import http
+import ahttp as http
from channels import *
# python modules
import re
import json
Index: channels/myoggradio.py
==================================================================
--- channels/myoggradio.py
+++ channels/myoggradio.py
@@ -24,11 +24,11 @@
from config import conf
from action import action
import re
import json
-from StringIO import StringIO
+from compat2and3 import StringIO
import copy
# open source radio sharing stie
Index: channels/punkcast.py
==================================================================
--- channels/punkcast.py
+++ channels/punkcast.py
@@ -7,11 +7,11 @@
# ST1 looked prettier with random images within.
#
import re
-import http
+import ahttp as http
from config import conf
import action
from channels import *
from config import __print__, dbg
Index: channels/shoutcast.py
==================================================================
--- channels/shoutcast.py
+++ channels/shoutcast.py
@@ -16,11 +16,11 @@
#
#
#
-import http
+import ahttp as http
import urllib
import re
from config import conf, __print__, dbg
from pq import pq
#from channels import * # works everywhere but in this plugin(???!)
@@ -64,15 +64,15 @@
html = http.get(self.base_url)
self.categories = []
__print__( dbg.DATA, html )
#
Radio Genres
- rx = re.compile(r'[\w\s]+', re.S)
+ rx = re.compile(r'[\w\s]+', re.S)
sub = []
for uu in rx.findall(html):
__print__( dbg.DATA, uu )
- (main,name,id) = uu
+ (main,name,id) = uu
name = urllib.unquote(name)
# main category
if main:
if sub:
Index: channels/timer.py
==================================================================
--- channels/timer.py
+++ channels/timer.py
@@ -18,10 +18,11 @@
# are stored in the description field, and can thus be edited. However, after editing
# times manually, streamtuner2 must be restarted for the changes to take effect.
#
+from config import __print__, dbg
from channels import *
import kronos
from mygtk import mygtk
from action import action
import copy
@@ -74,11 +75,11 @@
# prepare spool
self.sched = kronos.ThreadedScheduler()
for row in self.streams:
try: self.queue(row)
- except Exception,e: print("queuing error", e)
+ except Exception as e: __print__(dbg.ERR, "queuing error", e)
self.sched.start()
# display GUI for setting timespec
def edit_timer(self, *w):
Index: channels/tv.py
==================================================================
--- channels/tv.py
+++ channels/tv.py
@@ -16,11 +16,11 @@
# stored in .streams["all"] pseudo-category.
#
# icon: http://cemagraphics.deviantart.com/art/Little-Tv-Icon-96461135
from channels import *
-import http
+import ahttp as http
import lxml.etree
Index: channels/xiph.py
==================================================================
--- channels/xiph.py
+++ channels/xiph.py
@@ -17,11 +17,11 @@
# streamtuner2 modules
from config import conf
from mygtk import mygtk
-import http
+import ahttp as http
from channels import *
from config import __print__, dbg
# python modules
import re
Index: cli.py
==================================================================
--- cli.py
+++ cli.py
@@ -15,11 +15,11 @@
#
import sys
#from channels import *
-import http
+import ahttp
import action
from config import conf
import json
ADDED compat2and3.py
Index: compat2and3.py
==================================================================
--- compat2and3.py
+++ compat2and3.py
@@ -0,0 +1,58 @@
+#
+# encoding: UTF-8
+# api: python
+# type: functions
+# title: Python2 and Python3 compatibility
+# version: 0.1
+#
+# Renames some Python3 modules into their Py2 equivalent.
+# Slim local alternative to `six` module.
+#
+
+
+import sys
+
+
+# Python 2
+if sys.version_info < (3,0):
+
+ # version tags
+ PY2 = 1
+ PY3 = 0
+
+ # basic functions
+ xrange = xrange
+ range = xrange
+
+ # urllib modules
+ import urllib
+ import urllib2
+ from urllib import urlencode
+ import urlparse
+ import cookielib
+
+ # filesys
+ from StringIO import StringIO
+
+
+# Python 3
+else:
+
+ # version tags
+ PY2 = 0
+ PY3 = 1
+
+ # basic functions
+ xrange = range
+
+ # urllib modules
+ import urllib.request as urllib
+ import urllib.request as urllib2
+ from urllib.parse import urlencode
+ import urllib.parse as urlparse
+ from http import cookiejar as cookielib
+
+ # filesys
+ from io import StringIO
+
+
Index: favicon.py
==================================================================
--- favicon.py
+++ favicon.py
@@ -26,17 +26,16 @@
delete_google_stub = 1 # don't keep placeholder images
google_placeholder_filesizes = (726,896)
import os, os.path
-import urllib
+from compat2and3 import xrange, urllib
import re
-import urlparse
from config import conf
try: from processing import Process as Thread
except: from threading import Thread
-import http
+import ahttp
# ensure that we don't try to download a single favicon twice per session,
# if it's not available the first time, we won't get it after switching stations back and forth
@@ -87,16 +86,16 @@
title = rx_t.search(row["title"])
if title:
title = title.group(0).replace(" ", "%20")
# do a google search
- html = http.ajax("http://www.google.de/search?hl=de&q="+title, None)
+ html = ahttp.ajax("http://www.google.de/search?hl=de&q="+title, None)
# find first URL hit
url = rx_u.search(html)
if url:
- row["homepage"] = http.fix_url(url.group(1))
+ row["homepage"] = ahttp.fix_url(url.group(1))
pass
#-----------------
@@ -193,13 +192,13 @@
r = urllib.urlopen(favicon)
headers = r.info()
# abort on
if r.getcode() >= 300:
- raise "HTTP error", r.getcode()
+ raise Error("HTTP error" + r.getcode())
if not headers["Content-Type"].lower().find("image/"):
- raise "can't use text/* content"
+ raise Error("can't use text/* content")
# save file
fn_tmp = fn+".tmp"
f = open(fn_tmp, "wb")
f.write(r.read(32768))
@@ -234,11 +233,11 @@
# url or
if favicon.startswith("http://"):
None
# just /pathname
else:
- favicon = urlparse.urljoin(url, favicon)
+ favicon = ahttp.urlparse.urljoin(url, favicon)
#favicon = "http://" + domain(url) + "/" + favicon
# download
direct_download(favicon, file(url))
@@ -264,11 +263,11 @@
import operator
import struct
try:
from PIL import BmpImagePlugin, PngImagePlugin, Image
-except Exception, e:
+except Exception as e:
print("no PIL", e)
always_google = 1
only_google = 1
DELETED http.py
Index: http.py
==================================================================
--- http.py
+++ http.py
@@ -1,224 +0,0 @@
-#
-# encoding: UTF-8
-# api: streamtuner2
-# type: functions
-# title: http download / methods
-# description: http utility
-# version: 1.3
-#
-# Provides a http GET method with gtk.statusbar() callback.
-# And a function to add trailings slashes on http URLs.
-#
-# The latter code is pretty much unreadable. But let's put the
-# blame on urllib2, the most braindamaged code in the Python
-# standard library.
-#
-
-try:
- import urllib2
- from urllib import urlencode
-except:
- import urllib.request as urllib2
- import urllib.parse.urlencode as urlencode
-import config
-from config import __print__, dbg
-
-
-
-#-- url download ---------------------------------------------
-
-
-
-#-- chains to progress meter and status bar in main window
-feedback = None
-
-# sets either text or percentage, so may take two parameters
-def progress_feedback(*args):
-
- # use reset values if none given
- if not args:
- args = ["", 1.0]
-
- # send to main win
- if feedback:
- try: [feedback(d) for d in args]
- except: pass
-
-
-
-
-#-- GET
-def get(url, maxsize=1<<19, feedback="old"):
- __print__("GET", url)
-
- # statusbar info
- progress_feedback(url, 0.0)
-
- # read
- content = ""
- f = urllib2.urlopen(url)
- max = 222000 # mostly it's 200K, but we don't get any real information
- read_size = 1
-
- # multiple steps
- while (read_size and len(content) < maxsize):
-
- # partial read
- add = f.read(8192)
- content = content + add
- read_size = len(add)
-
- # set progress meter
- progress_feedback(float(len(content)) / float(max))
-
- # done
-
- # clean statusbar
- progress_feedback()
-
- # fin
- __print__(len(content))
- return content
-
-
-
-
-
-#-- fix invalid URLs
-def fix_url(url):
- if url is None:
- url = ""
- if len(url):
- # remove whitespace
- url = url.strip()
- # add scheme
- if (url.find("://") < 0):
- url = "http://" + url
- # add mandatory path
- if (url.find("/", 10) < 0):
- url = url + "/"
- return url
-
-
-
-
-# default HTTP headers for AJAX/POST request
-default_headers = {
- "User-Agent": "streamtuner2/2.1 (X11; U; Linux AMD64; en; rv:1.5.0.1) like WinAmp/2.1 but not like Googlebot/2.1", #"Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6",
- "Accept": "*/*;q=0.5, audio/*, url/*",
- "Accept-Language": "en-US,en,de,es,fr,it,*;q=0.1",
- "Accept-Encoding": "gzip,deflate",
- "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.1",
- "Keep-Alive": "115",
- "Connection": "keep-alive",
- #"Content-Length", "56",
- #"Cookie": "s_pers=%20s_getnr%3D1278607170446-Repeat%7C1341679170446%3B%20s_nrgvo%3DRepeat%7C1341679170447%3B; s_sess=%20s_cc%3Dtrue%3B%20s_sq%3Daolshtcst%252Caolsvc%253D%252526pid%25253Dsht%25252520%2525253A%25252520SHOUTcast%25252520Radio%25252520%2525257C%25252520Search%25252520Results%252526pidt%25253D1%252526oid%25253Dfunctiononclick%25252528event%25252529%2525257BshowMoreGenre%25252528%25252529%2525253B%2525257D%252526oidt%25253D2%252526ot%25253DDIV%3B; aolDemoChecked=1.849061",
- "Pragma": "no-cache",
- "Cache-Control": "no-cache",
-}
-
-
-
-# simulate ajax calls
-def ajax(url, post, referer=""):
-
- # request
- headers = default_headers
- headers.update({
- "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
- "X-Requested-With": "XMLHttpRequest",
- "Referer": (referer if referer else url),
- })
- if type(post) == dict:
- post = urlencode(post)
- request = urllib2.Request(url, post, headers)
-
- # open url
- __print__( vars(request) )
- progress_feedback(url, 0.2)
- r = urllib2.urlopen(request)
-
- # get data
- __print__( r.info() )
- progress_feedback(0.5)
- data = r.read()
- progress_feedback()
- return data
-
-
-
-# http://techknack.net/python-urllib2-handlers/
-from gzip import GzipFile
-from StringIO import StringIO
-class ContentEncodingProcessor(urllib2.BaseHandler):
- """A handler to add gzip capabilities to urllib2 requests """
-
- # add headers to requests
- def http_request(self, req):
- req.add_header("Accept-Encoding", "gzip, deflate")
- return req
-
- # decode
- def http_response(self, req, resp):
- old_resp = resp
- # gzip
- if resp.headers.get("content-encoding") == "gzip":
- gz = GzipFile(
- fileobj=StringIO(resp.read()),
- mode="r"
- )
- resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code)
- resp.msg = old_resp.msg
- # deflate
- if resp.headers.get("content-encoding") == "deflate":
- gz = StringIO( deflate(resp.read()) )
- resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code) # 'class to add info() and geturl() methods to an open file.'
- resp.msg = old_resp.msg
- return resp
-
-# deflate support
-import zlib
-def deflate(data): # zlib only provides the zlib compress format, not the deflate format;
- try: # so on top of all there's this workaround:
- return zlib.decompress(data, -zlib.MAX_WBITS)
- except zlib.error:
- return zlib.decompress(data)
-
-
-
-
-
-
-
-#-- init for later use
-if urllib2:
-
- # config 1
- handlers = [None, None, None]
-
- # base
- handlers[0] = urllib2.HTTPHandler()
- if config.conf.debug:
- handlers[0].set_http_debuglevel(3)
-
- # content-encoding
- handlers[1] = ContentEncodingProcessor()
-
- # store cookies at runtime
- import cookielib
- cj = cookielib.CookieJar()
- handlers[2] = urllib2.HTTPCookieProcessor( cj )
-
- # inject into urllib2
- urllib2.install_opener( urllib2.build_opener(*handlers) )
-
-
-
-
-# alternative function names
-AJAX=ajax
-POST=ajax
-GET=get
-URL=fix_url
-
-
Index: kronos.py
==================================================================
--- kronos.py
+++ kronos.py
@@ -272,11 +272,11 @@
def _run(self):
# Low-level run method to do the actual scheduling loop.
while self.running:
try:
self.sched.run()
- except Exception,x:
+ except Exception as x:
print >>sys.stderr, "ERROR DURING SCHEDULER EXECUTION",x
print >>sys.stderr, "".join(
traceback.format_exception(*sys.exc_info()))
print >>sys.stderr, "-" * 20
# queue is empty; sleep a short while before checking again
@@ -296,11 +296,11 @@
def __call__(self, schedulerref):
"""Execute the task action in the scheduler's thread."""
try:
self.execute()
- except Exception,x:
+ except Exception as x:
self.handle_exception(x)
self.reschedule(schedulerref())
def reschedule(self, scheduler):
"""This method should be defined in one of the sub classes!"""
@@ -464,11 +464,11 @@
def threadedcall(self):
# This method is run within its own thread, so we have to
# do the execute() call and exception handling here.
try:
self.execute()
- except Exception,x:
+ except Exception as x:
self.handle_exception(x)
class ThreadedIntervalTask(ThreadedTaskMixin, IntervalTask):
"""Interval Task that executes in its own thread."""
pass
@@ -531,11 +531,11 @@
pid = os.fork()
if pid == 0:
# we are the child
try:
self.execute()
- except Exception,x:
+ except Exception as x:
self.handle_exception(x)
os._exit(0)
else:
# we are the parent
self.reschedule(schedulerref())
Index: mygtk.py
==================================================================
--- mygtk.py
+++ mygtk.py
@@ -2,11 +2,11 @@
# encoding: UTF-8
# api: python
# type: functions
# title: mygtk helper functions
# description: simplify usage of some gtk widgets
-# version: 1.6
+# version: 1.7
# author: mario
# license: public domain
#
#
# Wrappers around gtk methods. The TreeView method .columns() allows
@@ -27,33 +27,42 @@
# debug
from config import __print__, dbg
+# filesystem
+import os.path
+import copy
+import sys
+
+if sys.version_info[0] >= 3:
+ unicode = str
+
-# gtk modules
-gtk = 0 # 0=gtk2, else gtk3
-if gtk:
+# gtk version
+ver = 2 # 2=gtk2, 3=gtk3
+if "--gtk3" in sys.argv:
+ ver = 3
+if sys.version_info >= (3, 0):
+ ver = 3
+# load gtk modules
+if ver==3:
from gi import pygtkcompat as pygtk
pygtk.enable()
pygtk.enable_gtk(version='3.0')
from gi.repository import Gtk as gtk
from gi.repository import GObject as gobject
from gi.repository import GdkPixbuf
ui_file = "gtk3.xml"
- __print__(gtk)
- __print__(gobject)
-if not gtk:
+ __print__(dbg.PROC, gtk)
+ __print__(dbg.PROC, gobject)
+else:
import pygtk
import gtk
import gobject
ui_file = "gtk2.xml"
-# filesystem
-import os.path
-import copy
-
try:
empty_pixbuf = gtk.gdk.pixbuf_new_from_data(b"\0\0\0\0",gtk.gdk.COLORSPACE_RGB,True,8,1,1,4)
except:
empty_pixbuf = GdkPixbuf.Pixbuf.new_from_data(b"\0\0\0\0", GdkPixbuf.Colorspace.RGB, True, 8, 1, 1, 4, None, None)
@@ -105,11 +114,11 @@
if (desc[1] > 0):
col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
col.set_fixed_width(desc[1])
# loop through cells
- for var in xrange(2, len(desc)):
+ for var in range(2, len(desc)):
cell = desc[var]
# cell renderer
if (cell[2] == "pixbuf"):
rend = gtk.CellRendererPixbuf() # img cell
if (cell[1] == str):
@@ -124,11 +133,11 @@
#col.set_sort_column_id(datapos) # only on textual cells
# attach cell to column
col.pack_end(rend, expand=cell[3].get("expand",True))
# apply attributes
- for attr,val in cell[3].iteritems():
+ for attr,val in list(cell[3].items()):
col.add_attribute(rend, attr, val)
# next
datapos += 1
__print__(cell)
@@ -147,11 +156,11 @@
#- expand datamap
vartypes = [] #(str, str, bool, str, int, int, gtk.gdk.Pixbuf, str, int)
rowmap = [] #["title", "desc", "bookmarked", "name", "count", "max", "img", ...]
if (not rowmap):
for desc in datamap:
- for var in xrange(2, len(desc)):
+ for var in range(2, len(desc)):
vartypes.append(desc[var][1]) # content types
rowmap.append(desc[var][0]) # dict{} column keys in entries[] list
# create gtk array storage
ls = gtk.ListStore(*vartypes) # could be a TreeStore, too
__print__(vartypes)
Index: pq.py
==================================================================
--- pq.py
+++ pq.py
@@ -17,11 +17,11 @@
from pyquery import PyQuery as pq
# pq.each_pq = lambda self,func: self.each( lambda i,html: func( pq(html, parser="html") ) )
-except Exception, e:
+except Exception as e:
# disable use
pq = None
config.conf.pyquery = False
Index: st2.py
==================================================================
--- st2.py
+++ st2.py
@@ -3,11 +3,11 @@
# api: python
# type: application
# title: streamtuner2
# description: directory browser for internet radio / audio streams
# depends: gtk, pygtk, xml.dom.minidom, threading, lxml, pyquery, kronos
-# version: 2.0.9.5
+# version: 2.0.9.6
# author: mario salzer
# license: public domain
# url: http://freshmeat.net/projects/streamtuner2
# config:
# category: multimedia
@@ -98,11 +98,11 @@
from mygtk import pygtk, gtk, gobject, ui_file, mygtk
# custom modules
from config import conf # initializes itself, so all conf.vars are available right away
from config import __print__, dbg
-import http
+import ahttp
import action # needs workaround... (action.main=main)
from channels import *
import favicon
#from pq import pq
@@ -168,11 +168,11 @@
except: __print__(dbg.INIT, "main.__init__: current_channel.first_show() initialization error")
# bind gtk/glade event names to functions
gui_startup(19/20.0)
- self.connect_signals(dict( {
+ self.connect_signals(dict( list({
"gtk_main_quit" : self.gtk_main_quit, # close window
# treeviews / notebook
"on_stream_row_activated" : self.on_play_clicked, # double click in a streams list
"on_category_clicked": self.on_category_clicked, # new selection in category list
"on_notebook_channels_switch_page": self.channel_switch, # channel notebook tab changed
@@ -218,11 +218,11 @@
"true": lambda w,*args: True,
"streamedit_open": streamedit.open,
"streamedit_save": streamedit.save,
"streamedit_new": streamedit.new,
"streamedit_cancel": streamedit.cancel,
- }.items() + self.add_signals.items() ))
+ }.items() ) + list( self.add_signals.items() ) ))
# actually display main window
gui_startup(99/100.0)
self.win_streamtuner2.show()
@@ -235,19 +235,19 @@
#-- Shortcut for glade.get_widget()
# Allows access to widgets as direct attributes instead of using .get_widget()
# Also looks in self.channels[] for the named channel plugins
def __getattr__(self, name):
- if (self.channels.has_key(name)):
+ if (name in self.channels):
return self.channels[name] # like self.shoutcast
else:
return self.get_object(name) # or gives an error if neither exists
# custom-named widgets are available from .widgets{} not via .get_widget()
def get_widget(self, name):
- if self.widgets.has_key(name):
+ if name in self.widgets:
return self.widgets[name]
else:
return gtk.Builder.get_object(self, name)
@@ -523,11 +523,14 @@
# end application and gtk+ main loop
def gtk_main_quit(self, widget, *x):
if conf.auto_save_appstate:
- self.app_state(widget)
+ try: # doesn't work with gtk3 yet (probably just hooking at the wrong time)
+ self.app_state(widget)
+ except:
+ None
gtk.main_quit()
@@ -1151,11 +1154,11 @@
#-- global configuration settings
"conf = Config()" # already happened with "from config import conf"
# graphical
- if len(sys.argv) < 2:
+ if len(sys.argv) < 2 or "--gtk3" in sys.argv:
# prepare for threading in Gtk+ callbacks
gobject.threads_init()
gui_startup(1/100.0)
@@ -1164,11 +1167,11 @@
main = StreamTunerTwo()
# module coupling
action.main = main # action (play/record) module needs a reference to main window for gtk interaction and some URL/URI callbacks
action = action.action # shorter name
- http.feedback = main.status # http module gives status feedbacks too
+ ahttp.feedback = main.status # http module gives status feedbacks too
# first invocation
if (conf.get("firstrun")):
config_dialog.open(None)
del conf.firstrun