userIdc2024 commited on
Commit
337ef40
·
verified ·
1 Parent(s): 6747cd0

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +178 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,180 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import random
3
+ import tempfile
4
+ import os
5
+ import subprocess
6
+ from datetime import datetime
7
+ from dotenv import load_dotenv
8
 
9
+ load_dotenv()
10
+
11
+ # -----------------------------
12
+ # Device base definitions
13
+ # -----------------------------
14
+ BASE_MODELS = [
15
+ "iPhone 11", "iPhone 11 Pro", "iPhone 12", "iPhone 12 Pro",
16
+ "iPhone 13", "iPhone 13 Pro", "iPhone 13 Pro Max",
17
+ "iPhone 14", "iPhone 14 Pro", "iPhone 14 Pro Max",
18
+ "iPhone 15", "iPhone 15 Plus", "iPhone 15 Pro", "iPhone 15 Pro Max",
19
+ "iPhone 16", "iPhone 16 Pro", "iPhone 16 Pro Max"
20
+ ]
21
+
22
+ IOS_VERSIONS = [f"iOS {v}" for v in range(14, 21)]
23
+ ISO_SPEEDS = [100, 200, 400, 800, 1600]
24
+ F_NUMBERS = ["f/1.3", "f/1.4", "f/1.5", "f/1.6", "f/1.8"]
25
+ 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"]
26
+ 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"]
27
+
28
+ # -----------------------------
29
+ # Metadata generation
30
+ # -----------------------------
31
+ def generate_max_random_metadata():
32
+ """Generate fully randomized video metadata simulating a random iPhone."""
33
+ model = random.choice(BASE_MODELS)
34
+ f_number = random.choice(F_NUMBERS)
35
+ focal_length = random.choice(FOCAL_LENGTHS)
36
+ exposure = random.choice(EXPOSURES)
37
+ iso_speed = random.choice(ISO_SPEEDS)
38
+ ios_version = random.choice(IOS_VERSIONS)
39
+
40
+ latitude = round(random.uniform(-90, 90), 6)
41
+ longitude = round(random.uniform(-180, 180), 6)
42
+ altitude = round(random.uniform(0, 100), 1)
43
+ camera_id = f"{model}-back-{random.randint(1000,9999)}"
44
+
45
+ metadata = {
46
+ "com.apple.quicktime.make": "Apple",
47
+ "com.apple.quicktime.model": model,
48
+ "com.apple.quicktime.software": ios_version,
49
+ "com.apple.quicktime.creationdate": datetime.now().strftime("%Y-%m-%dT%H:%M:%S"),
50
+ "com.apple.quicktime.location.name": "Random Location",
51
+ "com.apple.quicktime.location.ISO6709": f"{latitude}+{longitude}/",
52
+ "com.apple.quicktime.lensmodel": f"{model} {focal_length} {f_number}",
53
+ "com.apple.quicktime.make.identifier": "Apple iPhone Camera",
54
+ "com.apple.quicktime.camera.identifier": camera_id,
55
+ "com.apple.quicktime.exposuretime": exposure,
56
+ "com.apple.quicktime.fnumber": f_number,
57
+ "com.apple.quicktime.ISOSpeed": str(iso_speed),
58
+ "com.apple.quicktime.focallength": focal_length,
59
+ "com.apple.quicktime.gps.latitude": str(latitude),
60
+ "com.apple.quicktime.gps.longitude": str(longitude),
61
+ "com.apple.quicktime.gps.altitude": str(altitude)
62
+ }
63
+ return metadata
64
+
65
+ # -----------------------------
66
+ # Metadata update using FFmpeg
67
+ # -----------------------------
68
+ def update_video_metadata(video_bytes: bytes) -> tuple[bytes, dict]:
69
+ """Apply maximal randomized metadata to a video."""
70
+ metadata = generate_max_random_metadata()
71
+
72
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as temp_in:
73
+ temp_in.write(video_bytes)
74
+ temp_in_path = temp_in.name
75
+
76
+ fd, temp_out_path = tempfile.mkstemp(suffix=".mp4")
77
+ os.close(fd)
78
+
79
+ metadata_args = []
80
+ for key, value in metadata.items():
81
+ safe_value = str(value).replace(":", "-") # basic sanitization
82
+ metadata_args.extend(["-metadata", f"{key}={safe_value}"])
83
+
84
+ cmd = [
85
+ "ffmpeg", "-y", "-i", temp_in_path,
86
+ *metadata_args,
87
+ "-movflags", "use_metadata_tags",
88
+ "-map_metadata", "0",
89
+ "-codec", "copy",
90
+ temp_out_path
91
+ ]
92
+
93
+ process = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
94
+ if process.returncode != 0:
95
+ os.remove(temp_in_path)
96
+ if os.path.exists(temp_out_path):
97
+ os.remove(temp_out_path)
98
+ raise RuntimeError(f"FFmpeg failed: {process.stderr.decode(errors='ignore')}")
99
+
100
+ with open(temp_out_path, "rb") as f:
101
+ updated_bytes = f.read()
102
+
103
+ os.remove(temp_in_path)
104
+ os.remove(temp_out_path)
105
+ return updated_bytes, metadata
106
+
107
+ # -----------------------------
108
+ # Streamlit UI
109
+ # -----------------------------
110
+
111
+ # -----------------------------
112
+ # Access Control
113
+ # -----------------------------
114
+ ACCESS_TOKEN = os.getenv("ACCESS_TOKEN") # Change this to your desired password
115
+
116
+ # Check if user is authenticated
117
+ if "authenticated" not in st.session_state:
118
+ st.session_state.authenticated = False
119
+
120
+ # Show login form if not authenticated
121
+ if not st.session_state.authenticated:
122
+ st.title("🔒 Video Metadata Editor")
123
+ st.markdown("---")
124
+
125
+ # Input field for access token
126
+ user_input = st.text_input(
127
+ "Enter Access Token:",
128
+ type="password",
129
+ placeholder="Enter your access token"
130
+ )
131
+
132
+ # Login button
133
+ login_button = st.button("Login", type="primary")
134
+
135
+ if login_button:
136
+ if user_input == ACCESS_TOKEN:
137
+ st.session_state.authenticated = True
138
+ st.rerun()
139
+ else:
140
+ st.error("❌ Invalid access token. Please try again.")
141
+
142
+ st.stop()
143
+
144
+ # Main application (only shown if authenticated)
145
+ col1, col2 = st.columns([5, 1])
146
+
147
+ with col1:
148
+ st.title("Video Metadata Editor")
149
+
150
+ with col2:
151
+ if st.button("Logout", type="secondary"):
152
+ st.session_state.authenticated = False
153
+ st.rerun()
154
+
155
+ uploaded_video = st.file_uploader("Upload an MP4 video", type=["mp4"])
156
+
157
+ if uploaded_video is not None:
158
+ st.subheader("Original Video")
159
+ st.video(uploaded_video)
160
+
161
+ if st.button("Change Metadata"):
162
+ with st.spinner("Applying metadata..."):
163
+ try:
164
+ video_bytes = uploaded_video.read()
165
+ updated_video, metadata = update_video_metadata(video_bytes)
166
+
167
+ st.success(f"Metadata updated successfully!")
168
+
169
+ st.subheader("Updated Video")
170
+ st.video(updated_video)
171
+
172
+ st.download_button(
173
+ "Download Updated Video",
174
+ updated_video,
175
+ file_name="updated_metadata.mp4",
176
+ mime="video/mp4"
177
+ )
178
+
179
+ except Exception as e:
180
+ st.error(f"Error: {e}")