From e29702e115bbe015a6714c31c5ed7e36182255d5 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sat, 20 Jan 2024 21:35:04 +0100 Subject: [PATCH] remove frameos-python --- frameos-python/README.md | 1 - frameos-python/apps/boilerplate/config.json | 16 -- frameos-python/apps/boilerplate/frame.py | 55 ------ frameos-python/apps/calendar/config.json | 165 ------------------ frameos-python/apps/calendar/frame.py | 145 --------------- frameos-python/apps/google_photos/config.json | 16 -- frameos-python/apps/google_photos/frame.py | 38 ---- frameos-python/apps/screenshot/config.json | 42 ----- frameos-python/apps/screenshot/frame.py | 130 -------------- frameos-python/index.html | 103 ----------- 10 files changed, 711 deletions(-) delete mode 100644 frameos-python/README.md delete mode 100644 frameos-python/apps/boilerplate/config.json delete mode 100644 frameos-python/apps/boilerplate/frame.py delete mode 100644 frameos-python/apps/calendar/config.json delete mode 100644 frameos-python/apps/calendar/frame.py delete mode 100644 frameos-python/apps/google_photos/config.json delete mode 100644 frameos-python/apps/google_photos/frame.py delete mode 100644 frameos-python/apps/screenshot/config.json delete mode 100644 frameos-python/apps/screenshot/frame.py delete mode 100644 frameos-python/index.html diff --git a/frameos-python/README.md b/frameos-python/README.md deleted file mode 100644 index 19b199e4..00000000 --- a/frameos-python/README.md +++ /dev/null @@ -1 +0,0 @@ -This folder contains what's left of the old Python codebase, which hasn't been migrated to Nim yet. diff --git a/frameos-python/apps/boilerplate/config.json b/frameos-python/apps/boilerplate/config.json deleted file mode 100644 index 1855cce2..00000000 --- a/frameos-python/apps/boilerplate/config.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "Boilerplate", - "description": "Starter and documentation. Drag me onto the frame, then click the edit icon.", - "category": "boilerplate", - "version": "1.0.0", - "fields": [ - { - "name": "my_name", - "type": "string", - "value": "Pineapple", - "required": true, - "label": "Enter your name", - "placeholder": "Mr Cat" - } - ] -} \ No newline at end of file diff --git a/frameos-python/apps/boilerplate/frame.py b/frameos-python/apps/boilerplate/frame.py deleted file mode 100644 index 08520e5d..00000000 --- a/frameos-python/apps/boilerplate/frame.py +++ /dev/null @@ -1,55 +0,0 @@ -import io - -import requests -from PIL import Image, ImageDraw, ImageFont -from apps import App, ExecutionContext, DEFAULT_FONT -from frame.image_utils import draw_text_with_border - -class BoilerplateApp(App): - # Local state, persists between executions - local_state = {} - - def run(self, context: ExecutionContext): - # Each run starts from an event. - - # The most common event is "render". - if context.event == 'render': - # In the render context you can modify an image. - width, height = context.image.size - - # Swap it out if you want to - image_url = f"https://source.unsplash.com/random/{width}x{height}/?nature" - response = requests.get(image_url) - context.image = Image.open(io.BytesIO(response.content)) - - # Access config - my_name = self.config.get('my_name', "Bananas") - - # Draw some text on it - text = f"FrameOS is {my_name}" - draw = ImageDraw.Draw(context.image) - font = ImageFont.truetype(DEFAULT_FONT, 48 if width > 500 else 24) - text_width, text_height = draw.textsize(text, font=font) - x, y = (width - text_width) / 2, (height - text_height) / 2 - # draw.text((x, y), text, fill='white', font=font) # boring - draw_text_with_border(draw, (x, y), text, font, 'white', 'black', 3) - - # You can use the context's "state" to carry random data between apps. - # It's cleared each execution. - context.state["really_bananas"] = "i know right" - - # Use class variables to carry state between executions. - self.local_state["bananas"] = "yes" - - # Ask ChatGPT for other PIL drawing commands and go, well, you know it by now: bananas - - # We can react to button presses - if context.event == 'button_press': - button = context.payload.get('label', None) - self.log(f"button pressed: {button}") - if button == 'A': - context.state["keyword"] = "penguins" - if button == 'B': - context.state["keyword"] = "pineapple" - - diff --git a/frameos-python/apps/calendar/config.json b/frameos-python/apps/calendar/config.json deleted file mode 100644 index 251529a1..00000000 --- a/frameos-python/apps/calendar/config.json +++ /dev/null @@ -1,165 +0,0 @@ -{ - "name": "Calendar Overlay", - "description": "Overlay current month in a 7-day grid on the image", - "category": "overlay", - "version": "1.0.0", - "fields": [ - { - "name": "font_color", - "type": "string", - "value": "black", - "required": true, - "label": "Font Color", - "placeholder": "black" - }, - { - "name": "font_size", - "type": "string", - "value": "40", - "required": true, - "label": "Font Size", - "placeholder": "40" - }, - { - "name": "position", - "type": "select", - "options": [ - "top-left", - "top-center", - "top-right", - "center-left", - "center-center", - "center-right", - "bottom-left", - "bottom-center", - "bottom-right" - ], - "value": "center-center", - "required": true, - "label": "Position", - "placeholder": "center-center" - }, - { - "name": "border_width", - "type": "string", - "value": "2", - "required": true, - "label": "Border width (0 = disabled)", - "placeholder": "2" - }, - { - "name": "border_color", - "type": "string", - "value": "white", - "required": true, - "label": "Border Color", - "placeholder": "white" - }, - { - "name": "start_day", - "type": "select", - "options": ["Sunday", "Monday"], - "value": "Sunday", - "required": true, - "label": "First Day of the Week", - "placeholder": "Sunday" - }, - { - "name": "calendar_width_percentage", - "type": "string", - "value": "80", - "required": true, - "label": "Calendar Width as % of Image Width", - "placeholder": "80" - }, - { - "name": "calendar_height_percentage", - "type": "string", - "value": "80", - "required": true, - "label": "Calendar Height as % of Image Width", - "placeholder": "80" - }, - { - "name": "title_alignment", - "type": "select", - "options": ["left", "center", "right"], - "value": "left", - "required": true, - "label": "Title Alignment", - "placeholder": "left" - }, - { - "name": "title_template", - "type": "text", - "value": "Let's pretend it's \"{month}\"", - "required": true, - "label": "Title Template", - "placeholder": "Let's pretend it's \"{month}\"" - }, - { - "name": "title_font_color", - "type": "string", - "value": "black", - "required": true, - "label": "Title Font Color", - "placeholder": "black" - }, - { - "name": "title_font_size", - "type": "string", - "value": "50", - "required": true, - "label": "Title Font Size", - "placeholder": "50" - }, - { - "name": "title_border_width", - "type": "string", - "value": "2", - "required": true, - "label": "Title Border width (0 = disabled)", - "placeholder": "2" - }, - { - "name": "title_border_color", - "type": "string", - "value": "white", - "required": true, - "label": "Title Border Color", - "placeholder": "white" - }, - { - "name": "today_font_color", - "type": "string", - "value": "red", - "required": true, - "label": "Today Font Color", - "placeholder": "red" - }, - { - "name": "today_font_size", - "type": "string", - "value": "60", - "required": true, - "label": "Today Font Size", - "placeholder": "50" - }, - { - "name": "today_border_width", - "type": "string", - "value": "2", - "required": true, - "label": "Today Border width (0 = disabled)", - "placeholder": "2" - }, - { - "name": "today_border_color", - "type": "string", - "value": "black", - "required": true, - "label": "Today Border Color", - "placeholder": "black" - } - ] -} diff --git a/frameos-python/apps/calendar/frame.py b/frameos-python/apps/calendar/frame.py deleted file mode 100644 index 1f03d60d..00000000 --- a/frameos-python/apps/calendar/frame.py +++ /dev/null @@ -1,145 +0,0 @@ -from PIL import Image, ImageDraw, ImageFont -from datetime import datetime -from apps import App, ExecutionContext, DEFAULT_FONT -import calendar -from frame.image_utils import draw_text_with_border - - -class CalendarApp(App): - def run(self, context: ExecutionContext): - width, height = context.image.size - - # Get the current month details - now = datetime.now() - month_name = now.strftime('%B') - year = now.strftime('%Y') - _, last_day = calendar.monthrange(now.year, now.month) - - start_day_config = self.get_config('start_day', 'Sunday') - start_day = 0 if start_day_config == "Sunday" else 1 - - # Config settings - font_color = self.get_config('font_color', 'black') - font_size = int(self.get_config('font_size', 40)) - border_color = self.get_config('border_color', 'white') - border_width = int(self.get_config('border_width', 2)) - calendar_width_percentage = float(self.get_config('calendar_width_percentage', '80')) / 100 - calendar_height_percentage = float(self.get_config('calendar_height_percentage', '80')) / 100 - position = self.get_config('position', 'center-center') - font = ImageFont.truetype(DEFAULT_FONT, font_size) - - title_font_color = self.get_config('title_font_color', 'black') - title_font_size = int(self.get_config('title_font_size', 50)) - title_border_color = self.get_config('title_border_color', 'white') - title_border_width = int(self.get_config('title_border_width', 2)) - title_font = ImageFont.truetype(DEFAULT_FONT, title_font_size) - - today_font_color = self.get_config('today_font_color', 'red') - today_font_size = int(self.get_config('today_font_size', 60)) - today_border_color = self.get_config('today_border_color', 'black') - today_border_width = int(self.get_config('today_border_width', 2)) - today_font = ImageFont.truetype(DEFAULT_FONT, today_font_size) - - draw = ImageDraw.Draw(context.image) - - # Determine the first weekday of the month (0 = Monday, 6 = Sunday) - first_weekday_of_month = calendar.monthrange(now.year, now.month)[0] - - if start_day == 0: # Sunday as the start of the week - current_day = 1 - first_weekday_of_month + 6 # +6 because Sunday is represented as 6 in the weekday - else: # Monday as the start of the week - current_day = 1 - first_weekday_of_month - - # The logic to determine the number of rows required for the month - days_in_month = last_day - total_cells = first_weekday_of_month + days_in_month # number of days plus the starting position - max_rows = 1 + (total_cells + 6) // 7 # ceil division - - # Calculate cell size and start position - padding = 4 - cell_width = (width - 2 * padding) * calendar_width_percentage / 7 - cell_height = (height - 2 * padding) * calendar_height_percentage / max_rows - - if position == 'top-left': - start_x, start_y = padding, padding - elif position == 'top-center': - start_x = (width - cell_width * 7) / 2 - start_y = padding - elif position == 'top-right': - start_x = width - cell_width * 7 - padding - start_y = padding - elif position == 'center-left': - start_x = padding - start_y = (height - cell_height * max_rows) / 2 - elif position == 'center-center': - start_x = (width - cell_width * 7) / 2 - start_y = (height - cell_height * max_rows) / 2 - elif position == 'center-right': - start_x = width - cell_width * 7 - padding - start_y = (height - cell_height * max_rows) / 2 - elif position == 'bottom-left': - start_x = padding - start_y = height - cell_height * max_rows - padding - elif position == 'bottom-center': - start_x = (width - cell_width * 7) / 2 - start_y = height - cell_height * max_rows - padding - elif position == 'bottom-right': - start_x = width - cell_width * 7 - padding - start_y = height - cell_height * max_rows - padding - - # Vertical lines - for i in range(8): # There are 8 lines for 7 columns - line_start_x = start_x + i * cell_width - line_start_y = start_y + cell_height # Start below the month title - line_end_x = line_start_x - line_end_y = line_start_y + (max_rows - 1) * cell_height - draw.line([(line_start_x, line_start_y), (line_end_x, line_end_y)], fill=border_color) - - # Horizontal lines - for i in range(max_rows + 1): # There are max_rows+1 lines - line_start_x = start_x - line_start_y = start_y + i * cell_height + cell_height # Start below the month title - line_end_x = start_x + 7 * cell_width - line_end_y = line_start_y - draw.line([(line_start_x, line_start_y), (line_end_x, line_end_y)], fill=border_color) - - # Draw month name at the top - title_text = self.get_config('title_template', "Let's pretend it's \"{month}\"") - title_text = title_text.replace('{month}', month_name) - title_text = title_text.replace('{year}', year) - title_alignment = self.get_config('title_alignment', 'left') - - title_width, title_height = title_font.getsize(title_text) - title_height *= 1.15 - if title_alignment == 'center': - title_x = start_x + (7 * cell_width - title_width) / 2 - elif title_alignment == 'right': - title_x = start_x + 7 * cell_width - title_width - else: # left by default - title_x = start_x - - title_y = start_y + (cell_height - title_height) / 2 - - draw_text_with_border(draw, (title_x, title_y), title_text, title_font, title_font_color, title_border_color, title_border_width) - - # Draw the calendar grid - current_day = 1 - (start_day + 1) - for row in range(max_rows): - for col in range(7): - if 1 <= current_day <= last_day: - day_text = str(current_day) - if current_day == now.day: - text_width, text_height = draw.textsize(day_text, font=today_font) - else: - text_width, text_height = draw.textsize(day_text, font=font) - text_height *= 1.15 - - x = start_x + col * cell_width + (cell_width - text_width) / 2 - y = start_y + (row + 1) * cell_height + (cell_height - text_height) / 2 - if current_day == now.day: - draw_text_with_border(draw, (x, y), day_text, today_font, today_font_color, today_border_color, today_border_width) - else: - draw_text_with_border(draw, (x, y), day_text, font, font_color, border_color, border_width) - current_day += 1 - - self.log(f"Added calendar for month: {month_name}") \ No newline at end of file diff --git a/frameos-python/apps/google_photos/config.json b/frameos-python/apps/google_photos/config.json deleted file mode 100644 index e233704d..00000000 --- a/frameos-python/apps/google_photos/config.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "Google Photos Scraper", - "description": "Random image from a shared photos album. Scraped from the URL's source. Nothing fancy here, just low quality thumbnails.", - "category": "image", - "version": "1.0.0", - "fields": [ - { - "name": "photos_url", - "type": "string", - "value": "", - "required": true, - "label": "Google Photos URL", - "placeholder": "https://photos.google.com/share/....." - } - ] -} diff --git a/frameos-python/apps/google_photos/frame.py b/frameos-python/apps/google_photos/frame.py deleted file mode 100644 index 8ae2854f..00000000 --- a/frameos-python/apps/google_photos/frame.py +++ /dev/null @@ -1,38 +0,0 @@ -import random -import re - -from PIL import Image, UnidentifiedImageError -import requests -from requests.exceptions import RequestException -import io -from apps import App, ExecutionContext - -class GooglePhotosApp(App): - def run(self, context: ExecutionContext): - photos_url = self.get_config('photos_url', None) - if not photos_url: - raise ValueError("Photos URL is not provided in app config") - - width, height = context.image.size - photos_url = photos_url.replace('{width}', str(width)) - photos_url = photos_url.replace('{height}', str(height)) - - try: - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" - } - - response = requests.get(photos_url, headers=headers) - response.raise_for_status() - - image_urls = re.findall(r'https://lh3\.googleusercontent\.com/pw/[a-zA-Z0-9\-_]+', response.text) - random_image_url = random.choice(image_urls) - self.log(f"Downloading: {random_image_url}") - response = requests.get(random_image_url) - response.raise_for_status() - context.image = Image.open(io.BytesIO(response.content)) - self.log(f"Downloaded image: {context.image.width}x{context.image.height} {context.image.format} {context.image.mode}") - except RequestException as e: - raise Exception(f"Error fetching image from {photos_url}. Error: {e}") - except UnidentifiedImageError: - raise Exception(f"The content at {photos_url} is not a valid image format") diff --git a/frameos-python/apps/screenshot/config.json b/frameos-python/apps/screenshot/config.json deleted file mode 100644 index d9e3e8c7..00000000 --- a/frameos-python/apps/screenshot/config.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "Screenshot URL", - "description": "Capture a snapshot of a website. The first time you run this it'll install Fireforx or Chromium and another gigabyte of stuff. This only runs on 64-bit Raspberry Pis!", - "category": "image", - "version": "1.0.0", - "fields": [ - { - "name": "url", - "type": "text", - "value": "https://frameos.net/", - "required": true, - "label": "URL to capture", - "placeholder": "" - }, - { - "name": "browser", - "type": "select", - "options": ["chromium"], - "value": "chromium", - "required": true, - "label": "Browser" - }, - { - "name": "scaling_factor", - "type": "string", - "value": "1", - "label": "Scaling factor" - }, - { - "name": "wait_selector", - "type": "string", - "value": "", - "label": "Wait for CSS selector" - }, - { - "name": "wait_delay", - "type": "string", - "value": "0", - "label": "Extra Seconds to wait" - } - ] -} diff --git a/frameos-python/apps/screenshot/frame.py b/frameos-python/apps/screenshot/frame.py deleted file mode 100644 index f3b003d4..00000000 --- a/frameos-python/apps/screenshot/frame.py +++ /dev/null @@ -1,130 +0,0 @@ -from PIL import Image -import platform -import io - -from apps import App, ExecutionContext - - -class ScreenshotApp(App): - def __post_init__(self): - self.driver = None - if not self.architecture_supported(): - self.log("Sadly we can't take screenshots in a 32 bit system, as there's no browser that works anymore. Good bye.") - return - self.fetch_selenium() - self.fetch_browser() - - def architecture_supported(self) -> bool: - return platform.architecture()[0] != '32bit' - - def fetch_selenium(self): - self.log("Checking for required pip packages: selenium, webdriver-manager") - try: - import selenium - except ImportError: - self.log("Installing selenium. This might take a while.") - self.shell(f"pip3 install selenium==4.14.0") - try: - import webdriver_manager - except ImportError: - self.log("Installing webdriver-manager. This might take a while.") - self.shell(f"pip3 install webdriver-manager==4.0.1") - - def fetch_browser(self): - browser = self.get_config('browser', 'chromium') - if browser == 'chromium': - self.log("Checking for chromium. Installing via apt if missing.") - self.shell("dpkg -l | grep -q \"^ii chromium-browser\" || " - "(sudo apt -y update && sudo apt install -y chromium-browser xvfb chromium-chromedriver)") - elif browser == 'firefox': - self.log("Checking for firefox. Installing via apt if missing.") - self.shell("dpkg -l | grep -q \"^ii firefox-esr\" || " - "(sudo apt -y update && sudo apt install -y firefox-esr)") - else: - raise ValueError(f"Browser {browser} not supported") - - def init_driver(self): - browser = self.get_config('browser', 'chromium') - if browser == 'chromium': - scaling_factor = str(float(self.get_config('scaling_factor', 1))) - self.log(f"Creating chromium web driver with scaling factor {scaling_factor}") - from selenium.webdriver.chrome.options import Options - from selenium import webdriver - from selenium.webdriver.chrome.service import Service as ChromiumService - - options = Options() - options.headless = True - options.add_argument(f"--force-device-scale-factor={scaling_factor}") - options.add_argument("--use-gl=swiftshader") - options.add_argument("--disable-software-rasterizer") - options.add_argument("--no-sandbox") - options.add_argument("--headless") - options.add_argument("--disable-gpu") - options.add_argument("--disable-dev-shm-usage") - self.driver = webdriver.Chrome(service=ChromiumService("/usr/bin/chromedriver"), options=options) - self.log(f"Success! {self.driver.session_id}") - elif browser == 'firefox': - self.log("Creating firefox web driver...") - from selenium import webdriver - from selenium.webdriver.firefox.service import Service as FirefoxService - from webdriver_manager.firefox import GeckoDriverManager - # TODO: as far as I can tell, this installs a non_ARM binary and fails with - # "OSError: [Errno 8] Exec format error: '/home/raam/.wdm/drivers/geckodriver/linux64/v0.33.0/geckodriver'" - self.driver = webdriver.Firefox(service=FirefoxService(GeckoDriverManager().install())) - self.log(f"Success! {self.driver.session_id}") - else: - raise ValueError(f"Browser {browser} not supported") - - def run(self, context: ExecutionContext): - if not self.architecture_supported(): - return - - url = self.get_config('url', None) - if not url: - raise ValueError("URL not provided in app config") - - if self.driver is None: - self.init_driver() - if not self.driver: - raise ValueError("Selenium driver not initialized") - - scaling_factor = float(self.get_config('scaling_factor', 1)) - width, height = int(context.image.width / scaling_factor), int(context.image.height / scaling_factor) - self.log(f"Fetching {url} at {width}x{height} @{scaling_factor}x") - self.driver.set_window_size(width, height) - - if self.driver.current_url == url: - self.log(f"Refreshing url: {url}") - self.driver.refresh() - else: - self.log(f"Setting url: {url}") - self.driver.get(url) - - wait_selector = self.get_config('wait_selector', '') - if wait_selector: - timeout = 60 - self.log(f"Waiting for selector: {wait_selector} (timeout: {timeout}sec)") - try: - from selenium.webdriver.support import expected_conditions as EC - from selenium.webdriver.support.ui import WebDriverWait - element = WebDriverWait(self.driver, timeout).until( - EC.visibility_of_element_located(("css selector", wait_selector)) - ) - except Exception as e: - self.log(f"Error waiting for selector, exiting: {e}") - return - - wait_delay = int(self.get_config('wait_delay', '0')) - if wait_delay > 0: - self.log(f"Waiting {wait_delay} extra seconds") - self.driver.implicitly_wait(wait_delay) - - self.log(f"Saving screenshot of current url: {self.driver.current_url}") - - content = self.driver.get_screenshot_as_png() - try: - self.log(f"Screenshot taken, size: {len(content)} bytes") - except TypeError as e: - self.log(f"Screenshot error! Type: {type(content)}. Error: {e}") - - context.image = Image.open(io.BytesIO(content)) diff --git a/frameos-python/index.html b/frameos-python/index.html deleted file mode 100644 index 21f52aff..00000000 --- a/frameos-python/index.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - FrameOS Frame - - - - - -
-
- -
-

Image Display

- - -

Logs

-

-
- - - -