corrigindo upload
Deploy / Deploy (push) Failing after 11s Details

This commit is contained in:
JotaChina 2026-04-04 10:57:30 -03:00
parent b533873e69
commit a8fdfde48e
9 changed files with 4295 additions and 74 deletions

1146
_data/210424.yml Normal file

File diff suppressed because it is too large Load Diff

1579
_data/danse-le-temps.yml Normal file

File diff suppressed because it is too large Load Diff

174
_data/floating.yml Normal file
View File

@ -0,0 +1,174 @@
bpm: '200'
file: floating
original_title: floating
src: /var/www/html/trens/src_mmpSearch/mmp/floating.mmp
tags:
TAG:
- plugin
automation: []
bassline: []
plugin:
- zynaddsubfx
sample: []
tracks:
- arpeggiator:
arp: '0'
arp-enabled: '0'
arpcycle: '0'
arpdir: '0'
arpgate: '100'
arpmiss: '0'
arpmode: '0'
arprange: '1'
arpskip: '0'
arptime: '200'
arptime_denominator: '4'
arptime_numerator: '4'
arptime_syncmode: '0'
chordcreator:
chord: '0'
chord-enabled: '0'
chordrange: '1'
controller:
bandwidth_depth: '64'
filter_cutoff_depth: '64'
filter_q_depth: '64'
mod_wheel_depth: '80'
panning_depth: '64'
pitchwheel_bendrange: '100'
portamento_pitchthresh: '3'
portamento_pitchthreshtype: '1'
portamento_portamento: '0'
portamento_propdepth: '90'
portamento_proportional: '0'
portamento_proprate: '80'
portamento_time: '64'
portamento_updowntimestretch: '64'
resonance_bandwidth_depth: '64'
resonance_center_depth: '64'
elcut:
amt: '0'
att: '0'
ctlenvamt: '0'
dec: '0.5'
hold: '0.5'
lamt: '0'
latt: '0'
lpdel: '0'
lshp: '0'
lspd: '0.1'
lspd_denominator: '4'
lspd_numerator: '4'
lspd_syncmode: '0'
pdel: '0'
rel: '0.1'
sustain: '0.5'
userwavefile: ''
x100: '0'
eldata:
fcut: '14000'
fres: '0.5'
ftype: '0'
fwet: '0'
elres:
amt: '0'
att: '0'
ctlenvamt: '0'
dec: '0.5'
hold: '0.5'
lamt: '0'
latt: '0'
lpdel: '0'
lshp: '0'
lspd: '0.1'
lspd_denominator: '4'
lspd_numerator: '4'
lspd_syncmode: '0'
pdel: '0'
rel: '0.1'
sustain: '0.5'
userwavefile: ''
x100: '0'
elvol:
amt: '0'
att: '0'
ctlenvamt: '0'
dec: '0.5'
hold: '0.5'
lamt: '0'
latt: '0'
lpdel: '0'
lshp: '0'
lspd: '0.1'
lspd_denominator: '4'
lspd_numerator: '4'
lspd_syncmode: '0'
pdel: '0'
rel: '0.1'
sustain: '0.5'
userwavefile: ''
x100: '0'
fxchain:
enabled: '0'
numofeffects: '0'
insertion_effects:
- part: '-1'
type: '0'
- part: '-1'
type: '0'
- part: '-1'
type: '0'
- part: '-1'
type: '0'
- part: '-1'
type: '0'
- part: '-1'
type: '0'
- part: '-1'
type: '0'
- part: '-1'
type: '0'
instrument_effects:
- par: '119'
preset: '0'
route: '0'
type: '3'
- route: '0'
type: '0'
- route: '0'
type: '0'
instrument_name: zynaddsubfx
instrumenttrack:
basenote: '57'
fxch: '0'
pan: '0'
pitch: '0'
pitchrange: '1'
usemasterpitch: '1'
vol: '100'
midiport:
basevelocity: '63'
fixedinputvelocity: '-1'
fixedoutputnote: '-1'
fixedoutputvelocity: '-1'
inputchannel: '0'
inputcontroller: '0'
outputchannel: '1'
outputcontroller: '0'
outputprogram: '1'
readable: '0'
writable: '0'
system_effects:
- send_vol: '0'
type: '0'
vol: '0'
- send_vol: '0'
type: '0'
vol: '0'
- send_vol: '0'
type: '0'
vol: '0'
- type: '0'
vol: '0'
track_name: Ice Rhodes3
type: plugin

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,9 @@
"bassdrum_acoustic02_-_Copia.ogg": { "bassdrum_acoustic02_-_Copia.ogg": {
"_isFile": true "_isFile": true
}, },
"bassdrum01.ogg": {
"_isFile": true
},
"bassdrum_acoustic01_-_Copia.ogg": { "bassdrum_acoustic01_-_Copia.ogg": {
"_isFile": true "_isFile": true
}, },
@ -15,6 +18,9 @@
"bassdrum03_-_Copia.ogg": { "bassdrum03_-_Copia.ogg": {
"_isFile": true "_isFile": true
}, },
"bassdrum_acoustic01.ogg": {
"_isFile": true
},
"bassdrum02_-_Copia.ogg": { "bassdrum02_-_Copia.ogg": {
"_isFile": true "_isFile": true
} }

Binary file not shown.

View File

@ -1,5 +1,4 @@
import os import os
import sys
import multiprocessing import multiprocessing
import concurrent.futures # NOVO: Necessário para usar o ProcessPoolExecutor import concurrent.futures # NOVO: Necessário para usar o ProcessPoolExecutor
import csv import csv
@ -7,6 +6,7 @@ import time
import glob import glob
from datetime import datetime, timedelta from datetime import datetime, timedelta
# ================= 1. LIMITADOR DE CPU DINÂMICO ================= # ================= 1. LIMITADOR DE CPU DINÂMICO =================
def configurar_limites_cpu(): def configurar_limites_cpu():
try: try:
@ -28,6 +28,7 @@ def configurar_limites_cpu():
except Exception as e: except Exception as e:
print(f"Aviso: Não foi possível limitar CPU automaticamente: {e}") print(f"Aviso: Não foi possível limitar CPU automaticamente: {e}")
configurar_limites_cpu() configurar_limites_cpu()
# ================= IMPORTS ================= # ================= IMPORTS =================
@ -84,6 +85,7 @@ GLOBAL_EMBEDDING = None
GLOBAL_CLASSIFIER = None GLOBAL_CLASSIFIER = None
GLOBAL_CLASSES = None GLOBAL_CLASSES = None
# ================= CLASSE DE AUDITORIA OTIMIZADA ================= # ================= CLASSE DE AUDITORIA OTIMIZADA =================
class Auditoria: class Auditoria:
def __init__(self, arquivo_csv): def __init__(self, arquivo_csv):
@ -110,7 +112,9 @@ class Auditoria:
) )
self.file.flush() self.file.flush()
def registrar_processamento(self, nome_arquivo, tamanho_bytes, duracao_audio, tempo_gasto): def registrar_processamento(
self, nome_arquivo, tamanho_bytes, duracao_audio, tempo_gasto
):
try: try:
ram_mb = self.process.memory_info().rss / (1024 * 1024) ram_mb = self.process.memory_info().rss / (1024 * 1024)
cpu_pct = self.process.cpu_percent(interval=None) cpu_pct = self.process.cpu_percent(interval=None)
@ -145,6 +149,7 @@ class Auditoria:
# ================= FUNÇÕES AUXILIARES ================= # ================= FUNÇÕES AUXILIARES =================
def verificar_memoria_segura(tamanho_arquivo_bytes): def verificar_memoria_segura(tamanho_arquivo_bytes):
mem = psutil.virtual_memory() mem = psutil.virtual_memory()
livre_mb = mem.available / (1024 * 1024) livre_mb = mem.available / (1024 * 1024)
@ -225,7 +230,9 @@ def carregar_database_yaml():
logging.error(f"Erro ao ler lote {arquivo}: {e}") logging.error(f"Erro ao ler lote {arquivo}: {e}")
print(f"X Erro ao ler {os.path.basename(arquivo)}") 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) ---") print(
f"--- Total de Projetos no DB (Memória): {len(db)} (de {total_carregados} lidos) ---"
)
return db return db
@ -300,14 +307,14 @@ def calcular_complexidade(projeto_yaml):
score += num_fx * W_FX score += num_fx * W_FX
stats["num_effects"] += num_fx stats["num_effects"] += num_fx
if score <= 15: if score <= 120:
estrelas = 1 estrelas = 4
elif score <= 40:
estrelas = 2
elif score <= 80: elif score <= 80:
estrelas = 3 estrelas = 3
elif score <= 120: elif score <= 40:
estrelas = 4 estrelas = 2
elif score <= 15:
estrelas = 1
else: else:
estrelas = 5 estrelas = 5
@ -316,6 +323,7 @@ def calcular_complexidade(projeto_yaml):
# ================= NÚCLEO DE ANÁLISE ================= # ================= NÚCLEO DE ANÁLISE =================
def detectar_estrutura(audio_vec, sample_rate, duration): def detectar_estrutura(audio_vec, sample_rate, duration):
try: try:
if duration < 30: if duration < 30:
@ -496,7 +504,9 @@ def analisar_faixa(caminho_arquivo, embedding_model, classifier_model, classes_r
metadata["analise_ia"] = { metadata["analise_ia"] = {
"genero_macro": genero_pai, "genero_macro": genero_pai,
"estilo_principal": tags_detectadas[0]["tag"] if tags_detectadas else "Unknown", "estilo_principal": tags_detectadas[0]["tag"]
if tags_detectadas
else "Unknown",
"nuvem_tags": tags_detectadas, "nuvem_tags": tags_detectadas,
} }
@ -525,6 +535,7 @@ def worker_analise(caminho):
global GLOBAL_EMBEDDING, GLOBAL_CLASSIFIER, GLOBAL_CLASSES global GLOBAL_EMBEDDING, GLOBAL_CLASSIFIER, GLOBAL_CLASSES
return analisar_faixa(caminho, GLOBAL_EMBEDDING, GLOBAL_CLASSIFIER, GLOBAL_CLASSES) return analisar_faixa(caminho, GLOBAL_EMBEDDING, GLOBAL_CLASSIFIER, GLOBAL_CLASSES)
def main(): def main():
print("\n--- INICIANDO PROCESSADOR + AUDITORIA ---") print("\n--- INICIANDO PROCESSADOR + AUDITORIA ---")
@ -549,11 +560,27 @@ def main():
print(f"Auditoria será salva em: {ARQUIVO_AUDITORIA}") print(f"Auditoria será salva em: {ARQUIVO_AUDITORIA}")
# === CARREGAMENTO MODELOS GLOBAIS === # === CARREGAMENTO MODELOS GLOBAIS ===
path_embed = MODELO_EMBEDDING if os.path.exists(MODELO_EMBEDDING) else os.path.join(BASE_DIR, MODELO_EMBEDDING) path_embed = (
path_class = MODELO_CLASSIFIER if os.path.exists(MODELO_CLASSIFIER) else os.path.join(BASE_DIR, MODELO_CLASSIFIER) MODELO_EMBEDDING
path_json = MODELO_CLASSES if os.path.exists(MODELO_CLASSES) else os.path.join(BASE_DIR, MODELO_CLASSES) 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): if (
os.path.exists(path_embed)
and os.path.exists(path_class)
and os.path.exists(path_json)
):
print("--- Carregando Modelos de IA ---") print("--- Carregando Modelos de IA ---")
try: try:
with open(path_json, "r") as f: with open(path_json, "r") as f:
@ -561,13 +588,23 @@ def main():
print("[1/2] Carregando Extrator de Embeddings...") print("[1/2] Carregando Extrator de Embeddings...")
if hasattr(es, "TensorflowPredictEffnetDiscogs"): if hasattr(es, "TensorflowPredictEffnetDiscogs"):
GLOBAL_EMBEDDING = es.TensorflowPredictEffnetDiscogs(graphFilename=path_embed, output="PartitionedCall:1") GLOBAL_EMBEDDING = es.TensorflowPredictEffnetDiscogs(
graphFilename=path_embed, output="PartitionedCall:1"
)
else: else:
print(" -> Usando TensorflowPredict genérico.") print(" -> Usando TensorflowPredict genérico.")
GLOBAL_EMBEDDING = es.TensorflowPredict(graphFilename=path_embed, input="serving_default_model_Placeholder", output="PartitionedCall:1") GLOBAL_EMBEDDING = es.TensorflowPredict(
graphFilename=path_embed,
input="serving_default_model_Placeholder",
output="PartitionedCall:1",
)
print(" -> Embeddings carregados com sucesso.") print(" -> Embeddings carregados com sucesso.")
GLOBAL_CLASSIFIER = es.TensorflowPredict2D(graphFilename=path_class, input="serving_default_model_Placeholder", output="PartitionedCall:0") GLOBAL_CLASSIFIER = es.TensorflowPredict2D(
graphFilename=path_class,
input="serving_default_model_Placeholder",
output="PartitionedCall:0",
)
print("[2/2] Classificador de Gênero carregado.") print("[2/2] Classificador de Gênero carregado.")
except Exception as e: except Exception as e:
@ -587,11 +624,19 @@ def main():
contagem_sessao = 0 contagem_sessao = 0
try: try:
with concurrent.futures.ProcessPoolExecutor(max_workers=max_workers) as executor: with concurrent.futures.ProcessPoolExecutor(
resultados_futuros = {executor.submit(worker_analise, caminho): caminho for caminho in a_fazer} 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 # 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"): for future in tqdm(
concurrent.futures.as_completed(resultados_futuros),
total=len(a_fazer),
unit="track",
):
try: try:
res, duracao, tempo_gasto = future.result() res, duracao, tempo_gasto = future.result()
caminho = resultados_futuros[future] caminho = resultados_futuros[future]
@ -601,7 +646,9 @@ def main():
lista_resultados.append(res) lista_resultados.append(res)
tamanho = os.path.getsize(caminho) tamanho = os.path.getsize(caminho)
auditor.registrar_processamento(res['arquivo'], tamanho, duracao, tempo_gasto) auditor.registrar_processamento(
res["arquivo"], tamanho, duracao, tempo_gasto
)
contagem_sessao += 1 contagem_sessao += 1
auditor.verificar_marco(contagem_sessao) auditor.verificar_marco(contagem_sessao)

View File

@ -133,12 +133,12 @@ def calcular_nivel_ponderado(
pontos += 1 pontos += 1
# 3. Estrutura (Áudio) # 3. Estrutura (Áudio)
if qtd_secoes_audio >= 3: if qtd_secoes_audio >= 10:
pontos += 1.5 pontos += 5
elif qtd_secoes_audio >= 5: elif qtd_secoes_audio >= 5:
pontos += 3 pontos += 3
elif qtd_secoes_audio >= 10: elif qtd_secoes_audio >= 3:
pontos += 5 pontos += 1.5
if pontos < 3: if pontos < 3:
return "Iniciante" return "Iniciante"

View File

@ -7,6 +7,7 @@ import shutil
import time import time
import io import io
import threading import threading
from threading import Lock
from flask import Flask, request, jsonify, send_file, redirect from flask import Flask, request, jsonify, send_file, redirect
from flask_cors import CORS from flask_cors import CORS
@ -41,6 +42,15 @@ from utils import (
SAMPLE_MANIFEST, SAMPLE_MANIFEST,
) )
import logging
logging.basicConfig(
level=logging.INFO
) # obriga o servidor a mostrar os sucessos também
# Trava do build do Jekyll
JEKYLL_LOCK = Lock()
app = Flask(__name__) app = Flask(__name__)
# --- CONFIGURAÇÃO DE SEGURANÇA E BANCO --- # --- CONFIGURAÇÃO DE SEGURANÇA E BANCO ---
@ -164,17 +174,13 @@ def allowed_sample(filename):
and filename.rsplit(".", 1)[1].lower() in ALLOWED_SAMPLE_EXTENSIONS and filename.rsplit(".", 1)[1].lower() in ALLOWED_SAMPLE_EXTENSIONS
) )
def run_jekyll_build(): def run_jekyll_build():
RUBY_BIN_PATH = "/usr/bin/ruby3.2" RUBY_BIN_PATH = "/usr/bin/ruby3.2"
BUNDLE_PATH = ( BUNDLE_PATH = "/nethome/jotachina/projetos/mmpSearch/vendor/bundle/ruby/3.2.0/bin/bundle"
"/nethome/jotachina/projetos/mmpSearch/vendor/bundle/ruby/3.2.0/bin/bundle"
)
# Prepara o ambiente para o subprocesso
env_vars = os.environ.copy() env_vars = os.environ.copy()
# Adiciona o caminho do Ruby ao PATH do usuário www-data temporariamente
env_vars["PATH"] = f"{RUBY_BIN_PATH}:{env_vars.get('PATH', '')}" env_vars["PATH"] = f"{RUBY_BIN_PATH}:{env_vars.get('PATH', '')}"
print("Iniciando build do Jekyll...")
command = [ command = [
BUNDLE_PATH, BUNDLE_PATH,
"exec", "exec",
@ -183,18 +189,24 @@ def run_jekyll_build():
"--destination", "--destination",
"/var/www/html/trens/mmpSearch/", "/var/www/html/trens/mmpSearch/",
] ]
logging.info("⏳ Aguardando liberação para iniciar build do Jekyll...")
with JEKYLL_LOCK:
logging.info("🔨 Iniciando build do Jekyll (Travando a fila)...")
try: try:
# Redirecionamos a saída para DEVNULL para não encher o buffer e travar resultado = subprocess.run(
subprocess.Popen(
command, command,
cwd=BASE_DATA, cwd=BASE_DATA,
env=env_vars, env=env_vars,
stdout=subprocess.DEVNULL, capture_output=True,
stderr=subprocess.DEVNULL, text=True,
check=True
) )
print("Jekyll Build iniciado em segundo plano (background).") logging.info("✅ Jekyll Build concluído com sucesso!")
except subprocess.CalledProcessError as e:
logging.error(f"❌ ERRO FATAL no Jekyll Build (Code {e.returncode}):\n{e.stderr}")
except Exception as e: except Exception as e:
print(f"Erro ao iniciar Jekyll: {e}") logging.error(f"❌ Erro inesperado ao chamar Jekyll: {e}")
def load_manifest_keys(): def load_manifest_keys():
@ -321,31 +333,27 @@ def update_xml_paths_exact(mmp_filename, replacements):
def run_heavy_tasks_in_background(): def run_heavy_tasks_in_background():
"""Esta função roda isolada sem travar o usuário""" logging.info("--- [BACKGROUND] Iniciando tarefas assíncronas ---")
print("--- [BACKGROUND] Iniciando reconstrução de índices ---")
try: try:
# 2. Isso gera os manifestos (Python puro)
generate_manifests(SRC_MMPSEARCH) generate_manifests(SRC_MMPSEARCH)
# 3. Isso chama o subprocesso do Jekyll (Externo)
# Mantém sua função original que usa subprocess
run_jekyll_build() run_jekyll_build()
logging.info("--- [BACKGROUND] Todas as tarefas concluídas ---")
print("--- [BACKGROUND] Tarefas concluídas com sucesso ---")
except Exception as e: except Exception as e:
print(f"--- [BACKGROUND] Erro: {e} ---") logging.error(f"--- [BACKGROUND] Erro: {e} ---")
def process_and_build(filename): def process_and_build(filename):
"""Função chamada pela rota de upload""" """Função chamada pela rota de upload"""
# Processamento inicial do arquivo (rápido)
result = process_single_file(filename) # 1. Preparamos os 3 dados que o main.py exige: (file_name, clean_slug, total_files)
name_without_ext = os.path.splitext(filename)[0]
clean_slug = slugify(name_without_ext)
args_tuple = (filename, clean_slug, 1) # O 1 representa que é só 1 arquivo no total
# 2. Passamos a tupla!
result = process_single_file(args_tuple)
if result["success"]: if result["success"]:
# Em vez de chamar rebuild_indexes() direto, criamos a Thread # Em vez de chamar rebuild_indexes() direto, criamos a Thread
# O Flask vai responder o return abaixo imediatamente,
# enquanto a thread continua rodando no servidor.
task_thread = threading.Thread(target=run_heavy_tasks_in_background) task_thread = threading.Thread(target=run_heavy_tasks_in_background)
task_thread.start() task_thread.start()