summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--immich.py33
-rw-r--r--pix.py97
-rw-r--r--texture.py43
-rw-r--r--transition.py12
-rw-r--r--window.py110
5 files changed, 206 insertions, 89 deletions
diff --git a/immich.py b/immich.py
new file mode 100644
index 0000000..1f1ccb8
--- /dev/null
+++ b/immich.py
@@ -0,0 +1,33 @@
+import requests
+from io import BytesIO
+
+from texture import ImageTexture
+
+
+class ImmichConnector:
+ def __init__(self, server_url, api_key):
+ self.server_url = server_url.removesuffix("/")
+ self.api_key = api_key
+
+ def _request(self, endpoint):
+ return requests.get(f"{self.server_url}/api/{endpoint}", headers={ "x-api-key": self.api_key })
+
+ def load_album_assets(self, key):
+ response = self._request(f"albums/{key}")
+ if response.status_code != 200: return
+
+ data = response.json()
+ return data["assets"]
+
+ def load_image(self, pd, key, exif=None):
+ response = self._request(f"assets/{key}/thumbnail?size=fullsize")
+ if response.status_code != 200: return
+
+ image_data = BytesIO(response.content)
+ it = ImageTexture(image_data, exif)
+ pd.textures.append(it)
+ print(f"Loaded image {key}")
+
+ def idle(self, pd):
+ for asset in self.load_album_assets("bac029a5-972b-4519-bce0-a0d74add3969"):
+ self.load_image(pd, asset["id"], asset["exifInfo"])
diff --git a/pix.py b/pix.py
index 988f83d..41d28d6 100644
--- a/pix.py
+++ b/pix.py
@@ -1,89 +1,20 @@
import sys
-from OpenGL.GL import *
-from OpenGL.GLUT import *
-from OpenGL.GLU import *
-from PIL import Image
-from time import time
+from threading import Thread
-from transition import Transition
-
-display_time = 2.0
-transition_duration = 0.5
-
-# Load image as a texture using PIL
-def load_texture(image_path):
- img = Image.open(image_path)
- img = img.convert("RGBA") # Ensure the image is in RGBA mode (4 channels: R, G, B, A)
- img_data = img.tobytes("raw", "RGBA", 0, -1) # Convert image data to bytes
-
- texture_id = glGenTextures(1)
- glBindTexture(GL_TEXTURE_2D, texture_id)
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width, img.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data)
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
- return texture_id
-
-# Main display function
-transition = Transition()
-def display():
- global start_time, image_time, last_time, alpha, texture_id1, texture_id2
-
- current_time = time()
- alive_time = current_time - start_time
- delta_time = current_time - last_time
- last_time = current_time
- image_time += delta_time
-
- if (image_time < display_time):
- return
-
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
- glLoadIdentity()
-
- # Get window size
- window_width, window_height = glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT)
-
- # DRAW
- transition_time = image_time - display_time
- complete = transition.draw(texture_id1, texture_id2, window_width, window_height, delta_time, transition_time, transition_duration)
-
- glClearColor(0.0, 0.0, 0.0, 1.0) # Set the background color to black
-
- glutSwapBuffers()
-
- if (complete):
- image_time = 0
- texture_id1, texture_id2 = texture_id2, texture_id1
-
-# Initialization and main loop
-def main():
- global texture_id1, texture_id2, alpha, last_time, start_time, image_time
-
- # Initialize the window
- glutInit(sys.argv)
- glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
- glutCreateWindow("Image Viewer with Fade Transition")
- glEnable(GL_TEXTURE_2D)
+from window import PixDisplay
+from immich import ImmichConnector
+def load_textures(pd):
# Load two images for transition
- texture_id1 = load_texture("image1.jpg")
- texture_id2 = load_texture("image2.jpg")
-
- alpha = 0.0 # Start with fully transparent
- image_time = 0
- start_time = time()
- last_time = time()
-
- # Set up the OpenGL viewport and projection
- glMatrixMode(GL_PROJECTION)
- glLoadIdentity()
- glOrtho(-1, 1, -1, 1, -1, 1)
- glMatrixMode(GL_MODELVIEW)
-
- glutDisplayFunc(display)
- glutIdleFunc(display)
- glutMainLoop()
+ tex = ImageTexture("image1.jpg")
+ pd.textures.append(tex)
+ tex = ImageTexture("image2.jpg")
+ pd.textures.append(tex)
if __name__ == "__main__":
- main()
-
+ immichConnector = ImmichConnector("http://192.168.1.13", "m5nqOoBc4uhAba21gZdCP3z8D3JT4GPxDXL2psd52EA")
+ pd = PixDisplay()
+ #t1 = Thread(target=load_textures, daemon=True, args=(pd,))
+ t1 = Thread(target=immichConnector.idle, daemon=True, args=(pd,))
+ t1.start()
+ pd.main()
diff --git a/texture.py b/texture.py
new file mode 100644
index 0000000..cf37d6a
--- /dev/null
+++ b/texture.py
@@ -0,0 +1,43 @@
+from OpenGL.GL import *
+from OpenGL.GLUT import *
+from OpenGL.GLU import *
+from PIL import Image, ExifTags
+
+class ImageTexture:
+ def __init__(self, image_source, exif=None):
+ self.id = None
+ img = Image.open(image_source)
+ self.exif = exif or ImageTexture.get_exif(img)
+ img = ImageTexture.handle_orientation(img, exif)
+ img = img.convert("RGBA") # Ensure the image is in RGBA mode
+ self.width = img.width
+ self.height = img.height
+ self._img_data = img.tobytes("raw", "RGBA", 0, -1) # Convert image data to bytes
+
+ def gl_init(self):
+ if self.id:
+ return
+ #glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
+ self.id = glGenTextures(1)
+ glBindTexture(GL_TEXTURE_2D, self.id)
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self.width, self.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, self._img_data)
+ #glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.width, img.height, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
+ del self._img_data # No longer needed, clear mem
+
+ print(f"Loaded texture {self.id}")
+
+ @staticmethod
+ def get_exif(img):
+ return { ExifTags.TAGS[k]: v for k, v in img.getexif().items() if k in ExifTags }
+
+ @staticmethod
+ def handle_orientation(img, exif):
+ orientation = exif.get("Orientation", 1)
+
+ if orientation == 3: return img.rotate(180, expand=True)
+ if orientation == 6: return img.rotate(270, expand=True)
+ if orientation == 8: return img.rotate( 90, expand=True)
+
+ return img
diff --git a/transition.py b/transition.py
index cf2acb4..a076889 100644
--- a/transition.py
+++ b/transition.py
@@ -10,25 +10,25 @@ class Transition:
alpha = 1.0
# Draw the first image
- self._draw_image(texture_prev, 3840, 2160, window_width, window_height, 1 - alpha) # TODO instead of decreasing alpha, draw transparent letterboxes
+ self.draw_image(texture_prev, window_width, window_height, 1 - alpha) # TODO instead of decreasing alpha, draw transparent letterboxes
# Draw the second image (with transparency)
- self._draw_image(texture_next, 3840, 2160, window_width, window_height, alpha)
+ self.draw_image(texture_next, window_width, window_height, alpha)
return alpha >= 1.0 # Complete
# Draw the image with blending enabled (to allow fade effect)
- def _draw_image(self, texture_id, img_width, img_height, window_width, window_height, alpha):
- if (not alpha): return
+ def draw_image(self, texture, window_width, window_height, alpha):
+ if not alpha: return
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
- glBindTexture(GL_TEXTURE_2D, texture_id)
+ glBindTexture(GL_TEXTURE_2D, texture.id)
glColor4f(1.0, 1.0, 1.0, alpha) # Set alpha to control transparency
# Calculate aspect ratio
- img_aspect = img_width / float(img_height)
+ img_aspect = texture.width / float(texture.height)
win_aspect = window_width / float(window_height)
scaled_width = window_width
diff --git a/window.py b/window.py
new file mode 100644
index 0000000..29d0522
--- /dev/null
+++ b/window.py
@@ -0,0 +1,110 @@
+from OpenGL.GL import *
+from OpenGL.GLUT import *
+from OpenGL.GLU import *
+from time import time
+
+from transition import Transition
+from texture import ImageTexture
+
+
+class PixDisplay:
+ def __init__(self):
+ self.last_time = 0
+ self.start_time = 0
+ self.image_time = 0
+ self.textures = []
+ self.current_texture_index = 0
+ self.transition = Transition()
+ self.window_width = 0
+ self.window_height = 0
+
+ self.display_duration = 2.0
+ self.transition_duration = 0.5
+
+ @property
+ def next_texture_index(self): return (self.current_texture_index + 1) % len(self.textures)
+
+ @property
+ def texture_current(self): return self.textures[self.current_texture_index]
+
+ @property
+ def texture_next(self): return self.textures[self.next_texture_index]
+
+ # Main display function
+ def display(self):
+ # Calculate timings
+ current_time = time()
+ alive_time = current_time - self.start_time
+ delta_time = current_time - self.last_time
+ self.last_time = current_time
+
+ if not self.textures:
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+ glLoadIdentity()
+ glClearColor(0.0, 0.0, 0.0, 1.0) # Set the background color to black
+
+ glutSwapBuffers()
+ return
+
+ # Ensure textures are initialized
+ self.texture_current.gl_init()
+ self.texture_next.gl_init()
+
+ # Progress image time
+ self.image_time += delta_time
+
+ # Get window size
+ window_width, window_height = glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT)
+
+ if self.image_time < self.display_duration:
+ if window_width != self.window_width or window_height != self.window_height:
+ self.window_width, self.window_height = window_width, window_height
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+ glLoadIdentity()
+
+ self.transition.draw_image(self.texture_current, self.window_width, self.window_height, 1)
+
+ glClearColor(0.0, 0.0, 0.0, 1.0) # Set the background color to black
+ glutSwapBuffers()
+ return
+
+ self.window_width, self.window_height = window_width, window_height
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+ glLoadIdentity()
+
+ # DRAW
+ transition_time = self.image_time - self.display_duration
+ complete = self.transition.draw(self.texture_current, self.texture_next, self.window_width, self.window_height, delta_time, transition_time, self.transition_duration)
+
+ glClearColor(0.0, 0.0, 0.0, 1.0) # Set the background color to black
+
+ glutSwapBuffers()
+
+ if complete:
+ self.image_time = 0
+ self.current_texture_index = self.next_texture_index
+
+ # Initialization and main loop
+ def main(self):
+ # Initialize the window
+ glutInit(sys.argv)
+ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
+ glutCreateWindow("Image Viewer with Fade Transition")
+ glEnable(GL_TEXTURE_2D)
+
+ self.image_time = 0
+ self.start_time = time()
+ self.last_time = time()
+
+ # Set up the OpenGL viewport and projection
+ glMatrixMode(GL_PROJECTION)
+ glLoadIdentity()
+ glOrtho(-1, 1, -1, 1, -1, 1)
+ glMatrixMode(GL_MODELVIEW)
+
+ glutDisplayFunc(self.display)
+ glutIdleFunc(self.display)
+ glutMainLoop()
+