Files
mmpSearch/pages/enviaProjeto.md
T

20 KiB
Executable File

layout, title, permalink
layout title permalink
default MMPSearch - Envie seu projeto /envie_seu_projeto/

  <div class="tabs is-centered is-boxed is-medium mb-6">
    {% include sidebar.html %}
  </div>

  <div class="columns is-centered">
    <div class="column is-8 has-text-centered">
        <h1 class="title is-3 has-text-grey-dark mb-3">
            <span class="icon has-text-info mr-2"><i class="fa-solid fa-cloud-arrow-up"></i></span>
            Enviar Projeto
        </h1>
        <p class="subtitle is-6 has-text-grey">
            Contribua com a comunidade! Envie seus arquivos <code>.mmp</code> ou <code>.mmpz</code> para que outros possam estudar e remixar.
        </p>
        <div style="width: 60px; height: 4px; background-color: #3273dc; margin: 1rem auto; border-radius: 2px;"></div>
    </div>
  </div>

  <div class="columns is-centered">
    <div class="column is-8">

        <div class="box p-5" style="background-color: #f0f8ff; border: 1px solid #cfe8fc; border-radius: 12px;">
            <form id="upload-project-form">

                <div class="field mb-5">
                    <label class="label has-text-grey-dark">Arquivo do Projeto (LMMS)</label>
                    <div class="file-upload-wrapper" id="drop-zone">
                        <input class="file-upload-input" type="file" name="project_file" id="project_file" accept=".mmp, .mmpz" required>
                        <div class="drag-text has-text-centered">
                            <span class="icon is-large has-text-info mb-2"><i class="fa-solid fa-file-audio fa-3x"></i></span>
                            <h3 class="title is-5 has-text-grey-dark mb-1">Arraste e solte o arquivo aqui</h3>
                            <p class="is-size-7 has-text-grey">ou clique para selecionar (.mmp, .mmpz)</p>
                            <p id="file-name-display" class="tag is-info is-light mt-3 is-hidden"></p>
                        </div>
                    </div>
                </div>

                <div class="columns">
                    <div class="column is-6">
                        <div class="field">
                            <label class="label has-text-grey-dark">Nome do Projeto</label>
                            <div class="control has-icons-left">
                                <input class="input" type="text" placeholder="Ex: Manda a maladeza" required>
                                <span class="icon is-small is-left">
                                    <i class="fa-solid fa-music"></i>
                                </span>
                            </div>
                        </div>
                    </div>
                    <div class="column is-6">
                        <div class="field">
                            <label class="label has-text-grey-dark">Qual o seu vulgo?</label>
                            <div class="control has-icons-left">
                                <input class="input" type="text" placeholder="Seu nome ou nick" required>
                                <span class="icon is-small is-left">
                                    <i class="fa-solid fa-user"></i>
                                </span>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="columns">
                    <div class="column is-4">
                        <div class="field">
                            <label class="label has-text-grey-dark">BPM</label>
                            <div class="control has-icons-left">
                                <input class="input" type="number" placeholder="128" min="1" max="999">
                                <span class="icon is-small is-left">
                                    <i class="fa-solid fa-gauge-high"></i>
                                </span>
                            </div>
                        </div>
                    </div>
                    <div class="column is-8">
                        <div class="field">
                            <label class="label has-text-grey-dark">Tags (Opcional)</label>
                            <div class="control has-icons-left">
                                <input class="input" type="text" placeholder="Ex: rap, hip hop, boom bap, remix">
                                <span class="icon is-small is-left">
                                    <i class="fa-solid fa-tags"></i>
                                </span>
                            </div>
                            <p class="help">Separe por vírgulas.</p>
                        </div>
                    </div>
                </div>

                <div class="field">
                    <label class="label has-text-grey-dark">Descrição</label>
                    <div class="control">
                        <textarea class="textarea" placeholder="Conte um pouco sobre como você criou esse projeto, plugins usados, etc." rows="3"></textarea>
                    </div>
                </div>

                <div class="field mb-5">
                    <label class="label has-text-grey-dark">Preview de Áudio (Opcional)</label>
                    <div class="file has-name is-fullwidth">
                      <label class="file-label">
                        <input class="file-input" type="file" name="audio_preview" accept=".wav, .mp3, .ogg">
                        <span class="file-cta">
                          <span class="file-icon">
                            <i class="fa-solid fa-upload"></i>
                          </span>
                          <span class="file-label">
                            Escolher arquivo de áudio...
                          </span>
                        </span>
                        <span class="file-name has-text-grey-light">
                          Nenhum arquivo selecionado
                        </span>
                      </label>
                    </div>
                    <p class="help">Envie um .mp3 ou .wav para que as pessoas possam ouvir antes de baixar.</p>
                </div>

                <div class="field mt-5">
                    <div class="control">
                        <button type="submit" class="button is-info is-fullwidth is-medium shadow-sm" id="submit-btn">
                            <span class="icon"><i class="fa-solid fa-paper-plane"></i></span>
                            <span>Enviar Projeto</span>
                        </button>
                    </div>
                </div>

            </form>
        </div>

    </div>
  </div>

</div>

Sucesso!

Projeto Enviado!

Obrigado por contribuir. Seu projeto está sendo processado e em breve aparecerá na lista.

Ver Projetos Enviar Outro

Arquivos Externos Detectados

<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="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>

      <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>
<style> /* Estilo do Dropzone */ .file-upload-wrapper { position: relative; width: 100%; height: 200px; border: 2px dashed #3273dc; border-radius: 8px; background-color: #fff; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; cursor: pointer; } .file-upload-wrapper:hover, .file-upload-wrapper.dragover { background-color: #eef6fc; border-color: #205081; } .file-upload-input { position: absolute; width: 100%; height: 100%; top: 0; left: 0; opacity: 0; cursor: pointer; } .drag-text { pointer-events: none; /* Deixa o clique passar para o input */ } /* Inputs customizados */ .input:focus, .textarea:focus { border-color: #3273dc; box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } </style> <script> document.addEventListener('DOMContentLoaded', () => { // --- ELEMENTOS GERAIS --- const dropZone = document.getElementById('drop-zone'); const fileInput = document.getElementById('project_file'); const fileNameDisplay = document.getElementById('file-name-display'); const dragTextTitle = dropZone.querySelector('.title'); // --- MODAIS --- const successModal = document.getElementById('success-modal'); const resolveModal = document.getElementById('resolve-modal'); const closeBtns = document.querySelectorAll('.delete, #close-success-modal, .modal-background'); // --- FORMULÁRIOS --- const mainForm = document.getElementById('upload-project-form'); const resolveForm = document.getElementById('resolve-form'); const submitBtn = document.getElementById('submit-btn'); const resolveBtn = document.getElementById('resolve-btn'); // 1. VISUAL DO DROPZONE ['dragenter', 'dragover'].forEach(evt => dropZone.addEventListener(evt, (e) => { e.preventDefault(); dropZone.classList.add('dragover'); })); ['dragleave', 'drop'].forEach(evt => dropZone.addEventListener(evt, (e) => { dropZone.classList.remove('dragover'); })); fileInput.addEventListener('change', () => { if (fileInput.files.length > 0) { fileNameDisplay.textContent = fileInput.files[0].name; fileNameDisplay.classList.remove('is-hidden'); dragTextTitle.textContent = "Arquivo Pronto!"; dropZone.style.borderColor = "#4caf50"; dropZone.style.backgroundColor = "#f0fff4"; } }); // 2. INPUT DE ÁUDIO & RESOLVE (Nomes de arquivo) document.querySelectorAll('.file-input').forEach(input => { input.addEventListener('change', () => { if (input.files.length > 0) { const nameContainer = input.parentElement.querySelector('.file-name'); if (nameContainer) { nameContainer.textContent = input.files.length > 1 ? `${input.files.length} arquivos selecionados` : input.files[0].name; } } }); }); // 3. FUNÇÃO DE FECHAR MODAIS closeBtns.forEach(btn => { btn.addEventListener('click', () => { successModal.classList.remove('is-active'); resolveModal.classList.remove('is-active'); document.documentElement.classList.remove('is-clipped'); }); }); // ============================================================ // 4. SUBMISSÃO PRINCIPAL (PROJETO) // ============================================================ mainForm.addEventListener('submit', async (e) => { e.preventDefault(); submitBtn.classList.add('is-loading'); const formData = new FormData(mainForm); try { // Rota principal de upload const response = await fetch('https://alice.ufsj.edu.br:33002/api/upload', { method: 'POST', body: formData }); // Parseamos o JSON independente do status para ler mensagens const result = await response.json(); submitBtn.classList.remove('is-loading'); if (response.status === 200) { // SUCESSO TOTAL (Status 200) mostrarSucesso(); mainForm.reset(); resetDropzone(); } else if (response.status === 202) { // SUCESSO PARCIAL (Status 202 - Faltam Samples) mostrarResolveModal(result); } else { // ERRO (400, 500) alert('Erro: ' + (result.error || 'Ocorreu um erro desconhecido.')); } } catch (error) { console.error('Erro:', error); alert('Erro de conexão com o servidor.'); submitBtn.classList.remove('is-loading'); } }); // ============================================================ // 5. SUBMISSÃO DE RESOLUÇÃO (SAMPLES FALTANDO) // ============================================================ resolveForm.addEventListener('submit', async (e) => { e.preventDefault(); resolveBtn.classList.add('is-loading'); const formData = new FormData(resolveForm); try { // Rota de resolução (envio dos samples faltantes) const response = await fetch('https://alice.ufsj.edu.br:33002/api/upload/resolve', { method: 'POST', body: formData }); const result = await response.json(); resolveBtn.classList.remove('is-loading'); if (response.ok) { // Agora sim, tudo certo! resolveModal.classList.remove('is-active'); mostrarSucesso(); mainForm.reset(); resetDropzone(); } else { alert('Erro ao enviar samples: ' + (result.error || 'Erro desconhecido')); } } catch (error) { console.error(error); alert('Erro ao enviar samples.'); resolveBtn.classList.remove('is-loading'); } }); // --- FUNÇÕES AUXILIARES --- function mostrarSucesso() { successModal.classList.add('is-active'); document.documentElement.classList.add('is-clipped'); } // --- FUNÇÃO PARA GERAR A TABELA DE UPLOAD --- function mostrarResolveModal(data) { // 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 const resolveBtn = document.getElementById('resolve-btn'); resolveBtn.disabled = true; // Começa desabilitado até preencher tudo // 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 = `${originalName}`; // Coluna 2: Ícone de Status const tdStatus = document.createElement('td'); tdStatus.style.verticalAlign = "middle"; tdStatus.style.textAlign = "center"; tdStatus.innerHTML = ``; // 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 = ``; // 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 = ``; // Ícone de trocar const icon = tdStatus.querySelector('.status-icon'); icon.innerHTML = ``; 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++; } // 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'); } function resetDropzone() { fileNameDisplay.classList.add('is-hidden'); dragTextTitle.textContent = "Arraste e solte o arquivo aqui"; dropZone.style.borderColor = "#3273dc"; dropZone.style.backgroundColor = "#fff"; } }); </script>