225 lines
7.4 KiB
Python
Executable file
225 lines
7.4 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Usage: python flickr-wallpaper.py CONFIG_PATH
|
|
|
|
ARGUMENTS:
|
|
-c or --config : path to config file ('./config')
|
|
|
|
Config File must have the following format:
|
|
groups = Comma-separated list of Flickr Group IDs (ex. 796617@N22)
|
|
image_root = PATH to where downloaded files should go (ex. ~/wallpapers)
|
|
api_key = API KEY from Flickr
|
|
|
|
"""
|
|
|
|
import sys
|
|
import urllib.request, urllib.parse, urllib.error
|
|
import hashlib
|
|
import os
|
|
import string
|
|
from urllib.parse import urlencode
|
|
from urllib.request import urlopen
|
|
from xml.dom import minidom
|
|
from pprint import pprint
|
|
from configobj import ConfigObj
|
|
from time import time
|
|
from datetime import datetime
|
|
|
|
HOST = 'http://flickr.com'
|
|
API = '/services/rest'
|
|
AUTH = False
|
|
debug = False
|
|
|
|
CONFIG_FILENAME = ''
|
|
API_KEY = ''
|
|
|
|
def main(*argv):
|
|
from getopt import getopt, GetoptError
|
|
|
|
global API_KEY
|
|
global CONFIG_FILENAME
|
|
|
|
try:
|
|
(opts, args) = getopt(argv[1:],\
|
|
'c:',\
|
|
['config'])
|
|
except GetoptError as e:
|
|
print(e)
|
|
print(__doc__)
|
|
return 1
|
|
|
|
for o, a in opts:
|
|
if o in ('-c' , '--config'):
|
|
CONFIG_FILENAME = a
|
|
else:
|
|
print("Unknown argument: %s" % o)
|
|
print(__doc__)
|
|
return 1
|
|
|
|
if (CONFIG_FILENAME == ''):
|
|
CONFIG_FILENAME = '~/.flickr-wallpaper-config'
|
|
|
|
CONFIG_FILENAME = os.path.expandvars(CONFIG_FILENAME)
|
|
CONFIG_FILENAME = os.path.expanduser(CONFIG_FILENAME)
|
|
print('Using Config file: %s' % CONFIG_FILENAME)
|
|
|
|
API_KEY = get_api_key()
|
|
if (API_KEY == ''):
|
|
print("Missing API KEY, must have a line in config file for Flickr API Key, ex. api_key = dddddddd")
|
|
print(__doc__)
|
|
return 1
|
|
|
|
image_root = get_image_root()
|
|
if (image_root == ''):
|
|
print("Missing image_root, must have a line in config file for image root, ex. image_root = /path/to/download")
|
|
print(__doc__)
|
|
return 1
|
|
|
|
if not os.path.exists(image_root):
|
|
print('Image root path not found, creating path: ' + image_root)
|
|
os.makedirs(image_root)
|
|
|
|
for groupid in get_groups():
|
|
min_upload_date = get_min_upload_date(groupid)
|
|
download_photos_from_group(image_root, groupid, min_upload_date)
|
|
set_min_upload_date(groupid);
|
|
|
|
def photos_search(user_id='', auth=False, tags='', tag_mode='', text='',\
|
|
min_upload_date='', max_upload_date='',\
|
|
min_taken_date='', max_taken_date='', \
|
|
license='', per_page='', page='', sort='',\
|
|
safe_search='', content_type='',
|
|
extras='', group_id=''):
|
|
"""Returns a list of Photo objects.
|
|
|
|
If auth=True then will auth the user. Can see private etc
|
|
"""
|
|
method = 'flickr.photos.search'
|
|
|
|
data = _doget(method, auth=auth, user_id=user_id, tags=tags, text=text,\
|
|
min_upload_date=min_upload_date,\
|
|
max_upload_date=max_upload_date, \
|
|
min_taken_date=min_taken_date, \
|
|
max_taken_date=max_taken_date, \
|
|
license=license, per_page=per_page,\
|
|
page=page, sort=sort, safe_search=safe_search, \
|
|
content_type=content_type, \
|
|
extras=extras, group_id=group_id, \
|
|
tag_mode=tag_mode)
|
|
|
|
return data
|
|
|
|
def _doget(method, auth=False, **params):
|
|
#uncomment to check you aren't killing the flickr server
|
|
#print "***** do get %s" % method
|
|
|
|
params = _prepare_params(params)
|
|
url = '%s%s/?api_key=%s&method=%s&%s%s'% \
|
|
(HOST, API, API_KEY, method, urlencode(params),
|
|
_get_auth_url_suffix(method, auth, params))
|
|
|
|
#another useful debug print statement
|
|
if (debug): print("_doget", url)
|
|
|
|
return minidom.parse(urlopen(url))
|
|
|
|
def _prepare_params(params):
|
|
"""Convert lists to strings with ',' between items."""
|
|
for (key, value) in list(params.items()):
|
|
if isinstance(value, list):
|
|
params[key] = ','.join([item for item in value])
|
|
return params
|
|
|
|
def _get_auth_url_suffix(method, auth, params):
|
|
"""Figure out whether we want to authorize, and if so, construct a suitable
|
|
URL suffix to pass to the Flickr API."""
|
|
authentication = False
|
|
|
|
# auth may be passed in via the API, AUTH may be set globally (in the same
|
|
# manner as API_KEY, etc). We do a few more checks than may seem necessary
|
|
# because we allow the 'auth' parameter to actually contain the
|
|
# authentication token, not just True/False.
|
|
if auth or AUTH:
|
|
token = userToken()
|
|
authentication = True;
|
|
elif auth != False:
|
|
token = auth;
|
|
authentication = True;
|
|
elif AUTH != False:
|
|
token = AUTH;
|
|
authentication = True;
|
|
|
|
# If we're not authenticating, no suffix is required.
|
|
if not authentication:
|
|
return ''
|
|
|
|
full_params = params
|
|
full_params['method'] = method
|
|
|
|
return '&auth_token=%s&api_sig=%s' % (token, _get_api_sig(full_params) )
|
|
|
|
def build_photo(photo):
|
|
url = photo.getAttribute('url_o')
|
|
height = photo.getAttribute('height_o')
|
|
width = photo.getAttribute('width_o')
|
|
title = photo.getAttribute('title')
|
|
id = photo.getAttribute('id')
|
|
return (url, height, width, title, id)
|
|
|
|
def get_min_upload_date(groupid):
|
|
"""Figure out the last time this script was run, so we only get recent pictures"""
|
|
if (os.path.exists(CONFIG_FILENAME)):
|
|
config = ConfigObj(CONFIG_FILENAME)
|
|
try:
|
|
return config[groupid]['min_upload_date']
|
|
except KeyError:
|
|
return '0'
|
|
else:
|
|
return '0'
|
|
|
|
def set_min_upload_date(groupid):
|
|
config = ConfigObj(CONFIG_FILENAME)
|
|
config[groupid] = {}
|
|
config[groupid]['min_upload_date'] = time()
|
|
config.write()
|
|
|
|
def get_groups():
|
|
config = ConfigObj(CONFIG_FILENAME)
|
|
return config['groups']
|
|
|
|
def sanitize_filename(filename):
|
|
import unicodedata
|
|
validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits)
|
|
cleanedFilename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore')
|
|
return ''.join(c for c in cleanedFilename if c in validFilenameChars)
|
|
|
|
def download_photos_from_group(image_root, groupid, min_upload_date):
|
|
print('Finding pictures for group: ' + groupid + ' since: ' + datetime.fromtimestamp(float(min_upload_date)).strftime("%Y-%m-%d %H:%M:%S"))
|
|
photos = photos_search(group_id=groupid, min_upload_date=min_upload_date, extras='url_o, original_format')
|
|
|
|
for photo in photos.getElementsByTagName('photo'):
|
|
(url, height, width, title, id) = build_photo(photo)
|
|
if (url != '' and height != '' and int(height) > 900):
|
|
try:
|
|
image = urlopen(url)
|
|
picture = image.read()
|
|
filename = os.path.join(image_root, id + '-' + sanitize_filename(string.replace(title, ' ', '-')) + url[-4:])
|
|
if not os.path.exists(filename):
|
|
fout = open(filename, 'wb')
|
|
fout.write(picture)
|
|
fout.close()
|
|
print('Saved ' + filename)
|
|
else:
|
|
print('Skipped ' + filename)
|
|
except IOError:
|
|
print('Error on url: ' + url + ', skipping')
|
|
|
|
def get_image_root():
|
|
config = ConfigObj(CONFIG_FILENAME)
|
|
return config['image_root']
|
|
|
|
def get_api_key():
|
|
config = ConfigObj(CONFIG_FILENAME)
|
|
return config['api_key']
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main(*sys.argv))
|