-
Notifications
You must be signed in to change notification settings - Fork 121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat 4122 compress image python #160
base: main
Are you sure you want to change the base?
Changes from 5 commits
8536bc4
5982fc0
547da9d
9c2d8ff
57173ec
1507bcf
2387de0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# 🖼️ Compress Image with TinyPNG and KrakenIO | ||
|
||
A Python Cloud Function for compressing images without losing quality using [Tinypng API](https://tinypng.com/) and [KrakenIO](https://kraken.io/). | ||
|
||
|
||
Example input with Tinypng: | ||
```json | ||
{ | ||
"provider":"tinypng", | ||
"image":"iVBORw0KGgoAAAANSUhEUgAAAaQAAALiCAY...QoH9hbkTPQAAAABJRU5ErkJggg==" | ||
} | ||
``` | ||
Example input with KrakenIO: | ||
```json | ||
{ | ||
"provider":"krakenio", | ||
"image":"iVBORw0KGgoAAAANSUhEUgAAAaQAAALiCAY...QoH9hbkTPQAAAABJRU5ErkJggg==" | ||
} | ||
``` | ||
|
||
Example output: | ||
```json | ||
{ | ||
"success":true, | ||
"image":"iVBORw0KGgoAAAANSUhE...o6Ie+UAAAAASU5CYII=" | ||
} | ||
``` | ||
Example error output: | ||
```json | ||
{ | ||
"success":false, | ||
"image":"iVBORw0KGgoAAAANSUhE...o6Ie+UAAAAASU5CYII=" | ||
} | ||
``` | ||
|
||
## 📝 Environment Variables | ||
|
||
List of environment variables used by this cloud function: | ||
|
||
- **API_KEY** - Tinypng API Key or KrakenIO API Key | ||
- **SECRET_API_KEY** - KrakenIO Secret API Key | ||
|
||
|
||
ℹ️ _Create your TinyPNG API key at https://tinypng.com/developers_. <br> | ||
ℹ️ _Create your KrakenIO API key at https://kraken.io/docs/getting-started_. <br> | ||
|
||
|
||
## 🚀 Deployment | ||
|
||
1. Clone this repository, and enter this function folder: | ||
|
||
```bash | ||
git clone https://github.com/open-runtimes/examples.git | ||
cd examples/python/compress-image | ||
``` | ||
|
||
2. Enter this function folder and build the code: | ||
```bash | ||
docker run --rm --interactive --tty --volume $PWD:/usr/code openruntimes/python:v2-3.10 sh /usr/local/src/build.sh | ||
``` | ||
As a result, a `code.tar.gz` file will be generated. | ||
|
||
3. Start the Open Runtime: | ||
```bash | ||
docker run -p 3000:3000 -e INTERNAL_RUNTIME_KEY=secret-key -e INTERNAL_RUNTIME_ENTRYPOINT=main.py --rm --interactive --tty --volume $PWD/code.tar.gz:/tmp/code.tar.gz:ro openruntimes/python:v2-3.10 sh /usr/local/src/start.sh | ||
``` | ||
|
||
> Make sure to replace `YOUR_API_KEY` with your key. | ||
Your function is now listening on port `3000`, and you can execute it by sending `POST` request with appropriate authorization headers. To learn more about runtime, you can visit Python runtime [README](https://github.com/open-runtimes/open-runtimes/tree/main/openruntimes/python:v2-3.10). | ||
4. Run the cURL function to send request. | ||
>TinyPNG Curl Example (Supports only API_KEY in Environment Variables) | ||
```bash | ||
curl http://localhost:3000/ -H "X-Internal-Challenge: secret-key" -H "Content-Type: application/json" -d '{"payload": {"provider": "tinypng", "image": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj+L+U4T8ABu8CpCYJ1DQAAAAASUVORK5CYII="}, "variables": {"API_KEY": "<YOUR_API_KEY>"}}' | ||
``` | ||
>KrakenIO Curl Example (Supports API_KEY and SECRET_API_KEY in Environment Variables) | ||
```bash | ||
curl http://localhost:3000/ -H "X-Internal-Challenge: secret-key" -H "Content-Type: application/json" -d '{"payload": {"provider": "krakenio", "image": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj+L+U4T8ABu8CpCYJ1DQAAAAASUVORK5CYII="}, "variables": {"API_KEY": "<YOUR_API_KEY>", "SECRET_API_KEY": "<YOUR_SECRET_API_KEY>"}}' | ||
``` | ||
## 📝 Notes | ||
- This function is designed for use with Appwrite Cloud Functions. You can learn more about it in [Appwrite docs](https://appwrite.io/docs/functions). | ||
- This example is compatible with Python 3.10. Other versions may work but are not guaranteed to work as they haven't been tested. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
""" Compress image function using Tinypng and Krakenio API.""" | ||
import base64 | ||
import json | ||
import tinify | ||
import requests | ||
|
||
KRAKEN_API_ENDPOINT = "https://api.kraken.io/v1/upload" | ||
KRAKEN_USER_AGENT = ( | ||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64)AppleWebKit/" | ||
"537.36(KHTML, like Gecko)Chrome/40.0.2214.85 Safari/537.36" | ||
) | ||
|
||
|
||
def krakenio_impl(variables): | ||
""" | ||
Implements image optimization using the Kraken.io API. | ||
|
||
Input: | ||
variables (dict): A dictionary containing the | ||
required variables for optimization. | ||
Returns: | ||
optimized_image (bytes): decoded optimized image. | ||
Raises: | ||
raise_for_status (method): raise an HTTPError if the HTTP request | ||
returned an unsuccessful status code. | ||
""" | ||
# Headers for post request | ||
headers = {"User-Agent": KRAKEN_USER_AGENT} | ||
# Image that we will pass in | ||
files = {"file": variables["decoded_image"]} | ||
# Parameters for post request | ||
params = { | ||
"auth": { | ||
"api_key": variables["api_key"], | ||
"api_secret": variables["api_secret_key"] | ||
}, | ||
"wait": True, # Optional: Wait for the optimization to complete. | ||
"dev": False, # Optional: Set to false to enter user mode. | ||
} | ||
response = requests.post( | ||
url=KRAKEN_API_ENDPOINT, | ||
headers=headers, | ||
files=files, | ||
data={"data": json.dumps(params)}, | ||
timeout=10, | ||
) | ||
# Check status code of response | ||
response.raise_for_status() | ||
data = response.json() | ||
# Response unsuccessful, raise error | ||
if not data["success"]: | ||
raise ValueError("KrakenIO was not able to compress image.") | ||
# Response successful, parse the response | ||
optimized_url = data["kraked_url"] | ||
optimized_image = requests.get(optimized_url, timeout=10).content | ||
return optimized_image | ||
|
||
|
||
def tinypng_impl(variables): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. methods are more commonly named as verbs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We fixed this one. |
||
""" | ||
Implements image optimization using the Tinypng API. | ||
|
||
Input: | ||
variables (dict): A dictionary containing the required variables | ||
for optimization. Includes api_key and decoded_image. | ||
Returns: | ||
tinify.from_buffer().tobuffer() (bytes): decoded optimized image. | ||
Raises: | ||
tinify.error (method): raised if tinify fails to compress image. | ||
""" | ||
tinify.key = variables["api_key"] | ||
return tinify.from_buffer(variables["decoded_image"]).to_buffer() | ||
|
||
|
||
def validate_request(req): | ||
""" | ||
Validates the request and extracts the necessary information. | ||
|
||
Input: | ||
req (json): The request object containing the payload and variables. | ||
Returns: | ||
result (dict): Contains the validated request information. | ||
Raises: | ||
ValueError: If any required value is missing or invalid. | ||
""" | ||
# Check if payload is empty | ||
if not req.payload: | ||
raise ValueError("Missing payload") | ||
# Accessing provider from payload | ||
if not req.payload.get("provider"): | ||
raise ValueError("Missing provider") | ||
# Check if payload is not empty | ||
if not req.variables: | ||
raise ValueError("Missing variables.") | ||
# Accessing api_key from variables | ||
if not req.variables.get("API_KEY"): | ||
raise ValueError("Missing API_KEY") | ||
# Accessing encoded image from payload | ||
if not req.payload.get("image"): | ||
raise ValueError("Missing encoding image") | ||
result = { | ||
"provider": req.payload.get("provider").lower(), | ||
"api_key": req.variables.get("API_KEY"), | ||
"decoded_image": base64.b64decode(req.payload.get("image")), | ||
} | ||
# Get secret key | ||
if req.payload.get("provider") == "krakenio": | ||
if not req.variables.get("SECRET_API_KEY"): | ||
raise ValueError("Missing api secret key.") | ||
result["api_secret_key"] = req.variables.get("SECRET_API_KEY") | ||
return result | ||
|
||
|
||
IMPLEMENTATIONS = { | ||
"krakenio": krakenio_impl, | ||
"tinypng": tinypng_impl, | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This approach to defining different implementations is a little odd. An object-oriented approach is more common. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We fixed this one. |
||
|
||
|
||
def main(req, res): | ||
""" | ||
The main function that runs validate_request and calls IMPLEMENTATIONS. | ||
|
||
Input: | ||
req (json): The request object. | ||
res (json): The response object. | ||
|
||
Returns: | ||
res (json): A JSON response containing the optimization results. | ||
""" | ||
try: | ||
variables = validate_request(req) | ||
except (ValueError) as value_error: | ||
return res.json({ | ||
"success": False, | ||
"error": f"{value_error}", | ||
}) | ||
try: | ||
optimized_image = IMPLEMENTATIONS[variables["provider"]](variables) | ||
except Exception as error: | ||
return res.json({ | ||
"success": False, | ||
"error": f"{type(error).__name__}: {error}", | ||
}) | ||
return res.json({ | ||
"success": True, | ||
"image": base64.b64encode(optimized_image).decode(), | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
tinify==1.6.0 | ||
requests==2.31.0 | ||
parameterized==0.9.0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
''' | ||
API Key for tinyPNG and KrakenIO are stored here | ||
|
||
You should be in python/compress-image directory to run test_main.py | ||
''' | ||
API_KEY_TINYPNG = None | ||
API_KEY_KRAKENIO = None | ||
SECRET_API_KEY_KRAKENIO = None |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
iVBORw0KGgoAAAANSUhEUgAAABIAAAAUCAIAAAAP9fodAAABz0lEQVR42mP4TxZgoEjbtwMH3nV1vW1u/rh06d/PnyGCa8+8Kll1C4gWHX2GRdvLwsLHnp4PbWweWls/dnF5Ghr6+9kz155zhg0nVCqOApF+3XEgF0Xb1337gEofGBoioyN1faq5uyQLDyEjZDsZ3rS2PrK2RtO2xz9eN3sLmrbU+VcR2l7X1DyytETTdtA9RD9rE5q20GkXEdo+zp//yN4eTdvSmDSN3B3SERNVPdKdfHyT4ryq67IWrl6F0Pbn3bsnPj7Ievb52UT1hMlahqjomWqpKeioydoZyMQ5Sb9faPrjRCsiJH/dvftuwoSHyQlnA1yXhJkHdTjolFrIYYCV6VyfFxn+fnwIEW9AO0+e2Z6zINF5kpPhXBdlLy1MbZmuwh+nS/043Y2SSvY+OOK7Jl5/ngsQKdmpY2oLtRb7OEX4x+EqFG2nn19M2FoI0aYarIuprSZA8NNsxV+XZqNo+/Dj06QzcyHadDrt0PT4molfahb+tiv93+cn6En50ccncy8uz9hRHrohzbM5xCvc1c3eyMlMLc5N+Wi7wff9hX/eXseeA4B2Xn97++yLSxdfXjtx6dSp44eObl9y5cDSP0+P/v1wj0oZZ3BrAwARK/tUoZKzbgAAAABJRU5ErkJggg== |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
iVBORw0KGgoAAAANSUhEUgAAABIAAAAUCAMAAAC3SZ14AAABLFBMVEX///8eHh7gMTEZccIvnkTiOjrw+PL87u6ixeb+8uF0qtrM6NJFjM4wf8gkeMX85MT2v78mbbkuarPujY3thITkSkrjRETiPT1AMyFeRB8rJh7ymR398/OtzOr75ubi8eT63d3X7dv87dfR6dZln9VVltHI0sq94MTKx8LJzLehq6qd0afUwKXxpaXFt6Txn5+SzJ360psbWJHEfo74y4v4yIXrfHz4w3pouHf3v3FgtXHpcXF7fmr2umclSGdVsGalXGZsoGHhY2BNrF+8T19ajFblVVVJg1TBR1QdOVRCTE1iXkpKV0XNUEUxnURDkkJSiEAsiT6+TjwsdTikVjfvoTUkLjVGPzLzojHFhzAlUy0jSioiPidNNhdVOxbxlBPSgxNpRRN8UBGpZwp9EYrKAAAAs0lEQVQY063KRXYCQBAA0UkyBHd3d3d3d3eH+9+B1z0cgAW1/K/Ip8mUqrCJELNAwHtLUa3laDoWkVgiEYvYo/mFkvI/CD8VB6mlQHIBZRj1GLmBolqkmjxRGQxLASCuGmTR7m8MBp1+nQazKRvj1Wz5gz13Uvz89enkyOhuTBHI26T0xOhyzSIJC5QeGD2McaT/GKVzlPOtzCeYNZirdkdbnX6fdyDAZxc6Pb5ISArPN3sB8REVVO/DWBYAAAAASUVORK5CYII= |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be 3 different variables for the different values so that a function can be deployed with all tinypng and krakenio variables and then executed with either tinypng or krakenio.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello, we recently added a commit addressing the 3 variables for the different values. It should show now.