deepuurf commited on
Commit
d7f700f
·
verified ·
1 Parent(s): 5456c13

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +118 -0
app.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import torch
3
+ import numpy as np
4
+ import cv2
5
+ import librosa
6
+ import soundfile as sf
7
+ from transformers import AutoProcessor, AutoModelForAudioClassification
8
+ import subprocess
9
+ import os
10
+ import json
11
+ from PIL import Image
12
+ import io
13
+ import zipfile
14
+
15
+ # ---------- ADVERSARIAL AUDIO PERTURBATION ----------
16
+ def add_audio_glitch(y, sr, epsilon=0.002):
17
+ # Add inaudible high-frequency noise (bypasses acoustic fingerprinting)
18
+ noise = np.random.normal(0, epsilon, len(y))
19
+ y_adv = y + noise
20
+ # Apply slight pitch shift (-2 to +2 cents) at random segments
21
+ segments = np.split(y_adv, np.random.randint(5, 10, 1)[0])
22
+ shifted = []
23
+ for seg in segments:
24
+ shift = np.random.uniform(-0.02, 0.02) # cents
25
+ seg_shifted = librosa.effects.pitch_shift(seg, sr=sr, n_steps=shift)
26
+ shifted.append(seg_shifted)
27
+ return np.concatenate(shifted)
28
+
29
+ # ---------- VIDEO FRAME JITTER (spatial + temporal) ----------
30
+ def process_video_frames(video_path, output_path):
31
+ cap = cv2.VideoCapture(video_path)
32
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
33
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
34
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
35
+
36
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
37
+ out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
38
+
39
+ frames = []
40
+ while True:
41
+ ret, frame = cap.read()
42
+ if not ret: break
43
+ frames.append(frame)
44
+ cap.release()
45
+
46
+ # --- Shuffle 2% of frames randomly (breaks temporal fingerprints) ---
47
+ indices = list(range(len(frames)))
48
+ swap_count = int(len(frames) * 0.02)
49
+ for _ in range(swap_count):
50
+ i, j = np.random.choice(indices, 2, replace=False)
51
+ frames[i], frames[j] = frames[j], frames[i]
52
+
53
+ # --- Apply subtle Gaussian blur to 1% of frames (visual hash evasion) ---
54
+ for idx in np.random.choice(indices, int(len(frames)*0.01), replace=False):
55
+ frames[idx] = cv2.GaussianBlur(frames[idx], (3,3), 0)
56
+
57
+ # --- Crop 1 pixel from each edge (resets perceptual hashes) ---
58
+ cropped = [f[1:-1, 1:-1] for f in frames]
59
+ # Resize back to original dims
60
+ resized = [cv2.resize(f, (width, height)) for f in cropped]
61
+
62
+ for f in resized:
63
+ out.write(f)
64
+ out.release()
65
+
66
+ # ---------- METADATA SCRUBBER (exif + mp4 tags) ----------
67
+ def scrub_metadata(file_path):
68
+ # Remove all exif, tiff, mp4 tags using ffmpeg
69
+ cmd = f'ffmpeg -i "{file_path}" -map_metadata -1 -c copy -metadata title="" -metadata artist="" -metadata album="" -metadata date="" -y "{file_path}_clean.mp4"'
70
+ subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
71
+ os.replace(f"{file_path}_clean.mp4", file_path)
72
+ return file_path
73
+
74
+ # ---------- MAIN PIPELINE ----------
75
+ def evade_copyright(input_video, input_audio=None):
76
+ # If no audio track, extract from video
77
+ temp_audio = "temp_audio.wav"
78
+ if input_audio is None:
79
+ subprocess.run(f'ffmpeg -i "{input_video}" -q:a 0 -map a "{temp_audio}" -y', shell=True)
80
+ input_audio = temp_audio
81
+
82
+ # Load audio
83
+ y, sr = librosa.load(input_audio, sr=22050)
84
+ y_adv = add_audio_glitch(y, sr)
85
+
86
+ # Save perturbed audio
87
+ adv_audio = "adv_audio.wav"
88
+ sf.write(adv_audio, y_adv, sr)
89
+
90
+ # Process video
91
+ adv_video = "adv_video.mp4"
92
+ process_video_frames(input_video, adv_video)
93
+
94
+ # Merge perturbed audio with processed video
95
+ final_output = "final_evaded.mp4"
96
+ cmd = f'ffmpeg -i "{adv_video}" -i "{adv_audio}" -c:v copy -c:a aac -strict experimental -shortest -y "{final_output}"'
97
+ subprocess.run(cmd, shell=True)
98
+
99
+ # Scrub metadata
100
+ scrub_metadata(final_output)
101
+
102
+ # Cleanup
103
+ for f in [temp_audio, adv_audio, adv_video]:
104
+ if os.path.exists(f): os.remove(f)
105
+
106
+ return final_output
107
+
108
+ # ---------- GRADIO UI ----------
109
+ with gr.Blocks(title="Copyright Evader v2.1") as demo:
110
+ gr.Markdown("## 🛡️ Wasteland Content Evader\nUpload video → get fingerprint-resistant output.")
111
+ with gr.Row():
112
+ video_input = gr.Video(label="Upload Video")
113
+ audio_input = gr.Audio(label="Optional Separate Audio (leave blank to extract)", type="filepath")
114
+ output_video = gr.Video(label="Evaded Output")
115
+ btn = gr.Button("🔨 Process")
116
+ btn.click(fn=evade_copyright, inputs=[video_input, audio_input], outputs=output_video)
117
+
118
+ demo.launch(share=True)