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

⌈⌋ ⎇ branch:  streamtuner2


Check-in [743c60ff80]

Overview
Comment:Remove old .ico workaround, as PIL2(Pillow) now integrates support. Leave the remaining code in shambles, and with excessive logging. Still needs a huge rewrite to optionalize Google reliance again. (The manual favicon fetching never worked, because the regexp is too crude for most sites; and URL joining is off too.)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 743c60ff8031cceaf1f6811b2f404e305461df5a
User & Date: mario on 2015-04-21 22:07:29
Other Links: manifest | tags
Context
2015-04-22
20:49
Let bookmarks channel use generic.load() to reapply scroll position y= after inserts. check-in: a6ba97bce0 user: mario tags: trunk
2015-04-21
22:07
Remove old .ico workaround, as PIL2(Pillow) now integrates support. Leave the remaining code in shambles, and with excessive logging. Still needs a huge rewrite to optionalize Google reliance again. (The manual favicon fetching never worked, because the regexp is too crude for most sites; and URL joining is off too.) check-in: 743c60ff80 user: mario tags: trunk
22:05
Playlist DND import and conversion has been greatly simplified. (To the detriment of the action module now becoming more complex.) check-in: 705c7d16c2 user: mario tags: trunk
Changes

Modified favicon.py from [c9075572d2] to [0e317dc343].

25
26
27
28
29
30
31
32
33
34


35
36
37
38
39
40
41
delete_google_stub = 1   # don't keep placeholder images
google_placeholder_filesizes = (726,896)


import os, os.path
from compat2and3 import xrange, urllib
import re
from config import conf
from threading import Thread
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
tried_urls = []








|


>
>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
delete_google_stub = 1   # don't keep placeholder images
google_placeholder_filesizes = (726,896)


import os, os.path
from compat2and3 import xrange, urllib
import re
from config import *
from threading import Thread
import ahttp
import compat2and3
from PIL import Image



# 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
tried_urls = []

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176

177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  # fastest method, so default to google for now
  if always_google:
      google_ico2png(url)
      if available(url) or only_google:
         return

  try:    # look for /favicon.ico first
    #print("favicon.ico")
    direct_download("http://"+domain(url)+"/favicon.ico", file(url))

  except:
    try:    # extract facicon filename from website <link rel>
      #print("html <rel favicon>")
      html_download(url)

    except:    # fallback
      #print("google ico2png")
      google_ico2png(url)




# retrieve PNG via Google ico2png
def google_ico2png(url):


  #try:
     GOOGLE = "http://www.google.com/s2/favicons?domain="
     (fn, headers) = urllib.urlretrieve(GOOGLE+domain(url), file(url))

     # test for stub image
     if delete_google_stub and (filesize(fn) in google_placeholder_filesizes):
        os.remove(fn)

  
def filesize(fn):
   return os.stat(fn).st_size










|




|


|
|







>

<
|
|

|
|
|







155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180

181
182
183
184
185
186
187
188
189
190
191
192
193
  # fastest method, so default to google for now
  if always_google:
      google_ico2png(url)
      if available(url) or only_google:
         return

  try:    # look for /favicon.ico first
    log.FAVICON("try /favicon.ico")
    direct_download("http://"+domain(url)+"/favicon.ico", file(url))

  except:
    try:    # extract facicon filename from website <link rel>
      log.FAVICON("html <rel favicon>")
      html_download(url)

    except Exception as e:    # fallback
      log.ERR(e)
      google_ico2png(url)




# retrieve PNG via Google ico2png
def google_ico2png(url):
    log.FAVICON("google ico2png")


    GOOGLE = "http://www.google.com/s2/favicons?domain="
    (fn, headers) = urllib.urlretrieve(GOOGLE+domain(url), file(url))

    # test for stub image
    if delete_google_stub and (filesize(fn) in google_placeholder_filesizes):
       os.remove(fn)

  
def filesize(fn):
   return os.stat(fn).st_size



200
201
202
203
204
205
206
207
208
209
210

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255

256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
      return "*/*"



# favicon.ico
def direct_download(favicon, fn):

#  try: 
    # URL download
    r = urllib.urlopen(favicon)
    headers = r.info()

    
    # abort on
    if r.getcode() >= 300:
       raise Error("HTTP error" + r.getcode())
    if not headers["Content-Type"].lower().find("image/") == 0:
       raise Error("can't use text/* content")
       
    # save file
    fn_tmp = fn+".tmp"
    f = open(fn_tmp, "wb")
    f.write(r.read(32768))
    f.close()
        
    # check type
    if headers["Content-Type"].lower()=="image/png" and favicon.find(".png") and filetype(fn)=="image/png":
       pngresize(fn_tmp)
       os.mv(fn_tmp, fn)
    else:
       ico2png(fn_tmp, fn)
       os.remove(fn_tmp)

 # except:
  #  "File not found" and False


  
# peek at URL, download favicon.ico <link rel>
def html_download(url):


  # <link rel>
  #try:
    # download html, look for @href in <link rel=shortcut icon>
    r = urllib.urlopen(url)
    html = r.read(4096)
    r.close()
    rx = re.compile("""<link[^<>]+rel\s*=\s*"?\s*(?:shortcut\s+|fav)?icon[^<>]+href=["'](?P<href>[^<>"']+)["'<>\s].""")
    favicon = "".join(rx.findall(html))

    
    # url or
    if favicon.startswith("http://"):
       None
    # just /pathname
    else:
       favicon = ahttp.urlparse.urljoin(url, favicon)

       #favicon = "http://" + domain(url) + "/" + favicon

    # download
    direct_download(favicon, file(url))





#@obsolete since Pillow 2.1.x

#
# title: workaround for PIL.Image to preserve the transparency for .ico import
#
# http://stackoverflow.com/questions/987916/how-to-determine-the-transparent-color-index-of-ico-image-with-pil
# http://djangosnippets.org/snippets/1287/
#
# Author: dc
# Posted: January 17, 2009
# Languag: Python
# Django Version: 1.0
# Tags: pil image ico 
# Score: 2 (after 2 ratings)
#
         
import operator
import struct

try:
    from PIL import BmpImagePlugin, PngImagePlugin, Image
except Exception as e:
    print("no PIL", e)
    always_google = 1
    only_google = 1


def load_icon(file, index=None):
    '''
    Load Windows ICO image.

    See http://en.wikipedia.org/w/index.php?oldid=264332061 for file format
    description.
    '''
    if isinstance(file, basestring):
        file = open(file, 'rb')

    try:
        header = struct.unpack('<3H', file.read(6))
    except:
        raise IOError('Not an ICO file')

    # Check magic
    if header[:2] != (0, 1):
        raise IOError('Not an ICO file')

    # Collect icon directories
    directories = []
    for i in xrange(header[2]):
        directory = list(struct.unpack('<4B2H2I', file.read(16)))
        for j in xrange(3):
            if not directory[j]:
                directory[j] = 256

        directories.append(directory)

    if index is None:
        # Select best icon
        directory = max(directories, key=operator.itemgetter(slice(0, 3)))
    else:
        directory = directories[index]

    # Seek to the bitmap data
    file.seek(directory[7])

    prefix = file.read(16)
    file.seek(-16, 1)

    if PngImagePlugin._accept(prefix):
        # Windows Vista icon with PNG inside
        image = PngImagePlugin.PngImageFile(file)
    else:
        # Load XOR bitmap
        image = BmpImagePlugin.DibImageFile(file)
        if image.mode == 'RGBA':
            # Windows XP 32-bit color depth icon without AND bitmap
            pass
        else:
            # Patch up the bitmap height
            image.size = image.size[0], image.size[1] >> 1
            d, e, o, a = image.tile[0]
            image.tile[0] = d, (0, 0) + image.size, o, a

            # Calculate AND bitmap dimensions. See
            # http://en.wikipedia.org/w/index.php?oldid=264236948#Pixel_storage
            # for description
            offset = o + a[1] * image.size[1]
            stride = ((image.size[0] + 31) >> 5) << 2
            size = stride * image.size[1]

            # Load AND bitmap
            file.seek(offset)
            string = file.read(size)
            mask = Image.fromstring('1', image.size, string, 'raw',
                                    ('1;I', stride, -1))

            image = image.convert('RGBA')
            image.putalpha(mask)

    return image




# convert .ico file to .png format
def ico2png(ico, png_fn):
  #print("ico2png", ico, png, image)
  
  try:  # .ico
    image = load_icon(ico, None)
  except:  # automatic img file type guessing
    image = Image.open(ico)
       
  # resize
  if image.size[0] > 16:
    image.resize((16, 16), Image.ANTIALIAS)

  # .png format
  image.save(png_fn, "PNG", quality=98)


# resize an image
def pngresize(fn, x=16, y=16):
  image = Image.open(fn)
  if image.size[0] > x:
    image.resize((x, y), Image.ANTIALIAS)
    image.save(fn, "PNG", quality=98)




#-- test
if __name__ == "__main__":
    import sys
    download(sys.argv[1])









<



>



|

|















<
<
<














>






|
>







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


<
<
<
<
<

|
|
|
|
<
|
|




|
|
|
|










202
203
204
205
206
207
208

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263










































































































264
265





266
267
268
269
270

271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
      return "*/*"



# favicon.ico
def direct_download(favicon, fn):


    # URL download
    r = urllib.urlopen(favicon)
    headers = r.info()
    log.HTTP(headers)
    
    # abort on
    if r.getcode() >= 300:
       raise Exception("HTTP error %s" % r.getcode())
    if not headers["Content-Type"].lower().find("image/") == 0:
       raise Exception("can't use text/* content")
       
    # save file
    fn_tmp = fn+".tmp"
    f = open(fn_tmp, "wb")
    f.write(r.read(32768))
    f.close()
        
    # check type
    if headers["Content-Type"].lower()=="image/png" and favicon.find(".png") and filetype(fn)=="image/png":
       pngresize(fn_tmp)
       os.mv(fn_tmp, fn)
    else:
       ico2png(fn_tmp, fn)
       os.remove(fn_tmp)





  
# peek at URL, download favicon.ico <link rel>
def html_download(url):


  # <link rel>
  #try:
    # download html, look for @href in <link rel=shortcut icon>
    r = urllib.urlopen(url)
    html = r.read(4096)
    r.close()
    rx = re.compile("""<link[^<>]+rel\s*=\s*"?\s*(?:shortcut\s+|fav)?icon[^<>]+href=["'](?P<href>[^<>"']+)["'<>\s].""")
    favicon = "".join(rx.findall(html))
    log.DATA(favicon)
    
    # url or
    if favicon.startswith("http://"):
       None
    # just /pathname
    else:
       favicon = compat2and3.urlparse.urljoin(url, favicon)
       log.FAVICON(favicon)
       #favicon = "http://" + domain(url) + "/" + favicon

    # download
    direct_download(favicon, file(url))













































































































# convert .ico file to .png format
def ico2png(ico, png_fn):





    image = Image.open(ico)
    log.ICO2PNG(ico, png, image)
    # resize
    if image.size[0] > 16:
        image.resize((16, 16), Image.ANTIALIAS)

    # .png format
    image.save(png_fn, "PNG", quality=98)


# resize an image
def pngresize(fn, x=16, y=16):
    image = Image.open(fn)
    if image.size[0] > x:
        image.resize((x, y), Image.ANTIALIAS)
        image.save(fn, "PNG", quality=98)




#-- test
if __name__ == "__main__":
    import sys
    download(sys.argv[1])