Spaces:
Runtime error
Runtime error
File size: 14,031 Bytes
0e132dd 0256877 0e132dd 6d43057 0256877 d3cde2b 4a029fc d3cde2b 6d43057 0256877 c4805ce 0256877 c4805ce 0256877 4703969 2d2e300 9b017d9 4703969 2d2e300 9b017d9 4703969 2d2e300 4703969 2d2e300 4703969 2d2e300 9b017d9 2d2e300 4703969 0256877 c4805ce 0256877 c4805ce 0256877 c4805ce 0256877 9b017d9 c4805ce 0256877 9b017d9 0256877 c4805ce 0256877 d6a08b7 0256877 d6a08b7 0256877 d6a08b7 0256877 d6a08b7 0256877 d6a08b7 4703969 0256877 d6a08b7 4703969 d6a08b7 49d3b3e d6a08b7 49d3b3e d6a08b7 0256877 4703969 d6a08b7 0256877 d6a08b7 0256877 d6a08b7 4703969 f977b64 d6a08b7 4703969 d6a08b7 f977b64 d6a08b7 0256877 d6a08b7 4703969 d6a08b7 0256877 d6a08b7 0256877 d6a08b7 0256877 d6a08b7 0256877 d6a08b7 0256877 d6a08b7 0256877 d6a08b7 0256877 d6a08b7 0256877 4703969 0256877 d6a08b7 0256877 d6a08b7 4703969 d6a08b7 4703969 d6a08b7 0256877 d6a08b7 4703969 d6a08b7 4703969 d6a08b7 4703969 d6a08b7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 | import os
import warnings
import logging
# Suppress technical initialization warnings from Torch/Transformers/HF
os.environ["TORCH_LOGS"] = "-all"
os.environ["TORCH_CPP_LOG_LEVEL"] = "3"
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
os.environ["HUGGINGFACE_HUB_VERBOSITY"] = "error"
warnings.filterwarnings("ignore", message=".*torch.classes.*")
logging.getLogger("torch").setLevel(logging.ERROR)
import streamlit as st
# Page Config must be first Streamlit command
st.set_page_config(
page_title="AutoContentForge AI",
page_icon="⚒️",
layout="wide",
initial_sidebar_state="expanded",
)
from streamlit_calendar import calendar
from forge_utils import ContentExtractor, ForgeAI
# Initialize ForgeAI with caching
@st.cache_resource
def get_forge():
return ForgeAI()
forge = get_forge()
# Session State for Content
if 'forge_results' not in st.session_state:
st.session_state.forge_results = None
if 'extracted_content' not in st.session_state:
st.session_state.extracted_content = None
# Custom CSS for Premium Look
st.markdown("""
<style>
/* Global Text Color */
.stApp {
color: #ffffff;
}
.main {
background-color: #0e1117;
color: #ffffff;
}
p, span, label, h1, h2, h3, h4, h5, h6 {
color: #ffffff !important;
}
/* Sidebar Carbon Black */
[data-testid="stSidebar"] {
background-color: #1a1c1e;
border-right: 1px solid rgba(255, 255, 255, 0.05);
}
/* Global Button Styling - Force Red */
button, [data-testid="stBaseButton-secondary"], [data-testid="stBaseButton-primary"] {
background-color: #4A4A4A !important;
color: white !important;
border: none !important;
border-radius: 6px !important;
transition: all 0.3s ease !important;
}
button:hover, [data-testid="stBaseButton-secondary"]:hover, [data-testid="stBaseButton-primary"]:hover {
background-color: #4A4A4A !important;
transform: translateY(-1px) !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important;
color: white !important;
}
button:active {
background-color: #991b1b !important;
transform: translateY(0px) !important;
}
/* Fix Help Tooltips (Black text on white background) */
div[data-testid="stTooltipContent"] {
background-color: #ffffff !important;
color: #000000 !important;
border: 1px solid #ddd !important;
}
div[data-testid="stTooltipContent"] p {
color: #000000 !important;
}
/* Ensure help icons themselves are visible */
[data-testid="stWidgetLabel"] .st-at {
color: #4A4A4A !important;
}
/* Tab and other refinements */
.stTabs [data-baseweb="tab-list"] {
gap: 24px;
background-color: transparent;
}
.stTabs [data-baseweb="tab"] {
height: 50px;
white-space: pre-wrap;
background-color: #1a1c24;
border-radius: 4px 4px 0px 0px;
color: #ffffff !important;
font-size: 16px;
font-weight: 500;
padding: 10px 20px;
}
.stTabs [aria-selected="true"] {
background-color: #6d28d9 !important;
border-bottom: 2px solid #8b5cf6 !important;
}
.pro-glass-card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
color: #ffffff;
}
.platform-card {
background: #1a1c24;
border-left: 5px solid #6d28d9;
padding: 15px;
border-radius: 8px;
margin-bottom: 10px;
color: #ffffff;
}
.sidebar-pro-cta {
background: linear-gradient(135deg, #6d28d9, #305CDE);
color: white !important;
padding: 15px;
border-radius: 8px;
text-align: center;
margin-bottom: 20px;
}
.sidebar-pro-cta small, .sidebar-pro-cta strong {
color: white !important;
}
</style>
""", unsafe_allow_html=True)
# Sidebar Branding & Pro CTA
with st.sidebar:
st.title("⚒️ AutoContentForge AI")
st.caption("Automate Your Content. Forge Every Platform.")
st.markdown("---")
st.markdown("""
<div class="sidebar-pro-cta">
<strong>🚀 Upgrade to Pro</strong><br>
<small>Self-Hosted Docker Version</small><br>
<br>
• Direct Social Posting<br>
• Full Bulk Scheduling<br>
• Video Automation<br>
• Google Docs sync
</div>
""", unsafe_allow_html=True)
if st.button("Contact for Pro Version", use_container_width=True):
st.info("Email: rajue@smackcoders.com with subject 'AutoContentForge AI Pro Inquiry'")
st.markdown("---")
st.markdown("### Resources")
st.markdown("[Documentation](#)")
st.markdown("[Pricing](#)")
st.markdown("[YouTube Channel](#)")
# Main Navigation Tabs
tab1, tab2, tab3 = st.tabs([
"📝 Single Post",
"📦 Bulk Posting",
"⚙️ Admin Settings"
])
# Initialize session states for the new workflow
if 'master_variations' not in st.session_state:
st.session_state.master_variations = None
if 'selected_variation' not in st.session_state:
st.session_state.selected_variation = None
if 'current_hashtags' not in st.session_state:
st.session_state.current_hashtags = ""
# Module 1: Single Post
with tab1:
st.header("📝 Create Single Social Post")
st.write("Fetch from a URL or use the manual editor to forge your master variations.")
col_in, col_out = st.columns([1, 1.2])
with col_in:
st.subheader("1. Source Content")
input_mode = st.radio(
"Input Mode",
["URL Fetch", "Manual Editor"],
horizontal=True,
help="Choose 'URL Fetch' to extract content from a website, or 'Manual Editor' to paste your own text directly."
)
if input_mode == "URL Fetch":
source_url = st.text_input(
"Enter Content URL",
placeholder="https://example.com/blog-post",
help="Paste the full link (including https://) of the article or blog post you want to repurpose."
)
if st.button("🚀 Fetch & Analyze"):
if not source_url or "://" not in source_url:
st.error("Please enter a valid URL (including https://)")
else:
with st.spinner("Forging 3 master variations..."):
extracted = ContentExtractor.from_url(source_url)
if "error" in extracted:
st.error(extracted["error"])
else:
st.session_state.extracted_content = extracted
st.session_state.master_variations = forge.generate_master_variations(extracted['text'])
st.session_state.current_hashtags = forge.extract_hashtags(extracted['text'])
st.session_state.selected_variation = None # Reset selection
st.success("Successfully generated 3 variations!")
else:
pasted_title = st.text_input("Post Title (Optional)", help="Give your post a title for easier tracking.")
pasted_text = st.text_area(
"Master Content / Article Text",
height=200,
help="Paste the core content you want to turn into social posts. Aim for at least 50 words for the best AI results."
)
if st.button("✨ Generate Variations"):
if len(pasted_text) < 50:
st.warning("Please provide more content for better AI results.")
else:
with st.spinner("Processing..."):
st.session_state.extracted_content = {"title": pasted_title or "Manual Input", "text": pasted_text}
st.session_state.master_variations = forge.generate_master_variations(pasted_text)
st.session_state.current_hashtags = forge.extract_hashtags(pasted_text)
st.session_state.selected_variation = None
st.success("Variations generated!")
if st.session_state.master_variations:
st.markdown("---")
st.subheader("2. Choose Master Variation")
st.info("💡 **Tip:** Each variation has a different tone. Expand them to see which one fits your brand voice best.")
for var in st.session_state.master_variations:
with st.expander(f"🔹 {var['type']}", expanded=(st.session_state.selected_variation == var['id'])):
st.write(var['text'])
if st.button(f"Select {var['type']}", key=f"sel_{var['id']}", help=f"Choose the {var['type']} to be adapted for your social networks."):
st.session_state.selected_variation = var['id']
st.rerun()
with col_out:
st.subheader("3. Preview & Adapt")
if not st.session_state.selected_variation:
st.info("Select a master variation on the left to see platform-specific previews.")
else:
selected_var_obj = next(v for v in st.session_state.master_variations if v['id'] == st.session_state.selected_variation)
platforms = st.multiselect(
"Select Social Networks",
["LinkedIn", "Twitter/X", "Instagram", "Facebook", "YouTube", "Bluesky"],
default=["LinkedIn", "Twitter/X"],
help="Choose which platforms you want to generate previews for. The AI will format your selected variation for each."
)
if st.session_state.extracted_content.get("images"):
st.image(st.session_state.extracted_content['images'][0], caption="Extracted Media", width=300)
else:
st.write("📸 *No media found. You can add media in the Pro version or manual editor.*")
st.markdown("---")
for platform in platforms:
adapted_text = forge.adapt_for_platform(selected_var_obj['text'], platform, st.session_state.current_hashtags)
with st.container():
st.markdown(f"""
<div class="platform-card">
<strong>{platform} Preview</strong><br>
<p style='font-size: 0.9em; color: #ccc;'>
{adapted_text if isinstance(adapted_text, str) else adapted_text.get('title')}
</p>
</div>
""", unsafe_allow_html=True)
if isinstance(adapted_text, dict) and platform == "YouTube":
st.caption(f"Description: {adapted_text.get('description')}")
if st.button("📅 Post / Schedule Now", type="primary"):
st.success("✅ Successfully queued! (Flow simulation)")
st.balloons()
st.info("🔒 In Pro: This would instantly publish or sync to your drag-and-drop calendar.")
# Module 2: Bulk Posting
with tab2:
st.header("📦 Bulk URL Posting")
st.write("Forge content from multiple URLs at once. Perfect for content migration or high-volume publishing.")
bulk_urls = st.text_area(
"List of URLs (one per line)",
height=250,
placeholder="https://site.com/post-1\nhttps://site.com/post-2",
help="Enter a list of blog post links. In the Pro version, the AI will process all of them in one batch."
)
st.markdown("""
<div class="pro-glass-card">
<strong>Pro Feature Highlights:</strong><br>
• Automatic batch processing of sitemaps<br>
• Intelligent image matching for bulk lists<br>
• Scheduled intervals (e.g., 1 post every 2 hours)<br>
• CSV / JSON Export for external schedulers
</div>
""", unsafe_allow_html=True)
st.button("🚀 Process Bulk List", disabled=True, help="This feature is exclusive to the Pro version.")
st.markdown("---")
if st.button("Unlock Bulk & Automation", key="bulk_pro", help="Contact us to upgrade to the full self-hosted version."):
st.info("Email: rajue@smackcoders.com with subject 'AutoContentForge AI Pro Inquiry'")
# Module 3: Admin Settings
with tab3:
st.header("⚙️ Admin Settings")
st.write("Authorize and manage your social network connections.")
networks = [
{"name": "LinkedIn", "icon": "👔"},
{"name": "Twitter/X", "icon": "🐦"},
{"name": "Instagram", "icon": "📸"},
{"name": "Facebook", "icon": "👥"},
{"name": "YouTube", "icon": "🎥"},
{"name": "Bluesky", "icon": "🦋"}
]
cols = st.columns(3)
for i, net in enumerate(networks):
with cols[i % 3]:
st.markdown(f"### {net['icon']} {net['name']}")
st.button(f"Authorize {net['name']}", key=f"auth_btn_{net['name']}", help=f"Connect your {net['name']} account via OAuth for direct posting.")
st.caption(f"Status: Not Connected")
st.markdown("---")
st.subheader("Global Settings")
st.checkbox("Auto-add smart hashtags to all posts", value=True, help="Automatically suggest and append hashtags based on content analysis.")
st.checkbox("Notify me on successful publishing", value=True, help="Send a notification once a post is live on the selected platform.")
st.selectbox("Default AI Tone", ["Professional", "Conversational", "Exciting", "Detailed"], help="Select the default voice for your AI-generated master variations.")
if st.button("Save Settings", help="Save your preferences for future sessions."):
st.success("Settings saved locally! (Simulation)")
|