Skip to content
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

Sample Project for Automatic Content Creation #403

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ script:
notifications:
email:
recipients:
- sdk_developers@cloudinary.com
- sdk_developers@cloudinary.com
145 changes: 145 additions & 0 deletions samples/content-creation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Social Media Content Creation API with Cloudinary

This project demonstrates a simple Flask API for social media content creation. The API allows users to upload images, apply transformations suitable for social media (like resizing, cropping, and adjusting image formats), and optionally set custom public IDs. Additionally, the API provides functionality to clean up old uploads by tag.

## Features

- Upload images for social media content creation.
- Apply transformations such as resizing, cropping, and format adjustment for optimal display on platforms like Instagram, Facebook, and Twitter.
- Option to assign custom public IDs for better image management.
- Cleanup of previously uploaded images by tag.
- **Image Uploading**: Easily upload images to Cloudinary for use in your content.
- **Image Transformation**: Utilize powerful transformation capabilities to manipulate images directly in your content generation process.
- **Content Delivery**: Benefit from fast and efficient image delivery through Cloudinary's global CDN.
- **AI Integration**: Enhance your content generation logic by integrating AI models for dynamic content creation.
- **Dynamic Content Creation**: Create personalized content based on user preferences or trends.

## Prerequisites

Before running this project, ensure you have:

1. [Python 3.x](https://www.python.org/downloads/)
2. A [Cloudinary account](https://cloudinary.com/users/register/free)
3. Cloudinary Python SDK installed via pip.

## Setup Instructions

### 1. Install Dependencies

After cloning or downloading this repository, install the required packages using `pip`:

```bash
pip install flask cloudinary
```

### 2. Configure Cloudinary

You need to configure the CLOUDINARY_URL environment variable with your Cloudinary credentials. You can find your credentials in the Cloudinary Management Console.

For Linux/MacOS (bash/zsh):
```bash
export CLOUDINARY_URL=cloudinary://<API-Key>:<API-Secret>@<Cloud-name>
```

For Windows (Command Prompt/PowerShell):
```bash
set CLOUDINARY_URL=cloudinary://<API-Key>:<API-Secret>@<Cloud-name>
```

### 3. Running the Flask App

Start the Flask server by running:

```bash
python app.py
```

The server will be available at http://127.0.0.1:5000/.

## Usage

### 1. Uploading an Image for Social Media

To upload an image with transformations applied (suitable for social media), send a POST request to the /generate_post endpoint with the image file. You can optionally provide a public_id for the image.

- **Endpoint**: /generate_post
- **Method**: POST
- **Parameters**:
- image (required): The image file to upload.
- public_id (optional): Custom public ID for the image.

**Image Transformations**:

The API will automatically resize the image to a 1:1 aspect ratio (200x200px), perfect for profile pictures, thumbnails, or other social media purposes.

**Example with cURL**:

```bash
curl -X POST http://localhost:5000/generate_post \
-F "image=@/path/to/your/social_media_image.jpg" \
-F "public_id=my_custom_id"
```

**Example Response**:
```bash
{
"status": "success",
"image_url": "http://res.cloudinary.com/<cloud-name>/image/upload/v12345678/my_custom_id.jpg"
}
```
The image is transformed (resized to 200x200, cropped to fill), optimized for social media platforms.

### 2. Cleaning Up Uploaded Images

To delete all images uploaded with the default tag (set as python_sample_basic), you can run:

```bash
python app.py cleanup
```

This will delete all images tagged under DEFAULT_TAG.

## Recommended Image Transformations for Social Media

- **Profile Pictures/Thumbnails**: Resize to 200x200px with a 1:1 aspect ratio.
- **Banners**: Crop to 1200x400px for optimal display on platforms like Twitter.
- **Story Images**: Resize to 1080x1920px (vertical aspect ratio) for Instagram or Snapchat stories.

**Example Transformations in the Code**:

Resize and Crop: Automatically applied transformation in the API:

```bash
url, options = cloudinary_url(
response['public_id'],
format=response['format'],
width=200,
height=200,
crop="fill"
)
```

This resizes the uploaded image to 200x200 pixels and crops it to fit.

## Additional Functionality

- **Setting Custom Public IDs**: You can assign custom public IDs for uploaded images, which is useful for managing content more effectively.
- **Dynamic Transformations**: Feel free to modify the transformations in upload_file() to match specific platform requirements (e.g., square thumbnails, vertical or horizontal banners, etc.).

### Environment Variables (Optional)

If you prefer, you can store the CLOUDINARY_URL in a .env file to make environment configuration easier:

```bash
CLOUDINARY_URL=cloudinary://<API-Key>:<API-Secret>@<Cloud-name>
```
After creating the .env file, load it by running:
```bash
source .env
```

## Conclusion

This project is designed to help you quickly set up an image uploading API tailored to social media content creation needs. It handles image transformations, easy uploads, and content management using Cloudinary. By leveraging the features of `pycloudinary`, you can create a robust content generation system that enhances your posts with relevant images.

Good luck and happy posting!
134 changes: 134 additions & 0 deletions samples/content-creation/cloudinary-ai-post-generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/usr/bin/env python
import os
import sys

from cloudinary.api import delete_resources_by_tag, resources_by_tag
from cloudinary.uploader import upload
from cloudinary.utils import cloudinary_url
from flask import Flask, request, jsonify

# Initialize Flask app
app = Flask(__name__)

# Config
os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '.'))
if os.path.exists('settings.py'):
exec(open('settings.py').read())

DEFAULT_TAG = "python_sample_basic"


def dump_response(response):
"""Function to print and handle upload response"""
print("Upload response:")
for key in sorted(response.keys()):
print(" %s: %s" % (key, response[key]))


def upload_file(file_path, public_id=None, mood=None, theme=None):
"""Upload a file to Cloudinary with options for custom public ID and transformations"""
print(f"--- Uploading {file_path}")

# Define transformations based on mood
transformations = []

if mood == "happy":
transformations.append({"effect": "brightness:30"}) # Increase brightness
elif mood == "sad":
transformations.append({"effect": "grayscale"}) # Convert to grayscale

# Add text overlay based on theme
if theme:
transformations.append({
"overlay": {
"font_family": "Arial",
"font_size": 20,
"text": f"{theme.capitalize()} - {mood.capitalize()}",
"text_color": "white"
},
"gravity": "north",
"y": 10
})

# Upload with transformations
response = upload(
file_path,
public_id=public_id,
transformation=transformations,
tags=DEFAULT_TAG
)

dump_response(response)

url, options = cloudinary_url(
response['public_id'],
format=response['format'],
width=200,
height=150,
crop="fill"
)
print("Image URL: " + url)
return url


@app.route('/generate_post', methods=['POST'])
def generate_post():
"""API endpoint to handle post generation and image upload"""
try:
# Get image file from request
image = request.files.get('image')

if not image:
return jsonify({"error": "No image file provided"}), 400

# Create uploads directory if it doesn't exist
uploads_dir = os.path.join(os.path.dirname(__file__), 'uploads')
os.makedirs(uploads_dir, exist_ok=True)

# Save image locally
file_path = os.path.join(uploads_dir, image.filename)
image.save(file_path)

# Upload file to Cloudinary
public_id = request.form.get('public_id', None)
mood = request.form.get('mood', None)
theme = request.form.get('theme', None)
image_url = upload_file(file_path, public_id=public_id, mood=mood, theme=theme)

# Clean up the local file after upload
os.remove(file_path)

# Return response
return jsonify({"status": "success", "image_url": image_url})

except Exception as e:
print(f"Error: {str(e)}") # Log the error
return jsonify({"error": str(e)}), 500


def cleanup():
"""Cleanup resources by tag"""
response = resources_by_tag(DEFAULT_TAG)
resources = response.get('resources', [])
if not resources:
print("No images found")
return
print(f"Deleting {len(resources)} images...")
delete_resources_by_tag(DEFAULT_TAG)
print("Done!")


@app.route('/')
def index():
return app.send_static_file('index.html')


if __name__ == '__main__':
if len(sys.argv) > 1:
if sys.argv[1] == 'upload':
upload_file("sample.jpg")
elif sys.argv[1] == 'cleanup':
cleanup()
else:
print("--- Starting Flask server ---")
app.run(debug=True)
File renamed without changes
7 changes: 7 additions & 0 deletions samples/content-creation/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import cloudinary

cloudinary.config(
cloud_name = "xxx",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These values set here break things for someone who is using CLOUDINARY_URL environment variable.
You can keep this file, just comment it out.

api_key = "xxx",
api_secret = "xxx"
)
77 changes: 77 additions & 0 deletions samples/content-creation/static/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Upload</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
form {
margin-bottom: 20px;
}
input[type="file"], input[type="text"], select {
margin-bottom: 10px;
width: 100%;
padding: 8px;
box-sizing: border-box;
}
button {
padding: 10px 15px;
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
#response {
margin-top: 20px;
}
</style>
</head>
<body>
<h1>Upload Image to Cloudinary</h1>
<form id="uploadForm">
<input type="file" name="image" accept="image/*" required>
<input type="text" name="public_id" placeholder="Public ID (optional)">
<input type="text" name="mood" placeholder="Mood (e.g., happy, sad)" required>
<input type="text" name="theme" placeholder="Theme (e.g., nature, technology)" required>
<select name="aspect_ratio" required>
<option value="1:1">1:1</option>
<option value="16:9">16:9</option>
<option value="4:3">4:3</option>
<option value="2:1">2:1</option>
</select>
<button type="submit">Upload</button>
</form>
<div id="response"></div>

<script>
document.getElementById('uploadForm').addEventListener('submit', function(event) {
event.preventDefault();
const formData = new FormData(this);
fetch('/generate_post', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
const responseDiv = document.getElementById('response');
if (data.error) {
responseDiv.innerHTML = `<p style="color: red;">Error: ${data.error}</p>`;
} else {
responseDiv.innerHTML = `<p style="color: green;">Success! Image URL: <a href="${data.image_url}" target="_blank">${data.image_url}</a></p>`;
responseDiv.innerHTML += `<p>Generated Content: ${data.content}</p>`;
}
})
.catch(error => {
document.getElementById('response').innerHTML = `<p style="color: red;">Error: ${error.message}</p>`;
});
});
</script>
</body>
</html>
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ deps =
django40: Django>=4.0,<4.1
django41: Django>=4.1,<4.2
setenv =
DJANGO_SETTINGS_MODULE=django_tests.settings
DJANGO_SETTINGS_MODULE=django_tests.settings