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}")