resolvendo dependencias dos projetos
Deploy / Deploy (push) Successful in 1m36s
Details
Deploy / Deploy (push) Successful in 1m36s
Details
This commit is contained in:
parent
a367abe597
commit
ef95dae4be
|
|
@ -804,6 +804,76 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// --- LÓGICA DE UPLOAD DE SAMPLE AVULSO ---
|
||||||
|
const uploadSampleBtn = document.getElementById("upload-sample-btn");
|
||||||
|
const sampleInput = document.getElementById("sample-file-input");
|
||||||
|
|
||||||
|
if (uploadSampleBtn && sampleInput) {
|
||||||
|
|
||||||
|
// 1. Botão clica no input invisível
|
||||||
|
uploadSampleBtn.addEventListener("click", () => {
|
||||||
|
sampleInput.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Quando o arquivo é selecionado
|
||||||
|
sampleInput.addEventListener("change", async () => {
|
||||||
|
if (sampleInput.files.length === 0) return;
|
||||||
|
|
||||||
|
const file = sampleInput.files[0];
|
||||||
|
|
||||||
|
// Pergunta a categoria para organizar no servidor (opcional)
|
||||||
|
// O backend usa isso para criar pastas: samples/drums, samples/vocals, etc.
|
||||||
|
const category = prompt(
|
||||||
|
"Em qual categoria este sample se encaixa? (Ex: drums, effects, vocals)",
|
||||||
|
"imported"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (category === null) {
|
||||||
|
// Usuário cancelou
|
||||||
|
sampleInput.value = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepara o formulário
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("sample_file", file); // Deve bater com 'sample_file' no Python
|
||||||
|
formData.append("subfolder", category); // Deve bater com 'subfolder' no Python
|
||||||
|
|
||||||
|
// Feedback Visual (ícone girando ou ficando transparente)
|
||||||
|
const originalIcon = uploadSampleBtn.className;
|
||||||
|
uploadSampleBtn.className = "fa-solid fa-spinner fa-spin"; // Ícone de loading
|
||||||
|
uploadSampleBtn.style.pointerEvents = "none";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("https://alice.ufsj.edu.br:33002/api/upload/sample", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
alert("Sucesso! " + result.message);
|
||||||
|
// Opcional: Recarregar a lista de samples lateral se você tiver uma função para isso
|
||||||
|
// reloadBrowser();
|
||||||
|
} else {
|
||||||
|
alert("Erro ao enviar: " + (result.error || "Desconhecido"));
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erro no upload:", error);
|
||||||
|
alert("Erro de conexão com o servidor.");
|
||||||
|
} finally {
|
||||||
|
// Restaura o botão e limpa o input
|
||||||
|
uploadSampleBtn.className = originalIcon;
|
||||||
|
uploadSampleBtn.style.pointerEvents = "auto";
|
||||||
|
sampleInput.value = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -220,17 +220,16 @@ def update_xml_paths_exact(mmp_filename, replacements):
|
||||||
@app.route("/api/download/<project_name>", methods=["GET"])
|
@app.route("/api/download/<project_name>", methods=["GET"])
|
||||||
def download_project_package(project_name):
|
def download_project_package(project_name):
|
||||||
"""
|
"""
|
||||||
Gera um ZIP contendo o arquivo .mmp e apenas os samples importados necessários.
|
Gera um ZIP onde:
|
||||||
Estrutura do ZIP:
|
1. O .mmp é modificado NA VOLÁTIL (sem salvar no disco) para ter caminhos curtos.
|
||||||
- projeto.mmp
|
2. A estrutura do ZIP fica limpa:
|
||||||
- src_mmpSearch/
|
- projeto.mmp
|
||||||
- samples/
|
- imported/
|
||||||
- imported/
|
- sample1.wav
|
||||||
- sample1.wav
|
- sample2.wav
|
||||||
- sample2.wav
|
|
||||||
"""
|
"""
|
||||||
# Garante que estamos pegando o .mmp (mesmo que peçam sem extensão)
|
# Garante extensão .mmp
|
||||||
if not project_name.endswith('.mmp'):
|
if not project_name.lower().endswith('.mmp'):
|
||||||
project_name += '.mmp'
|
project_name += '.mmp'
|
||||||
|
|
||||||
clean_name = secure_filename(project_name)
|
clean_name = secure_filename(project_name)
|
||||||
|
|
@ -239,56 +238,65 @@ def download_project_package(project_name):
|
||||||
if not os.path.exists(mmp_path):
|
if not os.path.exists(mmp_path):
|
||||||
return jsonify({"error": "Projeto não encontrado"}), 404
|
return jsonify({"error": "Projeto não encontrado"}), 404
|
||||||
|
|
||||||
# Prepara o buffer do ZIP na memória (não salva no disco para economizar I/O)
|
# Prepara o buffer do ZIP na memória
|
||||||
memory_file = io.BytesIO()
|
memory_file = io.BytesIO()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# 1. Parseia o XML original do disco
|
||||||
|
tree = ET.parse(mmp_path)
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
# Conjunto para rastrear quais arquivos físicos precisamos colocar no ZIP
|
||||||
|
samples_to_pack = set()
|
||||||
|
|
||||||
|
# 2. Modifica o XML na MEMÓRIA (não afeta o arquivo original no servidor)
|
||||||
|
for audio_node in root.findall(".//audiofileprocessor"):
|
||||||
|
src = audio_node.get('src', '')
|
||||||
|
|
||||||
|
# Verifica se é um sample importado do nosso sistema
|
||||||
|
if src.startswith(XML_IMPORTED_PATH_PREFIX):
|
||||||
|
# Pega apenas o nome do arquivo (ex: 'kick_deep.wav')
|
||||||
|
filename = os.path.basename(src)
|
||||||
|
|
||||||
|
# Define o NOVO caminho curto relativo para dentro do ZIP
|
||||||
|
# Antes: src_mmpSearch/samples/imported/kick_deep.wav
|
||||||
|
# Agora: imported/kick_deep.wav
|
||||||
|
short_path = f"imported/{filename}"
|
||||||
|
|
||||||
|
# Atualiza o XML na memória
|
||||||
|
audio_node.set('src', short_path)
|
||||||
|
|
||||||
|
# Adiciona à lista de arquivos para copiar
|
||||||
|
samples_to_pack.add(filename)
|
||||||
|
|
||||||
|
# 3. Cria o ZIP
|
||||||
with zipfile.ZipFile(memory_file, 'w', zipfile.ZIP_DEFLATED) as zf:
|
with zipfile.ZipFile(memory_file, 'w', zipfile.ZIP_DEFLATED) as zf:
|
||||||
|
|
||||||
# 1. Adiciona o arquivo .mmp na RAIZ do ZIP
|
# A) Escreve o .mmp MODIFICADO no ZIP
|
||||||
# arcname é o nome que o arquivo terá dentro do zip
|
# Convertemos a árvore XML modificada para string binária
|
||||||
zf.write(mmp_path, arcname=clean_name)
|
xml_str = ET.tostring(root, encoding='utf-8', method='xml')
|
||||||
|
zf.writestr(clean_name, xml_str)
|
||||||
|
|
||||||
# 2. Lê o XML para descobrir quais samples incluir
|
# B) Adiciona os samples físicos na pasta 'imported/' do ZIP
|
||||||
tree = ET.parse(mmp_path)
|
|
||||||
root = tree.getroot()
|
|
||||||
|
|
||||||
# Conjunto para evitar adicionar o mesmo sample 2x
|
|
||||||
samples_to_pack = set()
|
|
||||||
|
|
||||||
for audio_node in root.findall(".//audiofileprocessor"):
|
|
||||||
src = audio_node.get('src', '')
|
|
||||||
|
|
||||||
# Verifica se é um sample importado (nosso padrão)
|
|
||||||
# O src no XML já deve estar como: src_mmpSearch/samples/imported/nome.wav
|
|
||||||
if src.startswith(XML_IMPORTED_PATH_PREFIX):
|
|
||||||
# Extrai apenas o nome do arquivo (ex: kick.wav)
|
|
||||||
sample_filename = os.path.basename(src)
|
|
||||||
samples_to_pack.add(sample_filename)
|
|
||||||
|
|
||||||
# 3. Adiciona os samples encontrados ao ZIP mantendo a estrutura de pasta
|
|
||||||
for sample_name in samples_to_pack:
|
for sample_name in samples_to_pack:
|
||||||
# Caminho físico no servidor (onde o arquivo realmente está)
|
|
||||||
physical_path = os.path.join(XML_IMPORTED_PATH_PREFIX, sample_name)
|
physical_path = os.path.join(XML_IMPORTED_PATH_PREFIX, sample_name)
|
||||||
|
|
||||||
# Caminho DENTRO do ZIP (para o LMMS ler relativo ao mmp)
|
# Caminho curto dentro do ZIP
|
||||||
# Deve ser: src_mmpSearch/samples/imported/sample.wav
|
zip_entry_name = f"imported/{sample_name}"
|
||||||
zip_internal_path = f"{XML_IMPORTED_PATH_PREFIX}/{sample_name}"
|
|
||||||
|
|
||||||
if os.path.exists(physical_path):
|
if os.path.exists(physical_path):
|
||||||
zf.write(physical_path, arcname=zip_internal_path)
|
zf.write(physical_path, arcname=zip_entry_name)
|
||||||
print(f"[ZIP] Adicionado: {sample_name}")
|
print(f"[ZIP CLEAN] Adicionado: {zip_entry_name}")
|
||||||
else:
|
else:
|
||||||
print(f"[ZIP] AVISO: Sample listado no XML mas não encontrado no disco: {sample_name}")
|
print(f"[ZIP CLEAN] AVISO: Arquivo faltante no disco: {sample_name}")
|
||||||
|
|
||||||
# Finaliza o ponteiro do arquivo
|
# Finaliza e envia
|
||||||
memory_file.seek(0)
|
memory_file.seek(0)
|
||||||
|
|
||||||
return send_file(
|
return send_file(
|
||||||
memory_file,
|
memory_file,
|
||||||
mimetype='application/zip',
|
mimetype='application/zip',
|
||||||
as_attachment=True,
|
as_attachment=True,
|
||||||
download_name=f"{os.path.splitext(clean_name)[0]}_pack.zip"
|
download_name=f"{os.path.splitext(clean_name)[0]}.zip"
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue