gdms commited on
Commit
1542c35
·
1 Parent(s): eebb83e

Montando a solução para submissão (inicio)

Browse files
Analisador de Vídeo do YouTube com GPT-4o.md DELETED
@@ -1,125 +0,0 @@
1
- # Analisador de Vídeo do YouTube com GPT-4o
2
-
3
- Este script Python automatiza o processo de baixar um vídeo do YouTube, extrair frames em intervalos regulares, e usar a API do OpenAI (GPT-4o) para analisar cada frame (neste caso, contar aves). Os resultados são salvos em um arquivo JSON.
4
-
5
- ## Funcionalidades
6
-
7
- - Baixa vídeos do YouTube usando `yt-dlp`.
8
- - Extrai frames do vídeo a cada X segundos usando `opencv-python`.
9
- - Codifica os frames extraídos para o formato base64.
10
- - Envia cada frame para a API do OpenAI (GPT-4o) com um prompt customizável para análise.
11
- - Salva os resultados da análise (incluindo contagem de aves e a resposta bruta da API) em um arquivo JSON estruturado.
12
-
13
- ## Pré-requisitos
14
-
15
- - Python 3.7+
16
- - `pip` (gerenciador de pacotes Python)
17
- - `yt-dlp` instalado e acessível no PATH do sistema. (Se não estiver instalado via pip, pode ser necessário instalar separadamente: [https://github.com/yt-dlp/yt-dlp](https://github.com/yt-dlp/yt-dlp))
18
-
19
- ## Instalação
20
-
21
- 1. **Clone ou baixe este repositório/script:**
22
- Salve o arquivo `video_analyzer.py` e `requirements.txt` no seu computador.
23
-
24
- 2. **Crie um ambiente virtual (recomendado):**
25
- ```bash
26
- python -m venv venv
27
- source venv/bin/activate # No Windows use `venv\Scripts\activate`
28
- ```
29
-
30
- 3. **Instale as dependências:**
31
- ```bash
32
- pip install -r requirements.txt
33
- ```
34
- Isso instalará `yt-dlp`, `opencv-python`, e `openai`.
35
-
36
- ## Configuração
37
-
38
- Antes de executar o script, você **precisa** editar o arquivo `video_analyzer.py` e substituir os seguintes placeholders:
39
-
40
- 1. `VIDEO_URL`: Substitua `"URL_DO_SEU_VIDEO_AQUI"` pela URL completa do vídeo do YouTube que você deseja analisar.
41
- ```python
42
- VIDEO_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" # Exemplo
43
- ```
44
-
45
- 2. `OPENAI_API_KEY`: Substitua `"SUA_CHAVE_API_OPENAI_AQUI"` pela sua chave secreta da API OpenAI.
46
- ```python
47
- OPENAI_API_KEY = "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Exemplo
48
- ```
49
-
50
- 3. **(Opcional) `PROMPT_TEXT`**: Você pode ajustar o prompt enviado ao GPT-4o para se adequar melhor à sua necessidade de análise. O padrão é:
51
- ```python
52
- PROMPT_TEXT = "Quantas aves existem nesta imagem? Responda apenas com o número."
53
- ```
54
-
55
- 4. **(Opcional) `FRAME_INTERVAL_SECONDS`**: Altere o valor (padrão: 5) para definir o intervalo em segundos entre os frames extraídos.
56
- ```python
57
- FRAME_INTERVAL_SECONDS = 10 # Exemplo: extrair a cada 10 segundos
58
- ```
59
-
60
- 5. **(Opcional) `OUTPUT_DIR`**: Define o diretório onde o vídeo baixado, os frames e o arquivo de resultados serão salvos (padrão: `./video_analysis_output`).
61
- ```python
62
- OUTPUT_DIR = "./meus_resultados_video" # Exemplo
63
- ```
64
-
65
- ## Como Executar
66
-
67
- 1. Certifique-se de que você configurou a `VIDEO_URL` e `OPENAI_API_KEY` no script `video_analyzer.py`.
68
- 2. Navegue até o diretório onde você salvou os arquivos no seu terminal.
69
- 3. Execute o script:
70
- ```bash
71
- python video_analyzer.py
72
- ```
73
-
74
- O script irá:
75
- - Criar o diretório de saída (se não existir).
76
- - Baixar o vídeo especificado.
77
- - Extrair os frames no intervalo definido.
78
- - Codificar cada frame e enviá-lo para a API do OpenAI.
79
- - Imprimir o progresso e os resultados da análise no console.
80
- - Salvar os resultados detalhados no arquivo `analysis_results.json` dentro do diretório de saída.
81
-
82
- ## Arquivos de Saída
83
-
84
- - **`video_analysis_output/`** (ou o diretório definido em `OUTPUT_DIR`):
85
- - `downloaded_video.mp4`: O vídeo baixado do YouTube.
86
- - `frame_xxxx_time_yy.zzs.png`: Os frames extraídos do vídeo.
87
- - `analysis_results.json`: Um arquivo JSON contendo a lista de análises para cada frame processado, incluindo o caminho do frame, timestamp aproximado e a resposta da API.
88
-
89
- Exemplo de `analysis_results.json`:
90
- ```json
91
- [
92
- {
93
- "frame_path": "./video_analysis_output/frame_0000_time_0.00s.png",
94
- "timestamp_approx_sec": "0.00",
95
- "analysis": {
96
- "bird_count": 3,
97
- "raw_response": "3"
98
- }
99
- },
100
- {
101
- "frame_path": "./video_analysis_output/frame_0001_time_5.00s.png",
102
- "timestamp_approx_sec": "5.00",
103
- "analysis": {
104
- "error": "Failed to parse bird count from response.",
105
- "raw_response": "Parece haver algumas aves voando ao fundo."
106
- }
107
- },
108
- {
109
- "frame_path": "./video_analysis_output/frame_0002_time_10.00s.png",
110
- "timestamp_approx_sec": "10.00",
111
- "analysis": {
112
- "error": "API key not configured."
113
- }
114
- }
115
- // ... mais resultados
116
- ]
117
- ```
118
-
119
- ## Solução de Problemas
120
-
121
- - **Erro `yt-dlp: command not found`**: Certifique-se de que `yt-dlp` está instalado corretamente e acessível no PATH do seu sistema. Tente reinstalar com `pip install --force-reinstall yt-dlp`.
122
- - **Erro ao baixar vídeo**: Verifique a URL do vídeo e sua conexão com a internet. Alguns vídeos podem ter restrições de download.
123
- - **Erro da API OpenAI**: Verifique se sua chave da API está correta, se você tem créditos/limites suficientes na sua conta OpenAI e se o modelo (`gpt-4o`) está correto e disponível para sua chave.
124
- - **Frames não extraídos**: Verifique se o arquivo de vídeo foi baixado corretamente e se o OpenCV (`cv2`) está funcionando. O vídeo pode estar corrompido ou em um formato incompatível.
125
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -21,10 +21,3 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
21
  - Exemplo de prompt: You are a general AI assistant. I will ask you a question. Report your thoughts, and finish your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER]. YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
22
  - https://huggingface.co/spaces/agents-course/Students_leaderboard - Leaderboard do Curso
23
 
24
-
25
-
26
- ### Arquitetura da Solução
27
-
28
- Uma LLM só para fazer tudo me parece confuso, o prompt vai virar um MONSTRO!!!
29
- Acredito que a melhor solução é ter agentes especialistas provendo o orquestrador de informação, ele deve saber se o que foi informado é suficiente e pedir mais, por exemplo.
30
- Ter somente uma LLM obriga a explicar tudo, inclusive como navegar
 
21
  - Exemplo de prompt: You are a general AI assistant. I will ask you a question. Report your thoughts, and finish your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER]. YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
22
  - https://huggingface.co/spaces/agents-course/Students_leaderboard - Leaderboard do Curso
23
 
 
 
 
 
 
 
 
Script Python de Busca e Extração de Conteúdo Web.md DELETED
@@ -1,87 +0,0 @@
1
- # Script Python de Busca e Extração de Conteúdo Web
2
-
3
- ## 1. Propósito
4
-
5
- Este script Python foi projetado para automatizar o processo de busca de informações na web usando a API Tavily, com funcionalidades especiais para priorizar e extrair conteúdo de páginas da Wikipedia, incluindo revisões históricas específicas por data. O conteúdo das páginas encontradas é baixado e convertido para o formato Markdown para fácil leitura e processamento posterior.
6
-
7
- ## 2. Requisitos
8
-
9
- * **Python:** Versão 3.11 ou superior.
10
- * **Bibliotecas Python:** `requests`, `tavily-python`, `markdownify`, `wikipedia-api`.
11
-
12
- Você pode instalar as bibliotecas necessárias usando o pip para Python 3.11:
13
- ```bash
14
- python3.11 -m pip install requests tavily-python markdownify wikipedia-api
15
- ```
16
-
17
- ## 3. Configuração
18
-
19
- * **Chave da API Tavily:** O script requer uma chave da API Tavily para funcionar. Você precisa obter sua própria chave no [site da Tavily AI](https://tavily.com/).
20
- * **Inserir a Chave:** Abra o arquivo `search_script_v2.py` em um editor de texto e localize a linha que define a variável `TAVILY_API_KEY` (próximo ao início do script ou dentro do bloco `if __name__ == "__main__":`). Substitua o valor atual pela sua chave da API Tavily.
21
- ```python
22
- # Exemplo dentro do if __name__ == "__main__":
23
- TAVILY_API_KEY = "SUA_CHAVE_TAVILY_AQUI"
24
- ```
25
- *Nota: No script fornecido (`search_script_v2.py`), a chave que você forneceu já foi inserida para teste, mas é recomendável que você a gerencie de forma segura (ex: variáveis de ambiente) para uso futuro.*
26
-
27
- * **Idioma da Wikipedia:** Por padrão, o script está configurado para buscar na Wikipedia em inglês (`WIKI_LANG = 'en'`). Se desejar usar outro idioma, altere o valor desta variável perto do início do script.
28
- ```python
29
- WIKI_LANG = 'pt' # Exemplo para Português
30
- ```
31
-
32
- ## 4. Uso
33
-
34
- ### Execução Básica
35
-
36
- Você pode executar o script diretamente do terminal usando Python 3.11:
37
- ```bash
38
- python3.11 search_script_v2.py
39
- ```
40
- Por padrão, o script executará os exemplos definidos no bloco `if __name__ == "__main__":`:
41
- 1. Uma busca geral por "History of Artificial Intelligence".
42
- 2. Uma busca por "Albert Einstein" com prioridade para a Wikipedia.
43
- 3. Uma busca pela revisão histórica da página "Python (programming language)" da Wikipedia próxima a 15 de janeiro de 2010.
44
-
45
- ### Função Principal: `search_and_extract`
46
-
47
- O núcleo do script é a função `search_and_extract`. Você pode importá-la em outros scripts Python ou modificar o bloco `if __name__ == "__main__":` para realizar buscas personalizadas.
48
-
49
- **Parâmetros:**
50
-
51
- * `query` (str): O termo ou frase que você deseja pesquisar.
52
- * `use_wikipedia_priority` (bool, opcional): Se `True`, o script tentará encontrar um resultado da Wikipedia primeiro. Se encontrar, processará apenas esse resultado. Se não encontrar, processará os 5 primeiros resultados gerais. Padrão: `False`.
53
- * `wikipedia_date` (str, opcional): Se `use_wikipedia_priority` for `True` e esta data (formato "AAAA-MM-DD") for fornecida, o script buscará especificamente a revisão da página da Wikipedia (usando `query` como título da página) mais próxima e anterior ou igual a essa data. A busca geral da Tavily não será realizada neste caso. Padrão: `None`.
54
-
55
- **Exemplos de Modificação no Script:**
56
-
57
- ```python
58
- if __name__ == "__main__":
59
- TAVILY_API_KEY = "SUA_CHAVE_TAVILY_AQUI" # Não esqueça sua chave!
60
-
61
- # Exemplo: Buscar sobre "Machine Learning" (5 primeiros resultados)
62
- resultados_ml = search_and_extract("Machine Learning")
63
- # ... (código para salvar ou processar resultados_ml)
64
-
65
- # Exemplo: Buscar "Brazil" na Wikipedia (prioritário)
66
- resultados_br_wiki = search_and_extract("Brazil", use_wikipedia_priority=True)
67
- # ...
68
-
69
- # Exemplo: Buscar revisão histórica de "World War II" de 1945-05-08
70
- resultados_ww2_hist = search_and_extract("World War II", use_wikipedia_priority=True, wikipedia_date="1945-05-08")
71
- # ...
72
- ```
73
-
74
- ## 5. Saída
75
-
76
- * **Console:** O script imprime mensagens de progresso no console, indicando qual busca está sendo realizada, quais URLs estão sendo processadas e se houve algum erro (como falha ao acessar uma URL).
77
- * **Arquivos Markdown (.md):** Para cada página web processada com sucesso, o script salva seu conteúdo convertido em um arquivo `.md` no mesmo diretório onde o script foi executado. Os nomes dos arquivos são gerados com base no tipo de busca e na ordem dos resultados (ex: `general_result_1.md`, `wiki_priority_result_1.md`, `historical_result_python_2010.md`).
78
- * Arquivos de revisões históricas da Wikipedia incluem um cabeçalho indicando o título da página, o timestamp e ID da revisão, e a data alvo da busca.
79
- * Outros arquivos incluem um cabeçalho simples indicando a URL de origem.
80
-
81
- ## 6. Limitações
82
-
83
- * **Bloqueios de Sites:** Alguns sites podem bloquear tentativas de download automático de conteúdo (scraping), resultando em erros (como o erro 403 Forbidden observado com Cloudflare durante os testes). O script tenta lidar com isso, mas não pode garantir o acesso a todos os sites.
84
- * **Qualidade da Conversão para Markdown:** A conversão de HTML para Markdown depende da estrutura da página original e da biblioteca `markdownify`. Páginas complexas podem não ser convertidas perfeitamente.
85
- * **Precisão do Título da Wikipedia:** Ao buscar revisões históricas, é importante fornecer o título exato da página da Wikipedia (conforme usado na URL, substituindo espaços por underscores se necessário, embora o script tente lidar com isso). O script tenta encontrar o título canônico em caso de redirecionamentos, mas pode falhar em casos complexos.
86
- * **API Tavily:** A qualidade e a quantidade dos resultados dependem da API Tavily e do plano associado à sua chave.
87
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
{arquivos-perguntas → question_files}/cca530fc-4052-43b2-b130-b30968d8aa44.png RENAMED
File without changes
{arquivos-perguntas → question_files}/f918266a-b3e0-4914-865d-4faa564f1aef.py RENAMED
File without changes
requirements-audio-extractor.txt DELETED
@@ -1 +0,0 @@
1
- yt-dlp openai-whisper torch
 
 
requirements-search-web.txt DELETED
@@ -1 +0,0 @@
1
- gradio requests readability-lxml bs4
 
 
requirements-video.txt DELETED
@@ -1,2 +0,0 @@
1
- yt-dlp opencv-python openai
2
- google-generativeai
 
 
 
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ langgraph
2
+ langgraph-supervisor
3
+ langchain-tavily
4
+ langchain[openai]
5
+ bs4
6
+ tavily-python
7
+ markdownify
8
+ wikipedia-api
9
+ yt-dlp
support/cookie-youtube.txt ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Netscape HTTP Cookie File
2
+ # http://curl.haxx.se/rfc/cookie_spec.html
3
+ # This is a generated file! Do not edit.
4
+
5
+ .youtube.com TRUE / TRUE 1780509625 SSID A2Pc5uDJ8XKTF6zrk
6
+ .youtube.com TRUE / TRUE 1780509625 SAPISID SuAIDuB7KHb6yHE8/A1xVJfgW4L9fylCdI
7
+ .youtube.com TRUE / TRUE 1780509625 __Secure-1PAPISID SuAIDuB7KHb6yHE8/A1xVJfgW4L9fylCdI
8
+ .youtube.com TRUE / TRUE 1780509625 __Secure-3PAPISID SuAIDuB7KHb6yHE8/A1xVJfgW4L9fylCdI
9
+ .youtube.com TRUE / TRUE 1782894625 PREF f7=4100&tz=America.Sao_Paulo&f5=30000
10
+ .youtube.com TRUE / TRUE 1756738350 LOGIN_INFO AFmmF2swRAIgek-aNLieXLQ2W4kFo9JBLaQbThnQdBBnIJlfz2rST7kCIGfcKd08yuixn-a5j7G87RPe1QRC_10s0_EkT9GXuimM:QUQ3MjNmenpFMW41M1dOU0NneDM4Z1FRT3NoLUJHUWNnN01fcmhraHNiYkhod3RndFF4cUZpU2NpRUZGNkdNZHExUTFyUWRjWHFaQ2NyanZOU0VJMWJUVmZQem5wYXNxLTEwSTlIZjFncm9hc2pkREVpOW5heUpMMzZOTDZVT2tyb2FrbVg0MThTSFFaMjc5amF1SXJBYVpyR2pWUzhrWk5R
11
+ .youtube.com TRUE / FALSE 1780509625 HSID Ao9Ddl-xtiPdNQEQz
12
+ .youtube.com TRUE / FALSE 1780509625 APISID sNHjC4ep6KXK8HXd/ASnMdbn-GgFxtCMWy
13
+ .youtube.com TRUE / FALSE 1780509625 SID g.a000wQhXlZvpNNAfuZSmvofvKiscZ56fyLSRIAS0a9fEM9laJSqB0JQVjfZSIVO-nT2iGEqyeQACgYKAWMSARASFQHGX2Mi85DM6TB399jxYYfNvQEg2RoVAUF8yKqUjUDbmzNuNQtwzUkXtSRQ0076
14
+ .youtube.com TRUE / TRUE 1780509625 __Secure-1PSID g.a000wQhXlZvpNNAfuZSmvofvKiscZ56fyLSRIAS0a9fEM9laJSqBZy9vf6U-6rjKcY-OjnP2ZQACgYKAUESARASFQHGX2MiuzKUb8oHE2yM7z6aRx7R8BoVAUF8yKoF0vpTNlyKbMiJYAD_KjPp0076
15
+ .youtube.com TRUE / TRUE 1780509625 __Secure-3PSID g.a000wQhXlZvpNNAfuZSmvofvKiscZ56fyLSRIAS0a9fEM9laJSqBnrPl__ssU8-iX8vopuJLeQACgYKAdESARASFQHGX2MibltCjaidY0d6MsiEFfk3xxoVAUF8yKrbBezcIX1hfuh9wLWi2GZk0076
16
+ .youtube.com TRUE / TRUE 1764145825 NID 524=k0m4xJ8T9r3KqiV8SL5A1SXBDyLVMHT3jimUKAyqekaUMkBQehDT78G_z-8Qf1NrlKvlgJIjd-MOk1N_Vc_VYFD-Nqu6fLucmop6f2OgfUU1ghS0Qabc1Ilc0N4R_ong6vgYz-isrxq6AikAteY2-qgj8NWxpGYTMHCSAGmqvBhzuZ8DObwssGcuqCIt4oyHHe86FEXJ8uO19VJtAjK0clpTZ3vXY1LlkRYEw0gZOa6l5MtsurQrdJ52J_cC7w1eTj_FK5jWSDeLRfamVunE4T_4LoyRa9wZpg
17
+ .youtube.com TRUE / TRUE 1779870626 __Secure-1PSIDTS sidts-CjIB5H03PygaHPcqPrj80jQO8a7h-ukCE6muoH2H5baJlskUXzwBS4jNTZR94bjQqg81uhAA
18
+ .youtube.com TRUE / TRUE 1779870626 __Secure-3PSIDTS sidts-CjIB5H03PygaHPcqPrj80jQO8a7h-ukCE6muoH2H5baJlskUXzwBS4jNTZR94bjQqg81uhAA
19
+ .youtube.com TRUE / TRUE 1748335240 CONSISTENCY AKreu9usdmHmaLV5jSyHmMB5g9KrYw3X4SqSy4vmXXy6M0Fa3On-VRNqihVLsnKA05h-2aywO8VOxz0prR2qIupYa5titplBJk6uzazeioMpbxXoA6_fn4ylUnMDAGRS_QHJaygSPZwmBtWzkLD4AAz0
20
+ .youtube.com TRUE / FALSE 1779870771 SIDCC AKEyXzVVT-WPwJQ2KNnMG20tDGrI2gtVBu7h08g1jBfUx-FfrOFBgLJgHUwBpQ_Qr6X70OhdFwE
21
+ .youtube.com TRUE / TRUE 1779870771 __Secure-1PSIDCC AKEyXzWLIwbrS-HtbNLeAWxTCFBMD9GaGcXoerD3rbgR6Gq7wBbtVx1KlLcSaxQNLmzM7TcdViUF
22
+ .youtube.com TRUE / TRUE 1779870771 __Secure-3PSIDCC AKEyXzWNE948sYhtAabMgDW5hWMQNIllF2JdTZZePwama1lGF-WYk9dYE172x0eOiBOCYK4C5J_-
23
+ .youtube.com TRUE / TRUE 1763886621 VISITOR_INFO1_LIVE WnF8m8hwId0
24
+ .youtube.com TRUE / TRUE 1763886621 VISITOR_PRIVACY_METADATA CgJCUhIEGgAgYw%3D%3D
25
+ .youtube.com TRUE / TRUE 0 YSC p6JIu7cPkrA
26
+ .youtube.com TRUE / TRUE 1763886621 __Secure-ROLLOUT_TOKEN CKSSvYDioYnmBhCB4sLKtJOLAxj-ub2AnsONAw%3D%3D
todo (1).md DELETED
@@ -1,12 +0,0 @@
1
- # Todo List - Análise de Vídeo do YouTube
2
-
3
- - [ ] Instalar dependências: `yt-dlp`, `opencv-python`, `openai`.
4
- - [ ] Criar script Python (`video_analyzer.py`) com placeholders para URL, API Key e prompt.
5
- - [X] Implementar download do vídeo (`yt-dlp`).
6
- - [X] Implementar extração de frames a cada 5 segundos (`opencv-python`).
7
- - [X] Implementar codificação de frames para base64.
8
- - [X] Implementar chamada à API GPT-4o para análise de imagem.
9
- - [X] Implementar lógica principal e compilação de resultados.
10
- - [X] Criar arquivo `requirements.txt`.
11
- - [X] Criar arquivo `README.md` com instruções.
12
- - [X] Enviar arquivos gerados ao usuário.
 
 
 
 
 
 
 
 
 
 
 
 
 
todo.md DELETED
@@ -1,11 +0,0 @@
1
- # Tarefa: Criar Script Python de Busca e Extração de Conteúdo
2
-
3
- - [ ] 001: Analisar requisitos detalhados (feito)
4
- - [ ] 002: Selecionar API de busca (Tavily) (feito)
5
- - [X] 003: Desenvolver funções específicas para Wikipedia (priorização e acesso ao histórico)
6
- - [X] 004: Implementar extração de conteúdo e conversão para Markdown
7
- - [X] 005: Adicionar suporte a revisões históricas da Wikipedia por data
8
- - [X] 006: Integrar componentes no script completo
9
- - [X] 007: Testar funcionalidade do script
10
- - [X] 008: Documentar uso do código e entregar
11
-
 
 
 
 
 
 
 
 
 
 
 
 
tool_audio_extractor.py DELETED
@@ -1,140 +0,0 @@
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
- import shutil
14
- from openai import OpenAI
15
-
16
-
17
- # --- Configurações (Substitua os placeholders) ---
18
- VIDEO_URL = "https://www.youtube.com/watch?v=1htKBjuUWec" # Substitua pela URL do vídeo do YouTube
19
- OUTPUT_DIR = "./audio_analysis_output" # Diretório para salvar o áudio
20
- AUDIO_FILENAME = "downloaded_audio"
21
- TRANSCRIPT_FILENAME = "transcript.txt"
22
-
23
- # Verifica se a URL foi definida
24
- if VIDEO_URL == "URL_DO_SEU_VIDEO_AQUI":
25
- print("AVISO: A URL do vídeo não foi definida. Por favor, edite o script e insira a URL desejada.")
26
- # exit(1)
27
-
28
- # --- Funções ---
29
-
30
- def create_or_clear_output_directory():
31
- """Cria o diretório de saída se não existir."""
32
- if not os.path.exists(OUTPUT_DIR):
33
- os.makedirs(OUTPUT_DIR)
34
- print(f"Diretório criado: {OUTPUT_DIR}")
35
- else:
36
- # Limpa todos os arquivos e subdiretórios
37
- for filename in os.listdir(OUTPUT_DIR):
38
- file_path = os.path.join(OUTPUT_DIR, filename)
39
- try:
40
- if os.path.isfile(file_path) or os.path.islink(file_path):
41
- os.unlink(file_path)
42
- elif os.path.isdir(file_path):
43
- shutil.rmtree(file_path)
44
- except Exception as e:
45
- print(f"Erro ao excluir {file_path}: {e}")
46
- print(f"Diretório limpo: {OUTPUT_DIR}")
47
-
48
- def retirar_sufixo_codec_arquivo(directory) -> None:
49
- for filename in os.listdir(directory):
50
- # Procura padrão como ".f123" antes da extensão
51
- new_filename = re.sub(r'\.f\d{3}(?=\.\w+$)', '', filename)
52
- if new_filename != filename:
53
- old_path = os.path.join(directory, filename)
54
- new_path = os.path.join(directory, new_filename)
55
- os.rename(old_path, new_path)
56
- print(f"Renomeado: {filename} → {new_filename}")
57
-
58
-
59
- def download_audio(url):
60
- """Baixa apenas o áudio do YouTube usando yt-dlp."""
61
-
62
- output_path = f'{OUTPUT_DIR}/{AUDIO_FILENAME}.%(ext)s'
63
-
64
- print(f"Baixando áudio de {url} para {output_path}...")
65
- try:
66
- # Comando yt-dlp para baixar o melhor áudio disponível e convertê-lo para mp3
67
-
68
-
69
- command = [
70
- 'yt-dlp',
71
- '-f', 'bestaudio[ext=m4a]',
72
- '-o', output_path,
73
- url
74
- ]
75
-
76
- result = subprocess.run(command, check=True, capture_output=True, text=True)
77
- retirar_sufixo_codec_arquivo(OUTPUT_DIR)
78
-
79
- print("Download de áudio concluído com sucesso.")
80
- return True
81
- except subprocess.CalledProcessError as e:
82
- print(f"Erro ao baixar o áudio: {e}")
83
- print(f"Saída do erro: {e.stderr}")
84
- return False
85
- except FileNotFoundError:
86
- print("Erro: O comando 'yt-dlp' não foi encontrado. Certifique-se de que ele está instalado e no PATH do sistema.")
87
- return False
88
-
89
- def extract_text_from_audio() -> str:
90
- """
91
- Usa a API Whisper da OpenAI para transcrever o áudio em texto com quebras de linha naturais,
92
- removendo timestamps e IDs. Salva em arquivo .txt se o caminho for fornecido.
93
- """
94
- client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
95
-
96
- try:
97
- audio_path = f"{OUTPUT_DIR}/{AUDIO_FILENAME}.m4a"
98
- print(f"Iniciando transcrição (formato SRT simplificado): {audio_path}")
99
-
100
- with open(audio_path, "rb") as audio_file:
101
- transcription = client.audio.transcriptions.create(
102
- model="whisper-1",
103
- file=audio_file,
104
- response_format="srt"
105
- )
106
-
107
- # Remove linhas com números e timestamps
108
- lines = transcription.splitlines()
109
- only_text = [line.strip() for line in lines if not re.match(r"^\d+$", line) and "-->" not in line]
110
- formatted_text = "\n".join(only_text)
111
-
112
- # Salva em .txt se desejado
113
- output_txt_path = f"{OUTPUT_DIR}/{TRANSCRIPT_FILENAME}"
114
- with open(output_txt_path, "w", encoding="utf-8") as f:
115
- f.write(formatted_text)
116
- print(f"Transcrição salva em: {output_txt_path}")
117
-
118
- return formatted_text
119
- except Exception as e:
120
- print(f"Erro ao transcrever áudio: {e}")
121
- return ""
122
-
123
-
124
- # --- Atualização do Bloco Principal ---
125
- # (Adicionar inicialização do cliente OpenAI e o loop de análise)
126
- if __name__ == "__main__":
127
- create_or_clear_output_directory()
128
- # Etapa 1: Baixar o vídeo
129
- video_downloaded_or_exists = False
130
- if VIDEO_URL != "URL_DO_SEU_VIDEO_AQUI":
131
- if download_audio(VIDEO_URL):
132
- print(f"AUDIO salvo em: {OUTPUT_DIR}")
133
- video_downloaded_or_exists = True
134
- else:
135
- print("Falha no download do vídeo. Pulando etapas dependentes.")
136
- else:
137
- print("Vídeo não informado")
138
- extract_text_from_audio()
139
-
140
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tool_image_llm_template.py DELETED
@@ -1,289 +0,0 @@
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
- from openai import OpenAI # Importa a classe OpenAI
12
- import json
13
- import re
14
- import shutil
15
-
16
- import google.generativeai as genai
17
- import requests
18
-
19
-
20
- # --- Configurações (Substitua os placeholders) ---
21
- OUTPUT_DIR = "./image_analysis_output" # Diretório para salvar o vídeo e os frames
22
- GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
23
- GEMINI_MODEL = "gemini-2.0-flash"
24
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
25
- GPT_MODEL = "gpt-4o" # Modelo GPT a ser usado (certifique-se que é o correto para análise de imagem)
26
- PROMPT_TEXT = "Analyze the provided image of a chessboard, return the corresponding FEN (Forsyth–Edwards Notation), assuming black at the bottom and black turn. Include turn, castling rights, en passant (if possible), and full notation. Return only the FEN."
27
- #PROMPT_TEXT = "You are a chessboard position analyzer. Given an image of a chessboard: - Assume standard orientation: White at the bottom, Black at the top. - Identify all visible pieces and their positions. - Return the FEN string corresponding to the exact position. - Be precise. Do not omit or infer captured pieces. - Return only the FEN, no explanations."
28
- IMAGE_FILE = "arquivos-perguntas/cca530fc-4052-43b2-b130-b30968d8aa44.png"
29
- RESULTS_FILE = os.path.join(OUTPUT_DIR, "analysis_results.json")
30
- FEN_CORRETA = "3r2k1/pp3pp1/4b2p/7Q/3n4/PqBBR2P/5PP1/6K1 b - - 0 1"
31
- CHESSVISION_TO_FEN_URL = "http://app.chessvision.ai/predict"
32
- CHESS_MOVE_API = "https://chess-api.com/v1"
33
-
34
-
35
- if GEMINI_API_KEY == "SUA_CHAVE_API_OPENAI_AQUI" or not GEMINI_API_KEY or len(GEMINI_API_KEY) ==0 :
36
- print("AVISO: A chave da API GEMINI não foi definida. Por favor, edite o script e insira sua chave.")
37
- # Considerar sair do script ou lançar um erro se a chave for essencial para a execução completa
38
- # exit(1)
39
-
40
- # --- Funções ---
41
-
42
- def create_or_clear_output_directory():
43
- """Cria o diretório de saída se não existir."""
44
- if not os.path.exists(OUTPUT_DIR):
45
- os.makedirs(OUTPUT_DIR)
46
- print(f"Diretório criado: {OUTPUT_DIR}")
47
- else:
48
- # Limpa todos os arquivos e subdiretórios
49
- for filename in os.listdir(OUTPUT_DIR):
50
- file_path = os.path.join(OUTPUT_DIR, filename)
51
- try:
52
- if os.path.isfile(file_path) or os.path.islink(file_path):
53
- os.unlink(file_path)
54
- elif os.path.isdir(file_path):
55
- shutil.rmtree(file_path)
56
- except Exception as e:
57
- print(f"Erro ao excluir {file_path}: {e}")
58
- print(f"Diretório limpo: {OUTPUT_DIR}")
59
-
60
-
61
-
62
-
63
- def encode_image_to_base64(image_path):
64
- """Codifica um arquivo de imagem (frame) para base64."""
65
- try:
66
- with open(image_path, "rb") as image_file:
67
- return base64.b64encode(image_file.read()).decode('utf-8')
68
- except FileNotFoundError:
69
- print(f"Erro: Arquivo de frame não encontrado em {image_path}")
70
- return None
71
- except Exception as e:
72
- print(f"Erro ao codificar o frame {image_path} para base64: {e}")
73
- return None
74
-
75
-
76
- def analyze_image_with_gpt(base64_image, prompt):
77
- if OPENAI_API_KEY:
78
- try:
79
- openai_client = OpenAI(api_key=OPENAI_API_KEY)
80
- print("Cliente OpenAI inicializado.")
81
- except Exception as e:
82
- print(f"Erro ao inicializar o cliente OpenAI: {e}. As chamadas de API serão puladas.")
83
- else:
84
- print("Chave da API OpenAI não configurada. As chamadas de API serão puladas.")
85
-
86
- """Envia um frame codificado em base64 para a API GPT-4o e retorna a análise."""
87
- print(f"Enviando imagem para análise no {GPT_MODEL}...")
88
-
89
-
90
- payload = {
91
- "model": GPT_MODEL,
92
- "messages": [
93
- {
94
- "role": "user",
95
- "content": [
96
- {
97
- "type": "text",
98
- "text": prompt
99
- },
100
- {
101
- "type": "image_url",
102
- "image_url": {
103
- "url": f"data:image/png;base64,{base64_image}"
104
- }
105
- }
106
- ]
107
- }
108
- ],
109
- "max_tokens": 100 # Ajuste conforme necessário para a resposta esperada
110
- }
111
-
112
- try:
113
- # Cria o cliente OpenAI dentro da função para garantir que use a chave mais recente
114
- # (embora seja definida globalmente, isso pode ser útil se a chave for atualizada dinamicamente no futuro)
115
- # client = OpenAI(api_key=OPENAI_API_KEY)
116
-
117
- response = openai_client.chat.completions.create(
118
- model=payload["model"],
119
- messages=payload["messages"],
120
- max_tokens=payload["max_tokens"],
121
- temperature=0
122
- )
123
-
124
- # Extrai o conte��do da resposta
125
- analysis_result = response.choices[0].message.content.strip()
126
- fen = analysis_result.strip("`")
127
- fen = fen.replace("_", " ") #retorna _ no lugar de espaço em branco
128
- print(f"Análise recebida (raw): {analysis_result}")
129
- print(f"Análise tratada : {fen}")
130
- if fen != FEN_CORRETA:
131
- print(f"FEN INCORRETA ")
132
- else:
133
- print(f"FEN CORRETA ")
134
-
135
- return {"image_response": fen}
136
- except Exception as e:
137
- print(f"Erro ao chamar a API OpenAI: {e}")
138
- return {"error": str(e)}
139
-
140
-
141
- def analyze_image_with_gemini(base64_image, prompt):
142
- genai.configure(api_key=GEMINI_API_KEY)
143
- model = genai.GenerativeModel(GEMINI_MODEL)
144
-
145
- """Envia um frame codificado em base64 para a API GPT-4o e retorna a análise."""
146
- print(f"Enviando frame para análise no {GEMINI_MODEL}...")
147
-
148
-
149
- try:
150
-
151
- response = model.generate_content(
152
- contents=[
153
- {
154
- "role": "user",
155
- "parts": [
156
- {f"text": f"{prompt}"},
157
- {"inline_data": {
158
- "mime_type": "image/jpeg",
159
- "data": base64_image
160
- }}
161
- ]
162
- }
163
- ],
164
- generation_config={
165
- "temperature": 0.7,
166
- "max_output_tokens": 500
167
- })
168
-
169
- # Extrai o conteúdo da resposta
170
- analysis_result = response.text.strip()
171
- print(f"Análise recebida: {analysis_result}")
172
-
173
- return {"image_response": analysis_result}
174
-
175
- except Exception as e:
176
- print(f"Erro ao chamar a API Gemini: {e}")
177
- return {"error": str(e)}
178
-
179
-
180
- def analyze_image_with_chessvision(base64_image):
181
- base64_image_encoded = f"data:image/jpeg;base64,{base64_image}"
182
- url = CHESSVISION_TO_FEN_URL
183
- payload = {
184
- "board_orientation": "predict",
185
- "cropped": False,
186
- "current_player": "black",
187
- "image": base64_image_encoded,
188
- "predict_turn": False
189
- }
190
-
191
- response = requests.post(url, json=payload)
192
- if response.status_code == 200:
193
- dados = response.json()
194
- if dados.get("success"):
195
- print(f"Retorno Chessvision {dados}")
196
- fen = dados.get("result")
197
- fen = fen.replace("_", " ") #retorna _ no lugar de espaço em branco
198
- return fen
199
- else:
200
- raise Exception("Requisição feita, mas falhou na predição.")
201
- else:
202
- raise Exception(f"Erro na requisição: {response.status_code}")
203
-
204
- def get_best_next_move(fen: str):
205
- url = CHESS_MOVE_API
206
- payload = {
207
- "fen": f"{fen}",
208
- "depth": 1
209
- }
210
- print(f"Buscando melhor movimento em chess-api.com ")
211
-
212
- response = requests.post(url, json=payload)
213
- if response.status_code == 200:
214
- dados = response.json()
215
- if dados.get("success"):
216
- move_algebric_notation = dados.get("san")
217
- move = dados.get("text")
218
- print(f"Melhor jogada segundo chess-api {move}")
219
- return move_algebric_notation
220
- else:
221
- raise Exception("Requisição feita, mas falhou na predição do próximo movimento.")
222
- else:
223
- raise Exception(f"Erro na requisição: {response.status_code}")
224
-
225
- def save_results_to_json(results_list, output_file):
226
- """Salva a lista de resultados da análise em um arquivo JSON."""
227
- print(f"Salvando resultados da análise em {output_file}...")
228
- try:
229
- with open(output_file, 'w', encoding='utf-8') as f:
230
- json.dump(results_list, f, ensure_ascii=False, indent=4)
231
- print(f"Resultados salvos com sucesso em: {output_file}")
232
- return True
233
- except Exception as e:
234
- print(f"Erro ao salvar os resultados em JSON: {e}")
235
- return False
236
-
237
-
238
- # --- Atualização do Bloco Principal ---
239
- # (Adicionar inicialização do cliente OpenAI e o loop de análise)
240
- if __name__ == "__main__":
241
- create_or_clear_output_directory()
242
- analysis_results_list = []
243
-
244
- print(f"\nIniciando análise da imagem {IMAGE_FILE} frames com {GEMINI_MODEL}...")
245
- # Extrai timestamp do nome do arquivo, se possível
246
- base64_image = encode_image_to_base64(IMAGE_FILE)
247
- if base64_image:
248
- # Analisa a imagem com Gemini
249
- fen = analyze_image_with_chessvision(base64_image) #analyze_image_with_gpt(base64_image, PROMPT_TEXT)
250
- move = get_best_next_move(fen)
251
- result_entry = {
252
- "image": IMAGE_FILE,
253
- "fen": fen,
254
- "move": move
255
- }
256
- analysis_results_list.append(result_entry)
257
-
258
- else:
259
- print(f"Falha ao codificar o frame {IMAGE_FILE}. Pulando análise.")
260
- analysis_results_list.append({
261
- "frame_path": IMAGE_FILE,
262
- "analysis": {"error": "Failed to encode frame to base64."}
263
- })
264
-
265
- # break # teste somente uma chamada
266
- print("\nAnálise de imagem concluída.")
267
-
268
- # Próxima etapa: Compilar resultados
269
- print(f"\nPróxima etapa a ser implementada: Compilação dos resultados ({len(analysis_results_list)} análises) em um relatório.")
270
-
271
-
272
- # ... (código anterior para inicialização, download, extração, análise) ...
273
-
274
- # Etapa 5: Compilar e Salvar Resultados
275
- if analysis_results_list:
276
- print(f"\nCompilando {len(analysis_results_list)} resultados da análise...")
277
- if save_results_to_json(analysis_results_list, RESULTS_FILE):
278
- print("Compilação e salvamento dos resultados concluídos.")
279
- else:
280
- print("Falha ao salvar os resultados da análise.")
281
- else:
282
- print("Nenhum resultado de análise para compilar.")
283
-
284
- print("\n--- Processo de Análise de Vídeo Concluído ---")
285
- print(f"Verifique o diretório '{OUTPUT_DIR}' para os frames extraídos (se aplicável).")
286
- print(f"Verifique o arquivo '{RESULTS_FILE}' para os resultados da análise (se aplicável).")
287
- print("Lembre-se de substituir os placeholders para URL_DO_SEU_VIDEO_AQUI e SUA_CHAVE_API_OPENAI_AQUI no script.")
288
-
289
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tool_image_to_fen.py DELETED
@@ -1,290 +0,0 @@
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
- from openai import OpenAI # Importa a classe OpenAI
12
- import json
13
- import re
14
- import shutil
15
-
16
- import google.generativeai as genai
17
- import requests
18
-
19
-
20
- # --- Configurações (Substitua os placeholders) ---
21
- OUTPUT_DIR = "./image_analysis_output" # Diretório para salvar o vídeo e os frames
22
- GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
23
- GEMINI_MODEL = "gemini-2.0-flash"
24
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
25
- GPT_MODEL = "gpt-4o" # Modelo GPT a ser usado (certifique-se que é o correto para análise de imagem)
26
- PROMPT_TEXT = "Analyze the provided image of a chessboard, return the corresponding FEN (Forsyth–Edwards Notation), assuming black at the bottom and black turn. Include turn, castling rights, en passant (if possible), and full notation. Return only the FEN."
27
- #PROMPT_TEXT = "You are a chessboard position analyzer. Given an image of a chessboard: - Assume standard orientation: White at the bottom, Black at the top. - Identify all visible pieces and their positions. - Return the FEN string corresponding to the exact position. - Be precise. Do not omit or infer captured pieces. - Return only the FEN, no explanations."
28
- IMAGE_FILE = "arquivos-perguntas/cca530fc-4052-43b2-b130-b30968d8aa44.png"
29
- RESULTS_FILE = os.path.join(OUTPUT_DIR, "analysis_results.json")
30
- FEN_CORRETA = "3r2k1/pp3pp1/4b2p/7Q/3n4/PqBBR2P/5PP1/6K1 b - - 0 1"
31
- CHESSVISION_TO_FEN_URL = "http://app.chessvision.ai/predict"
32
- CHESS_MOVE_API = "https://chess-api.com/v1"
33
-
34
-
35
- if GEMINI_API_KEY == "SUA_CHAVE_API_OPENAI_AQUI" or not GEMINI_API_KEY or len(GEMINI_API_KEY) ==0 :
36
- print("AVISO: A chave da API GEMINI não foi definida. Por favor, edite o script e insira sua chave.")
37
- # Considerar sair do script ou lançar um erro se a chave for essencial para a execução completa
38
- # exit(1)
39
-
40
- # --- Funções ---
41
-
42
- def create_or_clear_output_directory():
43
- """Cria o diretório de saída se não existir."""
44
- if not os.path.exists(OUTPUT_DIR):
45
- os.makedirs(OUTPUT_DIR)
46
- print(f"Diretório criado: {OUTPUT_DIR}")
47
- else:
48
- # Limpa todos os arquivos e subdiretórios
49
- for filename in os.listdir(OUTPUT_DIR):
50
- file_path = os.path.join(OUTPUT_DIR, filename)
51
- try:
52
- if os.path.isfile(file_path) or os.path.islink(file_path):
53
- os.unlink(file_path)
54
- elif os.path.isdir(file_path):
55
- shutil.rmtree(file_path)
56
- except Exception as e:
57
- print(f"Erro ao excluir {file_path}: {e}")
58
- print(f"Diretório limpo: {OUTPUT_DIR}")
59
-
60
-
61
-
62
-
63
- def encode_image_to_base64(image_path):
64
- """Codifica um arquivo de imagem (frame) para base64."""
65
- try:
66
- with open(image_path, "rb") as image_file:
67
- return base64.b64encode(image_file.read()).decode('utf-8')
68
- except FileNotFoundError:
69
- print(f"Erro: Arquivo de frame não encontrado em {image_path}")
70
- return None
71
- except Exception as e:
72
- print(f"Erro ao codificar o frame {image_path} para base64: {e}")
73
- return None
74
-
75
-
76
- def analyze_image_with_gpt(base64_image, prompt):
77
- if OPENAI_API_KEY:
78
- try:
79
- openai_client = OpenAI(api_key=OPENAI_API_KEY)
80
- print("Cliente OpenAI inicializado.")
81
- except Exception as e:
82
- print(f"Erro ao inicializar o cliente OpenAI: {e}. As chamadas de API serão puladas.")
83
- else:
84
- print("Chave da API OpenAI não configurada. As chamadas de API serão puladas.")
85
-
86
- """Envia um frame codificado em base64 para a API GPT-4o e retorna a análise."""
87
- print(f"Enviando imagem para análise no {GPT_MODEL}...")
88
-
89
-
90
- payload = {
91
- "model": GPT_MODEL,
92
- "messages": [
93
- {
94
- "role": "user",
95
- "content": [
96
- {
97
- "type": "text",
98
- "text": prompt
99
- },
100
- {
101
- "type": "image_url",
102
- "image_url": {
103
- "url": f"data:image/png;base64,{base64_image}"
104
- }
105
- }
106
- ]
107
- }
108
- ],
109
- "max_tokens": 100 # Ajuste conforme necessário para a resposta esperada
110
- }
111
-
112
- try:
113
- # Cria o cliente OpenAI dentro da função para garantir que use a chave mais recente
114
- # (embora seja definida globalmente, isso pode ser útil se a chave for atualizada dinamicamente no futuro)
115
- # client = OpenAI(api_key=OPENAI_API_KEY)
116
-
117
- response = openai_client.chat.completions.create(
118
- model=payload["model"],
119
- messages=payload["messages"],
120
- max_tokens=payload["max_tokens"],
121
- temperature=0
122
- )
123
-
124
- # Extrai o conte��do da resposta
125
- analysis_result = response.choices[0].message.content.strip()
126
- fen = analysis_result.strip("`")
127
- fen = fen.replace("_", " ") #retorna _ no lugar de espaço em branco
128
- print(f"Análise recebida (raw): {analysis_result}")
129
- print(f"Análise tratada : {fen}")
130
- if fen != FEN_CORRETA:
131
- print(f"FEN INCORRETA ")
132
- else:
133
- print(f"FEN CORRETA ")
134
-
135
- return {"image_response": fen}
136
- except Exception as e:
137
- print(f"Erro ao chamar a API OpenAI: {e}")
138
- return {"error": str(e)}
139
-
140
-
141
- def analyze_image_with_gemini(base64_image, prompt):
142
- genai.configure(api_key=GEMINI_API_KEY)
143
- model = genai.GenerativeModel(GEMINI_MODEL)
144
-
145
- """Envia um frame codificado em base64 para a API GPT-4o e retorna a análise."""
146
- print(f"Enviando frame para análise no {GEMINI_MODEL}...")
147
-
148
-
149
- try:
150
-
151
- response = model.generate_content(
152
- contents=[
153
- {
154
- "role": "user",
155
- "parts": [
156
- {f"text": f"{prompt}"},
157
- {"inline_data": {
158
- "mime_type": "image/jpeg",
159
- "data": base64_image
160
- }}
161
- ]
162
- }
163
- ],
164
- generation_config={
165
- "temperature": 0.7,
166
- "max_output_tokens": 500
167
- })
168
-
169
- # Extrai o conteúdo da resposta
170
- analysis_result = response.text.strip()
171
- print(f"Análise recebida: {analysis_result}")
172
-
173
- return {"image_response": analysis_result}
174
-
175
- except Exception as e:
176
- print(f"Erro ao chamar a API Gemini: {e}")
177
- return {"error": str(e)}
178
-
179
-
180
- def analyze_image_with_chessvision(base64_image):
181
- base64_image_encoded = f"data:image/jpeg;base64,{base64_image}"
182
- url = CHESSVISION_TO_FEN_URL
183
- payload = {
184
- "board_orientation": "predict",
185
- "cropped": False,
186
- "current_player": "black",
187
- "image": base64_image_encoded,
188
- "predict_turn": False
189
- }
190
-
191
- response = requests.post(url, json=payload)
192
- if response.status_code == 200:
193
- dados = response.json()
194
- if dados.get("success"):
195
- print(f"Retorno Chessvision {dados}")
196
- fen = dados.get("result")
197
- fen = fen.replace("_", " ") #retorna _ no lugar de espaço em branco
198
- return fen
199
- else:
200
- raise Exception("Requisição feita, mas falhou na predição.")
201
- else:
202
- raise Exception(f"Erro na requisição: {response.status_code}")
203
-
204
- def get_best_next_move(fen: str):
205
- url = CHESS_MOVE_API
206
- payload = {
207
- "fen": fen,
208
- "depth": 1
209
- }
210
-
211
- print(f"Buscando melhor jogada em {CHESS_MOVE_API} - {payload}")
212
-
213
- response = requests.post(url, json=payload)
214
- if response.status_code == 200:
215
- #print(f"Retorno melhor jogada --> {response.text}")
216
- dados = response.json()
217
- move_algebric_notation = dados.get("san")
218
- move = dados.get("text")
219
- print(f"Melhor jogada segundo chess-api.com -> {move}")
220
-
221
- return move_algebric_notation
222
-
223
- else:
224
- raise Exception(f"Erro na requisição: {response.status_code}")
225
-
226
- def save_results_to_json(results_list, output_file):
227
- """Salva a lista de resultados da análise em um arquivo JSON."""
228
- print(f"Salvando resultados da análise em {output_file}...")
229
- try:
230
- with open(output_file, 'w', encoding='utf-8') as f:
231
- json.dump(results_list, f, ensure_ascii=False, indent=4)
232
- print(f"Resultados salvos com sucesso em: {output_file}")
233
- return True
234
- except Exception as e:
235
- print(f"Erro ao salvar os resultados em JSON: {e}")
236
- return False
237
-
238
-
239
- # --- Atualização do Bloco Principal ---
240
- # (Adicionar inicialização do cliente OpenAI e o loop de análise)
241
- if __name__ == "__main__":
242
- create_or_clear_output_directory()
243
- analysis_results_list = []
244
-
245
- print(f"\nIniciando análise da imagem {IMAGE_FILE} frames com {GEMINI_MODEL}...")
246
- # Extrai timestamp do nome do arquivo, se possível
247
- base64_image = encode_image_to_base64(IMAGE_FILE)
248
- if base64_image:
249
- # Analisa a imagem com Gemini
250
- fen = analyze_image_with_chessvision(base64_image) #analyze_image_with_gpt(base64_image, PROMPT_TEXT)
251
- move = get_best_next_move(fen)
252
- result_entry = {
253
- "image": IMAGE_FILE,
254
- "fen": fen,
255
- "move": move
256
- }
257
- analysis_results_list.append(result_entry)
258
-
259
- else:
260
- print(f"Falha ao codificar o frame {IMAGE_FILE}. Pulando análise.")
261
- analysis_results_list.append({
262
- "frame_path": IMAGE_FILE,
263
- "analysis": {"error": "Failed to encode frame to base64."}
264
- })
265
-
266
- # break # teste somente uma chamada
267
- print("\nAnálise de imagem concluída.")
268
-
269
- # Pr��xima etapa: Compilar resultados
270
- print(f"\nPróxima etapa a ser implementada: Compilação dos resultados ({len(analysis_results_list)} análises) em um relatório.")
271
-
272
-
273
- # ... (código anterior para inicialização, download, extração, análise) ...
274
-
275
- # Etapa 5: Compilar e Salvar Resultados
276
- if analysis_results_list:
277
- print(f"\nCompilando {len(analysis_results_list)} resultados da análise...")
278
- if save_results_to_json(analysis_results_list, RESULTS_FILE):
279
- print("Compilação e salvamento dos resultados concluídos.")
280
- else:
281
- print("Falha ao salvar os resultados da análise.")
282
- else:
283
- print("Nenhum resultado de análise para compilar.")
284
-
285
- print("\n--- Processo de Análise de Vídeo Concluído ---")
286
- print(f"Verifique o diretório '{OUTPUT_DIR}' para os frames extraídos (se aplicável).")
287
- print(f"Verifique o arquivo '{RESULTS_FILE}' para os resultados da análise (se aplicável).")
288
- print("Lembre-se de substituir os placeholders para URL_DO_SEU_VIDEO_AQUI e SUA_CHAVE_API_OPENAI_AQUI no script.")
289
-
290
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tool_search_script.py DELETED
@@ -1,311 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- import os
3
- import requests
4
- from tavily import TavilyClient
5
- import markdownify
6
- import wikipediaapi
7
- from datetime import datetime, timezone
8
- import urllib.parse
9
- from readability import Document
10
- from bs4 import BeautifulSoup
11
- import os
12
-
13
- # --- Configuração Inicial ---
14
- TAVILY_API_KEY = os.getenv("TAVILY_API_KEY")
15
- tavily = None # Será inicializado na função principal
16
-
17
- # Configuração da API da Wikipedia
18
- WIKI_LANG = 'en' # Linguagem da Wikipedia (atualizado para inglês)
19
- wiki_wiki = wikipediaapi.Wikipedia(
20
- language=WIKI_LANG,
21
- extract_format=wikipediaapi.ExtractFormat.HTML, # Usado apenas para page.exists()
22
- user_agent='MyCoolSearchBot/1.0 ([email protected])' # Definir um User-Agent é boa prática
23
- )
24
- MEDIAWIKI_API_URL = f"https://{WIKI_LANG}.wikipedia.org/w/api.php"
25
- HEADERS = {
26
- 'User-Agent': 'MyCoolSearchBot/1.0 ([email protected])'
27
- }
28
-
29
- # --- Funções Auxiliares ---
30
-
31
- def is_wikipedia_url(url):
32
- """Verifica se uma URL pertence ao domínio da Wikipedia."""
33
- return "wikipedia.org" in url.lower()
34
-
35
- def download_and_convert_to_md(url):
36
- """Baixa o conteúdo HTML de uma URL e converte para Markdown."""
37
- print(f"Baixando e convertendo: {url}")
38
- try:
39
- response = requests.get(url, headers=HEADERS, timeout=20)
40
- response.raise_for_status() # Verifica se houve erro no request
41
- # Tenta detectar a codificação, mas assume UTF-8 como fallback
42
- response.encoding = response.apparent_encoding or 'utf-8'
43
- html_content = response.text
44
-
45
- # Usa readability para extrair o conteúdo principal
46
- doc = Document(html_content)
47
- title = doc.short_title()
48
- cleaned_html = doc.summary()
49
-
50
- # Remove scripts e estilos restantes com BeautifulSoup (filtro extra)
51
- soup = BeautifulSoup(cleaned_html, "html.parser")
52
- for tag in soup(["script", "style", "noscript"]):
53
- tag.decompose()
54
-
55
- cleaned_html = str(soup)
56
-
57
- # Usa markdownify para converter HTML para Markdown
58
- md_content = markdownify.markdownify(
59
- cleaned_html,
60
- heading_style="ATX",
61
- strip=['script', 'style'],
62
- escape_underscores=False)
63
-
64
- #return md_content
65
- return f"# {title}\n\n" + md_content.strip()
66
- except requests.exceptions.RequestException as e:
67
- print(f"Erro ao acessar a URL {url}: {e}")
68
- return None
69
- except Exception as e:
70
- print(f"Erro ao converter HTML para Markdown da URL {url}: {e}")
71
- return None
72
-
73
- # --- Funções Específicas da Wikipedia ---
74
-
75
- def get_wikipedia_revision_info(page_title, target_date_str):
76
- """Busca o ID e timestamp da revisão mais próxima (<=) da data fornecida via API MediaWiki."""
77
- try:
78
- # Converte a data string para um objeto datetime e formata para ISO 8601 com Z (UTC)
79
- target_dt = datetime.strptime(target_date_str, '%Y-%m-%d')
80
- # Precisamos do final do dia para garantir que incluímos todas as revisões daquele dia
81
- target_dt_end_of_day = target_dt.replace(hour=23, minute=59, second=59, tzinfo=timezone.utc)
82
- target_timestamp_iso = target_dt_end_of_day.strftime('%Y-%m-%dT%H:%M:%SZ')
83
- except ValueError:
84
- print("Formato de data inválido. Use AAAA-MM-DD.")
85
- return None, None
86
-
87
- params = {
88
- "action": "query",
89
- "prop": "revisions",
90
- "titles": page_title,
91
- "rvlimit": 1,
92
- "rvdir": "older", # Busca a revisão imediatamente anterior ou igual ao timestamp
93
- "rvprop": "ids|timestamp", # Queremos o ID da revisão e o timestamp
94
- "rvstart": target_timestamp_iso, # Começa a busca a partir desta data/hora
95
- "format": "json",
96
- "formatversion": 2 # Formato JSON mais moderno e fácil de parsear
97
- }
98
-
99
- try:
100
- print(f"Consultando API MediaWiki para revisão de '{page_title}' em {target_date_str}...")
101
- response = requests.get(MEDIAWIKI_API_URL, params=params, headers=HEADERS, timeout=15)
102
- response.raise_for_status()
103
- data = response.json()
104
-
105
- # Verifica se a página foi encontrada
106
- page_data = data.get("query", {}).get("pages", [])
107
- if not page_data or page_data[0].get("missing", False):
108
- print(f"Página '{page_title}' não encontrada na API MediaWiki.")
109
- # Tenta verificar com a biblioteca wikipediaapi como fallback (pode pegar redirecionamentos)
110
- page = wiki_wiki.page(page_title)
111
- if page.exists():
112
- print(f"Página '{page_title}' existe (possivelmente redirecionada para '{page.title}'). Tentando novamente com o título canônico...")
113
- return get_wikipedia_revision_info(page.title, target_date_str) # Chama recursivamente com o título correto
114
- else:
115
- print(f"Página '{page_title}' realmente não encontrada.")
116
- return None, None
117
-
118
- # Extrai as revisões
119
- revisions = page_data[0].get("revisions", [])
120
- if not revisions:
121
- print(f"Nenhuma revisão encontrada para '{page_title}' antes ou em {target_date_str}.")
122
- # Pode acontecer se a página foi criada depois da data alvo
123
- return None, None
124
-
125
- revision = revisions[0]
126
- revid = revision.get("revid")
127
- timestamp = revision.get("timestamp")
128
- print(f"Encontrada revisão: ID={revid}, Timestamp={timestamp}")
129
- return revid, timestamp
130
-
131
- except requests.exceptions.RequestException as e:
132
- print(f"Erro ao chamar a API MediaWiki: {e}")
133
- return None, None
134
- except Exception as e:
135
- print(f"Erro ao processar resposta da API MediaWiki: {e}")
136
- return None, None
137
-
138
- def get_wikipedia_page_historical_content(page_title, target_date_str):
139
- """Obtém o conteúdo Markdown de uma revisão histórica específica da Wikipedia."""
140
- # Busca o ID da revisão correta usando a API MediaWiki
141
- revid, timestamp = get_wikipedia_revision_info(page_title, target_date_str)
142
-
143
- if not revid:
144
- print(f"Não foi possível encontrar uma revisão adequada para '{page_title}' em {target_date_str}.")
145
- return None
146
-
147
- # Constrói a URL para a revisão específica
148
- # Nota: Codifica o título da página para a URL
149
- # Precisamos garantir que estamos usando o título correto (pode ter sido redirecionado)
150
- page_check = wiki_wiki.page(page_title) # Verifica novamente para obter o título canônico se houve redirecionamento
151
- if not page_check.exists():
152
- print(f"Erro inesperado: Página '{page_title}' não encontrada após busca de revisão.")
153
- return None
154
- canonical_title = page_check.title
155
- encoded_title = urllib.parse.quote(canonical_title.replace(' ', '_'))
156
- revision_url = f"https://{WIKI_LANG}.wikipedia.org/w/index.php?title={encoded_title}&oldid={revid}"
157
-
158
- print(f"Acessando URL da revisão: {revision_url}")
159
- # Usa a função de download e conversão para obter o conteúdo em Markdown
160
- md_content = download_and_convert_to_md(revision_url)
161
-
162
- if md_content:
163
- # Adiciona informação sobre a revisão no início do conteúdo (CORRIGIDO)
164
- header = f"# Wikipedia Content for '{canonical_title}'\n"
165
- header += f"*Revision from {timestamp} (ID: {revid})*\n"
166
- header += f"*Regarding search date: {target_date_str}*\n\n"
167
- header += "---\n\n"
168
- return header + md_content
169
- else:
170
- print(f"Falha ao obter ou converter conteúdo da revisão {revid} para '{canonical_title}'.")
171
- return None
172
-
173
- # --- Função Principal de Busca ---
174
-
175
- def search_and_extract(query, use_wikipedia_priority=False, wikipedia_date=None):
176
- """Realiza a busca, filtra resultados, baixa e converte conteúdo."""
177
- global tavily
178
- if not tavily:
179
- if not TAVILY_API_KEY or TAVILY_API_KEY == "INSIRA_SUA_CHAVE_TAVILY_AQUI":
180
- print("Erro: Chave da API Tavily não configurada ou inválida.")
181
- return []
182
- try:
183
- tavily = TavilyClient(api_key=TAVILY_API_KEY)
184
- except Exception as e:
185
- print(f"Erro ao inicializar o cliente Tavily: {e}")
186
- return []
187
-
188
- results_md = []
189
-
190
- # Sempre faz a busca com Tavily primeiro
191
- print(f"\n--- Realizando busca por '{query}' usando Tavily ---")
192
- try:
193
- response = tavily.search(query=query, search_depth="basic", max_results=10)
194
- search_results = response.get('results', [])
195
- except Exception as e:
196
- print(f"Erro ao realizar busca com Tavily: {e}")
197
- return []
198
-
199
- if not search_results:
200
- print("Nenhum resultado encontrado pela busca Tavily.")
201
- return []
202
-
203
- urls_to_process = []
204
- if use_wikipedia_priority:
205
- print("Prioridade para Wikipedia habilitada. Filtrando resultados Tavily por Wikipedia...")
206
- wiki_urls = [res['url'] for res in search_results if is_wikipedia_url(res['url'])]
207
- if wiki_urls:
208
- # Pega o primeiro resultado da Wikipedia
209
- first_wiki_url = wiki_urls[0]
210
- page_title_guess = first_wiki_url.split('/')[-1].replace('_', ' ')
211
- page_check = wiki_wiki.page(page_title_guess)
212
- if page_check.exists():
213
- print(f"Encontrado resultado da Wikipedia: {first_wiki_url}")
214
- if wikipedia_date:
215
- # Busca revisão histórica
216
- md_content = get_wikipedia_page_historical_content(page_check.title, wikipedia_date)
217
- if md_content:
218
- results_md.append({
219
- "source": f"Wikipedia Revision ({page_check.title} @ {wikipedia_date})",
220
- "content": md_content
221
- })
222
- return results_md
223
- else:
224
- # Usa a URL atual
225
- urls_to_process = [first_wiki_url]
226
- else:
227
- print(f"Página da Wikipedia '{page_title_guess}' não encontrada. Usando os 5 primeiros resultados gerais.")
228
- urls_to_process = [res['url'] for res in search_results[:5]]
229
- else:
230
- print("Nenhuma URL da Wikipedia encontrada nos resultados. Usando os 5 primeiros resultados gerais.")
231
- urls_to_process = [res['url'] for res in search_results[:5]]
232
- else:
233
- print("Usando os 5 primeiros resultados gerais.")
234
- urls_to_process = [res['url'] for res in search_results[:5]]
235
-
236
- print(f"\n--- Processando {len(urls_to_process)} URLs selecionadas ---")
237
- for url in urls_to_process:
238
- md_content = download_and_convert_to_md(url)
239
- if md_content:
240
- results_md.append({
241
- "source": url,
242
- "content": md_content
243
- })
244
- else:
245
- print(f"Falha ao processar URL: {url}")
246
-
247
- return results_md
248
-
249
-
250
- # --- Exemplo de Uso ---
251
- if __name__ == "__main__":
252
- # Verifica se a chave foi definida
253
- if not TAVILY_API_KEY or TAVILY_API_KEY == "INSIRA_SUA_CHAVE_TAVILY_AQUI":
254
- print("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
255
- print("!!! Chave da API Tavily não encontrada ou não definida. !!!")
256
- print("!!! Verifique a variável TAVILY_API_KEY no início do script. !!!")
257
- print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n")
258
- # exit() # Descomente para parar a execução se a chave não estiver presente
259
-
260
- # Exemplo 1: Busca geral pelos 5 primeiros resultados (em inglês)
261
- print("\n================ Example 1: General Search ================")
262
- # Usando um tópico mais provável de ter bons resultados em inglês
263
- if False:
264
- resultados_gerais = search_and_extract("History of Artificial Intelligence")
265
- for i, res in enumerate(resultados_gerais):
266
- print(f"\nGeneral Result {i+1}: {res['source']}")
267
- filename = f"general_result_{i+1}.md"
268
- try:
269
- with open(filename, "w", encoding="utf-8") as f:
270
- f.write(f"# Source: {res['source']}\n\n")
271
- f.write(res['content'])
272
- print(f"Content saved to {filename}")
273
- except Exception as e:
274
- print(f"Error saving {filename}: {e}")
275
- # print(res['content'][:300] + "...") # Mostra um trecho
276
-
277
- # Exemplo 2: Busca com prioridade para Wikipedia (em inglês)
278
- print("\n============= Example 2: Wikipedia Priority ============")
279
- resultados_wiki = search_and_extract("studio albums published by Mercedes Sosa between.", use_wikipedia_priority=True, wikipedia_date='2022-12-31')
280
- for i, res in enumerate(resultados_wiki):
281
- print(f"\nWiki Priority Result {i+1}: {res['source']}")
282
- filename = f"wiki_priority_result_{i+1}.md"
283
- try:
284
- with open(filename, "w", encoding="utf-8") as f:
285
- f.write(f"# Source: {res['source']}\n\n")
286
- f.write(res['content'])
287
- print(f"Content saved to {filename}")
288
- except Exception as e:
289
- print(f"Error saving {filename}: {e}")
290
- # print(res['content'][:300] + "...")
291
-
292
- # Exemplo 3: Busca por revisão histórica da Wikipedia (em inglês)
293
- print("\n========== Example 3: Historical Wikipedia Revision ==========")
294
- # Nota: O título da página deve ser o mais exato possível.
295
- # Vamos buscar uma revisão do artigo 'Python (programming language)' perto de 15 Jan 2010
296
- if False:
297
- resultados_hist = search_and_extract("Python (programming language)", use_wikipedia_priority=True, wikipedia_date="2010-01-15")
298
- for i, res in enumerate(resultados_hist):
299
- print(f"\nHistorical Result {i+1}: {res['source']}")
300
- filename = f"historical_result_python_2010.md"
301
- try:
302
- with open(filename, "w", encoding="utf-8") as f:
303
- # O cabeçalho já está incluído pela função get_wikipedia_page_historical_content
304
- f.write(res['content'])
305
- print(f"Content saved to {filename}")
306
- except Exception as e:
307
- print(f"Error saving {filename}: {e}")
308
- # print(res['content'][:300] + "...")
309
-
310
- print("\nScript finished.")
311
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tool_video_analyzer.py DELETED
@@ -1,439 +0,0 @@
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
- from openai import OpenAI # Importa a classe OpenAI
12
- import json
13
- import re
14
- import shutil
15
-
16
- import google.generativeai as genai
17
-
18
-
19
- # --- Configurações (Substitua os placeholders) ---
20
- VIDEO_URL = "https://www.youtube.com/watch?v=L1vXCYZAYYM" # Substitua pela URL do vídeo do YouTube
21
- OUTPUT_DIR = "./video_analysis_output" # Diretório para salvar o vídeo e os frames
22
- FRAME_INTERVAL_SECONDS = 0.5 # Intervalo entre frames a serem extraídos
23
- INICIO_FRAME_IMPORTANTE = 191 # inicio intervalo relevante, para não ficar caro a inferencia ao gpt
24
- FIM_FRAME_IMPORTANTE = 193# fim intervalo relevante, para não ficar caro a inferencia ao gpt
25
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
26
- GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
27
- GPT_MODEL = "gpt-4o" # Modelo GPT a ser usado (certifique-se que é o correto para análise de imagem)
28
- GEMINI_MODEL = "gemini-2.0-flash"
29
- #PROMPT_TEXT = "You are an image analyzer, do not return any explanation. If asked to count items, return only an integer. If in doubt, return 0. How many different bird species are visible in the image?" # Prompt para o GPT-4o
30
- #PROMPT_TEXT = "You are an expert in visual species classification. Based on the image provided, determine and return the number of distinct bird species visible. Do not count individuals — only count different species based on visual traits like size, shape, color, and beak structure. Return only a single integer. If unsure, return your best estimate. Do not provide explanations or any extra text."
31
- #PROMPT_TEXT = "You are an expert in visual species classification. Based on the image provided, determine and return the number of distinct bird species visible. Do not count individuals — only count different species based on visual traits like size, shape, color, and beak structure. Return only a single integer. If unsure, return your best estimate. Do not provide explanations or any extra text."
32
- PROMPT_TEXT = "You are a world-class expert in avian species classification. Analyze the provided image and determine how many **distinct bird species** are present. Consider size, shape, plumage, coloration, and beak structure. Focus only on visible morphological differences. Return a **single integer** with no explanation. Do not count individuals of the same species. If unsure, assume that bird is a different specie."
33
-
34
- RESULTS_FILE = os.path.join(OUTPUT_DIR, "analysis_results.json")
35
- VIDEO_FILENAME = "downloaded_video.mp4"
36
- VIDEO_PATH = os.path.join(OUTPUT_DIR, VIDEO_FILENAME)
37
-
38
- # Verifica se a chave da API foi definida
39
- if OPENAI_API_KEY == "SUA_CHAVE_API_OPENAI_AQUI":
40
- print("AVISO: A chave da API OpenAI não foi definida. Por favor, edite o script e insira sua chave.")
41
- # Considerar sair do script ou lançar um erro se a chave for essencial para a execução completa
42
- # exit(1)
43
-
44
- if GEMINI_API_KEY == "SUA_CHAVE_API_OPENAI_AQUI" or not GEMINI_API_KEY or len(GEMINI_API_KEY) ==0 :
45
- print("AVISO: A chave da API GEMINI não foi definida. Por favor, edite o script e insira sua chave.")
46
- # Considerar sair do script ou lançar um erro se a chave for essencial para a execução completa
47
- # exit(1)
48
-
49
- # Verifica se a URL foi definida
50
- if VIDEO_URL == "URL_DO_SEU_VIDEO_AQUI":
51
- print("AVISO: A URL do vídeo não foi definida. Por favor, edite o script e insira a URL desejada.")
52
- # exit(1)
53
-
54
- # --- Funções ---
55
-
56
- def create_or_clear_output_directory():
57
- """Cria o diretório de saída se não existir."""
58
- if not os.path.exists(OUTPUT_DIR):
59
- os.makedirs(OUTPUT_DIR)
60
- print(f"Diretório criado: {OUTPUT_DIR}")
61
- else:
62
- # Limpa todos os arquivos e subdiretórios
63
- for filename in os.listdir(OUTPUT_DIR):
64
- file_path = os.path.join(OUTPUT_DIR, filename)
65
- try:
66
- if os.path.isfile(file_path) or os.path.islink(file_path):
67
- os.unlink(file_path)
68
- elif os.path.isdir(file_path):
69
- shutil.rmtree(file_path)
70
- except Exception as e:
71
- print(f"Erro ao excluir {file_path}: {e}")
72
- print(f"Diretório limpo: {OUTPUT_DIR}")
73
-
74
- def retirar_sufixo_codec_arquivo(directory) -> None:
75
- for filename in os.listdir(directory):
76
- # Procura padrão como ".f123" antes da extensão
77
- new_filename = re.sub(r'\.f\d{3}(?=\.\w+$)', '', filename)
78
- if new_filename != filename:
79
- old_path = os.path.join(directory, filename)
80
- new_path = os.path.join(directory, new_filename)
81
- os.rename(old_path, new_path)
82
- print(f"Renomeado: {filename} → {new_filename}")
83
-
84
-
85
- def download_video(url, output_path):
86
- """Baixa o vídeo do YouTube usando yt-dlp."""
87
- print(f"Baixando vídeo de {url} para {output_path}...")
88
- try:
89
- # Comando yt-dlp para baixar o melhor formato mp4
90
- command = [
91
- 'yt-dlp',
92
- '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
93
- '-o', output_path,
94
- url
95
- ]
96
- result = subprocess.run(command, check=True, capture_output=True, text=True)
97
- print("Download concluído com sucesso.")
98
- retirar_sufixo_codec_arquivo(OUTPUT_DIR)
99
- # print(result.stdout) # Descomente para ver a saída do yt-dlp
100
- return True
101
- except subprocess.CalledProcessError as e:
102
- print(f"Erro ao baixar o vídeo: {e}")
103
- print(f"Saída do erro: {e.stderr}")
104
- return False
105
- except FileNotFoundError:
106
- print("Erro: O comando 'yt-dlp' não foi encontrado. Certifique-se de que ele está instalado e no PATH do sistema.")
107
- print("Você pode instalá-lo com: pip install yt-dlp")
108
- return False
109
-
110
-
111
- def extract_frames(video_path, output_dir, interval_sec):
112
- """Extrai frames de um vídeo em intervalos específicos."""
113
- print(f"Extraindo frames de {video_path} a cada {interval_sec} segundos...")
114
- if not os.path.exists(video_path):
115
- print(f"Erro: Arquivo de vídeo não encontrado em {video_path}")
116
- return []
117
-
118
- cap = cv2.VideoCapture(video_path)
119
- # Verificar a resolução
120
- width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
121
- height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
122
- print(f"Resolução original do vídeo: {width}x{height}")
123
-
124
- if not cap.isOpened():
125
- print(f"Erro ao abrir o arquivo de vídeo: {video_path}")
126
- return []
127
-
128
- fps = cap.get(cv2.CAP_PROP_FPS)
129
- if fps == 0:
130
- print("Erro: Não foi possível obter o FPS do vídeo. Usando FPS padrão de 30.")
131
- fps = 30 # Valor padrão caso a leitura falhe
132
-
133
- # retirado para permitir fracionado frame_interval = int(fps * interval_sec)
134
- frame_interval = fps * interval_sec
135
-
136
- total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
137
- print(f"Vídeo FPS: {fps:.2f}, Intervalo de frames: {frame_interval}, Total de frames: {total_frames}")
138
-
139
- extracted_frames_paths = []
140
- frame_count = 0
141
- saved_frame_index = 5 # o importante nunca começa no inicio, é um deslocamento inicial para iniciar depois da introdução
142
-
143
-
144
- while True:
145
- # Define a posição do próximo frame a ser lido
146
- # Adiciona frame_interval para pegar o frame *após* o intervalo de tempo
147
- # ajustado para float target_frame_pos = saved_frame_index * frame_interval
148
- target_frame_pos = int(saved_frame_index * frame_interval)
149
-
150
- if target_frame_pos >= total_frames:
151
- break # Sai se o próximo frame alvo estiver além do final do vídeo
152
-
153
- if (saved_frame_index < INICIO_FRAME_IMPORTANTE or saved_frame_index > FIM_FRAME_IMPORTANTE):
154
- print(f"Pulando frame {saved_frame_index}")
155
- saved_frame_index += 1
156
- continue # evitar custo desnecessário para inferencia ao gpt
157
- cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame_pos)
158
- ret, frame = cap.read()
159
-
160
- if not ret:
161
- print(f"Não foi possível ler o frame na posição {target_frame_pos}. Pode ser o fim do vídeo ou um erro.")
162
- break # Sai se não conseguir ler o frame
163
-
164
- # redimensiona o frame (custo chamada)
165
- # removido porque poderia afetar a nitidez e impactar o resultado
166
- # frame = cv2.resize(frame, (1280, 720))
167
-
168
- # Calcula o timestamp em segundos
169
- timestamp_sec = target_frame_pos / fps
170
-
171
- # Salva o frame
172
- frame_filename = f"frame_{saved_frame_index:04d}_time_{timestamp_sec:.2f}s.png"
173
- frame_path = os.path.join(output_dir, frame_filename)
174
- try:
175
- # modificado para salvar com qualidade máxima cv2.imwrite(frame_path, frame)
176
- cv2.imwrite(frame_path, frame, [cv2.IMWRITE_PNG_COMPRESSION, 0])
177
-
178
- extracted_frames_paths.append(frame_path)
179
- print(f"Frame salvo: {frame_path} (Timestamp: {timestamp_sec:.2f}s)")
180
- saved_frame_index += 1
181
- except Exception as e:
182
- print(f"Erro ao salvar o frame {frame_path}: {e}")
183
- # Continua para o próximo intervalo mesmo se um frame falhar
184
-
185
- # Segurança para evitar loop infinito caso algo dê errado com a lógica de posição
186
- if saved_frame_index > (total_frames / frame_interval) + 2:
187
- print("Aviso: Número de frames salvos parece exceder o esperado. Interrompendo extração.")
188
- break
189
-
190
- cap.release()
191
- print(f"Extração de frames concluída. Total de frames salvos: {len(extracted_frames_paths)}")
192
- return extracted_frames_paths
193
-
194
- # --- Atualização do Bloco Principal ---
195
- # (Remover a linha print anterior sobre próximas etapas e adicionar a chamada da nova função)
196
-
197
-
198
-
199
-
200
- def encode_frame_to_base64(frame_path):
201
- """Codifica um arquivo de imagem (frame) para base64."""
202
- try:
203
- with open(frame_path, "rb") as image_file:
204
- return base64.b64encode(image_file.read()).decode('utf-8')
205
- except FileNotFoundError:
206
- print(f"Erro: Arquivo de frame não encontrado em {frame_path}")
207
- return None
208
- except Exception as e:
209
- print(f"Erro ao codificar o frame {frame_path} para base64: {e}")
210
- return None
211
-
212
-
213
-
214
- def analyze_frame_with_gpt4o(client, base64_image, prompt):
215
-
216
- """Envia um frame codificado em base64 para a API GPT-4o e retorna a análise."""
217
- print(f"Enviando frame para análise no {GPT_MODEL}...")
218
- headers = {
219
- "Content-Type": "application/json",
220
- "Authorization": f"Bearer {OPENAI_API_KEY}"
221
- }
222
-
223
- payload = {
224
- "model": GPT_MODEL,
225
- "messages": [
226
- {
227
- "role": "user",
228
- "content": [
229
- {
230
- "type": "text",
231
- "text": prompt
232
- },
233
- {
234
- "type": "image_url",
235
- "image_url": {
236
- "url": f"data:image/png;base64,{base64_image}"
237
- }
238
- }
239
- ]
240
- }
241
- ],
242
- "max_tokens": 100 # Ajuste conforme necessário para a resposta esperada
243
- }
244
-
245
- try:
246
- # Verifica se a chave da API é o placeholder
247
- if OPENAI_API_KEY == "SUA_CHAVE_API_OPENAI_AQUI":
248
- print("AVISO: Chave da API OpenAI não configurada. Pulando análise.")
249
- return {"error": "API key not configured."}
250
-
251
- # Cria o cliente OpenAI dentro da função para garantir que use a chave mais recente
252
- # (embora seja definida globalmente, isso pode ser útil se a chave for atualizada dinamicamente no futuro)
253
- # client = OpenAI(api_key=OPENAI_API_KEY)
254
-
255
- response = client.chat.completions.create(
256
- model=payload["model"],
257
- messages=payload["messages"],
258
- max_tokens=payload["max_tokens"]
259
- )
260
-
261
- # Extrai o conteúdo da resposta
262
- analysis_result = response.choices[0].message.content.strip()
263
- print(f"Análise recebida: {analysis_result}")
264
- # Tenta converter a resposta para um inteiro (contagem de aves)
265
- try:
266
- bird_count = int(analysis_result)
267
- return {"bird_count": bird_count, "raw_response": analysis_result}
268
- except ValueError:
269
- print(f"Aviso: Não foi possível converter a resposta '{analysis_result}' para um número inteiro.")
270
- return {"error": "Failed to parse bird count from response.", "raw_response": analysis_result}
271
-
272
- except Exception as e:
273
- print(f"Erro ao chamar a API OpenAI: {e}")
274
- return {"error": str(e)}
275
-
276
-
277
- def analyze_frame_with_gemini(base64_image, prompt):
278
- genai.configure(api_key=GEMINI_API_KEY)
279
- model = genai.GenerativeModel(GEMINI_MODEL)
280
-
281
- """Envia um frame codificado em base64 para a API GPT-4o e retorna a análise."""
282
- print(f"Enviando frame para análise no {GEMINI_MODEL}...")
283
-
284
- try:
285
-
286
- response = model.generate_content(
287
- contents=[
288
- {
289
- "role": "user",
290
- "parts": [
291
- {f"text": f"{prompt}"},
292
- {"inline_data": {
293
- "mime_type": "image/jpeg",
294
- "data": base64_image
295
- }}
296
- ]
297
- }
298
- ],
299
- generation_config={
300
- "temperature": 0.7,
301
- "max_output_tokens": 500
302
- })
303
-
304
- # Extrai o conteúdo da resposta
305
- analysis_result = response.text.strip()
306
- print(f"Análise recebida: {analysis_result}")
307
- # Tenta converter a resposta para um inteiro (contagem de aves)
308
- try:
309
- bird_count = int(analysis_result)
310
- return {"bird_count": bird_count, "raw_response": analysis_result}
311
- except ValueError:
312
- print(f"Aviso: Não foi possível converter a resposta '{analysis_result}' para um número inteiro.")
313
- return {"error": "Failed to parse bird count from response.", "raw_response": analysis_result}
314
-
315
- except Exception as e:
316
- print(f"Erro ao chamar a API Gemini: {e}")
317
- return {"error": str(e)}
318
-
319
-
320
-
321
-
322
- def save_results_to_json(results_list, output_file):
323
- """Salva a lista de resultados da análise em um arquivo JSON."""
324
- print(f"Salvando resultados da análise em {output_file}...")
325
- try:
326
- with open(output_file, 'w', encoding='utf-8') as f:
327
- json.dump(results_list, f, ensure_ascii=False, indent=4)
328
- print(f"Resultados salvos com sucesso em: {output_file}")
329
- return True
330
- except Exception as e:
331
- print(f"Erro ao salvar os resultados em JSON: {e}")
332
- return False
333
-
334
-
335
- # --- Atualização do Bloco Principal ---
336
- # (Adicionar inicialização do cliente OpenAI e o loop de análise)
337
- if __name__ == "__main__":
338
- create_or_clear_output_directory()
339
- extracted_frames = []
340
- analysis_results_list = []
341
-
342
- # Inicializa o cliente OpenAI (se a chave estiver definida)
343
- openai_client = None
344
- if OPENAI_API_KEY != "SUA_CHAVE_API_OPENAI_AQUI":
345
- try:
346
- openai_client = OpenAI(api_key=OPENAI_API_KEY)
347
- print("Cliente OpenAI inicializado.")
348
- except Exception as e:
349
- print(f"Erro ao inicializar o cliente OpenAI: {e}. As chamadas de API serão puladas.")
350
- else:
351
- print("Chave da API OpenAI não configurada. As chamadas de API serão puladas.")
352
-
353
- # Etapa 1: Baixar o vídeo
354
- video_downloaded_or_exists = False
355
- if VIDEO_URL != "URL_DO_SEU_VIDEO_AQUI":
356
- if download_video(VIDEO_URL, VIDEO_PATH):
357
- print(f"Vídeo salvo em: {VIDEO_PATH}")
358
- video_downloaded_or_exists = True
359
- else:
360
- print("Falha no download do vídeo. Pulando etapas dependentes.")
361
- elif os.path.exists(VIDEO_PATH):
362
- print(f"URL não fornecida, mas vídeo encontrado em {VIDEO_PATH}. Tentando processar.")
363
- video_downloaded_or_exists = True
364
- else:
365
- print("URL do vídeo não fornecida e vídeo local não encontrado. Pulando download e extração.")
366
-
367
- # Etapa 2: Extrair frames
368
- if video_downloaded_or_exists:
369
- extracted_frames = extract_frames(VIDEO_PATH, OUTPUT_DIR, FRAME_INTERVAL_SECONDS)
370
- else:
371
- print("Pulando extração de frames pois o vídeo não está disponível.")
372
-
373
- # Etapa 3 e 4: Codificar e Analisar Frames
374
- if extracted_frames and openai_client:
375
- print(f"\nIniciando análise de {len(extracted_frames)} frames com {GPT_MODEL}...")
376
- for frame_path in extracted_frames:
377
- print(f"\nProcessando frame: {frame_path}")
378
- # Extrai timestamp do nome do arquivo, se possível
379
- timestamp_str = "unknown"
380
- try:
381
- # Exemplo: frame_0000_time_0.00s.png
382
- parts = os.path.basename(frame_path).split('_')
383
- if len(parts) >= 4 and parts[2] == 'time':
384
- timestamp_str = parts[3].replace('s.png','')
385
- except Exception:
386
- pass # Mantém 'unknown' se o parsing falhar
387
-
388
- # Codifica o frame
389
- base64_image = encode_frame_to_base64(frame_path)
390
-
391
- if base64_image:
392
- # Analisa o frame com GPT-4o ou Gemini
393
- analysis_result = analyze_frame_with_gemini(base64_image, PROMPT_TEXT) #analyze_frame_with_gpt4o(openai_client, base64_image, PROMPT_TEXT)
394
- result_entry = {
395
- "frame_path": frame_path,
396
- "timestamp_approx_sec": timestamp_str,
397
- "analysis": analysis_result
398
- }
399
- analysis_results_list.append(result_entry)
400
-
401
- # Pausa opcional para evitar rate limiting
402
- time.sleep(1) # Pausa de 1 segundo entre as chamadas
403
- else:
404
- print(f"Falha ao codificar o frame {frame_path}. Pulando análise.")
405
- analysis_results_list.append({
406
- "frame_path": frame_path,
407
- "timestamp_approx_sec": timestamp_str,
408
- "analysis": {"error": "Failed to encode frame to base64."}
409
- })
410
-
411
- # break # teste somente uma chamada
412
- print("\nAnálise de todos os frames concluída.")
413
- elif not extracted_frames:
414
- print("Nenhum frame foi extraído. Pulando etapa de análise.")
415
- elif not openai_client:
416
- print("Cliente OpenAI não inicializado (verifique a API Key). Pulando etapa de análise.")
417
-
418
- # Próxima etapa: Compilar resultados
419
- print(f"\nPróxima etapa a ser implementada: Compilação dos resultados ({len(analysis_results_list)} análises) em um relatório.")
420
-
421
-
422
- # ... (código anterior para inicialização, download, extração, análise) ...
423
-
424
- # Etapa 5: Compilar e Salvar Resultados
425
- if analysis_results_list:
426
- print(f"\nCompilando {len(analysis_results_list)} resultados da análise...")
427
- if save_results_to_json(analysis_results_list, RESULTS_FILE):
428
- print("Compilação e salvamento dos resultados concluídos.")
429
- else:
430
- print("Falha ao salvar os resultados da análise.")
431
- else:
432
- print("Nenhum resultado de análise para compilar.")
433
-
434
- print("\n--- Processo de Análise de Vídeo Concluído ---")
435
- print(f"Verifique o diretório '{OUTPUT_DIR}' para os frames extraídos (se aplicável).")
436
- print(f"Verifique o arquivo '{RESULTS_FILE}' para os resultados da análise (se aplicável).")
437
- print("Lembre-se de substituir os placeholders para URL_DO_SEU_VIDEO_AQUI e SUA_CHAVE_API_OPENAI_AQUI no script.")
438
-
439
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
wiki_priority_result_1.md DELETED
@@ -1,176 +0,0 @@
1
- # Source: Wikipedia Revision (Mercedes Sosa @ 2022-12-31)
2
-
3
- # Wikipedia Content for 'Mercedes Sosa'
4
- *Revision from 2022-12-09T22:29:41Z (ID: 1126540422)*
5
- *Regarding search date: 2022-12-31*
6
-
7
- ---
8
-
9
- # Mercedes Sosa - Wikipedia
10
-
11
- Argentine singer (1935–2009)
12
-
13
- **Haydée Mercedes Sosa** ([Template:IPA-es](/w/index.php?title=Template:IPA-es&action=edit&redlink=1 "Template:IPA-es (page does not exist)"); 9 July 1935[[1]](#cite_note-birth-1) – 4 October 2009), sometimes known as ***La Negra*** (lit. 'The Black One', an affectionate nickname for people with a darker complexion in Argentina), was an Argentine singer who was popular throughout Latin America and many countries outside the region. With her roots in Argentine [folk music](/wiki/Folk_music "Folk music"), Sosa became one of the preeminent exponents of *El nuevo cancionero*. She gave voice to songs written by many Latin American songwriters. Her music made people hail her as the "voice of the voiceless ones".[[2]](#cite_note-tmc-2)
14
-
15
- Sosa performed in venues such as the [Lincoln Center](/wiki/Lincoln_Center "Lincoln Center") in New York City, the [Théâtre Mogador](/wiki/Th%C3%A9%C3%A2tre_Mogador "Théâtre Mogador") in Paris and the [Sistine Chapel](/wiki/Sistine_Chapel "Sistine Chapel") in Vatican City, as well as sell-out shows in New York's [Carnegie Hall](/wiki/Carnegie_Hall "Carnegie Hall") and the Roman [Colosseum](/wiki/Colosseum "Colosseum") during her final decade of life. Her career spanned four decades and she was the recipient of six [Latin Grammy](/wiki/Latin_Grammy "Latin Grammy") awards (2000, 2003, 2004, 2006, 2009, 2011), including a [Latin Grammy Lifetime Achievement Award](/wiki/Latin_Grammy_Lifetime_Achievement_Award "Latin Grammy Lifetime Achievement Award") in 2004 and two posthumous [Latin Grammy Award for Best Folk Album](/wiki/Latin_Grammy_Award_for_Best_Folk_Album "Latin Grammy Award for Best Folk Album") in 2009 and 2011. She won the [Premio Gardel](/wiki/Premios_Gardel "Premios Gardel") in 2000, the main musical award in Argentina. She served as an ambassador for [UNICEF](/wiki/UNICEF "UNICEF").
16
-
17
- ## Life
18
-
19
- Sosa was born on 9 July 1935, in [San Miguel de Tucumán](/wiki/San_Miguel_de_Tucum%C3%A1n "San Miguel de Tucumán"), in the northwestern Argentine province of [Tucumán](/wiki/Tucum%C3%A1n_Province "Tucumán Province"), of [mestizo](/wiki/Mestizo "Mestizo") ancestry. She was of French, Spanish and [Diaguita](/wiki/Diaguita "Diaguita") descent.[[3]](#cite_note-Legendary_folk_singer_Mercedes_Sosa_dies_at_74-3) Her parents were [Peronists](/wiki/Peronism "Peronism"), although they never registered in the party, and she started her career as a singer for the Peronist Party in Provincia Tucuman under the name Gladys Osorio.[[4]](#cite_note-4) In 1950, at age fifteen, she won a singing competition organized by a local radio station and was given a contract to perform for two months.[[5]](#cite_note-Mercedes_Sosa:_Obituary-5) She recorded her first album, *La Voz de la Zafra*, in 1959.[[5]](#cite_note-Mercedes_Sosa:_Obituary-5) A performance at the 1965 [Cosquín National Folklore Festival](/wiki/Cosqu%C3%ADn_Festival "Cosquín Festival")—where she was introduced and brought to the stage while sitting in the audience by fellow folk singer [Jorge Cafrune](/wiki/Jorge_Cafrune "Jorge Cafrune")—[[6]](#cite_note-test-6) brought her to the attention of the Argentine public.[[5]](#cite_note-Mercedes_Sosa:_Obituary-5)
20
-
21
- [![](//upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Mercedes_Sosa%2C_F%C3%A9lix_Luna_y_Ariel_Ram%C3%ADrez.jpg/250px-Mercedes_Sosa%2C_F%C3%A9lix_Luna_y_Ariel_Ram%C3%ADrez.jpg)](/wiki/File:Mercedes_Sosa,_F%C3%A9lix_Luna_y_Ariel_Ram%C3%ADrez.jpg)
22
-
23
- Sosa with [Félix Luna](/wiki/F%C3%A9lix_Luna "Félix Luna") and [Ariel Ramírez](/wiki/Ariel_Ram%C3%ADrez "Ariel Ramírez") (at the piano)
24
-
25
- Sosa and her first husband, Manuel Oscar Matus, with whom she had one son, were key players in the mid-60s *[nueva canción](/wiki/Nueva_canci%C3%B3n "Nueva canción")* movement (which was called *nuevo cancionero* in Argentina).[[7]](#cite_note-Latin_artist_Mercedes_Sosa_dies-7) Her second record was *Canciones con Fundamento*, a collection of Argentine folk songs.
26
-
27
- In 1967, Sosa toured the United States and Europe with great success. In later years, she performed and recorded extensively, broadening her repertoire to include material from throughout Latin America.
28
-
29
- In the early 1970s, Sosa released two concept albums in collaboration with composer [Ariel Ramírez](/wiki/Ariel_Ram%C3%ADrez "Ariel Ramírez") and lyricist [Félix Luna](/wiki/F%C3%A9lix_Luna "Félix Luna"): *Cantata Sudamericana* and *Mujeres Argentinas* (Argentine Women). She also recorded a tribute to Chilean musician [Violeta Parra](/wiki/Violeta_Parra "Violeta Parra") in 1971, including what was to become one of Sosa's signature songs, *[Gracias a la Vida](/wiki/Gracias_a_la_Vida "Gracias a la Vida")*.[[3]](#cite_note-Legendary_folk_singer_Mercedes_Sosa_dies_at_74-3)[[8]](#cite_note-ap-8) She also increased the popularity of songs written by [Milton Nascimento](/wiki/Milton_Nascimento "Milton Nascimento") of Brazil and [Pablo Milanés](/wiki/Pablo_Milan%C3%A9s "Pablo Milanés") and [Silvio Rodríguez](/wiki/Silvio_Rodr%C3%ADguez "Silvio Rodríguez") both from [Cuba](/wiki/Cuba "Cuba").[[3]](#cite_note-Legendary_folk_singer_Mercedes_Sosa_dies_at_74-3)
30
-
31
- [![](//upload.wikimedia.org/wikipedia/commons/thumb/6/65/Mercedes_Sosa_2.jpg/250px-Mercedes_Sosa_2.jpg)](/wiki/File:Mercedes_Sosa_2.jpg)
32
-
33
- Sosa in 1972
34
-
35
- After the [military junta](/wiki/National_Reorganization_Process "National Reorganization Process") of [Jorge Videla](/wiki/Jorge_Videla "Jorge Videla") came to power in 1976, the atmosphere in Argentina grew increasingly oppressive. Sosa faced death threats against both her and her family, but refused for many years to leave the country. At a concert in [La Plata](/wiki/La_Plata "La Plata") in 1979, Sosa was searched and arrested on stage, along with all those attending the concert.[[7]](#cite_note-Latin_artist_Mercedes_Sosa_dies-7) Their release came about through international intervention.[[5]](#cite_note-Mercedes_Sosa:_Obituary-5) Banned in her own country, she moved to Paris and then to [Madrid](/wiki/Madrid "Madrid").[[5]](#cite_note-Mercedes_Sosa:_Obituary-5)[[7]](#cite_note-Latin_artist_Mercedes_Sosa_dies-7)
36
-
37
- Sosa returned to Argentina from her exile in Europe in 1982,[[7]](#cite_note-Latin_artist_Mercedes_Sosa_dies-7) several months before the military regime collapsed as a result of the [Falklands War](/wiki/Falklands_War "Falklands War"), and gave a series of concerts at the *[Teatro Ópera](/wiki/Teatro_Opera "Teatro Opera")* in Buenos Aires, where she invited many of her younger colleagues to share the stage. A double album of recordings from these performances became an instant best seller. In subsequent years, Sosa continued to tour both in Argentina and abroad, performing in such venues as the [Lincoln Center](/wiki/Lincoln_Center "Lincoln Center") in New York City and the *[Théâtre Mogador](/wiki/Th%C3%A9%C3%A2tre_Mogador "Théâtre Mogador")* in Paris. By then, she was already called America's voice. In poor health for much of the 1990s, she performed a comeback show in Argentina in 1998.[[5]](#cite_note-Mercedes_Sosa:_Obituary-5) In 1994, she played the [Sistine Chapel](/wiki/Sistine_Chapel "Sistine Chapel") in Vatican City.[[3]](#cite_note-Legendary_folk_singer_Mercedes_Sosa_dies_at_74-3) In 2002, she sold out both [Carnegie Hall](/wiki/Carnegie_Hall "Carnegie Hall") in New York and the [Colosseum](/wiki/Colosseum "Colosseum") in Rome in the same year.[[3]](#cite_note-Legendary_folk_singer_Mercedes_Sosa_dies_at_74-3)
38
-
39
- [![](//upload.wikimedia.org/wikipedia/commons/thumb/a/a3/Mercedes_Sosa.jpg/250px-Mercedes_Sosa.jpg)](/wiki/File:Mercedes_Sosa.jpg)
40
-
41
- Sosa in 1973
42
-
43
- A supporter of [Perón](/wiki/Juan_Per%C3%B3n "Juan Perón"), she favored leftist causes throughout her life. She opposed President [Carlos Menem](/wiki/Carlos_Menem "Carlos Menem"), who was in office from 1989 to 1999, and supported the election of [Néstor Kirchner](/wiki/N%C3%A9stor_Kirchner "Néstor Kirchner"), who became president in 2003.[[9]](#cite_note-9)
44
- Sosa was a [UNESCO Goodwill Ambassador](/wiki/UNESCO_Goodwill_Ambassador "UNESCO Goodwill Ambassador") for Latin America and the Caribbean.[[7]](#cite_note-Latin_artist_Mercedes_Sosa_dies-7)[[10]](#cite_note-10)
45
-
46
- In a career spanning four decades, she worked with performers across several genres and generations, folk, opera, pop, rock, including [Martha Argerich](/wiki/Martha_Argerich "Martha Argerich"), [Andrea Bocelli](/wiki/Andrea_Bocelli "Andrea Bocelli"), [David Broza](/wiki/David_Broza "David Broza"), [Franco Battiato](/wiki/Franco_Battiato "Franco Battiato"), [Jaime Roos](/wiki/Jaime_Roos "Jaime Roos"), [Joan Baez](/wiki/Joan_Baez "Joan Baez"), [Francis Cabrel](/wiki/Francis_Cabrel "Francis Cabrel"), [Gal Costa](/wiki/Gal_Costa "Gal Costa"), [Luz Casal](/wiki/Luz_Casal "Luz Casal"), [Lila Downs](/wiki/Lila_Downs "Lila Downs"), [Lucio Dalla](/wiki/Lucio_Dalla "Lucio Dalla"), [Maria Farantouri](/wiki/Maria_Farantouri "Maria Farantouri"), [Lucecita Benitez](/wiki/Lucecita_Benitez "Lucecita Benitez"), [Nilda Fernández](/wiki/Nilda_Fern%C3%A1ndez "Nilda Fernández"), [Charly Garcia](/wiki/Charly_Garcia "Charly Garcia"), [León Gieco](/wiki/Le%C3%B3n_Gieco "León Gieco"), [Gian Marco](/wiki/Gian_Marco "Gian Marco"), [Nana Mouskouri](/wiki/Nana_Mouskouri "Nana Mouskouri"), [Pablo Milanés](/wiki/Pablo_Milan%C3%A9s "Pablo Milanés"), [Holly Near](/wiki/Holly_Near "Holly Near"), [Milton Nascimento](/wiki/Milton_Nascimento "Milton Nascimento"), [Pata Negra](/wiki/Pata_Negra "Pata Negra"), [Fito Páez](/wiki/Fito_P%C3%A1ez "Fito Páez"), [Franco De Vita](/wiki/Franco_De_Vita "Franco De Vita"), [Lourdes Pérez](/wiki/Lourdes_P%C3%A9rez "Lourdes Pérez"), [Luciano Pavarotti](/wiki/Luciano_Pavarotti "Luciano Pavarotti"), [Silvio Rodríguez](/wiki/Silvio_Rodr%C3%ADguez "Silvio Rodríguez"), [Ismael Serrano](/wiki/Ismael_Serrano "Ismael Serrano"), [Shakira](/wiki/Shakira "Shakira"), [Sting](/wiki/Sting_(musician) "Sting (musician)"), [Caetano Veloso](/wiki/Caetano_Veloso "Caetano Veloso"),[[3]](#cite_note-Legendary_folk_singer_Mercedes_Sosa_dies_at_74-3) [Julieta Venegas](/wiki/Julieta_Venegas "Julieta Venegas"), [Gustavo Cerati](/wiki/Gustavo_Cerati "Gustavo Cerati") and [Konstantin Wecker](/wiki/Konstantin_Wecker "Konstantin Wecker")[[7]](#cite_note-Latin_artist_Mercedes_Sosa_dies-7)
47
-
48
- Sosa participated in a 1999 production of [Ariel Ramírez](/wiki/Ariel_Ram%C3%ADrez "Ariel Ramírez")'s *Misa Criolla*.[[11]](#cite_note-11) Her song *Balderrama* is featured in the 2008 movie *[Che](/wiki/Che_(2008_film) "Che (2008 film)")*, starring [Benicio del Toro](/wiki/Benicio_del_Toro "Benicio del Toro") as the Argentine [Marxist](/wiki/Marxist "Marxist") revolutionary [Che Guevara](/wiki/Che_Guevara "Che Guevara").[[12]](#cite_note-12)
49
-
50
- Sosa was the former co-chair of the [Earth Charter](/wiki/Earth_Charter "Earth Charter") International Commission.
51
-
52
- ## Awards
53
-
54
- She won the Latin Grammy Award for Best Folk Album in 2000 (*Misa Criolla*),[[13]](#cite_note-13) 2003 (*Acústico*),[[14]](#cite_note-14) 2006 (*Corazón Libre*),[[15]](#cite_note-15) 2009 (*[Cantora 1](/wiki/Cantora,_un_Viaje_%C3%8Dntimo "Cantora, un Viaje Íntimo")*, which also won [Best Recording Package](/wiki/Latin_Grammy_Award_for_Best_Recording_Package "Latin Grammy Award for Best Recording Package") and was nominated for [Album of the Year](/wiki/Latin_Grammy_Award_for_Album_of_the_Year "Latin Grammy Award for Album of the Year")),[[16]](#cite_note-16) and 2011 (*Deja La Vida Volar*),[[17]](#cite_note-17) as well as several international awards.
55
-
56
- In 1995, [Konex Foundation](/wiki/Konex_Foundation "Konex Foundation") from Argentina granted her the Diamond [Konex Award](/wiki/Konex_Award "Konex Award"), one of the most prestigious awards in Argentina, as the most important personality in the Popular Music of her country in the last decade.[[18]](#cite_note-18)
57
-
58
- ## Death
59
-
60
- [![](//upload.wikimedia.org/wikipedia/commons/thumb/5/56/Funeral_de_Mercedes_Sosa.jpg/250px-Funeral_de_Mercedes_Sosa.jpg)](/wiki/File:Funeral_de_Mercedes_Sosa.jpg)
61
-
62
- Mercedes Sosa lying in repose, with her family and President [Cristina Fernández de Kirchner](/wiki/Cristina_Fern%C3%A1ndez_de_Kirchner "Cristina Fernández de Kirchner") viewing
63
-
64
- Suffering from recurrent endocrine and respiratory problems in later years, the 74-year-old Sosa was hospitalized in Buenos Aires on 18 September 2009.[[19]](#cite_note-19) She died from [multiple organ failure](/wiki/Multiple_organ_failure "Multiple organ failure") on 4 October 2009, at 5:15 am.[[8]](#cite_note-ap-8) She is survived by one son, Fabián Matus, born of her first marriage.[[5]](#cite_note-Mercedes_Sosa:_Obituary-5)[[20]](#cite_note-Argentine_singer_Mercedes_Sosa,_'voice_of_Latin_America,'_dies_at_74-20) He said: "She lived her 74 years to the fullest. She had done practically everything she wanted, she didn't have any type of barrier or any type of fear that limited her".[[20]](#cite_note-Argentine_singer_Mercedes_Sosa,_'voice_of_Latin_America,'_dies_at_74-20) The hospital expressed its sympathies to her relatives.[[21]](#cite_note-Argentine_folk_legend_Mercedes_Sosa_dead_at_74-21) Her website featured the following: "Her undisputed talent, her honesty and her profound convictions leave a great legacy to future generations".[[22]](#cite_note-Argentine_folk_icon_Sosa_dies_at_74-22)
65
-
66
- Her body was placed on display at the [National Congress](/wiki/Argentine_National_Congress "Argentine National Congress") building in Buenos Aires for the public to pay their respects, and President Fernández de Kirchner ordered three days of national mourning.[[20]](#cite_note-Argentine_singer_Mercedes_Sosa,_'voice_of_Latin_America,'_dies_at_74-20)[[23]](#cite_note-23) Thousands had queued by the end of the day.[[22]](#cite_note-Argentine_folk_icon_Sosa_dies_at_74-22) She was cremated on 5 October.[[22]](#cite_note-Argentine_folk_icon_Sosa_dies_at_74-22)[[24]](#cite_note-Argentine_singer_Mercedes_Sosa_dies_at_74-24)
67
-
68
- Sosa's obituary in *[The Daily Telegraph](/wiki/The_Daily_Telegraph "The Daily Telegraph")* said she was "an unrivalled interpreter of works by her compatriot, the Argentine [Atahualpa Yupanqui](/wiki/Atahualpa_Yupanqui "Atahualpa Yupanqui"), and Chile's [Violeta Parra](/wiki/Violeta_Parra "Violeta Parra")".[[5]](#cite_note-Mercedes_Sosa:_Obituary-5) Helen Popper of [Reuters](/wiki/Reuters "Reuters") reported her death by saying she "fought South America's dictators with her voice and became a giant of contemporary Latin American music".[[24]](#cite_note-Argentine_singer_Mercedes_Sosa_dies_at_74-24) Sosa received three [Latin Grammy](/wiki/Latin_Grammy "Latin Grammy") nominations for her album, in 2009 . She went on to win Best Folk Album about a month after her death.[[3]](#cite_note-Legendary_folk_singer_Mercedes_Sosa_dies_at_74-3)[[7]](#cite_note-Latin_artist_Mercedes_Sosa_dies-7)
69
-
70
- ## Tribute
71
-
72
- In 2019, she was celebrated by a [Google Doodle](/wiki/Google_Doodle "Google Doodle"). The doodle was showcased in [Argentina](/wiki/Argentina "Argentina"), Chile, [Uruguay](/wiki/Uruguay "Uruguay"), [Paraguay](/wiki/Paraguay "Paraguay"), [Bolivia](/wiki/Bolivia "Bolivia"), [Peru](/wiki/Peru "Peru"), [Ecuador](/wiki/Ecuador "Ecuador"), [Cuba](/wiki/Cuba "Cuba"), [Iceland](/wiki/Iceland "Iceland"), Sweden, [Serbia](/wiki/Serbia "Serbia"), [Greece](/wiki/Greece "Greece"), [Israel](/wiki/Israel "Israel") and [Vietnam](/wiki/Vietnam "Vietnam").[[25]](#cite_note-25)
73
-
74
- ## Discography
75
-
76
- [![](//upload.wikimedia.org/wikipedia/commons/thumb/9/93/Mercedes_sosa.jpg/250px-Mercedes_sosa.jpg)](/wiki/File:Mercedes_sosa.jpg)
77
-
78
- Sosa in 2005, with Argentina's then-First Lady (later president from 2007 to 2015), Cristina Fernández de Kirchner
79
-
80
- She recorded forty albums.[[3]](#cite_note-Legendary_folk_singer_Mercedes_Sosa_dies_at_74-3)[[7]](#cite_note-Latin_artist_Mercedes_Sosa_dies-7)
81
-
82
- ### Studio albums
83
-
84
- | Year | Album details |
85
- | --- | --- |
86
- | 1962 | [La Voz De La Zafra](/wiki/La_Voz_De_La_Zafra "La Voz De La Zafra") |
87
- | 1965 | Canciones Con Fundamento |
88
- | 1966 | Hermano |
89
- | 1966 | Yo No Canto Por Cantar |
90
- | 1967 | Para Cantarle A Mi Gente |
91
- | 1968 | Con Sabor A Mercedes Sosa |
92
- | 1969 | Mujeres Argentinas |
93
- | 1970 | El Grito De La Tierra |
94
- | 1970 | Navidad Con Mercedes Sosa |
95
- | 1971 | [Homenaje a Violeta Parra](/wiki/Homenaje_a_Violeta_Parra "Homenaje a Violeta Parra") |
96
- | 1972 | Hasta La Victoria |
97
- | 1972 | Cantata Sudamericana |
98
- | 1973 | Traigo Un Pueblo En Mi Voz |
99
- | 1975 | A Que Florezca Mi Pueblo |
100
- | 1976 | En Dirección Del Viento |
101
- | 1977 | Mercedes Sosa Interpreta A Atahualpa Yupanqui |
102
- | 1979 | Serenata Para La Tierra De Uno |
103
- | 1981 | A Quien Doy / Cuando Me Acuerdo de Mi País |
104
- | 1982 | Como Un Pájaro Libre |
105
- | 1983 | Mercedes Sosa |
106
- | 1984 | ¿Será Posible El Sur? |
107
- | 1985 | Vengo A Ofrecer Mi Corazón |
108
- | 1986 | Mercedes Sosa '86 |
109
- | 1987 | Mercedes Sosa '87 |
110
- | 1993 | Sino |
111
- | 1994 | Gestos De Amor |
112
- | 1996 | Escondido En Mi País |
113
- | 1997 | Alta Fidelidad (w/[Charly García](/wiki/Charly_Garc%C3%ADa "Charly García")) |
114
- | 1998 | Al Despertar |
115
- | 1999 | Misa Criolla |
116
- | 2005 | Corazón Libre |
117
- | 2009 | [Cantora 1](/wiki/Cantora,_un_Viaje_%C3%8Dntimo "Cantora, un Viaje Íntimo") (w/various artists) |
118
- | 2009 | [Cantora 2](/wiki/Cantora,_un_Viaje_%C3%8Dntimo "Cantora, un Viaje Íntimo") (w/various artists) |
119
- | 2011 | Censurada |
120
- | 2015 | Lucerito |
121
-
122
- ### EPs
123
-
124
- | Year | EP details |
125
- | --- | --- |
126
- | 1975 | Niño De Mañana |
127
-
128
- ### Live albums
129
-
130
- | Year | Album details |
131
- | --- | --- |
132
- | 1973 | Si Se Calla El Cantor (with Gloria Martin) |
133
- | 1980 | Gravado Ao Vivo No Brasil |
134
- | 1982 | [Mercedes Sosa en Argentina](/wiki/Mercedes_Sosa_en_Argentina "Mercedes Sosa en Argentina") |
135
- | 1985 | Corazón Americano (with [Milton Nascimento](/wiki/Milton_Nascimento "Milton Nascimento") & [León Gieco](/wiki/Le%C3%B3n_Gieco "León Gieco")) |
136
- | 1989 | Live in Europe * Label: Tropical Music/Polygram Argentina |
137
- | 1991 | De Mí |
138
- | 2002 | Acústico En Vivo * Label: Sony Music Argentina |
139
- | 2003 | Argentina Quiere Cantar (with [Víctor Heredia](/wiki/V%C3%ADctor_Heredia "Víctor Heredia") & [León Gieco](/wiki/Le%C3%B3n_Gieco "León Gieco")) |
140
- | 2010 | Deja La Vida Volar (En Gira) |
141
- | 2014 | Angel |
142
-
143
- ### Compilation albums
144
-
145
- | Year | Album details |
146
- | --- | --- |
147
- | 1975 | Disco De Oro |
148
- | 1983 | Recital |
149
- | 1988 | Amigos Míos |
150
- | 1993 | 30 Años * Label: Polygram Argentina |
151
- | 1995 | Oro |
152
- | 1997 | The Best Of Mercedes Sosa |
153
- | 2013 | Siempre En Ti |
154
-
155
- ## Filmography
156
-
157
- * *Güemes, la tierra en armas* (1971)
158
- * *Argentinísima* (1972)
159
- * *Esta es mi Argentina* (1974)
160
- * *Mercedes Sosa, como un pájaro libre* (1983)
161
- * *Será possible el sur: Mercedes Sosa* (1985)
162
- * *Historias de Argentina en vivo* (2001)
163
-
164
- ## Biographies
165
-
166
- Mercedes Sosa, La Negra by Rodolfo Braceli (Spanish only)
167
-
168
- Mercedes Sosa, La Mami by Fabián Matus (Spanish only)
169
-
170
- Mercedes Sosa, The Voice of Hope by Anette Christensen (translated into Spanish and Portuguese)
171
-
172
- Mercedes Sosa, More than a Song by Anette Christensen (translated into Spanish, French, Italian, Lithuanian and Portuguese)
173
-
174
- ## References
175
-
176
- ## External links
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
xadrez.py DELETED
File without changes