Aliwan commited on
Commit
e30142e
·
verified ·
1 Parent(s): a59d4d6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +146 -61
app.py CHANGED
@@ -1,69 +1,154 @@
 
 
 
 
 
1
  import gradio as gr
2
- from huggingface_hub import InferenceClient
3
-
4
-
5
- def respond(
6
- message,
7
- history: list[dict[str, str]],
8
- system_message,
9
- max_tokens,
10
- temperature,
11
- top_p,
12
- hf_token: gr.OAuthToken,
13
- ):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  """
15
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  """
17
- client = InferenceClient(token=hf_token.token, model="openai/gpt-oss-20b")
18
-
19
- messages = [{"role": "system", "content": system_message}]
20
-
21
- messages.extend(history)
22
-
23
- messages.append({"role": "user", "content": message})
24
-
25
- response = ""
26
-
27
- for message in client.chat_completion(
28
- messages,
29
- max_tokens=max_tokens,
30
- stream=True,
31
- temperature=temperature,
32
- top_p=top_p,
33
- ):
34
- choices = message.choices
35
- token = ""
36
- if len(choices) and choices[0].delta.content:
37
- token = choices[0].delta.content
38
-
39
- response += token
40
- yield response
41
-
42
-
43
- """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- chatbot = gr.ChatInterface(
47
- respond,
48
- additional_inputs=[
49
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
50
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
51
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
52
- gr.Slider(
53
- minimum=0.1,
54
- maximum=1.0,
55
- value=0.95,
56
- step=0.05,
57
- label="Top-p (nucleus sampling)",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  ),
59
  ],
 
 
 
 
 
 
 
 
60
  )
61
 
62
- with gr.Blocks() as demo:
63
- with gr.Sidebar():
64
- gr.LoginButton()
65
- chatbot.render()
66
-
67
-
68
  if __name__ == "__main__":
69
- demo.launch()
 
1
+ import os
2
+ import subprocess
3
+ import pathlib
4
+ import hashlib
5
+ import requests
6
  import gradio as gr
7
+
8
+ # ----------------------------------------------------------------------
9
+ # 1️⃣ Download a static FFmpeg build (Linux x86_64, CPU only)
10
+ # ----------------------------------------------------------------------
11
+ FFMPEG_URL = (
12
+ "https://github.com/BtbN/FFmpeg-Builds/releases/download"
13
+ "/latest/ffmpeg-master-latest-linux64-gpl.tar.xz"
14
+ )
15
+ FFMPEG_DIR = pathlib.Path("ffmpeg")
16
+ BINARY_PATH = FFMPEG_DIR / "ffmpeg"
17
+
18
+ def _download_and_extract():
19
+ """Download and extract FFmpeg if not already present."""
20
+ if BINARY_PATH.is_file():
21
+ return
22
+
23
+ # Create directory
24
+ FFMPEG_DIR.mkdir(parents=True, exist_ok=True)
25
+ tar_path = FFMPEG_DIR / "ffmpeg.tar.xz"
26
+
27
+ # Stream download (avoids loading whole file into memory)
28
+ with requests.get(FFMPEG_URL, stream=True, timeout=30) as r:
29
+ r.raise_for_status()
30
+ with open(tar_path, "wb") as f:
31
+ for chunk in r.iter_content(chunk_size=8192):
32
+ f.write(chunk)
33
+
34
+ # Extract only the binary (no need for docs, etc.)
35
+ import tarfile
36
+ with tarfile.open(tar_path, "r:xz") as tar:
37
+ # Find the member that ends with '/ffmpeg'
38
+ for member in tar.getmembers():
39
+ if member.name.endswith("/ffmpeg"):
40
+ member.path = os.path.basename(member.name) # flatten
41
+ tar.extract(member, path=FFMPEG_DIR)
42
+ break
43
+
44
+ # Clean up archive
45
+ tar_path.unlink()
46
+
47
+ # Make binary executable
48
+ BINARY_PATH.chmod(0o755)
49
+
50
+ _download_and_extract()
51
+
52
+ # ----------------------------------------------------------------------
53
+ # 2️⃣ Helper: run FFmpeg safely
54
+ # ----------------------------------------------------------------------
55
+ def run_ffmpeg(input_bytes: bytes, args: str) -> bytes:
56
  """
57
+ Execute a very limited FFmpeg command.
58
+
59
+ Parameters
60
+ ----------
61
+ input_bytes : bytes
62
+ Raw input file (e.g., video or audio) uploaded by the user.
63
+ args : str
64
+ Command‑line arguments *after* the input and before the output.
65
+ Example: "-c:a aac -b:a 128k" (convert audio to AAC).
66
+
67
+ Returns
68
+ -------
69
+ bytes
70
+ Output file produced by FFmpeg.
71
  """
72
+ # Validate arguments – allow only a whitelist of safe flags
73
+ allowed_flags = {
74
+ "-c:a", "-c:v", "-b:a", "-b:v", "-ar", "-ac", "-vn", "-an",
75
+ "-map", "-f", "-y", "-loglevel", "quiet"
76
+ }
77
+ tokenised = args.split()
78
+ if any(tok not in allowed_flags and not tok.startswith("-") for tok in tokenised):
79
+ raise ValueError("Unsupported FFmpeg flag detected.")
80
+
81
+ # Write input to a temporary file
82
+ input_path = pathlib.Path("input.tmp")
83
+ output_path = pathlib.Path("output.tmp")
84
+ input_path.write_bytes(input_bytes)
85
+
86
+ # Build the command
87
+ cmd = [
88
+ str(BINARY_PATH),
89
+ "-loglevel", "quiet", # suppress noisy output
90
+ "-i", str(input_path), # input file
91
+ *tokenised, # user‑provided args
92
+ str(output_path) # output file
93
+ ]
94
+
95
+ # Run FFmpeg; enforce a short timeout (e.g., 30 s) to keep the Space alive
96
+ try:
97
+ subprocess.run(
98
+ cmd,
99
+ check=True,
100
+ stdout=subprocess.PIPE,
101
+ stderr=subprocess.PIPE,
102
+ timeout=30,
103
+ )
104
+ finally:
105
+ # Clean up input regardless of success/failure
106
+ if input_path.exists():
107
+ input_path.unlink()
108
+
109
+ if not output_path.is_file():
110
+ raise RuntimeError("FFmpeg did not produce an output file.")
111
+
112
+ # Return output bytes and clean up
113
+ out_bytes = output_path.read_bytes()
114
+ output_path.unlink()
115
+ return out_bytes
116
+
117
+ # ----------------------------------------------------------------------
118
+ # 3️⃣ Gradio UI
119
+ # ----------------------------------------------------------------------
120
+ def ffmpeg_interface(file: gr.File, args: str) -> gr.File:
121
+ """
122
+ Gradio wrapper that takes an uploaded file and FFmpeg args,
123
+ returns the processed file.
124
+ """
125
+ output_bytes = run_ffmpeg(file.read(), args)
126
+ # Heuristic MIME type based on args; fallback to generic binary
127
+ mime = "application/octet-stream"
128
+ if "-c:a aac" in args or ".aac" in args:
129
+ mime = "audio/aac"
130
+ elif "-c:v libx264" in args or ".mp4" in args:
131
+ mime = "video/mp4"
132
+ return gr.File.update(value=output_bytes, mime_type=mime)
133
+
134
+ iface = gr.Interface(
135
+ fn=ffmpeg_interface,
136
+ inputs=[
137
+ gr.File(label="Upload video/audio file"),
138
+ gr.Textbox(
139
+ label="FFmpeg arguments (after input, before output)",
140
+ placeholder="-c:a aac -b:a 128k",
141
  ),
142
  ],
143
+ outputs=gr.File(label="Processed file"),
144
+ title=" FFmpeg on Hugging Face Spaces (CPU‑only)",
145
+ description=(
146
+ "Upload a media file and supply a limited set of FFmpeg flags. "
147
+ "The app runs on a free‑tier CPU space, so keep jobs short."
148
+ ),
149
+ allow_flagging="never",
150
+ analytics_enabled=False,
151
  )
152
 
 
 
 
 
 
 
153
  if __name__ == "__main__":
154
+ iface.launch()