AI Video Generator
Project Overview:
I’m proud to present a state-of-the-art digital content automation project, meticulously designed to streamline the creation and distribution of multimedia content across various online platforms. This project embodies a fusion of cutting-edge technologies and sophisticated programming techniques, aimed at revolutionizing how digital content is handled and published.
Key Highlights:
- Versatile Platform Integration: The project seamlessly interfaces with major platforms such as Instagram, YouTube, and WordPress, utilizing their APIs to automate content posting and management processes. This integration demonstrates a deep understanding of each platform’s unique features and requirements.
- Advanced Multimedia Processing: Leveraging powerful libraries for video editing, image processing, and audio management, the project offers comprehensive multimedia handling capabilities. From custom video effects to audio transcription, every aspect is crafted to ensure high-quality content creation.
- AI-Driven Content Generation: At the heart of this project lies the integration of AI services, including OpenAI’s GPT models, to generate engaging and relevant text content. This AI-powered approach ensures creative and contextually appropriate content for various media.
- Robust and Reliable: Meticulous error handling and a focus on robustness are integral to the project, ensuring reliability and smooth operation across all functionalities.
- User-Centric Design: With an emphasis on user interaction and feedback, the project offers an intuitive experience, guiding users through each step of the process and providing clear feedback and logging.
This project represents my commitment to delivering high-quality, innovative solutions in digital content automation. It is tailored to meet the dynamic needs of content creators, marketers, and businesses aiming to enhance their digital presence efficiently and effectively.
This is an example of the a video generated from my Python script. The image displayed is based on an AI description generated from my specified topic. The caption and script is also generated from AI based on my topic. I program the watermark and shadow gradient in Python as well. Please see my scripts below for more information
main.py
import logging
from flask import Flask, request, jsonify
from flask_cors import CORS
from contentgenerator import ProcessPosts, Generatepost
def generate_article():
try:
#generator = ProcessPosts()
list = []
processpost = ProcessPosts()
result = processpost.process_post()
# Additional code...
#return jsonify(result)
except Exception as e:
print("Error occurred: " + str(e))
raise
if __name__ == '__main__':
try:
generate_article()
except Exception as e:
print(e)
Video_Hanlding.py
- Integration with Multiple Libraries:
- Utilizes a range of libraries like
moviepy
,PIL
,pydub
, andgoogle.cloud
for various multimedia operations. Each library has its specific use, like editing video clips, processing images, handling audio, and speech-to-text conversion.
- Utilizes a range of libraries like
- Video and Audio Processing:
- Involves complex operations such as adding gradients to images, converting audio files between different formats (MP3 to FLAC), and re-encoding videos to be compatible with specific platforms like Instagram.
- Generates audio from text using Google’s Text-to-Speech API, which requires handling API calls and processing the audio output.
- Speech Recognition:
- Implements speech-to-text functionality using Google’s Speech-to-Text API for transcribing audio, which involves handling and parsing the API’s response, and managing Google Cloud credentials securely.
- Video Composition and Effects:
- Creates composite video clips by combining various elements like base images, watermarks, text subtitles, and audio tracks. This requires a deep understanding of video composition techniques.
- Implements custom video effects, such as zoom effects on images and frames, requiring mathematical calculations and image processing skills.
- Subtitle Generation and Synchronization:
- Generates subtitles from transcribed audio, involving timing synchronization between the spoken words in the audio and their textual representation in the video.
- File Management and Web Integration:
- Handles file operations, including reading and writing files, and integrates with web browsers for previewing video clips.
- Custom Utility Functions:
- Includes custom utility functions like
ease_in_out
for smooth transition effects in videos, demonstrating an understanding of animation techniques.
- Includes custom utility functions like
- Error Handling and Robustness:
- The script includes error handling and validation checks to ensure that each step in the video creation process is executed correctly.
- Modularity and Reusability:
- The class is designed to be modular, allowing different methods to be reused for various video creation tasks.
- Platform-Specific Considerations:
- Adapts videos for specific platforms (like Instagram) in terms of format and encoding, which requires knowledge of platform-specific constraints and standards.
In summary, video_handling.py
is a sophisticated and comprehensive script for creating and processing videos, integrating a wide range of multimedia processing techniques and external libraries. It demonstrates a high level of proficiency in video and audio processing, API utilization, and programming for multimedia content creation.
sold
contentgenerator.py
- Integration with AI and Web Services:
- The script integrates with AI services (like OpenAI’s GPT models) for generating text, image descriptions, and video scripts. This requires handling API requests, processing responses, and managing errors.
- It interacts with external web services like WordPress, Instagram, and YouTube for content uploading, which involves authentication, media handling, and API communication.
- Image and Video Processing:
- The script includes functionalities for image editing (like overlaying text on images) and video creation (like adding zoom effects and subtitles). This requires a good understanding of image and video processing libraries.
- Managing different media formats and ensuring they meet platform-specific requirements (like aspect ratios and file sizes for Instagram and YouTube) adds another layer of complexity.
- Data Management and Workflow Control:
- It involves reading and writing data to Excel files, which requires handling file I/O operations and ensuring data integrity.
- The script manages a workflow that includes generating topics, creating content, and posting it online. This flow requires careful error handling and data synchronization across different stages and platforms.
- Modular Design and Class Interactions:
- The script is structured into classes (
Generatepost
andProcessPosts
) with specific responsibilities. Understanding how these classes interact with each other and with external modules (likewordPress
,OpenAIAssistant
,Images
,Instagram
,YouTubeShortsUploader
,VideoCreator
) is crucial. - Each class has multiple methods with dependencies on external services and internal utilities, adding to the complexity.
- The script is structured into classes (
- User Interface and Interaction:
- The script includes elements of user interaction (like dialog boxes and prompts), which require a seamless integration to provide a good user experience.
- Error Handling and Robustness:
- The script needs to be robust and handle various scenarios like API failures, internet connectivity issues, and invalid user inputs. This involves implementing comprehensive error handling and validation checks.
- Security and Credentials Management:
- Managing authentication credentials securely for different services (like Google, WordPress, Instagram) is crucial. The script must handle credentials in a secure manner, especially when dealing with OAuth tokens and API keys.
Overall, the script exemplifies a complex integration of AI content generation, multimedia processing, web service interactions, data management, and user interface design. It automates the end-to-end process of content creation and distribution, making it a sophisticated tool for digital content management and social media automation.
import pandas as pd
from wordpress import wordPress
from aiassistant import OpenAIAssistant
from image_handling import Images
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import requests
from gui import CustomDialog, show_post_dialog
import openpyxl
import json
import utils
import re
from config import DATABASE_LOCATION, POST_DATABASE, POSTS, IMAGE_LOCATION, VIDEO_LOCATION
import os
import time
import textwrap
from instagram import Instagram
from youtube import YouTubeShortsUploader
from video_handling import VideoCreator
class Generatepost:
def __init__(self):
self.post = OpenAIAssistant()
self.images = Images(self.post)
self.videos =VideoCreator()
self.wppost = wordPress()
self.IGPost = Instagram()
self.youtube = YouTubeShortsUploader()
self.image_counter = 1 # Initialize a counter for image naming
def generate_image_description(self, post_text):
prompt = f"Based on the following post, provide a short description for a relevant image:\n\n{post_text}"
return self.post.ask_gpt(prompt)
def generate_post_img_desc(self, topic):
prompt = f"""Provide one short image description for Dalle to generate a relevent and beautiful image on the topic '{topic}'. The description should be surrounded by brackets in a python list format like: ["an intersting desciription for featured image of post"] """
response = self.post.ask_gpt(prompt)
try:
# Parsing the string response into a Python list
image_keywords_list = eval(response) # Use eval() cautiously
print(image_keywords_list)
# Joining the list into a comma-separated string
image_keywords = ', '.join(image_keywords_list)
print(image_keywords)
return image_keywords
except Exception as e:
print(f"Could not generate image keywords: {e}")
# Handle the error appropriately
return []
def generate_reel_script(self, topic):
scriptAI = OpenAIAssistant()
prompt = f"Provide an inspiring and factual 30-50 word speech on the topic, '{topic}'. Your response should be surrounded by brackets in python list format such as: [this is text for short inspirational speech]."
response = scriptAI.ask_gpt(prompt)
try:
# Removing brackets and quotes, and trimming whitespace
cleaned_response = response.replace('[', '').replace(']', '').replace("'", "").replace('"', '').strip()
print(cleaned_response)
return cleaned_response
except Exception as e:
print(f"Could not generate script: {e}")
# Handle the error appropriately
return "Follow us @NeuralNetNews for more content like this!"
def generate_caption(self, topic):
captionAI = OpenAIAssistant()
prompt = f"Provide an inspiring and factual 150 to 1500 character paragraph on the topic, '{topic}'. This will be used as an Instagram caption. Your response should be surrounded by brackets in python list format such as: [this is the caption for instagram]."
response = captionAI.ask_gpt(prompt)
print(f"\nCaption is {response}\n")
# Attempt to parse the response
try:
# Removing brackets and quotes, and trimming whitespace
cleaned_response = response.replace('[', '').replace(']', '').replace("'", "").replace('"', '').strip()
# Splitting the string into a list based on commas
#category_list = cleaned_response.split(',')
# Get the first item in the list as the category, or default to "Uncategorized"
#category = category_list[0].strip() if category_list else "No Caption"
cleaned_response = cleaned_response + "\nFollow us @NeuralNetNews for more content like this!"
return cleaned_response
except Exception as e:
print(f"An error occurred: {e}")
# Handle the error appropriately
return "No Caption"
def generate_post(self,topic_list, topic):
page_list = []
original_image_directory, post_images_directory = self.create_folders(topic,content_type='image')
if topic_list:
page_list = topic_list
else:
page_list.append(topic)
for page_item in page_list:
image_description = self.generate_post_img_desc(page_item)
image_url = self.post.generate_image(image_description)
#image_url = 'https://m.media-amazon.com/images/W/MEDIAX_792452-T2/images/I/71PQ8F9jyOL.jpg'
# overlay image with txt
# Overlay text on the image
original_image = self.save_image(image_url,original_image_directory, topic)
# Check if the post is less than 50 words
image_with_text = self.images.overlay_text_on_image(original_image, page_item)
# Save or store the image
finished_image_path = self.save_image(image_with_text,post_images_directory, topic)
# Insert images into the post
caption = self.generate_caption(topic)
media_id = self.IGPost.post_to_instagram(post_images_directory,topic,caption)
return media_id
#featured_media_id, _ = self.images.insert_featured_images(image_keywords, topic)
#text
def generate_video_post(self, topic):
original_image_directory, post_videos_directory = self.create_folders(topic, content_type='video')
# Generate an image description
image_description = self.generate_post_img_desc(topic)
image_url = self.post.generate_image(image_description, size = "1024x1792")
# Save the original image
original_image = self.save_image(image_url, original_image_directory, topic)
# Generate a script for the reel
script = self.generate_reel_script(topic)
# Create and save the video
output_video_path=os.path.join(post_videos_directory, self.generate_video_name(topic))
output_video_path=self.videos.create_zooming_video_with_subtitles(image_path=original_image, script=script, output_video_path=output_video_path)
# Generate a caption for the post
caption = self.generate_caption(topic)
reel_id = self.IGPost.post_to_instagram(output_video_path, topic, caption, media_type='REELS')
short_id = self.youtube.upload_video(output_video_path,topic, caption, "24", ["AI", "Education"])
#short_id = 1234
# Here you can add logic to post the video to a platform or store it
# For example, post to Instagram, save to a database, etc.
return reel_id, short_id # or other relevant information
def create_folders(self, topic, content_type='image'):
# Choose the base location depending on content type
base_location = VIDEO_LOCATION if content_type == 'video' else IMAGE_LOCATION
# Sanitize the topic name and limit it to 20 characters
sanitized_topic = self.sanitize_topic_name(topic)[:20]
# Create the top directory for the topic
topic_directory = os.path.join(base_location, sanitized_topic).replace('\\', '/')
os.makedirs(topic_directory, exist_ok=True)
# Create subdirectories based on content type
if content_type == 'video':
original_image_directory = os.path.join(topic_directory, "Original_Image").replace('\\', '/')
post_videos_directory = os.path.join(topic_directory, "Post_Videos").replace('\\', '/')
os.makedirs(original_image_directory, exist_ok=True)
os.makedirs(post_videos_directory, exist_ok=True)
return original_image_directory,post_videos_directory
else:
original_image_directory = os.path.join(topic_directory, "Original_Image").replace('\\', '/')
post_images_directory = os.path.join(topic_directory, "Post_Images").replace('\\', '/')
os.makedirs(original_image_directory, exist_ok=True)
os.makedirs(post_images_directory, exist_ok=True)
return original_image_directory, post_images_directory
def save_image(self, image_input, image_folder, topic):
image_name = self.generate_image_name(topic)
image_path = os.path.join(image_folder, image_name).replace('\\', '/')
# Check if the input is a URL or an Image object
if isinstance(image_input, str):
# Assuming it's a URL, download and save the image
response = requests.get(image_input)
if response.status_code == 200:
image = Image.open(BytesIO(response.content))
else:
print(f"Failed to download image from {image_input}")
return None
elif isinstance(image_input, Image.Image):
# If it's an Image object, use it directly
image = image_input
else:
raise ValueError("Invalid image input type.")
image.save(image_path)
print(f"Image saved at {image_path}")
return image_path
def sanitize_topic_name(self, topic):
# Replace spaces with underscores and remove special characters
sanitized_name = ''.join(e for e in topic if e.isalnum() or e == ' ').replace(' ', '_')
# Limit the sanitized name to a maximum of 15 characters
max_length = 15
truncated_name = sanitized_name[:max_length]
return truncated_name
def generate_image_name(self, topic):
# Generate a unique image name using the topic and current time
sanitized_topic = self.sanitize_topic_name(topic)
timestamp = int(time.time())
return f"{sanitized_topic}_{timestamp}.jpg"
def generate_video_name(self, topic):
# Generate a unique video name using the topic and current time
sanitized_topic = self.sanitize_topic_name(topic)
timestamp = int(time.time())
return f"{sanitized_topic}_{timestamp}.mp4"
def _clean_post(self, post):
# Add your cleaning logic here
cleaned_post = post # Replace this with actual cleaning operations
return cleaned_post
class ProcessPosts:
def __init__(self):
self.topic_ai = OpenAIAssistant()
def generate_topc(self, n_topics=1):
topics = []
for _ in range(n_topics):
response = self.topic_ai.ask_gpt(self._topic_prompt())
response_dict, error = utils.parse_json_string(utils.clean_response_string(response))
if error:
# Handle the error appropriately
continue
topics.append(response_dict)
utils.append_data_to_excel(pd.DataFrame(response_dict), POSTS)
return topics
def process_post(self):
workbook, worksheet = utils.load_or_initialize_workbook(DATABASE_LOCATION + POSTS, ["Topic"])
database_workbook, database_worksheet = utils.load_or_initialize_workbook(DATABASE_LOCATION + POST_DATABASE, ["Topic"])
topics = utils.read_worksheet(worksheet)
database_topics = utils.read_worksheet(database_worksheet)
matching_topics = set(topics).intersection(database_topics)
utils.delete_matching_rows(worksheet, matching_topics)
utils.save_workbook(workbook, DATABASE_LOCATION + POSTS)
# Define delimiters for splitting topics
delimiters = ["?"]
for row in utils.iterate_rows(worksheet, ["Topic", "Type"]):
topic = row["Topic"].strip()
post_type = row["Type"].strip().lower()
if topic and topic not in matching_topics:
if post_type == "image":
# Split topic by delimiters and create a list
topic_list = [t.strip() for d in delimiters for t in topic.split(d) if t.strip()]
print(topic_list)
self._generate_and_process_image_post(topic_list, topic, database_worksheet, database_workbook, worksheet, workbook, post_type)
elif post_type == "video":
self._generate_and_process_video_post(topic, database_worksheet, database_workbook, worksheet, workbook, post_type)
utils.save_workbook(database_workbook, DATABASE_LOCATION + POST_DATABASE)
def _topic_prompt(self):
return("""
create a nested dictionary of interesting topics for POSTS,
keywords for images to represent that post, and a
category for that post in the format shown below:
topic_dict = {
"topic1": {
"Topic": "interesting topic",
"Image Search": [keyword1, keyword2, keyword3],
"Category": "website category"
},
"topic2": {
"Topic": "interesting topic",
"Image Search": [keyword1, keyword2, keyword3],
"Category": "website category"
},
"topic3": {
"Topic": "interesting topic",
"Image Search": [keyword1, keyword2, keyword3],
"Category": "website category"
}
}
please don't write anything else but the dictionary
""")
def _generate_and_process_image_post(self, topic_list, topic, database_worksheet, database_workbook, worksheet, workbook, post_type):
Image_generator = Generatepost()
PostID = Image_generator.generate_post(topic_list, topic)
if PostID:
# Append the topic to the database worksheet
database_worksheet.append([topic])
print(f"Post for topic '{topic}' has been generated and posted with ID {PostID}.")
# Save changes to the database workbook
utils.save_workbook(database_workbook, DATABASE_LOCATION + POST_DATABASE)
# Delete the row from the posttopics worksheet that was used to generate the topic and save changes
utils.delete_topic_row(worksheet, topic)
utils.save_workbook(workbook, DATABASE_LOCATION + POSTS)
def _generate_and_process_video_post(self, topic, database_worksheet, database_workbook, worksheet, workbook, post_type):
Video_generator = Generatepost()
ReelID,ShortID = Video_generator.generate_video_post(topic)
if ReelID:
# Append the topic to the database worksheet
database_worksheet.append([topic])
print(f"Video for topic '{topic}' has been generated and posted with ID {ReelID}.")
# Save changes to the database workbook
utils.save_workbook(database_workbook, DATABASE_LOCATION + POST_DATABASE)
# Delete the row from the posttopics worksheet that was used to generate the topic and save changes
utils.delete_topic_row(worksheet, topic)
utils.save_workbook(workbook, DATABASE_LOCATION + POSTS)
instagram.py
- Diverse Media Handling:
- The script supports posting different types of media on Instagram, including single images, image carousels, and video reels. Each media type requires distinct handling, from creation to publishing.
- Integration with WordPress:
- For video reels, the script uploads videos to WordPress before posting them to Instagram. This requires managing interactions between two different platforms, each with its own API and response structure.
- Instagram API Communication:
- The script heavily interacts with the Instagram API for various tasks, such as creating media objects, publishing media, and checking the status of media uploads. This involves constructing API requests, handling responses, and managing access tokens.
- Access Token Management:
- The script manages Instagram access tokens, refreshing them when necessary. It handles authentication with the Instagram API, which is crucial for maintaining a secure and uninterrupted connection.
- Error Handling and Retry Logic:
- The script includes robust error handling and retry mechanisms. It makes multiple attempts to publish media if the first attempt fails, waiting for a specified delay between retries.
- Conditional Logic for Media Processing:
- Based on the media type (image, carousel, or reel), the script follows different processing paths. This requires careful implementation of conditional logic to ensure that each media type is handled correctly.
- Media Object Creation and Publishing:
- Creating and publishing media objects to Instagram involves several steps, each with its own set of parameters and API endpoints. The script must correctly format requests and handle the responses for successful uploads.
- Scheduling and Time-based Actions:
- The script checks the current date and performs certain actions (like refreshing the access token) on specific days. This adds a scheduling aspect to the script.
- Interfacing with External Libraries:
- The script uses external libraries like
requests
for API communication andPIL
(Python Imaging Library) for image processing, which requires understanding these libraries and handling their integration smoothly.
- The script uses external libraries like
- Data Parsing and Processing:
- The script parses and processes data received from APIs and other sources. This includes handling JSON data, extracting relevant information, and using it for further processing.
In summary, the instagram.py
script is a sophisticated automation tool that deals with various aspects of social media posting, including API communication, media handling, authentication, error handling, and integration with external platforms like WordPress. The complexity lies in seamlessly managing these different aspects to provide a smooth and automated posting experience on Instagram.
from PIL import Image, ImageDraw, ImageFont
from config import FONT, INSTAGRAM_PASSWORD, INSTAGRAM_USER_ID, META_ACCESS_TOKEN
import textwrap
import requests
import json
import os
from wordpress import wordPress
import threading
import time
import datetime
class Instagram:
def __init__(self):
self.wp = wordPress()
self.max_retries = 3
self.retry_delay = 10 # seconds
self.current_date = datetime.date.today()
def post_to_instagram(self, media_path, topic, caption, media_type='IMAGE'):
if self.current_date.day == 15:
print("Today is the 15th of the month. Running refresh token...")
self.refresh_long_access_token(META_ACCESS_TOKEN)
try:
if media_type in ['IMAGE', 'CAROUSEL']:
return self.process_image_post(media_path, caption, media_type)
elif media_type == 'REELS':
media_id,video_url = self.wp.upload_video_to_wordpress(media_path)
print(video_url)
return self.post_video_reel(video_url, caption)
else:
return {'error': f'Unsupported media type: {media_type}'}
except Exception as e:
print('Exception:', str(e))
return {'error': 'General Exception', 'details': str(e)}
def process_image_post(self, image_folder, caption, media_type):
media_object_ids = []
media_object_ids_story = []
is_carousel = len(os.listdir(image_folder)) > 1
for image_file in os.listdir(image_folder):
image_path = image_folder + "/" + image_file
print (image_path)
if os.path.isfile(image_path) and image_path.lower().endswith(('.png', '.jpg', '.jpeg')):
wordpress_image_id, wordpress_image_url = self.wp.upload_media_to_wordpress(image_path)
if wordpress_image_url:
media_object_id = self.create_media_object_image(wordpress_image_url, caption, is_carousel, media_type='')
media_object_id_story = self.create_media_object_image(wordpress_image_url, caption,is_carousel, media_type = "STORIES")
if media_object_id:
media_object_ids.append(media_object_id)
media_object_ids_story.append(media_object_id_story)
if not media_object_ids:
return {'error': 'No media objects created'}
if len(media_object_ids) == 1:
post_id_story = self.publish_single_image(media_object_ids_story[0])
post_id = self.publish_single_image(media_object_ids[0])
return post_id
else:
post_id=self.publish_carousel(media_object_ids, caption)
post_id_story=self.publish_carousel(media_object_ids_story, caption)
return post_id
def get_media_object_status(self, media_object_id):
url = f'https://graph.facebook.com/v18.0/{media_object_id}?fields=status_code'
headers = {'Authorization': f'Bearer {META_ACCESS_TOKEN}'}
response = requests.get(url, headers=headers)
print(response.json())
if response.status_code == 200:
return response.json().get('status_code', 'UNKNOWN')
else:
return 'ERROR'
def post_video_reel(self, video_url, caption):
try:
# Create a dictionary to store media object IDs
media_object_ids = {}
# Function for creating video media objects
def create_media_object(media_type, cap):
media_object_id = self.create_video_media_object(video_url, caption, media_type)
media_object_ids[media_type] = media_object_id
# Function to check the status of media object
def check_media_object_status(media_object_id):
for _ in range(5): # Retry for a maximum of 5 minutes
status = self.get_media_object_status(media_object_id)
if status == 'FINISHED':
return True
elif status in ['ERROR', 'FAILED', 'EXPIRED']:
return False
time.sleep(60) # Wait for 1 minute before retrying
return False
# Create media objects without threading
for media_type, cap in [("REELS", caption), ("STORIES", "")]:
create_media_object(media_type, cap)
# Check if media objects were created successfully
if not all(media_object_ids.values()):
return {'error': 'Failed to create video media object'}
# Check if media objects are finished processing
for media_id in media_object_ids.values():
if not check_media_object_status(media_id):
return {'error': f'Media object {media_id} failed to process'}
# Publish videos
publish_result_post = self.publish_video(media_object_ids["REELS"])
publish_result_story = self.publish_video(media_object_ids["STORIES"])
# Return the results of publishing
return {'reels': publish_result_post, 'stories': publish_result_story}
except Exception as e:
print('Exception in posting video reel:', str(e))
return {'error': 'General Exception', 'details': str(e)}
def create_media_object_image(self, image_url, caption, is_carousel_item, media_type = ''):
create_url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_USER_ID}/media"
create_payload = {
'media_type' : media_type,
'image_url': image_url,
'caption': caption,
'is_carousel_item': is_carousel_item,
'access_token': META_ACCESS_TOKEN
}
create_response = requests.post(create_url, data=create_payload)
if create_response.status_code == 200:
return create_response.json()['id']
else:
print("Failed to create media object:", create_response.text)
return None
def create_carousel_container(self, item_container_ids, caption):
url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_USER_ID}/media"
payload = {
'media_type': 'CAROUSEL',
'children': ','.join(item_container_ids),
'caption': caption,
'access_token': META_ACCESS_TOKEN
}
response = requests.post(url, data=payload)
if response.status_code == 200:
return response.json()['id']
else:
print("Failed to create carousel container:", response.text)
return None
def create_video_media_object(self, video_url, caption, media_type="REELS"):
create_url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_USER_ID}/media"
create_payload = {
'media_type': media_type,
'video_url': video_url,
'caption': caption,
'access_token': META_ACCESS_TOKEN
}
create_response = requests.post(create_url, data=create_payload)
if create_response.status_code == 200:
return create_response.json()['id']
else:
print("Failed to create video media object:", create_response.text)
return None
def publish_single_image(self, container_id):
url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_USER_ID}/media_publish"
payload = {
'creation_id': container_id,
'access_token': META_ACCESS_TOKEN
}
return self.try_request_with_retry(url, payload)
def publish_carousel(self, container_ids, caption):
carousel_container_id = self.create_carousel_container(container_ids, caption)
if not carousel_container_id:
return {'error': 'Failed to create carousel container'}
url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_USER_ID}/media_publish"
payload = {
'creation_id': carousel_container_id,
'access_token': META_ACCESS_TOKEN
}
return self.try_request_with_retry(url, payload)
def publish_video(self, media_object_id):
url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_USER_ID}/media_publish"
payload = {
'creation_id': media_object_id,
'access_token': META_ACCESS_TOKEN
}
return self.try_request_with_retry(url, payload)
def try_request_with_retry(self, url, payload, error_code=9007):
for attempt in range(self.max_retries):
try:
response = requests.post(url, data=payload)
if response.status_code == 200:
return response.json()
response_data = response.json()
if response_data.get('error', {}).get('code') == error_code:
print(f"Attempt {attempt + 1}: Media not ready, retrying in {self.retry_delay} seconds...")
time.sleep(self.retry_delay)
continue
print(f"Failed to publish: {response.text}")
return {'error': 'Failed to publish', 'details': response_data}
except Exception as e:
print(f"Exception during publishing: {e}")
return {'error': 'Exception during publishing', 'details': str(e)}
return {'error': 'Failed to publish after retries'}
def refresh_long_access_token(self,META_ACCESS_TOKEN):
create_url = f"https://graph.facebook.com/v18.0/oauth/access_token"
# Define your parameters
params = {
"grant_type": "fb_exchange_token",
"client_id": 876454464108455,
"client_secret": '63419032c6a2f1c08aa5688af1ab9475',
"fb_exchange_token": META_ACCESS_TOKEN
}
create_response = requests.post(create_url, data=params)
if create_response.status_code == 200:
test = create_response.json()['access_token']
expires = create_response.json()['expires_in']
print(test)
print( "\n"+expires)
else:
print("Failed to create media object:", create_response.text)
return None
wordpress.py
- API Communication with WordPress:
- The script extensively interacts with the WordPress REST API for various tasks, such as creating categories, uploading media, and creating posts. This involves constructing API requests, handling responses, and managing errors.
- Authentication and Security:
- Manages authentication using Basic Auth (Base64 encoded username and password). The script handles credentials securely, encoding them and attaching them to the headers of each API request.
- Media Handling:
- Uploads different types of media to WordPress. This includes determining whether a media source is a URL or a local file, handling file data (reading from URLs or files), and constructing multipart/form-data requests for uploading.
- Conditional Logic for Media Processing:
- Implements conditional logic to handle different types of media sources. For instance, the approach to uploading a media item varies based on whether the source is a URL or a local file path.
- Error Handling and Robustness:
- Includes error handling mechanisms to capture and handle exceptions arising from network requests, file operations, or API interactions.
- Data Parsing and Processing:
- Parses JSON responses from the WordPress API to extract needed information like media IDs and URLs.
- Content Creation and Posting:
- Creates posts on WordPress with options to set titles, content, categories, and featured media. This requires a detailed understanding of the WordPress post structure and the REST API’s capabilities.
- Category Management:
- Checks for the existence of categories and creates new ones if needed. This involves searching for categories by name and parsing the response to determine if a category needs to be created.
- Response Validation and Feedback:
- Validates API responses and provides feedback via print statements. This is essential for debugging and understanding the script’s operation during execution.
- Utility Functions:
- Contains utility functions to streamline operations, such as checking for category existence, creating categories, and creating posts.
In summary, wordpress.py
is a sophisticated tool for automating content management on WordPress sites. It demonstrates a deep integration with WordPress’s REST API, handling various aspects of blogging and media management, from category creation to post publishing. The script’s complexity lies in its API interaction, media handling capabilities, error handling, and the secure management of authentication credentials.
import requests
import base64
import json
from io import BytesIO
from PIL import Image
import os
from config import WORDPRESS_API_PASSWORD, WORDPRESS_PASSWORD, WORDPRESS_EMAIL, WORDPRESS_SITE_URL, WORDPRESS_USERNAME
#Word Press Integration Class
class wordPress:
def __init__(self):
self.username = WORDPRESS_USERNAME # Replace with your WordPress email
self.password = WORDPRESS_PASSWORD # Replace with your WordPress password
self.site_url = WORDPRESS_SITE_URL # Replace with your WordPress site URL
self.credentials = f"{self.username}:{self.password}"
self.token = base64.b64encode(self.credentials.encode())
self.headers = {"Authorization": f"Basic {self.token.decode('utf-8')}"}
def category_exists(self, category_name):
# Check if the category already exists
url = f'{WORDPRESS_SITE_URL}/wp-json/wp/v2/categories?search={category_name}'
response = requests.get(url, headers=self.headers)
if response.status_code == 200:
categories = json.loads(response.content)
if categories:
return categories[0]['id']
return None
def create_category(self,category_name):
# Create a new category
url = f'{WORDPRESS_SITE_URL}/wp-json/wp/v2/categories'
data = {
'name': category_name }
response = requests.post(url, headers=self.headers, json=data)
if response.status_code == 201:
category_id = json.loads(response.content)['id']
print(f'Category created with ID: {category_id}')
return category_id
return None
def get_category_id(self,category_name):
# Get the ID of the category
category_id = self.category_exists(category_name)
if category_id:
return category_id
else:
return self.create_category(category_name)
def upload_media_to_wordpress(self, image_source):
try:
# Check if the image source is a URL or a local file path
if image_source.startswith('http://') or image_source.startswith('https://'):
# Download the image from the URL
response = requests.get(image_source)
response.raise_for_status()
image = Image.open(BytesIO(response.content))
buffer = BytesIO()
image.convert('RGB').save(buffer, format="JPEG")
buffer.seek(0)
file_data = buffer.getvalue()
file_name = 'image.jpg'
else:
# Open the local image file
with open(image_source, 'rb') as image_file:
file_data = image_file.read()
file_name = os.path.basename(image_source)
# Prepare the data and files payload for the POST request
data = {
"media_type": "image/jpeg",
}
files = {'file': (file_name, file_data, 'image/jpeg', {'Expires': '0'})}
# Post the image to WordPress
upload_response = requests.post(f'{WORDPRESS_SITE_URL}/wp-json/wp/v2/media', headers=self.headers, data=data, files=files)
upload_response.raise_for_status()
# Parse the response
if upload_response.status_code == 201:
media_id = upload_response.json()['id']
media_url = upload_response.json()['guid']['rendered']
print(f'Featured image uploaded with ID: {media_id}')
return media_id, media_url
else:
print(f"Failed to upload media: {upload_response.text}")
return None, None
except requests.RequestException as e:
print(f"Request failed: {e}")
return None, None
def create_post(self, title, content, category_id, featured_media_id):
# Create a post on the WordPress site
url = f"{self.site_url}/wp-json/wp/v2/posts"
data = {
"title": title,
"content": content,
"status": "publish",
"categories": [category_id],
"featured_media": featured_media_id,
}
response = requests.post(url, headers=self.headers, json=data)
if response.status_code == 201:
print(json.loads(response.content))
post_id = json.loads(response.content)["id"]
post_url = json.loads(response.content)["guid"]["rendered"]
print(f"nPost created with ID: {post_id}")
print(f"nPost created with url: {post_url}")
return post_id, post_url
else:
print(f"Failed to create post. Status Code: {response.status_code}")
print(f"Response: {response.content}")
return None
youtube.py
- OAuth 2.0 Authentication:
- Implements OAuth 2.0 authentication flow using Google’s libraries. This process involves handling credentials, refreshing tokens, and securely storing and retrieving authentication tokens using pickle.
- API Service Initialization:
- Initializes the YouTube API client using the Google API client libraries. This requires specifying the API service name, version, and authenticated credentials.
- Video Upload Functionality:
- Provides a method to upload videos to YouTube. This involves creating a request body with video metadata (like title, description, category, and tags), handling the video file with
MediaFileUpload
, and making an API call to insert the video.
- Provides a method to upload videos to YouTube. This involves creating a request body with video metadata (like title, description, category, and tags), handling the video file with
- Error Handling in API Interactions:
- Manages potential errors in API calls, both in authentication and video upload processes. This includes handling exceptions and invalid responses.
- Credential Management:
- Manages user credentials securely. The script checks for existing credentials stored in a pickle file and refreshes them if necessary. If no valid credentials are found, it initiates a new OAuth flow.
- YouTube API Usage:
- Leverages the YouTube API for uploading videos, which involves understanding and correctly using the YouTube API’s capabilities and requirements.
- File Handling and Data Serialization:
- Reads and writes credentials to a file using the
pickle
module for serialization and deserialization. This requires careful handling to ensure security and integrity of the data.
- Reads and writes credentials to a file using the
- Configurable Video Parameters:
- Allows setting various parameters for the video upload, such as category, privacy status, and tags, making the script versatile for different upload requirements.
- Scopes and Permissions:
- Uses specific OAuth scopes (
youtube.upload
) that limit the access to just uploading videos, adhering to the principle of least privilege.
- Uses specific OAuth scopes (
- Network Communication and Response Parsing:
- Communicates with YouTube’s servers over the network and parses the response to extract and return useful information about the uploaded video.
In summary, the YouTubeShortsUploader
class encapsulates the complexity of handling OAuth authentication, YouTube API communication, file handling, and network response parsing. It’s a specialized tool designed to automate the process of uploading videos to YouTube, particularly for YouTube Shorts, with a focus on secure and efficient handling of user credentials and API interactions.
import os
import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from config import GOOGLE_JSON_OAUTH_CRED, GOOGLE_API_PICKLE_TOKEN
import pickle
from google.auth.transport.requests import Request
class YouTubeShortsUploader:
def __init__(self):
self.client_secrets_file = GOOGLE_JSON_OAUTH_CRED
self.token_file = GOOGLE_API_PICKLE_TOKEN
self.youtube = None
self.authenticate()
def authenticate(self):
scopes = ["https://www.googleapis.com/auth/youtube.upload"]
api_service_name = "youtube"
api_version = "v3"
# Check if token file exists
credentials = None
if os.path.exists(self.token_file):
with open(self.token_file, 'rb') as token:
credentials = pickle.load(token)
# If there are no valid credentials available, let the user log in.
if not credentials or not credentials.valid:
if credentials and credentials.expired and credentials.refresh_token:
credentials.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(self.client_secrets_file, scopes)
credentials = flow.run_local_server()
# Save the credentials for the next run
with open(self.token_file, 'wb') as token:
pickle.dump(credentials, token)
self.youtube = build(api_service_name, api_version, credentials=credentials)
def upload_video(self, file_path, title, description, category_id, tags):
request_body = {
"snippet": {
"categoryId": category_id,
"title": title,
"description": description,
"tags": tags
},
"status": {
"privacyStatus": "public"
}
}
media_file = googleapiclient.http.MediaFileUpload(file_path)
request = self.youtube.videos().insert(
part="snippet,status",
body=request_body,
media_body=media_file
)
response = request.execute()
return response