├── webpage_from_ipic.png ├── LICENSE ├── README.rst ├── setup.py └── src └── ipic └── __init__.py /webpage_from_ipic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexwlchan/ipic/master/webpage_from_ipic.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Dr. Drang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ipic 2 | ==== 3 | 4 | Python script for searching and downloading images from the iTunes, App, and Mac App Stores. 5 | 6 | Installation 7 | ************ 8 | 9 | I install this `with pipsi `_: 10 | 11 | .. code-block:: console 12 | 13 | git clone git@github.com:alexwlchan/ipic.git 14 | cd ipic 15 | pipsi install . 16 | 17 | Usage 18 | ----- 19 | 20 | .. code-block:: 21 | 22 | ipic (-i | --ios) 23 | ipic (-m | --mac) 24 | ipic (-a | --album) 25 | ipic (-f | --film) 26 | ipic (-t | --tv) 27 | ipic (-b | --book) 28 | ipic (-n | --narration) 29 | ipic (-h | --help) 30 | 31 | Generate and open a web page of thumbnail images and links to larger images 32 | for items in the iTunes/App/Mac App Stores. 33 | 34 | The switches are 35 | 36 | * ``-i``/``--ios``: iOS app 37 | * ``-m``/``--mac``: Mac app 38 | * ``-a``/``--album``: album 39 | * ``-f``/``--film``: movie (mnemonic: film) 40 | * ``-t``/``--tv``: TV show 41 | * ``-b``/``--book``: book 42 | * ``-n``/``--narration``: audiobook (mnemonic: narration) 43 | * ``-h``/``--help``: show the help message 44 | 45 | Only one switch is allowed. The HTML for the generated web page is saved to a temporary file. 46 | 47 | Because the API usually returns several hits, `ipic` creates and opens a web page of thumbnail images that let you choose the one you want visually. Here's an example: 48 | 49 | .. image:: webpage_from_ipic.png 50 | 51 | Each thumbnail is a link to a full-sized version of the image, 512×512 for apps and 600×600 otherwise. Hovering over a thumbnail will show the name of the item. 52 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding: utf-8 -*- 3 | 4 | import codecs 5 | import os 6 | 7 | from setuptools import find_packages, setup 8 | 9 | 10 | def local_file(name): 11 | return os.path.relpath(os.path.join(os.path.dirname(__file__), name)) 12 | 13 | 14 | SOURCE = local_file('src') 15 | README = local_file('README.rst') 16 | long_description = codecs.open(README, encoding='utf-8').read() 17 | 18 | 19 | setup( 20 | name='ipic', 21 | version='1.0.0', 22 | description='A tool for searching the iTunes, App and Mac App Stores', 23 | long_description=long_description, 24 | url='https://github.com/alexwlchan/ipic', 25 | author='Alex Chan', 26 | author_email='alex@alexwlchan.net', 27 | license='MIT', 28 | 29 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 30 | classifiers=[ 31 | 'Development Status :: 5 - Production/Stable', 32 | 'Intended Audience :: Other Audience', 33 | 'License :: OSI Approved :: MIT License', 34 | 'Programming Language :: Python :: 2', 35 | 'Programming Language :: Python :: 2.7', 36 | 'Programming Language :: Python :: 3', 37 | 'Programming Language :: Python :: 3.3', 38 | 'Programming Language :: Python :: 3.4', 39 | 'Programming Language :: Python :: 3.5', 40 | ], 41 | packages=find_packages(SOURCE), 42 | package_dir={'': SOURCE}, 43 | install_requires=[ 44 | 'docopt', 45 | 'requests', 46 | ], 47 | 48 | # To provide executable scripts, use entry points in preference to the 49 | # "scripts" keyword. Entry points provide cross-platform support and allow 50 | # pip to create the appropriate form of executable for the target platform. 51 | entry_points={ 52 | 'console_scripts': [ 53 | 'ipic=ipic:main', 54 | ], 55 | }, 56 | ) 57 | -------------------------------------------------------------------------------- /src/ipic/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf8 3 | """ 4 | iTunes API searcher. 5 | 6 | Usage: 7 | ipic (-i | --ios) 8 | ipic (-m | --mac) 9 | ipic (-a | --album) 10 | ipic (-f | --film) 11 | ipic (-t | --tv) 12 | ipic (-b | --book) 13 | ipic (-n | --narration) 14 | ipic 15 | ipic (-h | --help) 16 | 17 | Options: 18 | -h --help Show this screen 19 | -i --ios iOS app 20 | -m --mac Mac app 21 | -a --album album 22 | -f --film movie (film) 23 | -t --tv TV show 24 | -b --book book 25 | -n --narration audiobook (narration) 26 | """ 27 | 28 | import collections 29 | import subprocess 30 | import tempfile 31 | 32 | import docopt 33 | import requests 34 | 35 | 36 | # URL for the iTunes search API 37 | API_URL = 'https://itunes.apple.com/search' 38 | 39 | # Browser bundle identifier 40 | BROWSER = 'com.apple.Safari' 41 | 42 | 43 | def parse_command_line_args(args=None): 44 | """ 45 | Parses the command-line arguments, and returns a tuple with 46 | the search term, and some variables for the iTunes API call. 47 | """ 48 | # If no arguments are provided, read them from the command-line. 49 | if args is None: 50 | args = docopt.docopt(__doc__) 51 | 52 | # Get the search term we're using. 53 | search_term = args[''] 54 | 55 | # Set up the API parameters for the different types of media. 56 | APIParameters = collections.namedtuple( 57 | 'APIParameters', ['size', 'media', 'entity', 'name']) 58 | 59 | if args['--ios']: 60 | parms = APIParameters(512, 'software', 'software', 'trackName') 61 | elif args['--mac']: 62 | parms = APIParameters(512, 'software', 'macSoftware', 'trackName') 63 | elif args['--album']: 64 | parms = APIParameters(600, 'music', 'album', 'collectionName') 65 | elif args['--film']: 66 | parms = APIParameters(600, 'movie', 'movie', 'trackName') 67 | elif args['--tv']: 68 | parms = APIParameters(600, 'tvShow', 'tvSeason', 'collectionName') 69 | elif args['--book']: 70 | parms = APIParameters(600, 'ebook', 'ebook', 'trackName') 71 | else: 72 | parms = APIParameters(600, '', '', '') 73 | 74 | return search_term, parms 75 | 76 | 77 | def retrieve_itunes_api_results(search_term, api_params): 78 | """ 79 | Given search terms and API parameters, call the iTunes API. 80 | Return a list of (thumb_url, img_url, name) tuples. 81 | """ 82 | # Make the iTunes search call 83 | req_params = { 84 | 'term': search_term, 85 | 'media': api_params.media, 86 | 'entity': api_params.entity, 87 | } 88 | req = requests.get(API_URL, params=req_params) 89 | 90 | # Read the results from the API 91 | raw_results = req.json()['results'] 92 | 93 | # Clean up the results 94 | Result = collections.namedtuple('Result', ['thumb_url', 'img_url', 'name']) 95 | results = [] 96 | for result in raw_results: 97 | thumb_url = result['artworkUrl100'] 98 | img_url = thumb_url.replace('100x100', '{0}x{0}'.format(api_params.size)) 99 | name = api_params.name 100 | results.append(Result(thumb_url, img_url, name.encode('utf8'))) 101 | 102 | return results 103 | 104 | 105 | def construct_html(search_term, results): 106 | """ 107 | Given a list of results, construct the HTML page. 108 | """ 109 | link_format = '{0.name}' 110 | html_links = '\n'.join([link_format.format(result) for result in results]) 111 | 112 | html_output = ( 113 | '' 114 | '{search_term} pictures' 115 | '' 116 | '

“{search_term}” pictures

' 117 | '{html_links}' 118 | '' 119 | '' 120 | ).format(search_term=search_term, html_links=html_links) 121 | 122 | return html_output 123 | 124 | 125 | def main(): 126 | import sys 127 | 128 | # This is some special casing to allow the script to be called 129 | # by Alfred. Since Alfred can only pass through a single query 130 | # string, and can't break it up, we do it for Alfred. If the first 131 | # term of the search string is a filter argument, then use that -- 132 | # otherwise just drop the --alfred flag. 133 | if (len(sys.argv) >= 2) and (sys.argv[1] == '--alfred'): 134 | try: 135 | media_type, search_term = sys.argv[2].split(' ', 1) 136 | except ValueError: 137 | media_type, search_term = None, sys.argv[2] 138 | 139 | if media_type in ('ios', 'mac', 'album', 'film', 'tv', 'book', 'narration'): 140 | sys.argv = [sys.argv[0], '--{0}'.format(media_type), search_term] 141 | else: 142 | sys.argv = [sys.argv[0], sys.argv[2]] 143 | 144 | # Parse the command-line arguments 145 | search_term, api_params = parse_command_line_args() 146 | 147 | # Given the API parameters for iTunes and the search term, 148 | # call the iTunes API and retrieve the results 149 | results = retrieve_itunes_api_results(search_term, api_params) 150 | 151 | # Render those results into an HTML string 152 | html_output = construct_html(search_term, results) 153 | 154 | # Write that string to an HTML file, and open it in the browser 155 | _, html_file = tempfile.mkstemp(suffix='.html') 156 | with open(html_file, 'w') as outfile: 157 | outfile.write(html_output) 158 | 159 | subprocess.check_call(['open', '-b', BROWSER, html_file]) 160 | --------------------------------------------------------------------------------