diff --git a/.gitignore b/.gitignore index fe5066a..fa8ab65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ downloads/ .idea/ __pycache__/ -*.pyc \ No newline at end of file +*.pyc +*.pyo +config.toml \ No newline at end of file diff --git a/README.md b/README.md index b7bfac7..1154596 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,12 @@ Useful resources for more providers -- https://github.com/goodtrailer/daily-desktop/tree/main \ No newline at end of file +- https://github.com/goodtrailer/daily-desktop/tree/main + +Documentation from third party tools +- https://github.com/genzj/pybingwallpaper +- https://github.com/goodtrailer/daily-desktop/blob/main/DailyDesktop.Providers.Bing/BingProvider.cs + +Unsplash Applications +- https://unsplash.com/oauth/applications + +Wikipedia API Docs +- https://api.wikimedia.org/wiki/Getting_featured_content_from_Wikipedia_with_Python#Today's_featured_article \ No newline at end of file diff --git a/daily-wallpaper.py b/daily-wallpaper.py index edf1ec8..927df15 100644 --- a/daily-wallpaper.py +++ b/daily-wallpaper.py @@ -1,54 +1,49 @@ +import datetime import os import sys -import tomllib 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": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0" + "User-Agent": config['general']['user_agent'] }) - # Load settings from settings.toml - settings = tomllib.load(open("settings.toml", mode='b+r')) - - general_settings = settings.get("general") - - download_location = general_settings.get("location") - logging.debug(f"Download location: {download_location}") - - provider_name = general_settings.get("provider") - logging.debug(f"Provider: {provider_name}") - - provider_settings = settings.get(provider_name) + # 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.{provider_name}") - logging.debug(f"Provider: {provider}") + provider = importlib.import_module(f"providers.{config['general']['provider']}") # Create an instance of the provider - provider_obj = getattr(provider, provider_name)(provider_settings, session) - - # Get the image URL - image_url = provider_obj.get_image_url() + 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 its actually text, log it - if image.startswith(b""): - logging.error("Image is actually HTML") - logging.error(image) - sys.exit(1) - if not os.path.exists(download_location): os.mkdir(download_location) - with open(f"{download_location}/wallpaper.jpg", "wb") as file: + 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__": diff --git a/poetry.lock b/poetry.lock index 6cf8688..66cf0da 100644 --- a/poetry.lock +++ b/poetry.lock @@ -139,6 +139,23 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "python-slugify" +version = "8.0.4" +description = "A Python slugify application that also handles Unicode" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856"}, + {file = "python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8"}, +] + +[package.dependencies] +text-unidecode = ">=1.3" + +[package.extras] +unidecode = ["Unidecode (>=1.1.1)"] + [[package]] name = "requests" version = "2.32.3" @@ -160,6 +177,28 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "text-unidecode" +version = "1.3" +description = "The most basic Text::Unidecode port" +optional = false +python-versions = "*" +files = [ + {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, + {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, +] + +[[package]] +name = "tomlkit" +version = "0.13.2" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, + {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, +] + [[package]] name = "urllib3" version = "1.26.20" @@ -179,4 +218,4 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "5fe821e2ab03f0c4de8c3a81fb06c6dc74655045a0ac2d8c712ce703c8322389" +content-hash = "cbcfeef2301c00347932493e38948d8930eb3e2a1cc65f1781d86d50614ac3f6" diff --git a/providers/provider.py b/providers/_provider.py similarity index 88% rename from providers/provider.py rename to providers/_provider.py index 049b194..102ce11 100644 --- a/providers/provider.py +++ b/providers/_provider.py @@ -10,5 +10,5 @@ class Provider(ABC): self.session = session @abstractmethod - def get_image_url(self): + def get_image_info(self): pass \ No newline at end of file diff --git a/providers/bing.py b/providers/bing.py index 8b512fa..0b65d12 100644 --- a/providers/bing.py +++ b/providers/bing.py @@ -1,10 +1,7 @@ import logging -import requests -from requests import session +from providers._provider import Provider -from providers.provider import Provider - -class bing(Provider): +class Bing(Provider): name = "Bing" url = "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=en-US" idx = 0 @@ -16,16 +13,17 @@ class bing(Provider): self.country = settings.get("country") self.market = settings.get("market") - def get_image_url(self): + def get_image_info(self): query = f"https://www.bing.com/HPImageArchive.aspx?format=js&idx={self.idx}&n={self.number}&mkt={self.market}" logging.debug(f"Query: {query}") response = self.session.get(query).json() - logging.debug(f"Response: {response}") + # logging.debug(f"Response: {response}") image_url = response["images"][0]["urlbase"] - logging.debug(f"Image URL: {image_url}") + logging.debug(f"Image URL: {image_url}")\ + + title = response["images"][0]["title"] image_url = f"https://www.bing.com{image_url}_{self.size}.jpg" - return image_url - + return image_url, title diff --git a/providers/unsplash.py b/providers/unsplash.py index 5dd653b..4e15aa2 100644 --- a/providers/unsplash.py +++ b/providers/unsplash.py @@ -1,20 +1,18 @@ # Using official Unsplash API to get images # curl https://api.unsplash.com/collections/1459961/photos # But we will scrape >:) - import logging -import requests import re -from providers.provider import Provider +from providers._provider import Provider -class unsplash(Provider): +class Unsplash(Provider): name = "Unsplash" url = "https://unsplash.com/collections/1459961/photo-of-the-day-(archive)" def __init__(self, settings, session): super().__init__(settings, session) - def get_image_url(self): + def get_image_info(self): query = "https://unsplash.com/collections/1459961/photo-of-the-day-(archive)" logging.debug(f"Query: {query}") @@ -30,7 +28,8 @@ class unsplash(Provider): image_id = image_slug.split("-")[-1] logging.debug(f"Image ID: {image_id}") + title = image_slug.replace("-", " ").replace(image_id, "").strip().title() + image_url = f"https://unsplash.com/photos/{image_id}/download" logging.debug(f"Image URL: {image_url}") - return image_url - + return image_url, title diff --git a/providers/wikimedia.py b/providers/wikimedia.py index 58c0aca..dd038c8 100644 --- a/providers/wikimedia.py +++ b/providers/wikimedia.py @@ -1,20 +1,18 @@ import datetime import logging -import requests import re -from providers.provider import Provider +from providers._provider import Provider # https://api.wikimedia.org/wiki/Feed_API/Reference/Featured_content -class wikimedia(Provider): +class Wikimedia(Provider): name = "Wikimedia" url = "https://api.wikimedia.org/feed/v1/wikipedia/en/featured/" - def __init__(self, settings, session): super().__init__(settings, session) - def get_image_url(self): + def get_image_info(self): today = datetime.datetime.now() date = today.strftime('%Y/%m/%d') logging.debug(f"Date: {date}") @@ -22,11 +20,12 @@ class wikimedia(Provider): logging.debug(f"URL: {url}") response = self.session.get(url).json() - logging.debug(f"Response: {response}") + # logging.debug(f"Response: {response}") image = response['image']['image']['source'] logging.debug(f"Image: {image}") image_url = image - return image_url + title = response['image']['description']['text'] + return image_url, title diff --git a/pyproject.toml b/pyproject.toml index 5e44a55..e947a99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,8 @@ readme = "README.md" python = "^3.11" requests = "^2.32" urllib3 = "^1.26" +python-slugify = "^8.0" +tomlkit = "^0.13.2" [build-system] diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..2677a58 --- /dev/null +++ b/settings.py @@ -0,0 +1,75 @@ +import os.path +import tomlkit + +local_path = os.path.abspath("config.toml") +user_path = os.path.expanduser("~/.config/daily-wallpaper/config.toml") + +def default_settings(): + 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["location"].comment("Download location") + general.add("provider", "bing") + general["provider"].comment("Which wallpaper provider to use") + general.add("include_title", True) + general.value.item("include_title").comment("Include image title in filename") + general.add("set_wallpaper", False) + 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.value.item("log").comment("Log to file, located in the download location") + general.add("log_level", "INFO") + general["log_level"].comment("Log level, possible values: DEBUG, INFO, WARNING, ERROR, CRITICAL") + general.add("overwrite", False) + general.value.item("overwrite").comment("Overwrite existing files") + general.add("user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0") + general["user_agent"].comment("User-Agent to use for requests, change to avoid being blocked or to comply with ToS") + general.add(tomlkit.nl()) + + bing = tomlkit.table() + bing.add("size", "UHD") + bing["size"].comment('Image size, possible values: "UHD", "1920x1080"') + bing.add("country", "us") + bing["country"].comment("Country, currently unused") + bing.add("market", "en-US") + bing["market"].comment("Market, overrides country") + bing.add(tomlkit.nl()) + + unsplash = tomlkit.table() + unsplash.add("collection", 1459961) + unsplash["collection"].comment("Collection ID, which gallery to use") + unsplash.add("application_id", "") + unsplash["application_id"].comment("Unset, currently not used, as we scrape") + unsplash.add("access_key", "") + unsplash["access_key"].comment("Unset, currently not used, as we scrape") + unsplash.add("secret_key", "") + unsplash["secret_key"].comment("Unset, currently not used, as we scrape") + unsplash.add(tomlkit.nl()) + + wikimedia = tomlkit.table() + wikimedia.add("authorization", "") + wikimedia["authorization"].comment("Unset, currently not used, as Wikimedia does not require it") + + defaults = tomlkit.document() + defaults.add("general", general) + defaults.add("bing", bing) + defaults.add("unsplash", unsplash) + defaults.add("wikimedia", wikimedia) + + return defaults + +def load_settings(): + if os.path.exists(local_path): + settings = tomlkit.parse(open(local_path, mode='r').read()) + elif os.path.exists(user_path): + settings = tomlkit.parse(open(user_path, mode='b+r').read()) + else: + settings = default_settings() + os.makedirs(os.path.dirname(user_path), exist_ok=True) + with open(user_path, mode='w') as file: + tomlkit.dump(settings, file) + return settings \ No newline at end of file diff --git a/settings.toml b/settings.toml deleted file mode 100644 index c37a120..0000000 --- a/settings.toml +++ /dev/null @@ -1,23 +0,0 @@ -[general] -provider = "wikimedia" # bing, unsplash, wikimedia -location = "downloads" -daemon = false - -# Documentation from third party tools -# https://github.com/genzj/pybingwallpaper -# https://github.com/goodtrailer/daily-desktop/blob/main/DailyDesktop.Providers.Bing/BingProvider.cs -[bing] -size = "UHD" # UHD, 1920x1080, 1280x720, 640x480 -country = "us" -market = "en-US" - -# https://unsplash.com/oauth/applications -[unsplash] # If unset, will scrape -application_id = "" -access_key = "" -secret_key = "" - -# https://api.wikimedia.org/wiki/Getting_featured_content_from_Wikipedia_with_Python#Today's_featured_article -[wikimedia] -authorization = "" # Not required, but recommended -user_agent = "" # Not required, but recommended \ No newline at end of file