diff --git a/.gitignore b/.gitignore index eb1fd66..e418afb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ keys.py *.pyc -.image-cache/ +.cache/ test.png wifi_networks.yml \ No newline at end of file diff --git a/deploy/deploy.playbook.yml b/deploy/deploy.playbook.yml index f6796f3..4b18fe7 100644 --- a/deploy/deploy.playbook.yml +++ b/deploy/deploy.playbook.yml @@ -4,9 +4,12 @@ # become: true vars: + # mostly fixed repo_dest: /home/remy/eink code_dest: /home/remy/eink/src service_target_dir: /etc/systemd/system/ + # adapt to taste + own_image_percent: 90 tasks: - name: Pull the latest version of the code @@ -22,7 +25,7 @@ - zlib1g state: present become: true - + - name: Install from the pipenv-file command: "pipenv install --system --deploy --categories=\"packages prod-packages\"" args: diff --git a/deploy/templates/eink-show.service.j2 b/deploy/templates/eink-show.service.j2 index cb1d78a..1790156 100644 --- a/deploy/templates/eink-show.service.j2 +++ b/deploy/templates/eink-show.service.j2 @@ -6,6 +6,7 @@ Type=oneshot User={{ ansible_user }} WorkingDirectory={{ code_dest }} ExecStart=python main.py +Environment="OWN_IMAGE_PERCENT={{ own_image_percent }}" [Install] diff --git a/src/get/combined.py b/src/get/combined.py index e69de29..c7bce88 100644 --- a/src/get/combined.py +++ b/src/get/combined.py @@ -0,0 +1,25 @@ +from .immich import ImageGetImmich +from .unsplash import ImageGetUnsplash +from .museum import ImageGetMuseum +import random +import os + +igetter = ImageGetImmich() +ugettter = ImageGetUnsplash() +mgetter = ImageGetMuseum() + +class ImageGetCombined: + + def __init__(self) -> None: + own_image_percent = int(os.getenv("OWN_IMAGE_PERCENT", 90)) + other_image_percent = int(100 - own_image_percent) + self.getters = [igetter] * own_image_percent \ + + [ugettter] * int(other_image_percent/2) \ + + [mgetter] * int(other_image_percent/2) + # representing weighted probabilities + print(f"Using {len(self.getters)} image sources: ~{own_image_percent}% own, ~{other_image_percent}% foreign images.") + + + def get_random_image(self) -> bytearray: + getter = random.choice(self.getters) + return getter.get_random_image() \ No newline at end of file diff --git a/src/get/immich.py b/src/get/immich.py index 2423d23..dc4f2ff 100644 --- a/src/get/immich.py +++ b/src/get/immich.py @@ -17,8 +17,9 @@ class ImageGetImmich(ImageGet): headers=headers ) + def get_random_image_ids(self, num=1) -> str: - url = keys.immich_api_root_url + "album/" + keys.immich_album_id + url = self.base_url + "album/" + keys.immich_album_id headers = self.headers | {"Accept": "application/json"} response = h.request("GET", url, headers=headers, data={}) @@ -41,7 +42,7 @@ class ImageGetImmich(ImageGet): def get_image_file(self, image_id: str) -> bytearray: - url = keys.immich_api_root_url + "asset/download/" + image_id + url = self.base_url + "asset/download/" + image_id headers = self.headers | {"Accept": "application/octet-stream"} response = h.request("POST", url, headers=headers, data={}) @@ -49,9 +50,3 @@ class ImageGetImmich(ImageGet): 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/museum.py b/src/get/museum.py new file mode 100644 index 0000000..8f59490 --- /dev/null +++ b/src/get/museum.py @@ -0,0 +1,66 @@ +import httpx as h +import random +import json + +from .template import ImageGet, ImageGetException + +class ImageGetMuseum(ImageGet): + + def __init__(self) -> None: + headers = { + "AIC-User-Agent": "Eink Art Display (remoll@ethz.ch)" + } + + super().__init__( + base_url="https://api.artic.edu/api/v1/artworks/", + headers=headers + ) + + + def get_random_image_ids(self, num=1) -> list[str]: + post_data = { + "resources": "artworks", + "fields": ["id","image_id"], + "boost": False, + "limit": num, + "query": { + "function_score": { + "query": { + "bool": { + "filter": [ + {"term": {"is_public_domain": True}}, + {"exists": {"field": "image_id"}} + ] + } + }, + "boost_mode": "replace", + "random_score": { + "field": "id", + "seed": f"{random.randint(1, 1000000)}" + } + } + } + } + + response = h.post(self.base_url + "search", json=post_data, headers=self.headers) + if not response.status_code == 200: + raise ImageGetException("Error in step get_image_file: " + str(response.status_code)) + + response = json.loads(response.text) + images = response["data"] + ids = [img["id"] for img in images] + return ids + + + def get_image_file(self, image_id: str) -> bytearray: + url = self.base_url + f"{image_id}" + print(url) + response = h.get(url, headers=self.headers) + if not response.status_code == 200: + raise ImageGetException("Error in step get_image_file: " + str(response.status_code)) + + response = json.loads(response.text) + image_url = response["config"]["iiif_url"] + "/" + response["data"]["image_id"] + "/full/843,/0/default.jpg" + image = h.get(image_url).content + return image + diff --git a/src/get/public.py b/src/get/public.py deleted file mode 100644 index e679414..0000000 --- a/src/get/public.py +++ /dev/null @@ -1,24 +0,0 @@ -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 index 63f3214..ac8fe0a 100644 --- a/src/get/template.py +++ b/src/get/template.py @@ -13,8 +13,10 @@ class ImageGet: self.base_url = base_url self.headers = headers self.cache_num = cache_num - self.cache_dir = Path("../.cache/{self.__class__.__name__}/") + self.cache_dir = Path(f"../.cache/{self.__class__.__name__}/") self.cache_dir.mkdir(parents=True, exist_ok=True) + + self.save_cached_files() # Main entrypoint, called externally @@ -22,7 +24,7 @@ class ImageGet: try: id = self.get_random_image_ids()[0] bytes = self.get_image_file(id) - # self.save_cached_files() + 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() @@ -33,21 +35,21 @@ class ImageGet: 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 + present_count = len(list(self.cache_dir.glob("*"))) + missing = self.cache_num - present_count if missing == 0: return - ids = self.get_random_image_ids(missing) + ids = self.get_random_image_ids(num = missing) for i, id in enumerate(ids): print(f"Caching image {i + 1}") - new_cache = self.cached_path / f"{uuid.uuid4()}" + new_cache = self.cache_dir / 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("*")) + """Returns a random file from self.cache_dir""" + files = list(self.cache_dir.glob("*")) if len(files) == 0: raise ImageGetException("Could not load cached file: directory empty") @@ -59,7 +61,7 @@ class ImageGet: - def get_random_image_ids(self) -> list[str]: + def get_random_image_ids(self, num=1) -> list[str]: raise NotImplementedError diff --git a/src/get/unsplash.py b/src/get/unsplash.py new file mode 100644 index 0000000..90a483c --- /dev/null +++ b/src/get/unsplash.py @@ -0,0 +1,27 @@ +import httpx as h + +from .template import ImageGet, ImageGetException + +class ImageGetUnsplash(ImageGet): + + def __init__(self) -> None: + + super().__init__( + base_url="https://source.unsplash.com/", + headers={} + ) + + + def get_random_image_ids(self, num=1) -> list[str]: + # there are no ids since the api has a random endpoint + return [0 for i in range(num)] + + + def get_image_file(self, image_id: str = "doesntmatter") -> bytearray: + url = self.base_url + "random" + response = h.get(url, follow_redirects=True) + + if not response.status_code == 200: + raise ImageGetException("Error in step get_image_file: " + str(response.status_code)) + + return response.content diff --git a/src/main.py b/src/main.py index 429ef89..7085ea8 100644 --- a/src/main.py +++ b/src/main.py @@ -1,6 +1,6 @@ import sys from image_convert import ImageShrink -from get import immich +from get.combined import ImageGetCombined from image_show import ImageShow if len(sys.argv) == 2 and sys.argv[1] == "test": @@ -18,7 +18,7 @@ if "noreduce" in sys.argv: print("Disabling color reduction") shrink_kwargs["colors"] = -1 -getter = immich.ImageGetImmich() +getter = ImageGetCombined() converter = ImageShrink(**shrink_kwargs) shower = ImageShow()