2025-04-17 14:40:59 +08:00
|
|
|
import os
|
|
|
|
|
import time
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
import argparse
|
|
|
|
|
import sys
|
2025-04-18 15:52:31 +08:00
|
|
|
import traceback
|
2025-04-22 13:58:08 +08:00
|
|
|
import json
|
2025-04-17 11:05:46 +08:00
|
|
|
|
2025-04-17 14:40:59 +08:00
|
|
|
from core.ai_agent import AI_Agent
|
2025-04-22 14:16:29 +08:00
|
|
|
# from core.topic_parser import TopicParser # No longer needed directly in main?
|
2025-04-17 15:30:24 +08:00
|
|
|
import core.contentGen as contentGen
|
|
|
|
|
import core.posterGen as posterGen
|
|
|
|
|
import core.simple_collage as simple_collage
|
2025-04-17 14:40:59 +08:00
|
|
|
from utils.resource_loader import ResourceLoader
|
2025-04-22 14:30:25 +08:00
|
|
|
from utils.tweet_generator import ( # Import the moved functions
|
|
|
|
|
run_topic_generation_pipeline,
|
|
|
|
|
generate_content_for_topic,
|
|
|
|
|
generate_posters_for_topic
|
|
|
|
|
)
|
2025-04-22 14:19:21 +08:00
|
|
|
from utils.prompt_manager import PromptManager # Import PromptManager
|
2025-04-18 11:08:54 +08:00
|
|
|
import random
|
2025-04-18 15:52:31 +08:00
|
|
|
|
2025-04-22 13:58:08 +08:00
|
|
|
def load_config(config_path="poster_gen_config.json"):
|
|
|
|
|
"""Loads configuration from a JSON file."""
|
|
|
|
|
if not os.path.exists(config_path):
|
|
|
|
|
print(f"Error: Configuration file '{config_path}' not found.")
|
|
|
|
|
print("Please copy 'example_config.json' to 'poster_gen_config.json' and customize it.")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
try:
|
|
|
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
|
|
|
config = json.load(f)
|
|
|
|
|
# Basic validation (can be expanded)
|
|
|
|
|
required_keys = ["api_url", "model", "api_key", "resource_dir", "prompts_dir", "output_dir", "num", "variants", "topic_system_prompt", "topic_user_prompt", "content_system_prompt", "image_base_dir"]
|
|
|
|
|
if not all(key in config for key in required_keys):
|
2025-04-22 14:16:29 +08:00
|
|
|
missing_keys = [key for key in required_keys if key not in config]
|
|
|
|
|
print(f"Error: Config file '{config_path}' is missing required keys: {missing_keys}")
|
2025-04-22 13:58:08 +08:00
|
|
|
sys.exit(1)
|
|
|
|
|
# Resolve relative paths based on config location or a defined base path if necessary
|
|
|
|
|
# For simplicity, assuming paths in config are relative to project root or absolute
|
|
|
|
|
return config
|
|
|
|
|
except json.JSONDecodeError:
|
|
|
|
|
print(f"Error: Could not decode JSON from '{config_path}'. Check the file format.")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Error loading configuration from '{config_path}': {e}")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
2025-04-22 14:16:29 +08:00
|
|
|
# Removed generate_topics_step function definition from here
|
|
|
|
|
# Its logic is now in utils.tweet_generator.run_topic_generation_pipeline
|
2025-04-22 13:58:08 +08:00
|
|
|
|
2025-04-22 14:30:25 +08:00
|
|
|
# --- Main Orchestration Step (Remains in main.py) ---
|
|
|
|
|
|
2025-04-22 13:58:08 +08:00
|
|
|
def generate_content_and_posters_step(config, run_id, tweet_topic_record):
|
2025-04-22 14:30:25 +08:00
|
|
|
"""Generates content and posters by calling decoupled functions for each topic."""
|
|
|
|
|
if not run_id or not tweet_topic_record or not tweet_topic_record.topics_list:
|
|
|
|
|
print("Missing run_id or valid topics data. Skipping content and poster generation.")
|
2025-04-22 13:58:08 +08:00
|
|
|
return
|
|
|
|
|
|
2025-04-22 14:30:25 +08:00
|
|
|
print("\nStep 2: Processing Topics for Content and Posters...")
|
2025-04-22 13:58:08 +08:00
|
|
|
base_output_dir = config["output_dir"]
|
2025-04-22 14:30:25 +08:00
|
|
|
|
|
|
|
|
# --- Initialize shared resources once ---
|
|
|
|
|
prompt_manager = None
|
2025-04-22 14:16:29 +08:00
|
|
|
ai_agent = None
|
2025-04-22 14:30:25 +08:00
|
|
|
# We might not need to initialize these here if the moved functions handle it
|
|
|
|
|
# content_gen = None
|
|
|
|
|
# poster_gen_instance = None
|
|
|
|
|
initialized_ok = False
|
2025-04-22 14:16:29 +08:00
|
|
|
try:
|
2025-04-22 14:30:25 +08:00
|
|
|
prompt_manager = PromptManager(config)
|
2025-04-22 14:16:29 +08:00
|
|
|
print(f"Initializing AI Agent ({config['model']})...")
|
|
|
|
|
ai_agent = AI_Agent(config["api_url"], config["model"], config["api_key"])
|
2025-04-22 14:30:25 +08:00
|
|
|
# content_gen = core_contentGen.ContentGenerator() # Instantiation moved to generate_posters_for_topic
|
|
|
|
|
# poster_gen_instance = core_posterGen.PosterGenerator() # Instantiation moved to generate_posters_for_topic
|
|
|
|
|
|
|
|
|
|
# Check image base directory validity once
|
|
|
|
|
image_base_dir = config.get("image_base_dir", None)
|
|
|
|
|
if not image_base_dir or not os.path.isdir(image_base_dir):
|
|
|
|
|
raise ValueError(f"'image_base_dir' ({image_base_dir}) not specified or not a valid directory in config.")
|
|
|
|
|
initialized_ok = True
|
|
|
|
|
|
2025-04-22 14:16:29 +08:00
|
|
|
except Exception as e:
|
2025-04-22 14:30:25 +08:00
|
|
|
print(f"Error initializing resources for content/poster generation: {e}")
|
2025-04-22 14:16:29 +08:00
|
|
|
traceback.print_exc()
|
2025-04-22 14:30:25 +08:00
|
|
|
if ai_agent: ai_agent.close()
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# --- Process each topic using decoupled functions ---
|
|
|
|
|
total_topics = len(tweet_topic_record.topics_list)
|
2025-04-22 13:58:08 +08:00
|
|
|
for i, topic in enumerate(tweet_topic_record.topics_list):
|
|
|
|
|
topic_index = i + 1
|
2025-04-22 14:30:25 +08:00
|
|
|
print(f"\nProcessing Topic {topic_index}/{total_topics}: {topic.get('title', 'N/A')}")
|
|
|
|
|
|
|
|
|
|
# 1. Generate Content for this topic (Call function from utils.tweet_generator)
|
|
|
|
|
tweet_content_list = generate_content_for_topic(
|
|
|
|
|
ai_agent, prompt_manager, config, topic,
|
|
|
|
|
base_output_dir, run_id, topic_index
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 2. Generate Posters for this topic (Call function from utils.tweet_generator)
|
|
|
|
|
if tweet_content_list:
|
|
|
|
|
# Pass only necessary parts of config? Or the whole config?
|
|
|
|
|
# Pass config for now, the function extracts what it needs.
|
|
|
|
|
generate_posters_for_topic(
|
|
|
|
|
config, # Pass config
|
|
|
|
|
topic, tweet_content_list,
|
|
|
|
|
base_output_dir, run_id, topic_index
|
|
|
|
|
# Instances are now created inside generate_posters_for_topic
|
|
|
|
|
# poster_gen_instance, content_gen,
|
|
|
|
|
)
|
2025-04-22 13:58:08 +08:00
|
|
|
else:
|
2025-04-22 14:30:25 +08:00
|
|
|
print(f" Skipping poster generation for Topic {topic_index} due to content generation failure.")
|
2025-04-22 13:58:08 +08:00
|
|
|
|
2025-04-22 14:16:29 +08:00
|
|
|
# --- Cleanup ---
|
|
|
|
|
if ai_agent:
|
2025-04-22 14:30:25 +08:00
|
|
|
print("\nClosing shared AI Agent...")
|
2025-04-22 14:16:29 +08:00
|
|
|
ai_agent.close()
|
2025-04-22 14:30:25 +08:00
|
|
|
print("\nFinished processing all topics.")
|
2025-04-22 14:16:29 +08:00
|
|
|
|
2025-04-22 13:58:08 +08:00
|
|
|
def main():
|
|
|
|
|
# No argparse for now, directly load default config
|
|
|
|
|
config = load_config() # Load from poster_gen_config.json
|
|
|
|
|
|
|
|
|
|
# Execute steps sequentially
|
2025-04-22 14:16:29 +08:00
|
|
|
# Step 1: Generate Topics (using the function from utils.tweet_generator)
|
|
|
|
|
run_id, tweet_topic_record = run_topic_generation_pipeline(config)
|
2025-04-22 13:58:08 +08:00
|
|
|
|
2025-04-22 14:16:29 +08:00
|
|
|
# Step 2: Generate Content and Posters (if Step 1 was successful)
|
2025-04-22 13:58:08 +08:00
|
|
|
if run_id and tweet_topic_record:
|
|
|
|
|
generate_content_and_posters_step(config, run_id, tweet_topic_record)
|
|
|
|
|
else:
|
|
|
|
|
print("Exiting due to issues in topic generation.")
|
2025-04-17 15:30:24 +08:00
|
|
|
|
2025-04-17 11:05:46 +08:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|