Spaces:
Sleeping
Sleeping
avanço nas tools
Browse files
requirements-audio-extractor.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
yt-dlp openai-whisper torch
|
tool_audio_extractor.py
ADDED
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# -*- coding: utf-8 -*-
|
2 |
+
"""
|
3 |
+
Script para baixar um vídeo do YouTube, extrair frames, analisar com GPT-4o e contar aves.
|
4 |
+
"""
|
5 |
+
|
6 |
+
import os
|
7 |
+
import subprocess
|
8 |
+
import cv2
|
9 |
+
import base64
|
10 |
+
import time
|
11 |
+
import json
|
12 |
+
import re
|
13 |
+
|
14 |
+
# --- Configurações (Substitua os placeholders) ---
|
15 |
+
VIDEO_URL = "https://www.youtube.com/watch?v=L1vXCYZAYYM" # Substitua pela URL do vídeo do YouTube
|
16 |
+
RESULTS_FILE = os.path.join(OUTPUT_DIR, "analysis_results.json")
|
17 |
+
OUTPUT_DIR = "./video_analysis_output" # Diretório para salvar o vídeo e os frames
|
18 |
+
AUDIO_FILENAME = "downloaded_audio"
|
19 |
+
VIDEO_PATH = os.path.join(OUTPUT_DIR, AUDIO_FILENAME)
|
20 |
+
|
21 |
+
# Verifica se a URL foi definida
|
22 |
+
if VIDEO_URL == "URL_DO_SEU_VIDEO_AQUI":
|
23 |
+
print("AVISO: A URL do vídeo não foi definida. Por favor, edite o script e insira a URL desejada.")
|
24 |
+
# exit(1)
|
25 |
+
|
26 |
+
# --- Funções ---
|
27 |
+
|
28 |
+
def create_output_directory():
|
29 |
+
"""Cria o diretório de saída se não existir."""
|
30 |
+
if not os.path.exists(OUTPUT_DIR):
|
31 |
+
os.makedirs(OUTPUT_DIR)
|
32 |
+
print(f"Diretório criado: {OUTPUT_DIR}")
|
33 |
+
|
34 |
+
def retirar_sufixo_codec_arquivo(directory) -> None:
|
35 |
+
for filename in os.listdir(directory):
|
36 |
+
# Procura padrão como ".f123" antes da extensão
|
37 |
+
new_filename = re.sub(r'\.f\d{3}(?=\.\w+$)', '', filename)
|
38 |
+
if new_filename != filename:
|
39 |
+
old_path = os.path.join(directory, filename)
|
40 |
+
new_path = os.path.join(directory, new_filename)
|
41 |
+
os.rename(old_path, new_path)
|
42 |
+
print(f"Renomeado: {filename} → {new_filename}")
|
43 |
+
|
44 |
+
|
45 |
+
def download_audio(url, output_path):
|
46 |
+
"""Baixa apenas o áudio do YouTube usando yt-dlp."""
|
47 |
+
print(f"Baixando áudio de {url} para {output_path}...")
|
48 |
+
try:
|
49 |
+
# Comando yt-dlp para baixar o melhor áudio disponível e convertê-lo para mp3
|
50 |
+
command = [
|
51 |
+
'yt-dlp',
|
52 |
+
'-f', 'bestaudio',
|
53 |
+
'--extract-audio',
|
54 |
+
'--audio-format', 'mp3', # Pode ser 'mp3', 'm4a', 'wav', etc.
|
55 |
+
'-o', output_path,
|
56 |
+
url
|
57 |
+
]
|
58 |
+
result = subprocess.run(command, check=True, capture_output=True, text=True)
|
59 |
+
retirar_sufixo_codec_arquivo(OUTPUT_DIR)
|
60 |
+
|
61 |
+
print("Download de áudio concluído com sucesso.")
|
62 |
+
return True
|
63 |
+
except subprocess.CalledProcessError as e:
|
64 |
+
print(f"Erro ao baixar o áudio: {e}")
|
65 |
+
print(f"Saída do erro: {e.stderr}")
|
66 |
+
return False
|
67 |
+
except FileNotFoundError:
|
68 |
+
print("Erro: O comando 'yt-dlp' não foi encontrado. Certifique-se de que ele está instalado e no PATH do sistema.")
|
69 |
+
return False
|
70 |
+
|
71 |
+
|
72 |
+
|
73 |
+
# --- Atualização do Bloco Principal ---
|
74 |
+
# (Adicionar inicialização do cliente OpenAI e o loop de análise)
|
75 |
+
if __name__ == "__main__":
|
76 |
+
create_output_directory()
|
77 |
+
|
78 |
+
# Etapa 1: Baixar o vídeo
|
79 |
+
video_downloaded_or_exists = False
|
80 |
+
if VIDEO_URL != "URL_DO_SEU_VIDEO_AQUI":
|
81 |
+
if download_video(VIDEO_URL, VIDEO_PATH):
|
82 |
+
print(f"Vídeo salvo em: {VIDEO_PATH}")
|
83 |
+
video_downloaded_or_exists = True
|
84 |
+
else:
|
85 |
+
print("Falha no download do vídeo. Pulando etapas dependentes.")
|
86 |
+
elif os.path.exists(VIDEO_PATH):
|
87 |
+
print(f"URL não fornecida, mas vídeo encontrado em {VIDEO_PATH}. Tentando processar.")
|
88 |
+
video_downloaded_or_exists = True
|
89 |
+
else:
|
90 |
+
print("URL do vídeo não fornecida e vídeo local não encontrado. Pulando download e extração.")
|
91 |
+
|
92 |
+
# Etapa 2: Extrair frames
|
93 |
+
if video_downloaded_or_exists:
|
94 |
+
extracted_frames = extract_frames(VIDEO_PATH, OUTPUT_DIR, FRAME_INTERVAL_SECONDS)
|
95 |
+
else:
|
96 |
+
print("Pulando extração de frames pois o vídeo não está disponível.")
|
97 |
+
|
98 |
+
# Etapa 3 e 4: Codificar e Analisar Frames
|
99 |
+
if extracted_frames and openai_client:
|
100 |
+
print(f"\nIniciando análise de {len(extracted_frames)} frames com {GPT_MODEL}...")
|
101 |
+
for frame_path in extracted_frames:
|
102 |
+
print(f"\nProcessando frame: {frame_path}")
|
103 |
+
# Extrai timestamp do nome do arquivo, se possível
|
104 |
+
timestamp_str = "unknown"
|
105 |
+
try:
|
106 |
+
# Exemplo: frame_0000_time_0.00s.png
|
107 |
+
parts = os.path.basename(frame_path).split('_')
|
108 |
+
if len(parts) >= 4 and parts[2] == 'time':
|
109 |
+
timestamp_str = parts[3].replace('s.png','')
|
110 |
+
except Exception:
|
111 |
+
pass # Mantém 'unknown' se o parsing falhar
|
112 |
+
|
113 |
+
# Codifica o frame
|
114 |
+
base64_image = encode_frame_to_base64(frame_path)
|
115 |
+
|
116 |
+
if base64_image:
|
117 |
+
# Analisa o frame com GPT-4o
|
118 |
+
# analysis_result = analyze_frame_with_gpt4o(openai_client, base64_image, PROMPT_TEXT)
|
119 |
+
result_entry = {
|
120 |
+
"frame_path": frame_path,
|
121 |
+
"timestamp_approx_sec": timestamp_str,
|
122 |
+
"analysis": f' pulado frame {frame_path}' #analysis_result
|
123 |
+
}
|
124 |
+
analysis_results_list.append(result_entry)
|
125 |
+
|
126 |
+
# Pausa opcional para evitar rate limiting
|
127 |
+
time.sleep(1) # Pausa de 1 segundo entre as chamadas
|
128 |
+
else:
|
129 |
+
print(f"Falha ao codificar o frame {frame_path}. Pulando análise.")
|
130 |
+
analysis_results_list.append({
|
131 |
+
"frame_path": frame_path,
|
132 |
+
"timestamp_approx_sec": timestamp_str,
|
133 |
+
"analysis": {"error": "Failed to encode frame to base64."}
|
134 |
+
})
|
135 |
+
print("\nAnálise de todos os frames concluída.")
|
136 |
+
elif not extracted_frames:
|
137 |
+
print("Nenhum frame foi extraído. Pulando etapa de análise.")
|
138 |
+
elif not openai_client:
|
139 |
+
print("Cliente OpenAI não inicializado (verifique a API Key). Pulando etapa de análise.")
|
140 |
+
|
141 |
+
# Próxima etapa: Compilar resultados
|
142 |
+
print(f"\nPróxima etapa a ser implementada: Compilação dos resultados ({len(analysis_results_list)} análises) em um relatório.")
|
143 |
+
|
144 |
+
|
145 |
+
# ... (código anterior para inicialização, download, extração, análise) ...
|
146 |
+
|
147 |
+
# Etapa 5: Compilar e Salvar Resultados
|
148 |
+
if analysis_results_list:
|
149 |
+
print(f"\nCompilando {len(analysis_results_list)} resultados da análise...")
|
150 |
+
if save_results_to_json(analysis_results_list, RESULTS_FILE):
|
151 |
+
print("Compilação e salvamento dos resultados concluídos.")
|
152 |
+
else:
|
153 |
+
print("Falha ao salvar os resultados da análise.")
|
154 |
+
else:
|
155 |
+
print("Nenhum resultado de análise para compilar.")
|
156 |
+
|
157 |
+
print("\n--- Processo de Análise de Vídeo Concluído ---")
|
158 |
+
print(f"Verifique o diretório '{OUTPUT_DIR}' para os frames extraídos (se aplicável).")
|
159 |
+
print(f"Verifique o arquivo '{RESULTS_FILE}' para os resultados da análise (se aplicável).")
|
160 |
+
print("Lembre-se de substituir os placeholders para URL_DO_SEU_VIDEO_AQUI e SUA_CHAVE_API_OPENAI_AQUI no script.")
|
161 |
+
|
162 |
+
|
search_script_v2.py → tool_search_script.py
RENAMED
File without changes
|
video_analyzer.py → tool_video_analyzer.py
RENAMED
File without changes
|