Bils commited on
Commit
3fe530b
·
verified ·
1 Parent(s): 4a143c9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +224 -178
app.py CHANGED
@@ -1,6 +1,8 @@
1
  import streamlit as st
2
  import torch
3
  import scipy.io.wavfile
 
 
4
  from transformers import (
5
  AutoTokenizer,
6
  AutoModelForCausalLM,
@@ -8,149 +10,200 @@ from transformers import (
8
  AutoProcessor,
9
  MusicgenForConditionalGeneration
10
  )
 
11
 
12
  # ---------------------------------------------------------------------
13
- # Page Configuration
14
  # ---------------------------------------------------------------------
15
  st.set_page_config(
 
16
  page_icon="🎧",
17
- layout="wide",
18
- page_title="Radio Imaging Audio Generator - Llama 3",
19
- initial_sidebar_state="expanded",
20
  )
21
 
22
  # ---------------------------------------------------------------------
23
- # Custom CSS for a Catchy UI
24
  # ---------------------------------------------------------------------
25
- CUSTOM_CSS = """
26
  <style>
 
27
  body {
28
- background-color: #FAFCFF;
 
29
  color: #1F2937;
30
- font-family: 'Segoe UI', Tahoma, sans-serif;
31
  }
 
 
 
 
 
 
 
32
  h1, h2, h3, h4, h5, h6 {
33
  color: #3B82F6;
34
  margin-bottom: 0.5em;
35
  }
 
 
36
  .stButton>button {
37
  background-color: #3B82F6 !important;
38
  color: #FFFFFF !important;
39
- border-radius: 8px !important;
40
- font-size: 16px !important;
41
- margin: 0.5em 0;
42
  }
 
 
43
  .sidebar .sidebar-content {
44
  background: #E0F2FE;
45
  }
46
- .material-card {
47
- border: 1px solid #D1D5DB;
48
- border-radius: 8px;
49
- padding: 1rem;
50
- margin-bottom: 1rem;
51
- background-color: #ffffff;
 
 
 
 
 
 
 
 
 
 
 
52
  }
 
 
53
  .footer-note {
54
  text-align: center;
55
- opacity: 0.6;
56
  font-size: 14px;
57
- margin-top: 30px;
58
  }
 
 
 
59
  </style>
60
  """
61
- st.markdown(CUSTOM_CSS, unsafe_allow_html=True)
62
 
63
  # ---------------------------------------------------------------------
64
- # Header Section
65
  # ---------------------------------------------------------------------
66
- st.markdown(
 
67
  """
68
- <h1>🎙 Radio Imaging Audio Generator <span style="font-size: 24px; color: #F59E0B;">(Beta with Llama 3)</span></h1>
69
- <p style='font-size:18px;'>
70
- Generate custom radio ads, station promos, and jingles in multiple languages
71
- using the **hypothetical Llama 3.3** Instruct model & MusicGen!
72
- </p>
73
- """,
74
- unsafe_allow_html=True
75
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  st.markdown("---")
77
 
78
  # ---------------------------------------------------------------------
79
- # Instructions Section
80
  # ---------------------------------------------------------------------
81
- with st.expander("📘 How to Use This Web App"):
82
  st.markdown(
83
  """
84
- 1. **Enter a concept** in any language: Describe the style, mood, length, etc.
85
- 2. **Choose Language**: If you want a Spanish script, select Spanish below (multi-language).
86
- 3. **Refine with Llama 3**: Let the model transform your brief into a catchy script.
87
- 4. **Set Audio Options**: Choose a style (Rock, Pop, Classical...) and max tokens for MusicGen output.
88
- 5. **Generate Audio**: Listen & optionally download or upload the WAV file.
89
-
90
- **Future Enhancements**:
91
- - **User Authentication**: Restrict access or track usage with logins.
92
- - **Advanced Fine-tuning**: Adjust Llama or MusicGen for specialized station branding.
93
- - **Cloud Storage**: Upload final WAVs to a server or cloud bucket for easy sharing.
 
 
94
  """
95
  )
96
 
97
  # ---------------------------------------------------------------------
98
- # Sidebar: Model Selection & Options
99
  # ---------------------------------------------------------------------
100
  with st.sidebar:
101
- st.header("🔧 Model & Audio Config")
102
-
103
- # Llama 3 model ID on Hugging Face (hypothetical)
104
  llama_model_id = st.text_input(
105
- "Llama 3 Instruct Model ID",
106
- value="meta-llama/Llama-3.3-70B-Instruct",
107
- help="Requires license acceptance on Hugging Face, if/when available."
108
  )
109
-
110
  device_option = st.selectbox(
111
  "Hardware Device",
112
  ["auto", "cpu"],
113
- help="If running locally with a GPU, choose 'auto'. CPU-only might be slow for large models."
 
114
  )
115
-
116
- st.markdown("---")
117
-
118
- # Multi-language prompt
119
- language = st.selectbox(
120
- "Choose Output Language",
121
- ["English", "Spanish", "French", "German", "Other (explain in your prompt)"]
122
  )
123
-
124
- st.markdown("---")
125
-
126
- # Audio style and tokens
127
  music_style = st.selectbox(
128
  "Preferred Music Style",
129
  ["Pop", "Rock", "Electronic", "Classical", "Hip-Hop", "Reggae", "Ambient", "Other"]
130
  )
131
- audio_tokens = st.slider(
132
- "MusicGen Max Tokens (Approx. Track Length)",
133
- min_value=128, max_value=1024, value=512, step=64
134
- )
135
 
136
  # ---------------------------------------------------------------------
137
- # Prompt Input
138
  # ---------------------------------------------------------------------
139
- st.markdown("## ✍🏻 Write Your Concept Brief")
140
  prompt = st.text_area(
141
- "Describe the radio imaging or jingle you want to create.",
142
- placeholder="e.g. 'An energetic 15-second pop jingle in Spanish for a morning radio show...'"
143
  )
144
 
145
  # ---------------------------------------------------------------------
146
- # Text Generation with Llama 3
147
  # ---------------------------------------------------------------------
148
  @st.cache_resource
149
  def load_llama_pipeline(model_id: str, device: str):
150
  """
151
- Load the Llama or other open-source model as a text-generation pipeline.
152
- This is hypothetical for Llama 3.3.
153
- Must accept license on HF if the model is restricted.
154
  """
155
  tokenizer = AutoTokenizer.from_pretrained(model_id)
156
  model = AutoModelForCausalLM.from_pretrained(
@@ -158,135 +211,128 @@ def load_llama_pipeline(model_id: str, device: str):
158
  torch_dtype=torch.float16 if device == "auto" else torch.float32,
159
  device_map=device
160
  )
161
- gen_pipeline = pipeline(
162
  "text-generation",
163
  model=model,
164
  tokenizer=tokenizer,
165
  device_map=device
166
  )
167
- return gen_pipeline
168
 
169
- def generate_description(user_prompt: str, pipeline_gen, language_choice: str):
170
  """
171
- Use the pipeline to create a refined description for MusicGen,
172
- with multi-language capabilities.
173
  """
174
- # Instruction for Llama (system prompt):
175
- system_prompt = (
176
- "You are a creative ad copywriter specialized in radio imaging. "
177
- "Refine the user's concept into a concise script. "
178
- "Incorporate the language choice and creative elements for a promotional audio spot."
179
  )
180
-
181
- # Combine user prompt + language + the system instructions
182
- combined_prompt = (
183
- f"{system_prompt}\n"
184
- f"Language to use: {language_choice}\n"
185
- f"User Concept: {user_prompt}\n"
186
- f"Your refined ad script:"
187
- )
188
-
189
- result = pipeline_gen(
190
- combined_prompt,
191
  max_new_tokens=300,
192
  do_sample=True,
193
  temperature=0.8
194
  )
195
- generated_text = result[0]["generated_text"]
196
-
197
- # Attempt to isolate the script portion
198
- if "script:" in generated_text.lower():
199
- generated_text = generated_text.split("script:", 1)[-1].strip()
200
 
201
- # Add a sign-off or brand line
202
- generated_text += "\n\n(Generated by Radio Imaging Audio Generator - Powered by Llama 3)"
203
- return generated_text
204
-
205
- # Button: Generate Description
206
- if st.button("📄 Refine Description with Llama 3"):
207
- if not prompt.strip():
208
- st.error("Please provide a concept before generating a description.")
209
- else:
210
- with st.spinner("Generating a refined description..."):
211
- try:
212
- pipeline_llama = load_llama_pipeline(llama_model_id, device_option)
213
- refined_text = generate_description(prompt, pipeline_llama, language)
214
- st.session_state['refined_prompt'] = refined_text
215
- st.success("Description successfully refined!")
216
- st.write(refined_text)
217
- st.download_button(
218
- "📥 Download Description",
219
- refined_text,
220
- file_name="refined_description.txt"
221
- )
222
- except Exception as e:
223
- st.error(f"Error while generating with Llama 3: {e}")
224
-
225
- st.markdown("---")
226
 
227
  # ---------------------------------------------------------------------
228
- # MusicGen: Generate Audio
229
  # ---------------------------------------------------------------------
230
- @st.cache_resource
231
- def load_musicgen_model():
232
- """Load and cache the MusicGen model and processor."""
233
- mg_model = MusicgenForConditionalGeneration.from_pretrained("facebook/musicgen-small")
234
- mg_processor = AutoProcessor.from_pretrained("facebook/musicgen-small")
235
- return mg_model, mg_processor
236
 
237
- if st.button("▶ Generate Audio with MusicGen"):
238
- if 'refined_prompt' not in st.session_state or not st.session_state['refined_prompt']:
239
- st.error("Please generate or have a refined script before creating audio.")
240
- else:
241
- descriptive_text = st.session_state['refined_prompt']
242
- with st.spinner("Generating your audio..."):
243
- try:
244
- musicgen_model, processor = load_musicgen_model()
245
-
246
- # Incorporate the style preference into the final text
247
- final_text_for_music = f"{descriptive_text}\nStyle preference: {music_style}"
248
-
249
- # Use the refined prompt + style as input
250
- inputs = processor(
251
- text=[final_text_for_music],
252
- padding=True,
253
- return_tensors="pt"
254
- )
255
- # Adjust max_new_tokens for track length
256
- audio_values = musicgen_model.generate(**inputs, max_new_tokens=audio_tokens)
257
- sampling_rate = musicgen_model.config.audio_encoder.sampling_rate
258
 
259
- # Save & display the audio
260
- audio_filename = f"radio_imaging_output_{music_style.lower()}.wav"
261
- scipy.io.wavfile.write(
262
- audio_filename,
263
- rate=sampling_rate,
264
- data=audio_values[0, 0].numpy()
265
- )
 
 
 
266
 
267
- st.success("Audio successfully generated!")
268
- st.audio(audio_filename)
 
 
 
 
 
 
 
 
269
 
270
- # Optionally, prompt to "Upload to Cloud" or "Save to Directory"
271
- if st.checkbox("Upload this WAV to cloud storage? (Demo)"):
272
- with st.spinner("Uploading... (This is a placeholder)"):
273
- # Pseudocode for your custom logic, e.g.:
274
- # upload_to_s3(audio_filename, bucket_name="radio-imaging-bucket")
275
- st.success("File uploaded to your cloud storage (placeholder).")
276
- except Exception as e:
277
- st.error(f"Error while generating audio: {e}")
 
 
 
 
 
 
 
 
278
 
279
  # ---------------------------------------------------------------------
280
- # Footer Section
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  # ---------------------------------------------------------------------
282
  st.markdown("---")
283
  st.markdown(
284
- "<div class='footer-note'>"
285
- "✅ Built with a hypothetical Llama 3.3 & MusicGen · "
286
- "Multi-language, advanced styles, and a hint of future expansions · "
287
- "Happy producing!"
288
- "</div>",
 
289
  unsafe_allow_html=True
290
  )
291
- # Hide Streamlit's default menu and footer if you wish
292
- st.markdown("<style>#MainMenu {visibility: hidden;} footer {visibility: hidden;}</style>", unsafe_allow_html=True)
 
1
  import streamlit as st
2
  import torch
3
  import scipy.io.wavfile
4
+ import requests
5
+ from io import BytesIO
6
  from transformers import (
7
  AutoTokenizer,
8
  AutoModelForCausalLM,
 
10
  AutoProcessor,
11
  MusicgenForConditionalGeneration
12
  )
13
+ from streamlit_lottie import st_lottie # pip install streamlit-lottie
14
 
15
  # ---------------------------------------------------------------------
16
+ # 1) Page Configuration
17
  # ---------------------------------------------------------------------
18
  st.set_page_config(
19
+ page_title="Modern Radio Imaging Generator - Llama 3 & MusicGen",
20
  page_icon="🎧",
21
+ layout="wide"
 
 
22
  )
23
 
24
  # ---------------------------------------------------------------------
25
+ # 2) Custom CSS for a Sleek, Modern Look
26
  # ---------------------------------------------------------------------
27
+ MODERN_CSS = """
28
  <style>
29
+ /* Body styling */
30
  body {
31
+ background: linear-gradient(to bottom right, #ffffff, #f3f4f6);
32
+ font-family: 'Helvetica Neue', Arial, sans-serif;
33
  color: #1F2937;
 
34
  }
35
+
36
+ /* Make the container narrower for a sleek look */
37
+ .block-container {
38
+ max-width: 1100px;
39
+ }
40
+
41
+ /* Heading style */
42
  h1, h2, h3, h4, h5, h6 {
43
  color: #3B82F6;
44
  margin-bottom: 0.5em;
45
  }
46
+
47
+ /* Buttons */
48
  .stButton>button {
49
  background-color: #3B82F6 !important;
50
  color: #FFFFFF !important;
51
+ border-radius: 0.8rem !important;
52
+ font-size: 1rem !important;
53
+ padding: 0.6rem 1.2rem !important;
54
  }
55
+
56
+ /* Sidebar customization */
57
  .sidebar .sidebar-content {
58
  background: #E0F2FE;
59
  }
60
+
61
+ /* Text input areas */
62
+ textarea, input, select {
63
+ border-radius: 0.5rem !important;
64
+ }
65
+
66
+ /* Animate some elements on hover (just an example) */
67
+ .stButton>button:hover {
68
+ background-color: #2563EB !important;
69
+ transition: background-color 0.3s ease-in-out;
70
+ }
71
+
72
+ /* Lottie container style */
73
+ .lottie-container {
74
+ display: flex;
75
+ justify-content: center;
76
+ margin: 1rem 0;
77
  }
78
+
79
+ /* Footer note */
80
  .footer-note {
81
  text-align: center;
82
+ opacity: 0.7;
83
  font-size: 14px;
84
+ margin-top: 2rem;
85
  }
86
+
87
+ /* Hide default Streamlit branding if desired */
88
+ #MainMenu, footer {visibility: hidden;}
89
  </style>
90
  """
91
+ st.markdown(MODERN_CSS, unsafe_allow_html=True)
92
 
93
  # ---------------------------------------------------------------------
94
+ # 3) Lottie Animation Loader
95
  # ---------------------------------------------------------------------
96
+ @st.cache_data
97
+ def load_lottie_url(url: str):
98
  """
99
+ Loads a Lottie animation JSON from a given URL.
100
+ """
101
+ r = requests.get(url)
102
+ if r.status_code != 200:
103
+ return None
104
+ return r.json()
105
+
106
+ # Example Lottie animations (feel free to replace with your own):
107
+ LOTTIE_URL_HEADER = "https://assets1.lottiefiles.com/packages/lf20_amhnytsm.json" # music-themed animation
108
+ lottie_music = load_lottie_url(LOTTIE_URL_HEADER)
109
+
110
+ # ---------------------------------------------------------------------
111
+ # 4) Header & Intro with a Lottie Animation
112
+ # ---------------------------------------------------------------------
113
+ col_header1, col_header2 = st.columns([3, 2], gap="medium")
114
+
115
+ with col_header1:
116
+ st.markdown(
117
+ """
118
+ <h1>🎙 Radio Imaging Generator (Beta)</h1>
119
+ <p style='font-size:18px;'>
120
+ Create catchy radio promos, ads, and station jingles with
121
+ a modern UI, Llama 3 text generation, and MusicGen audio!
122
+ </p>
123
+ """,
124
+ unsafe_allow_html=True
125
+ )
126
+ with col_header2:
127
+ if lottie_music:
128
+ with st.container():
129
+ st_lottie(lottie_music, height=180, key="header_lottie")
130
+ else:
131
+ # Fallback if Lottie fails to load
132
+ st.markdown("*(Animation unavailable)*")
133
+
134
  st.markdown("---")
135
 
136
  # ---------------------------------------------------------------------
137
+ # 5) Explanation in an Expander
138
  # ---------------------------------------------------------------------
139
+ with st.expander("📘 How to Use This App"):
140
  st.markdown(
141
  """
142
+ **Steps**:
143
+ 1. **Model & Language**: In the sidebar, choose the Llama model ID (e.g. a real Llama 2) and the device.
144
+ 2. **Enter Concept**: Provide a short description of the ad or jingle you want.
145
+ 3. **Refine**: Click on "Refine with Llama 3" to get a polished script in your chosen language or style.
146
+ 4. **Generate Audio**: Use MusicGen to create a short audio snippet from that refined script.
147
+ 5. **Listen & Download**: Enjoy or download the result as a WAV file.
148
+
149
+ **Note**:
150
+ - If "Llama 3.3" doesn't exist, you'll get errors. Use a real model from [Hugging Face](https://huggingface.co/models)
151
+ like `meta-llama/Llama-2-7b-chat-hf`.
152
+ - Some large models require GPU (or specialized hardware) for feasible speeds.
153
+ - This example uses [streamlit-lottie](https://github.com/andfanilo/streamlit-lottie) for animation.
154
  """
155
  )
156
 
157
  # ---------------------------------------------------------------------
158
+ # 6) Sidebar Configuration
159
  # ---------------------------------------------------------------------
160
  with st.sidebar:
161
+ st.header("🔧 Llama 3 & Audio Settings")
162
+
163
+ # Model input
164
  llama_model_id = st.text_input(
165
+ "Llama Model ID",
166
+ value="meta-llama/Llama-3.3-70B-Instruct", # Fictitious, please replace with a real model
167
+ help="Replace with a real model, e.g. meta-llama/Llama-2-7b-chat-hf"
168
  )
169
+
170
  device_option = st.selectbox(
171
  "Hardware Device",
172
  ["auto", "cpu"],
173
+ index=0,
174
+ help="If local GPU is available, choose 'auto'. CPU might be slow for large models."
175
  )
176
+
177
+ # Multi-language or style
178
+ language_choice = st.selectbox(
179
+ "Choose Language",
180
+ ["English", "Spanish", "French", "German", "Other (describe in prompt)"]
 
 
181
  )
182
+
183
+ # Music style & max tokens
 
 
184
  music_style = st.selectbox(
185
  "Preferred Music Style",
186
  ["Pop", "Rock", "Electronic", "Classical", "Hip-Hop", "Reggae", "Ambient", "Other"]
187
  )
188
+ audio_tokens = st.slider("MusicGen Max Tokens (Track Length)", 128, 1024, 512, 64)
 
 
 
189
 
190
  # ---------------------------------------------------------------------
191
+ # 7) Prompt for the Radio Imaging Concept
192
  # ---------------------------------------------------------------------
193
+ st.markdown("## ✍️ Your Radio Concept")
194
  prompt = st.text_area(
195
+ "Describe the theme, audience, length, energy level, etc.",
196
+ placeholder="E.g. 'A high-energy 10-second pop jingle for a morning radio show...'"
197
  )
198
 
199
  # ---------------------------------------------------------------------
200
+ # 8) Load Llama Pipeline
201
  # ---------------------------------------------------------------------
202
  @st.cache_resource
203
  def load_llama_pipeline(model_id: str, device: str):
204
  """
205
+ Loads the specified Llama or other HF model as a text-generation pipeline.
206
+ This references a hypothetical Llama 3.3.
 
207
  """
208
  tokenizer = AutoTokenizer.from_pretrained(model_id)
209
  model = AutoModelForCausalLM.from_pretrained(
 
211
  torch_dtype=torch.float16 if device == "auto" else torch.float32,
212
  device_map=device
213
  )
214
+ pipe = pipeline(
215
  "text-generation",
216
  model=model,
217
  tokenizer=tokenizer,
218
  device_map=device
219
  )
220
+ return pipe
221
 
222
+ def refine_description_with_llama(user_prompt: str, pipeline_llama, lang: str):
223
  """
224
+ Create a polished script using Llama.
225
+ Incorporate a language preference or style instructions.
226
  """
227
+ system_msg = (
228
+ "You are an expert radio imaging script writer. "
229
+ "Refine the user's concept into a concise, compelling piece. "
230
+ "Ensure to reflect any language or style requests."
 
231
  )
232
+ combined = f"{system_msg}\nLanguage: {lang}\nUser Concept: {user_prompt}\nRefined Script:"
233
+
234
+ result = pipeline_llama(
235
+ combined,
 
 
 
 
 
 
 
236
  max_new_tokens=300,
237
  do_sample=True,
238
  temperature=0.8
239
  )
240
+ text = result[0]["generated_text"]
 
 
 
 
241
 
242
+ # Attempt to isolate the final portion
243
+ if "Refined Script:" in text:
244
+ text = text.split("Refined Script:")[-1].strip()
245
+
246
+ text += "\n\n(Generated with Llama 3 - Modern Radio Generator)"
247
+ return text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
  # ---------------------------------------------------------------------
250
+ # 9) Buttons & Outputs
251
  # ---------------------------------------------------------------------
252
+ col_gen1, col_gen2 = st.columns(2)
 
 
 
 
 
253
 
254
+ with col_gen1:
255
+ if st.button("📄 Refine with Llama 3"):
256
+ if not prompt.strip():
257
+ st.error("Please provide a brief concept first.")
258
+ else:
259
+ with st.spinner("Refining your script..."):
260
+ try:
261
+ pipeline_llama = load_llama_pipeline(llama_model_id, device_option)
262
+ refined_text = refine_description_with_llama(prompt, pipeline_llama, language_choice)
263
+ st.session_state['refined_prompt'] = refined_text
264
+ st.success("Refined text generated!")
265
+ st.write(refined_text)
266
+ st.download_button(
267
+ "💾 Download Script",
268
+ refined_text,
269
+ file_name="refined_jingle_script.txt"
270
+ )
271
+ except Exception as e:
272
+ st.error(f"Error: {e}")
 
 
273
 
274
+ with col_gen2:
275
+ if st.button("▶ Generate Audio with MusicGen"):
276
+ if 'refined_prompt' not in st.session_state or not st.session_state['refined_prompt']:
277
+ st.error("No refined prompt found. Please generate/refine your script first.")
278
+ else:
279
+ final_text_for_music = st.session_state['refined_prompt']
280
+ final_text_for_music += f"\nPreferred style: {music_style}"
281
+ with st.spinner("Generating audio..."):
282
+ try:
283
+ mg_model, mg_processor = None, None
284
 
285
+ # Load MusicGen model once
286
+ mg_model, mg_processor = load_musicgen_model()
287
+
288
+ inputs = mg_processor(
289
+ text=[final_text_for_music],
290
+ padding=True,
291
+ return_tensors="pt"
292
+ )
293
+ audio_output = mg_model.generate(**inputs, max_new_tokens=audio_tokens)
294
+ sr = mg_model.config.audio_encoder.sampling_rate
295
 
296
+ audio_filename = f"radio_imaging_{music_style.lower()}.wav"
297
+ scipy.io.wavfile.write(
298
+ audio_filename,
299
+ rate=sr,
300
+ data=audio_output[0, 0].numpy()
301
+ )
302
+ st.success("Audio generated! Listen below:")
303
+ st.audio(audio_filename)
304
+
305
+ # Optional Save/Upload prompt
306
+ if st.checkbox("Upload this WAV to a cloud (demo)?"):
307
+ with st.spinner("Uploading..."):
308
+ # Placeholder for your own S3 or cloud logic
309
+ st.success("Uploaded (placeholder).")
310
+ except Exception as e:
311
+ st.error(f"Error generating audio: {e}")
312
 
313
  # ---------------------------------------------------------------------
314
+ # 10) Load & Cache MusicGen
315
+ # ---------------------------------------------------------------------
316
+ @st.cache_resource
317
+ def load_musicgen_model():
318
+ """
319
+ Load and cache the MusicGen model & processor.
320
+ Using 'facebook/musicgen-small' as example.
321
+ """
322
+ mgm = MusicgenForConditionalGeneration.from_pretrained("facebook/musicgen-small")
323
+ mgp = AutoProcessor.from_pretrained("facebook/musicgen-small")
324
+ return mgm, mgp
325
+
326
+ # ---------------------------------------------------------------------
327
+ # 11) Footer
328
  # ---------------------------------------------------------------------
329
  st.markdown("---")
330
  st.markdown(
331
+ """
332
+ <div class='footer-note'>
333
+ © 2025 Modern Radio Generator - Built with Llama & MusicGen |
334
+ <a href='https://example.com' target='_blank'>YourCompany</a>
335
+ </div>
336
+ """,
337
  unsafe_allow_html=True
338
  )