How To: Automatically download the latest and greatest wallpapers from Reddit

How To: Automatically download the latest and greatest wallpapers from Reddit
Photo by Mohammad Kazemi / Unsplash

Intro

Do you set wallpapers for your PC? I do! I like having pretty art on my desktop. I have a folder of wallpaper images that I have collected over the years.

I set my OS to pick a random image from the folder at one-hour intervals. I like the surprise factor when I see a different wallpaper show up on the desktop every now and then.

About twice a year, I set out on a journey to the internet to find new wallpapers for my PC. In particular, I find that Reddit is a great source for wallpaper images. They have subreddits where people post art and images in large resolutions, suited for PC wallpapers. I would go in there, browse through the top posts and find pretty images that I want displayed on my desktop.

However, I have a fickle personality and I tend to get bored with things very quickly. Same goes for wallpapers. When I set a new image for my desktop, I get the "new wallpaper feeling" (much like the "new car feeling" or the "new phone feeling") for about 20 minutes, but the novelty quickly wears off.

Having said that, I also can't be bothered to search for wallpapers all the time. I mean, I do get bored of having the same wallpaper, and I like collecting new ones for my PC. But, me being satiated over the same set of wallpapers is usually not big enough of a problem to get my lazy ass into gear. It takes about 6 months for my boredom to "build up" before I finally decide on visiting r/wallpaper and downloading a new set of wallpapers.

Therefore, as any typical developer would do, I decided to automate the image hunting process!

How it works

The Reddit API

Reddit provides a very powerful data API that you can use to fetch content programmatically. I found that the methods inside the API were very intuitive and similar to the Reddit browsing experience. You can fetch subreddits, sort the posts inside the subreddit by top posts/hot posts/most recent, and you can even set time filters for weekly/monthly/all time.

You can also make PUT/POST requests to write data to Reddit, like creating a submission, writing a comment or upvoting on a submission. We will only use GET methods for this article, but have a look at the documentation on POST methods if you are interested.

PRAW

When you use an API programmatically, you want to use a wrapper to simplify the interactions and streamline the data fetching process. There are many Reddit API wrappers for different programming languages. In this article I will be using Python, so the wrapper of choice is PRAW.

The steps for using PRAW is simple. First, you create credentials for authenticating into the Reddit service. This can be done by visiting https://www.reddit.com/prefs/apps and creating a new app for yourself. Simply click "Create App" and fill in the following information:

  • name: A name for your app
  • App type: Choose the script option
  • description: You can leave this blank
  • about url: You can leave this blank
  • redirect url: http://www.example.com/unused/redirect/uri (We won't be using this as a redirect)
Example Reddit app configuration

Once you create the app, copy down the Client ID and Secret for your application. We will use this later to authenticate to the Reddit API.

Second, you create a USER_AGENT variable in Python to declare who you are. To use the Reddit API, you need a unique USER_AGENT value so that Reddit can authenticate you. The format for this value is <platform>:<app ID>:<version string> (by u/<Reddit username>). For my example, it will be linux:com.example.wallpaper-fetch:v1.0 (by u/<my username>) .

Third, create a praw.Reddit instance in Python. Here you pass in the Client ID and Secret from step 1, and the USER_AGENT variable from step 2.

USER_AGENT = (
    f"linux:com.example.wallpaper-fetch:v1.0 (by u/{os.getenv('REDDIT_USERNAME')})"
)

reddit = praw.Reddit(
    client_id=os.getenv("CLIENT_ID"),
    client_secret=os.getenv("CLIENT_SECRET"),
    user_agent=USER_AGENT,
)

With the praw.Reddit instance created, you can go ahead and browse subreddits! To do this, use the subreddit method. You can also set time filters by using the hot() or top() methods inside the Subreddit class.

# Get the r/wallpaper subreddit
wp_subreddit = reddit.subreddit("wallpaper")

# Get the hottest posts in the sub
hot_posts = wp_subreddit.hot()

# Get the top posts for the week
weekly_top_posts = wp_subreddit.top(time_filter="week")

Downloading the image

Now that we have access to the r/wallpaper subreddit, the next step is to download content from it. The subreddit.top() method returns a list of submission items. These items contain the information required for us to fetch wallpapers from the submission.

Lets take a step back and browse the r/wallpaper subreddit. You will see that all the posts contain only images. There are no text in any of the posts. This is because the subreddit is moderated - the subreddit rules specifically states:

Submit direct links to images or imgur/reddit albums only

This is handy for us programmers, since we won't have to worry about edge cases of poorly formatted posts. All we have to do is look for the url attribute in the submission items to get the link to the images:

for submission in reddit_instance.subreddit(subreddit).top(time_filter="week"):
    # Get the image URL from the reddit post
    url = submission.url

    # Download the image
    r = requests.get(url)

    # Write the image to a file
    with open("image.png", "wb") as f:
        f.write(r.content)

That's it! Now we can find the top submissions in r/wallpaper and download images from it. Now lets add some checks to make the script more robust.

Checking the Image

The first thing we have to consider is image extension. Image files can come in different formats (.png, .jpg, .jpeg, etc) and we have to distinguish them before saving. To do this, we will extract the image extension from the URL of the image file. We will also use the submission title as the image name while we're here:

# Get the image URL from the reddit post
url = submission.url

# Create the file name
file_extension = "." + url.split(".")[-1]
image_name = submission.title.replace(" ", "_")
filename = image_name + file_extension

# Download the image
r = requests.get(url)

# Write the image to a file
with open(filename, "wb") as f:
    f.write(r.content)

Another thing I want to check is the image resolution. I have two monitors: one is 3440x1440 and another is 1920x1080. Thankfully, the r/wallpaper subreddit is moderated so nicely that it requires all posts to have the resolution of the image stated in the post title.

Title must include the image resolution (width x height surrounded by brackets) AND must include a description of the image

God bless content moderation! All I have to do is check if my desired resolution is in the submission title:

resolution = "1920x1080"
for submission in reddit_instance.subreddit(subreddit).top(time_filter="week"):
    if resolution in submission.title:
        # Get the image URL from the reddit post
        url = submission.url
        
        # Create the file name
        file_extension = "." + url.split(".")[-1]
        image_name = submission.title.replace(" ", "_")
        filename = image_name + file_extension
        
        # Download the image
        r = requests.get(url)
        
        # Write the image to a file
        with open(filename, "wb") as f:
            f.write(r.content)

HOWEVER, there is the edge case that an image is declared to be a certain resolution, but the actual file is not. To deal with this, I will use the Python Imaging Library pillow to check the resolution of the image before downloading the file:

from PIL import Image

def check_image_resolution(image_data, expected_resolution):
    try:
        with Image.open(BytesIO(image_data)) as img:
            width, height = img.size
            return f"{width}x{height}" == expected_resolution
    except Exception as e:
        return False

resolution = "1920x1080"
for submission in reddit_instance.subreddit(subreddit).top(time_filter="week"):
    if resolution in submission.title:
        # Get the image URL from the reddit post
        url = submission.url
        
        # Create the file name
        file_extension = "." + url.split(".")[-1]
        image_name = submission.title.replace(" ", "_")
        filename = image_name + file_extension
        
        # Download the image
        r = requests.get(url)
        
        # Check if the image resolution matches the expected resolution
        if check_image_resolution(r.content, resolution):
            with open(image_save_location + filename, "wb") as f:
                f.write(r.content)
            break
        else:
            continue

Running it Periodically

As I mentioned at the beginning, I want to run this script periodically to get the latest and greatest images. To do this, I set my script to run weekly using cron.

crontab -e

# Add the following line to your crontab file
0 8 * * 0 /path/to/your/script/fetch_reddit_wallpapers/run.py > /path/to/your/script/fetch_reddit_wallpapers/log.txt 2>&1

I set my script to run on 8AM on Sunday. You can change when the script runs and how often by editing the first five characters of the cron expression. For more information, visit https://crontab.guru/#0_8_*_*_0.

The code

I have published my code at https://github.com/rolzy/fetch-reddit-wallpaper. I have made some changes so that it is more configurable for different users, such as:

  • Setting the image source subreddit, image save location and target resolution using a configuration file
  • Removing old images from the image save location once the number of images in the folder exceeds a certain amount (by default I retain 50 images)

Hope you find the article helpful! Happy image-collecting!

Read more

今年読んだ本を振り返る 2024

今年読んだ本を振り返る 2024

早いもので、今年も年の瀬。皆さん、年末いかがお過ごしでしょうか。 いきなりですが、読書歴って人の性格とマイブームを如実に写し出すものだと思うんですね。というわけで、年末というキリのいい時期に、今年読んだ本を振り返って見ようと思います。 「ああ、年初はこんなこと考えてたなぁ」 「こんな本読んでたっけ!?」 思い出にふける自分用の記事になってます。 「最強!」のニーチェ入門 幸福になる哲学 (河出文庫) 去年末、國分 功一郎先生の「暇と退屈の倫理学」を読んでから哲学にハマってました。その流れを汲んで2024年一発目の本は哲学書。「暇と退屈の倫理学」の中でニーチェに触れ、彼がカッコいいと思いニーチェ入門書を購入…みたいな理由だったと思います。 数年前、飲茶先生の著書「史上最強の哲学入門」を読み、"とにかく分かりやすいなー!"と思ったのは覚えてます。哲学の入門書といえば飲茶先生です。 この「ニーチェ入門」も、著者と一般人の対話形式なので凄く分かりやすいです。哲学書によくある難しい言い回しはないですし、ページ数も少ないので一般人でも読みやすい本になってます。 入門書なんですが、

By Roland Thompson
Keychron Q11のすゝめ

Keychron Q11のすゝめ

年がら年中パソコンに張り付いてるネット民の同志の皆様、ごきげんよう。快適なパソコンライフ送ってますか? 私はと言うと、実はとても調子がいいのです! え?なぜかって?実はこいつのおかげでしてね・・・ Keychron Q11!! このスパッと2つに割れたキーボードを使い始めてから体の調子がすこぶる良くて、 * 長年悩まされた肩痛が治った * 姿勢が良くなった * 彼女ができた * 宝くじがあたった * この前、初めてホームランを打ったの! と、いいこと三昧なんですね。 そこで今回はこのKeychron Q11を皆さんに宣伝紹介したいと思います。 私とキーボード 我々の生活にスマホが普及して久しいですが、パソコンもまだまだ現役。読者の皆様は常日頃からパソコンを使う方が多いと思います。 総務省の情報通信白書によると、パソコンの世帯保有率は70%、インターネット利用率は50%前後を推移しています。まだまだ高い水準ですね。 かく言う私もパソコンがないと生きていけない生活を送ってます。 * まず、仕事がエンジニアなので、パソコンがないと飯が食えない * 続

By Roland Thompson