OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python2 |
| 2 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 from __future__ import absolute_import |
| 7 from __future__ import division |
| 8 from __future__ import print_function |
| 9 |
| 10 import argparse |
| 11 import contextlib |
| 12 import glob |
| 13 import io |
| 14 import json |
| 15 import os |
| 16 import sys |
| 17 import urllib |
| 18 import Image |
| 19 import StringIO |
| 20 |
| 21 """ |
| 22 This script downloads the default popular sites and large icons associated |
| 23 with it. If an icon is too large, it will get resized in the process. |
| 24 """ |
| 25 |
| 26 DEFAULT_POPULAR_SITES = ("https://www.gstatic.com/chrome/ntp/" |
| 27 "suggested_sites_DEFAULT_5.json") |
| 28 LARGE_ICON_KEY = "large_icon_url" |
| 29 SITE_TITLE_KEY = "title" |
| 30 MAXIMAL_SIZE = 144 |
| 31 MAXIMAL_FILE_SIZE = 8192 # 8 KiB |
| 32 SITE_ICON_DELETE = "icon[0-9].png" |
| 33 SITE_ICON_FORMAT = "icon%d.png" |
| 34 NTP_TILES_RESOURCE_PATH = os.path.join( |
| 35 os.path.dirname(os.path.realpath(__file__)), "resources") |
| 36 DEFAULT_POPULAR_SITES_PATH = os.path.join(NTP_TILES_RESOURCE_PATH, |
| 37 "default_popular_sites.json") |
| 38 |
| 39 |
| 40 def download_as_json(url): |
| 41 """Downloads data from the given |url| and returns it in JSON format.""" |
| 42 print("Downloading popular sites... (" + url + ")") |
| 43 with contextlib.closing(urllib.urlopen(url=url)) as url_data: |
| 44 data = json.load(url_data) |
| 45 print("... done. (%d sites found)" % len(data)) |
| 46 return data |
| 47 |
| 48 |
| 49 def download_as_file(url, image_name): |
| 50 """Downloads the given |url| as file and returns the path.""" |
| 51 image_path = os.path.join(NTP_TILES_RESOURCE_PATH, image_name) |
| 52 urllib.urlretrieve(url, image_path) |
| 53 return image_path |
| 54 |
| 55 |
| 56 def write_to_json(data, out_path, pretty_print): |
| 57 """Writes |data| to the given |out_path|. Minifies the JSON unless |
| 58 |pretty_print| is set to true.""" |
| 59 separators = (",", ":") |
| 60 indent = None |
| 61 sort = False |
| 62 if pretty_print: |
| 63 separators = (",", ": ") |
| 64 indent = 4 |
| 65 sort = True |
| 66 with open(out_path, "w") as f: |
| 67 json.dump(data, f, sort_keys=sort, indent=indent, separators=separators) |
| 68 print("JSON was written to " + out_path) |
| 69 |
| 70 |
| 71 def delete_old_icons(): |
| 72 """Deletes all PNG icons within the resource path.""" |
| 73 print("Deleting old icons..") |
| 74 for f in glob.glob(os.path.join(NTP_TILES_RESOURCE_PATH, SITE_ICON_DELETE)): |
| 75 os.remove(os.path.join(f)) |
| 76 print("... done.") |
| 77 |
| 78 |
| 79 def resize_if_too_large(image_path, max_size): |
| 80 """Takes a square icon and resizes if it exceeds the maximal width.""" |
| 81 image = Image.open(image_path) |
| 82 if image.size[0] > max_size: |
| 83 print("... and resizing image from %s to %s" % |
| 84 (image.size, (max_size, max_size))); |
| 85 image.thumbnail((max_size, max_size), Image.ANTIALIAS) |
| 86 image.save(image_path, "PNG", compress_level=9) |
| 87 |
| 88 |
| 89 def exceeds_max_file_size(image_path): |
| 90 """A file that is below a threshold would only loose quality on scaling |
| 91 down for a neglible memory gain. Impact especially for text-heavy icons.""" |
| 92 return os.path.getsize(image_path) > MAXIMAL_FILE_SIZE |
| 93 |
| 94 |
| 95 def lacks_required_keys(site): |
| 96 """A site must at least provide a title and a large icon to be processed.""" |
| 97 return not SITE_TITLE_KEY in site or not LARGE_ICON_KEY in site |
| 98 |
| 99 |
| 100 def main(): |
| 101 parser = argparse.ArgumentParser( |
| 102 description="Downloads the latest popular sites and their icons. \n\n" |
| 103 "It is possible to customize the default like this:\n" |
| 104 " 1. python " + __file__ + " -o temp.json --no_icons " |
| 105 "--pretty_print\n" |
| 106 " 2. Adjust the downloaded temp.json\n" |
| 107 " 3. python " + __file__ + " -f temp.json -s 96\n\n" |
| 108 "The result would be a minified version of your customized JSON " |
| 109 "and all icons would be downloaded as you specified.\n The icons " |
| 110 "had a max size of 96x96.", |
| 111 formatter_class=argparse.RawTextHelpFormatter) |
| 112 parser.add_argument("-s", "--size", metavar="size_in_px", type=int, |
| 113 default=MAXIMAL_SIZE, |
| 114 help="size to scale icons (exceeding %d KiB) down to;" |
| 115 "defaults to 144px" % (MAXIMAL_FILE_SIZE / 1024)) |
| 116 parser.add_argument("-u", "--url", type=str, |
| 117 default=DEFAULT_POPULAR_SITES, |
| 118 help="the endpoint to query for json of sites") |
| 119 parser.add_argument("--no_icons", action="store_true", |
| 120 help="do not download icons") |
| 121 parser.add_argument("--no_resizing", action="store_true", |
| 122 help="do not resize any icons") |
| 123 parser.add_argument("--force_resizing", action="store_true", |
| 124 help="resize every icons") |
| 125 parser.add_argument("-f", "--in_file", metavar="path_to_json_file", |
| 126 type=str, |
| 127 help="skip download and load icons for a local json") |
| 128 parser.add_argument("-o", "--out_path", metavar="path_to_out_file", |
| 129 type=str, default=DEFAULT_POPULAR_SITES_PATH, |
| 130 help="skip download and load icons for a local json") |
| 131 parser.add_argument("-p", "--pretty_print", action="store_true", |
| 132 help="pretty_print instead of minifying the JSON") |
| 133 args = parser.parse_args() |
| 134 |
| 135 if args.in_file: |
| 136 with open(args.in_file) as f: |
| 137 popular_sites = json.load(f) |
| 138 else: |
| 139 popular_sites = download_as_json(args.url) |
| 140 write_to_json(popular_sites, args.out_path, args.pretty_print) |
| 141 |
| 142 if args.no_icons: |
| 143 return |
| 144 |
| 145 delete_old_icons() |
| 146 for i, site in enumerate(popular_sites): |
| 147 if lacks_required_keys(site): |
| 148 print("Could not download large image for site: %r" % site) |
| 149 continue |
| 150 print("Downloading icon for '%r'" % site[SITE_TITLE_KEY]) |
| 151 image_path = download_as_file(site[LARGE_ICON_KEY], |
| 152 SITE_ICON_FORMAT % i) |
| 153 if args.force_resizing or (exceeds_max_file_size(image_path) and |
| 154 not args.no_resizing): |
| 155 resize_if_too_large(image_path, args.size) |
| 156 |
| 157 if __name__ == "__main__": |
| 158 main() |
OLD | NEW |