Compare commits
2 Commits
19dd251165
...
303219dfb1
Author | SHA1 | Date | |
---|---|---|---|
303219dfb1 | |||
90dc4b78f1 |
77
app/daily-wallpaper.py
Normal file
77
app/daily-wallpaper.py
Normal 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()
|
@ -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):
|
@ -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
53
poetry.lock
generated
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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]
|
||||||
|
Loading…
Reference in New Issue
Block a user