Upload app (4).py
Browse files- app (4).py +918 -0
app (4).py
ADDED
|
@@ -0,0 +1,918 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import requests
|
| 3 |
+
from PIL import Image, ImageDraw, ImageFont
|
| 4 |
+
import io
|
| 5 |
+
import time
|
| 6 |
+
import json
|
| 7 |
+
import base64
|
| 8 |
+
import uuid
|
| 9 |
+
import hashlib
|
| 10 |
+
import urllib.parse
|
| 11 |
+
import random
|
| 12 |
+
import datetime
|
| 13 |
+
import re
|
| 14 |
+
from collections import Counter
|
| 15 |
+
from bs4 import BeautifulSoup
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
# --- BATCH 1: MEDIA & FILE FUNCTIONS ---
|
| 20 |
+
|
| 21 |
+
def tool_youtube_downloader():
|
| 22 |
+
st.header("🎥 YouTube Media Extractor")
|
| 23 |
+
url = st.text_input("Paste YouTube URL", placeholder="https://youtube.com/...")
|
| 24 |
+
format_type = st.radio("Format", ["Video (MP4)", "Audio Only (MP3)"], horizontal=True)
|
| 25 |
+
|
| 26 |
+
if url and st.button("🚀 Process Media"):
|
| 27 |
+
with st.spinner("Contacting server..."):
|
| 28 |
+
try:
|
| 29 |
+
headers = {"Accept": "application/json", "Content-Type": "application/json"}
|
| 30 |
+
payload = {
|
| 31 |
+
"url": url,
|
| 32 |
+
"vQuality": "1080",
|
| 33 |
+
"isAudioOnly": True if "Audio" in format_type else False
|
| 34 |
+
}
|
| 35 |
+
# Using Cobalt API
|
| 36 |
+
response = requests.post("https://api.cobalt.tools/api/json", headers=headers, json=payload)
|
| 37 |
+
data = response.json()
|
| 38 |
+
|
| 39 |
+
if "url" in data:
|
| 40 |
+
st.success("✅ Ready!")
|
| 41 |
+
st.link_button(f"⬇️ Download {format_type}", data["url"])
|
| 42 |
+
if "Audio" not in format_type:
|
| 43 |
+
st.video(data["url"])
|
| 44 |
+
else:
|
| 45 |
+
st.audio(data["url"])
|
| 46 |
+
else:
|
| 47 |
+
st.error(f"Error: {data.get('text', 'Unknown error')}")
|
| 48 |
+
except Exception as e:
|
| 49 |
+
st.error(f"Connection failed: {str(e)}")
|
| 50 |
+
|
| 51 |
+
def tool_smart_converter():
|
| 52 |
+
with st.spinner("Starting File Engine..."):
|
| 53 |
+
|
| 54 |
+
time.sleep(0.3)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
import pandas as pd
|
| 58 |
+
st.header("🔄 Smart File Converter")
|
| 59 |
+
st.info("Supports: Images (PNG/JPG/WEBP) and Data (CSV/JSON/Excel)")
|
| 60 |
+
|
| 61 |
+
uploaded_file = st.file_uploader("Upload File", type=['png', 'jpg', 'jpeg', 'webp', 'csv', 'json', 'xlsx'],key="smart_conv_upload")
|
| 62 |
+
|
| 63 |
+
if uploaded_file:
|
| 64 |
+
file_type = uploaded_file.name.split('.')[-1].lower()
|
| 65 |
+
|
| 66 |
+
# LOGIC: IMAGE CONVERSION
|
| 67 |
+
if file_type in ['png', 'jpg', 'jpeg', 'webp']:
|
| 68 |
+
image = Image.open(uploaded_file)
|
| 69 |
+
st.image(image, caption="Preview", width=300)
|
| 70 |
+
target_format = st.selectbox("Convert to:", ["PNG", "JPEG", "WEBP", "PDF"])
|
| 71 |
+
|
| 72 |
+
if st.button("Convert Image"):
|
| 73 |
+
buf = io.BytesIO()
|
| 74 |
+
# RGB required for JPEG/PDF
|
| 75 |
+
if image.mode in ("RGBA", "P") and target_format in ["JPEG", "PDF"]:
|
| 76 |
+
image = image.convert("RGB")
|
| 77 |
+
|
| 78 |
+
image.save(buf, format=target_format)
|
| 79 |
+
st.download_button(f"Download {target_format}", data=buf.getvalue(), file_name=f"converted.{target_format.lower()}")
|
| 80 |
+
|
| 81 |
+
# LOGIC: DATA CONVERSION
|
| 82 |
+
elif file_type in ['csv', 'json', 'xlsx']:
|
| 83 |
+
df = None
|
| 84 |
+
try:
|
| 85 |
+
if file_type == 'csv': df = pd.read_csv(uploaded_file)
|
| 86 |
+
elif file_type == 'json': df = pd.read_json(uploaded_file)
|
| 87 |
+
elif file_type == 'xlsx': df = pd.read_excel(uploaded_file)
|
| 88 |
+
|
| 89 |
+
st.write("Data Preview:", df.head())
|
| 90 |
+
target_data = st.selectbox("Convert to:", ["CSV", "JSON", "Excel"])
|
| 91 |
+
|
| 92 |
+
if st.button("Convert Data"):
|
| 93 |
+
buf = io.BytesIO()
|
| 94 |
+
if target_data == "CSV":
|
| 95 |
+
df.to_csv(buf, index=False)
|
| 96 |
+
ext = "csv"
|
| 97 |
+
elif target_data == "JSON":
|
| 98 |
+
df.to_json(buf, orient='records')
|
| 99 |
+
ext = "json"
|
| 100 |
+
elif target_data == "Excel":
|
| 101 |
+
df.to_excel(buf, index=False)
|
| 102 |
+
ext = "xlsx"
|
| 103 |
+
|
| 104 |
+
st.download_button(f"Download {target_data}", data=buf.getvalue(), file_name=f"converted.{ext}")
|
| 105 |
+
except Exception as e:
|
| 106 |
+
st.error(f"Error reading file: {e}")
|
| 107 |
+
|
| 108 |
+
def tool_image_compressor():
|
| 109 |
+
st.header("📉 Image Compressor")
|
| 110 |
+
# ADD key="compressor"
|
| 111 |
+
uploaded_file = st.file_uploader("Upload Image", type=['png', 'jpg', 'jpeg'], key="compressor")
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
if uploaded_file:
|
| 116 |
+
image = Image.open(uploaded_file)
|
| 117 |
+
st.write(f"Original Size: {uploaded_file.size / 1024:.2f} KB")
|
| 118 |
+
quality = st.slider("Quality (Lower = Smaller file)", 10, 95, 60)
|
| 119 |
+
|
| 120 |
+
if st.button("Compress"):
|
| 121 |
+
buf = io.BytesIO()
|
| 122 |
+
if image.mode in ("RGBA", "P"): image = image.convert("RGB")
|
| 123 |
+
image.save(buf, format="JPEG", quality=quality, optimize=True)
|
| 124 |
+
|
| 125 |
+
size_kb = len(buf.getvalue()) / 1024
|
| 126 |
+
st.success(f"Compressed Size: {size_kb:.2f} KB")
|
| 127 |
+
st.download_button("Download Compressed Image", data=buf.getvalue(), file_name="compressed.jpg")
|
| 128 |
+
|
| 129 |
+
def tool_image_resizer():
|
| 130 |
+
st.header("📐 Image Resizer")
|
| 131 |
+
# ADD key="resizer"
|
| 132 |
+
uploaded_file = st.file_uploader("Upload Image", type=['png', 'jpg', 'jpeg', 'webp'], key="resizer")
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
if uploaded_file:
|
| 137 |
+
image = Image.open(uploaded_file)
|
| 138 |
+
st.write(f"Current Dimensions: {image.size}")
|
| 139 |
+
|
| 140 |
+
col1, col2 = st.columns(2)
|
| 141 |
+
w = col1.number_input("Width", value=image.width)
|
| 142 |
+
h = col2.number_input("Height", value=image.height)
|
| 143 |
+
|
| 144 |
+
if st.button("Resize"):
|
| 145 |
+
new_img = image.resize((int(w), int(h)))
|
| 146 |
+
buf = io.BytesIO()
|
| 147 |
+
new_img.save(buf, format=image.format)
|
| 148 |
+
st.image(new_img, caption="Resized Preview")
|
| 149 |
+
st.download_button("Download Resized Image", data=buf.getvalue(), file_name=f"resized.{image.format.lower()}")
|
| 150 |
+
|
| 151 |
+
def tool_thumbnail_generator():
|
| 152 |
+
st.header("🚀 Ultimate Thumbnail Studio")
|
| 153 |
+
|
| 154 |
+
# --- DEPENDENCIES ---
|
| 155 |
+
from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageEnhance, ImageOps, ImageColor
|
| 156 |
+
import io
|
| 157 |
+
|
| 158 |
+
# --- LAYOUT FIX FOR PC/MOBILE ---
|
| 159 |
+
# We use a container for the controls to keep them tidy on PC
|
| 160 |
+
# and "use_column_width" to fix the crash.
|
| 161 |
+
|
| 162 |
+
# Top row: All Controls (Split into 2 columns for better PC spacing)
|
| 163 |
+
with st.expander("🎨 Thumbnail Settings & Controls", expanded=True):
|
| 164 |
+
col_c1, col_c2 = st.columns(2)
|
| 165 |
+
|
| 166 |
+
# --- LEFT CONTROL COLUMN ---
|
| 167 |
+
with col_c1:
|
| 168 |
+
st.subheader("1. Background & Filters")
|
| 169 |
+
bg_mode = st.radio("Background Type", ["Upload Image", "Solid Color", "Gradient"], horizontal=True)
|
| 170 |
+
|
| 171 |
+
bg_image = None
|
| 172 |
+
if bg_mode == "Upload Image":
|
| 173 |
+
bg_file = st.file_uploader("Upload BG", type=['png', 'jpg', 'jpeg', 'webp'], key="max_bg")
|
| 174 |
+
if bg_file: bg_image = Image.open(bg_file).convert("RGBA")
|
| 175 |
+
elif bg_mode == "Solid Color":
|
| 176 |
+
hex_bg = st.color_picker("Pick Color", "#1E1E1E")
|
| 177 |
+
st.session_state.thumb_bg_color = hex_bg
|
| 178 |
+
else: # Gradient
|
| 179 |
+
c1_col, c2_col = st.columns(2)
|
| 180 |
+
grad_c1 = c1_col.color_picker("Start", "#12c2e9")
|
| 181 |
+
grad_c2 = c2_col.color_picker("End", "#c471ed")
|
| 182 |
+
grad_dir = st.selectbox("Direction", ["Horizontal", "Vertical"])
|
| 183 |
+
|
| 184 |
+
st.markdown("---")
|
| 185 |
+
st.write("**Filters**")
|
| 186 |
+
f1, f2 = st.columns(2)
|
| 187 |
+
blur_amt = f1.slider("Blur", 0, 20, 0)
|
| 188 |
+
brightness = f2.slider("Brightness", 0.5, 1.5, 1.0)
|
| 189 |
+
|
| 190 |
+
# --- RIGHT CONTROL COLUMN ---
|
| 191 |
+
with col_c2:
|
| 192 |
+
st.subheader("2. Text & Branding")
|
| 193 |
+
main_text = st.text_area("Main Title", "THE ULTIMATE\nGUIDE TO PYTHON", height=100)
|
| 194 |
+
|
| 195 |
+
t1, t2 = st.columns(2)
|
| 196 |
+
font_size = t1.slider("Size", 20, 200, 80)
|
| 197 |
+
font_color = t2.color_picker("Text Color", "#FFFFFF")
|
| 198 |
+
|
| 199 |
+
st.write("**Styling**")
|
| 200 |
+
s1, s2 = st.columns(2)
|
| 201 |
+
stroke_width = s1.slider("Outline", 0, 10, 2)
|
| 202 |
+
stroke_color = s2.color_picker("Outline Color", "#000000")
|
| 203 |
+
|
| 204 |
+
st.markdown("---")
|
| 205 |
+
logo_file = st.file_uploader("Upload Logo (PNG)", type=['png', 'webp'], key="max_logo")
|
| 206 |
+
if logo_file:
|
| 207 |
+
l1, l2 = st.columns(2)
|
| 208 |
+
logo_size = l1.slider("Logo Size", 50, 300, 150)
|
| 209 |
+
logo_pos = l2.selectbox("Position", ["Top-Right", "Top-Left", "Bottom-Right", "Bottom-Left"])
|
| 210 |
+
else:
|
| 211 |
+
logo_size = 150
|
| 212 |
+
logo_pos = "Top-Right"
|
| 213 |
+
|
| 214 |
+
# --- PREVIEW SECTION (Full Width for PC clarity) ---
|
| 215 |
+
st.subheader("👁️ Preview & Download")
|
| 216 |
+
|
| 217 |
+
# GENERATION LOGIC
|
| 218 |
+
CANVAS_W, CANVAS_H = 1280, 720
|
| 219 |
+
canvas = Image.new("RGBA", (CANVAS_W, CANVAS_H), (0,0,0,0))
|
| 220 |
+
|
| 221 |
+
# 1. Background
|
| 222 |
+
if bg_mode == "Upload Image" and bg_image:
|
| 223 |
+
bg_ratio = bg_image.width / bg_image.height
|
| 224 |
+
target_ratio = CANVAS_W / CANVAS_H
|
| 225 |
+
if bg_ratio > target_ratio:
|
| 226 |
+
new_h = CANVAS_H
|
| 227 |
+
new_w = int(new_h * bg_ratio)
|
| 228 |
+
else:
|
| 229 |
+
new_w = CANVAS_W
|
| 230 |
+
new_h = int(new_w / bg_ratio)
|
| 231 |
+
bg_image = bg_image.resize((new_w, new_h), Image.Resampling.LANCZOS)
|
| 232 |
+
left = (new_w - CANVAS_W)/2
|
| 233 |
+
top = (new_h - CANVAS_H)/2
|
| 234 |
+
bg_image = bg_image.crop((left, top, left+CANVAS_W, top+CANVAS_H))
|
| 235 |
+
canvas.paste(bg_image, (0,0))
|
| 236 |
+
elif bg_mode == "Gradient":
|
| 237 |
+
base = Image.new('RGB', (CANVAS_W, CANVAS_H), grad_c1)
|
| 238 |
+
top_img = Image.new('RGB', (CANVAS_W, CANVAS_H), grad_c2)
|
| 239 |
+
mask = Image.new("L", (CANVAS_W, CANVAS_H))
|
| 240 |
+
mask_data = []
|
| 241 |
+
for y in range(CANVAS_H):
|
| 242 |
+
for x in range(CANVAS_W):
|
| 243 |
+
if grad_dir == "Vertical": mask_data.append(int(255 * (y / CANVAS_H)))
|
| 244 |
+
else: mask_data.append(int(255 * (x / CANVAS_W)))
|
| 245 |
+
mask.putdata(mask_data)
|
| 246 |
+
canvas = Image.composite(top_img, base, mask).convert("RGBA")
|
| 247 |
+
else:
|
| 248 |
+
if 'thumb_bg_color' not in st.session_state: st.session_state.thumb_bg_color = "#1E1E1E"
|
| 249 |
+
canvas = Image.new("RGBA", (CANVAS_W, CANVAS_H), st.session_state.thumb_bg_color)
|
| 250 |
+
|
| 251 |
+
# 2. Filters
|
| 252 |
+
if blur_amt > 0: canvas = canvas.filter(ImageFilter.GaussianBlur(blur_amt))
|
| 253 |
+
if brightness != 1.0:
|
| 254 |
+
enhancer = ImageEnhance.Brightness(canvas)
|
| 255 |
+
canvas = enhancer.enhance(brightness)
|
| 256 |
+
|
| 257 |
+
# 3. Text
|
| 258 |
+
txt_layer = Image.new("RGBA", canvas.size, (255,255,255,0))
|
| 259 |
+
draw = ImageDraw.Draw(txt_layer)
|
| 260 |
+
|
| 261 |
+
try:
|
| 262 |
+
font = ImageFont.truetype("arialbd.ttf", font_size)
|
| 263 |
+
except:
|
| 264 |
+
try:
|
| 265 |
+
font = ImageFont.truetype("arial.ttf", font_size)
|
| 266 |
+
except:
|
| 267 |
+
font = ImageFont.load_default()
|
| 268 |
+
|
| 269 |
+
# Center Text Calculation
|
| 270 |
+
# Using basic textsize for compatibility if textbbox fails in older Pillow
|
| 271 |
+
try:
|
| 272 |
+
bbox = draw.multiline_textbbox((0,0), main_text, font=font, align="center")
|
| 273 |
+
text_w = bbox[2] - bbox[0]
|
| 274 |
+
text_h = bbox[3] - bbox[1]
|
| 275 |
+
except:
|
| 276 |
+
# Fallback for very old Pillow versions
|
| 277 |
+
text_w, text_h = draw.textsize(main_text, font=font)
|
| 278 |
+
|
| 279 |
+
cx, cy = CANVAS_W // 2, CANVAS_H // 2
|
| 280 |
+
text_x = cx - (text_w // 2)
|
| 281 |
+
text_y = cy - (text_h // 2)
|
| 282 |
+
|
| 283 |
+
# Shadow
|
| 284 |
+
draw.multiline_text((text_x + 8, text_y + 8), main_text, font=font, align="center", fill="black")
|
| 285 |
+
# Main Text
|
| 286 |
+
draw.multiline_text((text_x, text_y), main_text, font=font, align="center", fill=font_color, stroke_width=stroke_width, stroke_fill=stroke_color)
|
| 287 |
+
|
| 288 |
+
# 4. Logo
|
| 289 |
+
if logo_file:
|
| 290 |
+
logo = Image.open(logo_file).convert("RGBA")
|
| 291 |
+
logo.thumbnail((logo_size, logo_size), Image.Resampling.LANCZOS)
|
| 292 |
+
pad = 30
|
| 293 |
+
if "Top" in logo_pos: ly = pad
|
| 294 |
+
elif "Bottom" in logo_pos: ly = CANVAS_H - logo.height - pad
|
| 295 |
+
if "Left" in logo_pos: lx = pad
|
| 296 |
+
elif "Right" in logo_pos: lx = CANVAS_W - logo.width - pad
|
| 297 |
+
txt_layer.paste(logo, (lx, ly), logo)
|
| 298 |
+
|
| 299 |
+
# Final Composite
|
| 300 |
+
final_comp = Image.alpha_composite(canvas, txt_layer)
|
| 301 |
+
|
| 302 |
+
# --- DISPLAY & DOWNLOAD ---
|
| 303 |
+
# Centered layout for PC look
|
| 304 |
+
col_show, col_down = st.columns([3, 1])
|
| 305 |
+
|
| 306 |
+
with col_show:
|
| 307 |
+
# FIX: Changed use_container_width to use_column_width
|
| 308 |
+
st.image(final_comp, caption="Result", use_column_width=True)
|
| 309 |
+
|
| 310 |
+
with col_down:
|
| 311 |
+
st.write("### Ready?")
|
| 312 |
+
buf = io.BytesIO()
|
| 313 |
+
final_comp.convert("RGB").save(buf, format="PNG")
|
| 314 |
+
st.download_button("💾 Download PNG", data=buf.getvalue(), file_name="thumbnail.png", mime="image/png")
|
| 315 |
+
# --- MAIN ROUTER (Paste this at the VERY END of app.py) ---
|
| 316 |
+
# This checks the URL params and decides which function to run
|
| 317 |
+
if __name__ == "__main__":
|
| 318 |
+
st.set_page_config(page_title="Lexical Space Tools", layout="centered")
|
| 319 |
+
|
| 320 |
+
# Get the 'mode' from the URL (e.g. ?mode=youtube)
|
| 321 |
+
params = st.query_params
|
| 322 |
+
mode = params.get("mode", "home")
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
# --- BATCH 2: WEBMASTER & SEO FUNCTIONS ---
|
| 329 |
+
|
| 330 |
+
def tool_meta_tag_generator():
|
| 331 |
+
st.header("🏷️ Meta Tag Generator")
|
| 332 |
+
title = st.text_input("Site Title", "My Awesome Blog")
|
| 333 |
+
desc = st.text_area("Description", "A blog about technology and coding.")
|
| 334 |
+
keywords = st.text_input("Keywords (comma separated)", "tech, coding, python")
|
| 335 |
+
author = st.text_input("Author", "Lexical Space")
|
| 336 |
+
|
| 337 |
+
if st.button("Generate Tags"):
|
| 338 |
+
code = f"""
|
| 339 |
+
<title>{title}</title>
|
| 340 |
+
<meta name="description" content="{desc}">
|
| 341 |
+
<meta name="keywords" content="{keywords}">
|
| 342 |
+
<meta name="author" content="{author}">
|
| 343 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 344 |
+
<meta property="og:type" content="website">
|
| 345 |
+
<meta property="og:title" content="{title}">
|
| 346 |
+
<meta property="og:description" content="{desc}">
|
| 347 |
+
"""
|
| 348 |
+
st.code(code, language="html")
|
| 349 |
+
|
| 350 |
+
def tool_slug_generator():
|
| 351 |
+
st.header("🐌 URL Slug Generator")
|
| 352 |
+
text = st.text_input("Enter Post Title", "How to Install Python on Windows 10!")
|
| 353 |
+
|
| 354 |
+
if text:
|
| 355 |
+
# Lowercase, strip whitespace, replace spaces with dashes, remove non-alphanumeric
|
| 356 |
+
slug = text.lower().strip()
|
| 357 |
+
slug = re.sub(r'[^a-z0-9\s-]', '', slug)
|
| 358 |
+
slug = re.sub(r'[\s-]+', '-', slug)
|
| 359 |
+
st.success(f"Slug: {slug}")
|
| 360 |
+
st.code(slug, language="text")
|
| 361 |
+
|
| 362 |
+
def tool_robots_generator():
|
| 363 |
+
st.header("🤖 Robots.txt Generator")
|
| 364 |
+
st.write("Control which crawlers can access your site.")
|
| 365 |
+
|
| 366 |
+
all_agents = st.checkbox("Apply to all robots (*)", value=True)
|
| 367 |
+
disallow_admin = st.checkbox("Disallow /admin", value=True)
|
| 368 |
+
disallow_private = st.checkbox("Disallow /private", value=False)
|
| 369 |
+
sitemap_url = st.text_input("Sitemap URL (Optional)", "https://yoursite.com/sitemap.xml")
|
| 370 |
+
|
| 371 |
+
if st.button("Generate Robots.txt"):
|
| 372 |
+
agent = "*" if all_agents else "Googlebot"
|
| 373 |
+
txt = f"User-agent: {agent}\n"
|
| 374 |
+
if disallow_admin: txt += "Disallow: /admin/\n"
|
| 375 |
+
if disallow_private: txt += "Disallow: /private/\n"
|
| 376 |
+
if sitemap_url: txt += f"\nSitemap: {sitemap_url}"
|
| 377 |
+
|
| 378 |
+
st.text_area("Result", txt, height=150)
|
| 379 |
+
|
| 380 |
+
def tool_sitemap_builder():
|
| 381 |
+
st.header("🗺️ XML Sitemap Builder")
|
| 382 |
+
urls = st.text_area("Paste URLs (one per line)", "https://site.com\nhttps://site.com/about")
|
| 383 |
+
|
| 384 |
+
if st.button("Build XML"):
|
| 385 |
+
xml = '<?xml version="1.0" encoding="UTF-8"?>\n'
|
| 386 |
+
xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
|
| 387 |
+
|
| 388 |
+
for url in urls.split('\n'):
|
| 389 |
+
if url.strip():
|
| 390 |
+
xml += f' <url>\n <loc>{url.strip()}</loc>\n <changefreq>monthly</changefreq>\n </url>\n'
|
| 391 |
+
|
| 392 |
+
xml += '</urlset>'
|
| 393 |
+
st.text_area("Sitemap.xml", xml, height=200)
|
| 394 |
+
|
| 395 |
+
def tool_keyword_density():
|
| 396 |
+
with st.spinner("Loading Analytics..."):
|
| 397 |
+
import pandas as pd
|
| 398 |
+
from collections import Counter
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
st.header("📊 Keyword Density Checker")
|
| 402 |
+
text = st.text_area("Paste Article Text", height=200,key="density_text")
|
| 403 |
+
|
| 404 |
+
if st.button("Analyze"):
|
| 405 |
+
# Simple stopword list to ignore
|
| 406 |
+
stopwords = set(['the', 'and', 'is', 'in', 'it', 'of', 'to', 'a', 'for', 'on', 'that', 'with', 'as'])
|
| 407 |
+
|
| 408 |
+
words = re.findall(r'\w+', text.lower())
|
| 409 |
+
filtered = [w for w in words if w not in stopwords and len(w) > 2]
|
| 410 |
+
|
| 411 |
+
counts = Counter(filtered).most_common(10)
|
| 412 |
+
|
| 413 |
+
df = pd.DataFrame(counts, columns=["Keyword", "Count"])
|
| 414 |
+
df['Density %'] = (df['Count'] / len(words) * 100).round(2)
|
| 415 |
+
st.table(df)
|
| 416 |
+
|
| 417 |
+
def tool_plagiarism_check():
|
| 418 |
+
st.header("🕵️ Plagiarism Scanner (Google Check)")
|
| 419 |
+
st.info("Splits text into sentences and searches Google for exact matches.")
|
| 420 |
+
text = st.text_area("Paste Text to Check", height=150,key="plag_text")
|
| 421 |
+
|
| 422 |
+
if st.button("Check Text"):
|
| 423 |
+
sentences = re.split(r'[.!?]', text)
|
| 424 |
+
clean_sentences = [s.strip() for s in sentences if len(s.strip()) > 20]
|
| 425 |
+
|
| 426 |
+
for i, s in enumerate(clean_sentences[:5]): # Limit to first 5 for demo
|
| 427 |
+
query = f'"{s}"'
|
| 428 |
+
url = f"https://www.google.com/search?q={query}"
|
| 429 |
+
st.markdown(f"**Sentence {i+1}:** {s[:50]}...")
|
| 430 |
+
st.link_button(f"🔍 Check Google for Match", url)
|
| 431 |
+
|
| 432 |
+
def tool_code_minifier():
|
| 433 |
+
st.header("🧹 Code Minifier")
|
| 434 |
+
mode = st.radio("Type", ["HTML", "CSS"])
|
| 435 |
+
raw_code = st.text_area("Input Code", height=200)
|
| 436 |
+
|
| 437 |
+
if st.button("Minify"):
|
| 438 |
+
minified = ""
|
| 439 |
+
if mode == "HTML":
|
| 440 |
+
# Basic whitespace removal between tags
|
| 441 |
+
lines = raw_code.split('\n')
|
| 442 |
+
minified = "".join([line.strip() for line in lines])
|
| 443 |
+
elif mode == "CSS":
|
| 444 |
+
# Remove comments and whitespace
|
| 445 |
+
# 1. Remove comments
|
| 446 |
+
minified = re.sub(r'/\*[\s\S]*?\*/', '', raw_code)
|
| 447 |
+
# 2. Remove whitespace around braces/colons
|
| 448 |
+
minified = re.sub(r'\s*([{:;,])\s*', r'\1', minified)
|
| 449 |
+
# 3. Remove newlines
|
| 450 |
+
minified = minified.replace('\n', '').replace('\r', '')
|
| 451 |
+
|
| 452 |
+
st.text_area("Minified Output", minified, height=200)
|
| 453 |
+
# --- BATCH 3: DEVELOPER TOOLS ---
|
| 454 |
+
|
| 455 |
+
def tool_qr_generator():
|
| 456 |
+
with st.spinner("Initializing QR Tool..."):
|
| 457 |
+
import qrcode
|
| 458 |
+
import io
|
| 459 |
+
|
| 460 |
+
st.header("🏁 QR Code Generator")
|
| 461 |
+
data = st.text_input("Enter Link or Text", "https://lexicalspace.blogspot.com")
|
| 462 |
+
|
| 463 |
+
if data:
|
| 464 |
+
qr = qrcode.QRCode(version=1, box_size=10, border=5)
|
| 465 |
+
qr.add_data(data)
|
| 466 |
+
qr.make(fit=True)
|
| 467 |
+
img = qr.make_image(fill='black', back_color='white')
|
| 468 |
+
|
| 469 |
+
buf = io.BytesIO()
|
| 470 |
+
img.save(buf)
|
| 471 |
+
st.image(img.get_image(), width=300)
|
| 472 |
+
st.download_button("Download QR", data=buf.getvalue(), file_name="qrcode.png")
|
| 473 |
+
|
| 474 |
+
def tool_json_formatter():
|
| 475 |
+
st.header("✨ JSON Prettifier")
|
| 476 |
+
raw = st.text_area("Paste Messy JSON", '{"id":1,"name":"Lexical","roles":["admin","dev"]}', height=150)
|
| 477 |
+
|
| 478 |
+
col1, col2 = st.columns(2)
|
| 479 |
+
if col1.button("Format (Pretty)"):
|
| 480 |
+
try:
|
| 481 |
+
parsed = json.loads(raw)
|
| 482 |
+
st.code(json.dumps(parsed, indent=4), language="json")
|
| 483 |
+
except Exception as e:
|
| 484 |
+
st.error(f"Invalid JSON: {e}")
|
| 485 |
+
|
| 486 |
+
if col2.button("Minify (Compact)"):
|
| 487 |
+
try:
|
| 488 |
+
parsed = json.loads(raw)
|
| 489 |
+
st.code(json.dumps(parsed, separators=(',', ':')), language="json")
|
| 490 |
+
except Exception as e:
|
| 491 |
+
st.error(f"Invalid JSON: {e}")
|
| 492 |
+
|
| 493 |
+
def tool_base64():
|
| 494 |
+
st.header("🔐 Base64 Converter")
|
| 495 |
+
mode = st.radio("Action", ["Encode", "Decode"], horizontal=True)
|
| 496 |
+
text = st.text_area("Input Text")
|
| 497 |
+
|
| 498 |
+
if st.button("Process"):
|
| 499 |
+
try:
|
| 500 |
+
if mode == "Encode":
|
| 501 |
+
res = base64.b64encode(text.encode()).decode()
|
| 502 |
+
else:
|
| 503 |
+
res = base64.b64decode(text).decode()
|
| 504 |
+
st.code(res)
|
| 505 |
+
except Exception as e:
|
| 506 |
+
st.error(f"Error: {e}")
|
| 507 |
+
|
| 508 |
+
def tool_url_encoder():
|
| 509 |
+
st.header("🔗 URL Encoder/Decoder")
|
| 510 |
+
text = st.text_input("Input URL", "https://example.com/search?q=hello world")
|
| 511 |
+
|
| 512 |
+
c1, c2 = st.columns(2)
|
| 513 |
+
with c1:
|
| 514 |
+
if st.button("Encode"):
|
| 515 |
+
st.code(urllib.parse.quote(text))
|
| 516 |
+
with c2:
|
| 517 |
+
if st.button("Decode"):
|
| 518 |
+
st.code(urllib.parse.unquote(text))
|
| 519 |
+
|
| 520 |
+
def tool_markdown_editor():
|
| 521 |
+
st.header("📝 Markdown Editor")
|
| 522 |
+
|
| 523 |
+
col1, col2 = st.columns(2)
|
| 524 |
+
with col1:
|
| 525 |
+
md_text = st.text_area("Write Markdown", "# Hello\n* Item 1\n* Item 2", height=400)
|
| 526 |
+
|
| 527 |
+
with col2:
|
| 528 |
+
st.markdown("### Preview")
|
| 529 |
+
st.markdown(md_text)
|
| 530 |
+
|
| 531 |
+
def tool_regex_tester():
|
| 532 |
+
st.header("🧪 Regex Tester")
|
| 533 |
+
pattern = st.text_input("Regex Pattern", r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
|
| 534 |
+
text = st.text_area("Test String", "Contact us at support@lexical.com or admin@site.org")
|
| 535 |
+
|
| 536 |
+
if pattern and text:
|
| 537 |
+
try:
|
| 538 |
+
matches = re.findall(pattern, text)
|
| 539 |
+
st.write(f"Found {len(matches)} matches:")
|
| 540 |
+
st.json(matches)
|
| 541 |
+
except Exception as e:
|
| 542 |
+
st.error(f"Regex Error: {e}")
|
| 543 |
+
|
| 544 |
+
def tool_uuid_gen():
|
| 545 |
+
st.header("🆔 UUID/GUID Generator")
|
| 546 |
+
count = st.number_input("How many?", 1, 100, 5)
|
| 547 |
+
|
| 548 |
+
if st.button("Generate"):
|
| 549 |
+
uuids = [str(uuid.uuid4()) for _ in range(count)]
|
| 550 |
+
st.code("\n".join(uuids), language="text")
|
| 551 |
+
|
| 552 |
+
def tool_hash_gen():
|
| 553 |
+
st.header("🔑 Hash Generator")
|
| 554 |
+
text = st.text_input("Input String", "mypassword")
|
| 555 |
+
|
| 556 |
+
if text:
|
| 557 |
+
st.write("**MD5:**")
|
| 558 |
+
st.code(hashlib.md5(text.encode()).hexdigest())
|
| 559 |
+
st.write("**SHA256:**")
|
| 560 |
+
st.code(hashlib.sha256(text.encode()).hexdigest())
|
| 561 |
+
# ... (Batch 1 & 2 Routing above) ...
|
| 562 |
+
|
| 563 |
+
# BATCH 3 ROUTING
|
| 564 |
+
elif mode == "qrcode": tool_qr_generator()
|
| 565 |
+
elif mode == "json": tool_json_formatter()
|
| 566 |
+
elif mode == "base64": tool_base64()
|
| 567 |
+
elif mode == "url": tool_url_encoder()
|
| 568 |
+
elif mode == "markdown": tool_markdown_editor()
|
| 569 |
+
elif mode == "regex": tool_regex_tester()
|
| 570 |
+
elif mode == "uuid": tool_uuid_gen()
|
| 571 |
+
elif mode == "hash": tool_hash_gen()
|
| 572 |
+
|
| 573 |
+
# ... (Home Dashboard below) ...
|
| 574 |
+
|
| 575 |
+
st.write("### 🛠️ Developer Tools")
|
| 576 |
+
st.markdown("""
|
| 577 |
+
* [🏁 QR Code Gen](?mode=qrcode)
|
| 578 |
+
* [✨ JSON Prettifier](?mode=json)
|
| 579 |
+
* [🔐 Base64 Converter](?mode=base64)
|
| 580 |
+
* [🔗 URL Encode/Decode](?mode=url)
|
| 581 |
+
* [📝 Markdown Editor](?mode=markdown)
|
| 582 |
+
* [🧪 Regex Tester](?mode=regex)
|
| 583 |
+
* [🆔 UUID Generator](?mode=uuid)
|
| 584 |
+
* [🔑 Hash Generator](?mode=hash)
|
| 585 |
+
""")
|
| 586 |
+
# --- BATCH 4: TEXT, UTILITIES & EXTRAS ---
|
| 587 |
+
|
| 588 |
+
def tool_case_converter():
|
| 589 |
+
st.header("🔠 Case Converter")
|
| 590 |
+
text = st.text_area("Input Text", "hello world")
|
| 591 |
+
|
| 592 |
+
c1, c2, c3, c4 = st.columns(4)
|
| 593 |
+
if c1.button("UPPERCASE"): st.code(text.upper(), language="text")
|
| 594 |
+
if c2.button("lowercase"): st.code(text.lower(), language="text")
|
| 595 |
+
if c3.button("Title Case"): st.code(text.title(), language="text")
|
| 596 |
+
if c4.button("aLtErNaTiNg"):
|
| 597 |
+
res = "".join([c.upper() if i%2==0 else c.lower() for i, c in enumerate(text)])
|
| 598 |
+
st.code(res, language="text")
|
| 599 |
+
|
| 600 |
+
def tool_lorem_ipsum():
|
| 601 |
+
st.header("📜 Lorem Ipsum Generator")
|
| 602 |
+
paras = st.slider("Paragraphs", 1, 10, 3)
|
| 603 |
+
|
| 604 |
+
dummy_text = [
|
| 605 |
+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
| 606 |
+
"Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
| 607 |
+
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.",
|
| 608 |
+
"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum.",
|
| 609 |
+
"Excepteur sint occaecat cupidatat non proident, sunt in culpa."
|
| 610 |
+
]
|
| 611 |
+
|
| 612 |
+
if st.button("Generate"):
|
| 613 |
+
result = "\n\n".join([random.choice(dummy_text) * 3 for _ in range(paras)])
|
| 614 |
+
st.text_area("Result", result, height=200)
|
| 615 |
+
|
| 616 |
+
def tool_word_counter():
|
| 617 |
+
st.header("🧮 Word & Character Counter")
|
| 618 |
+
text = st.text_area("Paste Text Here", height=200,key="counter_text")
|
| 619 |
+
|
| 620 |
+
if text:
|
| 621 |
+
words = len(text.split())
|
| 622 |
+
chars = len(text)
|
| 623 |
+
no_space = len(text.replace(" ", ""))
|
| 624 |
+
read_time = round(words / 200, 2)
|
| 625 |
+
|
| 626 |
+
c1, c2, c3, c4 = st.columns(4)
|
| 627 |
+
c1.metric("Words", words)
|
| 628 |
+
c2.metric("Chars", chars)
|
| 629 |
+
c3.metric("No Spaces", no_space)
|
| 630 |
+
c4.metric("Read Time", f"{read_time} min")
|
| 631 |
+
|
| 632 |
+
def tool_remove_duplicates():
|
| 633 |
+
st.header("🗑️ Remove Duplicate Lines")
|
| 634 |
+
text = st.text_area("Paste List (One per line)", "Apple\nBanana\nApple\nOrange")
|
| 635 |
+
|
| 636 |
+
if st.button("Clean"):
|
| 637 |
+
lines = text.split('\n')
|
| 638 |
+
seen = set()
|
| 639 |
+
clean = []
|
| 640 |
+
for line in lines:
|
| 641 |
+
if line not in seen and line.strip():
|
| 642 |
+
clean.append(line)
|
| 643 |
+
seen.add(line)
|
| 644 |
+
st.text_area("Cleaned List", "\n".join(clean), height=200)
|
| 645 |
+
|
| 646 |
+
def tool_text_to_speech():
|
| 647 |
+
with st.spinner("Loading Audio Engine..."):
|
| 648 |
+
from gtts import gTTS
|
| 649 |
+
import io
|
| 650 |
+
|
| 651 |
+
st.header("🗣️ Text to Speech")
|
| 652 |
+
text = st.text_area("Enter Text", "Hello, welcome to Lexical Space.")
|
| 653 |
+
lang = st.selectbox("Language", ["en", "es", "fr", "de", "hi"],key="tts_text")
|
| 654 |
+
|
| 655 |
+
if st.button("Speak"):
|
| 656 |
+
try:
|
| 657 |
+
tts = gTTS(text=text, lang=lang, slow=False)
|
| 658 |
+
buf = io.BytesIO()
|
| 659 |
+
tts.write_to_fp(buf)
|
| 660 |
+
st.audio(buf, format='audio/mp3')
|
| 661 |
+
except Exception as e:
|
| 662 |
+
st.error(f"Error: {e}")
|
| 663 |
+
|
| 664 |
+
def tool_timestamp():
|
| 665 |
+
st.header("⏰ Unix Timestamp Converter")
|
| 666 |
+
now = int(time.time())
|
| 667 |
+
st.write(f"Current Timestamp: `{now}`")
|
| 668 |
+
|
| 669 |
+
col1, col2 = st.columns(2)
|
| 670 |
+
with col1:
|
| 671 |
+
ts_input = st.number_input("Timestamp to Date", value=now)
|
| 672 |
+
if st.button("Convert to Date"):
|
| 673 |
+
st.success(datetime.datetime.fromtimestamp(ts_input))
|
| 674 |
+
|
| 675 |
+
with col2:
|
| 676 |
+
d_input = st.date_input("Date to Timestamp")
|
| 677 |
+
if st.button("Convert to Timestamp"):
|
| 678 |
+
ts = int(time.mktime(d_input.timetuple()))
|
| 679 |
+
st.success(ts)
|
| 680 |
+
|
| 681 |
+
def tool_color_palette():
|
| 682 |
+
st.header("🎨 Image Color Palette")
|
| 683 |
+
# ADD key="palette"
|
| 684 |
+
uploaded_file = st.file_uploader("Upload Image", type=['jpg', 'png'], key="palette")
|
| 685 |
+
|
| 686 |
+
|
| 687 |
+
|
| 688 |
+
if uploaded_file:
|
| 689 |
+
img = Image.open(uploaded_file).convert("RGB")
|
| 690 |
+
st.image(img, width=200)
|
| 691 |
+
|
| 692 |
+
# Simple extraction by resizing to 5 pixels
|
| 693 |
+
small = img.resize((5, 1))
|
| 694 |
+
colors = small.getdata()
|
| 695 |
+
|
| 696 |
+
st.write("Dominant Colors:")
|
| 697 |
+
cols = st.columns(5)
|
| 698 |
+
for i, color in enumerate(colors):
|
| 699 |
+
hex_code = '#{:02x}{:02x}{:02x}'.format(*color)
|
| 700 |
+
cols[i].color_picker(f"Color {i+1}", hex_code, disabled=True)
|
| 701 |
+
cols[i].code(hex_code)
|
| 702 |
+
|
| 703 |
+
def tool_password_strength():
|
| 704 |
+
st.header("💪 Password Strength")
|
| 705 |
+
pwd = st.text_input("Test Password", type="password")
|
| 706 |
+
|
| 707 |
+
if pwd:
|
| 708 |
+
score = 0
|
| 709 |
+
if len(pwd) >= 8: score += 1
|
| 710 |
+
if re.search(r"[A-Z]", pwd): score += 1
|
| 711 |
+
if re.search(r"[a-z]", pwd): score += 1
|
| 712 |
+
if re.search(r"\d", pwd): score += 1
|
| 713 |
+
if re.search(r"[!@#$%^&*]", pwd): score += 1
|
| 714 |
+
|
| 715 |
+
st.progress(score / 5)
|
| 716 |
+
if score < 3: st.warning("Weak")
|
| 717 |
+
elif score < 5: st.info("Moderate")
|
| 718 |
+
else: st.success("Strong!")
|
| 719 |
+
|
| 720 |
+
def tool_aspect_ratio():
|
| 721 |
+
st.header("🖥️ Aspect Ratio Calculator")
|
| 722 |
+
w = st.number_input("Width", 1920)
|
| 723 |
+
h = st.number_input("Height", 1080)
|
| 724 |
+
|
| 725 |
+
if w and h:
|
| 726 |
+
def gcd(a, b):
|
| 727 |
+
while b: a, b = b, a % b
|
| 728 |
+
return a
|
| 729 |
+
divisor = gcd(int(w), int(h))
|
| 730 |
+
st.metric("Aspect Ratio", f"{int(w/divisor)}:{int(h/divisor)}")
|
| 731 |
+
|
| 732 |
+
def tool_stopwatch():
|
| 733 |
+
st.header("⏱️ Stopwatch")
|
| 734 |
+
if 'start_time' not in st.session_state: st.session_state.start_time = None
|
| 735 |
+
|
| 736 |
+
if st.button("Start/Reset"):
|
| 737 |
+
st.session_state.start_time = time.time()
|
| 738 |
+
|
| 739 |
+
if st.session_state.start_time:
|
| 740 |
+
elapsed = time.time() - st.session_state.start_time
|
| 741 |
+
st.metric("Time Elapsed", f"{elapsed:.2f}s")
|
| 742 |
+
if st.button("Stop"):
|
| 743 |
+
st.session_state.start_time = None
|
| 744 |
+
|
| 745 |
+
|
| 746 |
+
def tool_python_checker():
|
| 747 |
+
st.header("🐍 Python Syntax & Error Checker")
|
| 748 |
+
st.markdown("Paste your Python code or upload a `.py` file to check for syntax errors.")
|
| 749 |
+
|
| 750 |
+
# Import dependencies inside the function to avoid global scope clutter
|
| 751 |
+
import tempfile
|
| 752 |
+
import os
|
| 753 |
+
import io
|
| 754 |
+
try:
|
| 755 |
+
from pylint.lint import Run
|
| 756 |
+
from pylint.reporters.text import TextReporter
|
| 757 |
+
except ImportError:
|
| 758 |
+
st.error("⚠️ Pylint is not installed. Please add `pylint` to your requirements.txt")
|
| 759 |
+
return
|
| 760 |
+
|
| 761 |
+
# --- INPUTS ---
|
| 762 |
+
col1, col2 = st.columns(2)
|
| 763 |
+
with col1:
|
| 764 |
+
paste_code = st.text_area("Paste Code Here", height=300)
|
| 765 |
+
with col2:
|
| 766 |
+
file_obj = st.file_uploader("Or Upload .py File", type=[".py"])
|
| 767 |
+
|
| 768 |
+
# --- PROCESS BUTTON ---
|
| 769 |
+
if st.button("Check Syntax 🚀", type="primary"):
|
| 770 |
+
code_to_check = ""
|
| 771 |
+
|
| 772 |
+
# Logic: Prefer File > Paste
|
| 773 |
+
if file_obj is not None:
|
| 774 |
+
try:
|
| 775 |
+
code_to_check = file_obj.getvalue().decode("utf-8")
|
| 776 |
+
except Exception as e:
|
| 777 |
+
st.error(f"❌ Error reading file: {str(e)}")
|
| 778 |
+
return
|
| 779 |
+
elif paste_code.strip() != "":
|
| 780 |
+
code_to_check = paste_code
|
| 781 |
+
else:
|
| 782 |
+
st.warning("⚠️ Please either paste code or upload a file.")
|
| 783 |
+
return
|
| 784 |
+
|
| 785 |
+
# --- LINTING LOGIC ---
|
| 786 |
+
# 1. Create Temp File (Pylint needs a file on disk)
|
| 787 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".py", mode='w', encoding='utf-8') as temp:
|
| 788 |
+
temp.write(code_to_check)
|
| 789 |
+
temp_path = temp.name
|
| 790 |
+
|
| 791 |
+
# 2. Run Pylint
|
| 792 |
+
pylint_output = io.StringIO()
|
| 793 |
+
reporter = TextReporter(pylint_output)
|
| 794 |
+
|
| 795 |
+
with st.spinner("Analyzing syntax..."):
|
| 796 |
+
try:
|
| 797 |
+
# --errors-only hides warnings, showing only code-breaking errors
|
| 798 |
+
Run([temp_path, "--errors-only"], reporter=reporter, exit=False)
|
| 799 |
+
except Exception as e:
|
| 800 |
+
st.error(f"System Error: {e}")
|
| 801 |
+
|
| 802 |
+
# 3. Cleanup & Display
|
| 803 |
+
os.unlink(temp_path) # Delete temp file
|
| 804 |
+
result = pylint_output.getvalue()
|
| 805 |
+
|
| 806 |
+
st.markdown("---")
|
| 807 |
+
if not result:
|
| 808 |
+
st.success("✅ No Syntax Errors Found! (Code looks valid)")
|
| 809 |
+
st.balloons()
|
| 810 |
+
else:
|
| 811 |
+
st.error("❌ Errors Found:")
|
| 812 |
+
# Hide the messy temp file path from the user
|
| 813 |
+
clean_report = result.replace(temp_path, "Your_Script.py")
|
| 814 |
+
st.code(clean_report, language="text")
|
| 815 |
+
|
| 816 |
+
|
| 817 |
+
# --- FINAL MAIN ROUTER (SPA VERSION) ---
|
| 818 |
+
if __name__ == "__main__":
|
| 819 |
+
# 1. INITIALIZE SESSION STATE (This replaces the URL logic)
|
| 820 |
+
if 'mode' not in st.session_state:
|
| 821 |
+
st.session_state.mode = 'home'
|
| 822 |
+
|
| 823 |
+
|
| 824 |
+
# 3. NAVIGATION HANDLING
|
| 825 |
+
# Function to change mode without URL reload
|
| 826 |
+
def set_mode(new_mode):
|
| 827 |
+
st.session_state.mode = new_mode
|
| 828 |
+
|
| 829 |
+
# Show "Back to Dashboard" button if not home
|
| 830 |
+
if st.session_state.mode != 'home':
|
| 831 |
+
if st.button("⬅️ Back to Grid"):
|
| 832 |
+
set_mode('home')
|
| 833 |
+
st.rerun()
|
| 834 |
+
|
| 835 |
+
# 4. TOOL ROUTING (Checks session_state instead of URL)
|
| 836 |
+
mode = st.session_state.mode
|
| 837 |
+
|
| 838 |
+
if mode == "youtube": tool_youtube_downloader()
|
| 839 |
+
elif mode == "smart_converter": tool_smart_converter()
|
| 840 |
+
elif mode == "compressor": tool_image_compressor()
|
| 841 |
+
elif mode == "resizer": tool_image_resizer()
|
| 842 |
+
elif mode == "thumbnail": tool_thumbnail_generator()
|
| 843 |
+
elif mode == "metatags": tool_meta_tag_generator()
|
| 844 |
+
elif mode == "slug": tool_slug_generator()
|
| 845 |
+
elif mode == "robots": tool_robots_generator()
|
| 846 |
+
elif mode == "sitemap": tool_sitemap_builder()
|
| 847 |
+
elif mode == "density": tool_keyword_density()
|
| 848 |
+
elif mode == "plagiarism": tool_plagiarism_check()
|
| 849 |
+
elif mode == "minify": tool_code_minifier()
|
| 850 |
+
elif mode == "qrcode": tool_qr_generator()
|
| 851 |
+
elif mode == "json": tool_json_formatter()
|
| 852 |
+
elif mode == "base64": tool_base64()
|
| 853 |
+
elif mode == "url": tool_url_encoder()
|
| 854 |
+
elif mode == "markdown": tool_markdown_editor()
|
| 855 |
+
elif mode == "regex": tool_regex_tester()
|
| 856 |
+
elif mode == "uuid": tool_uuid_gen()
|
| 857 |
+
elif mode == "hash": tool_hash_gen()
|
| 858 |
+
elif mode == "case": tool_case_converter()
|
| 859 |
+
elif mode == "lorem": tool_lorem_ipsum()
|
| 860 |
+
elif mode == "counter": tool_word_counter()
|
| 861 |
+
elif mode == "dedupe": tool_remove_duplicates()
|
| 862 |
+
elif mode == "tts": tool_text_to_speech()
|
| 863 |
+
elif mode == "timestamp": tool_timestamp()
|
| 864 |
+
elif mode == "palette": tool_color_palette()
|
| 865 |
+
elif mode == "password": tool_password_strength()
|
| 866 |
+
elif mode == "ratio": tool_aspect_ratio()
|
| 867 |
+
elif mode == "stopwatch": tool_stopwatch()
|
| 868 |
+
elif mode == "python": tool_python_checker()
|
| 869 |
+
|
| 870 |
+
# 5. HOME DASHBOARD (Button Grid)
|
| 871 |
+
else:
|
| 872 |
+
st.write("### ⚡ Select a tool to get started:")
|
| 873 |
+
|
| 874 |
+
# We use standard Streamlit columns to create a grid layout
|
| 875 |
+
# This replaces the Markdown links with actual Buttons
|
| 876 |
+
|
| 877 |
+
c1, c2 = st.columns(2)
|
| 878 |
+
|
| 879 |
+
with c1:
|
| 880 |
+
st.info("**📂 Media & Files**")
|
| 881 |
+
if st.button("🎥 YouTube Downloader"): set_mode("youtube"); st.rerun()
|
| 882 |
+
if st.button("🔄 Smart File Converter"): set_mode("smart_converter"); st.rerun()
|
| 883 |
+
if st.button("📉 Image Compressor"): set_mode("compressor"); st.rerun()
|
| 884 |
+
if st.button("📐 Image Resizer"): set_mode("resizer"); st.rerun()
|
| 885 |
+
if st.button("🖼️ Thumbnail Gen"): set_mode("thumbnail"); st.rerun()
|
| 886 |
+
|
| 887 |
+
st.info("**🛠️ Developer Tools**")
|
| 888 |
+
if st.button("🏁 QR Code Gen"): set_mode("qrcode"); st.rerun()
|
| 889 |
+
if st.button("✨ JSON Prettifier"): set_mode("json"); st.rerun()
|
| 890 |
+
if st.button("🔐 Base64 Converter"): set_mode("base64"); st.rerun()
|
| 891 |
+
if st.button("🔗 URL Encoder"): set_mode("url"); st.rerun()
|
| 892 |
+
if st.button("📝 Markdown Editor"): set_mode("markdown"); st.rerun()
|
| 893 |
+
if st.button("🧪 Regex Tester"): set_mode("regex"); st.rerun()
|
| 894 |
+
if st.button("🆔 UUID Gen"): set_mode("uuid"); st.rerun()
|
| 895 |
+
if st.button("🔑 Hash Gen"): set_mode("hash"); st.rerun()
|
| 896 |
+
|
| 897 |
+
with c2:
|
| 898 |
+
st.info("**🕸️ SEO & Webmaster**")
|
| 899 |
+
if st.button("🏷️ Meta Tag Gen"): set_mode("metatags"); st.rerun()
|
| 900 |
+
if st.button("🐌 Slug Generator"): set_mode("slug"); st.rerun()
|
| 901 |
+
if st.button("🤖 Robots.txt Gen"): set_mode("robots"); st.rerun()
|
| 902 |
+
if st.button("🗺️ Sitemap Builder"): set_mode("sitemap"); st.rerun()
|
| 903 |
+
if st.button("📊 Density Checker"): set_mode("density"); st.rerun()
|
| 904 |
+
if st.button("🕵️ Plagiarism Check"): set_mode("plagiarism"); st.rerun()
|
| 905 |
+
if st.button("🧹 Code Minifier"): set_mode("minify"); st.rerun()
|
| 906 |
+
|
| 907 |
+
st.info("**📝 Text & Utilities**")
|
| 908 |
+
if st.button("🔠 Case Converter"): set_mode("case"); st.rerun()
|
| 909 |
+
if st.button("📜 Lorem Ipsum"): set_mode("lorem"); st.rerun()
|
| 910 |
+
if st.button("🧮 Word Counter"): set_mode("counter"); st.rerun()
|
| 911 |
+
if st.button("🗑️ Dedupe Lines"): set_mode("dedupe"); st.rerun()
|
| 912 |
+
if st.button("🗣️ Text to Speech"): set_mode("tts"); st.rerun()
|
| 913 |
+
if st.button("⏰ Unix Timestamp"): set_mode("timestamp"); st.rerun()
|
| 914 |
+
if st.button("🎨 Color Palette"): set_mode("palette"); st.rerun()
|
| 915 |
+
if st.button("💪 Password Strength"): set_mode("password"); st.rerun()
|
| 916 |
+
if st.button("🖥️ Aspect Ratio"): set_mode("ratio"); st.rerun()
|
| 917 |
+
if st.button("⏱️ Stopwatch"): set_mode("stopwatch"); st.rerun()
|
| 918 |
+
if st.button("Python Checker"): set_mode("python"); st.rerun()
|