| import os |
| import re |
| import math |
| import shutil |
| import random |
| import subprocess |
| from datetime import datetime, timedelta, timezone |
| import gradio as gr |
| import spaces |
|
|
|
|
| def get_video_duration(input_file): |
| cmd = [ |
| 'ffprobe', '-v', 'error', |
| '-show_entries', 'format=duration', |
| '-of', 'default=noprint_wrappers=1:nokey=1', |
| input_file |
| ] |
| try: |
| result = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode().strip() |
| return float(result) |
| except Exception: |
| return None |
|
|
|
|
| def has_audio_stream(input_file): |
| cmd = [ |
| 'ffprobe', '-v', 'error', |
| '-select_streams', 'a', |
| '-show_entries', 'stream=codec_type', |
| '-of', 'csv=p=0', |
| input_file |
| ] |
| try: |
| result = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode().strip() |
| return bool(result) |
| except Exception: |
| return False |
|
|
|
|
| def generate_fake_metadata_args(user_date, user_loc, user_make, user_model): |
| if user_date: |
| creation_time_str = user_date |
| else: |
| days_ago = random.randint(1, 180) |
| hours_ago = random.randint(0, 23) |
| minutes_ago = random.randint(0, 59) |
| random_date = datetime.now() - timedelta(days=days_ago, hours=hours_ago, minutes=minutes_ago) |
| creation_time_str = random_date.strftime("%Y-%m-%dT%H:%M:%SZ") |
|
|
| lat_num, lon_num = 40.7128, -74.0060 |
| if user_loc: |
| location_str = user_loc |
| coords = re.findall(r'[-+]?\d*\.\d+|\d+', user_loc) |
| if len(coords) >= 2: |
| try: |
| lat_num = float(coords[0]) |
| lon_num = float(coords[1]) |
| except ValueError: |
| pass |
| else: |
| lat_num = round(random.uniform(36.0, 42.0), 4) |
| lon_num = round(random.uniform(26.0, 45.0), 4) |
| location_str = f"{lat_num:+.4f}{lon_num:+.4f}/" |
|
|
| profiles = [ |
| { |
| "make": "Apple", "model": "iPhone 15 Pro", |
| "lens": "iPhone 15 Pro back camera 6.86mm f/1.78", "software": "17.5.1", |
| "handler_v": "Core Media Video", "handler_a": "Core Media Audio", |
| "fnumber": 1.78, "focal": 6.86 |
| }, |
| { |
| "make": "Apple", "model": "iPhone 14 Pro", |
| "lens": "iPhone 14 Pro back camera 6.86mm f/1.78", "software": "17.4.1", |
| "handler_v": "Core Media Video", "handler_a": "Core Media Audio", |
| "fnumber": 1.78, "focal": 6.86 |
| }, |
| { |
| "make": "Samsung", "model": "SM-S928B", |
| "lens": "Galaxy S24 Ultra Main Camera 6.3mm f/1.7", "software": "UP1A.231005.007.S928BXXU1AXB5", |
| "handler_v": "VideoHandle", "handler_a": "SoundHandle", |
| "fnumber": 1.7, "focal": 6.3 |
| } |
| ] |
|
|
| if user_make or user_model: |
| selected_profile = { |
| "make": user_make if user_make else "Apple", |
| "model": user_model if user_model else "iPhone 15 Pro", |
| "lens": "iPhone Camera Lens 6.86mm f/1.78", |
| "software": "17.5.1", |
| "handler_v": "Core Media Video", |
| "handler_a": "Core Media Audio", |
| "fnumber": 1.78, "focal": 6.86 |
| } |
| else: |
| selected_profile = random.choice(profiles) |
|
|
| selected_profile["creation_time"] = creation_time_str |
| selected_profile["lat_num"] = lat_num |
| selected_profile["lon_num"] = lon_num |
| selected_profile["location_str"] = location_str |
|
|
| metadata_args = [ |
| '-map_metadata', '-1', |
| '-metadata', f'location={location_str}', |
| '-metadata', f'location-eng={location_str}', |
| '-metadata', f'make={selected_profile["make"]}', |
| '-metadata', f'model={selected_profile["model"]}', |
| '-metadata', f'software={selected_profile["software"]}', |
| '-metadata', f'creation_time={creation_time_str}', |
| '-metadata', 'encoder=', |
| '-metadata:s:v:0', 'encoder=', |
| '-metadata:s:v:0', f'handler_name={selected_profile["handler_v"]}', |
| '-metadata:s:a:0', 'encoder=', |
| '-metadata:s:a:0', f'handler_name={selected_profile["handler_a"]}', |
| ] |
|
|
| return metadata_args, selected_profile |
|
|
|
|
| def apply_exiftool_metadata(file_path, profile): |
| exiftool_bin = shutil.which("exiftool") |
| if not exiftool_bin: |
| return "⚠️ UYARI: System ExifTool bulunamadı!" |
|
|
| try: |
| iso_val = random.choice([50, 64, 100, 125, 200]) |
| lat = abs(profile.get("lat_num", 40.7128)) |
| lat_ref = "N" if profile.get("lat_num", 40.7128) >= 0 else "S" |
| lon = abs(profile.get("lon_num", -74.0060)) |
| lon_ref = "E" if profile.get("lon_num", -74.0060) >= 0 else "W" |
|
|
| make_val = profile.get("make", "Apple") |
| model_val = profile.get("model", "iPhone 15 Pro") |
| soft_val = profile.get("software", "17.5.1") |
| c_time = profile.get("creation_time", "") |
|
|
| exif_cmd = [ |
| exiftool_bin, |
| '-overwrite_original', |
| '-XMP-all=', |
| f'-Make={make_val}', |
| f'-Model={model_val}', |
| f'-Keys:Make={make_val}', |
| f'-Keys:Model={model_val}', |
| f'-LensModel={profile.get("lens")}', |
| f'-Software={soft_val}', |
| f'-ISO={iso_val}', |
| f'-FNumber={profile.get("fnumber", 1.78)}', |
| f'-FocalLength={profile.get("focal", 6.86)} mm', |
| f'-CreateDate={c_time}', |
| f'-ModifyDate={c_time}', |
| f'-TrackCreateDate={c_time}', |
| f'-TrackModifyDate={c_time}', |
| f'-MediaCreateDate={c_time}', |
| f'-MediaModifyDate={c_time}', |
| f'-GPSLatitude={lat}', |
| f'-GPSLatitudeRef={lat_ref}', |
| f'-GPSLongitude={lon}', |
| f'-GPSLongitudeRef={lon_ref}', |
| '-CompressorName=H.264', |
| '-video:CompressorName=H.264', |
| '-Encoder=', |
| '-Keys:Encoder=', |
| '-UserData:Encoder=', |
| '-ItemList:Encoder=', |
| f'-HandlerName={profile.get("handler_v", "Core Media Video")}', |
| file_path |
| ] |
| subprocess.run(exif_cmd, capture_output=True) |
| return "✅ ExifTool: Derin EXIF bilgileri & H.264 mühürlemesi uygulandı." |
| except Exception as e: |
| return f"⚠️ ExifTool Hatası: {e}" |
|
|
|
|
| @spaces.GPU |
| def process_video_web(video_file, user_date, user_loc, user_make, user_model): |
| if video_file is None: |
| return None, "❌ Lütfen bir video yükleyin!" |
|
|
| |
| input_file = video_file.name if hasattr(video_file, 'name') else str(video_file) |
|
|
| output_file = "/tmp/ozgunlestirilmis_video.mp4" |
| if os.path.exists(output_file): |
| os.remove(output_file) |
|
|
| logs = [] |
| logs.append("⚡ v10.2 Anti-Detection Engine Başlatılıyor (ZeroGPU Active)...") |
|
|
| raw_duration = get_video_duration(input_file) or 60.0 |
| audio_exists = has_audio_stream(input_file) |
|
|
| trim_start = round(random.uniform(0.3, 0.6), 3) |
| trim_end = round(random.uniform(0.3, 0.6), 3) |
| |
| if raw_duration > (trim_start + trim_end + 3.0): |
| effective_dur = raw_duration - trim_start - trim_end |
| trim_end_pts = raw_duration - trim_end |
| else: |
| trim_start, trim_end_pts = 0.0, raw_duration |
| effective_dur = raw_duration |
|
|
| cut_duration = round(random.uniform(0.3, 0.5), 3) |
| cut_start_relative = round(effective_dur * random.uniform(0.4, 0.6), 3) |
| cut_end_relative = cut_start_relative + cut_duration |
|
|
| logs.append(f"✂️ Jump-Cut: {cut_start_relative}s anından {cut_duration}s kesildi.") |
|
|
| target_fps = random.choice([29.97, 30.0, 59.94, 60.0]) |
| gop_size = "30" if target_fps in [29.97, 30.0] else "60" |
| |
| brightness = round(random.uniform(-0.012, 0.018), 3) |
| contrast = round(random.uniform(1.01, 1.03), 3) |
| speed = round(random.uniform(0.98, 1.02), 3) |
|
|
| p1_start = trim_start |
| p1_end = trim_start + cut_start_relative |
| p2_start = trim_start + cut_end_relative |
| p2_end = trim_end_pts |
|
|
| filter_parts = [] |
| filter_parts.append(f"[0:v]trim=start={p1_start:.3f}:end={p1_end:.3f},setpts=PTS-STARTPTS[v1]") |
| filter_parts.append(f"[0:v]trim=start={p2_start:.3f}:end={p2_end:.3f},setpts=PTS-STARTPTS[v2]") |
| filter_parts.append("[v1][v2]concat=n=2:v=1:a=0[v_jumped]") |
|
|
| cbh_val = random.choice([-1, 1]) |
| chroma_shift = f"chromashift=cbh={cbh_val}:cbv={-cbh_val}:crh={-cbh_val}:crv={cbh_val}" |
| unsharp_val = round(random.uniform(0.35, 0.60), 3) |
|
|
| v_fx = ( |
| f"[v_jumped]hflip[v_flipped]; " |
| f"[v_flipped]scale=1120:1980,rotate=0.005:c=black[v_rot]; " |
| f"[v_rot]crop=iw-4:ih-4:'2+1.2*sin(n*0.15)':'2+1.2*cos(n*0.15)'," |
| f"{chroma_shift}," |
| f"unsharp=luma_msize_x=5:luma_msize_y=5:luma_amount={unsharp_val}," |
| f"eq=brightness={brightness}:contrast={contrast}:saturation=1.01," |
| f"colorbalance=rs=0.01:bs=-0.01,vignette=PI/6," |
| f"crop=1080:1920:(iw-1080)/2+12*sin(n*0.025):(ih-1920)/2+12*cos(n*0.025)," |
| f"noise=alls=7:allf=t+u," |
| f"setpts=(PTS-STARTPTS)/{speed}," |
| f"fps={target_fps},scale=1080:1920,setsar=1,format=yuv420p[vout_final]" |
| ) |
| filter_parts.append(v_fx) |
|
|
| map_args = ['-map', '[vout_final]'] |
| audio_codec_args = [] |
|
|
| if audio_exists: |
| filter_parts.append(f"[0:a]atrim=start={p1_start:.3f}:end={p1_end:.3f},asetpts=PTS-STARTPTS[a1]") |
| filter_parts.append(f"[0:a]atrim=start={p2_start:.3f}:end={p2_end:.3f},asetpts=PTS-STARTPTS[a2]") |
| filter_parts.append("[a1][a2]concat=n=2:v=0:a=1[a_jumped]") |
|
|
| pitch_factor = round(random.choice([0.97, 0.98, 1.02, 1.03]), 3) |
| inv_pitch = round(1 / pitch_factor, 6) |
| new_rate = int(48000 * pitch_factor) |
| audio_delay_ms = random.randint(15, 30) |
|
|
| a_fx = ( |
| f"[a_jumped]atempo={speed}," |
| f"asetrate={new_rate},atempo={inv_pitch}," |
| f"highpass=f=80,equalizer=f=850:width_type=q:width=1:g=-5," |
| f"aecho=0.8:0.85:12:0.08,bass=g=2:f=80,treble=g=1.5:f=12000," |
| f"adelay={audio_delay_ms}|{audio_delay_ms},volume=1.02,aresample=48000,apad[aout_final]" |
| ) |
| filter_parts.append(a_fx) |
| map_args += ['-map', '[aout_final]'] |
| audio_codec_args = ['-c:a', 'aac', '-b:a', '192k', '-ac', '2', '-ar', '48000'] |
|
|
| filter_complex = "; ".join(filter_parts) |
| metadata_args, profile_data = generate_fake_metadata_args(user_date, user_loc, user_make, user_model) |
|
|
| cmd = [ |
| 'ffmpeg', |
| '-i', input_file, |
| '-filter_complex', filter_complex, |
| *map_args, |
| '-c:v', 'libx264', |
| '-crf', '22', |
| '-preset', 'fast', |
| '-g', gop_size, |
| '-bf', '0', |
| '-bsf:v', 'filter_units=remove_types=6', |
| '-brand', 'mp42', |
| '-color_primaries', 'bt709', |
| '-color_trc', 'bt709', |
| '-colorspace', 'bt709', |
| '-color_range', 'tv', |
| *audio_codec_args, |
| '-pix_fmt', 'yuv420p', |
| '-movflags', '+faststart', |
| '-shortest', |
| *metadata_args, |
| '-y', |
| output_file |
| ] |
|
|
| res = subprocess.run(cmd, capture_output=True, text=True) |
|
|
| if res.returncode == 0: |
| exif_log = apply_exiftool_metadata(output_file, profile_data) |
| logs.append(exif_log) |
| logs.append("🎉 Video Başarıyla Özgünleştirildi!") |
| return output_file, "\n".join(logs) |
| else: |
| logs.append("❌ FFmpeg Hatası:") |
| logs.append(res.stderr[-400:]) |
| return None, "\n".join(logs) |
|
|
|
|
| |
| with gr.Blocks(title="Sosyal Medya Video Özgünleştirici Pro v10.2") as demo: |
| gr.Markdown( |
| """ |
| # ⚡ Sosyal Medya Video Özgünleştirici Pro (v10.2 ZeroGPU) |
| """ |
| ) |
| with gr.Row(): |
| with gr.Column(): |
| video_input = gr.File(label="📹 Videonuzu Yükleyin (.mp4, .mov)", file_types=[".mp4", ".mov", ".mkv"]) |
| |
| with gr.Accordion("📝 Özel Metadata & Cihaz Ayarları (Opsiyonel)", open=False): |
| user_date = gr.Textbox(label="Tarih", placeholder="YYYY-MM-DDTHH:MM:SSZ") |
| user_loc = gr.Textbox(label="GPS Konum", placeholder="+40.7128-74.0060/") |
| user_make = gr.Textbox(label="Marka", placeholder="Apple / Samsung") |
| user_model = gr.Textbox(label="Model", placeholder="iPhone 15 Pro") |
| |
| btn_submit = gr.Button("🚀 Videoyu Özgünleştir", variant="primary") |
|
|
| with gr.Column(): |
| video_output = gr.Video(label="✅ Özgünleştirilmiş Video") |
| log_output = gr.Textbox(label="📋 İşlem Logları", lines=8) |
|
|
| btn_submit.click( |
| fn=process_video_web, |
| inputs=[video_input, user_date, user_loc, user_make, user_model], |
| outputs=[video_output, log_output] |
| ) |
|
|
| if __name__ == "__main__": |
| demo.queue().launch(ssr_mode=False) |