Vital Vialas

A python script using Reddit's API to download most upvoted wallpaper and change it

#!/usr/bin/python
# -*- coding: utf-8 -*-

import argparse
import praw
import urllib
import os
import subprocess
from bs4 import BeautifulSoup
import re
import sys

'''
The praw.Reddit connection requires these:
client_id='2ZMSO5JBG4DR5w'
client_secret='B4m8XSe2N2V1dcgRM-EY10YWAJ8'
my_user = 'reddit_user_name'
my_password = 'not.my.password'
user_agent='pc:r_wallpapers_sets_my_wallpaper:0.1 '
1 either provide them here or in a praw.ini file residing in the same directory (or  ~/.config/)
reddit = praw.Reddit(client_id=client_id,
					client_secret=client_secret,
					password=my_password,
					user_agent=user_agent,
					username=my_user)
2 or read required parameters from praw.ini under [user_data] 
[user_data]
client_id=2ZMSO5JBG4DR5w
client_secret=B4m8XSe2N2V1dcgRM-EY10YWAJ8
username=redit_user_name
password=not.my.password
user_agent=pc:r_wallpapers_sets_my_wallpaper:0.1
'''


parser = argparse.ArgumentParser()
parser.add_argument("--subreddit", required=True, 
	help="name of the subreddit to fetch image from. E.g: 'wallpapers' or 'EarthPorn'")
parser.add_argument("--top", required=True, 
	help="subreddit Top time-limit submission: 'day', 'week', 'year'")


home_dir = os.path.expanduser('~')
wallpapers_dir = os.path.join(home_dir, ".wallpapers")



def get_submission(subreddit, time, limit):

	for submission in subreddit.top(time_filter=time, limit=limit):
		title = submission.title
		url = submission.url
	return url, title




def configure_wallpaper_path(title):
	
	if not os.path.isdir(wallpapers_dir):
		os.mkdir(wallpapers_dir)
	wallpaper_path = os.path.join(wallpapers_dir, str(title)+".jpg")
	return wallpaper_path



def get_screen_dimensions(): 
	#sys.stdout.isatty() -> True if executed from console, False if executed from cron
	
	'''
	runs xrandr to get screen dimension from console call. 
	Can't get it to work when executed from cron (Is it even possible?)
	since cron is not tty-based so I can't capture stdout from xrandr call.
	Just provide an argument file with screen dimensions when executing from cron?
	'''
	screen_dim = subprocess.Popen("xrandr | grep \* | awk '/x/{print $1}'", shell=True, 
		stdout=subprocess.PIPE).stdout.read().strip("\n")
	screen_w, screen_h = int(screen_dim.split("x")[0]), int(screen_dim.split("x")[1])
	return (screen_w, screen_h)



def check_img_fits_screen(original_title, screen_dimensions):

	'''
	checks the image is more or less rectangular (width/height ratio) to be used as wallpaper
	by comparing to local screen dimensions
	Uses the original title, bc it's subreddit rules to include image resolution
	'''
	try: 
		screen_w, screen_h = screen_dimensions[0], screen_dimensions[1]
		original_title = original_title.replace(",", '').replace(".",'') 
		pattern = "[\[\(]\d+?[ ]?[*xX][ ]?\d+?[\]\)]"
		img_dimensions = re.search(pattern, original_title).group(0)
		w_by_h = re.compile("[*xX]").split(img_dimensions)
		img_w, img_h = int(w_by_h[0][1:]), int(w_by_h[1][:-1])
		ratio_img = (img_w + .0) / img_h
		ratio_screen = (screen_w + .0) / screen_h
		if (screen_w - img_w < 200) and (ratio_screen - ratio_img < 0.5):
			return True
		else:
			return False
	except AttributeError: 
		#'NoneType' object has no attribute 'group' when original title doesnt comply
		return False



def get_imgur_image_url(url):

	'''
	works either for imgur.com/a/ (album) or just a regular imgur page 
	with just one picture imgur.com/something
	in the first case just grabs url for first picture in the album, 
	otherwise grabs 'the' image url
	'''
	html = urllib.urlopen(url).read()
	soup = BeautifulSoup(html, "lxml")
	matches = soup.find_all('img', attrs={"class": "post-image-placeholder"})
	#pick first image in album:
	url = re.search("i.imgur.com/(.+?\")", str(matches[0])).group(0).strip("\"")
	return  "http://"+url



def download_wallpaper(url, path):

	'''
	first checks the url respnds OK(200), then check it's not html 
	'''
	if urllib.urlopen(url).getcode() == 200:
		if not 'html' in urllib.urlopen(url).read():
			urllib.urlretrieve(url, path)
			return urllib.urlopen(url).getcode()



def change_local_wallpaper(wallpaper_path):

	'''
	runs 
	gsettings set org.gnome.desktop.background picture-uri file://" + wallpaper_path
	or dconf write if gsettings gives the dreaded "GLib-GIO-Message: 
		Using the 'memory' GSettings backend" error message
	'''

	file = 'file://%s'%wallpaper_path
	p = subprocess.Popen(['gsettings', 'set', 'org.gnome.desktop.background', 
	'picture-uri', file], stderr=subprocess.PIPE)
	err = p.communicate()[1]
	if "GLib-GIO-Message: Using the 'memory' GSettings backend" in err:
		p = subprocess.Popen(['dconf', 'write',
			'/org/gnome/desktop/background/picture-uri', 
			"\"%s\""%file], stderr=subprocess.PIPE)
		out, err = p.communicate()





def main():

	subreddit_arg = args.subreddit #subreddit_arg = 'EarthPorn'

	if subreddit_arg.lower() not in ["wallpapers", "earthporn"]:
		sys.exit('supported subreddits: wallpapers, EarthPorn')

	time = args.top	#time = 'day'
	reddit = praw.Reddit('user_data')
	limit=1

	subreddit = reddit.subreddit(subreddit_arg)
	url, original_title = get_submission(subreddit, time, limit)

	screen_dimensions = get_screen_dimensions()

	img_fits_screen = check_img_fits_screen(original_title, screen_dimensions)
	
	while img_fits_screen == False:
		limit+=1
		url, original_title = get_submission(subreddit, time, limit)
		img_fits_screen = check_img_fits_screen(original_title, screen_dimensions)

	short_title = original_title.replace(" ", "_").strip(".")[0:30]
	 #replace non-ascii for '?'
	short_title = ''.join([i if ord(i) < 128 else '?' for i in short_title])
	
	wallpaper_path = configure_wallpaper_path(short_title)

	#i.imgur.com/ and i.redd.it/ are images, not html
	if 'imgur.com' in url and not 'i.imgur.com/' in url: 
		if 'html' in urllib.urlopen(url).read():
			url = get_imgur_image_url(url)
		
	if download_wallpaper(str(url), wallpaper_path):
		print "\nDownloaded \"" + short_title + "\" from " + str(url)

	change_local_wallpaper(wallpaper_path)
	print "Set \"" + short_title + "\" as wallpaper"



if __name__ == "__main__":

	args = parser.parse_args()

	main()