Check-in [56776a4e90]
Overview
Comment: | Make favicon redisplay work after google_find_homepage() and existing icons in cache. Shorter timeout/display for status label for google search. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
56776a4e90031342e9369fd4f6ba0ec0 |
User & Date: | mario on 2015-05-13 19:00:11 |
Other Links: | manifest | tags |
Context
2015-05-14
| ||
00:05 | Resize logo together with toolbar buttons. check-in: edc282b52b user: mario tags: trunk | |
2015-05-13
| ||
19:00 | Make favicon redisplay work after google_find_homepage() and existing icons in cache. Shorter timeout/display for status label for google search. check-in: 56776a4e90 user: mario tags: trunk | |
00:00 | Move mime_fmt() into regular function. Fix live365 ahttp feedback= bug. Regroup functions and update a few comments in channels/__init__ check-in: 2335ea7a46 user: mario tags: trunk | |
Changes
Modified ahttp.py from [a2c670d7ab] to [f0736abd2b].
︙ | ︙ | |||
21 22 23 24 25 26 27 | #-- hooks to progress meter and status bar in main window feedback = None # Sets either text or percentage of main windows' status bar. # # Can either take a float parameter (e.g. 0.99 for % indicator) # or text message. Alternatively two parameters to update both. | | > | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #-- hooks to progress meter and status bar in main window feedback = None # Sets either text or percentage of main windows' status bar. # # Can either take a float parameter (e.g. 0.99 for % indicator) # or text message. Alternatively two parameters to update both. def progress_feedback(*args, **kwargs): # use reset values if none given if not args: args = ["", 1.0] timeout = kwargs.get("timeout", 50) # send to main win if feedback: try: [feedback(d, timeout=timeout) for d in args] except: pass # prepare default query object session = requests.Session() # default HTTP headers for requests session.headers.update({ |
︙ | ︙ | |||
57 58 59 60 61 62 63 | url, params={}, referer="", post=0, ajax=0, binary=0, content=True, encoding=None, verify=False, statusmsg=None, timeout=9.25, quieter=0 ): # statusbar info if not quieter: | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | url, params={}, referer="", post=0, ajax=0, binary=0, content=True, encoding=None, verify=False, statusmsg=None, timeout=9.25, quieter=0 ): # statusbar info if not quieter: progress_feedback(url, timeout=timeout/1.5) # combine headers headers = {} if ajax: headers["X-Requested-With"] = "XMLHttpRequest" if referer: headers["Referer"] = (referer if referer else url) |
︙ | ︙ |
Modified channels/favicon.py from [7782a3f227] to [2be50aefaa].
1 2 3 4 5 6 7 8 9 10 11 | # 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 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # 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. # |
︙ | ︙ | |||
45 46 47 48 49 50 51 | # Has recently been rewritten, is somewhat less entangled with other # modules now: # # · GenericChannel presets row["favicon"] with cache image filenames | | < < | | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | # Has recently been rewritten, is somewhat less entangled with other # modules now: # # · GenericChannel presets row["favicon"] with cache image filenames # in any case. It calls row_to_fn() per prepare_filters hook after # 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? |
︙ | ︙ | |||
100 101 102 103 104 105 106 | # 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 | | | > > > | | > > > | > | > > < | | | | 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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | # 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 if row.get("favicon") and False: pass # Cache image filename: have or can't have favicon_fn = row_to_fn(row) if not favicon_fn: continue try: # Image already exists if os.path.exists(favicon_fn): if not always_update: continue else: # For freshly added ["homepage"] when favicon already ok = True # exists in cache. Then just update pix store. # Download custom "img" banner/logo as favicon elif row.get("img"): tried_urls.append(row["img"]) ok = banner_localcopy(row["img"], favicon_fn) # Fetch homepage favicon into local png elif row.get("homepage"): tried_urls.append(row["homepage"]) if conf.favicon_google_first: ok = fav_google_ico2png(row["homepage"], favicon_fn) else: ok = fav_from_homepage(row["homepage"], favicon_fn) |
︙ | ︙ | |||
184 185 186 187 188 189 190 | try: p = gtk.gdk.pixbuf_new_from_file(fn) ls[i][pix_entry] = p except Exception as e: log.ERR("Update_pixstore image", fn, "error:", e) | | > > > | 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | try: p = gtk.gdk.pixbuf_new_from_file(fn) ls[i][pix_entry] = p 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 --- # # Should become a distinct feature plugin. - It just depends on correct # invocation order for both plugins to interact. # Googling is often blocked anyway, because this is clearly a bot request. |
︙ | ︙ | |||
210 211 212 213 214 215 216 | rx_u = re.compile(r'/url\?q=(https?://[^"&/]+)') # Use literal station title now title = row["title"] #title = title.group(0).replace(" ", "%20") # Do 'le google search | | > | | | 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 | rx_u = re.compile(r'/url\?q=(https?://[^"&/]+)') # Use literal station title now title = row["title"] #title = title.group(0).replace(" ", "%20") # Do 'le google search html = ahttp.get("http://www.google.com/search", params=dict(hl="en", q=title, client="streamtuner2"), ajax=1, timeout=3.5) # Find first URL hit url = rx_u.findall(html) if url: row["homepage"] = ahttp.fix_url(url[0]) return True pass #----------------- # Convert row["img"] or row["homepage"] into local favicon cache filename rx_strip_proto = re.compile("^\w+://|/$") rx_non_wordchr = re.compile("[^\w._-]") def row_to_fn(row): url = row.get("img") or row.get("homepage") or None if url: url = url.lower() url = rx_strip_proto.sub("", url) # strip proto:// and trailing / url = rx_non_wordchr.sub("_", url) # remove any non-word characters url = "{}/{}.png".format(conf.icon_dir, url) # prefix cache directory return url # Copy banner row["img"] into icons/ directory def banner_localcopy(url, fn): # Check URL and target filename if not re.match("^https?://[\w.-]{10}", url): return False # Fetch and save imgdata = ahttp.get(url, binary=1, verify=False) if imgdata: return store_image(imgdata, fn) # Check for valid image binary, possibly convert or resize, then save to cache filename def store_image(imgdata, fn, resize=None): # Convert accepted formats -- even PNG for filtering now if re.match(br'^(.PNG|GIF\d+|.{0,15}JFIF|\x00\x00\x01\x00|.{0,255}<svg[^>]+svg)', imgdata): try: # Read from byte/str image = Image.open(BytesIO(imgdata)) |
︙ | ︙ | |||
328 329 330 331 332 333 334 | # Convert, resize and save return store_image(r.content, fn, resize=16) # Download HTML, look for favicon name in <link rel=shortcut icon>. # | | | | 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | # Convert, resize and save return store_image(r.content, fn, resize=16) # Download HTML, look for favicon name in <link rel=shortcut icon>. # # Very rough: doesn't respect any <base href=> and manually patches # icon path to homepage url; doesn't do much HTML entity decoding. # def html_link_icon(url, href="/favicon.png"): html = ahttp.get(url, encoding="iso-8859-1", timeout=4.5, quieter=1) # Extract for link in re.findall(r""" <link ([^<>]+) > """, html, re.X): pair = re.findall(r""" \b(rel|href) \s*=\s* ["']? ([^<>"']+) ["']? """, link, re.X) pair = { name: val for name, val in pair } |
︙ | ︙ |
Modified st2.py from [507d090792] to [4c26e084be].
︙ | ︙ | |||
272 273 274 275 276 277 278 | # Play button def on_play_clicked(self, widget, event=None, *args): self.status("Starting player...") channel = self.channel() pixstore = [channel.ls, channel.pix_entry, channel.rowno()] row = channel.play() self.status("") | | | 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | # Play button def on_play_clicked(self, widget, event=None, *args): self.status("Starting player...") channel = self.channel() pixstore = [channel.ls, channel.pix_entry, channel.rowno()] row = channel.play() self.status("") [callback(row, pixstore=pixstore, channel=channel) for callback in self.hooks["play"]] # Recording: invoke streamripper for current stream URL def on_record_clicked(self, widget): self.status("Recording station...") row = self.channel().record() [callback(row) for callback in self.hooks["record"]] |
︙ | ︙ |