From 596d0dcfb8f017f75e58f03848bc56e7ac529e45 Mon Sep 17 00:00:00 2001 From: JotaChina Date: Sat, 4 Apr 2026 13:27:22 -0300 Subject: [PATCH] melhorando build --- _layouts/projetos.html | 686 ++++++-------------------- pages/projetos.html | 1070 ++-------------------------------------- readme.md | 77 +-- requirements.txt | 48 ++ 4 files changed, 272 insertions(+), 1609 deletions(-) create mode 100644 requirements.txt diff --git a/_layouts/projetos.html b/_layouts/projetos.html index 2210c3a1..7e166107 100755 --- a/_layouts/projetos.html +++ b/_layouts/projetos.html @@ -1,580 +1,172 @@ --- layout: default -title: "{{ page.file }}" +title: "Visualizando Projeto" --- -
+
+ +
+ +
-{% capture instrument_logic %} {% assign plugin_name = -instrument.instrument_name | downcase %} {% assign is_sample = false %} {% -assign sample_src = "" %} {% if plugin_name == 'audiofileprocessor' or -instrument.audiofileprocessor %} {% assign is_sample = true %} {% assign -sample_src = instrument.audiofileprocessor.src | default: "" | strip %} {% endif -%} {% assign should_open = false %} {% if is_sample == false %} {% assign -should_open = true %} {% elsif is_sample == true and sample_src != "" %} {% -assign should_open = true %} {% endif %} - -
-
  • -
    - -
    - {% assign display_name = instrument.instrument_name %} {% if is_sample - %} {% if display_name contains "audiofileprocessor" and - instrument.patterns %} {% assign first_pattern_name = - instrument.patterns | map: 'name' | first %} {% if first_pattern_name - and first_pattern_name != empty %} {% assign display_name = - first_pattern_name | remove: ".ogg" | remove: ".ogg" | remove: ".flac" - | remove: ".mp3" %} {% elsif instrument.audiofileprocessor.src %} {% - assign src_parts = instrument.audiofileprocessor.src | split: '/' %} - {% assign file_name = src_parts | last %} {% assign display_name = - file_name | remove: ".ogg" | remove: ".ogg" | remove: ".flac" | - remove: ".mp3" %} {% endif %} {% endif %} {% endif %} {% assign - instrument_slug = display_name | replace: ' ', '+' %} - - {{ display_name }} - {% unless is_sample %} - (Synth 🎹){% - endunless %} - -
    -
    - - {% for pattern in instrument.patterns %} {% assign pattern_steps = - pattern.steps %} {% if pattern_steps and pattern_steps.size > 0 %} -
    - {% assign bassline_index = forloop.index | minus: 1 %} {% if - track.bassline_name %} - {{ bassline_index }}: - {% endif %} - -
    - {% assign total_steps = pattern_steps.size %} {% assign chunk_size = 4 - %} {% assign num_chunks = total_steps | divided_by: chunk_size %} {% - assign remainder = total_steps | modulo: chunk_size %} {% if remainder - > 0 %}{% assign num_chunks = num_chunks | plus: 1 %}{% endif %} {% for - i in (0..num_chunks) %} {% assign start_index = i | times: chunk_size - %} {% assign current_chunk_array = pattern_steps | slice: start_index, - chunk_size %} {% if current_chunk_array.size > 0 %} {% assign - chunk_string = "" %} {% for step in current_chunk_array %} {% if step - == true or step == 'true' or step == 1 %} {% assign chunk_string = - chunk_string | append: '1' %} {% else %} {% assign chunk_string = - chunk_string | append: '0' %} {% endif %} {% endfor %} {% assign - search_url = '/pattern/?p=' | append: chunk_string | relative_url %} - -
    - {% for step_active in current_chunk_array %} {% assign step_color - = '#e0e0e0' %} {% if step_active == true or step_active == 'true' - or step_active == 1 %} {% assign step_color = '#4caf50' %} {% - endif %} -
    - {% endfor %} -
    -
    - {% endif %} {% endfor %} -
    -
    - {% endif %} {% endfor %} -
    - -
    - {% if is_sample %} {% if sample_src != "" %} {% assign - audio_filename_with_path = 'src/samples/' | append: sample_src %} - - {% else %} -

    O sample **não foi enviado** 😢

    - {% endif %} {% else %} - - Via Web Audio API - {% endif %} -
    -
  • -
    -{% endcapture %} - - - - - diff --git a/pages/projetos.html b/pages/projetos.html index 1acb26ee..bce5f89c 100755 --- a/pages/projetos.html +++ b/pages/projetos.html @@ -4,1056 +4,66 @@ title: MMPSearch - Projetos permalink: /projetos/ --- -{% assign list_bpm = "" %} -{% assign list_plugins = "" %} -{% assign list_instruments = "" %} -{% assign list_samples = "" %} -{% assign list_bassline = "" %} -{% assign list_automation = "" %} - -{% for p in site.data.all %} - {% assign raw_bpm = p.bpm | append: "" %} - {% if raw_bpm != "" and raw_bpm != "N/A" and raw_bpm != "nil" %} - {% assign list_bpm = list_bpm | append: raw_bpm | append: "|||" %} - {% endif %} - - {% for item in p.tags.plugin %}{% if item != "" %}{% assign list_plugins = list_plugins | append: item | append: "|||" %}{% endif %}{% endfor %} - {% if p.tags.bassline %}{% for item in p.tags.bassline %}{% if item != "" %}{% assign list_bassline = list_bassline | append: item | append: "|||" %}{% endif %}{% endfor %}{% endif %} - {% if p.tags.automation %}{% for item in p.tags.automation %}{% if item != "" %}{% assign list_automation = list_automation | append: item | append: "|||" %}{% endif %}{% endfor %}{% endif %} - - {% for track in p.tracks %} - {% if track.instruments %}{% for inst in track.instruments %}{% if inst.instrument_name %}{% assign list_instruments = list_instruments | append: inst.instrument_name | append: "|||" %}{% endif %}{% endfor %} - {% elsif track.instrument_name %}{% assign list_instruments = list_instruments | append: track.instrument_name | append: "|||" %}{% endif %} - {% if track.sample %}{% for smp in track.sample %}{% assign list_samples = list_samples | append: smp.sample_name | append: "|||" %}{% endfor %} - {% elsif track.sample_name %}{% assign list_samples = list_samples | append: track.sample_name | append: "|||" %}{% endif %} - {% endfor %} -{% endfor %} - -{% assign unique_bpm_array = list_bpm | split: "|||" | uniq | sort %} -{% assign unique_plugins = list_plugins | split: "|||" | uniq | sort %} -{% assign unique_instruments = list_instruments | split: "|||" | uniq | sort %} -{% assign unique_samples = list_samples | split: "|||" | uniq | sort %} -{% assign unique_bassline = list_bassline | split: "|||" | uniq | sort %} -{% assign unique_automation = list_automation | split: "|||" | uniq | sort %} - -{% assign min_limit = 0 %} -{% assign max_limit = 300 %} -{% if unique_bpm_array.size > 0 %} - {% assign last_bpm = unique_bpm_array | last | plus: 0 %} - {% if last_bpm > max_limit %}{% assign max_limit = last_bpm %}{% endif %} -{% endif %} -

    -
    {% include sidebar.html %}
    -
    -
    -

    📁 Projetos Disponíveis

    -

    - Utilize os filtros laterais para refinar sua busca. -

    -
    - -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    - -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    -
    - -
    - -
    - - - - - -
    -
    -
    - -
    - 🥁 Pattern Rítmico -
    - -
    -
    -

    Desenhe o ritmo:

    - -

    Clique para ativar steps

    -
    -
    -
    -
    - Gêneros (IA) -
    - -
    - -
    -
    - -
    - Tom / Key -
    - -
    -
    - {% assign keys_list = "C,Cm,C#,C#m,Db,Dm,D,D#m,Eb,E,Em,F,Fm,F#,F#m,Gb,G,Gm,G#,Ab,A,Am,Bb,B,Bm" | split: "," %} - {% for key in keys_list %} - - {% endfor %} -
    -
    -
    - -
    - Faixa de BPM -
    - -
    -
    -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - {% assign filter_groups = "instruments|Instrumentos,plugins|Plugins,bassline|Bassline,automation|Automação,samples|Samples" | split: "," %} - {% for group in filter_groups %} - {% assign parts = group | split: "|" %} - {% assign cat_key = parts[0] %} - {% assign cat_label = parts[1] %} - -
    - {{ cat_label }} -
    - -
    -
    - {% assign current_list = "" %} - {% if cat_key == 'instruments' %}{% assign current_list = unique_instruments %} - {% elsif cat_key == 'plugins' %}{% assign current_list = unique_plugins %} - {% elsif cat_key == 'bassline' %}{% assign current_list = unique_bassline %} - {% elsif cat_key == 'automation' %}{% assign current_list = unique_automation %} - {% elsif cat_key == 'samples' %}{% assign current_list = unique_samples %} - {% endif %} - - {% for item in current_list %}{% if item != "" %} - - {% endif %}{% endfor %} -
    -
    -
    - {% endfor %} - -
    +
    - - - -
    - {% assign project_pages = site.pages | where_exp: "page", "page.path contains 'projetos/'" | sort: "title" %} - {% for page in project_pages %} - {% if page.url != '/projetos/' %} - - {% assign raw_bpm = page.bpm | append: "" %} - {% if raw_bpm == "" or raw_bpm == "N/A" or raw_bpm == "nil" %}{% assign p_bpm = 0 %}{% else %}{% assign p_bpm = raw_bpm | plus: 0 %}{% endif %} - - {% assign p_insts_array = "" | split: "," %} - - {% assign project_patterns_flat = "" | split: "," %} - {% for track in page.tracks %} - {% if track.instruments %}{% for inst in track.instruments %}{% if inst.instrument_name %}{% assign p_insts_array = p_insts_array | push: inst.instrument_name %}{% endif %} - {% if inst.patterns %} - {% for pattern in inst.patterns %} - {% assign pattern_steps = pattern.steps %} - {% if pattern_steps and pattern_steps.size > 0 %} - {% assign chunk_string = "" %} - {% assign step_count = 0 %} - {% for step in pattern_steps %} - {% if step == true or step == 'true' or step == 1 %}{% assign chunk_string = chunk_string | append: '1' %}{% else %}{% assign chunk_string = chunk_string | append: '0' %}{% endif %} - {% assign step_count = step_count | plus: 1 %} - {% if step_count == 4 %} - {% unless project_patterns_flat contains chunk_string %} - {% assign project_patterns_flat = project_patterns_flat | push: chunk_string %} - {% endunless %} - {% assign chunk_string = "" %} - {% assign step_count = 0 %} - {% endif %} - {% endfor %} - {% endif %} - {% endfor %} - {% endif %} - {% endfor %} - {% elsif track.instrument_name %}{% assign p_insts_array = p_insts_array | push: track.instrument_name %}{% endif %} - {% endfor %} - - {% assign p_patterns_str = project_patterns_flat | join: ',' %} - {% assign p_instruments_str = p_insts_array | uniq | join: ',' %} - {% assign p_plugins = page.tags.plugin | join: ',' %} - {% assign p_bassline = page.tags.bassline | join: ',' %} - {% assign p_automation = page.tags.automation | join: ',' %} - {% assign p_samples = page.tags.sample | join: ',' %} - {% assign p_name = page.title | default: page.name | downcase %} - -
    - -
    - -{% assign matching_beat = nil %} -{% if site.data.beats %} - {% assign proj_slug = page.file | slugify %} - {% for filename in site.data.beats %} - {% assign beat_slug = filename | remove: '.ogg' | remove: '.mp3' | slugify %} - {% if beat_slug == proj_slug %} - {% assign matching_beat = filename %} - {% break %} - {% endif %} - {% endfor %} -{% endif %} - - -
    - -
    - -
    - -

    -{{ page.title | default: page.name | replace: '.html', '' }} -

    - -
    -{% if p_bpm > 0 %} -🎵 {{ p_bpm }} BPM -{% else %} -⚠️ BPM N/A -{% endif %} -
    - -{% if matching_beat %} -
    - -
    -{% endif %} - -
    - -
    -{% assign unique_insts_page = p_insts_array | uniq | sort %} -{% if unique_insts_page.size > 0 %} -
    -
    🎸 INSTRUMENTS
    -
    -
    -{% endif %} - -{% assign has_patterns_display = false %} -{% capture patterns_html_content %} -
    - {% for track in page.tracks %} - {% if track.instruments %} - {% for inst in track.instruments %} - {% if inst.patterns %} - {% assign current_inst_patterns = "" | split: "," %} - {% for pattern in inst.patterns %} - {% assign p_steps = pattern.steps %} - {% if p_steps and p_steps.size > 0 %} - {% assign total_s = p_steps.size %} - {% assign n_chunks = total_s | divided_by: 4 %} - {% for i in (0..n_chunks) %} - {% assign idx = i | times: 4 %} - {% assign chunk = p_steps | slice: idx, 4 %} - {% if chunk.size > 0 %} - {% assign c_str = "" %} - {% for s in chunk %} - {% if s == true or s == 1 or s == 'true' %}{% assign c_str = c_str | append: "1" %}{% else %}{% assign c_str = c_str | append: "0" %}{% endif %} - {% endfor %} - {% if c_str != "0000" %} - {% unless current_inst_patterns contains c_str %} - {% assign current_inst_patterns = current_inst_patterns | push: c_str %} - {% endunless %} - {% endif %} - {% endif %} - {% endfor %} - {% endif %} - {% endfor %} - - {% if current_inst_patterns.size > 0 %} - {% assign has_patterns_display = true %} -
    - - {{ inst.instrument_name | truncate: 12 }} - -
    - {% for pat in current_inst_patterns %} -
    - {% assign bits = pat | split: '' %} - {% for bit in bits %} -
    - {% endfor %} -
    - {% endfor %} -
    -
    - {% endif %} - {% endif %} - {% endfor %} - {% endif %} - {% endfor %} -
    -{% endcapture %} - -{% if has_patterns_display %} -
    - -
    🥁 RITMO
    - -
    -
    - {{ patterns_html_content }} -
    -
    -{% endif %} -{% if page.tags.plugin.size > 0 %} -
    -
    🔌 PLUGINS
    -
    -{% for tag in page.tags.plugin %}{% if tag != "" %} -{{ tag | truncate: 18 }} -{% endif %}{% endfor %} -
    -
    -{% endif %} - -{% if page.tags.bassline.size > 0 %} -
    -
    🎹 BASSLINE
    -
    -{% for tag in page.tags.bassline %}{% if tag != "" %} -{{ tag | truncate: 18 }} -{% endif %}{% endfor %} -
    -
    -{% endif %} - -{% if page.tags.automation.size > 0 %} -
    -
    🎚️ AUTOMATION
    -
    -{% for tag in page.tags.automation %}{% if tag != "" %} -{{ tag | truncate: 18 }} -{% endif %}{% endfor %} -
    -
    -{% endif %} - -{% if page.tags.sample.size > 0 %} -
    -
    🎤 SAMPLES
    -
    -{% for tag in page.tags.sample %}{% if tag != "" %} -{{ tag | truncate: 18 }} -{% endif %}{% endfor %} -
    -
    -{% endif %} -
    -
    - - -
    -Prévia -{% assign creation_url = '/mmpSearch/creation.html?project=' | append: page.file %} -{% assign embed_url = creation_url | append: '&embed=true' %} -Prévia do Editor -
    - -
    -
    -{% endif %} -{% endfor %} -
    - - +
    +
    -{% include preview-modal.html %} - - - \ No newline at end of file + }); + diff --git a/readme.md b/readme.md index ae10464a..37aa91d1 100755 --- a/readme.md +++ b/readme.md @@ -16,67 +16,80 @@ O sistema é um ecossistema completo para mineração, submissão, processamento O script `crawler.py` atua como um bot de mineração para popular a base de dados da plataforma baixando projetos diretamente do repositório oficial (LMMS Sharing Platform - `lmms.io/lsp/`). -- **Segurança e Estabilidade:** O crawler é multiprocessado (limitado a 4 workers) e possui um sistema de _throttle_ que pausa a execução caso a memória RAM livre do servidor caia abaixo de 500MB. -- **Sanitização e Auditoria:** Os arquivos baixados têm seus nomes sanitizados (slugify) e o processo gera um arquivo de auditoria (`audit_crawler.csv`) registrando tamanho, velocidade e status de cada download. +- **Segurança e Estabilidade:** O crawler é multiprocessado e possui um sistema de _throttle_ que pausa a execução caso a memória RAM livre do servidor caia abaixo de 500MB. +- **Sanitização e Auditoria:** Utiliza `BeautifulSoup4` para extração de links e gera relatórios de performance em CSV. ### 2. Pipeline ETL e Motor de Renderização -A conversão dos arquivos nativos em artefatos web é orquestrada pelo script `pipeline_etl.sh`, que executa três etapas sequenciais: +A conversão dos arquivos nativos em artefatos web é orquestrada pelo script `pipeline_etl.sh`, executando o processamento em lotes: -1. **Extração e Renderização (`handler/main.py`):** O script utiliza a biblioteca `multiprocessing` e processa os projetos em lotes. Ele invoca o binário do LMMS em modo `offscreen` (Headless) para exportar o áudio gerado nativamente em `.ogg` e converte a estrutura do `.mmp` em dicionários de dados (JSON/YAML). -2. **Análise de Áudio (`classificacao/analise_audio.py`):** Processamento das características sonoras. -3. **Classificação Mestre (`classificacao/classificacao_mestre.py`):** Organização final e indexação para o frontend. +1. **Extração e Renderização (`handler/main.py`):** Invoca o LMMS em modo `offscreen` para gerar áudio e converte o XML `.mmp` em JSON/YAML. +2. **Análise e Inteligência Artificial:** Utiliza a biblioteca **Essentia (Tensorflow)** para extração de características acústicas e classificação de áudio. -### 3. MMPSearch (Catálogo Estático Frontend) +--- -Biblioteca gerada via Jekyll a partir dos arquivos JSON/YAML exportados pelo Pipeline ETL, focada em leveza, indexação e busca de projetos, com renderização de áudio via Web Audio API. +## 📦 Ecossistema Python e Dependências (venv) -### 4. MMPCreator (Colaboração em Tempo Real) +O ambiente virtual (`venv`) contém as bibliotecas necessárias para o ciclo de vida do dado. As principais dependências instaladas são: -Sequenciador interativo. Utiliza WebSockets para sincronizar edições simultâneas (áudio e MIDI) entre múltiplos usuários em salas virtuais. +### Web & API Framework + +- **Flask (v3.1.2):** Engine principal da API. +- **Flask-SocketIO:** Suporte a comunicação bi-direcional via WebSockets. +- **Gunicorn:** Servidor HTTP WSGI para produção. +- **Flask-Admin & WTForms:** Interface de gerenciamento e validação de formulários. + +### Segurança e Persistência + +- **SQLAlchemy (v2.0):** ORM para manipulação do banco `users.db`. +- **Flask-Bcrypt & Cryptography:** Hashing de senhas e camadas de criptografia. + +### Processamento e Ciência de Dados + +- **Essentia-Tensorflow:** Biblioteca avançada para análise de áudio e music information retrieval (MIR). +- **NumPy:** Suporte para arrays multidimensionais e cálculos matemáticos pesados. +- **PyYAML:** Geração de metadados para integração com o Jekyll. + +### Utilidades e Monitoramento + +- **Psutil:** Monitoramento de hardware para controle de processos paralelos. +- **Watchdog:** Monitoramento de eventos no sistema de arquivos. +- **tqdm:** Barras de progresso para tarefas de longa duração no terminal. --- ## ⚙️ Serviços de Hospedagem (Produção) -A infraestrutura em produção é orquestrada por gerenciadores de serviço do Linux (`systemd`), garantindo que a API e o servidor de WebSockets estejam sempre no ar. +A infraestrutura é mantida via `systemd` para garantir alta disponibilidade. -### Servidor de Upload, Autenticação e Build (Flask/Gunicorn) +### API de Upload e Processamento (Gunicorn) -Serviço responsável por receber arquivos, gerenciar a sessão dos usuários (SQLite, Bcrypt, Flask-Login) e automatizar o build do site via threads em segundo plano. - -- **Serviço:** `/etc/systemd/system/upload_mmpSearch.service` -- **Tecnologia:** Gunicorn servindo a aplicação Flask (`upload_server:app`). -- **Configuração:** Roda com o usuário `www-data`. Utiliza 3 _workers_ e possui um timeout estendido de 300 segundos, o que é vital para não derrubar a conexão durante renderizações pesadas de áudio. -- **Acesso da API:** `http://0.0.0.0:33002` +- **Serviço:** `upload_mmpSearch.service` +- **Porta:** 33002 +- **Timeout:** 300s (Essencial para o tempo de renderização do LMMS) ### Backend de Colaboração (NodeJS) -Hospeda a lógica de WebSockets responsável por gerenciar as salas e a colaboração online do editor MMPCreator em tempo real. - -- **Serviço:** `/etc/systemd/system/backend-MMPCreator.service` -- **Tecnologia:** NodeJS (`server.js`). -- **Configuração de Segurança:** Roda sob o usuário `www-data` e possui acesso direto aos certificados Let's Encrypt via variáveis de ambiente (`SSL_FULLCHAIN` e `SSL_PRIVKEY`). Essa abordagem elimina a necessidade do Apache atuar como proxy reverso, facilitando a comunicação direta e segura via `wss://` e `https://`. -- **Comportamento:** Configurado com `Restart=always` para garantir alta disponibilidade. +- **Serviço:** `backend-MMPCreator.service` +- **Porta:** 33001 +- **Tecnologia:** Sincronização via Socket.io com SSL nativo. --- ## 🛠️ Área de Desenvolvimento (Execução Manual) -Para testes ou execuções manuais, o site está sendo desenvolvido em um ambiente virtual Python (`venv`). - -1. Ative o ambiente: +1. Ative o ambiente virtual: `source venv/bin/activate` -2. Para inicializar a biblioteca de samples do MMPCreator: - `cd scripts/creations && python generateManifest.py` - -3. Para forçar a indexação e execução manual do pipeline ETL: +2. Execução do Pipeline ETL completo: `cd scripts && ./pipeline_etl.sh` +3. Inicialização de Manifestos: + `cd scripts/creations && python generateManifest.py` + --- -## ✅ Funcionalidades Já Implementadas +## ✅ Funcionalidades Implementadas - [x] Web Crawler automatizado com auditoria de hardware. - [x] Processamento de arquivos LMMS de forma _headless_ (renderização de áudio em servidor sem interface gráfica). diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..d5de4383 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,48 @@ +bcrypt==5.0.0 +beautifulsoup4==4.14.3 +bidict==0.23.1 +blinker==1.9.0 +bs4==0.0.2 +certifi==2025.10.5 +cffi==2.0.0 +charset-normalizer==3.4.4 +click==8.3.1 +cryptography==46.0.3 +essentia-tensorflow==2.1b6.dev1389 +Flask==3.1.2 +Flask-Admin==2.0.2 +Flask-Bcrypt==1.0.1 +flask-cors==6.0.1 +Flask-Login==0.6.3 +Flask-SocketIO==5.5.1 +Flask-SQLAlchemy==3.1.1 +gevent==25.9.1 +greenlet==3.2.4 +gunicorn==23.0.0 +h11==0.16.0 +idna==3.11 +itsdangerous==2.2.0 +Jinja2==3.1.6 +MarkupSafe==3.0.3 +numpy==2.3.5 +packaging==25.0 +psutil==7.2.2 +pycparser==2.23 +pyOpenSSL==25.3.0 +python-engineio==4.12.3 +python-socketio==5.14.3 +PyYAML==6.0.3 +requests==2.32.5 +simple-websocket==1.1.0 +six==1.17.0 +soupsieve==2.8.3 +SQLAlchemy==2.0.44 +tqdm==4.67.1 +typing_extensions==4.15.0 +urllib3==2.5.0 +watchdog==6.0.0 +Werkzeug==3.1.4 +wsproto==1.2.0 +WTForms==3.2.1 +zope.event==6.1 +zope.interface==8.0.1