Pinterest-Bot / app.py
Unknown-Geek
Persistent Session
7e120ed
"""
Pinterest Automation Bot - Fixed Version
"""
import gradio as gr
import os
import logging
import tempfile
import shutil
import stat
from pathlib import Path
import datetime
import re
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Global Pinterest automation instance for session management
pinterest_bot = None
def get_pinterest_bot():
"""Get or create the global Pinterest bot instance"""
global pinterest_bot
if pinterest_bot is None:
try:
from pinterest_automation_portable import PortablePinterestAutomation
pinterest_bot = PortablePinterestAutomation(headless=True, fast_typing=True)
logger.info("Created new Pinterest bot instance")
except Exception as e:
logger.error(f"Failed to create Pinterest bot: {e}")
return None
return pinterest_bot
def cleanup_pinterest_bot():
"""Cleanup the global Pinterest bot instance"""
global pinterest_bot
if pinterest_bot is not None:
try:
pinterest_bot.quit()
logger.info("Pinterest bot instance cleaned up")
except Exception as e:
logger.error(f"Error cleaning up Pinterest bot: {e}")
finally:
pinterest_bot = None
def reset_session():
"""Reset the Pinterest session"""
cleanup_pinterest_bot()
return "πŸ”„ Session reset successfully! You can now login with different credentials or refresh the connection."
def ensure_executable_permissions():
"""Ensure Chrome binary and ChromeDriver have executable permissions"""
try:
# Define paths to executables
chrome_binary = Path("chrome_portable/chrome")
chromedriver_binary = Path("drivers/chromedriver")
# Set executable permissions if files exist
if chrome_binary.exists():
current_permissions = chrome_binary.stat().st_mode
chrome_binary.chmod(current_permissions | stat.S_IEXEC | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
logger.info(f"Set executable permission for: {chrome_binary}")
else:
logger.warning(f"Chrome binary not found: {chrome_binary}")
if chromedriver_binary.exists():
current_permissions = chromedriver_binary.stat().st_mode
chromedriver_binary.chmod(current_permissions | stat.S_IEXEC | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
logger.info(f"Set executable permission for: {chromedriver_binary}")
else:
logger.warning(f"ChromeDriver not found: {chromedriver_binary}")
except Exception as e:
logger.error(f"Failed to set executable permissions: {str(e)}")
def upload_pin(email, password, image, title, description, board_name, link_url, headless):
"""Main function to upload a pin to Pinterest with session management"""
# Input validation
if not email or not password:
return "❌ Please enter your Pinterest email and password"
if not image:
return "❌ Please upload an image"
if not title or not description or not board_name:
return "❌ Please fill in title, description, and board name"
# Validate URL format if provided
if link_url:
# Simple URL validation
url_pattern = re.compile(
r'^https?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|' # domain...
r'localhost|' # localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
if not url_pattern.match(link_url):
return "❌ Please enter a valid URL (starting with http:// or https://)"
# Create uploads directory if it doesn't exist
uploads_dir = Path("uploads")
uploads_dir.mkdir(exist_ok=True)
# Generate unique filename with timestamp
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
image_extension = Path(image).suffix or '.jpg'
stored_image_name = f"upload_{timestamp}{image_extension}"
stored_image_path = uploads_dir / stored_image_name
try:
# Get or create Pinterest bot instance
pinterest = get_pinterest_bot()
if not pinterest:
return "❌ Failed to initialize Pinterest bot"
# Check if we need to setup driver
if not hasattr(pinterest, 'driver') or not pinterest.driver:
logger.info("Setting up Chrome driver...")
if not pinterest._setup_driver():
return "❌ Failed to setup Chrome driver. Please try again."
# Check if we need to login or if user changed
if not pinterest.is_logged_in or pinterest.current_email != email:
logger.info(f"Logging in as {email}...")
if not pinterest.login(email, password):
return "❌ Login failed. Please check your credentials."
else:
logger.info(f"Already logged in as {email}, continuing with existing session")
try:
# Ensure Chrome and ChromeDriver have executable permissions
ensure_executable_permissions()
# Store image in uploads directory
shutil.copy2(image, stored_image_path)
logger.info(f"Image stored at: {stored_image_path}")
# Navigate to pin creation page (reuse existing session)
logger.info("Navigating to pin creation page...")
pinterest.driver.get("https://www.pinterest.com/pin-builder/")
pinterest._human_delay(3, 5)
# Create temp directory for processing
temp_dir = tempfile.mkdtemp()
temp_image_path = os.path.join(temp_dir, "uploaded_image.jpg")
shutil.copy2(image, temp_image_path)
# Upload image
if not pinterest.upload_image(temp_image_path):
return "❌ Failed to upload image."
# Set pin details
if not pinterest.set_title(title):
return "❌ Failed to set title."
if not pinterest.set_description(description):
return "❌ Failed to set description."
# Set optional link (only if valid URL provided)
if link_url and link_url.strip():
if not pinterest.set_link(link_url.strip()):
return "❌ Failed to set link. Please check the URL format."
# Select board
if not pinterest.select_board(board_name):
return "❌ Failed to select board. Make sure it exists in your Pinterest."
# Publish pin
if not pinterest.publish_pin():
return "❌ Failed to publish pin. Please check for validation errors."
# Cleanup temp directory
shutil.rmtree(temp_dir)
# If successful, clean up stored image
if stored_image_path.exists():
stored_image_path.unlink()
logger.info(f"Cleaned up stored image: {stored_image_path}")
logger.info("Pin uploaded successfully! Bot session maintained for next upload.")
return f"βœ… Success! Pin '{title}' uploaded to board '{board_name}'. Session maintained for faster future uploads."
except Exception as inner_e:
# If there's an error, keep the stored image for debugging
logger.error(f"Upload failed, keeping image for debugging: {stored_image_path}")
return f"❌ Upload failed: {str(inner_e)}. Image saved at {stored_image_path} for debugging."
except ImportError as e:
return f"❌ Setup error: {str(e)}. Chrome setup is missing."
except Exception as e:
logger.error(f"Error: {str(e)}")
return f"❌ Error: {str(e)}"
# Simple interface that should work with Gradio 4.40.0
with gr.Blocks(title="Pinterest Automation Bot") as app:
gr.Markdown("# πŸ“Œ Pinterest Automation Bot")
gr.Markdown("Upload pins to Pinterest automatically with advanced automation.")
# Status indicator
with gr.Row():
status_text = gr.Markdown("🟒 **Status**: Ready to upload pins!")
# Login section
gr.Markdown("### πŸ” Pinterest Login")
email = gr.Textbox(label="Email", placeholder="your-email@example.com")
password = gr.Textbox(label="Password", type="password")
# Pin details section
gr.Markdown("### πŸ“ Pin Details")
image = gr.File(label="Image")
title = gr.Textbox(label="Title", placeholder="Enter pin title...")
description = gr.Textbox(label="Description", lines=3, placeholder="Enter pin description...")
board_name = gr.Textbox(label="Board Name", placeholder="Enter existing board name...")
link_url = gr.Textbox(label="Link (Optional)", placeholder="https://example.com")
# Options
gr.Markdown("### βš™οΈ Options")
headless = gr.Checkbox(label="Run in headless mode (recommended)", value=True)
# Submit button
submit_btn = gr.Button("πŸš€ Upload Pin to Pinterest", variant="primary")
# Session reset button
reset_btn = gr.Button("πŸ”„ Reset Session", variant="secondary")
# Output
result = gr.Textbox(label="Result", lines=3, interactive=False)
# Event handlers
submit_btn.click(
upload_pin,
inputs=[email, password, image, title, description, board_name, link_url, headless],
outputs=result
)
reset_btn.click(
reset_session,
inputs=[],
outputs=result
)
gr.Markdown("""
### πŸ“– Instructions
1. Enter your Pinterest credentials
2. Upload an image (PNG, JPG, GIF, WebP)
3. Fill in pin details (title, description, board name)
4. Optionally add a destination link
5. Click "Upload Pin to Pinterest"
**Note**: Board must already exist in your Pinterest account.
### πŸ”„ Session Management
- The bot maintains your login session between uploads for faster operation
- Use "Reset Session" to logout and start fresh (useful when switching accounts or if errors occur)
- Session is automatically maintained until you reset or restart the application
""")
if __name__ == "__main__":
try:
logger.info("=== Starting Pinterest Bot Application ===")
# Ensure executable permissions are set
logger.info("Setting executable permissions...")
ensure_executable_permissions()
logger.info("Executable permissions set successfully")
# Check if running on Hugging Face Spaces
is_spaces = os.getenv('SPACE_ID') is not None
logger.info(f"Running on Hugging Face Spaces: {is_spaces}")
# For HF Spaces, start the app quickly and do heavy initialization later
if is_spaces:
logger.info("Quick launch for HF Spaces...")
app.queue() # Enable queuing for better performance
app.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
show_error=True,
quiet=False,
favicon_path=None,
enable_monitoring=False
)
else:
# For local development, do full initialization
logger.info("Full initialization for local development...")
# Test imports to make sure everything is working
logger.info("Testing module imports...")
try:
from pinterest_automation_portable import PortablePinterestAutomation
logger.info("βœ… Pinterest automation module imported successfully")
except Exception as e:
logger.error(f"❌ Failed to import Pinterest automation module: {e}")
try:
from chrome_wrapper import get_chrome_manager
logger.info("βœ… Chrome wrapper imported successfully")
# Test chrome manager creation (but don't start browser)
chrome_manager = get_chrome_manager()
config_info = chrome_manager.get_config_info()
logger.info(f"βœ… Chrome setup verified: {config_info['chrome_binary']}")
except Exception as e:
logger.error(f"❌ Failed to setup Chrome wrapper: {e}")
logger.info("Launching Gradio app...")
app.launch(
server_name="0.0.0.0",
server_port=7861,
share=True,
show_error=True,
quiet=False
)
except Exception as e:
logger.error(f"Failed to start application: {e}")
raise