diff --git a/.travis.yml b/.travis.yml index abc300c0..a363b6f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,4 +39,4 @@ script: notifications: email: recipients: - - sdk_developers@cloudinary.com + - sdk_developers@cloudinary.com \ No newline at end of file diff --git a/samples/content-creation/README.md b/samples/content-creation/README.md new file mode 100644 index 00000000..3c9732db --- /dev/null +++ b/samples/content-creation/README.md @@ -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://:@ +``` + +For Windows (Command Prompt/PowerShell): +```bash +set CLOUDINARY_URL=cloudinary://:@ +``` + +### 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//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://:@ +``` +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! \ No newline at end of file diff --git a/samples/content-creation/cloudinary-ai-post-generator.py b/samples/content-creation/cloudinary-ai-post-generator.py new file mode 100644 index 00000000..e4d80417 --- /dev/null +++ b/samples/content-creation/cloudinary-ai-post-generator.py @@ -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) \ No newline at end of file diff --git a/samples/basic/lake.jpg b/samples/content-creation/lake.jpg similarity index 100% rename from samples/basic/lake.jpg rename to samples/content-creation/lake.jpg diff --git a/samples/content-creation/settings.py b/samples/content-creation/settings.py new file mode 100644 index 00000000..e0add057 --- /dev/null +++ b/samples/content-creation/settings.py @@ -0,0 +1,7 @@ +import cloudinary + +cloudinary.config( + cloud_name = "xxx", + api_key = "xxx", + api_secret = "xxx" +) diff --git a/samples/content-creation/static/index.html b/samples/content-creation/static/index.html new file mode 100644 index 00000000..331180aa --- /dev/null +++ b/samples/content-creation/static/index.html @@ -0,0 +1,77 @@ + + + + + + Image Upload + + + +

Upload Image to Cloudinary

+
+ + + + + + +
+
+ + + + \ No newline at end of file diff --git a/tox.ini b/tox.ini index 14404d1a..62a2d38a 100644 --- a/tox.ini +++ b/tox.ini @@ -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 \ No newline at end of file