att base de dados
Deploy / Deploy (push) Successful in 3m56s Details

This commit is contained in:
JotaChina 2026-03-28 12:50:52 -03:00
parent 97d0b4432e
commit b533873e69
23 changed files with 4683725 additions and 1 deletions

1
.gitignore vendored
View File

@ -10,6 +10,5 @@ venv
.gitignore
.git
.gitea
scripts
src
assets/js/creations/server/data

4651326
scripts/classificacao/all.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,631 @@
import os
import sys
import multiprocessing
import concurrent.futures # NOVO: Necessário para usar o ProcessPoolExecutor
import csv
import time
import glob
from datetime import datetime, timedelta
# ================= 1. LIMITADOR DE CPU DINÂMICO =================
def configurar_limites_cpu():
try:
total_cores = multiprocessing.cpu_count()
print("--- Configuração de Hardware ---")
print(f"Núcleos Totais: {total_cores}")
print("Núcleos Alocados: 1 thread por processo (Multiprocessing Real)")
print("Núcleos Reservados para o Sistema: 1")
# IMPORTANTE: Em processamento paralelo real (ProcessPoolExecutor),
# as libs em C++ devem usar apenas "1" thread para evitar colisão na CPU (oversubscription)
os.environ["OMP_NUM_THREADS"] = "1"
os.environ["MKL_NUM_THREADS"] = "1"
os.environ["TF_NUM_INTRAOP_THREADS"] = "1"
os.environ["TF_NUM_INTEROP_THREADS"] = "1"
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
except Exception as e:
print(f"Aviso: Não foi possível limitar CPU automaticamente: {e}")
configurar_limites_cpu()
# ================= IMPORTS =================
import json
import yaml
import logging
import gc
import numpy as np
import psutil
import essentia
import essentia.standard as es
from tqdm import tqdm
import unicodedata
# Desativa logs internos do Essentia
essentia.log.info.active = False
essentia.log.warning.active = False
# ================= CONFIGURAÇÕES =================
BASE_DIR = "/var/www/html/trens/src_mmpSearch"
PASTA_WAVS = os.path.join(BASE_DIR, "wav")
PASTA_LOTES_YAML = os.path.join(BASE_DIR, "lotes_yaml")
# --- SISTEMA DE DOIS MODELOS ---
MODELO_EMBEDDING = "discogs-effnet-bs64-1.pb"
MODELO_CLASSIFIER = "genre_discogs400.pb"
MODELO_CLASSES = "genre_discogs400.json"
# Saídas
DIR_SAIDA = os.path.join(BASE_DIR, "saida_analises")
os.makedirs(DIR_SAIDA, exist_ok=True)
ARQUIVO_CHECKPOINT = os.path.join(DIR_SAIDA, "checkpoint_analises.jsonl")
ARQUIVO_FINAL_JSON = os.path.join(DIR_SAIDA, "db_final_completo.json")
ARQUIVO_AUDITORIA = os.path.join(DIR_SAIDA, "audit_performance.csv")
# Limites de Segurança
LIMITE_RAM_MINIMA_MB = 500
MAX_DURACAO_ANALISE = 20 * 60
# Logs
LOG_DIR = os.path.join(BASE_DIR, "logs/classificacao")
os.makedirs(LOG_DIR, exist_ok=True)
TIMESTAMP = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
logging.basicConfig(
filename=os.path.join(LOG_DIR, f"log_{TIMESTAMP}.log"),
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
)
DATABASE_YAML = {}
# Variáveis globais para os Workers conseguirem enxergar os modelos em memória
GLOBAL_EMBEDDING = None
GLOBAL_CLASSIFIER = None
GLOBAL_CLASSES = None
# ================= CLASSE DE AUDITORIA OTIMIZADA =================
class Auditoria:
def __init__(self, arquivo_csv):
self.arquivo_csv = arquivo_csv
self.inicio_global = time.time()
self.process = psutil.Process(os.getpid())
# Mantém o arquivo aberto em modo 'append' na memória (Resolve Gargalo HD)
cabecalho = not os.path.exists(self.arquivo_csv)
self.file = open(self.arquivo_csv, "a", newline="", encoding="utf-8")
self.writer = csv.writer(self.file)
if cabecalho:
self.writer.writerow(
[
"timestamp",
"arquivo",
"tamanho_mb",
"duracao_audio_s",
"tempo_proc_s",
"ram_uso_mb",
"cpu_percent",
]
)
self.file.flush()
def registrar_processamento(self, nome_arquivo, tamanho_bytes, duracao_audio, tempo_gasto):
try:
ram_mb = self.process.memory_info().rss / (1024 * 1024)
cpu_pct = self.process.cpu_percent(interval=None)
tamanho_mb = tamanho_bytes / (1024 * 1024)
agora = datetime.now().strftime("%H:%M:%S")
# Escreve na mesma conexão de arquivo aberta
self.writer.writerow(
[
agora,
nome_arquivo,
round(tamanho_mb, 2),
round(duracao_audio, 2),
round(tempo_gasto, 4),
round(ram_mb, 2),
cpu_pct,
]
)
self.file.flush()
except Exception as e:
logging.error(f"Erro ao auditar arquivo: {e}")
def verificar_marco(self, contagem):
pass # Você pode customizar logs de progresso aqui depois se quiser
def fechar(self):
try:
self.file.close()
except:
pass
# ================= FUNÇÕES AUXILIARES =================
def verificar_memoria_segura(tamanho_arquivo_bytes):
mem = psutil.virtual_memory()
livre_mb = mem.available / (1024 * 1024)
estimativa_uso_ram = tamanho_arquivo_bytes * 15
uso_mb = estimativa_uso_ram / (1024 * 1024)
if livre_mb < LIMITE_RAM_MINIMA_MB:
print(f" [!] RAM Crítica ({livre_mb:.1f}MB livres). Forçando limpeza...")
gc.collect()
time.sleep(2)
mem = psutil.virtual_memory()
if mem.available / (1024 * 1024) < LIMITE_RAM_MINIMA_MB:
return False, "RAM insuficiente no sistema"
if uso_mb > (livre_mb * 0.8):
return (
False,
f"Arquivo muito grande para RAM atual (Est: {uso_mb:.1f}MB, Livre: {livre_mb:.1f}MB)",
)
return True, "OK"
def normalizar_chave(texto):
if not texto:
return ""
s = str(texto)
if "/" in s:
s = os.path.basename(s)
s = os.path.splitext(s)[0]
s = unicodedata.normalize("NFKD", s).encode("ASCII", "ignore").decode("ASCII")
return s.lower().replace(" ", "").replace("-", "").replace("_", "").strip()
def carregar_database_yaml():
print(f"--- Carregando Metadados de Lotes YAML em: {PASTA_LOTES_YAML} ---")
padrao = os.path.join(PASTA_LOTES_YAML, "*.yml")
arquivos_lote = sorted(glob.glob(padrao))
db = {}
if not arquivos_lote:
logging.warning(f"Nenhum lote encontrado em {PASTA_LOTES_YAML}")
return {}
total_carregados = 0
for arquivo in arquivos_lote:
try:
with open(arquivo, "r", encoding="utf-8") as f:
dados_lote = yaml.safe_load(f)
if not dados_lote:
continue
lista = dados_lote if isinstance(dados_lote, list) else [dados_lote]
for proj in lista:
if not isinstance(proj, dict):
continue
candidatos = [
proj.get("file"),
proj.get("original_title"),
proj.get("src"),
]
for k in candidatos:
if k:
chave = normalizar_chave(k)
if chave:
db[chave] = proj
total_carregados += len(lista)
print(f"-> Carregado: {os.path.basename(arquivo)} ({len(lista)} projetos)")
except Exception as e:
logging.error(f"Erro ao ler lote {arquivo}: {e}")
print(f"X Erro ao ler {os.path.basename(arquivo)}")
print(f"--- Total de Projetos no DB (Memória): {len(db)} (de {total_carregados} lidos) ---")
return db
def carregar_checkpoint():
processados = set()
dados_existentes = []
if os.path.exists(ARQUIVO_CHECKPOINT):
with open(ARQUIVO_CHECKPOINT, "r", encoding="utf-8") as f:
for linha in f:
try:
obj = json.loads(linha)
reprocessar = False
if "analise_ia" in obj and "erro" in obj["analise_ia"]:
reprocessar = True
if "analise_tecnica" in obj:
est = obj["analise_tecnica"].get("estrutura_segundos", [])
if len(est) == 2 and est[1] == 12.0:
reprocessar = True
if reprocessar:
continue
processados.add(obj["arquivo"])
dados_existentes.append(obj)
except:
pass
return processados, dados_existentes
def salvar_progresso(resultado):
with open(ARQUIVO_CHECKPOINT, "a", encoding="utf-8") as f:
json.dump(resultado, f, ensure_ascii=False)
f.write("\n")
def calcular_complexidade(projeto_yaml):
score = 0
W_INST_SYNTH, W_INST_SAMPLE = 3.0, 1.0
W_AUTOMATION, W_PATTERN, W_FX = 2.0, 0.5, 1.5
stats = {"num_automations": 0, "num_patterns": 0, "num_effects": 0, "num_synths": 0}
tracks = projeto_yaml.get("tracks", [])
for track in tracks:
instruments = track.get("instruments", [])
for inst in instruments:
nome_plugin = inst.get("plugin_name", "").lower()
nome_inst = inst.get("instrument_name", "").lower()
if "automation" in nome_inst or "automation" in nome_plugin:
score += W_AUTOMATION
stats["num_automations"] += 1
elif nome_plugin in [
"zynaddsubfx",
"monstro",
"lb302",
"watsyn",
"opyd",
"sfxr",
]:
score += W_INST_SYNTH
stats["num_synths"] += 1
else:
score += W_INST_SAMPLE
patterns = inst.get("patterns", [])
qtd_patterns = len(patterns)
score += qtd_patterns * W_PATTERN
stats["num_patterns"] += qtd_patterns
fxchain = inst.get("fxchain", {})
if str(fxchain.get("enabled")) == "1":
num_fx = int(fxchain.get("numofeffects", 0))
score += num_fx * W_FX
stats["num_effects"] += num_fx
if score <= 15:
estrelas = 1
elif score <= 40:
estrelas = 2
elif score <= 80:
estrelas = 3
elif score <= 120:
estrelas = 4
else:
estrelas = 5
return {"score_total": round(score, 2), "estrelas": estrelas, "detalhes": stats}
# ================= NÚCLEO DE ANÁLISE =================
def detectar_estrutura(audio_vec, sample_rate, duration):
try:
if duration < 30:
return [0.0, round(duration, 2)]
frame_size = 2048
hop_size = 512
rms_algo = es.RMS()
flux_algo = es.Flux()
w = es.Windowing(type="hann")
spec = es.Spectrum()
rms_curve = []
flux_curve = []
for frame in es.FrameGenerator(
audio_vec, frameSize=frame_size, hopSize=hop_size
):
s = spec(w(frame))
rms_curve.append(rms_algo(frame))
flux_curve.append(flux_algo(s))
min_len = min(len(rms_curve), len(flux_curve))
rms_curve = np.array(rms_curve[:min_len])
flux_curve = np.array(flux_curve[:min_len])
if np.max(rms_curve) > 0:
rms_curve = rms_curve / np.max(rms_curve)
if np.max(flux_curve) > 0:
flux_curve = flux_curve / np.max(flux_curve)
novelty = rms_curve * 0.5 + flux_curve * 0.5
segundos_por_frame = hop_size / sample_rate
pontos_mudanca = [0.0]
window_frames = int(10 / segundos_por_frame)
step_frames = int(5 / segundos_por_frame)
total_frames = len(novelty)
for i in range(0, total_frames - window_frames, step_frames):
janela = novelty[i : i + window_frames]
if np.std(janela) > 0.1:
pico_idx = i + np.argmax(janela)
tempo = round(pico_idx * segundos_por_frame, 2)
if tempo - pontos_mudanca[-1] > 15:
pontos_mudanca.append(tempo)
pontos_mudanca.append(round(duration, 2))
return pontos_mudanca
except Exception as e:
logging.error(f"Erro estrutura nova: {e}")
return [0.0, round(duration, 2)]
def analisar_faixa(caminho_arquivo, embedding_model, classifier_model, classes_raw):
# --- AUDITORIA: INÍCIO DO TIMER ---
inicio_timer = time.perf_counter()
tamanho_arquivo = os.path.getsize(caminho_arquivo)
pode_processar, motivo = verificar_memoria_segura(tamanho_arquivo)
if not pode_processar:
logging.warning(f"SKIPPED {os.path.basename(caminho_arquivo)}: {motivo}")
return None, 0, 0
nome_arquivo = os.path.basename(caminho_arquivo)
chave_busca = normalizar_chave(nome_arquivo)
dados_humano = DATABASE_YAML.get(chave_busca)
metadata = {
"arquivo": nome_arquivo,
"caminho": caminho_arquivo,
"dados_projeto": {},
"analise_tecnica": {},
"analise_ia": {},
}
if dados_humano:
metadata["dados_projeto"] = {
"titulo": dados_humano.get("original_title") or dados_humano.get("file"),
"bpm_original": dados_humano.get("bpm"),
"tags_yaml": dados_humano.get("tags"),
"autor": dados_humano.get("author", "Desconhecido"),
}
else:
metadata["analise_tecnica"]["complexidade"] = {
"score_total": 0,
"estrelas": 0,
"detalhes": "YAML não encontrado",
}
duracao_real = 0.0
# 1. ANÁLISE TÉCNICA E EXTRAÇÃO ÚNICA (HQ 44.1kHz)
try:
# Lê do HD APENAS UMA VEZ
loader_hq = es.MonoLoader(filename=caminho_arquivo, sampleRate=44100)
audio_hq = loader_hq()
if len(audio_hq) > (MAX_DURACAO_ANALISE * 44100):
audio_hq = audio_hq[: int(MAX_DURACAO_ANALISE * 44100)]
duracao_real = len(audio_hq) / 44100.0
audio_hq_vec = essentia.array(audio_hq)
rhythm = es.RhythmExtractor2013(method="multifeature")
bpm, _, conf, _, _ = rhythm(audio_hq_vec)
key_ex = es.KeyExtractor()
key, scale, _ = key_ex(audio_hq_vec)
loudness = es.Loudness()(audio_hq_vec)
estrutura = detectar_estrutura(audio_hq_vec, 44100, duracao_real)
bpm_final = round(bpm, 1)
origem = "algoritmo"
if dados_humano and dados_humano.get("bpm"):
try:
bpm_str = str(dados_humano.get("bpm")).replace("'", "").replace('"', "")
b = float(bpm_str)
if b > 0:
bpm_final = b
origem = "yaml (humano)"
except:
pass
metadata["analise_tecnica"].update(
{
"bpm": bpm_final,
"origem_bpm": origem,
"tom": key,
"escala": scale,
"intensidade_db": round(loudness, 2),
"estrutura_segundos": estrutura,
"duracao_detectada": round(duracao_real, 2),
}
)
metadata["analise_tecnica"]["complexidade"] = (
calcular_complexidade(dados_humano) if dados_humano else None
)
except Exception as e:
logging.error(f"Erro math {nome_arquivo}: {e}")
metadata["analise_tecnica"]["erro"] = str(e)
return metadata, 0, 0
# 2. ANÁLISE IA (LQ 16kHz via Resample da RAM - Resolve Leitura Dupla de HD)
if embedding_model and classifier_model and classes_raw:
try:
resample_algo = es.Resample(inputSampleRate=44100, outputSampleRate=16000)
audio_lq_vec = resample_algo(audio_hq_vec)
embeddings = embedding_model(audio_lq_vec)
predictions = classifier_model(embeddings)
avg_preds = np.mean(predictions, axis=0)
top_indices = np.argsort(avg_preds)[::-1][:10]
tags_detectadas = []
genero_pai = "Unknown"
for i, idx in enumerate(top_indices):
score = float(avg_preds[idx])
if score < 0.05:
continue
nome_full = classes_raw[idx]
partes = nome_full.split("---")
tag_limpa = partes[-1]
if i == 0:
genero_pai = partes[0] if len(partes) > 0 else tag_limpa
tags_detectadas.append({"tag": tag_limpa, "score": round(score, 3)})
metadata["analise_ia"] = {
"genero_macro": genero_pai,
"estilo_principal": tags_detectadas[0]["tag"] if tags_detectadas else "Unknown",
"nuvem_tags": tags_detectadas,
}
del audio_lq_vec
del embeddings
except Exception as e:
msg = f"Erro IA Pipeline: {str(e)}"
logging.error(msg)
metadata["analise_ia"] = {"erro": msg}
else:
metadata["analise_ia"] = {"status": "Modelos nao carregados"}
# Limpeza Limpa
del audio_hq
del audio_hq_vec
gc.collect()
tempo_gasto = time.perf_counter() - inicio_timer
return metadata, duracao_real, tempo_gasto
# ================= MAIN (Multiprocessing) =================
def worker_analise(caminho):
# Processa usando as instâncias dos modelos carregadas na thread mestre
global GLOBAL_EMBEDDING, GLOBAL_CLASSIFIER, GLOBAL_CLASSES
return analisar_faixa(caminho, GLOBAL_EMBEDDING, GLOBAL_CLASSIFIER, GLOBAL_CLASSES)
def main():
print("\n--- INICIANDO PROCESSADOR + AUDITORIA ---")
# Referencia variávies globais do script para alimentar os childs process do pool
global DATABASE_YAML, GLOBAL_EMBEDDING, GLOBAL_CLASSIFIER, GLOBAL_CLASSES
DATABASE_YAML = carregar_database_yaml()
ja_processados, lista_resultados = carregar_checkpoint()
auditor = Auditoria(ARQUIVO_AUDITORIA)
if not os.path.exists(PASTA_WAVS):
print("CRÍTICO: Pasta WAV não encontrada!")
return
todos = [f for f in os.listdir(PASTA_WAVS) if f.lower().endswith((".wav", ".ogg"))]
a_fazer = [os.path.join(PASTA_WAVS, f) for f in todos if f not in ja_processados]
total = len(a_fazer)
print(f"Total Arquivos: {len(todos)} | Novos a Fazer: {total}")
print(f"Auditoria será salva em: {ARQUIVO_AUDITORIA}")
# === CARREGAMENTO MODELOS GLOBAIS ===
path_embed = MODELO_EMBEDDING if os.path.exists(MODELO_EMBEDDING) else os.path.join(BASE_DIR, MODELO_EMBEDDING)
path_class = MODELO_CLASSIFIER if os.path.exists(MODELO_CLASSIFIER) else os.path.join(BASE_DIR, MODELO_CLASSIFIER)
path_json = MODELO_CLASSES if os.path.exists(MODELO_CLASSES) else os.path.join(BASE_DIR, MODELO_CLASSES)
if os.path.exists(path_embed) and os.path.exists(path_class) and os.path.exists(path_json):
print("--- Carregando Modelos de IA ---")
try:
with open(path_json, "r") as f:
GLOBAL_CLASSES = json.load(f)["classes"]
print("[1/2] Carregando Extrator de Embeddings...")
if hasattr(es, "TensorflowPredictEffnetDiscogs"):
GLOBAL_EMBEDDING = es.TensorflowPredictEffnetDiscogs(graphFilename=path_embed, output="PartitionedCall:1")
else:
print(" -> Usando TensorflowPredict genérico.")
GLOBAL_EMBEDDING = es.TensorflowPredict(graphFilename=path_embed, input="serving_default_model_Placeholder", output="PartitionedCall:1")
print(" -> Embeddings carregados com sucesso.")
GLOBAL_CLASSIFIER = es.TensorflowPredict2D(graphFilename=path_class, input="serving_default_model_Placeholder", output="PartitionedCall:0")
print("[2/2] Classificador de Gênero carregado.")
except Exception as e:
print(f"ERRO CRÍTICO AO CARREGAR IA: {e}")
return
else:
print("Arquivos de modelo incompletos.")
return
if total == 0:
print("Tudo atualizado.")
return
# ProcessPoolExecutor lidará com N filas ao mesmo tempo
max_workers = max(1, multiprocessing.cpu_count() - 1)
print(f"-> Iniciando processamento paralelo com {max_workers} workers.")
contagem_sessao = 0
try:
with concurrent.futures.ProcessPoolExecutor(max_workers=max_workers) as executor:
resultados_futuros = {executor.submit(worker_analise, caminho): caminho for caminho in a_fazer}
# O as_completed processa os que terminam mais rápido em vez de travar na ordem
for future in tqdm(concurrent.futures.as_completed(resultados_futuros), total=len(a_fazer), unit="track"):
try:
res, duracao, tempo_gasto = future.result()
caminho = resultados_futuros[future]
if res:
salvar_progresso(res)
lista_resultados.append(res)
tamanho = os.path.getsize(caminho)
auditor.registrar_processamento(res['arquivo'], tamanho, duracao, tempo_gasto)
contagem_sessao += 1
auditor.verificar_marco(contagem_sessao)
except Exception as e:
logging.error(f"Erro ao processar um dos arquivos no worker: {e}")
except KeyboardInterrupt:
print("\nParando paralelismo graciosamente...")
finally:
auditor.fechar() # Fecha o CSV com segurança
tempo_total_sessao = time.time() - auditor.inicio_global
print("\n--- FIM DO PROCESSAMENTO ---")
print(f"Tempo Total: {str(timedelta(seconds=int(tempo_total_sessao)))}")
print("Gerando JSON final consolidado...")
with open(ARQUIVO_FINAL_JSON, "w", encoding="utf-8") as f:
json.dump(lista_resultados, f, indent=4, ensure_ascii=False)
print(f"Dados JSON: {ARQUIVO_FINAL_JSON}")
print(f"Auditoria CSV: {ARQUIVO_AUDITORIA}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,457 @@
import yaml
import json
import os
import logging
import csv
import unicodedata
import time
import psutil
import glob # Import necessário para ler múltiplos arquivos
from datetime import datetime, timedelta
# --- CONFIGURAÇÕES GERAIS ---
BASE_DIR = "/var/www/html/trens/src_mmpSearch"
LOG_DIR = os.path.join(BASE_DIR, "logs/classificacao_pedagogica")
os.makedirs(LOG_DIR, exist_ok=True)
TIMESTAMP = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
# Configuração de Log
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - CLASSIFICADOR - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(os.path.join(LOG_DIR, f"log_pedagogico_{TIMESTAMP}.log")),
logging.StreamHandler(),
],
)
# --- ARQUIVOS DE ENTRADA/SAÍDA ---
def encontrar_caminho(nome_arquivo):
"""Procura o arquivo em locais comuns do projeto."""
candidatos = [
os.path.join(BASE_DIR, "saida_analises", nome_arquivo),
os.path.join(BASE_DIR, "data", nome_arquivo),
os.path.join(BASE_DIR, nome_arquivo),
nome_arquivo,
]
for c in candidatos:
if os.path.exists(c):
return c
return nome_arquivo
# --- MUDANÇA AQUI: Aponta para a pasta dos lotes ---
PASTA_LOTES_YAML = os.path.join(BASE_DIR, "lotes_yaml")
# ARQUIVO_YAML = "all.yml" <-- Removido/Comentado
ARQUIVO_AUDIO_INPUT = encontrar_caminho("db_final_completo.json")
ARQUIVO_FINAL_OUTPUT = os.path.join(
BASE_DIR, "saida_analises", "db_projetos_classificados.json"
)
ARQUIVO_AUDITORIA = os.path.join(
BASE_DIR, "saida_analises", "audit_classificacao_pedagogica.csv"
)
# --- LISTAS DE REFERÊNCIA ---
PLUGINS_NATIVOS = [
"tripleoscillator",
"zynaddsubfx",
"sfxr",
"organic",
"audiofileprocessor",
"lb302",
"kicker",
"watsyn",
"bitinvader",
"freeboy",
"mallets",
"vibed",
]
# --- CLASSE DE AUDITORIA DE PERFORMANCE ---
class AuditoriaPerformance:
def __init__(self):
self.inicio_global = time.time()
self.process = psutil.Process(os.getpid())
self.marcos = [1, 10, 100, 500, 1000, 2000, 5000]
def get_metricas(self):
"""Retorna uso atual de RAM (MB) e CPU (%)"""
try:
ram_mb = self.process.memory_info().rss / (1024 * 1024)
cpu_pct = self.process.cpu_percent(interval=None)
return round(ram_mb, 2), cpu_pct
except:
return 0.0, 0.0
def verificar_marco(self, contagem):
if contagem in self.marcos:
tempo_decorrido = time.time() - self.inicio_global
logging.info(
f"--- MARCO: {contagem} projetos processados em {str(timedelta(seconds=int(tempo_decorrido)))} ---"
)
# --- FUNÇÕES AUXILIARES ---
def normalizar_chave(texto):
if not texto:
return ""
s = str(texto)
if "." in s:
s = os.path.splitext(s)[0]
s = unicodedata.normalize("NFKD", s).encode("ASCII", "ignore").decode("ASCII")
return s.lower().replace(" ", "").replace("-", "").replace("_", "").strip()
def calcular_nivel_ponderado(
num_tracks, tem_automacao, usa_zyn, qtd_secoes_audio, fx_criativos
):
pontos = 0
# 1. Volume
if num_tracks >= 8:
pontos += 1
if num_tracks >= 16:
pontos += 2
if num_tracks >= 32:
pontos += 3
if num_tracks >= 64:
pontos += 4
elif num_tracks >= 128:
pontos += 5
# 2. Técnica
if tem_automacao:
pontos += 2
if usa_zyn:
pontos += 1.5
if fx_criativos:
pontos += 1
# 3. Estrutura (Áudio)
if qtd_secoes_audio >= 3:
pontos += 1.5
elif qtd_secoes_audio >= 5:
pontos += 3
elif qtd_secoes_audio >= 10:
pontos += 5
if pontos < 3:
return "Iniciante"
if pontos < 6:
return "Intermediário"
return "Avançado"
def analisar_tags_pedagogicas(tracks, bpm, qtd_secoes):
tags = []
tem_kicker = False
tem_zyn = False
tem_automacao = False
tem_fx_chain = False
rolagem_trap = False
for t in tracks:
inst = t.get("instrumenttrack")
if not inst:
continue
nome = inst.get("name", "").lower()
plugin = inst.get("plugin_name", "").lower()
if plugin == "kicker":
tem_kicker = True
if plugin == "zynaddsubfx":
tem_zyn = True
if "automation" in str(inst).lower():
tem_automacao = True
fx = inst.get("fxchain", {})
if fx and int(fx.get("numofeffects", 0)) > 0:
tem_fx_chain = True
patterns = inst.get("patterns", [])
if patterns:
for pat in patterns:
steps = pat.get("steps", [])
consecutivos = 0
for step in steps:
val = 1 if step else 0
consecutivos = (consecutivos + 1) if val else 0
if consecutivos >= 3 and ("hat" in nome):
rolagem_trap = True
if tem_kicker:
tags.append("Síntese de Bateria")
if tem_zyn:
tags.append("Design de Som Avançado")
if tem_automacao:
tags.append("Automação")
if tem_fx_chain:
tags.append("Mixagem com FX")
if rolagem_trap and bpm > 115:
tags.append("Trap Rhythm")
if bpm < 100 and not rolagem_trap:
tags.append("Boombap Groove")
if qtd_secoes >= 4:
tags.append("Arranjo Completo")
elif qtd_secoes <= 1:
tags.append("Loop Estático")
else:
tags.append("Estrutura Simples")
return tags, tem_automacao, tem_zyn, tem_fx_chain
# --- FUNÇÃO PARA CARREGAR LOTES YAML ---
def carregar_todos_yamls(pasta_lotes):
"""Lê todos os arquivos YAML da pasta especificada e retorna uma lista única."""
logging.info(f"Buscando arquivos YAML em: {pasta_lotes}")
padrao = os.path.join(pasta_lotes, "*.yml")
arquivos = sorted(glob.glob(padrao))
todos_projetos = []
if not arquivos:
logging.warning("Nenhum arquivo YAML encontrado na pasta de lotes.")
# Tenta fallback para o arquivo único antigo se existir
caminho_unico = encontrar_caminho("all.yml")
if os.path.exists(caminho_unico):
logging.info(f"Fallback: Carregando arquivo único {caminho_unico}")
with open(caminho_unico, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
return []
for arq in arquivos:
try:
logging.info(f"Carregando lote: {os.path.basename(arq)}")
with open(arq, "r", encoding="utf-8") as f:
lote = yaml.safe_load(f)
if lote:
# Garante que seja uma lista
if isinstance(lote, list):
todos_projetos.extend(lote)
elif isinstance(lote, dict):
todos_projetos.append(lote)
except Exception as e:
logging.error(f"Erro ao ler arquivo {arq}: {e}")
logging.info(f"Total de projetos carregados dos lotes: {len(todos_projetos)}")
return todos_projetos
# --- MAIN ---
def main():
logging.info("--- INICIANDO CLASSIFICADOR PEDAGÓGICO (COM AUDITORIA E LOTES) ---")
auditor = AuditoriaPerformance()
# 1. Carregar YAML (Adaptado para Lotes)
if not os.path.exists(PASTA_LOTES_YAML):
logging.warning(f"Pasta de lotes não encontrada: {PASTA_LOTES_YAML}")
# Tenta criar se não existir (embora deva existir com arquivos)
# os.makedirs(PASTA_LOTES_YAML, exist_ok=True)
dados_projetos = carregar_todos_yamls(PASTA_LOTES_YAML)
if not dados_projetos:
logging.critical("Nenhum dado de projeto carregado. Encerrando.")
return
# 2. Carregar JSON de Áudio
lookup_audio = {}
total_audio_carregado = 0
if os.path.exists(ARQUIVO_AUDIO_INPUT):
logging.info(f"Lendo Análise de Áudio: {ARQUIVO_AUDIO_INPUT}")
with open(ARQUIVO_AUDIO_INPUT, "r", encoding="utf-8") as f:
lista_audio = json.load(f)
for item in lista_audio:
chave = normalizar_chave(item.get("arquivo", ""))
if chave:
lookup_audio[chave] = item
total_audio_carregado = len(lista_audio)
else:
logging.warning(f"Arquivo de áudio {ARQUIVO_AUDIO_INPUT} não encontrado.")
db_final = []
estatisticas = {"Iniciante": 0, "Intermediário": 0, "Avançado": 0, "Total": 0}
# --- PREPARAR CSV DE AUDITORIA ---
with open(ARQUIVO_AUDITORIA, "w", newline="", encoding="utf-8") as csvfile:
writer = csv.writer(csvfile)
# Cabeçalho expandido com métricas de performance
writer.writerow(
[
"Timestamp",
"ID_Projeto",
"Audio_Encontrado",
"Qtd_Secoes",
"Nivel",
"Genero_Final",
"Tempo_Proc_ms",
"RAM_Uso_MB",
"Tags",
]
)
logging.info(f"Processando {len(dados_projetos)} projetos...")
contagem_processados = 0
for proj in dados_projetos:
if not proj or not isinstance(proj, dict):
continue
# --- INÍCIO TIMER INDIVIDUAL ---
inicio_item = time.perf_counter()
nome_arquivo = proj.get("file", "")
if not nome_arquivo:
continue
try:
# Lógica de Classificação
chave_proj = normalizar_chave(nome_arquivo)
dados_audio = lookup_audio.get(chave_proj, {})
analise_tec = dados_audio.get("analise_tecnica", {})
analise_ia = dados_audio.get("analise_ia", {})
raw_bpm = proj.get("bpm", 120)
try:
bpm_projeto = float(raw_bpm)
except:
bpm_projeto = 120.0
est_seg = analise_tec.get("estrutura_segundos", [])
qtd_secoes = len(est_seg) if isinstance(est_seg, list) else 1
tracks = proj.get("tracks", [])
tags, tem_auto, tem_zyn, tem_fx = analisar_tags_pedagogicas(
tracks, bpm_projeto, qtd_secoes
)
plugins_usados = set()
usa_vst_externo = False
for t in tracks:
inst_track = t.get("instrumenttrack", {})
if not inst_track:
continue
p = inst_track.get("plugin_name", "")
if p:
p_norm = p.lower()
plugins_usados.add(p_norm)
if p_norm not in PLUGINS_NATIVOS:
usa_vst_externo = True
nivel = calcular_nivel_ponderado(
len(tracks), tem_auto, tem_zyn, qtd_secoes, tem_fx
)
estatisticas[nivel] += 1
estatisticas["Total"] += 1
genero = "Desconhecido"
if analise_ia and "estilo_principal" in analise_ia:
genero = analise_ia["estilo_principal"]
elif (
"genero_macro" in analise_ia
and analise_ia["genero_macro"] != "Unknown"
):
genero = analise_ia["genero_macro"]
if genero == "Desconhecido" or genero == "Unknown":
if "Trap Rhythm" in tags:
genero = "Trap (Rule-based)"
elif bpm_projeto > 120:
genero = "Electronic (Rule-based)"
else:
genero = "HipHop/Downtempo (Rule-based)"
# Objeto Final
item = {
"id": nome_arquivo,
"titulo": proj.get("original_title", nome_arquivo),
"match_audio": bool(dados_audio),
"pedagogia": {
"nivel": nivel,
"tags_aprendizado": tags,
"plugins_principais": list(plugins_usados),
"requer_vst_externo": usa_vst_externo,
},
"tecnica": {
"bpm": bpm_projeto,
"tom": analise_tec.get("tom", "N/A"),
"escala": analise_tec.get("escala", "N/A"),
"estrutura_qtd_secoes": qtd_secoes,
"intensidade_media": analise_tec.get("intensidade_db", "N/A"),
},
"classificacao": {
"genero": genero,
"mood": "Energético"
if analise_tec.get("intensidade_db", -20) > -10
else "Suave",
},
}
db_final.append(item)
# --- FIM TIMER INDIVIDUAL ---
tempo_ms = (time.perf_counter() - inicio_item) * 1000
ram_atual, _ = auditor.get_metricas()
ts_agora = datetime.now().strftime("%H:%M:%S")
# Registra Auditoria com Performance
writer.writerow(
[
ts_agora,
nome_arquivo,
"SIM" if dados_audio else "NAO",
qtd_secoes,
nivel,
genero,
f"{tempo_ms:.3f}", # Tempo em milissegundos com 3 casas
f"{ram_atual:.2f}", # RAM em MB
"|".join(tags),
]
)
contagem_processados += 1
auditor.verificar_marco(contagem_processados)
except Exception as e:
logging.error(
f"Erro ao processar projeto '{proj.get('file', 'unknown')}': {e}"
)
continue
# Salvar Resultado JSON
logging.info(f"Salvando JSON Final: {ARQUIVO_FINAL_OUTPUT}")
with open(ARQUIVO_FINAL_OUTPUT, "w", encoding="utf-8") as f:
json.dump(db_final, f, indent=4, ensure_ascii=False)
tempo_total = time.time() - auditor.inicio_global
logging.info("--- RELATÓRIO FINAL ---")
logging.info(f"Tempo Total de Execução: {str(timedelta(seconds=int(tempo_total)))}")
logging.info(f"Total Processado: {estatisticas['Total']}")
logging.info(f"Iniciantes: {estatisticas['Iniciante']}")
logging.info(f"Intermediários: {estatisticas['Intermediário']}")
logging.info(f"Avançados: {estatisticas['Avançado']}")
logging.info(
f"Taxa de Casamento com Áudio: {len(lookup_audio)}/{total_audio_carregado}"
)
logging.info(f"Auditoria salva em: {ARQUIVO_AUDITORIA}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,274 @@
import yaml
import json
import os
import logging
# --- CONFIGURAÇÕES DE LOG ---
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - CLASSIFICADOR - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler("classificacao_pedagogica.log"),
logging.StreamHandler(),
],
)
# --- CONFIGURAÇÕES ---
ARQUIVO_YAML = "all.yml"
ARQUIVO_AUDIO = "analise_audio.json"
ARQUIVO_FINAL = "db_projetos_classificados.json"
PLUGINS_NATIVOS = [
"tripleoscillator",
"zynaddsubfx",
"sfxr",
"organic",
"audiofileprocessor",
"lb302",
"kicker",
"watsyn",
"bitinvader",
"freeboy",
"mallets",
"vibed",
]
# --- LÓGICA PEDAGÓGICA AVANÇADA ---
def calcular_nivel_ponderado(
num_tracks, tem_automacao, plugins_complexos, qtd_secoes_audio, fx_criativos
):
"""
Calcula nível pedagógico (Iniciante, Intermediário, Avançado)
baseado em esforço de engenharia e complexidade musical.
"""
pontos = 0
# 1. Volume de Trabalho
if num_tracks >= 8:
pontos += 1
if num_tracks >= 16:
pontos += 1
# 2. Profundidade Técnica
if tem_automacao:
pontos += 2 # Automação é sinal forte de polimento
if plugins_complexos:
pontos += 1.5 # Usar ZynAddSubFx requer estudo
if fx_criativos:
pontos += 1 # Usar efeitos além do básico
# 3. Estrutura Musical (Vinda do Essentia)
# Se o áudio tem > 3 seções detectadas, há arranjo (não é só loop)
if qtd_secoes_audio >= 3:
pontos += 2
elif qtd_secoes_audio >= 5:
pontos += 3
# Classificação
if pontos < 3:
return "Iniciante"
if pontos < 6:
return "Intermediário"
return "Avançado"
def analisar_tags_pedagogicas(tracks, bpm, qtd_secoes, tom, escala):
tags = []
# Flags de estado técnico
tem_zyn = False
tem_automacao = False
tem_fx_chain = False
qtd_nativos = 0
qtd_vst_externos = 0
tem_audio_processor = False
for t in tracks:
inst = t.get("instrumenttrack")
if not inst:
continue
plugin = inst.get("plugin_name", "").lower()
# Mapeamento de Plugins e Dependências
if plugin in PLUGINS_NATIVOS:
qtd_nativos += 1
elif plugin == "vestige":
qtd_vst_externos += 1
if plugin == "zynaddsubfx":
tem_zyn = True
if plugin == "audiofileprocessor":
tem_audio_processor = True
if "automation" in str(inst).lower():
tem_automacao = True
fx = inst.get("fxchain", {})
if fx and int(fx.get("numofeffects", 0)) > 0:
tem_fx_chain = True
# --- 1. Estética da Escassez e Modulação Marginal ---
if qtd_nativos > 0 and qtd_vst_externos == 0:
tags.append("Estética da Escassez: Síntese Nativa")
if tem_automacao and qtd_vst_externos == 0:
tags.append("Design de Som Marginal (Automação como Timbre)")
# --- 2. Taxonomia de Bloom (Carga Cognitiva) ---
if tem_zyn or tem_fx_chain:
tags.append("Bloom: Criação (Sound Design Complexo)")
elif tem_audio_processor and not tem_automacao:
tags.append("Bloom: Aplicação (Sample Inalterado)")
# --- 3. Fluência Estrutural (Macroforma Musical) ---
if qtd_secoes >= 3:
tags.append("Fluência Estrutural: Arranjo Completo (Macroforma)")
else:
tags.append("Fluência Estrutural: Loop Estático")
# --- 4. Acessibilidade Ergonômica (Música de Computador) ---
if tom == "A" and escala == "minor":
tags.append("Ergonomia Autodidata (Teclado QWERTY / Lá Menor)")
# --- 5. Herança Cultural / Andamento ---
if 80 <= bpm <= 95:
tags.append("Cadência Clássica (Boom Bap)")
elif bpm >= 130:
tags.append("Estética Trap/Funk/Eletrônica / Alta Energia")
return tags
def main():
logging.info("Iniciando Classificador Mestre...")
# 1. Carregar YAML
if not os.path.exists(ARQUIVO_YAML):
logging.critical(f"{ARQUIVO_YAML} não encontrado.")
return
with open(ARQUIVO_YAML, "r", encoding="utf-8") as f:
dados_projetos = yaml.safe_load(f)
# 2. Carregar JSON de Áudio
lookup_audio = {}
if os.path.exists(ARQUIVO_AUDIO):
with open(ARQUIVO_AUDIO, "r", encoding="utf-8") as f:
lista_audio = json.load(f)
for item in lista_audio:
# Normaliza chave de busca (nome do arquivo sem extensão)
nome_base = os.path.splitext(item["arquivo"])[0]
lookup_audio[nome_base] = item
else:
logging.warning(
"JSON de áudio não encontrado. Classificação será apenas estática."
)
db_final = []
logging.info(f"Processando {len(dados_projetos)} entradas do YAML...")
for proj in dados_projetos:
if not proj:
continue
try:
nome_arquivo = proj.get("file", "")
if not nome_arquivo:
continue
# Tenta casar com dados de áudio
# Nota: Seu YAML usa nomes como 'trap-fyrebreak', o arquivo de audio deve ser 'trap-fyrebreak.wav'
# O lookup_audio já está sem extensão.
dados_audio = lookup_audio.get(nome_arquivo, {})
analise_tec = dados_audio.get("analise_tecnica", {})
analise_ia = dados_audio.get("analise_ia", {})
# Dados Técnicos
raw_bpm = proj.get("bpm", 120)
bpm_projeto = (
float(raw_bpm) if str(raw_bpm).replace(".", "", 1).isdigit() else 120.0
)
# Feature nova: Quantidade de seções (Intro, Verso...)
qtd_secoes = analise_tec.get("qtd_secoes_detectadas", 1)
# Análise Estática (Tracks e Plugins)
tracks = proj.get("tracks", [])
tags, tem_auto, tem_zyn, tem_fx = analisar_tags_pedagogicas(
tracks=proj.get("tracks", []),
bpm=bpm_projeto,
qtd_secoes=qtd_secoes,
tom=analise_tec.get("tom", "N/A"),
escala=analise_tec.get("escala", "N/A"),
)
# Verifica plugins externos
plugins_usados = set()
usa_vst_externo = False
for t in tracks:
p = t.get("instrumenttrack", {}).get("plugin_name", "")
if p:
plugins_usados.add(p)
if p.lower() not in PLUGINS_NATIVOS:
usa_vst_externo = True
# Cálculo de Nível (Nova Lógica)
nivel = calcular_nivel_ponderado(
len(tracks), tem_auto, tem_zyn, qtd_secoes, tem_fx
)
# Define Gênero (IA tem prioridade, depois regras manuais)
genero = analise_ia.get("genero_predito", "Desconhecido")
if genero == "Desconhecido" or analise_ia.get("confianca_genero", 0) < 0.4:
# Fallback manual simples
if "Trap Rhythm" in tags:
genero = "Trap"
elif bpm_projeto > 120:
genero = "Electronic"
else:
genero = "HipHop/Downtempo"
# Montagem do Objeto
item = {
"id": nome_arquivo,
"titulo": proj.get("original_title", nome_arquivo),
"pedagogia": {
"nivel": nivel,
"tags_aprendizado": tags,
"plugins_principais": list(plugins_usados),
"requer_vst_externo": usa_vst_externo,
},
"tecnica": {
"bpm": bpm_projeto,
"tom": analise_tec.get("tom", "N/A"),
"escala": analise_tec.get("escala", "N/A"),
"estrutura_detectada": f"{qtd_secoes} seções distintas",
},
"classificacao": {
"genero": genero,
"mood": "Energético"
if analise_tec.get("intensidade", -20) > -10
else "Suave",
},
}
db_final.append(item)
except Exception as e:
logging.error(f"Erro ao processar projeto '{proj.get('file')}': {e}")
continue
# Salvar Resultado
with open(ARQUIVO_FINAL, "w", encoding="utf-8") as f:
json.dump(db_final, f, indent=4, ensure_ascii=False)
logging.info(f"Sucesso! {len(db_final)} projetos classificados e salvos.")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,468 @@
{
"name": "EffnetDiscogs",
"type": "Music style classification and embeddings",
"link": "https://essentia.upf.edu/models/music-style-classification/discogs-effnet/discogs-effnet-bs64-1.pb",
"version": "1",
"description": "Prediction of the top-400 music styles in the Discogs-4M dataset (unreleased)",
"author": "Pablo Alonso",
"email": "pablo.alonso@upf.edu",
"release_date": "2022-02-17",
"framework": "tensorflow",
"framework_version": "2.8.0",
"classes": [
"Blues---Boogie Woogie",
"Blues---Chicago Blues",
"Blues---Country Blues",
"Blues---Delta Blues",
"Blues---Electric Blues",
"Blues---Harmonica Blues",
"Blues---Jump Blues",
"Blues---Louisiana Blues",
"Blues---Modern Electric Blues",
"Blues---Piano Blues",
"Blues---Rhythm & Blues",
"Blues---Texas Blues",
"Brass & Military---Brass Band",
"Brass & Military---Marches",
"Brass & Military---Military",
"Children's---Educational",
"Children's---Nursery Rhymes",
"Children's---Story",
"Classical---Baroque",
"Classical---Choral",
"Classical---Classical",
"Classical---Contemporary",
"Classical---Impressionist",
"Classical---Medieval",
"Classical---Modern",
"Classical---Neo-Classical",
"Classical---Neo-Romantic",
"Classical---Opera",
"Classical---Post-Modern",
"Classical---Renaissance",
"Classical---Romantic",
"Electronic---Abstract",
"Electronic---Acid",
"Electronic---Acid House",
"Electronic---Acid Jazz",
"Electronic---Ambient",
"Electronic---Bassline",
"Electronic---Beatdown",
"Electronic---Berlin-School",
"Electronic---Big Beat",
"Electronic---Bleep",
"Electronic---Breakbeat",
"Electronic---Breakcore",
"Electronic---Breaks",
"Electronic---Broken Beat",
"Electronic---Chillwave",
"Electronic---Chiptune",
"Electronic---Dance-pop",
"Electronic---Dark Ambient",
"Electronic---Darkwave",
"Electronic---Deep House",
"Electronic---Deep Techno",
"Electronic---Disco",
"Electronic---Disco Polo",
"Electronic---Donk",
"Electronic---Downtempo",
"Electronic---Drone",
"Electronic---Drum n Bass",
"Electronic---Dub",
"Electronic---Dub Techno",
"Electronic---Dubstep",
"Electronic---Dungeon Synth",
"Electronic---EBM",
"Electronic---Electro",
"Electronic---Electro House",
"Electronic---Electroclash",
"Electronic---Euro House",
"Electronic---Euro-Disco",
"Electronic---Eurobeat",
"Electronic---Eurodance",
"Electronic---Experimental",
"Electronic---Freestyle",
"Electronic---Future Jazz",
"Electronic---Gabber",
"Electronic---Garage House",
"Electronic---Ghetto",
"Electronic---Ghetto House",
"Electronic---Glitch",
"Electronic---Goa Trance",
"Electronic---Grime",
"Electronic---Halftime",
"Electronic---Hands Up",
"Electronic---Happy Hardcore",
"Electronic---Hard House",
"Electronic---Hard Techno",
"Electronic---Hard Trance",
"Electronic---Hardcore",
"Electronic---Hardstyle",
"Electronic---Hi NRG",
"Electronic---Hip Hop",
"Electronic---Hip-House",
"Electronic---House",
"Electronic---IDM",
"Electronic---Illbient",
"Electronic---Industrial",
"Electronic---Italo House",
"Electronic---Italo-Disco",
"Electronic---Italodance",
"Electronic---Jazzdance",
"Electronic---Juke",
"Electronic---Jumpstyle",
"Electronic---Jungle",
"Electronic---Latin",
"Electronic---Leftfield",
"Electronic---Makina",
"Electronic---Minimal",
"Electronic---Minimal Techno",
"Electronic---Modern Classical",
"Electronic---Musique Concr\u00e8te",
"Electronic---Neofolk",
"Electronic---New Age",
"Electronic---New Beat",
"Electronic---New Wave",
"Electronic---Noise",
"Electronic---Nu-Disco",
"Electronic---Power Electronics",
"Electronic---Progressive Breaks",
"Electronic---Progressive House",
"Electronic---Progressive Trance",
"Electronic---Psy-Trance",
"Electronic---Rhythmic Noise",
"Electronic---Schranz",
"Electronic---Sound Collage",
"Electronic---Speed Garage",
"Electronic---Speedcore",
"Electronic---Synth-pop",
"Electronic---Synthwave",
"Electronic---Tech House",
"Electronic---Tech Trance",
"Electronic---Techno",
"Electronic---Trance",
"Electronic---Tribal",
"Electronic---Tribal House",
"Electronic---Trip Hop",
"Electronic---Tropical House",
"Electronic---UK Garage",
"Electronic---Vaporwave",
"Folk, World, & Country---African",
"Folk, World, & Country---Bluegrass",
"Folk, World, & Country---Cajun",
"Folk, World, & Country---Canzone Napoletana",
"Folk, World, & Country---Catalan Music",
"Folk, World, & Country---Celtic",
"Folk, World, & Country---Country",
"Folk, World, & Country---Fado",
"Folk, World, & Country---Flamenco",
"Folk, World, & Country---Folk",
"Folk, World, & Country---Gospel",
"Folk, World, & Country---Highlife",
"Folk, World, & Country---Hillbilly",
"Folk, World, & Country---Hindustani",
"Folk, World, & Country---Honky Tonk",
"Folk, World, & Country---Indian Classical",
"Folk, World, & Country---La\u00efk\u00f3",
"Folk, World, & Country---Nordic",
"Folk, World, & Country---Pacific",
"Folk, World, & Country---Polka",
"Folk, World, & Country---Ra\u00ef",
"Folk, World, & Country---Romani",
"Folk, World, & Country---Soukous",
"Folk, World, & Country---S\u00e9ga",
"Folk, World, & Country---Volksmusik",
"Folk, World, & Country---Zouk",
"Folk, World, & Country---\u00c9ntekhno",
"Funk / Soul---Afrobeat",
"Funk / Soul---Boogie",
"Funk / Soul---Contemporary R&B",
"Funk / Soul---Disco",
"Funk / Soul---Free Funk",
"Funk / Soul---Funk",
"Funk / Soul---Gospel",
"Funk / Soul---Neo Soul",
"Funk / Soul---New Jack Swing",
"Funk / Soul---P.Funk",
"Funk / Soul---Psychedelic",
"Funk / Soul---Rhythm & Blues",
"Funk / Soul---Soul",
"Funk / Soul---Swingbeat",
"Funk / Soul---UK Street Soul",
"Hip Hop---Bass Music",
"Hip Hop---Boom Bap",
"Hip Hop---Bounce",
"Hip Hop---Britcore",
"Hip Hop---Cloud Rap",
"Hip Hop---Conscious",
"Hip Hop---Crunk",
"Hip Hop---Cut-up/DJ",
"Hip Hop---DJ Battle Tool",
"Hip Hop---Electro",
"Hip Hop---G-Funk",
"Hip Hop---Gangsta",
"Hip Hop---Grime",
"Hip Hop---Hardcore Hip-Hop",
"Hip Hop---Horrorcore",
"Hip Hop---Instrumental",
"Hip Hop---Jazzy Hip-Hop",
"Hip Hop---Miami Bass",
"Hip Hop---Pop Rap",
"Hip Hop---Ragga HipHop",
"Hip Hop---RnB/Swing",
"Hip Hop---Screw",
"Hip Hop---Thug Rap",
"Hip Hop---Trap",
"Hip Hop---Trip Hop",
"Hip Hop---Turntablism",
"Jazz---Afro-Cuban Jazz",
"Jazz---Afrobeat",
"Jazz---Avant-garde Jazz",
"Jazz---Big Band",
"Jazz---Bop",
"Jazz---Bossa Nova",
"Jazz---Contemporary Jazz",
"Jazz---Cool Jazz",
"Jazz---Dixieland",
"Jazz---Easy Listening",
"Jazz---Free Improvisation",
"Jazz---Free Jazz",
"Jazz---Fusion",
"Jazz---Gypsy Jazz",
"Jazz---Hard Bop",
"Jazz---Jazz-Funk",
"Jazz---Jazz-Rock",
"Jazz---Latin Jazz",
"Jazz---Modal",
"Jazz---Post Bop",
"Jazz---Ragtime",
"Jazz---Smooth Jazz",
"Jazz---Soul-Jazz",
"Jazz---Space-Age",
"Jazz---Swing",
"Latin---Afro-Cuban",
"Latin---Bai\u00e3o",
"Latin---Batucada",
"Latin---Beguine",
"Latin---Bolero",
"Latin---Boogaloo",
"Latin---Bossanova",
"Latin---Cha-Cha",
"Latin---Charanga",
"Latin---Compas",
"Latin---Cubano",
"Latin---Cumbia",
"Latin---Descarga",
"Latin---Forr\u00f3",
"Latin---Guaguanc\u00f3",
"Latin---Guajira",
"Latin---Guaracha",
"Latin---MPB",
"Latin---Mambo",
"Latin---Mariachi",
"Latin---Merengue",
"Latin---Norte\u00f1o",
"Latin---Nueva Cancion",
"Latin---Pachanga",
"Latin---Porro",
"Latin---Ranchera",
"Latin---Reggaeton",
"Latin---Rumba",
"Latin---Salsa",
"Latin---Samba",
"Latin---Son",
"Latin---Son Montuno",
"Latin---Tango",
"Latin---Tejano",
"Latin---Vallenato",
"Non-Music---Audiobook",
"Non-Music---Comedy",
"Non-Music---Dialogue",
"Non-Music---Education",
"Non-Music---Field Recording",
"Non-Music---Interview",
"Non-Music---Monolog",
"Non-Music---Poetry",
"Non-Music---Political",
"Non-Music---Promotional",
"Non-Music---Radioplay",
"Non-Music---Religious",
"Non-Music---Spoken Word",
"Pop---Ballad",
"Pop---Bollywood",
"Pop---Bubblegum",
"Pop---Chanson",
"Pop---City Pop",
"Pop---Europop",
"Pop---Indie Pop",
"Pop---J-pop",
"Pop---K-pop",
"Pop---Kay\u014dkyoku",
"Pop---Light Music",
"Pop---Music Hall",
"Pop---Novelty",
"Pop---Parody",
"Pop---Schlager",
"Pop---Vocal",
"Reggae---Calypso",
"Reggae---Dancehall",
"Reggae---Dub",
"Reggae---Lovers Rock",
"Reggae---Ragga",
"Reggae---Reggae",
"Reggae---Reggae-Pop",
"Reggae---Rocksteady",
"Reggae---Roots Reggae",
"Reggae---Ska",
"Reggae---Soca",
"Rock---AOR",
"Rock---Acid Rock",
"Rock---Acoustic",
"Rock---Alternative Rock",
"Rock---Arena Rock",
"Rock---Art Rock",
"Rock---Atmospheric Black Metal",
"Rock---Avantgarde",
"Rock---Beat",
"Rock---Black Metal",
"Rock---Blues Rock",
"Rock---Brit Pop",
"Rock---Classic Rock",
"Rock---Coldwave",
"Rock---Country Rock",
"Rock---Crust",
"Rock---Death Metal",
"Rock---Deathcore",
"Rock---Deathrock",
"Rock---Depressive Black Metal",
"Rock---Doo Wop",
"Rock---Doom Metal",
"Rock---Dream Pop",
"Rock---Emo",
"Rock---Ethereal",
"Rock---Experimental",
"Rock---Folk Metal",
"Rock---Folk Rock",
"Rock---Funeral Doom Metal",
"Rock---Funk Metal",
"Rock---Garage Rock",
"Rock---Glam",
"Rock---Goregrind",
"Rock---Goth Rock",
"Rock---Gothic Metal",
"Rock---Grindcore",
"Rock---Grunge",
"Rock---Hard Rock",
"Rock---Hardcore",
"Rock---Heavy Metal",
"Rock---Indie Rock",
"Rock---Industrial",
"Rock---Krautrock",
"Rock---Lo-Fi",
"Rock---Lounge",
"Rock---Math Rock",
"Rock---Melodic Death Metal",
"Rock---Melodic Hardcore",
"Rock---Metalcore",
"Rock---Mod",
"Rock---Neofolk",
"Rock---New Wave",
"Rock---No Wave",
"Rock---Noise",
"Rock---Noisecore",
"Rock---Nu Metal",
"Rock---Oi",
"Rock---Parody",
"Rock---Pop Punk",
"Rock---Pop Rock",
"Rock---Pornogrind",
"Rock---Post Rock",
"Rock---Post-Hardcore",
"Rock---Post-Metal",
"Rock---Post-Punk",
"Rock---Power Metal",
"Rock---Power Pop",
"Rock---Power Violence",
"Rock---Prog Rock",
"Rock---Progressive Metal",
"Rock---Psychedelic Rock",
"Rock---Psychobilly",
"Rock---Pub Rock",
"Rock---Punk",
"Rock---Rock & Roll",
"Rock---Rockabilly",
"Rock---Shoegaze",
"Rock---Ska",
"Rock---Sludge Metal",
"Rock---Soft Rock",
"Rock---Southern Rock",
"Rock---Space Rock",
"Rock---Speed Metal",
"Rock---Stoner Rock",
"Rock---Surf",
"Rock---Symphonic Rock",
"Rock---Technical Death Metal",
"Rock---Thrash",
"Rock---Twist",
"Rock---Viking Metal",
"Rock---Y\u00e9-Y\u00e9",
"Stage & Screen---Musical",
"Stage & Screen---Score",
"Stage & Screen---Soundtrack",
"Stage & Screen---Theme"
],
"model_types": [
"frozen_model",
"SavedModel",
"onnx"
],
"dataset": {
"name": "Discogs-4M (unreleased)",
"citation": "In-house dataset",
"size": "4M full tracks (3.3M used)",
"metrics": {
"ROC-AUC": 0.95417,
"PR-AUC": 0.20629
}
},
"schema": {
"inputs": [
{
"name": "serving_default_melspectrogram",
"type": "float",
"shape": [
64,
128,
96
]
}
],
"outputs": [
{
"name": "PartitionedCall:0",
"type": "float",
"shape": [
64,
400
],
"op": "Sigmoid",
"output_purpose": "predictions"
},
{
"name": "PartitionedCall:1",
"type": "float",
"shape": [
64,
1280
],
"op": "Flatten",
"output_purpose": "embeddings"
}
]
},
"citation": "@inproceedings{alonso2022music,\n title={Music Representation Learning Based on Editorial Metadata from Discogs},\n author={Alonso-Jim{\\'e}nez, Pablo and Serra, Xavier and Bogdanov, Dmitry},\n booktitle={Conference of the International Society for Music Information Retrieval (ISMIR)},\n year={2022}\n}",
"inference": {
"sample_rate": 16000,
"algorithm": "TensorflowPredictEffnetDiscogs"
}
}

Binary file not shown.

View File

@ -0,0 +1,452 @@
{
"name": "Genre Discogs400",
"type": "Music genre classification",
"link": "https://essentia.upf.edu/models/classification-heads/genre_discogs400/genre_discogs400-discogs-effnet-1.pb",
"version": "1",
"description": "Prediction of 400 music styles in the from the Discogs taxonomy",
"author": "Pablo Alonso",
"email": "pablo.alonso@upf.edu",
"release_date": "2023-05-04",
"framework": "tensorflow",
"framework_version": "2.8.0",
"classes": [
"Blues---Boogie Woogie",
"Blues---Chicago Blues",
"Blues---Country Blues",
"Blues---Delta Blues",
"Blues---Electric Blues",
"Blues---Harmonica Blues",
"Blues---Jump Blues",
"Blues---Louisiana Blues",
"Blues---Modern Electric Blues",
"Blues---Piano Blues",
"Blues---Rhythm & Blues",
"Blues---Texas Blues",
"Brass & Military---Brass Band",
"Brass & Military---Marches",
"Brass & Military---Military",
"Children's---Educational",
"Children's---Nursery Rhymes",
"Children's---Story",
"Classical---Baroque",
"Classical---Choral",
"Classical---Classical",
"Classical---Contemporary",
"Classical---Impressionist",
"Classical---Medieval",
"Classical---Modern",
"Classical---Neo-Classical",
"Classical---Neo-Romantic",
"Classical---Opera",
"Classical---Post-Modern",
"Classical---Renaissance",
"Classical---Romantic",
"Electronic---Abstract",
"Electronic---Acid",
"Electronic---Acid House",
"Electronic---Acid Jazz",
"Electronic---Ambient",
"Electronic---Bassline",
"Electronic---Beatdown",
"Electronic---Berlin-School",
"Electronic---Big Beat",
"Electronic---Bleep",
"Electronic---Breakbeat",
"Electronic---Breakcore",
"Electronic---Breaks",
"Electronic---Broken Beat",
"Electronic---Chillwave",
"Electronic---Chiptune",
"Electronic---Dance-pop",
"Electronic---Dark Ambient",
"Electronic---Darkwave",
"Electronic---Deep House",
"Electronic---Deep Techno",
"Electronic---Disco",
"Electronic---Disco Polo",
"Electronic---Donk",
"Electronic---Downtempo",
"Electronic---Drone",
"Electronic---Drum n Bass",
"Electronic---Dub",
"Electronic---Dub Techno",
"Electronic---Dubstep",
"Electronic---Dungeon Synth",
"Electronic---EBM",
"Electronic---Electro",
"Electronic---Electro House",
"Electronic---Electroclash",
"Electronic---Euro House",
"Electronic---Euro-Disco",
"Electronic---Eurobeat",
"Electronic---Eurodance",
"Electronic---Experimental",
"Electronic---Freestyle",
"Electronic---Future Jazz",
"Electronic---Gabber",
"Electronic---Garage House",
"Electronic---Ghetto",
"Electronic---Ghetto House",
"Electronic---Glitch",
"Electronic---Goa Trance",
"Electronic---Grime",
"Electronic---Halftime",
"Electronic---Hands Up",
"Electronic---Happy Hardcore",
"Electronic---Hard House",
"Electronic---Hard Techno",
"Electronic---Hard Trance",
"Electronic---Hardcore",
"Electronic---Hardstyle",
"Electronic---Hi NRG",
"Electronic---Hip Hop",
"Electronic---Hip-House",
"Electronic---House",
"Electronic---IDM",
"Electronic---Illbient",
"Electronic---Industrial",
"Electronic---Italo House",
"Electronic---Italo-Disco",
"Electronic---Italodance",
"Electronic---Jazzdance",
"Electronic---Juke",
"Electronic---Jumpstyle",
"Electronic---Jungle",
"Electronic---Latin",
"Electronic---Leftfield",
"Electronic---Makina",
"Electronic---Minimal",
"Electronic---Minimal Techno",
"Electronic---Modern Classical",
"Electronic---Musique Concr\u00e8te",
"Electronic---Neofolk",
"Electronic---New Age",
"Electronic---New Beat",
"Electronic---New Wave",
"Electronic---Noise",
"Electronic---Nu-Disco",
"Electronic---Power Electronics",
"Electronic---Progressive Breaks",
"Electronic---Progressive House",
"Electronic---Progressive Trance",
"Electronic---Psy-Trance",
"Electronic---Rhythmic Noise",
"Electronic---Schranz",
"Electronic---Sound Collage",
"Electronic---Speed Garage",
"Electronic---Speedcore",
"Electronic---Synth-pop",
"Electronic---Synthwave",
"Electronic---Tech House",
"Electronic---Tech Trance",
"Electronic---Techno",
"Electronic---Trance",
"Electronic---Tribal",
"Electronic---Tribal House",
"Electronic---Trip Hop",
"Electronic---Tropical House",
"Electronic---UK Garage",
"Electronic---Vaporwave",
"Folk, World, & Country---African",
"Folk, World, & Country---Bluegrass",
"Folk, World, & Country---Cajun",
"Folk, World, & Country---Canzone Napoletana",
"Folk, World, & Country---Catalan Music",
"Folk, World, & Country---Celtic",
"Folk, World, & Country---Country",
"Folk, World, & Country---Fado",
"Folk, World, & Country---Flamenco",
"Folk, World, & Country---Folk",
"Folk, World, & Country---Gospel",
"Folk, World, & Country---Highlife",
"Folk, World, & Country---Hillbilly",
"Folk, World, & Country---Hindustani",
"Folk, World, & Country---Honky Tonk",
"Folk, World, & Country---Indian Classical",
"Folk, World, & Country---La\u00efk\u00f3",
"Folk, World, & Country---Nordic",
"Folk, World, & Country---Pacific",
"Folk, World, & Country---Polka",
"Folk, World, & Country---Ra\u00ef",
"Folk, World, & Country---Romani",
"Folk, World, & Country---Soukous",
"Folk, World, & Country---S\u00e9ga",
"Folk, World, & Country---Volksmusik",
"Folk, World, & Country---Zouk",
"Folk, World, & Country---\u00c9ntekhno",
"Funk / Soul---Afrobeat",
"Funk / Soul---Boogie",
"Funk / Soul---Contemporary R&B",
"Funk / Soul---Disco",
"Funk / Soul---Free Funk",
"Funk / Soul---Funk",
"Funk / Soul---Gospel",
"Funk / Soul---Neo Soul",
"Funk / Soul---New Jack Swing",
"Funk / Soul---P.Funk",
"Funk / Soul---Psychedelic",
"Funk / Soul---Rhythm & Blues",
"Funk / Soul---Soul",
"Funk / Soul---Swingbeat",
"Funk / Soul---UK Street Soul",
"Hip Hop---Bass Music",
"Hip Hop---Boom Bap",
"Hip Hop---Bounce",
"Hip Hop---Britcore",
"Hip Hop---Cloud Rap",
"Hip Hop---Conscious",
"Hip Hop---Crunk",
"Hip Hop---Cut-up/DJ",
"Hip Hop---DJ Battle Tool",
"Hip Hop---Electro",
"Hip Hop---G-Funk",
"Hip Hop---Gangsta",
"Hip Hop---Grime",
"Hip Hop---Hardcore Hip-Hop",
"Hip Hop---Horrorcore",
"Hip Hop---Instrumental",
"Hip Hop---Jazzy Hip-Hop",
"Hip Hop---Miami Bass",
"Hip Hop---Pop Rap",
"Hip Hop---Ragga HipHop",
"Hip Hop---RnB/Swing",
"Hip Hop---Screw",
"Hip Hop---Thug Rap",
"Hip Hop---Trap",
"Hip Hop---Trip Hop",
"Hip Hop---Turntablism",
"Jazz---Afro-Cuban Jazz",
"Jazz---Afrobeat",
"Jazz---Avant-garde Jazz",
"Jazz---Big Band",
"Jazz---Bop",
"Jazz---Bossa Nova",
"Jazz---Contemporary Jazz",
"Jazz---Cool Jazz",
"Jazz---Dixieland",
"Jazz---Easy Listening",
"Jazz---Free Improvisation",
"Jazz---Free Jazz",
"Jazz---Fusion",
"Jazz---Gypsy Jazz",
"Jazz---Hard Bop",
"Jazz---Jazz-Funk",
"Jazz---Jazz-Rock",
"Jazz---Latin Jazz",
"Jazz---Modal",
"Jazz---Post Bop",
"Jazz---Ragtime",
"Jazz---Smooth Jazz",
"Jazz---Soul-Jazz",
"Jazz---Space-Age",
"Jazz---Swing",
"Latin---Afro-Cuban",
"Latin---Bai\u00e3o",
"Latin---Batucada",
"Latin---Beguine",
"Latin---Bolero",
"Latin---Boogaloo",
"Latin---Bossanova",
"Latin---Cha-Cha",
"Latin---Charanga",
"Latin---Compas",
"Latin---Cubano",
"Latin---Cumbia",
"Latin---Descarga",
"Latin---Forr\u00f3",
"Latin---Guaguanc\u00f3",
"Latin---Guajira",
"Latin---Guaracha",
"Latin---MPB",
"Latin---Mambo",
"Latin---Mariachi",
"Latin---Merengue",
"Latin---Norte\u00f1o",
"Latin---Nueva Cancion",
"Latin---Pachanga",
"Latin---Porro",
"Latin---Ranchera",
"Latin---Reggaeton",
"Latin---Rumba",
"Latin---Salsa",
"Latin---Samba",
"Latin---Son",
"Latin---Son Montuno",
"Latin---Tango",
"Latin---Tejano",
"Latin---Vallenato",
"Non-Music---Audiobook",
"Non-Music---Comedy",
"Non-Music---Dialogue",
"Non-Music---Education",
"Non-Music---Field Recording",
"Non-Music---Interview",
"Non-Music---Monolog",
"Non-Music---Poetry",
"Non-Music---Political",
"Non-Music---Promotional",
"Non-Music---Radioplay",
"Non-Music---Religious",
"Non-Music---Spoken Word",
"Pop---Ballad",
"Pop---Bollywood",
"Pop---Bubblegum",
"Pop---Chanson",
"Pop---City Pop",
"Pop---Europop",
"Pop---Indie Pop",
"Pop---J-pop",
"Pop---K-pop",
"Pop---Kay\u014dkyoku",
"Pop---Light Music",
"Pop---Music Hall",
"Pop---Novelty",
"Pop---Parody",
"Pop---Schlager",
"Pop---Vocal",
"Reggae---Calypso",
"Reggae---Dancehall",
"Reggae---Dub",
"Reggae---Lovers Rock",
"Reggae---Ragga",
"Reggae---Reggae",
"Reggae---Reggae-Pop",
"Reggae---Rocksteady",
"Reggae---Roots Reggae",
"Reggae---Ska",
"Reggae---Soca",
"Rock---AOR",
"Rock---Acid Rock",
"Rock---Acoustic",
"Rock---Alternative Rock",
"Rock---Arena Rock",
"Rock---Art Rock",
"Rock---Atmospheric Black Metal",
"Rock---Avantgarde",
"Rock---Beat",
"Rock---Black Metal",
"Rock---Blues Rock",
"Rock---Brit Pop",
"Rock---Classic Rock",
"Rock---Coldwave",
"Rock---Country Rock",
"Rock---Crust",
"Rock---Death Metal",
"Rock---Deathcore",
"Rock---Deathrock",
"Rock---Depressive Black Metal",
"Rock---Doo Wop",
"Rock---Doom Metal",
"Rock---Dream Pop",
"Rock---Emo",
"Rock---Ethereal",
"Rock---Experimental",
"Rock---Folk Metal",
"Rock---Folk Rock",
"Rock---Funeral Doom Metal",
"Rock---Funk Metal",
"Rock---Garage Rock",
"Rock---Glam",
"Rock---Goregrind",
"Rock---Goth Rock",
"Rock---Gothic Metal",
"Rock---Grindcore",
"Rock---Grunge",
"Rock---Hard Rock",
"Rock---Hardcore",
"Rock---Heavy Metal",
"Rock---Indie Rock",
"Rock---Industrial",
"Rock---Krautrock",
"Rock---Lo-Fi",
"Rock---Lounge",
"Rock---Math Rock",
"Rock---Melodic Death Metal",
"Rock---Melodic Hardcore",
"Rock---Metalcore",
"Rock---Mod",
"Rock---Neofolk",
"Rock---New Wave",
"Rock---No Wave",
"Rock---Noise",
"Rock---Noisecore",
"Rock---Nu Metal",
"Rock---Oi",
"Rock---Parody",
"Rock---Pop Punk",
"Rock---Pop Rock",
"Rock---Pornogrind",
"Rock---Post Rock",
"Rock---Post-Hardcore",
"Rock---Post-Metal",
"Rock---Post-Punk",
"Rock---Power Metal",
"Rock---Power Pop",
"Rock---Power Violence",
"Rock---Prog Rock",
"Rock---Progressive Metal",
"Rock---Psychedelic Rock",
"Rock---Psychobilly",
"Rock---Pub Rock",
"Rock---Punk",
"Rock---Rock & Roll",
"Rock---Rockabilly",
"Rock---Shoegaze",
"Rock---Ska",
"Rock---Sludge Metal",
"Rock---Soft Rock",
"Rock---Southern Rock",
"Rock---Space Rock",
"Rock---Speed Metal",
"Rock---Stoner Rock",
"Rock---Surf",
"Rock---Symphonic Rock",
"Rock---Technical Death Metal",
"Rock---Thrash",
"Rock---Twist",
"Rock---Viking Metal",
"Rock---Y\u00e9-Y\u00e9",
"Stage & Screen---Musical",
"Stage & Screen---Score",
"Stage & Screen---Soundtrack",
"Stage & Screen---Theme"
],
"model_types": ["frozen_model", "SavedModel", "onnx"],
"dataset": {
"name": "Discogs-4M (unreleased)",
"citation": "In-house dataset",
"size": "4M full tracks (3.3M used)",
"metrics": {
"ROC-AUC": 0.95417,
"PR-AUC": 0.20629
}
},
"schema": {
"inputs": [
{
"name": "serving_default_model_Placeholder",
"type": "float",
"shape": ["batch_size", 1280]
}
],
"outputs": [
{
"name": "PartitionedCall:0",
"type": "float",
"shape": ["batch_size", 400],
"op": "Sigmoid",
"output_purpose": "predictions"
}
]
},
"citation": "@inproceedings{alonso2022music,\n title={Music Representation Learning Based on Editorial Metadata from Discogs},\n author={Alonso-Jim{\\'e}nez, Pablo and Serra, Xavier and Bogdanov, Dmitry},\n booktitle={Conference of the International Society for Music Information Retrieval (ISMIR)},\n year={2022}\n}",
"inference": {
"sample_rate": 16000,
"algorithm": "TensorflowPredict2D",
"embedding_model": {
"algorithm": "TensorflowPredictEffnetDiscogs",
"model_name": "discogs-effnet-bs64-1",
"link": "https://essentia.upf.edu/models/music-style-classification/discogs-effnet/discogs-effnet-bs64-1.pb"
}
}
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
2026-02-18 08:36:40,339 - WARNING - Nenhum lote encontrado em src_mmpSearch/lotes_yaml

View File

@ -0,0 +1,19 @@
2026-02-18 15:05:50,085 - CLASSIFICADOR - INFO - --- INICIANDO CLASSIFICADOR PEDAGÓGICO (COM AUDITORIA E LOTES) ---
2026-02-18 15:05:50,085 - CLASSIFICADOR - WARNING - Pasta de lotes não encontrada: src_mmpSearch/lotes_yaml
2026-02-18 15:05:50,085 - CLASSIFICADOR - INFO - Buscando arquivos YAML em: src_mmpSearch/lotes_yaml
2026-02-18 15:05:50,086 - CLASSIFICADOR - WARNING - Nenhum arquivo YAML encontrado na pasta de lotes.
2026-02-18 15:05:50,086 - CLASSIFICADOR - INFO - Fallback: Carregando arquivo único all.yml
2026-02-18 15:07:01,534 - CLASSIFICADOR - WARNING - Arquivo de áudio db_final_completo.json não encontrado.
2026-02-18 15:07:01,534 - CLASSIFICADOR - INFO - Processando 108 projetos...
2026-02-18 15:07:01,535 - CLASSIFICADOR - INFO - --- MARCO: 1 projetos processados em 0:01:11 ---
2026-02-18 15:07:01,538 - CLASSIFICADOR - INFO - --- MARCO: 10 projetos processados em 0:01:11 ---
2026-02-18 15:07:01,562 - CLASSIFICADOR - INFO - --- MARCO: 100 projetos processados em 0:01:11 ---
2026-02-18 15:07:01,564 - CLASSIFICADOR - INFO - Salvando JSON Final: src_mmpSearch/saida_analises/db_projetos_classificados.json
2026-02-18 15:07:01,570 - CLASSIFICADOR - INFO - --- RELATÓRIO FINAL ---
2026-02-18 15:07:01,570 - CLASSIFICADOR - INFO - Tempo Total de Execução: 0:01:11
2026-02-18 15:07:01,570 - CLASSIFICADOR - INFO - Total Processado: 107
2026-02-18 15:07:01,570 - CLASSIFICADOR - INFO - Iniciantes: 107
2026-02-18 15:07:01,570 - CLASSIFICADOR - INFO - Intermediários: 0
2026-02-18 15:07:01,570 - CLASSIFICADOR - INFO - Avançados: 0
2026-02-18 15:07:01,570 - CLASSIFICADOR - INFO - Taxa de Casamento com Áudio: 0/0
2026-02-18 15:07:01,570 - CLASSIFICADOR - INFO - Auditoria salva em: src_mmpSearch/saida_analises/audit_classificacao_pedagogica.csv

View File

@ -0,0 +1,108 @@
Timestamp,ID_Projeto,Audio_Encontrado,Qtd_Secoes,Nivel,Genero_Final,Tempo_Proc_ms,RAM_Uso_MB,Tags
15:07:01,trap-fyrebreak-vanguard-original-mix,NAO,0,Iniciante,Electronic (Rule-based),0.167,548.47,Loop Estático
15:07:01,trap-vespertine-mthrfckr,NAO,0,Iniciante,Electronic (Rule-based),0.095,548.47,Loop Estático
15:07:01,hybrid-trap-vespertine-headshot,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.141,548.47,Loop Estático
15:07:01,big-room-vespertine-bigfoot,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.124,548.47,Loop Estático
15:07:01,43yu,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.072,548.47,Loop Estático
15:07:01,umltovruml-the-last-night,NAO,0,Iniciante,Electronic (Rule-based),0.080,548.47,Loop Estático
15:07:01,future-bass-vespertine-spectrum,NAO,0,Iniciante,Electronic (Rule-based),0.114,548.47,Loop Estático
15:07:01,dubstep-fyrebreak-saichania-original-mix,NAO,0,Iniciante,Electronic (Rule-based),0.161,548.47,Loop Estático
15:07:01,dubstep-vespertine-murdah,NAO,0,Iniciante,Electronic (Rule-based),0.161,548.47,Loop Estático
15:07:01,hip-hop-vespertine-where-it-ends,NAO,0,Iniciante,Electronic (Rule-based),0.072,548.47,Loop Estático
15:07:01,trap-vespertine-avada-kedavra,NAO,0,Iniciante,Electronic (Rule-based),0.095,548.47,Loop Estático
15:07:01,ysysysysysy,NAO,0,Iniciante,Electronic (Rule-based),0.061,548.47,Loop Estático
15:07:01,trap-vespertine-betrayal,NAO,0,Iniciante,Electronic (Rule-based),0.087,548.47,Loop Estático
15:07:01,horror-trap-vespertine-haunted,NAO,0,Iniciante,Electronic (Rule-based),0.182,548.47,Loop Estático
15:07:01,future-bass-vespertine-ft-stephanie-kay-dont-need-you,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.169,548.47,Loop Estático
15:07:01,blue-nights,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.139,548.47,Boombap Groove|Loop Estático
15:07:01,porch-swing,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.056,548.47,Boombap Groove|Loop Estático
15:07:01,reel-2-real-i-like-to-move-it-dj-ayz-edit,NAO,0,Iniciante,Electronic (Rule-based),0.036,548.47,Loop Estático
15:07:01,demo-aesthetescence,NAO,0,Iniciante,Electronic (Rule-based),0.041,548.47,Loop Estático
15:07:01,estagiodocencia1,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.026,548.47,Boombap Groove|Loop Estático
15:07:01,remix-shape-of-you-by-clackster-dimitrion,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.106,548.47,Boombap Groove|Loop Estático
15:07:01,progressive-house-popsip-electric-dancer-vortexsupernova-edit,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.120,548.47,Loop Estático
15:07:01,future-house-vespertine-checkpoint,NAO,0,Iniciante,Electronic (Rule-based),0.104,548.47,Loop Estático
15:07:01,future-house-vespertine-people-on-the-dancefloor,NAO,0,Iniciante,Electronic (Rule-based),0.102,548.47,Loop Estático
15:07:01,piano-remake-the-weeknd-the-hills,NAO,0,Iniciante,Electronic (Rule-based),0.052,548.47,Loop Estático
15:07:01,future-house-vespertine-opulent,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.079,548.47,Loop Estático
15:07:01,the-riddle-amandyte,NAO,0,Iniciante,Electronic (Rule-based),0.089,548.47,Loop Estático
15:07:01,ideg-ie-ideg-2,NAO,0,Iniciante,Electronic (Rule-based),0.045,548.47,Loop Estático
15:07:01,boss-fights-breaking-antagonist,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.047,548.47,Loop Estático
15:07:01,beryl,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.025,548.47,Boombap Groove|Loop Estático
15:07:01,melodic-dubstepazl-alonefeat-elation,NAO,0,Iniciante,Electronic (Rule-based),0.100,548.47,Loop Estático
15:07:01,trap-leche-baggage,NAO,0,Iniciante,Electronic (Rule-based),0.064,548.47,Loop Estático
15:07:01,hardstyle-vespertine-destiny,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.082,548.47,Loop Estático
15:07:01,advait,NAO,0,Iniciante,Electronic (Rule-based),0.088,548.47,Loop Estático
15:07:01,dreamhop-animal-l-bonus-r0und-ep,NAO,0,Iniciante,Electronic (Rule-based),0.163,548.47,Loop Estático
15:07:01,big-room-vespertine-wendigo,NAO,0,Iniciante,Electronic (Rule-based),0.118,548.47,Loop Estático
15:07:01,big-room-vespertine-the-ultimate,NAO,0,Iniciante,Electronic (Rule-based),0.066,548.47,Loop Estático
15:07:01,calvin-harris-summer,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.150,548.47,Loop Estático
15:07:01,assault,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.090,548.47,Boombap Groove|Loop Estático
15:07:01,progressive-house-vespertine-ft-xenia-fischer-thomas-linkwald-feel-the-sun,NAO,0,Iniciante,Electronic (Rule-based),0.087,548.47,Loop Estático
15:07:01,chiptune-1,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.112,548.47,Boombap Groove|Loop Estático
15:07:01,trap-bonnie-x-clyde-in-the-city-vespertine-remix,NAO,0,Iniciante,Electronic (Rule-based),0.101,548.47,Loop Estático
15:07:01,prodplue-trap-beat,NAO,0,Iniciante,Electronic (Rule-based),0.039,548.47,Loop Estático
15:07:01,by-gagansingh1-instagram,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.085,548.47,Boombap Groove|Loop Estático
15:07:01,house-vespertine-miguel-our-dream,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.094,548.47,Loop Estático
15:07:01,big-room-vespertine-squad-goals,NAO,0,Iniciante,Electronic (Rule-based),0.109,548.47,Loop Estático
15:07:01,drake,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.025,548.47,Loop Estático
15:07:01,psy-trance-vespertine-ufo,NAO,0,Iniciante,Electronic (Rule-based),0.069,548.47,Loop Estático
15:07:01,doideirinha,NAO,0,Iniciante,Electronic (Rule-based),0.028,548.47,Loop Estático
15:07:01,2019winter-song,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.082,548.47,Loop Estático
15:07:01,unfinished-another-world-future-bass,NAO,0,Iniciante,Electronic (Rule-based),0.100,548.47,Loop Estático
15:07:01,bass-house-vespertine-vortex,NAO,0,Iniciante,Electronic (Rule-based),0.122,548.47,Loop Estático
15:07:01,drifting-rev-d-csammis-track-1,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.056,548.47,Loop Estático
15:07:01,big-room-vespertine-warrior,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.145,548.47,Loop Estático
15:07:01,all-we-know,NAO,0,Iniciante,Electronic (Rule-based),0.182,548.47,Loop Estático
15:07:01,classical-sample-of-a-melodic-music-lmms,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.087,548.47,Boombap Groove|Loop Estático
15:07:01,sect02,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.110,548.47,Loop Estático
15:07:01,big-room-vespertine-witchcraft,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.112,548.47,Loop Estático
15:07:01,210424,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.024,548.47,Boombap Groove|Loop Estático
15:07:01,hybrid-trap-vespertine-fck-you,NAO,0,Iniciante,Electronic (Rule-based),0.130,548.47,Loop Estático
15:07:01,around-to-green,NAO,0,Iniciante,Electronic (Rule-based),0.061,548.47,Loop Estático
15:07:01,hardcore-vespertine-payback,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.098,548.47,Loop Estático
15:07:01,lo-fi-beat-bubblegum,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.118,548.47,Boombap Groove|Loop Estático
15:07:01,hjkl,NAO,0,Iniciante,Electronic (Rule-based),0.068,548.47,Loop Estático
15:07:01,evolutionthreading-two,NAO,0,Iniciante,Electronic (Rule-based),0.082,548.47,Loop Estático
15:07:01,frenchcore-vespertine-merde-not-finished,NAO,0,Iniciante,Electronic (Rule-based),0.049,548.47,Loop Estático
15:07:01,trap-vespertine-acab,NAO,0,Iniciante,Electronic (Rule-based),0.125,548.47,Loop Estático
15:07:01,trap-vespertine-predator,NAO,0,Iniciante,Electronic (Rule-based),0.090,548.47,Loop Estático
15:07:01,calvin-harris-im-not-alone,NAO,0,Iniciante,Electronic (Rule-based),0.111,548.47,Loop Estático
15:07:01,trap-leches-future-bass-drop-clackster-remix,NAO,0,Iniciante,Electronic (Rule-based),0.083,548.47,Loop Estático
15:07:01,traurig,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.113,548.47,Loop Estático
15:07:01,titulo-incorreto-1,NAO,0,Iniciante,Electronic (Rule-based),0.022,548.47,Loop Estático
15:07:01,dr-wily-theme,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.063,548.47,Loop Estático
15:07:01,remixtrap-n,NAO,0,Iniciante,Electronic (Rule-based),0.074,548.47,Loop Estático
15:07:01,free-playboi-carti-x-lil-uzi-vert-type-beat-flexin-prod-hxrperr,NAO,0,Iniciante,Electronic (Rule-based),0.032,548.47,Loop Estático
15:07:01,lo-fi-rum-rage,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.079,548.47,Boombap Groove|Loop Estático
15:07:01,future-bassopen-your-eyesraiel-version,NAO,0,Iniciante,Electronic (Rule-based),0.101,548.47,Loop Estático
15:07:01,electronic-vortexsupernova-headache-smash-120,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.101,548.47,Loop Estático
15:07:01,remix-jordankyser-reactive-to-drops,NAO,0,Iniciante,Electronic (Rule-based),0.163,548.47,Loop Estático
15:07:01,trap-remix-triple-ocillator-trap-song,NAO,0,Iniciante,Electronic (Rule-based),0.035,548.47,Loop Estático
15:07:01,ideg-ie-ideg,NAO,0,Iniciante,Electronic (Rule-based),0.041,548.47,Loop Estático
15:07:01,4r3st,NAO,0,Iniciante,Electronic (Rule-based),0.046,548.47,Loop Estático
15:07:01,bop-in,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.081,548.47,Loop Estático
15:07:01,new-yellow-claw-type-song,NAO,0,Iniciante,Electronic (Rule-based),0.040,548.47,Loop Estático
15:07:01,dubstep-fyrebreak-the-summit,NAO,0,Iniciante,Electronic (Rule-based),0.093,548.47,Loop Estático
15:07:01,alan-walker-the-spectre,NAO,0,Iniciante,Electronic (Rule-based),0.156,548.47,Loop Estático
15:07:01,trap-vespertine-genocide,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.145,548.47,Loop Estático
15:07:01,7-is-the-answer-060224,NAO,0,Iniciante,Electronic (Rule-based),0.093,548.47,Loop Estático
15:07:01,melodies-free-to-use-1,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.074,548.47,Boombap Groove|Loop Estático
15:07:01,traptwinshot,NAO,0,Iniciante,Electronic (Rule-based),0.027,548.47,Loop Estático
15:07:01,bootleg-remix-deorro-five-hours-swedish-house-mafia-the-weeknd-moth-to-a-flame,NAO,0,Iniciante,Electronic (Rule-based),0.046,548.47,Loop Estático
15:07:01,deep-house-vespertine-feat-georg-no-days-off,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.111,548.47,Loop Estático
15:07:01,melodic-dubstep-xcalibur-entwined,NAO,0,Iniciante,Electronic (Rule-based),0.147,548.47,Loop Estático
15:07:01,screams,NAO,0,Iniciante,Electronic (Rule-based),0.126,548.47,Loop Estático
15:07:01,orin-new-year-original-mix,NAO,0,Iniciante,Electronic (Rule-based),0.109,548.47,Loop Estático
15:07:01,synthpop-xcalibur-retrospect,NAO,0,Iniciante,Electronic (Rule-based),0.041,548.47,Loop Estático
15:07:01,melodic-dubstep-galantis-runaway-u-i-xcalibur-remix,NAO,0,Iniciante,Electronic (Rule-based),0.026,548.47,Loop Estático
15:07:01,hardcore-vespertine-immortal,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.095,548.47,Loop Estático
15:07:01,live,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.056,548.47,Boombap Groove|Loop Estático
15:07:01,bigbadguy,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.023,548.47,Boombap Groove|Loop Estático
15:07:01,aelig,NAO,0,Iniciante,Electronic (Rule-based),0.035,548.47,Loop Estático
15:07:01,melbourne-bounce-vespertine-bounce-generation,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.159,548.47,Loop Estático
15:07:01,animestep-chaotic-growls-anime-wubs,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.059,548.47,Loop Estático
15:07:01,hardstyle-vespertine-symphony-of-bass,NAO,0,Iniciante,Electronic (Rule-based),0.068,548.47,Loop Estático
15:07:01,melbourne-bounce-vespertine-maze,NAO,0,Iniciante,Electronic (Rule-based),0.045,548.47,Loop Estático
15:07:01,future-bass-ash-space-vespertine-remix,NAO,0,Iniciante,Electronic (Rule-based),0.068,548.47,Loop Estático
15:07:01,progressive-house-vespertine-limerence,NAO,0,Iniciante,HipHop/Downtempo (Rule-based),0.100,548.47,Loop Estático
1 Timestamp ID_Projeto Audio_Encontrado Qtd_Secoes Nivel Genero_Final Tempo_Proc_ms RAM_Uso_MB Tags
2 15:07:01 trap-fyrebreak-vanguard-original-mix NAO 0 Iniciante Electronic (Rule-based) 0.167 548.47 Loop Estático
3 15:07:01 trap-vespertine-mthrfckr NAO 0 Iniciante Electronic (Rule-based) 0.095 548.47 Loop Estático
4 15:07:01 hybrid-trap-vespertine-headshot NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.141 548.47 Loop Estático
5 15:07:01 big-room-vespertine-bigfoot NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.124 548.47 Loop Estático
6 15:07:01 43yu NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.072 548.47 Loop Estático
7 15:07:01 umltovruml-the-last-night NAO 0 Iniciante Electronic (Rule-based) 0.080 548.47 Loop Estático
8 15:07:01 future-bass-vespertine-spectrum NAO 0 Iniciante Electronic (Rule-based) 0.114 548.47 Loop Estático
9 15:07:01 dubstep-fyrebreak-saichania-original-mix NAO 0 Iniciante Electronic (Rule-based) 0.161 548.47 Loop Estático
10 15:07:01 dubstep-vespertine-murdah NAO 0 Iniciante Electronic (Rule-based) 0.161 548.47 Loop Estático
11 15:07:01 hip-hop-vespertine-where-it-ends NAO 0 Iniciante Electronic (Rule-based) 0.072 548.47 Loop Estático
12 15:07:01 trap-vespertine-avada-kedavra NAO 0 Iniciante Electronic (Rule-based) 0.095 548.47 Loop Estático
13 15:07:01 ysysysysysy NAO 0 Iniciante Electronic (Rule-based) 0.061 548.47 Loop Estático
14 15:07:01 trap-vespertine-betrayal NAO 0 Iniciante Electronic (Rule-based) 0.087 548.47 Loop Estático
15 15:07:01 horror-trap-vespertine-haunted NAO 0 Iniciante Electronic (Rule-based) 0.182 548.47 Loop Estático
16 15:07:01 future-bass-vespertine-ft-stephanie-kay-dont-need-you NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.169 548.47 Loop Estático
17 15:07:01 blue-nights NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.139 548.47 Boombap Groove|Loop Estático
18 15:07:01 porch-swing NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.056 548.47 Boombap Groove|Loop Estático
19 15:07:01 reel-2-real-i-like-to-move-it-dj-ayz-edit NAO 0 Iniciante Electronic (Rule-based) 0.036 548.47 Loop Estático
20 15:07:01 demo-aesthetescence NAO 0 Iniciante Electronic (Rule-based) 0.041 548.47 Loop Estático
21 15:07:01 estagiodocencia1 NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.026 548.47 Boombap Groove|Loop Estático
22 15:07:01 remix-shape-of-you-by-clackster-dimitrion NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.106 548.47 Boombap Groove|Loop Estático
23 15:07:01 progressive-house-popsip-electric-dancer-vortexsupernova-edit NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.120 548.47 Loop Estático
24 15:07:01 future-house-vespertine-checkpoint NAO 0 Iniciante Electronic (Rule-based) 0.104 548.47 Loop Estático
25 15:07:01 future-house-vespertine-people-on-the-dancefloor NAO 0 Iniciante Electronic (Rule-based) 0.102 548.47 Loop Estático
26 15:07:01 piano-remake-the-weeknd-the-hills NAO 0 Iniciante Electronic (Rule-based) 0.052 548.47 Loop Estático
27 15:07:01 future-house-vespertine-opulent NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.079 548.47 Loop Estático
28 15:07:01 the-riddle-amandyte NAO 0 Iniciante Electronic (Rule-based) 0.089 548.47 Loop Estático
29 15:07:01 ideg-ie-ideg-2 NAO 0 Iniciante Electronic (Rule-based) 0.045 548.47 Loop Estático
30 15:07:01 boss-fights-breaking-antagonist NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.047 548.47 Loop Estático
31 15:07:01 beryl NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.025 548.47 Boombap Groove|Loop Estático
32 15:07:01 melodic-dubstepazl-alonefeat-elation NAO 0 Iniciante Electronic (Rule-based) 0.100 548.47 Loop Estático
33 15:07:01 trap-leche-baggage NAO 0 Iniciante Electronic (Rule-based) 0.064 548.47 Loop Estático
34 15:07:01 hardstyle-vespertine-destiny NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.082 548.47 Loop Estático
35 15:07:01 advait NAO 0 Iniciante Electronic (Rule-based) 0.088 548.47 Loop Estático
36 15:07:01 dreamhop-animal-l-bonus-r0und-ep NAO 0 Iniciante Electronic (Rule-based) 0.163 548.47 Loop Estático
37 15:07:01 big-room-vespertine-wendigo NAO 0 Iniciante Electronic (Rule-based) 0.118 548.47 Loop Estático
38 15:07:01 big-room-vespertine-the-ultimate NAO 0 Iniciante Electronic (Rule-based) 0.066 548.47 Loop Estático
39 15:07:01 calvin-harris-summer NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.150 548.47 Loop Estático
40 15:07:01 assault NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.090 548.47 Boombap Groove|Loop Estático
41 15:07:01 progressive-house-vespertine-ft-xenia-fischer-thomas-linkwald-feel-the-sun NAO 0 Iniciante Electronic (Rule-based) 0.087 548.47 Loop Estático
42 15:07:01 chiptune-1 NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.112 548.47 Boombap Groove|Loop Estático
43 15:07:01 trap-bonnie-x-clyde-in-the-city-vespertine-remix NAO 0 Iniciante Electronic (Rule-based) 0.101 548.47 Loop Estático
44 15:07:01 prodplue-trap-beat NAO 0 Iniciante Electronic (Rule-based) 0.039 548.47 Loop Estático
45 15:07:01 by-gagansingh1-instagram NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.085 548.47 Boombap Groove|Loop Estático
46 15:07:01 house-vespertine-miguel-our-dream NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.094 548.47 Loop Estático
47 15:07:01 big-room-vespertine-squad-goals NAO 0 Iniciante Electronic (Rule-based) 0.109 548.47 Loop Estático
48 15:07:01 drake NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.025 548.47 Loop Estático
49 15:07:01 psy-trance-vespertine-ufo NAO 0 Iniciante Electronic (Rule-based) 0.069 548.47 Loop Estático
50 15:07:01 doideirinha NAO 0 Iniciante Electronic (Rule-based) 0.028 548.47 Loop Estático
51 15:07:01 2019winter-song NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.082 548.47 Loop Estático
52 15:07:01 unfinished-another-world-future-bass NAO 0 Iniciante Electronic (Rule-based) 0.100 548.47 Loop Estático
53 15:07:01 bass-house-vespertine-vortex NAO 0 Iniciante Electronic (Rule-based) 0.122 548.47 Loop Estático
54 15:07:01 drifting-rev-d-csammis-track-1 NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.056 548.47 Loop Estático
55 15:07:01 big-room-vespertine-warrior NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.145 548.47 Loop Estático
56 15:07:01 all-we-know NAO 0 Iniciante Electronic (Rule-based) 0.182 548.47 Loop Estático
57 15:07:01 classical-sample-of-a-melodic-music-lmms NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.087 548.47 Boombap Groove|Loop Estático
58 15:07:01 sect02 NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.110 548.47 Loop Estático
59 15:07:01 big-room-vespertine-witchcraft NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.112 548.47 Loop Estático
60 15:07:01 210424 NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.024 548.47 Boombap Groove|Loop Estático
61 15:07:01 hybrid-trap-vespertine-fck-you NAO 0 Iniciante Electronic (Rule-based) 0.130 548.47 Loop Estático
62 15:07:01 around-to-green NAO 0 Iniciante Electronic (Rule-based) 0.061 548.47 Loop Estático
63 15:07:01 hardcore-vespertine-payback NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.098 548.47 Loop Estático
64 15:07:01 lo-fi-beat-bubblegum NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.118 548.47 Boombap Groove|Loop Estático
65 15:07:01 hjkl NAO 0 Iniciante Electronic (Rule-based) 0.068 548.47 Loop Estático
66 15:07:01 evolutionthreading-two NAO 0 Iniciante Electronic (Rule-based) 0.082 548.47 Loop Estático
67 15:07:01 frenchcore-vespertine-merde-not-finished NAO 0 Iniciante Electronic (Rule-based) 0.049 548.47 Loop Estático
68 15:07:01 trap-vespertine-acab NAO 0 Iniciante Electronic (Rule-based) 0.125 548.47 Loop Estático
69 15:07:01 trap-vespertine-predator NAO 0 Iniciante Electronic (Rule-based) 0.090 548.47 Loop Estático
70 15:07:01 calvin-harris-im-not-alone NAO 0 Iniciante Electronic (Rule-based) 0.111 548.47 Loop Estático
71 15:07:01 trap-leches-future-bass-drop-clackster-remix NAO 0 Iniciante Electronic (Rule-based) 0.083 548.47 Loop Estático
72 15:07:01 traurig NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.113 548.47 Loop Estático
73 15:07:01 titulo-incorreto-1 NAO 0 Iniciante Electronic (Rule-based) 0.022 548.47 Loop Estático
74 15:07:01 dr-wily-theme NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.063 548.47 Loop Estático
75 15:07:01 remixtrap-n NAO 0 Iniciante Electronic (Rule-based) 0.074 548.47 Loop Estático
76 15:07:01 free-playboi-carti-x-lil-uzi-vert-type-beat-flexin-prod-hxrperr NAO 0 Iniciante Electronic (Rule-based) 0.032 548.47 Loop Estático
77 15:07:01 lo-fi-rum-rage NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.079 548.47 Boombap Groove|Loop Estático
78 15:07:01 future-bassopen-your-eyesraiel-version NAO 0 Iniciante Electronic (Rule-based) 0.101 548.47 Loop Estático
79 15:07:01 electronic-vortexsupernova-headache-smash-120 NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.101 548.47 Loop Estático
80 15:07:01 remix-jordankyser-reactive-to-drops NAO 0 Iniciante Electronic (Rule-based) 0.163 548.47 Loop Estático
81 15:07:01 trap-remix-triple-ocillator-trap-song NAO 0 Iniciante Electronic (Rule-based) 0.035 548.47 Loop Estático
82 15:07:01 ideg-ie-ideg NAO 0 Iniciante Electronic (Rule-based) 0.041 548.47 Loop Estático
83 15:07:01 4r3st NAO 0 Iniciante Electronic (Rule-based) 0.046 548.47 Loop Estático
84 15:07:01 bop-in NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.081 548.47 Loop Estático
85 15:07:01 new-yellow-claw-type-song NAO 0 Iniciante Electronic (Rule-based) 0.040 548.47 Loop Estático
86 15:07:01 dubstep-fyrebreak-the-summit NAO 0 Iniciante Electronic (Rule-based) 0.093 548.47 Loop Estático
87 15:07:01 alan-walker-the-spectre NAO 0 Iniciante Electronic (Rule-based) 0.156 548.47 Loop Estático
88 15:07:01 trap-vespertine-genocide NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.145 548.47 Loop Estático
89 15:07:01 7-is-the-answer-060224 NAO 0 Iniciante Electronic (Rule-based) 0.093 548.47 Loop Estático
90 15:07:01 melodies-free-to-use-1 NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.074 548.47 Boombap Groove|Loop Estático
91 15:07:01 traptwinshot NAO 0 Iniciante Electronic (Rule-based) 0.027 548.47 Loop Estático
92 15:07:01 bootleg-remix-deorro-five-hours-swedish-house-mafia-the-weeknd-moth-to-a-flame NAO 0 Iniciante Electronic (Rule-based) 0.046 548.47 Loop Estático
93 15:07:01 deep-house-vespertine-feat-georg-no-days-off NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.111 548.47 Loop Estático
94 15:07:01 melodic-dubstep-xcalibur-entwined NAO 0 Iniciante Electronic (Rule-based) 0.147 548.47 Loop Estático
95 15:07:01 screams NAO 0 Iniciante Electronic (Rule-based) 0.126 548.47 Loop Estático
96 15:07:01 orin-new-year-original-mix NAO 0 Iniciante Electronic (Rule-based) 0.109 548.47 Loop Estático
97 15:07:01 synthpop-xcalibur-retrospect NAO 0 Iniciante Electronic (Rule-based) 0.041 548.47 Loop Estático
98 15:07:01 melodic-dubstep-galantis-runaway-u-i-xcalibur-remix NAO 0 Iniciante Electronic (Rule-based) 0.026 548.47 Loop Estático
99 15:07:01 hardcore-vespertine-immortal NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.095 548.47 Loop Estático
100 15:07:01 live NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.056 548.47 Boombap Groove|Loop Estático
101 15:07:01 bigbadguy NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.023 548.47 Boombap Groove|Loop Estático
102 15:07:01 aelig NAO 0 Iniciante Electronic (Rule-based) 0.035 548.47 Loop Estático
103 15:07:01 melbourne-bounce-vespertine-bounce-generation NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.159 548.47 Loop Estático
104 15:07:01 animestep-chaotic-growls-anime-wubs NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.059 548.47 Loop Estático
105 15:07:01 hardstyle-vespertine-symphony-of-bass NAO 0 Iniciante Electronic (Rule-based) 0.068 548.47 Loop Estático
106 15:07:01 melbourne-bounce-vespertine-maze NAO 0 Iniciante Electronic (Rule-based) 0.045 548.47 Loop Estático
107 15:07:01 future-bass-ash-space-vespertine-remix NAO 0 Iniciante Electronic (Rule-based) 0.068 548.47 Loop Estático
108 15:07:01 progressive-house-vespertine-limerence NAO 0 Iniciante HipHop/Downtempo (Rule-based) 0.100 548.47 Loop Estático

View File

@ -0,0 +1 @@
timestamp,arquivo,tamanho_mb,duracao_audio_s,tempo_proc_s,ram_uso_mb,cpu_percent
1 timestamp arquivo tamanho_mb duracao_audio_s tempo_proc_s ram_uso_mb cpu_percent

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,339 @@
# benchmark.py
import os
import shutil
import subprocess
import multiprocessing
import logging
import time
import platform
import sys
import re
import unicodedata
import csv
import psutil
from datetime import datetime, timedelta
# Importando apenas o necessário (sem sobrescrever as pastas do utils nativo no processamento global)
from file_parser import parse_mmp_file
from file_saver import save_to_json, save_to_yaml
from utils import SRC_MMPSEARCH, MMP_FOLDER
# === ISOLAMENTO DE DIRETÓRIOS PARA O BENCHMARK ===
# Tudo será gerado dentro de "benchmark_output" para não afetar o Jekyll
BENCHMARK_OUT = os.path.join(SRC_MMPSEARCH, "benchmark_output")
DATA_FOLDER = os.path.join(BENCHMARK_OUT, "data")
METADATA_FOLDER = os.path.join(BENCHMARK_OUT, "metadata")
WAV_FOLDER = os.path.join(BENCHMARK_OUT, "wav")
MMPZ_FOLDER = os.path.join(BENCHMARK_OUT, "mmpz")
LOG_FOLDER = os.path.join(BENCHMARK_OUT, "logs")
SAIDA_ANALISES = os.path.join(BENCHMARK_OUT, "analises")
LOTES_FOLDER = os.path.join(BENCHMARK_OUT, "lotes_yaml")
# === CONFIGURAÇÕES GERAIS ===
TIMEOUT_RENDER_SECONDS = 300
MIN_RAM_FREE_MB = 500
global_counter = None
def slugify(value):
value = str(value)
value = (
unicodedata.normalize("NFKD", value).encode("ascii", "ignore").decode("ascii")
)
value = value.lower()
value = re.sub(r"[^\w\s-]", "", value)
value = re.sub(r"[-\s_]+", "-", value)
return value.strip("-_")
def get_cpu_safe_count():
try:
count = multiprocessing.cpu_count()
return max(1, count - 1)
except:
return 1
def init_worker(counter):
global global_counter
global_counter = counter
# === FUNÇÃO ORIGINAL DO SEU CÓDIGO (Com leve ajuste de segurança de arquivos) ===
def process_single_file(args):
file_name, clean_slug, total_files = args
file_path = os.path.join(MMP_FOLDER, file_name)
pid = os.getpid()
global global_counter
current_idx = 0
if global_counter:
with global_counter.get_lock():
global_counter.value += 1
current_idx = global_counter.value
start_time = time.time()
result = {
"success": False,
"file": file_name,
"slug": clean_slug,
"data": None,
"error": None,
"duration_s": 0.0,
"ram_mb": 0.0,
"file_size_mb": 0.0,
}
try:
if os.path.exists(file_path):
result["file_size_mb"] = os.path.getsize(file_path) / (1024 * 1024)
mem = psutil.virtual_memory()
if (mem.available / (1024 * 1024)) < MIN_RAM_FREE_MB:
result["error"] = "RAM Insuficiente"
return result
original_base_name = os.path.splitext(file_name)[0]
ogg_name = clean_slug + ".ogg"
json_name = clean_slug + ".json"
yml_name = clean_slug + ".yml"
target_mmp_path = ""
if file_name.endswith(".mmpz"):
destination_path = os.path.join(MMPZ_FOLDER, file_name)
# ALERTA DE BENCHMARK: Trocado move por copy2 para não destruir o dataset de testes!
if not os.path.exists(destination_path):
shutil.copy2(file_path, destination_path)
mmp_temp_name = clean_slug + ".mmp"
output_mmp_path = os.path.join(MMP_FOLDER, mmp_temp_name)
abs_dest = os.path.abspath(destination_path)
abs_mmp_out = os.path.abspath(output_mmp_path)
with open(abs_mmp_out, "w") as outfile:
subprocess.run(
["lmms", "--dump", abs_dest],
stdout=outfile,
stderr=subprocess.PIPE,
check=True,
timeout=60,
env={"QT_QPA_PLATFORM": "offscreen", **os.environ},
)
target_mmp_path = output_mmp_path
elif file_name.endswith(".mmp"):
target_mmp_path = file_path
# Renderização do Áudio
if os.path.exists(target_mmp_path):
abs_target_mmp = os.path.abspath(target_mmp_path)
abs_ogg_out = os.path.abspath(os.path.join(WAV_FOLDER, ogg_name))
# Aqui estava o errinho do "wav_cmd" que arrumamos na revisão!
ogg_cmd = ["lmms", "-r", abs_target_mmp, "-o", abs_ogg_out, "-f", "ogg"]
render_process = subprocess.run(
ogg_cmd,
check=False,
capture_output=True,
text=True,
timeout=TIMEOUT_RENDER_SECONDS,
env={"QT_QPA_PLATFORM": "offscreen", **os.environ},
)
if render_process.returncode != 0:
raise subprocess.CalledProcessError(
render_process.returncode, "lmms render", stderr="Erro LMMS"
)
# Parsing do XML
if os.path.exists(target_mmp_path):
mmp_data = parse_mmp_file(target_mmp_path)
if mmp_data:
mmp_data["file"] = clean_slug
mmp_data["original_title"] = original_base_name
save_to_json(mmp_data, os.path.join(METADATA_FOLDER, json_name))
save_to_yaml(mmp_data, os.path.join(DATA_FOLDER, yml_name))
result["success"] = True
result["data"] = mmp_data
try:
process = psutil.Process(pid)
# rss = Resident Set Size (RAM real usada pelo processo)
result["ram_mb"] = process.memory_info().rss / (1024 * 1024)
except:
result["ram_mb"] = 0.0
except Exception as e:
result["error"] = str(e)
result["duration_s"] = time.time() - start_time
return result
# === MOTOR DE BENCHMARK ===
def execute_batch(limit_files, mode):
"""
Executa um lote de arquivos.
Se mode == 'sequencial', usa apenas 1 núcleo.
Se mode == 'paralelo', usa o safe count de núcleos.
"""
start_time_global = time.time()
# Prepara pastas limpas para o teste
for folder in [
MMPZ_FOLDER,
WAV_FOLDER,
METADATA_FOLDER,
DATA_FOLDER,
LOTES_FOLDER,
SAIDA_ANALISES,
]:
os.makedirs(folder, exist_ok=True)
all_files_raw = [f for f in os.listdir(MMP_FOLDER) if f.endswith((".mmp", ".mmpz"))]
if limit_files > 0:
all_files_raw = all_files_raw[:limit_files]
total_files = len(all_files_raw)
if total_files == 0:
return 0, 0, 0, 0, 0
shared_counter = multiprocessing.Value("i", 0)
tasks = []
for idx, file_name in enumerate(all_files_raw):
slug = slugify(os.path.splitext(file_name)[0])
if not slug:
slug = f"proj-{idx}"
tasks.append((file_name, slug, total_files))
num_cores = 1 if mode == "sequencial" else get_cpu_safe_count()
print(
f"\n[{mode.upper()}] Processando {total_files} arquivos com {num_cores} núcleo(s)..."
)
# Execução
with multiprocessing.Pool(
processes=num_cores, initializer=init_worker, initargs=(shared_counter,)
) as pool:
results = pool.map(process_single_file, tasks)
# --- CÁLCULO DE MÉDIAS E SUCESSOS ---
sucessos = [r for r in results if r["success"]]
qtd_sucessos = len(sucessos)
avg_time = (
sum(r["duration_s"] for r in sucessos) / qtd_sucessos if qtd_sucessos > 0 else 0
)
avg_size = (
sum(r["file_size_mb"] for r in sucessos) / qtd_sucessos
if qtd_sucessos > 0
else 0
)
avg_ram = (
sum(r["ram_mb"] for r in sucessos) / qtd_sucessos if qtd_sucessos > 0 else 0
)
duration_total = time.time() - start_time_global
# --- SALVAR RELATÓRIO DETALHADO POR LOTE ---
detalhado_csv = os.path.join(
BENCHMARK_OUT, f"auditoria_detalhada_{mode}_{limit_files}.csv"
)
with open(detalhado_csv, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(
["Arquivo", "Tamanho_MB", "Tempo_s", "RAM_MB", "Status", "Erro"]
)
for r in results:
status_str = "SUCESSO" if r["success"] else "FALHA"
writer.writerow(
[
r["file"],
f"{r['file_size_mb']:.2f}",
f"{r['duration_s']:.2f}",
f"{r['ram_mb']:.2f}",
status_str,
r["error"] or "",
]
)
print(
f"[{mode.upper()}] Concluído em {duration_total:.2f}s (Sucessos: {qtd_sucessos}/{total_files})"
)
print(
f"[{mode.upper()}] Médias -> Tempo: {avg_time:.2f}s | Tamanho: {avg_size:.2f}MB | RAM: {avg_ram:.2f}MB"
)
return duration_total, qtd_sucessos, avg_time, avg_size, avg_ram
def run_benchmark():
print("==================================================")
print(" INICIANDO TESTE DE SPEEDUP (SEQUENCIAL VS PARALELO)")
print("==================================================")
test_sizes = [1, 10, 100, 1000]
modes = ["sequencial", "paralelo"]
results_table = []
for size in test_sizes:
print(f"\n>>> INICIANDO BATERIA PARA {size} ARQUIVO(S) <<<")
row = {"Tamanho": size}
for mode in modes:
duration, success_count, avg_time, avg_size, avg_ram = execute_batch(
size, mode
)
row[f"Tempo_Total_{mode}_(s)"] = round(duration, 2)
row[f"Tempo_Medio_Proj_{mode}_(s)"] = round(avg_time, 2)
row[f"Tamanho_Medio_Proj_(MB)"] = round(
avg_size, 2
) # O tamanho é igual para ambos, mas guardamos
row[f"RAM_Media_{mode}_(MB)"] = round(avg_ram, 2)
row[f"Sucesso_{mode}"] = success_count
# Calcula o Speedup (Tempo Total Sequencial / Tempo Total Paralelo)
speedup = (
row["Tempo_Total_sequencial_(s)"] / row["Tempo_Total_paralelo_(s)"]
if row["Tempo_Total_paralelo_(s)"] > 0
else 0
)
row["Speedup"] = round(speedup, 2)
results_table.append(row)
print(f"--- Fim da bateria de {size}. Speedup Alcançado: {speedup:.2f}x ---")
# Salva o resultado final num CSV consolidado
os.makedirs(BENCHMARK_OUT, exist_ok=True)
csv_file = os.path.join(BENCHMARK_OUT, "resultado_grafico_speedup.csv")
# Lista de colunas para o CSV final
colunas = [
"Tamanho",
"Speedup",
"Tempo_Total_sequencial_(s)",
"Tempo_Total_paralelo_(s)",
"Tempo_Medio_Proj_sequencial_(s)",
"Tempo_Medio_Proj_paralelo_(s)",
"RAM_Media_sequencial_(MB)",
"RAM_Media_paralelo_(MB)",
"Tamanho_Medio_Proj_(MB)",
"Sucesso_sequencial",
"Sucesso_paralelo",
]
with open(csv_file, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=colunas)
writer.writeheader()
writer.writerows(results_table)
print("\n==================================================")
print(f"BENCHMARK FINALIZADO! Resultados salvos em:\n{BENCHMARK_OUT}")
print("==================================================")
if __name__ == "__main__":
run_benchmark()

View File

@ -0,0 +1,53 @@
nohup: ignoring input
==================================================
INICIANDO TESTE DE SPEEDUP (SEQUENCIAL VS PARALELO)
==================================================
>>> INICIANDO BATERIA PARA 1 ARQUIVO(S) <<<
[SEQUENCIAL] Processando 1 arquivos com 1 núcleo(s)...
[SEQUENCIAL] Concluído em 43.78s (Sucessos: 1/1)
[SEQUENCIAL] Médias -> Tempo: 43.69s | Tamanho: 0.54MB | RAM: 27.22MB
[PARALELO] Processando 1 arquivos com 11 núcleo(s)...
[PARALELO] Concluído em 46.51s (Sucessos: 1/1)
[PARALELO] Médias -> Tempo: 46.48s | Tamanho: 0.54MB | RAM: 27.58MB
--- Fim da bateria de 1. Speedup Alcançado: 0.94x ---
>>> INICIANDO BATERIA PARA 10 ARQUIVO(S) <<<
[SEQUENCIAL] Processando 10 arquivos com 1 núcleo(s)...
Process ForkPoolWorker-13:
Traceback (most recent call last):
File "/usr/lib/python3.12/multiprocessing/pool.py", line 131, in worker
put((job, i, result))
File "/usr/lib/python3.12/multiprocessing/queues.py", line 399, in put
self._writer.send_bytes(obj)
File "/usr/lib/python3.12/multiprocessing/connection.py", line 200, in send_bytes
self._send_bytes(m[offset:offset + size])
File "/usr/lib/python3.12/multiprocessing/connection.py", line 420, in _send_bytes
self._send(header)
File "/usr/lib/python3.12/multiprocessing/connection.py", line 384, in _send
n = write(self._handle, buf)
^^^^^^^^^^^^^^^^^^^^^^^^
BrokenPipeError: [Errno 32] Broken pipe
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.12/multiprocessing/process.py", line 314, in _bootstrap
self.run()
File "/usr/lib/python3.12/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "/usr/lib/python3.12/multiprocessing/pool.py", line 136, in worker
put((job, i, (False, wrapped)))
File "/usr/lib/python3.12/multiprocessing/queues.py", line 399, in put
self._writer.send_bytes(obj)
File "/usr/lib/python3.12/multiprocessing/connection.py", line 200, in send_bytes
self._send_bytes(m[offset:offset + size])
File "/usr/lib/python3.12/multiprocessing/connection.py", line 420, in _send_bytes
self._send(header)
File "/usr/lib/python3.12/multiprocessing/connection.py", line 384, in _send
n = write(self._handle, buf)
^^^^^^^^^^^^^^^^^^^^^^^^
BrokenPipeError: [Errno 32] Broken pipe

230
scripts/handler/crawler.py Normal file
View File

@ -0,0 +1,230 @@
import os
import time
import threading
import requests
import csv
import psutil
import re
import unicodedata
from datetime import datetime, timedelta
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse, parse_qs
from concurrent.futures import ThreadPoolExecutor, as_completed
from utils import (
SRC_MMPSEARCH,
MMP_FOLDER,
)
# ================= CONFIGURAÇÕES PADRONIZADAS =================
BASE_DIR = SRC_MMPSEARCH
PASTA_DESTINO = MMP_FOLDER # Pasta de entrada do pipeline
LOG_DIR = os.path.join(BASE_DIR, "logs/crawler")
ARQUIVO_AUDITORIA = os.path.join(LOG_DIR, "audit_crawler.csv")
# Segurança de Hardware
MIN_RAM_FREE_MB = 500 # Pausa se tiver menos que 500MB livre
MAX_WORKERS_SAFE = 4 # I/O Bound não precisa de tantos workers quanto CPU, mas 4 é seguro
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Compatible; MMPResearchBot/1.0; +http://seu-site.com)'
}
# Cria pastas
for p in [PASTA_DESTINO, LOG_DIR]:
os.makedirs(p, exist_ok=True)
# ================= FUNÇÕES AUXILIARES =================
def slugify(value):
"""Sanitização robusta de nomes de arquivo (Padrão do Pipeline)"""
value = str(value)
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
value = value.lower()
value = re.sub(r'[^\w\s-]', '', value)
value = re.sub(r'[-\s_]+', '-', value)
return value.strip('-_')
def verificar_memoria_segura():
"""Retorna True se é seguro continuar"""
try:
mem = psutil.virtual_memory()
return (mem.available / (1024 * 1024)) > MIN_RAM_FREE_MB
except:
return True
# ================= CLASSE DE AUDITORIA =================
class AuditoriaCrawler:
def __init__(self, arquivo_csv):
self.arquivo_csv = arquivo_csv
self.inicio_global = time.time()
self.lock = threading.Lock()
# Cria CSV se não existir
if not os.path.exists(self.arquivo_csv):
with open(self.arquivo_csv, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow([
"Timestamp", "Nome_Original", "Slug_Final",
"Tamanho_MB", "Tempo_DL_s", "Velocidade_KBps",
"Status", "URL_Origem"
])
def registrar_download(self, nome_orig, slug, tamanho_bytes, tempo_s, url, status="SUCESSO"):
with self.lock:
try:
tamanho_mb = tamanho_bytes / (1024 * 1024)
velocidade = (tamanho_bytes / 1024) / tempo_s if tempo_s > 0 else 0
ts = datetime.now().strftime("%H:%M:%S")
with open(self.arquivo_csv, 'a', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow([
ts, nome_orig, slug,
f"{tamanho_mb:.2f}", f"{tempo_s:.2f}",
f"{velocidade:.2f}", status, url
])
# Log Console Simplificado
print(f"[{ts}] {status}: {slug} ({tamanho_mb:.2f}MB @ {velocidade:.0f}KB/s)")
except Exception as e:
print(f"Erro ao auditar: {e}")
# ================= WORKER =================
def baixar_projeto_worker(args):
url_detalhes, auditor = args
# Check de Segurança (Throttle)
if not verificar_memoria_segura():
time.sleep(5) # Espera RAM liberar
if not verificar_memoria_segura():
return (False, "RAM Crítica - Download Abortado")
try:
# 1. Pega página de detalhes
resp = requests.get(url_detalhes, headers=HEADERS, timeout=15)
if resp.status_code != 200:
auditor.registrar_download("N/A", "N/A", 0, 0, url_detalhes, f"HTTP_{resp.status_code}")
return (False, f"HTTP {resp.status_code}")
soup = BeautifulSoup(resp.text, 'html.parser')
# Lógica de encontrar link de download (Manteve a sua, que é boa)
link_tag = soup.find('a', href=lambda h: h and 'download_file.php' in h)
if not link_tag:
link_tag = soup.find('a', string='Download')
if link_tag:
url_download = urljoin(url_detalhes, link_tag['href'])
inicio_dl = time.time()
# Request do Arquivo
with requests.get(url_download, headers=HEADERS, stream=True, timeout=60) as r_file:
r_file.raise_for_status()
# --- DESCIFRAR NOME (COM SANITIZAÇÃO) ---
nome_bruto = ""
if "Content-Disposition" in r_file.headers:
cd = r_file.headers["Content-Disposition"]
if "filename=" in cd:
nome_bruto = cd.split("filename=")[1].strip('"')
if not nome_bruto:
query = parse_qs(urlparse(url_download).query)
nome_bruto = query['name'][0] if 'name' in query else f"proj_{int(time.time())}.mmpz"
# Aplica Slugify (Padrão do Pipeline)
nome_base, ext = os.path.splitext(nome_bruto)
if not ext: ext = ".mmpz" # Default seguro
slug = slugify(nome_base)
nome_final = f"{slug}{ext}"
caminho_final = os.path.join(PASTA_DESTINO, nome_final)
# Skip se já existe
if os.path.exists(caminho_final):
auditor.registrar_download(nome_bruto, slug, os.path.getsize(caminho_final), 0, url_detalhes, "SKIPPED_EXISTS")
return (True, "Já existe")
# Gravação em chunks (Seguro para RAM)
bytes_downloaded = 0
with open(caminho_final, 'wb') as f:
for chunk in r_file.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
bytes_downloaded += len(chunk)
tempo_total = time.time() - inicio_dl
# Auditoria de Sucesso
auditor.registrar_download(nome_bruto, slug, bytes_downloaded, tempo_total, url_detalhes, "SUCESSO")
return (True, "OK")
else:
auditor.registrar_download("N/A", "N/A", 0, 0, url_detalhes, "LINK_NOT_FOUND")
return (False, "Link não encontrado")
except Exception as e:
auditor.registrar_download("Erro", "Erro", 0, 0, url_detalhes, f"EXCEPT_{str(e)[:20]}")
return (False, str(e))
# ================= MAIN =================
def crawler_manager(url_base, max_paginas=2):
print(f"\n=== INICIANDO CRAWLER PADRONIZADO ===")
print(f"Destino: {PASTA_DESTINO}")
print(f"Auditoria: {ARQUIVO_AUDITORIA}")
auditor = AuditoriaCrawler(ARQUIVO_AUDITORIA)
pagina_atual = 1
total_sucesso = 0
# Executor seguro
with ThreadPoolExecutor(max_workers=MAX_WORKERS_SAFE) as executor:
while pagina_atual <= max_paginas:
print(f"\n--- Varrendo Página {pagina_atual} ---")
operador = '&' if '?' in url_base else '?'
url_pag = f"{url_base}{operador}page={pagina_atual}"
try:
# Pega a lista de projetos da página
r = requests.get(url_pag, headers=HEADERS)
soup = BeautifulSoup(r.text, 'html.parser')
# Filtro específico do LMMS Sharing Platform
links = soup.find_all('a', href=lambda h: h and '?action=show&file=' in h)
urls_unicas = sorted(list(set([l['href'] for l in links])))
if not urls_unicas:
print("Nenhum projeto encontrado nesta página. Encerrando.")
break
# Submete tarefas
futures = []
for u in urls_unicas:
url_completa = urljoin(url_base, u)
futures.append(executor.submit(baixar_projeto_worker, (url_completa, auditor)))
# Processa resultados conforme chegam
for future in as_completed(futures):
ok, msg = future.result()
if ok: total_sucesso += 1
except Exception as e:
print(f"Erro na paginação: {e}")
pagina_atual += 1
time.sleep(1) # Respeito ao servidor
tempo_total = time.time() - auditor.inicio_global
print(f"\n=== CRAWLER FINALIZADO ===")
print(f"Tempo: {timedelta(seconds=int(tempo_total))}")
print(f"Downloads com Sucesso: {total_sucesso}")
if __name__ == "__main__":
# URL de exemplo (Projetos ordenados por data)
URL_ALVO = 'https://lmms.io/lsp/?action=browse&category=Projects&sort=date'
crawler_manager(URL_ALVO, max_paginas=499)

Binary file not shown.

72
scripts/login/login.py Executable file
View File

@ -0,0 +1,72 @@
from flask import Flask, request, jsonify, session
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
app = Flask(__name__)
app.config['SECRET_KEY'] = '25de5592bf94c2ca18e27baa0ae2d4ee22a63012f32e1be719d31f530c215a387b9ec0c9d96be38e80a7ccdd859e04408facefff8fd9119e7f5a2d987d85abb7' # Troque isso!
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users_mmpSearch.db' # O arquivo do banco
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
# --- Modelo do Banco de Dados ---
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(150), unique=True, nullable=False)
password = db.Column(db.String(150), nullable=False)
# Cria o banco na primeira execução
with app.app_context():
db.create_all()
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
# --- Rotas ---
@app.route('/api/register', methods=['POST'])
def register():
data = request.json
hashed_password = bcrypt.generate_password_hash(data['password']).decode('utf-8')
new_user = User(username=data['username'], password=hashed_password)
try:
db.session.add(new_user)
db.session.commit()
return jsonify({"message": "Usuário criado com sucesso!"}), 201
except:
return jsonify({"message": "Usuário já existe"}), 400
@app.route('/api/login', methods=['POST'])
def login():
data = request.json
user = User.query.filter_by(username=data['username']).first()
if user and bcrypt.check_password_hash(user.password, data['password']):
login_user(user)
return jsonify({"message": "Login realizado", "user": user.username}), 200
return jsonify({"message": "Credenciais inválidas"}), 401
@app.route('/api/logout', methods=['POST'])
@login_required
def logout():
logout_user()
return jsonify({"message": "Logout realizado"}), 200
@app.route('/api/check_auth', methods=['GET'])
def check_auth():
if current_user.is_authenticated:
return jsonify({"logged_in": True, "user": current_user.username})
return jsonify({"logged_in": False})
# Rota para seus uploads (exemplo de como proteger)
@app.route('/api/upload_seguro', methods=['POST'])
@login_required
def upload_seguro():
# Aqui entraria sua lógica de salvar o arquivo
# Como você já tem a lógica pronta, você pode chamar ela aqui
# ou mover o código para cá.
return jsonify({"message": f"Upload recebido de {current_user.username}"})
if __name__ == '__main__':
app.run(port=33005)

34
scripts/pipeline_etl.sh Executable file
View File

@ -0,0 +1,34 @@
#!/bin/bash
# Interrompe a execução imediatamente se qualquer comando falhar
set -e
# Define o caminho absoluto para o seu Python do ambiente virtual
PYTHON_BIN="/nethome/jotachina/projetos/mmpSearch/venv/bin/python3"
# Define o diretório raiz do projeto para garantir que os caminhos relativos funcionem
BASE_DIR="/nethome/jotachina/projetos/mmpSearch/scripts"
echo "================================================================="
echo "Iniciando Pipeline Completo do MMPSearch"
echo "================================================================="
# Navega até a raiz do projeto para que os caminhos dos scripts funcionem corretamente
cd "$BASE_DIR"
echo "[1/3] Executando: handler/main.py..."
"$PYTHON_BIN" handler/main.py
echo "handler/main.py concluído!"
echo "-----------------------------------------------------------------"
echo "[2/3] Executando: classificacao/analise_audio.py..."
"$PYTHON_BIN" classificacao/analise_audio.py
echo "classificacao/analise_audio.py concluído!"
echo "-----------------------------------------------------------------"
echo "[3/3] Executando: classificacao/classificacao_mestre.py..."
"$PYTHON_BIN" classificacao/classificacao_mestre.py
echo "classificacao/classificacao_mestre.py concluído!"
echo "================================================================="
echo "Pipeline finalizado com sucesso!"
echo "================================================================="