1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
# encoding: utf-8
# api: streamtuner2
# title: Favicons
# description: Display station favicons/logos. Instantly download them when â–¸playing.
# config:
# { name: favicon_google_first, type: bool, value: 1, description: "Prefer faster Google favicon to PNG conversion service." }
# { name: favicon_delete_stub , type: bool, value: 1, description: "Don't accept any placeholder favicons." }
# [ main-name: google_homepage ]
# [ main-name: load_favicon ]
# type: feature
# category: ui
# version: 1.9
# depends: streamtuner2 >= 2.1.9, python:pil
# priority: standard
#
# This module fetches a favicon for each station, or a small banner
# or logo for some channel modules. It converts .ico image files and
# sanitizes .png or .jpeg images even prior display.
#
# It prepares cache files in ~/.config/streamtuner2/icons/ in silent
# agreement with the station list display logic. Either uses station
# row["homepage"] or row["img"] URLs from any entry.
#
# While it can often discover favicons directly from station homepages,
# it's often speedier to use the Google PNG conversion service. Both
# depend on a recent Pillow2 python module (superseding the PIL module).
# Else may display images with fragments if converted from ICO files.
import os, os.path
from io import BytesIO
import re
import channels
from config import *
|
>
|
<
>
>
>
>
|
|
|
|
|
<
|
|
>
|
>
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
# encoding: utf-8
# api: streamtuner2
# title: Favicons
# description: Display station favicons/logos. Instantly download them when â–¸playing.
# config:
# { name: load_favicon, type: bool, value: 1, description: "Load favicon instantly when â–¸playing a station.", color: yellow }
# { name: favicon_google_first, type: bool, value: 1, description: "Prefer faster Google favicon to PNG conversion service." }
# { name: favicon_delete_stub , type: bool, value: 1, description: "Don't accept any placeholder favicons." }
# { name: google_homepage, type: bool, value: 0, description: "Google missing station homepages right away." }
# type: feature
# category: ui
# version: 1.9
# depends: streamtuner2 >= 2.1.9, python:pil
# priority: standard
# png:
# iVBORw0KGgoAAAANSUhEUgAAABYAAAAWBAMAAAA2mnEIAAAAJ1BMVEUAAACwDw5oKh1RRU5OTSCOTxp0Um9zcyFUhSXsbwChdp/lgCNbrA7VFTQPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHU
# gAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQffBQ4ENQJMtfdfAAAAmUlEQVQY02NgcAECYxBgYODeOXPmTKtVQLCAwXsmjL2YQRPINDNGsFclI7GXQdmzZ87MSoOyI0pnpgHVLAOy1c+c
# mTkzeFWioBSUbQZkiy1mcPFpCXUxTksTFEtm8Ojp6OhQVDJWVFJi8DkDBIIgIARhKyKx3c8g2GfOBCKxFeHspg6EmiZFJDbEHB44W4CBwQNor5MSEDAAAGcoaQmD1t8TAAAAAElFTkSuQmCC
#
# This module fetches a favicon for each station. Some channels
# provide small logos/banners even. It sanitizes and converts
# any .ico/.png/.jpeg file prior display.
#
# Cache files are kept in ~/.config/streamtuner2/icons/ where
# the station column display picks them up form.
#
# While it can often discover favicons directly from station
# homepages, it's mostly speedier to use the PNG conversion
# service from Google.
# Both methods depend on a recent Pillow2 python module (that
# superseded the PIL module). Else icon display may have some
# transparency fragments.
import os, os.path
from io import BytesIO
import re
import channels
from config import *
|
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
# station list updates.
#
# · uikit.columns() merely checks row["favicon"] for file existence
# when redrawing a station list.
#
# · main calls .update_playing() on hooks["play"],
# or .update_all() per menu command
#
# · urllib is no longer required. Using just ahttp/requests API now.
#
# · Might need unhtml() utility from channels/__init__ later..
#
# · Still need to consolidate config options → Move main favicon
# options here?
#
# Hook up as feature plugin
#
class favicon(object):
# plugin attributes
module = "favicon"
meta = plugin_meta()
# Register with main
def __init__(self, parent):
# Reference main, and register hook
self.parent, self.main = parent, parent
parent.hooks["play"].append(self.update_playing)
# Prepare favicon cache directory
conf.icon_dir = conf.dir + "/icons"
if not os.path.exists(conf.icon_dir):
os.mkdir(conf.icon_dir)
open(icon_dir+"/.nobackup", "a").close()
# Hook into channel/streams updating pipine
channels.GenericChannel.prepare_filters.append(self.prepare_filter_favicon)
# Main callback: update favicon cache for complete list of station rows
def update_all(self, *args, **kwargs):
#kwargs[pixstore] = self.parent.channel()._ls, ...
self.parent.thread(self.update_rows, *args, **kwargs)
# Main callback for a single play() event
def update_playing(self, row, pixstore=None, channel=None, **x):
# Homepage search
if conf.google_homepage and not len(row.get("homepage", "")):
found = google_find_homepage(row)
# could call channel.save() now to preserve found homepage URL
else:
found = False
# Favicon only for currently playing station
if conf.load_favicon:
if row.get("homepage") or row.get("img"):
self.update_all([row], pixstore=pixstore, always_update=found)
# Run through rows[] to update "favicon" from "homepage" or "img",
# optionally display new image right away in ListStore
def update_rows(self, entries, pixstore=None, always_update=False):
for i,row in enumerate(entries):
ok = False
# Try just once
if row.get("homepage") in tried_urls:
continue
# Ignore existing ["favicon"] filename
|
<
<
<
<
<
<
<
<
|
>
>
>
|
<
<
<
<
|
|
|
>
>
|
|
|
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
# station list updates.
#
# · uikit.columns() merely checks row["favicon"] for file existence
# when redrawing a station list.
#
# · main calls .update_playing() on hooks["play"],
# or .update_all() per menu command
# Hook up as feature plugin
#
class favicon(object):
# plugin attributes
module = "favicon"
meta = plugin_meta()
# Register with main
def __init__(self, parent):
# Reference main, and register station .play() hook for conf.load_favicon
self.parent, self.main = parent, parent
parent.hooks["play"].append(self.update_playing)
# Register in channel/streams updating pipeline (to predefine row["favicon"] filename from `homepage` or `img`)
channels.GenericChannel.prepare_filters.append(self.prepare_filter_favicon)
# Prepare favicon cache directory
conf.icon_dir = conf.dir + "/icons"
if not os.path.exists(conf.icon_dir):
os.mkdir(conf.icon_dir)
open(icon_dir+"/.nobackup", "a").close()
# Main menu "Update favicons": update favicon cache for complete list of station rows
def update_all(self, *args, **kwargs):
#kwargs[pixstore] = self.parent.channel()._ls, ...
self.parent.thread(self.update_rows, *args, **kwargs)
# Main [â–¸play] event for a single station
def update_playing(self, row, pixstore=None, channel=None, **x):
# Homepage search
if conf.google_homepage and not len(row.get("homepage", "")):
found = google_find_homepage(row)
# Save channel list right away to preserve found homepage URL
if found and conf.auto_save_stations:
channel.save()
else:
found = False
# Favicon only for currently playing station
if conf.load_favicon:
if row.get("homepage") or row.get("img"):
self.parent.thread(self.update_rows, [row], pixstore=pixstore, always_update=found)
# Run through rows[] to update "favicon" from "homepage" or "img",
# optionally display new image right away in ListStore
def update_rows(self, entries, pixstore=None, always_update=False, **x):
for i,row in enumerate(entries):
ok = False
# Try just once
if row.get("homepage") in tried_urls:
continue
# Ignore existing ["favicon"] filename
|
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
except Exception as e:
log.ERR("Update_pixstore image", fn, "error:", e)
# Run after any channel .update_streams() to populate row["favicon"]
# from `homepage` or `img` url.
def prepare_filter_favicon(self, row):
row["favicon"] = row_to_fn(row)
#--- somewhat unrelated ---
#
|
>
|
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
except Exception as e:
log.ERR("Update_pixstore image", fn, "error:", e)
# Run after any channel .update_streams() to populate row["favicon"]
# from `homepage` or `img` url.
def prepare_filter_favicon(self, row):
# if conf.show_favicons: (do we still need that?)
row["favicon"] = row_to_fn(row)
#--- somewhat unrelated ---
#
|