From b487f62ba7cd7dbbcadb2b5704ec1c99f6578390 Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Tue, 6 May 2025 21:17:38 -0500 Subject: initial commit --- pix.py | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 pix.py (limited to 'pix.py') diff --git a/pix.py b/pix.py new file mode 100644 index 0000000..988f83d --- /dev/null +++ b/pix.py @@ -0,0 +1,89 @@ +import sys +from OpenGL.GL import * +from OpenGL.GLUT import * +from OpenGL.GLU import * +from PIL import Image +from time import time + +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) + + # 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() + +if __name__ == "__main__": + main() + -- cgit v1.2.3 From 145de3311cb9f32ac0bc9842b0600fcad84fed16 Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Fri, 9 May 2025 21:58:54 -0500 Subject: add support for downloading new images from immich api on a separate thread --- pix.py | 97 ++++++++++-------------------------------------------------------- 1 file changed, 14 insertions(+), 83 deletions(-) (limited to 'pix.py') 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() -- cgit v1.2.3 From 2bd4da7678b027843cac1a03a1b5f6cc70a7cc81 Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Sat, 10 May 2025 11:52:02 -0500 Subject: requirements.txt added, significantly reduced cpu usage using timer func and vertex/fragment shader instead of drawing new quad etc every frame --- pix.py | 8 -------- 1 file changed, 8 deletions(-) (limited to 'pix.py') diff --git a/pix.py b/pix.py index 41d28d6..1e00fcf 100644 --- a/pix.py +++ b/pix.py @@ -4,17 +4,9 @@ from threading import Thread from window import PixDisplay from immich import ImmichConnector -def load_textures(pd): - # Load two images for transition - tex = ImageTexture("image1.jpg") - pd.textures.append(tex) - tex = ImageTexture("image2.jpg") - pd.textures.append(tex) - if __name__ == "__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() -- cgit v1.2.3 From c45551035d4f2ef65f8de0472bfda4018636f309 Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Sat, 10 May 2025 18:21:27 -0500 Subject: handle ctrl+c --- pix.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'pix.py') diff --git a/pix.py b/pix.py index 1e00fcf..6e53b8d 100644 --- a/pix.py +++ b/pix.py @@ -1,12 +1,26 @@ import sys +import signal from threading import Thread +from OpenGL.GLUT import glutLeaveMainLoop from window import PixDisplay from immich import ImmichConnector + +def handle_sigint(sig, frame): + try: + glutLeaveMainLoop() + sys.exit(0) + except: + pass + finally: + print("Exiting on Ctrl+C") + + if __name__ == "__main__": immichConnector = ImmichConnector("http://192.168.1.13", "m5nqOoBc4uhAba21gZdCP3z8D3JT4GPxDXL2psd52EA") pd = PixDisplay() t1 = Thread(target=immichConnector.idle, daemon=True, args=(pd,)) t1.start() - pd.main() + signal.signal(signal.SIGINT, handle_sigint) + pd.main(sys.argv) -- cgit v1.2.3 From e7036d21d5e5c87702724283f55a77d07344f4fe Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Sat, 10 May 2025 19:47:31 -0500 Subject: add flaskapi --- pix.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'pix.py') diff --git a/pix.py b/pix.py index 6e53b8d..aa1495f 100644 --- a/pix.py +++ b/pix.py @@ -5,6 +5,7 @@ from OpenGL.GLUT import glutLeaveMainLoop from window import PixDisplay from immich import ImmichConnector +from flaskapi import app def handle_sigint(sig, frame): @@ -18,9 +19,16 @@ def handle_sigint(sig, frame): if __name__ == "__main__": - immichConnector = ImmichConnector("http://192.168.1.13", "m5nqOoBc4uhAba21gZdCP3z8D3JT4GPxDXL2psd52EA") pd = PixDisplay() - t1 = Thread(target=immichConnector.idle, daemon=True, args=(pd,)) + immich_connector = ImmichConnector("http://192.168.1.13", "m5nqOoBc4uhAba21gZdCP3z8D3JT4GPxDXL2psd52EA") + + t1 = Thread(target=immich_connector.idle, daemon=True, args=(pd,)) t1.start() + + app.config["pix_display"] = pd + app.config["immich_connector"] = immich_connector + flask_thread = Thread(target=app.run, daemon=True, kwargs={ "port": 5000 }) + flask_thread.start() + signal.signal(signal.SIGINT, handle_sigint) pd.main(sys.argv) -- cgit v1.2.3 From e1a6fc09afc088dcb67263ed5923f5be41c32c31 Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Sun, 25 May 2025 21:38:37 -0500 Subject: use lazy caching texture list to limit number of images loaded at one time --- pix.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'pix.py') diff --git a/pix.py b/pix.py index aa1495f..4064892 100644 --- a/pix.py +++ b/pix.py @@ -3,6 +3,7 @@ import signal from threading import Thread from OpenGL.GLUT import glutLeaveMainLoop +from lazycachelist import LazyCachingTextureList from window import PixDisplay from immich import ImmichConnector from flaskapi import app @@ -19,10 +20,12 @@ def handle_sigint(sig, frame): if __name__ == "__main__": - pd = PixDisplay() immich_connector = ImmichConnector("http://192.168.1.13", "m5nqOoBc4uhAba21gZdCP3z8D3JT4GPxDXL2psd52EA") + album_keys = [ "38617851-6b57-44f1-b5f7-82577606afc4" ] + lazy_texture_list = LazyCachingTextureList(immich_connector, album_keys, 30) + pd = PixDisplay(lazy_texture_list) - t1 = Thread(target=immich_connector.idle, daemon=True, args=(pd,)) + t1 = Thread(target=immich_connector.idle, daemon=True) t1.start() app.config["pix_display"] = pd -- cgit v1.2.3 From cd1657ece1fa199964abd6544b81b394ab9369aa Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Sun, 15 Jun 2025 15:06:34 -0500 Subject: callbacks on lctl, websocket controls --- pix.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'pix.py') diff --git a/pix.py b/pix.py index 4064892..45e3f70 100644 --- a/pix.py +++ b/pix.py @@ -6,7 +6,7 @@ from OpenGL.GLUT import glutLeaveMainLoop from lazycachelist import LazyCachingTextureList from window import PixDisplay from immich import ImmichConnector -from flaskapi import app +from flaskapi import app, socketio def handle_sigint(sig, frame): @@ -22,7 +22,7 @@ def handle_sigint(sig, frame): if __name__ == "__main__": immich_connector = ImmichConnector("http://192.168.1.13", "m5nqOoBc4uhAba21gZdCP3z8D3JT4GPxDXL2psd52EA") album_keys = [ "38617851-6b57-44f1-b5f7-82577606afc4" ] - lazy_texture_list = LazyCachingTextureList(immich_connector, album_keys, 30) + lazy_texture_list = LazyCachingTextureList(immich_connector, album_keys, 30, lambda d: socketio.emit("seek", d)) pd = PixDisplay(lazy_texture_list) t1 = Thread(target=immich_connector.idle, daemon=True) @@ -30,6 +30,7 @@ if __name__ == "__main__": app.config["pix_display"] = pd app.config["immich_connector"] = immich_connector + app.config["textures"] = lazy_texture_list flask_thread = Thread(target=app.run, daemon=True, kwargs={ "port": 5000 }) flask_thread.start() -- cgit v1.2.3 From b8df4605b42d9a61bb4ae4731efabbdc38166063 Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Mon, 16 Jun 2025 21:50:38 -0500 Subject: add config and add application thread manager --- pix.py | 41 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 32 deletions(-) (limited to 'pix.py') diff --git a/pix.py b/pix.py index 45e3f70..236d2f1 100644 --- a/pix.py +++ b/pix.py @@ -1,38 +1,15 @@ -import sys -import signal -from threading import Thread -from OpenGL.GLUT import glutLeaveMainLoop +import argparse -from lazycachelist import LazyCachingTextureList -from window import PixDisplay -from immich import ImmichConnector from flaskapi import app, socketio - - -def handle_sigint(sig, frame): - try: - glutLeaveMainLoop() - sys.exit(0) - except: - pass - finally: - print("Exiting on Ctrl+C") +from settings import Config +from manager import PixMan if __name__ == "__main__": - immich_connector = ImmichConnector("http://192.168.1.13", "m5nqOoBc4uhAba21gZdCP3z8D3JT4GPxDXL2psd52EA") - album_keys = [ "38617851-6b57-44f1-b5f7-82577606afc4" ] - lazy_texture_list = LazyCachingTextureList(immich_connector, album_keys, 30, lambda d: socketio.emit("seek", d)) - pd = PixDisplay(lazy_texture_list) - - t1 = Thread(target=immich_connector.idle, daemon=True) - t1.start() - - app.config["pix_display"] = pd - app.config["immich_connector"] = immich_connector - app.config["textures"] = lazy_texture_list - flask_thread = Thread(target=app.run, daemon=True, kwargs={ "port": 5000 }) - flask_thread.start() + p = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) + p.add_argument("--config", type=str, help="set config file path", default="config.json") + p.add_argument("--host", type=str, help="set web interface host", default="0.0.0.0") + p.add_argument("--port", type=int, help="set web interface port", default=80) + args = p.parse_args() - signal.signal(signal.SIGINT, handle_sigint) - pd.main(sys.argv) + PixMan.initialize(args.config, app, socketio, args.host, args.port) -- cgit v1.2.3