resolvendo dependencias dos projetos
Deploy / Deploy (push) Successful in 1m48s
Details
Deploy / Deploy (push) Successful in 1m48s
Details
This commit is contained in:
parent
7c2b1897ba
commit
be62cb7c11
1191
_data/all.yml
1191
_data/all.yml
File diff suppressed because it is too large
Load Diff
|
|
@ -29,6 +29,7 @@
|
|||
- deep-house-vespertine-feat-georg-no-days-off.wav
|
||||
- demo-aesthetescence.wav
|
||||
- dr-dre.wav
|
||||
- drake.wav
|
||||
- dreamhop-animal-l-bonus-r0und-ep.wav
|
||||
- drifting-rev-d-csammis-track-1.wav
|
||||
- dubstep-fyrebreak-saichania-original-mix.wav
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,5 +1,9 @@
|
|||
{
|
||||
"imported": {},
|
||||
"imported": {
|
||||
"bassdrum_acoustic01_-_Copia.ogg": {
|
||||
"_isFile": true
|
||||
}
|
||||
},
|
||||
"samples": {},
|
||||
"drumsynth": {
|
||||
"misc_synth": {
|
||||
|
|
|
|||
|
|
@ -169,42 +169,42 @@ permalink: /envie_seu_projeto/
|
|||
|
||||
<div id="resolve-modal" class="modal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<div class="modal-card" style="width: 600px; max-width: 95%;">
|
||||
<header class="modal-card-head has-background-warning-light">
|
||||
<p class="modal-card-title has-text-warning-dark">Atenção: Arquivos Faltando</p>
|
||||
<p class="modal-card-title has-text-warning-dark">
|
||||
<span class="icon mr-2"><i class="fa-solid fa-link-slash"></i></span>
|
||||
Arquivos Externos Detectados
|
||||
</p>
|
||||
<button class="delete" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body p-5">
|
||||
<div class="notification is-warning is-light">
|
||||
<span class="icon"><i class="fa-solid fa-triangle-exclamation"></i></span>
|
||||
<strong>Seu projeto usa samples externos!</strong>
|
||||
<p class="mt-2">Para que o projeto funcione para todos, precisamos dos seguintes arquivos:</p>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<ul id="missing-files-list" style="list-style: disc; margin-left: 20px; color: #d63031;">
|
||||
</ul>
|
||||
<section class="modal-card-body">
|
||||
<div class="content is-small mb-4">
|
||||
<p>Este projeto utiliza samples que não são nativos do LMMS. Para que o som saia correto, precisamos que você envie os arquivos correspondentes.</p>
|
||||
</div>
|
||||
|
||||
<form id="resolve-form">
|
||||
<input type="hidden" name="project_file" id="hidden-project-name">
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Envie os arquivos listados acima:</label>
|
||||
<div class="file has-name is-fullwidth is-warning">
|
||||
<label class="file-label">
|
||||
<input class="file-input" type="file" name="sample_files" multiple required>
|
||||
<span class="file-cta">
|
||||
<span class="file-icon"><i class="fa-solid fa-upload"></i></span>
|
||||
<span class="file-label">Selecionar Samples...</span>
|
||||
</span>
|
||||
<span class="file-name" id="resolve-file-name">Nenhum selecionado</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<table class="table is-fullwidth is-striped is-hoverable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Arquivo Faltante (XML)</th>
|
||||
<th style="width: 50px;">Status</th>
|
||||
<th style="width: 120px;">Ação</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="missing-files-table-body">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button is-warning is-fullwidth mt-4" id="resolve-btn">
|
||||
Enviar Arquivos e Finalizar
|
||||
<div class="notification is-danger is-light is-hidden" id="resolve-error-msg"></div>
|
||||
|
||||
<button type="submit" class="button is-success is-fullwidth mt-4" id="resolve-btn" disabled>
|
||||
<span class="icon"><i class="fa-solid fa-check"></i></span>
|
||||
<span>Enviar Todos e Processar</span>
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
|
|
@ -396,24 +396,95 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
document.documentElement.classList.add('is-clipped');
|
||||
}
|
||||
|
||||
// --- FUNÇÃO PARA GERAR A TABELA DE UPLOAD ---
|
||||
function mostrarResolveModal(data) {
|
||||
// 1. Preenche o input hidden com o nome do projeto (retornado pelo backend)
|
||||
// 1. Configurações iniciais
|
||||
document.getElementById('hidden-project-name').value = data.project_file;
|
||||
const tbody = document.getElementById('missing-files-table-body');
|
||||
tbody.innerHTML = ''; // Limpa tabela
|
||||
|
||||
// 2. Preenche a lista visual de arquivos faltando
|
||||
const list = document.getElementById('missing-files-list');
|
||||
list.innerHTML = ''; // Limpa lista anterior
|
||||
const resolveBtn = document.getElementById('resolve-btn');
|
||||
resolveBtn.disabled = true; // Começa desabilitado até preencher tudo
|
||||
|
||||
if (data.missing_files && data.missing_files.length > 0) {
|
||||
data.missing_files.forEach(file => {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = file;
|
||||
list.appendChild(li);
|
||||
// Controle de validação
|
||||
let totalFiles = data.missing_files.length;
|
||||
let filledFiles = 0;
|
||||
|
||||
// 2. Gera uma linha para cada arquivo faltante
|
||||
data.missing_files.forEach((originalName, index) => {
|
||||
const tr = document.createElement('tr');
|
||||
|
||||
// Coluna 1: Nome do arquivo
|
||||
const tdName = document.createElement('td');
|
||||
tdName.style.verticalAlign = "middle";
|
||||
tdName.innerHTML = `<span class="has-text-grey-dark is-family-code">${originalName}</span>`;
|
||||
|
||||
// Coluna 2: Ícone de Status
|
||||
const tdStatus = document.createElement('td');
|
||||
tdStatus.style.verticalAlign = "middle";
|
||||
tdStatus.style.textAlign = "center";
|
||||
tdStatus.innerHTML = `<span class="icon has-text-grey-light status-icon"><i class="fa-solid fa-circle-xmark"></i></span>`;
|
||||
|
||||
// Coluna 3: Botão de Upload
|
||||
const tdAction = document.createElement('td');
|
||||
tdAction.style.textAlign = "right";
|
||||
|
||||
// Cria o input file invisível
|
||||
// O 'name' do input é o NOME ORIGINAL DO ARQUIVO. O Backend usará isso para mapear.
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.name = originalName; // <--- O PULO DO GATO
|
||||
fileInput.style.display = 'none';
|
||||
fileInput.accept = ".wav, .mp3, .ogg, .flac, .ds";
|
||||
|
||||
// Botão bonito que aciona o input
|
||||
const uploadLabel = document.createElement('label');
|
||||
uploadLabel.className = 'button is-small is-info is-outlined';
|
||||
uploadLabel.innerHTML = `<span class="icon"><i class="fa-solid fa-upload"></i></span>`;
|
||||
|
||||
// Evento: Ao clicar no botão, abre o seletor de arquivo
|
||||
uploadLabel.addEventListener('click', (e) => {
|
||||
e.preventDefault(); // Evita submit
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
// Evento: Quando o usuário seleciona um arquivo
|
||||
fileInput.addEventListener('change', () => {
|
||||
if (fileInput.files.length > 0) {
|
||||
// Muda visual para sucesso
|
||||
uploadLabel.className = 'button is-small is-success';
|
||||
uploadLabel.innerHTML = `<span class="icon"><i class="fa-solid fa-rotate"></i></span>`; // Ícone de trocar
|
||||
|
||||
const icon = tdStatus.querySelector('.status-icon');
|
||||
icon.innerHTML = `<i class="fa-solid fa-circle-check"></i>`;
|
||||
icon.classList.remove('has-text-grey-light');
|
||||
icon.classList.add('has-text-success');
|
||||
|
||||
// Lógica de validação (simples)
|
||||
if (!fileInput.dataset.filled) {
|
||||
fileInput.dataset.filled = "true";
|
||||
filledFiles++;
|
||||
}
|
||||
|
||||
// 3. Abre o modal
|
||||
resolveModal.classList.add('is-active');
|
||||
// Se todos foram preenchidos, habilita o botão final
|
||||
if (filledFiles >= totalFiles) {
|
||||
resolveBtn.disabled = false;
|
||||
resolveBtn.classList.remove('is-outlined');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tdAction.appendChild(fileInput);
|
||||
tdAction.appendChild(uploadLabel);
|
||||
|
||||
tr.appendChild(tdName);
|
||||
tr.appendChild(tdStatus);
|
||||
tr.appendChild(tdAction);
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
|
||||
// 3. Exibe o modal
|
||||
document.getElementById('resolve-modal').classList.add('is-active');
|
||||
document.documentElement.classList.add('is-clipped');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,19 +13,11 @@ from werkzeug.utils import secure_filename
|
|||
|
||||
# Importa suas funções e configurações existentes
|
||||
from main import process_single_file, rebuild_indexes, generate_manifests, slugify
|
||||
from utils import MMP_FOLDER, MMPZ_FOLDER, CERT_PATH, KEY_PATH, BASE_DATA, SRC_MMPSEARCH, SAMPLE_SRC, METADATA_FOLDER
|
||||
from utils import ALLOWED_EXTENSIONS, ALLOWED_SAMPLE_EXTENSIONS, MMP_FOLDER, MMPZ_FOLDER, CERT_PATH, KEY_PATH, BASE_DATA, SRC_MMPSEARCH, SAMPLE_SRC, METADATA_FOLDER, XML_IMPORTED_PATH_PREFIX, SAMPLE_MANIFEST
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
ALLOWED_EXTENSIONS = {"mmp", "mmpz"}
|
||||
ALLOWED_SAMPLE_EXTENSIONS = {'wav', 'mp3', 'ogg', 'flac', 'ds'}
|
||||
|
||||
# --- CONFIGURAÇÕES ---
|
||||
MANIFEST_PATH = os.path.join(METADATA_FOLDER, "samples-manifest.json")
|
||||
XML_IMPORTED_PATH_PREFIX = "src_mmpSearch/samples/imported"
|
||||
PHYSICAL_IMPORTED_FOLDER = os.path.join(SAMPLE_SRC, "imported")
|
||||
|
||||
def allowed_file(filename):
|
||||
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
|
||||
|
|
@ -43,10 +35,10 @@ def run_jekyll_build():
|
|||
|
||||
def load_manifest_keys():
|
||||
"""Carrega as pastas de fábrica (keys do manifesto)."""
|
||||
if not os.path.exists(MANIFEST_PATH):
|
||||
if not os.path.exists(SAMPLE_MANIFEST):
|
||||
return ["drums", "instruments", "effects", "presets", "samples"]
|
||||
try:
|
||||
with open(MANIFEST_PATH, 'r', encoding='utf-8') as f:
|
||||
with open(SAMPLE_MANIFEST, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
return list(data.keys())
|
||||
except Exception:
|
||||
|
|
@ -173,44 +165,54 @@ def analyze_mmp_dependencies(mmp_filename):
|
|||
|
||||
return len(missing_samples) == 0, list(missing_samples)
|
||||
|
||||
def update_xml_paths(mmp_filename, file_map):
|
||||
def update_xml_paths_exact(mmp_filename, replacements):
|
||||
"""
|
||||
Atualiza os caminhos no XML para apontar para a pasta imported.
|
||||
Substitui caminhos no XML baseando-se no mapeamento exato:
|
||||
replacements = { 'nome_original_no_xml.wav': 'novo_nome_seguro.wav' }
|
||||
"""
|
||||
filepath = os.path.join(MMP_FOLDER, mmp_filename)
|
||||
|
||||
try:
|
||||
tree = ET.parse(filepath)
|
||||
root = tree.getroot()
|
||||
factory_folders = load_manifest_keys()
|
||||
changes_made = False
|
||||
|
||||
# Normaliza as chaves para minúsculo para garantir o match
|
||||
# Ex: {'Kick.wav': 'kick.wav'} -> {'kick.wav': 'kick.wav'}
|
||||
replacements_lower = {k.lower(): v for k, v in replacements.items()}
|
||||
|
||||
for audio_node in root.findall(".//audiofileprocessor"):
|
||||
src = audio_node.get('src', '')
|
||||
if not src: continue
|
||||
|
||||
is_factory = any(src.startswith(folder + "/") for folder in factory_folders)
|
||||
is_already_imported = src.startswith(XML_IMPORTED_PATH_PREFIX)
|
||||
# Pega o nome do arquivo que está atualmente no XML
|
||||
current_basename = os.path.basename(src)
|
||||
current_basename_lower = current_basename.lower()
|
||||
|
||||
if not is_factory and not is_already_imported:
|
||||
base_name = os.path.basename(src)
|
||||
# Verifica se esse nome está na lista de substituições
|
||||
if current_basename_lower in replacements_lower:
|
||||
new_filename = replacements_lower[current_basename_lower]
|
||||
|
||||
# Verifica se temos esse arquivo mapeado (upload do usuário)
|
||||
if base_name in file_map:
|
||||
safe_name = file_map[base_name]
|
||||
new_src = f"{XML_IMPORTED_PATH_PREFIX}/{safe_name}"
|
||||
# Constrói o novo caminho completo
|
||||
new_src = f"{XML_IMPORTED_PATH_PREFIX}/{new_filename}"
|
||||
|
||||
# Aplica a alteração
|
||||
audio_node.set('src', new_src)
|
||||
audio_node.set('vol', audio_node.get('vol', '1'))
|
||||
audio_node.set('vol', audio_node.get('vol', '1')) # Garante volume
|
||||
|
||||
print(f"[XML FIX] Substituído: {current_basename} -> {new_src}")
|
||||
changes_made = True
|
||||
print(f"Path corrigido: {base_name} -> {new_src}")
|
||||
|
||||
if changes_made:
|
||||
tree.write(filepath, encoding='UTF-8', xml_declaration=True)
|
||||
print(f"Projeto {mmp_filename} atualizado com novos caminhos.")
|
||||
|
||||
print(f"Projeto {mmp_filename} salvo com novos caminhos.")
|
||||
return True
|
||||
else:
|
||||
print("Aviso: Nenhum caminho foi alterado no XML (match não encontrado).")
|
||||
return True # Retorna True para não travar o processo, mas avisa no log
|
||||
|
||||
except Exception as e:
|
||||
print(f"Erro ao atualizar XML: {e}")
|
||||
print(f"Erro crítico ao editar XML: {e}")
|
||||
return False
|
||||
|
||||
# --- ROTAS ---
|
||||
|
|
@ -289,43 +291,59 @@ def upload_file():
|
|||
|
||||
return jsonify({"error": "Tipo de arquivo não permitido"}), 400
|
||||
|
||||
|
||||
@app.route("/api/upload/resolve", methods=["POST"])
|
||||
def resolve_samples():
|
||||
"""
|
||||
Recebe o nome do arquivo .mmp (já existente no servidor) e os samples.
|
||||
Recebe inputs onde:
|
||||
name="nome_original.wav" (chave) -> value=Arquivo (conteúdo)
|
||||
"""
|
||||
project_filename = request.form.get('project_file') # Espera o nome .mmp
|
||||
uploaded_files = request.files.getlist("sample_files")
|
||||
project_filename = request.form.get('project_file') # Nome do .mmp
|
||||
|
||||
if not project_filename or not uploaded_files:
|
||||
return jsonify({"error": "Dados incompletos"}), 400
|
||||
if not project_filename:
|
||||
return jsonify({"error": "Nome do projeto não informado"}), 400
|
||||
|
||||
# Garante pasta
|
||||
os.makedirs(PHYSICAL_IMPORTED_FOLDER, exist_ok=True)
|
||||
os.chmod(PHYSICAL_IMPORTED_FOLDER, 0o775)
|
||||
# Cria pasta imported
|
||||
os.makedirs(XML_IMPORTED_PATH_PREFIX, exist_ok=True)
|
||||
try:
|
||||
os.chmod(XML_IMPORTED_PATH_PREFIX, 0o775)
|
||||
except: pass
|
||||
|
||||
file_map = {}
|
||||
# Dicionário de substituição: { 'Original.wav': 'Novo_Seguro.wav' }
|
||||
replacements = {}
|
||||
|
||||
# 1. Salva os samples
|
||||
for file in uploaded_files:
|
||||
if file and allowed_sample(file.filename):
|
||||
clean_sample_name = secure_filename(file.filename)
|
||||
save_path = os.path.join(PHYSICAL_IMPORTED_FOLDER, clean_sample_name)
|
||||
# request.files é um dicionário imutável, iteramos sobre ele
|
||||
for original_name, file_storage in request.files.items():
|
||||
if file_storage and allowed_sample(file_storage.filename):
|
||||
|
||||
file.save(save_path)
|
||||
# 1. Salva o arquivo fisicamente
|
||||
# Usamos secure_filename no arquivo NOVO para evitar problemas no disco
|
||||
safe_new_name = secure_filename(file_storage.filename)
|
||||
|
||||
# Se secure_filename deixou vazio (ex: "???"), gera um nome
|
||||
if not safe_new_name:
|
||||
safe_new_name = f"sample_{int(time.time())}.wav"
|
||||
|
||||
save_path = os.path.join(XML_IMPORTED_PATH_PREFIX, safe_new_name)
|
||||
file_storage.save(save_path)
|
||||
|
||||
try:
|
||||
os.chmod(save_path, 0o664)
|
||||
except: pass
|
||||
|
||||
# Mapeia nome original -> nome salvo
|
||||
file_map[file.filename] = clean_sample_name
|
||||
file_map[clean_sample_name] = clean_sample_name # Fallback
|
||||
# 2. Mapeia para substituição no XML
|
||||
# A chave 'original_name' vem do `name` do input HTML, que definimos como o nome original
|
||||
replacements[original_name] = safe_new_name
|
||||
|
||||
# 2. Atualiza o XML (.mmp)
|
||||
if update_xml_paths(project_filename, file_map):
|
||||
# 3. Processa e Builda
|
||||
if not replacements:
|
||||
return jsonify({"error": "Nenhum arquivo válido enviado."}), 400
|
||||
|
||||
# 3. Atualiza o XML
|
||||
if update_xml_paths_exact(project_filename, replacements):
|
||||
# 4. Processa (Gera WAV na pasta correta e JSON)
|
||||
return process_and_build(project_filename)
|
||||
else:
|
||||
return jsonify({"error": "Falha ao atualizar referências no projeto."}), 500
|
||||
return jsonify({"error": "Falha ao atualizar o arquivo de projeto."}), 500
|
||||
|
||||
|
||||
def process_and_build(filename):
|
||||
"""Encapsula a chamada do main.py"""
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ SAMPLE_MANIFEST = os.path.join(BASE_PATH, "src_mmpSearch", "metadata", "samples-
|
|||
SAMPLE_SRC = os.path.join(SRC_MMPSEARCH, "samples")
|
||||
MMP_MANIFEST = os.path.join(BASE_PATH, "src_mmpSearch", "metadata", "mmp-manifest.json")
|
||||
DATA_FOLDER = os.path.join(BASE_DATA, "_data")
|
||||
XML_IMPORTED_PATH_PREFIX = os.path.join(SAMPLE_SRC, "imported")
|
||||
SAMPLE_MANIFEST_DATA = os.path.join(DATA_FOLDER, "samples-manifest.json")
|
||||
LOG_FOLDER = os.path.join(SRC_MMPSEARCH, "logs", "handler_logs")
|
||||
CERT_PATH = "/etc/letsencrypt/live/alice.ufsj.edu.br/fullchain.pem"
|
||||
|
|
@ -96,6 +97,9 @@ SUPPORTED_PLUGINS = [
|
|||
"zynaddsubfx",
|
||||
]
|
||||
|
||||
ALLOWED_EXTENSIONS = {"mmp", "mmpz"}
|
||||
ALLOWED_SAMPLE_EXTENSIONS = {'wav', 'mp3', 'ogg', 'flac', 'ds'}
|
||||
|
||||
tags = {}
|
||||
tags["TAG"] = []
|
||||
tags["plugin"] = []
|
||||
|
|
|
|||
Loading…
Reference in New Issue