diff --git a/g4f/Provider/needs_auth/PuterJS.py b/g4f/Provider/needs_auth/PuterJS.py index 2a1cfb09..656c8b99 100644 --- a/g4f/Provider/needs_auth/PuterJS.py +++ b/g4f/Provider/needs_auth/PuterJS.py @@ -20,6 +20,7 @@ class PuterJS(AsyncGeneratorProvider, ProviderModelMixin): login_url = "https://github.com/HeyPuter/puter-cli" api_endpoint = "https://api.puter.com/drivers/call" working = True + active_by_default = True needs_auth = True default_model = 'gpt-4o' diff --git a/g4f/api/__init__.py b/g4f/api/__init__.py index 7d117ee3..154eee28 100644 --- a/g4f/api/__init__.py +++ b/g4f/api/__init__.py @@ -681,25 +681,13 @@ class Api: other_name = os.path.join(get_media_dir(), os.path.basename(quote_plus(filename))) if os.path.isfile(other_name): target = other_name - if thumbnail and has_pillow: - thumbnail_dir = os.path.join(get_media_dir(), "thumbnails") - thumbnail = os.path.join(thumbnail_dir, filename) - try: - if not os.path.isfile(thumbnail): - image = Image.open(target) - os.makedirs(thumbnail_dir, exist_ok=True) - image = process_image(image) - image.save(os.path.join(thumbnail_dir, filename)) - except Exception as e: - logger.exception(e) - if os.path.isfile(thumbnail): - target = thumbnail + result = target ext = os.path.splitext(filename)[1][1:] mime_type = EXTENSIONS_MAP.get(ext) stat_result = SimpleNamespace() stat_result.st_size = 0 - if os.path.isfile(target): - stat_result.st_size = os.stat(target).st_size + if os.path.isfile(result): + stat_result.st_size = os.stat(result).st_size stat_result.st_mtime = int(f"{filename.split('_')[0]}") if filename.startswith("1") else 0 headers = { "cache-control": "public, max-age=31536000", @@ -742,10 +730,22 @@ class Api: debug.error(f"Download failed: {source_url}") debug.error(e) return RedirectResponse(url=source_url) - if not os.path.isfile(target): + if thumbnail and has_pillow: + thumbnail_dir = os.path.join(get_media_dir(), "thumbnails") + thumbnail = os.path.join(thumbnail_dir, filename) + try: + if not os.path.isfile(thumbnail): + image = Image.open(target) + os.makedirs(thumbnail_dir, exist_ok=True) + process_image(image, save=os.path.join(thumbnail_dir, filename)) + except Exception as e: + logger.exception(e) + if os.path.isfile(thumbnail): + result = thumbnail + if not os.path.isfile(result): return ErrorResponse.from_message("File not found", HTTP_404_NOT_FOUND) async def stream(): - with open(target, "rb") as file: + with open(result, "rb") as file: while True: chunk = file.read(65536) if not chunk: diff --git a/g4f/gui/server/backend_api.py b/g4f/gui/server/backend_api.py index f76db802..d5b64f29 100644 --- a/g4f/gui/server/backend_api.py +++ b/g4f/gui/server/backend_api.py @@ -383,8 +383,7 @@ class Backend_Api(Api): image = Image.open(copyfile) thumbnail_dir = os.path.join(bucket_dir, "thumbnail") os.makedirs(thumbnail_dir, exist_ok=True) - image = process_image(image) - image.save(os.path.join(thumbnail_dir, filename)) + process_image(image, save=os.path.join(thumbnail_dir, filename)) except Exception as e: logger.exception(e) elif is_supported: diff --git a/g4f/image/__init__.py b/g4f/image/__init__.py index e50cbf2a..e4c9430c 100644 --- a/g4f/image/__init__.py +++ b/g4f/image/__init__.py @@ -7,6 +7,8 @@ import base64 from io import BytesIO from pathlib import Path from typing import Optional +from collections import defaultdict + try: from PIL.Image import open as open_image, new as new_image from PIL.Image import FLIP_LEFT_RIGHT, ROTATE_180, ROTATE_270, ROTATE_90 @@ -14,9 +16,15 @@ try: has_requirements = True except ImportError: has_requirements = False +try: + import piexif + has_piexif = True +except ImportError: + has_piexif = False from ..typing import ImageType, Image from ..errors import MissingRequirementsError +from .. import debug EXTENSIONS_MAP: dict[str, str] = { # Image @@ -201,6 +209,11 @@ def extract_data_uri(data_uri: str) -> bytes: data = base64.b64decode(data) return data +def get_orientation_key() -> int: + for tag, value in ExifTags.TAGS.items(): + if value == 'Orientation': + return tag + def get_orientation(image: Image) -> int: """ Gets the orientation of the given image. @@ -212,12 +225,9 @@ def get_orientation(image: Image) -> int: int: The orientation value. """ exif_data = image.getexif() if hasattr(image, 'getexif') else image._getexif() - if exif_data: - for tag, value in ExifTags.TAGS.items(): - if value == 'Orientation': - return exif_data.get(tag) + return exif_data.get(get_orientation_key()) if exif_data else None -def process_image(image: Image, new_width: int = 800, new_height: int = 800) -> Image: +def process_image(image: Image, new_width: int = 800, new_height: int = 800, save: str = None) -> Image: """ Processes the given image by adjusting its orientation and resizing it. @@ -232,6 +242,7 @@ def process_image(image: Image, new_width: int = 800, new_height: int = 800) -> # Fix orientation orientation = get_orientation(image) if orientation: + debug.log(f"Image orientation: {orientation}") if orientation > 4: image = image.transpose(FLIP_LEFT_RIGHT) if orientation in [3, 4]: @@ -251,6 +262,16 @@ def process_image(image: Image, new_width: int = 800, new_height: int = 800) -> # Convert to RGB for jpg format elif image.mode != "RGB": image = image.convert("RGB") + # Remove EXIF data + if has_piexif and save is not None: + try: + exif_dict = piexif.load(image.info["exif"]) + except KeyError: + exif_dict = defaultdict(dict) + if exif_dict['Exif']: + exif_dict['Exif'] = {} + elif save is not None: + image.save(save) return image def to_bytes(image: ImageType) -> bytes: