anufrievroman / waypaper

GUI wallpaper manager for Wayland and Xorg Linux systems
https://anufrievroman.gitbook.io/waypaper
GNU General Public License v3.0
399 stars 29 forks source link

Add NASA Astronomy Picture Of the Day (APOD) feature? #25

Closed tien-vo closed 11 months ago

tien-vo commented 11 months ago

One of the features I sorely miss from Nitrogen is the ability to pull wallpapers from APIs like NASA APOD. I'm currently implementing it with swww through this short python script, which can download, display, and delete wallpapers at custom intervals. I thought this project might be interested in incorporating this feature, since it's also written in Python. I'm happy to submit a PR. Let me know how you want it done.

#!/usr/bin/env python3

import os
import shutil
import subprocess
import threading
import time
from datetime import date, timedelta
from pathlib import Path
from random import choice, randrange
from tempfile import NamedTemporaryFile

import requests

URL = "https://api.nasa.gov/planetary/apod"
API_KEY = "i8VA6xi1tsQCodKcHP0MAr5ReizUNcHktwVspBdU"
QUERY_WITHIN = timedelta(days=6 * 30)
SAVE_DIR = Path(os.getenv("XDG_DATA_HOME")) / "wallpapers"
KEEP_NUMBER = 30

class RepeatedTimer(object):
    def __init__(self, interval, function, *args, **kwargs):
        self._timer = None
        self.function = function
        self.interval = interval
        self.args = args
        self.kwargs = kwargs
        self.is_running = False
        self.start()

    def _run(self):
        self.is_running = False
        self.start()
        self.function(*self.args, **self.kwargs)

    def start(self):
        if not self.is_running:
            self._timer = threading.Timer(self.interval, self._run)
            self._timer.start()
            self.is_running = True

    def stop(self):
        self._timer.cancel()
        self.is_running = False

def random_available_wallpaper():
    return choice(list(SAVE_DIR.iterdir()))

def organize_directory():
    downloaded_number = len(list(SAVE_DIR.iterdir()))
    if downloaded_number > KEEP_NUMBER:
        for _ in range(downloaded_number - KEEP_NUMBER):
            os.remove(random_available_wallpaper())

def display_random_wallpaper():
    wallpaper = random_available_wallpaper()
    subprocess.run(["swww", "img", str(wallpaper)])

def random_date():
    end = date.today()
    start = end - QUERY_WITHIN
    return start + timedelta(days=randrange(QUERY_WITHIN.days))

def request_wallpaper():
    response = requests.get(
        url=URL, params={"api_key": API_KEY, "date": random_date().isoformat()}
    )
    response.raise_for_status()
    info = response.json()
    if "hdurl" in info:
        url = info["hdurl"]
        suffix = Path(info["hdurl"]).suffix
    else:
        url = info["url"]
        suffix = Path(info["url"]).suffix

    return {"url": url, "file_name": f"APOD_{info['date']}{suffix}"}

def already_downloaded(info):
    return info["file_name"] in [file.name for file in SAVE_DIR.iterdir()]

def download(info: dict, number_of_trials: int = 3):
    with NamedTemporaryFile(mode="wb") as temp_file:
        for _ in range(number_of_trials):
            try:
                response = requests.get(url=info["url"])
                response.raise_for_status()
                temp_file.write(response.content)

                SAVE_DIR.mkdir(parents=True, exist_ok=True)
                shutil.copyfile(temp_file.name, SAVE_DIR / info["file_name"])
                break
            except (
                requests.ConnectionError,
                requests.HTTPError,
                requests.Timeout,
            ):
                time.sleep(10.0)

def download_wallpaper():
    while True:
        info = request_wallpaper()
        if not already_downloaded(info):
            download(info)
            break

# Start daemon
subprocess.run(["swww", "kill"])
subprocess.run(["swww", "init"])
time.sleep(1.0)

# Schedule wallpaper download
RepeatedTimer(3600.0, download_wallpaper)

# Schedule wallpaper rotation
RepeatedTimer(3600.0, display_random_wallpaper)

# Schedule organizer
RepeatedTimer(7200.0, organize_directory)
anufrievroman commented 11 months ago

Interesting! I didn't know you have this feature in Nitrogen. Could you describe a bit more how it works? Instead of a local folder, you input a URL and the program downloads images from there into a temporary folder?

Although I see many problems with NASA thing specifically, I like it as an ides. How I see it might work, it could be a button or a field where you input the URL, and it would load things from that URL, and display them as a virtual folder. And the upon the Refresh button it would update that virtual folder and upon Random it would choose a random image. I am not sure about the regular automatic updates, because I don't think anyone would run it continuously.

But for NASA basically, you need api key for that, right? And this works only for this particular NASA website, and there aren't many other such sources? Another problem is that most pictures there are not really wallpaper like, and sometimes it's a video.

tien-vo commented 11 months ago

Oops it seem I've made a mistake! I just looked at what I was using in my old computer before moving to a full wayland setup and the application is Variety, not Nitrogen. Variety seems to work well in wayland with swaybg, so it seems I wrote that script for nothing :)

Regardless, the behavior I like from Variety is that it has a few locations to pick wallpapers from (e.g. favorites folder, /usr/share/backgrounds, or web APIs like APOD). Users can tell the application to change wallpaper every x minutes. If the location is an API (besides APOD, Variety works with flickr, bing, earthview, natgeo, unsplash), it pulls image down to a "fetched" folder and displays it. If the user likes the image, they can move it into a "favorites" folder to be re-chosen later.

Since Variety is already working for me, feel free to close this if you don't wish to implement this feature!

P/s: Yes you'll need an API key but NASA gives it out for free. And if the request returns a video, you can just make another one until you find a new image I guess. Some people might not want the changing wallpaper but I personally like to have it change every hour on the second monitor when I work to keep track of time :) Plus random pretty space photos are refreshing haha.

anufrievroman commented 11 months ago

Ah, that's an interesting project, I didn't know about Variety. Well, yes, the work seems to be already done very well there, I guess if you wish to visually select a wallpaper from that library, you could just point waypaper to variety folder. It's actually pretty cool, maybe I'll add an option to include Variety folder by default. Let's close this issue.