├── LICENSE ├── README.md ├── getWalls.py └── screenshot.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 mrsorensen 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.md: -------------------------------------------------------------------------------- 1 | # reddit-wallpaper-downloader 2 | Downloads HD wallpapers from whichever subreddit you want 3 | 4 | ![Screenshot](https://github.com/mrsorensen/reddit-wallpaper-downloader/blob/master/screenshot.png "Screenshot") 5 | 6 | 7 | ## How to configure 8 | Edit config in getWalls.py 9 | ``` 10 | # Where to store downloaded images 11 | directory = '~/Pictures/Wallpapers/Reddit' 12 | # Which subreddit to download from 13 | subreddit = 'wallpapers' 14 | # Minimum width of image 15 | min_width = 1920 16 | # Minimum height of image 17 | min_height = 1080 18 | # How many posts to loop through (max 100) 19 | maxDownloads = 50 20 | ``` 21 | ## How to run 22 | You can run: 23 | ``` 24 | python ~/reddit-wallpaper-downloader/getWalls.py 25 | ``` 26 | or, to overwrite which subreddit you want to download from, run: 27 | ``` 28 | python ~/reddit-wallpaper-downloader/getWalls.py earthporn 29 | ``` 30 | -------------------------------------------------------------------------------- /getWalls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # --------------------- 4 | # SCRIPT INFO --------- 5 | # --------------------- 6 | # This script downloads X amount of images from a 7 | # selected subreddit. The subreddit can be specified 8 | # in the user config section of this srcipt or as 9 | # a parameter in the script. 10 | # 11 | # Run example: python getWalls.py earthporn 12 | 13 | 14 | 15 | 16 | # --------------------- 17 | # USER CONFIG --------- 18 | # --------------------- 19 | 20 | # Where to store downloaded images 21 | directory = '~/Pictures/Wallpapers/Reddit/' 22 | # Which subreddit to download from 23 | subreddit = 'wallpapers' 24 | # Minimum width of image 25 | min_width = 1920 26 | # Minimum height of image 27 | min_height = 1080 28 | # How many posts to get for each request (Max 100) 29 | jsonLimit = 100 30 | # Increase this number if the number above (jsonLimit) isn't enough posts 31 | loops = 5 32 | 33 | 34 | 35 | 36 | 37 | # --------------------- 38 | # IMPORTS ------------- 39 | # --------------------- 40 | import os 41 | from os.path import expanduser 42 | import sys 43 | import requests 44 | import urllib 45 | from PIL import ImageFile 46 | 47 | 48 | # --------------------- 49 | # FUNCTIONS ----------- 50 | # --------------------- 51 | 52 | # Returns false on status code error 53 | def validURL(URL): 54 | statusCode = requests.get(URL, headers = {'User-agent':'getWallpapers'}).status_code 55 | if statusCode == 404: 56 | return False 57 | else: return True 58 | 59 | # Creates download directory if needed 60 | def prepareDirectory(directory): 61 | if not os.path.exists(directory): 62 | os.makedirs(directory) 63 | print('Created directory {}'.format(directory)) 64 | 65 | # Returns false if subreddit doesn't exist 66 | def verifySubreddit(subreddit): 67 | URL = 'https://reddit.com/r/{}.json'.format(subreddit) 68 | result= requests.get(URL, headers = {'User-agent':'getWallpapers'}).json() 69 | try: 70 | result['error'] 71 | return False 72 | except: 73 | return True 74 | 75 | # Returns list of posts from subreddit as json 76 | def getPosts(subreddit, loops, after): 77 | allPosts = [] 78 | 79 | i = 0 80 | while i < loops: 81 | URL = 'https://reddit.com/r/{}/top/.json?t=all&limit={}&after={}'.format(subreddit, jsonLimit, after) 82 | posts = requests.get(URL, headers = {'User-agent':'getWallpapers'}).json() 83 | # allPosts.append(posts['data']['children']) 84 | for post in posts['data']['children']: 85 | allPosts.append(post) 86 | after = posts['data']['after'] 87 | i += 1 88 | 89 | return allPosts 90 | 91 | # Returns false if URL is not an image 92 | def isImg(URL): 93 | if URL.endswith(('.png', '.jpeg', '.jpg')): 94 | return True 95 | else: return False 96 | 97 | # Returns false if image from URL is not HD (Specified by min-/max_width) 98 | def isHD(URL, min_widht, min_height): 99 | file = urllib.request.urlopen(URL) 100 | size = file.headers.get("content-length") 101 | if size: size = int(size) 102 | p = ImageFile.Parser() 103 | while 1: 104 | data = file.read(1024) 105 | if not data: 106 | break 107 | p.feed(data) 108 | if p.image: 109 | # return p.image.size 110 | if p.image.size[0] >= min_width and p.image.size[1] >= min_height: 111 | return True 112 | break 113 | else: 114 | return False 115 | break 116 | file.close() 117 | return False 118 | 119 | # Returns false if image from URL is not landscape 120 | def isLandscape(URL): 121 | file = urllib.request.urlopen(URL) 122 | size = file.headers.get("content-length") 123 | if size: size = int(size) 124 | p = ImageFile.Parser() 125 | while 1: 126 | data = file.read(1024) 127 | if not data: 128 | break 129 | p.feed(data) 130 | if p.image: 131 | # return p.image.size 132 | if p.image.size[0] >= p.image.size[1]: 133 | return True 134 | break 135 | else: 136 | return False 137 | break 138 | file.close() 139 | return False 140 | 141 | # Returns true if image from URL is already downloaded 142 | def alreadyDownloaded(URL): 143 | imgName = os.path.basename(URL) 144 | localFilePath = os.path.join(directory, imgName) 145 | if(os.path.isfile(localFilePath)): 146 | return True 147 | else: return False 148 | 149 | # Returns false if image from post/URL is not from reddit or imgur domain 150 | def knownURL(post): 151 | if post.lower().startswith('https://i.redd.it/') or post.lower().startswith('http://i.redd.it/') or post.lower().startswith('https://i.imgur.com/') or post.lower().startswith('http://i.imgur.com/'): 152 | return True 153 | else: return False 154 | 155 | # Returns true if image from post/URL is stored locally 156 | def storeImg(post): 157 | if urllib.request.urlretrieve(post, os.path.join(directory, os.path.basename(post))): 158 | return True 159 | else: return False 160 | 161 | 162 | # --------------------- 163 | # COLORS -------------- 164 | # --------------------- 165 | DARK = '\033[1;30m' 166 | RED = '\033[1;31m' 167 | GREEN = '\033[1;32m' 168 | ORANGE = '\033[1;33m' 169 | PURPLE = '\033[1;35m' 170 | NC = '\033[0m' 171 | 172 | 173 | # --------------------- 174 | # START SCRIPT -------- 175 | # --------------------- 176 | 177 | # Check if subreddit name is specified as parameter 178 | try: 179 | subreddit = sys.argv[1] 180 | except: 181 | pass 182 | 183 | # Creates directory 184 | directory = expanduser(directory) 185 | directory = os.path.join(directory, subreddit) 186 | prepareDirectory(directory) 187 | 188 | # Exits if invalid subreddit name 189 | if not verifySubreddit(subreddit): 190 | print('r/{} is not a valid subreddit'.format(subreddit)) 191 | sys.exit() 192 | 193 | # For reddit pagination (Leave empty) 194 | after = '' 195 | 196 | # Stores posts from function 197 | posts = getPosts(subreddit, loops, after) 198 | 199 | # For adding index numbers to loop 200 | index = 1 201 | 202 | # Counting amount of images downloaded 203 | downloadCount = 0 204 | 205 | # Print starting message 206 | print() 207 | print(DARK + '--------------------------------------------' + NC) 208 | print(PURPLE + 'Downloading to : ' + ORANGE + directory + NC) 209 | print(PURPLE + 'From r/ : ' + ORANGE + subreddit + NC) 210 | print(PURPLE + 'Minimum resolution : ' + ORANGE + str(min_width) + 'x' + str(min_height) + NC) 211 | print(PURPLE + 'Maximum downloads : ' + ORANGE + str(jsonLimit*loops) + NC) 212 | print(DARK + '--------------------------------------------' + NC) 213 | print() 214 | 215 | 216 | # Loops through all posts 217 | for post in posts: 218 | 219 | # Shortening variable name 220 | post = post['data']['url'] 221 | 222 | # Skip post on 404 error 223 | if not validURL(post): 224 | print(RED + '{}) 404 error'.format(index) + NC) 225 | index += 1 226 | continue 227 | 228 | # Skip unknown URLs 229 | elif not knownURL(post): 230 | print(RED + '{}) Skipping unknown URL'.format(index) + NC) 231 | index += 1 232 | continue 233 | 234 | # Skip post if not image 235 | elif not isImg(post): 236 | print(RED + '{}) No image in this post'.format(index) + NC + NC + NC + NC) 237 | index += 1 238 | continue 239 | 240 | # Skip post if not landscape 241 | elif not isLandscape(post): 242 | print(RED + '{}) Skipping portrait image'.format(index) + NC) 243 | index += 1 244 | continue 245 | 246 | # Skip post if not HD 247 | elif not isHD(post, min_width, min_height): 248 | print(RED + '{}) Skipping low resolution image'.format(index) + NC) 249 | index += 1 250 | continue 251 | 252 | # Skip already downloaded images 253 | elif alreadyDownloaded(post): 254 | print(RED + '{}) Skipping already downloaded image'.format(index) + NC) 255 | index += 1 256 | continue 257 | 258 | # All checks cleared, download image 259 | else: 260 | # Store image from post locally 261 | if storeImg(post): 262 | print(GREEN + '{}) Downloaded {}'.format(index, os.path.basename(post)) + NC) 263 | downloadCount += 1 264 | index += 1 265 | # For unexpected errors 266 | else: 267 | print(RED + 'Unexcepted error' + NC) 268 | index += 1 269 | 270 | 271 | # Print info when loop is finished 272 | print() 273 | print(ORANGE + '{}'.format(downloadCount) + PURPLE + ' images was downloaded to ' + ORANGE + '{}'.format(directory) + NC) 274 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrsorensen/reddit-wallpaper-downloader/013d075fc7c1b51e1ecbaf0908ab66cbe0db9b45/screenshot.png --------------------------------------------------------------------------------