Compare commits

...

2 Commits

Author SHA1 Message Date
303219dfb1 clean up a bit 2024-11-20 18:56:19 -04:00
90dc4b78f1 Add functionality for rest of settings 2024-11-20 18:22:46 -04:00
7 changed files with 178 additions and 75 deletions

77
app/daily-wallpaper.py Normal file
View File

@ -0,0 +1,77 @@
import datetime
import os
import requests
import importlib
import logging
import slugify
import settings
import time
import croniter
def main():
config = settings.load_settings()
logging.basicConfig(level=config['general']['log_level'], format="%(asctime)s [%(levelname)s] %(message)s", force=True)
logging.debug(f"Config: {config}")
chosen_providers = config['general']['provider']
# Daemon mode
if config['daemon']['daemon']:
while True:
for provider in chosen_providers:
download_with_provider(provider, config)
if config['daemon']['cron'] != "":
now = datetime.datetime.now()
cron = croniter.croniter(config['daemon']['cron'], now)
next_run = cron.get_next(datetime.datetime)
logging.info(f"Next run: {next_run}")
time.sleep((next_run - now).total_seconds())
else:
time.sleep(config['daemon']['interval'])
# Download once
else:
for provider in chosen_providers:
download_with_provider(provider, config)
def download_with_provider(provider_name, config):
session = requests.Session()
session.headers.update({
"User-Agent": config['general']['user_agent']
})
# Convenience variables, could be inlined
provider_settings = config[provider_name] if provider_name in config else None
download_location = os.path.abspath(os.path.expanduser(config['general']['location']))
# Load the provider module
provider = importlib.import_module(f"providers.{provider_name}")
# Create an instance of the provider
provider_obj = getattr(provider, provider_name.title())(provider_settings, session)
# Get the image URL and title
image_url, image_title = provider_obj.get_image_info()
logging.debug(f"Image URL: {image_url}")
# Variables for the file path
date = datetime.datetime.now().strftime("%Y-%m-%d")
image_title = slugify.slugify(image_title)
file_path = f"{download_location}/{provider_name.title()}/{date} [{image_title}].jpg"
# Check if we should include the title in the filename
if not config['general']['include_title']:
file_path = f"{download_location}/{provider_name.title()}/{date}.jpg"
# Create the download location if it doesn't exist
if not os.path.exists(download_location):
os.mkdir(download_location)
if not os.path.exists(f"{download_location}/{provider_name.title()}"):
os.mkdir(f"{download_location}/{provider_name.title()}")
# Check if the file exists and if we should overwrite it
if os.path.exists(file_path) and not config['general']['overwrite']:
return
# Download the image
image = session.get(image_url).content
with open(file_path, "wb") as file:
file.write(image)
if __name__ == "__main__":
main()

View File

@ -2,24 +2,18 @@ import os.path
import tomlkit import tomlkit
local_path = os.path.abspath("config.toml") local_path = os.path.abspath("config.toml")
user_path = os.path.expanduser("~/.config/daily-wallpaper/config.toml") user_path = os.path.expanduser("~/.config/app/config.toml")
def default_settings(): def default_settings():
general = tomlkit.table() general = tomlkit.table()
general.add("interval", 86400)
general["interval"].comment("Interval in seconds (24 hours)")
general.add("start", 7200)
general["start"].comment("Start time in seconds (2:00 AM)")
general.add("location", "~/Pictures/Wallpapers") general.add("location", "~/Pictures/Wallpapers")
general["location"].comment("Download location") general["location"].comment("Download location")
general.add("provider", "bing") general.add("provider", ["bing", "unsplash", "wikimedia"])
general["provider"].comment("Which wallpaper provider to use") general["provider"].comment("Which wallpaper provider to use, in order of preference (do no include if you don't want to download the file)")
general.add("include_title", True) general.add("include_title", True)
general.value.item("include_title").comment("Include image title in filename") general.value.item("include_title").comment("Include image title in filename")
general.add("set_wallpaper", False) general.add("set_wallpaper", False)
general.value.item("set_wallpaper").comment("Set wallpaper after download") general.value.item("set_wallpaper").comment("Set wallpaper after download")
general.add("daemon", False)
general.value.item("daemon").comment("Run as daemon (continuously in the background)")
general.add("log", False) general.add("log", False)
general.value.item("log").comment("Log to file, located in the download location") general.value.item("log").comment("Log to file, located in the download location")
general.add("log_level", "INFO") general.add("log_level", "INFO")
@ -30,6 +24,15 @@ def default_settings():
general["user_agent"].comment("User-Agent to use for requests, change to avoid being blocked or to comply with ToS") general["user_agent"].comment("User-Agent to use for requests, change to avoid being blocked or to comply with ToS")
general.add(tomlkit.nl()) general.add(tomlkit.nl())
daemon = tomlkit.table()
daemon.add("daemon", False)
daemon.value.item("daemon").comment("Run as daemon (continuously in the background)")
daemon.add("interval", 86400)
daemon["interval"].comment("Interval in seconds (24 hours)")
daemon.add("cron", "0 0 * * *")
daemon["cron"].comment("Cron expression, overrides interval")
daemon.add(tomlkit.nl())
bing = tomlkit.table() bing = tomlkit.table()
bing.add("size", "UHD") bing.add("size", "UHD")
bing["size"].comment('Image size, possible values: "UHD", "1920x1080"') bing["size"].comment('Image size, possible values: "UHD", "1920x1080"')
@ -56,6 +59,7 @@ def default_settings():
defaults = tomlkit.document() defaults = tomlkit.document()
defaults.add("general", general) defaults.add("general", general)
defaults.add("daemon", daemon)
defaults.add("bing", bing) defaults.add("bing", bing)
defaults.add("unsplash", unsplash) defaults.add("unsplash", unsplash)
defaults.add("wikimedia", wikimedia) defaults.add("wikimedia", wikimedia)
@ -63,6 +67,17 @@ def default_settings():
return defaults return defaults
def load_settings(): def load_settings():
# TODO: Find a better way to do this
if os.environ.get("RAPID_DEVELOPMENT") is not None:
if os.path.exists(user_path):
os.remove(user_path)
if os.path.exists(local_path):
os.remove(local_path)
settings = default_settings()
with open(local_path, mode='w') as file:
tomlkit.dump(settings, file)
return settings
if os.path.exists(local_path): if os.path.exists(local_path):
settings = tomlkit.parse(open(local_path, mode='r').read()) settings = tomlkit.parse(open(local_path, mode='r').read())
elif os.path.exists(user_path): elif os.path.exists(user_path):

View File

@ -1,50 +0,0 @@
import datetime
import os
import sys
import requests
import importlib
import logging
import slugify
import settings
def main():
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
config = settings.load_settings()
logging.debug(f"Config: {config}")
session = requests.Session()
session.headers.update({
"User-Agent": config['general']['user_agent']
})
# Convenience variables, could be inlined
provider_name = config['general']['provider']
provider_settings = config[config['general']['provider']]
download_location = os.path.abspath(os.path.expanduser(config['general']['location']))
# Load the provider module
provider = importlib.import_module(f"providers.{config['general']['provider']}")
# Create an instance of the provider
provider_obj = getattr(provider, provider_name.title())(provider_settings, session)
# Get the image URL and title
image_url, image_title = provider_obj.get_image_info()
logging.debug(f"Image URL: {image_url}")
# Download the image
image = session.get(image_url).content
if not os.path.exists(download_location):
os.mkdir(download_location)
if not os.path.exists(f"{download_location}/{provider_name.title()}"):
os.mkdir(f"{download_location}/{provider_name.title()}")
date = datetime.datetime.now().strftime("%Y-%m-%d")
image_title = slugify.slugify(image_title)
with open(f"{download_location}/{provider_name.title()}/{date} [{image_title}].jpg", "wb") as file:
file.write(image)
if __name__ == "__main__":
main()

53
poetry.lock generated
View File

@ -125,6 +125,21 @@ files = [
{file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"},
] ]
[[package]]
name = "croniter"
version = "5.0.1"
description = "croniter provides iteration for datetime object with cron like format"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.6"
files = [
{file = "croniter-5.0.1-py2.py3-none-any.whl", hash = "sha256:eb28439742291f6c10b181df1a5ecf421208b1fc62ef44501daec1780a0b09e9"},
{file = "croniter-5.0.1.tar.gz", hash = "sha256:7d9b1ef25b10eece48fdf29d8ac52f9b6252abff983ac614ade4f3276294019e"},
]
[package.dependencies]
python-dateutil = "*"
pytz = ">2021.1"
[[package]] [[package]]
name = "idna" name = "idna"
version = "3.10" version = "3.10"
@ -139,6 +154,20 @@ files = [
[package.extras] [package.extras]
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
]
[package.dependencies]
six = ">=1.5"
[[package]] [[package]]
name = "python-slugify" name = "python-slugify"
version = "8.0.4" version = "8.0.4"
@ -156,6 +185,17 @@ text-unidecode = ">=1.3"
[package.extras] [package.extras]
unidecode = ["Unidecode (>=1.1.1)"] unidecode = ["Unidecode (>=1.1.1)"]
[[package]]
name = "pytz"
version = "2024.2"
description = "World timezone definitions, modern and historical"
optional = false
python-versions = "*"
files = [
{file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"},
{file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"},
]
[[package]] [[package]]
name = "requests" name = "requests"
version = "2.32.3" version = "2.32.3"
@ -177,6 +217,17 @@ urllib3 = ">=1.21.1,<3"
socks = ["PySocks (>=1.5.6,!=1.5.7)"] socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
[[package]] [[package]]
name = "text-unidecode" name = "text-unidecode"
version = "1.3" version = "1.3"
@ -218,4 +269,4 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.11" python-versions = "^3.11"
content-hash = "cbcfeef2301c00347932493e38948d8930eb3e2a1cc65f1781d86d50614ac3f6" content-hash = "65c5d981b2146aee4317e6d04fc32e949094e70be5dd7a26f44c76614a92ee33"

View File

@ -13,7 +13,7 @@ class Unsplash(Provider):
super().__init__(settings, session) super().__init__(settings, session)
def get_image_info(self): def get_image_info(self):
query = "https://unsplash.com/collections/1459961/photo-of-the-day-(archive)" query = f"https://unsplash.com/collections/{self.settings['collection']}"
logging.debug(f"Query: {query}") logging.debug(f"Query: {query}")
response = self.session.get(query).text response = self.session.get(query).text

View File

@ -1,6 +1,7 @@
import datetime import datetime
import logging import logging
import re import time
from providers._provider import Provider from providers._provider import Provider
# https://api.wikimedia.org/wiki/Feed_API/Reference/Featured_content # https://api.wikimedia.org/wiki/Feed_API/Reference/Featured_content
@ -13,19 +14,24 @@ class Wikimedia(Provider):
super().__init__(settings, session) super().__init__(settings, session)
def get_image_info(self): def get_image_info(self):
today = datetime.datetime.now() # Since wikipedia API seems to fail to provide image url, we will retry on key error until we get the image url
date = today.strftime('%Y/%m/%d') try:
logging.debug(f"Date: {date}") today = datetime.datetime.now()
url = 'https://api.wikimedia.org/feed/v1/wikipedia/en/featured/' + date date = today.strftime('%Y/%m/%d')
logging.debug(f"URL: {url}") logging.debug(f"Date: {date}")
url = 'https://api.wikimedia.org/feed/v1/wikipedia/en/featured/' + date
logging.debug(f"URL: {url}")
response = self.session.get(url).json() response = self.session.get(url).json()
# logging.debug(f"Response: {response}") # logging.debug(f"Response: {response}")
image = response['image']['image']['source'] image = response['image']['image']['source']
logging.debug(f"Image: {image}") logging.debug(f"Image: {image}")
image_url = image image_url = image
title = response['image']['description']['text'] title = response['image']['description']['text']
return image_url, title
return image_url, title except KeyError:
logging.error("KeyError, retrying...")
time.sleep(10) # Wait 10 seconds
return self.get_image_info()

View File

@ -11,6 +11,10 @@ requests = "^2.32"
urllib3 = "^1.26" urllib3 = "^1.26"
python-slugify = "^8.0" python-slugify = "^8.0"
tomlkit = "^0.13.2" tomlkit = "^0.13.2"
croniter = "^5.0"
[tool.poetry.scripts]
daily-wallpaper = "app.daily_wallpaper:__main__"
[build-system] [build-system]