From b684c293b0ed284764b30ec65f686b0d290d4a65 Mon Sep 17 00:00:00 2001 From: Remy Moll <me@moll.re> Date: Tue, 14 Nov 2023 17:06:31 +0100 Subject: [PATCH] Refactor get process to add multiple sources later on --- deploy/deploy.playbook.yml | 7 +- deploy/templates/eink-show.timer.j2 | 2 +- image_get.py | 94 ------------------------ src/get/combined.py | 0 src/get/immich.py | 57 ++++++++++++++ src/get/public.py | 24 ++++++ src/get/template.py | 67 +++++++++++++++++ image_convert.py => src/image_convert.py | 0 image_show.py => src/image_show.py | 0 main.py => src/main.py | 18 ++--- 10 files changed, 162 insertions(+), 107 deletions(-) delete mode 100644 image_get.py create mode 100644 src/get/combined.py create mode 100644 src/get/immich.py create mode 100644 src/get/public.py create mode 100644 src/get/template.py rename image_convert.py => src/image_convert.py (100%) rename image_show.py => src/image_show.py (100%) rename main.py => src/main.py (59%) diff --git a/deploy/deploy.playbook.yml b/deploy/deploy.playbook.yml index bba8d1c..f6796f3 100644 --- a/deploy/deploy.playbook.yml +++ b/deploy/deploy.playbook.yml @@ -4,14 +4,15 @@ # become: true vars: - code_dest: /home/remy/eink + repo_dest: /home/remy/eink + code_dest: /home/remy/eink/src service_target_dir: /etc/systemd/system/ tasks: - name: Pull the latest version of the code git: repo: https://git.kluster.moll.re/remoll/rpi-eink-picture-frame - dest: "{{ code_dest }}" + dest: "{{ repo_dest }}" version: main - name: Install pillow dependencies @@ -25,7 +26,7 @@ - name: Install from the pipenv-file command: "pipenv install --system --deploy --categories=\"packages prod-packages\"" args: - chdir: "{{ code_dest }}" + chdir: "{{ repo_dest }}" - name: Copy keys python file copy: diff --git a/deploy/templates/eink-show.timer.j2 b/deploy/templates/eink-show.timer.j2 index 26be874..fa6722f 100644 --- a/deploy/templates/eink-show.timer.j2 +++ b/deploy/templates/eink-show.timer.j2 @@ -2,7 +2,7 @@ Description=Run photo update regularly [Timer] -OnCalendar=08:00 +OnCalendar=7:30 OnCalendar=16:00 Persistent=true diff --git a/image_get.py b/image_get.py deleted file mode 100644 index f8b2400..0000000 --- a/image_get.py +++ /dev/null @@ -1,94 +0,0 @@ -import httpx as h -import json -import random -from pathlib import Path -import uuid -import keys - - -class ImageGetException(Exception): - pass - - -class ImageGetter: - headers = { - "x-api-key": keys.immich_api_key - } - cached_num = 10 - cached_path = Path("./.image-cache/") - - - def get_random_image(self) -> bytearray: - try: - id = self.get_random_image_ids()[0] - bytes = self.get_image_file(id) - self.save_cached_files() - except (h.ConnectError, h.HTTPStatusError, h.NetworkError, h.RequestError, h.DecodingError, h.TransportError, ImageGetException): - print("Loading image from cache") - bytes = self.load_cached_file() - - return bytes - - - def get_random_image_ids(self, num=1) -> str: - url = keys.immich_api_root_url + "album/" + keys.immich_album_id - headers = self.headers | {"Accept": "application/json"} - - response = h.request("GET", url, headers=headers, data={}) - # raises an htppx exception if anything goes wrong - if response.status_code == 200: - response = json.loads(response.text) - else: - raise ImageGetException("Error in step get_random_image_id: " + str(response.status_code)) - - images = response['assets'] - - print(f"Picking {num} random id(s) out of {len(images)} album images") - ids = [] - for i in range(num): - image = random.choice(images) - print(f"Image considered: {image['exifInfo']}") - ids.append(image["id"]) - - return ids - - - def get_image_file(self, image_id: str) -> bytearray: - url = keys.immich_api_root_url + "asset/download/" + image_id - - headers = self.headers | {"Accept": "application/octet-stream"} - response = h.request("POST", url, headers=headers, data={}) - if not response.status_code == 200: - raise ImageGetException("Error in step get_image_file: " + str(response.status_code)) - - return response.content - - - def save_cached_files(self) -> None: - """Ensures self.cached_num files are at self.cached_path at any time""" - if not self.cached_path.exists(): - self.cached_path.mkdir() - - present_count = len(list(self.cached_path.glob("*"))) - missing = self.cached_num - present_count - if missing == 0: - return - - ids = self.get_random_image_ids(missing) - for i, id in enumerate(ids): - print(f"Caching image {i + 1}") - new_cache = self.cached_path / f"{uuid.uuid4()}" - new_cache.write_bytes(self.get_image_file(id)) - - - def load_cached_file(self) -> bytearray: - """Returns a random file from self.cached_path""" - files = list(self.cached_path.glob("*")) - - if len(files) == 0: - raise ImageGetException("Could not load cached file: directory empty") - - file = random.choice(files) - bytes = file.read_bytes() - file.unlink() - return bytes diff --git a/src/get/combined.py b/src/get/combined.py new file mode 100644 index 0000000..e69de29 diff --git a/src/get/immich.py b/src/get/immich.py new file mode 100644 index 0000000..2423d23 --- /dev/null +++ b/src/get/immich.py @@ -0,0 +1,57 @@ +import keys +import httpx as h +import random +import json + +from .template import ImageGet, ImageGetException + +class ImageGetImmich(ImageGet): + + def __init__(self) -> None: + headers = { + "x-api-key": keys.immich_api_key + } + + super().__init__( + base_url=keys.immich_api_root_url, + headers=headers + ) + + def get_random_image_ids(self, num=1) -> str: + url = keys.immich_api_root_url + "album/" + keys.immich_album_id + headers = self.headers | {"Accept": "application/json"} + + response = h.request("GET", url, headers=headers, data={}) + # raises an htppx exception if anything goes wrong + if response.status_code == 200: + response = json.loads(response.text) + else: + raise ImageGetException(f"Error in step get_random_image_id: {response.status_code}") + + images = response['assets'] + + print(f"Picking {num} random id(s) out of {len(images)} album images") + ids = [] + for i in range(num): + image = random.choice(images) + print(f"Image considered: {image['exifInfo']}") + ids.append(image["id"]) + + return ids + + + def get_image_file(self, image_id: str) -> bytearray: + url = keys.immich_api_root_url + "asset/download/" + image_id + + headers = self.headers | {"Accept": "application/octet-stream"} + response = h.request("POST", url, headers=headers, data={}) + if not response.status_code == 200: + raise ImageGetException("Error in step get_image_file: " + str(response.status_code)) + + return response.content + + + # def save_cached_files(self) -> None: + # in super + # return + diff --git a/src/get/public.py b/src/get/public.py new file mode 100644 index 0000000..e679414 --- /dev/null +++ b/src/get/public.py @@ -0,0 +1,24 @@ +import keys +import httpx as h +import random +import json + +from image_get import ImageGet, ImageGetException + +class ImageGetImmich(ImageGet): + + def __init__(self) -> None: + headers = { + "AIC-User-Agent": "Eink Art Display (remoll@ethz.ch)" + } + + super().__init__( + base_url=keys.immich_api_root_url, + headers=self.headers + ) + + def get_random_image_ids(self) -> list[str]: + raise NotImplementedError + + def get_image_file(self, image_id: str) -> bytearray: + raise NotImplementedError diff --git a/src/get/template.py b/src/get/template.py new file mode 100644 index 0000000..63f3214 --- /dev/null +++ b/src/get/template.py @@ -0,0 +1,67 @@ +import httpx as h +import random +from pathlib import Path +import uuid + + +class ImageGetException(Exception): + pass + + +class ImageGet: + def __init__(self, base_url, headers, cache_num = 10) -> None: + self.base_url = base_url + self.headers = headers + self.cache_num = cache_num + self.cache_dir = Path("../.cache/{self.__class__.__name__}/") + self.cache_dir.mkdir(parents=True, exist_ok=True) + + + # Main entrypoint, called externally + def get_random_image(self) -> bytearray: + try: + id = self.get_random_image_ids()[0] + bytes = self.get_image_file(id) + # self.save_cached_files() + except (h.ConnectError, h.HTTPStatusError, h.NetworkError, h.RequestError, h.DecodingError, h.TransportError, ImageGetException): + print("Loading image from cache") + bytes = self.load_cached_file() + + return bytes + + + def save_cached_files(self) -> None: + """Ensures self.cache_num files are at self.cache_dir at any time""" + + present_count = len(list(self.cached_path.glob("*"))) + missing = self.cached_num - present_count + if missing == 0: + return + + ids = self.get_random_image_ids(missing) + for i, id in enumerate(ids): + print(f"Caching image {i + 1}") + new_cache = self.cached_path / f"{uuid.uuid4()}" + new_cache.write_bytes(self.get_image_file(id)) + + + def load_cached_file(self) -> bytearray: + """Returns a random file from self.cached_path""" + files = list(self.cached_path.glob("*")) + + if len(files) == 0: + raise ImageGetException("Could not load cached file: directory empty") + + file = random.choice(files) + bytes = file.read_bytes() + file.unlink() + return bytes + + + + def get_random_image_ids(self) -> list[str]: + raise NotImplementedError + + + def get_image_file(self, image_id: str) -> bytearray: + raise NotImplementedError diff --git a/image_convert.py b/src/image_convert.py similarity index 100% rename from image_convert.py rename to src/image_convert.py diff --git a/image_show.py b/src/image_show.py similarity index 100% rename from image_show.py rename to src/image_show.py diff --git a/main.py b/src/main.py similarity index 59% rename from main.py rename to src/main.py index b73347c..429ef89 100644 --- a/main.py +++ b/src/main.py @@ -1,12 +1,12 @@ import sys from image_convert import ImageShrink -from image_get import ImageGetter +from get import immich from image_show import ImageShow if len(sys.argv) == 2 and sys.argv[1] == "test": print("Running test") - show = ImageShow() - show.draw_sample_image() + shower = ImageShow() + shower.draw_sample_image() sys.exit() @@ -18,10 +18,10 @@ if "noreduce" in sys.argv: print("Disabling color reduction") shrink_kwargs["colors"] = -1 -get = ImageGetter() -convert = ImageShrink(**shrink_kwargs) -show = ImageShow() +getter = immich.ImageGetImmich() +converter = ImageShrink(**shrink_kwargs) +shower = ImageShow() -image = get.get_random_image() -image = convert.convert(image) -show.show_image(image) +image = getter.get_random_image() +image = converter.convert(image) +shower.show_image(image)