File size: 6,217 Bytes
b27044a 337ef40 b27044a 337ef40 |
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 |
import streamlit as st
import random
import tempfile
import os
import subprocess
from datetime import datetime
from dotenv import load_dotenv
load_dotenv()
# -----------------------------
# Device base definitions
# -----------------------------
BASE_MODELS = [
"iPhone 11", "iPhone 11 Pro", "iPhone 12", "iPhone 12 Pro",
"iPhone 13", "iPhone 13 Pro", "iPhone 13 Pro Max",
"iPhone 14", "iPhone 14 Pro", "iPhone 14 Pro Max",
"iPhone 15", "iPhone 15 Plus", "iPhone 15 Pro", "iPhone 15 Pro Max",
"iPhone 16", "iPhone 16 Pro", "iPhone 16 Pro Max"
]
IOS_VERSIONS = [f"iOS {v}" for v in range(14, 21)]
ISO_SPEEDS = [100, 200, 400, 800, 1600]
F_NUMBERS = ["f/1.3", "f/1.4", "f/1.5", "f/1.6", "f/1.8"]
FOCAL_LENGTHS = ["3.99 mm", "4.0 mm", "4.2 mm", "4.3 mm", "5.0 mm", "5.1 mm", "5.2 mm", "6.0 mm", "6.1 mm", "6.2 mm", "6.5 mm", "6.6 mm", "6.7 mm"]
EXPOSURES = ["1/60", "1/70", "1/100", "1/110", "1/120", "1/130", "1/140", "1/200", "1/220", "1/240", "1/300", "1/320", "1/400", "1/500", "1/600", "1/700", "1/1000"]
# -----------------------------
# Metadata generation
# -----------------------------
def generate_max_random_metadata():
"""Generate fully randomized video metadata simulating a random iPhone."""
model = random.choice(BASE_MODELS)
f_number = random.choice(F_NUMBERS)
focal_length = random.choice(FOCAL_LENGTHS)
exposure = random.choice(EXPOSURES)
iso_speed = random.choice(ISO_SPEEDS)
ios_version = random.choice(IOS_VERSIONS)
latitude = round(random.uniform(-90, 90), 6)
longitude = round(random.uniform(-180, 180), 6)
altitude = round(random.uniform(0, 100), 1)
camera_id = f"{model}-back-{random.randint(1000,9999)}"
metadata = {
"com.apple.quicktime.make": "Apple",
"com.apple.quicktime.model": model,
"com.apple.quicktime.software": ios_version,
"com.apple.quicktime.creationdate": datetime.now().strftime("%Y-%m-%dT%H:%M:%S"),
"com.apple.quicktime.location.name": "Random Location",
"com.apple.quicktime.location.ISO6709": f"{latitude}+{longitude}/",
"com.apple.quicktime.lensmodel": f"{model} {focal_length} {f_number}",
"com.apple.quicktime.make.identifier": "Apple iPhone Camera",
"com.apple.quicktime.camera.identifier": camera_id,
"com.apple.quicktime.exposuretime": exposure,
"com.apple.quicktime.fnumber": f_number,
"com.apple.quicktime.ISOSpeed": str(iso_speed),
"com.apple.quicktime.focallength": focal_length,
"com.apple.quicktime.gps.latitude": str(latitude),
"com.apple.quicktime.gps.longitude": str(longitude),
"com.apple.quicktime.gps.altitude": str(altitude)
}
return metadata
# -----------------------------
# Metadata update using FFmpeg
# -----------------------------
def update_video_metadata(video_bytes: bytes) -> tuple[bytes, dict]:
"""Apply maximal randomized metadata to a video."""
metadata = generate_max_random_metadata()
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as temp_in:
temp_in.write(video_bytes)
temp_in_path = temp_in.name
fd, temp_out_path = tempfile.mkstemp(suffix=".mp4")
os.close(fd)
metadata_args = []
for key, value in metadata.items():
safe_value = str(value).replace(":", "-") # basic sanitization
metadata_args.extend(["-metadata", f"{key}={safe_value}"])
cmd = [
"ffmpeg", "-y", "-i", temp_in_path,
*metadata_args,
"-movflags", "use_metadata_tags",
"-map_metadata", "0",
"-codec", "copy",
temp_out_path
]
process = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if process.returncode != 0:
os.remove(temp_in_path)
if os.path.exists(temp_out_path):
os.remove(temp_out_path)
raise RuntimeError(f"FFmpeg failed: {process.stderr.decode(errors='ignore')}")
with open(temp_out_path, "rb") as f:
updated_bytes = f.read()
os.remove(temp_in_path)
os.remove(temp_out_path)
return updated_bytes, metadata
# -----------------------------
# Streamlit UI
# -----------------------------
# -----------------------------
# Access Control
# -----------------------------
ACCESS_TOKEN = os.getenv("ACCESS_TOKEN") # Change this to your desired password
# Check if user is authenticated
if "authenticated" not in st.session_state:
st.session_state.authenticated = False
# Show login form if not authenticated
if not st.session_state.authenticated:
st.title("π Video Metadata Editor")
st.markdown("---")
# Input field for access token
user_input = st.text_input(
"Enter Access Token:",
type="password",
placeholder="Enter your access token"
)
# Login button
login_button = st.button("Login", type="primary")
if login_button:
if user_input == ACCESS_TOKEN:
st.session_state.authenticated = True
st.rerun()
else:
st.error("β Invalid access token. Please try again.")
st.stop()
# Main application (only shown if authenticated)
col1, col2 = st.columns([5, 1])
with col1:
st.title("Video Metadata Editor")
with col2:
if st.button("Logout", type="secondary"):
st.session_state.authenticated = False
st.rerun()
uploaded_video = st.file_uploader("Upload an MP4 video", type=["mp4"])
if uploaded_video is not None:
st.subheader("Original Video")
st.video(uploaded_video)
if st.button("Change Metadata"):
with st.spinner("Applying metadata..."):
try:
video_bytes = uploaded_video.read()
updated_video, metadata = update_video_metadata(video_bytes)
st.success(f"Metadata updated successfully!")
st.subheader("Updated Video")
st.video(updated_video)
st.download_button(
"Download Updated Video",
updated_video,
file_name="updated_metadata.mp4",
mime="video/mp4"
)
except Exception as e:
st.error(f"Error: {e}") |