CamiloVega commited on
Commit
d0873df
·
verified ·
1 Parent(s): 654a56c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +222 -103
app.py CHANGED
@@ -4,175 +4,294 @@ import whisper
4
  import tempfile
5
  import gradio as gr
6
  from pydub import AudioSegment
7
- import fitz # PyMuPDF for handling PDFs
8
- import docx # For handling .docx files
9
- import pandas as pd # For handling .xlsx and .csv files
10
- # from google.colab import userdata # Import userdata from google.colab
11
  import requests
12
  from bs4 import BeautifulSoup
 
 
 
13
 
14
- # Configure your OpenAI API key using Google Colab userdata
15
- # openai.api_key = userdata.get('OPENAI_API_KEY')
 
16
 
17
- # Load environment variables from the Hugging Face environment
 
 
 
18
  openai.api_key = os.getenv("OPENAI_API_KEY")
19
 
20
- # Load the highest quality Whisper model once
21
  model = whisper.load_model("large")
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  def preprocess_audio(audio_file):
24
- """Preprocess the audio file to improve quality."""
25
  try:
26
  audio = AudioSegment.from_file(audio_file)
27
  audio = audio.apply_gain(-audio.dBFS + (-20))
28
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_file:
29
  audio.export(temp_file.name, format="mp3")
 
30
  return temp_file.name
31
  except Exception as e:
32
- return f"Error preprocessing the audio file: {str(e)}"
 
33
 
34
- def transcribe_audio(audio_file):
35
- """Transcribe an audio file."""
36
  try:
37
- file_path = preprocess_audio(audio_file) if isinstance(audio_file, str) else preprocess_audio(tempfile.NamedTemporaryFile(delete=False, suffix=".mp3", mode='w+b').name)
38
- result = model.transcribe(file_path)
39
- return result.get("text", "Error in transcription")
 
 
 
 
 
 
 
 
 
 
 
 
40
  except Exception as e:
41
- return f"Error processing the audio file: {str(e)}"
 
42
 
43
- def read_document(document_path):
44
- """Read the content of a PDF, DOCX, XLSX or CSV document."""
45
  try:
46
- if document_path.endswith(".pdf"):
47
- doc = fitz.open(document_path)
48
- return "\n".join([page.get_text() for page in doc])
49
- elif document_path.endswith(".docx"):
50
- doc = docx.Document(document_path)
51
- return "\n".join([paragraph.text for paragraph in doc.paragraphs])
52
- elif document_path.endswith(".xlsx"):
53
- return pd.read_excel(document_path).to_string()
54
- elif document_path.endswith(".csv"):
55
- return pd.read_csv(document_path).to_string()
56
  else:
57
- return "Unsupported file type. Please upload a PDF, DOCX, XLSX or CSV document."
58
  except Exception as e:
59
- return f"Error reading the document: {str(e)}"
60
 
61
- def read_url(url):
62
- """Read the content of a URL."""
63
  try:
64
  response = requests.get(url)
65
  response.raise_for_status()
66
  soup = BeautifulSoup(response.content, 'html.parser')
67
  return soup.get_text()
68
  except Exception as e:
69
- return f"Error reading the URL: {str(e)}"
70
 
71
- def generate_news(instructions, facts, size, tone, urls, *args):
72
- """Generate a news article based on instructions, facts, URLs, documents, and transcriptions."""
73
- knowledge_base = {"instructions": instructions, "facts": facts, "document_content": [], "audio_data": [], "url_content": []}
74
- num_audios = 5 * 3 # 5 audios * 3 fields (audio, name, position)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  audios = args[:num_audios]
76
- documents = args[num_audios:]
 
 
77
 
78
- for url in urls.split():
79
  if url:
80
- knowledge_base["url_content"].append(read_url(url))
81
 
82
- for document in documents:
83
- if document is not None:
84
- knowledge_base["document_content"].append(read_document(document.name))
85
 
86
  for i in range(0, len(audios), 3):
87
- audio_file, name, position = audios[i:i+3]
88
  if audio_file is not None:
89
- knowledge_base["audio_data"].append({"audio": audio_file, "name": name, "position": position})
90
 
91
- transcriptions_text, raw_transcriptions, total_direct_quotes = "", "", 0
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- for idx, data in enumerate(knowledge_base["audio_data"]):
 
 
94
  if data["audio"] is not None:
95
- transcription = transcribe_audio(data["audio"])
96
- transcription_text = f'"{transcription}" - {data["name"]}, {data["position"]}'
97
- raw_transcription = f'[Audio {idx + 1}]: "{transcription}" - {data["name"]}, {data["position"]}'
98
- if total_direct_quotes < len(knowledge_base["audio_data"]) * 0.8:
99
- transcriptions_text += transcription_text + "\n"
100
- total_direct_quotes += 1
101
- else:
102
- transcriptions_text += f'{data["name"]} mentioned that {transcription}' + "\n"
103
- raw_transcriptions += raw_transcription + "\n\n"
104
-
105
- document_content = "\n\n".join(knowledge_base["document_content"])
106
- url_content = "\n\n".join(knowledge_base["url_content"])
107
-
108
- internal_prompt = """
109
- Instructions for the model:
110
- - Follow the principles of news writing: always try to answer the 5 Ws of a news story in the first paragraph (Who?, What?, When?, Where?, Why?).
111
- - Ensure that at least 80% of the quotes are direct and in quotation marks.
112
- - The remaining 20% can be indirect quotes.
113
- - Do not invent new information.
114
- - Be rigorous with the provided facts.
115
- - When processing uploaded documents, extract and highlight important quotes and verbatim testimonies from sources.
116
- - When processing uploaded documents, extract and highlight key figures.
 
 
 
 
 
 
 
 
 
117
  """
118
 
119
  prompt = f"""
120
- {internal_prompt}
121
- Write a news article with the following information, including a title, a 15-word hook (additional information that complements the title), and the body content with a size of {size} words. The tone should be {tone}.
122
- Instructions: {knowledge_base["instructions"]}
123
- Facts: {knowledge_base["facts"]}
124
- Additional content from documents: {document_content}
125
- Additional content from URLs: {url_content}
126
- Use the following transcriptions as direct and indirect quotes (without changing or inventing content):
127
- {transcriptions_text}
128
  """
129
 
130
  try:
131
- response = openai.ChatCompletion.create(
132
- model="gpt-3.5-turbo",
133
  messages=[{"role": "user", "content": prompt}],
134
  temperature=0.1
135
  )
136
- news_article = response['choices'][0]['message']['content']
137
- return news_article, raw_transcriptions
138
  except Exception as e:
139
- return f"Error generating the news article: {str(e)}", ""
 
140
 
141
  with gr.Blocks() as demo:
142
- gr.Markdown("## All-in-One News Generator")
143
  with gr.Row():
144
  with gr.Column(scale=2):
145
- instructions = gr.Textbox(label="Instructions for the news article", lines=2)
146
- facts = gr.Textbox(label="Describe the facts of the news", lines=4)
147
- size = gr.Number(label="Size of the news body (in words)", value=100)
148
- tone = gr.Dropdown(label="Tone of the news", choices=["serious", "neutral", "lighthearted"], value="neutral")
149
- urls = gr.Textbox(label="URLs (separated by space)", lines=2)
150
  with gr.Column(scale=3):
151
- inputs_list = [instructions, facts, size, tone, urls]
152
  with gr.Tabs():
153
  for i in range(1, 6):
154
- with gr.TabItem(f"Audio {i}"):
155
- audio = gr.Audio(type="filepath", label=f"Audio {i}")
156
- name = gr.Textbox(label="Name", scale=1)
157
- position = gr.Textbox(label="Position", scale=1)
158
- inputs_list.extend([audio, name, position])
 
 
 
 
 
 
 
 
 
 
159
  for i in range(1, 6):
160
- with gr.TabItem(f"Document {i}"):
161
- document = gr.File(label=f"Document {i}", type="filepath", file_count="single")
162
- inputs_list.append(document)
163
 
164
- gr.Markdown("---") # Visual separator
165
 
166
  with gr.Row():
167
- transcriptions_output = gr.Textbox(label="Transcriptions", lines=10)
168
 
169
- gr.Markdown("---") # Visual separator
170
 
171
  with gr.Row():
172
- generate = gr.Button("Generate draft")
173
  with gr.Row():
174
- news_output = gr.Textbox(label="Generated draft", lines=20)
175
 
176
- generate.click(fn=generate_news, inputs=inputs_list, outputs=[news_output, transcriptions_output])
177
 
178
  demo.launch(share=True)
 
4
  import tempfile
5
  import gradio as gr
6
  from pydub import AudioSegment
7
+ import fitz # PyMuPDF para manejar PDFs
8
+ import docx # Para manejar archivos .docx
9
+ import pandas as pd # Para manejar archivos .xlsx y .csv
10
+ #from google.colab import userdata # Importa userdata de google.colab
11
  import requests
12
  from bs4 import BeautifulSoup
13
+ from moviepy.editor import VideoFileClip
14
+ import yt_dlp
15
+ import logging
16
 
17
+ # Configurar logging
18
+ logging.basicConfig(level=logging.INFO)
19
+ logger = logging.getLogger(_name_)
20
 
21
+ # Configura tu clave API de OpenAI usando Google Colab userdata
22
+ #openai.api_key = userdata.get('OPENAI_API_KEY')
23
+
24
+ # Cargar las variables de entorno desde el entorno de Hugging Face
25
  openai.api_key = os.getenv("OPENAI_API_KEY")
26
 
27
+ # Cargar el modelo Whisper de mayor calidad una vez
28
  model = whisper.load_model("large")
29
 
30
+ def download_social_media_video(url):
31
+ """Descarga un video de redes sociales."""
32
+ ydl_opts = {
33
+ 'format': 'bestaudio/best',
34
+ 'postprocessors': [{
35
+ 'key': 'FFmpegExtractAudio',
36
+ 'preferredcodec': 'mp3',
37
+ 'preferredquality': '192',
38
+ }],
39
+ 'outtmpl': '%(id)s.%(ext)s',
40
+ }
41
+ try:
42
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
43
+ info_dict = ydl.extract_info(url, download=True)
44
+ audio_file = f"{info_dict['id']}.mp3"
45
+ logger.info(f"Video descargado exitosamente: {audio_file}")
46
+ return audio_file
47
+ except Exception as e:
48
+ logger.error(f"Error al descargar el video: {str(e)}")
49
+ raise
50
+
51
+ def convert_video_to_audio(video_file):
52
+ """Convierte un archivo de video a audio."""
53
+ try:
54
+ video = VideoFileClip(video_file)
55
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_file:
56
+ video.audio.write_audiofile(temp_file.name)
57
+ logger.info(f"Video convertido a audio: {temp_file.name}")
58
+ return temp_file.name
59
+ except Exception as e:
60
+ logger.error(f"Error al convertir el video a audio: {str(e)}")
61
+ raise
62
+
63
  def preprocess_audio(audio_file):
64
+ """Preprocesa el archivo de audio para mejorar la calidad."""
65
  try:
66
  audio = AudioSegment.from_file(audio_file)
67
  audio = audio.apply_gain(-audio.dBFS + (-20))
68
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_file:
69
  audio.export(temp_file.name, format="mp3")
70
+ logger.info(f"Audio preprocesado: {temp_file.name}")
71
  return temp_file.name
72
  except Exception as e:
73
+ logger.error(f"Error al preprocesar el archivo de audio: {str(e)}")
74
+ raise
75
 
76
+ def transcribir_audio(file):
77
+ """Transcribe un archivo de audio o video."""
78
  try:
79
+ if isinstance(file, str) and file.startswith('http'):
80
+ logger.info(f"Descargando video de red social: {file}")
81
+ archivo_path = download_social_media_video(file)
82
+ elif isinstance(file, str) and file.lower().endswith(('.mp4', '.avi', '.mov', '.mkv')):
83
+ logger.info(f"Convirtiendo video local a audio: {file}")
84
+ archivo_path = convert_video_to_audio(file)
85
+ else:
86
+ logger.info(f"Preprocesando archivo de audio: {file}")
87
+ archivo_path = preprocess_audio(file)
88
+
89
+ logger.info(f"Transcribiendo audio: {archivo_path}")
90
+ resultado = model.transcribe(archivo_path)
91
+ transcripcion = resultado.get("text", "Error en la transcripción")
92
+ logger.info(f"Transcripción completada: {transcripcion[:50]}...")
93
+ return transcripcion
94
  except Exception as e:
95
+ logger.error(f"Error al procesar el archivo: {str(e)}")
96
+ return f"Error al procesar el archivo: {str(e)}"
97
 
98
+ def leer_documento(documento_path):
99
+ """Lee el contenido de un documento PDF, DOCX, XLSX o CSV."""
100
  try:
101
+ if documento_path.endswith(".pdf"):
102
+ doc = fitz.open(documento_path)
103
+ return "\n".join([pagina.get_text() for pagina in doc])
104
+ elif documento_path.endswith(".docx"):
105
+ doc = docx.Document(documento_path)
106
+ return "\n".join([parrafo.text for parrafo in doc.paragraphs])
107
+ elif documento_path.endswith(".xlsx"):
108
+ return pd.read_excel(documento_path).to_string()
109
+ elif documento_path.endswith(".csv"):
110
+ return pd.read_csv(documento_path).to_string()
111
  else:
112
+ return "Tipo de archivo no soportado. Por favor suba un documento PDF, DOCX, XLSX o CSV."
113
  except Exception as e:
114
+ return f"Error al leer el documento: {str(e)}"
115
 
116
+ def leer_url(url):
117
+ """Lee el contenido de una URL."""
118
  try:
119
  response = requests.get(url)
120
  response.raise_for_status()
121
  soup = BeautifulSoup(response.content, 'html.parser')
122
  return soup.get_text()
123
  except Exception as e:
124
+ return f"Error al leer la URL: {str(e)}"
125
 
126
+ def procesar_contenido_social(url):
127
+ """Procesa el contenido de una URL de red social, manejando tanto texto como video."""
128
+ try:
129
+ # Primero, intentamos leer el contenido como texto
130
+ contenido_texto = leer_url(url)
131
+
132
+ # Luego, intentamos procesar como video
133
+ try:
134
+ contenido_video = transcribir_audio(url)
135
+ except Exception:
136
+ contenido_video = None
137
+
138
+ return {
139
+ "texto": contenido_texto,
140
+ "video": contenido_video
141
+ }
142
+ except Exception as e:
143
+ logger.error(f"Error al procesar contenido social: {str(e)}")
144
+ return None
145
+
146
+ def generar_noticia(instrucciones, hechos, tamaño, tono, *args):
147
+ """Genera una noticia a partir de instrucciones, hechos, URLs, documentos, transcripciones y contenido de redes sociales."""
148
+ base_de_conocimiento = {
149
+ "instrucciones": instrucciones,
150
+ "hechos": hechos,
151
+ "contenido_documentos": [],
152
+ "audio_data": [],
153
+ "contenido_urls": [],
154
+ "contenido_social": []
155
+ }
156
+ num_audios = 5 * 3 # 5 audios/videos * 3 campos (archivo, nombre, cargo)
157
+ num_social_urls = 3 * 3 # 3 URLs de redes sociales * 3 campos (URL, nombre, contexto)
158
+ num_urls = 5 # 5 URLs generales
159
  audios = args[:num_audios]
160
+ social_urls = args[num_audios:num_audios+num_social_urls]
161
+ urls = args[num_audios+num_social_urls:num_audios+num_social_urls+num_urls]
162
+ documentos = args[num_audios+num_social_urls+num_urls:]
163
 
164
+ for url in urls:
165
  if url:
166
+ base_de_conocimiento["contenido_urls"].append(leer_url(url))
167
 
168
+ for documento in documentos:
169
+ if documento is not None:
170
+ base_de_conocimiento["contenido_documentos"].append(leer_documento(documento.name))
171
 
172
  for i in range(0, len(audios), 3):
173
+ audio_file, nombre, cargo = audios[i:i+3]
174
  if audio_file is not None:
175
+ base_de_conocimiento["audio_data"].append({"audio": audio_file, "nombre": nombre, "cargo": cargo})
176
 
177
+ for i in range(0, len(social_urls), 3):
178
+ social_url, social_nombre, social_contexto = social_urls[i:i+3]
179
+ if social_url:
180
+ contenido_social = procesar_contenido_social(social_url)
181
+ if contenido_social:
182
+ base_de_conocimiento["contenido_social"].append({
183
+ "url": social_url,
184
+ "nombre": social_nombre,
185
+ "contexto": social_contexto,
186
+ "texto": contenido_social["texto"],
187
+ "video": contenido_social["video"]
188
+ })
189
+ logger.info(f"Contenido de red social procesado: {social_url}")
190
 
191
+ transcripciones_texto, transcripciones_brutas = "", ""
192
+
193
+ for idx, data in enumerate(base_de_conocimiento["audio_data"]):
194
  if data["audio"] is not None:
195
+ transcripcion = transcribir_audio(data["audio"])
196
+ transcripcion_texto = f'"{transcripcion}" - {data["nombre"]}, {data["cargo"]}'
197
+ transcripcion_bruta = f'[Audio/Video {idx + 1}]: "{transcripcion}" - {data["nombre"]}, {data["cargo"]}'
198
+ transcripciones_texto += transcripcion_texto + "\n"
199
+ transcripciones_brutas += transcripcion_bruta + "\n\n"
200
+
201
+ for data in base_de_conocimiento["contenido_social"]:
202
+ if data["texto"]:
203
+ transcripcion_texto = f'[Texto de red social]: "{data["texto"][:200]}..." - {data["nombre"]}, {data["contexto"]}'
204
+ transcripciones_texto += transcripcion_texto + "\n"
205
+ transcripciones_brutas += transcripcion_texto + "\n\n"
206
+ if data["video"]:
207
+ transcripcion_video = f'[Video de red social]: "{data["video"]}" - {data["nombre"]}, {data["contexto"]}'
208
+ transcripciones_texto += transcripcion_video + "\n"
209
+ transcripciones_brutas += transcripcion_video + "\n\n"
210
+
211
+ contenido_documentos = "\n\n".join(base_de_conocimiento["contenido_documentos"])
212
+ contenido_urls = "\n\n".join(base_de_conocimiento["contenido_urls"])
213
+
214
+ prompt_interno = """
215
+ Instrucciones para el modelo:
216
+ - Debes seguir los principios de una noticia: es decir, procura siempre responder las 5 W de una noticia en el primer párrafo (Who?, What?, When?, Where?, Why?).
217
+ - Asegúrate de que al menos el 80% de las citas sean directas y estén entrecomilladas.
218
+ - El 20% restante puede ser citas indirectas.
219
+ - No inventes información nueva.
220
+ - Sé riguroso con los hechos proporcionados.
221
+ - Al procesar los documentos cargados, extrae y resalta citas importantes y testimonios textuales de las fuentes.
222
+ - Al procesar los documentos cargados, extrae y resalta cifras clave.
223
+ - Evita usar la fecha al comienzo del cuerpo de la noticia. Empieza directamente con las 5W.
224
+ - Incluye el contenido de las redes sociales de manera relevante, citando la fuente y proporcionando el contexto adecuado.
225
+ - Asegúrate de relacionar el contexto proporcionado para el contenido de red social con su transcripción o texto correspondiente.
226
  """
227
 
228
  prompt = f"""
229
+ {prompt_interno}
230
+ Escribe una noticia con la siguiente información, incluyendo un título, un gancho de 15 palabras (el gancho es lo que se conoce en inglés como hook, información adicional que complementa el título), y el cuerpo del contenido cuyo tamaño es {tamaño} palabras. El tono debe ser {tono}.
231
+ Instrucciones: {base_de_conocimiento["instrucciones"]}
232
+ Hechos: {base_de_conocimiento["hechos"]}
233
+ Contenido adicional de los documentos: {contenido_documentos}
234
+ Contenido adicional de las URLs: {contenido_urls}
235
+ Utiliza las siguientes transcripciones como citas directas e indirectas (sin cambiar ni inventar contenido):
236
+ {transcripciones_texto}
237
  """
238
 
239
  try:
240
+ respuesta = openai.ChatCompletion.create(
241
+ model="gpt-4o-mini",
242
  messages=[{"role": "user", "content": prompt}],
243
  temperature=0.1
244
  )
245
+ noticia = respuesta['choices'][0]['message']['content']
246
+ return noticia, transcripciones_brutas
247
  except Exception as e:
248
+ logger.error(f"Error al generar la noticia: {str(e)}")
249
+ return f"Error al generar la noticia: {str(e)}", ""
250
 
251
  with gr.Blocks() as demo:
252
+ gr.Markdown("## Generador de noticias todo en uno")
253
  with gr.Row():
254
  with gr.Column(scale=2):
255
+ instrucciones = gr.Textbox(label="Instrucciones para la noticia", lines=2)
256
+ hechos = gr.Textbox(label="Describe los hechos de la noticia", lines=4)
257
+ tamaño = gr.Number(label="Tamaño del cuerpo de la noticia (en palabras)", value=100)
258
+ tono = gr.Dropdown(label="Tono de la noticia", choices=["serio", "neutral", "divertido"], value="neutral")
 
259
  with gr.Column(scale=3):
260
+ inputs_list = [instrucciones, hechos, tamaño, tono]
261
  with gr.Tabs():
262
  for i in range(1, 6):
263
+ with gr.TabItem(f"Audio/Video {i}"):
264
+ file = gr.File(label=f"Audio/Video {i}", type="filepath", file_types=["audio", "video"])
265
+ nombre = gr.Textbox(label="Nombre", scale=1)
266
+ cargo = gr.Textbox(label="Cargo", scale=1)
267
+ inputs_list.extend([file, nombre, cargo])
268
+ for i in range(1, 4):
269
+ with gr.TabItem(f"Red Social {i}"):
270
+ social_url = gr.Textbox(label=f"URL de red social {i}", lines=1)
271
+ social_nombre = gr.Textbox(label=f"Nombre de persona/cuenta {i}", scale=1)
272
+ social_contexto = gr.Textbox(label=f"Contexto del contenido {i}", lines=2)
273
+ inputs_list.extend([social_url, social_nombre, social_contexto])
274
+ for i in range(1, 6):
275
+ with gr.TabItem(f"URL {i}"):
276
+ url = gr.Textbox(label=f"URL {i}", lines=1)
277
+ inputs_list.append(url)
278
  for i in range(1, 6):
279
+ with gr.TabItem(f"Documento {i}"):
280
+ documento = gr.File(label=f"Documento {i}", type="filepath", file_count="single")
281
+ inputs_list.append(documento)
282
 
283
+ gr.Markdown("---") # Separador visual
284
 
285
  with gr.Row():
286
+ transcripciones_output = gr.Textbox(label="Transcripciones", lines=10)
287
 
288
+ gr.Markdown("---") # Separador visual
289
 
290
  with gr.Row():
291
+ generar = gr.Button("Generar borrador")
292
  with gr.Row():
293
+ noticia_output = gr.Textbox(label="Borrador generado", lines=20)
294
 
295
+ generar.click(fn=generar_noticia, inputs=inputs_list, outputs=[noticia_output, transcripciones_output])
296
 
297
  demo.launch(share=True)