sockets funcionais

This commit is contained in:
JotaChina committed 2025-11-10 16:16:59 -03:00
1 parent d38cd9e60f
commit 509c51b783
7 files changed
+945 -35

No files matched your search

+2 -2
View File
@@ -4,8 +4,8 @@ description: >- # this means to ignore newlines until "baseurl:"
Write an awesome description for your new site here. You can edit this
line in _config.yml. It will appear in your document head meta (for
Google search results) and in your feed.xml site description.
baseurl: "/mmpSearch/" # the subpath of your site, e.g. /blog
url: "https://alice.ufsj.edu.br" # the base hostname & protocol for your site, e.g. http://example.com
baseurl: "/~jotachina/mmpSearch/" # the subpath of your site, e.g. /blog
url: "https://alice.ufsj.edu.br/" # the base hostname & protocol for your site, e.g. http://example.com
theme: alice
+58 -13
View File
@@ -8,10 +8,8 @@ title: "{{ page.file }}"
<main class="content">
<div class="container">
<!-- Título -->
<h1 class="title is-3 mb-4"><code>{{ page.file }}</h1></code>
<!-- Metadados (Arquivo fonte + BPM) -->
{% if page.file or page.bpm %}
<div class="mb-5">
<div class="columns is-mobile is-multiline is-vcentered">
@@ -29,7 +27,10 @@ title: "{{ page.file }}"
</div>
{% endif %}
<!-- Tags -->
<div class="columns">
<div class="column is-two-thirds">
{% if page.tags %}
{% assign tags_vazias = true %}
{% for categoria in page.tags %}
@@ -40,10 +41,8 @@ title: "{{ page.file }}"
{% unless tags_vazias %}
<section class="tags-section mb-6">
<!-- Verificar se o arquivo .wav existe -->
{% assign audio_file = '/mmp/wav/' | append: page.file | append: '.wav' %}
<!-- Exibir player de áudio -->
{% if audio_file %}
<section class="audio-player-section mb-6">
<audio controls>
@@ -63,7 +62,6 @@ title: "{{ page.file }}"
{% if valor != "" %}
{% assign tag_slug = valor | replace: ' ', '+' %}
<!-- Gerar link para filtro por categoria -->
{% if categoria[0] == 'bassline' %}
<a href="{{ '/bassline/?bassline=' | append: tag_slug | relative_url }}" class="tag is-info is-light">{{ valor }}</a>
{% elsif categoria[0] == 'sample' %}
@@ -84,8 +82,6 @@ title: "{{ page.file }}"
</section>
{% endunless %}
{% endif %}
<!-- Instrumentos -->
{% if page.tracks and page.tracks.size > 0 %}
<section class="instruments-section">
<h2 class="title is-5"><code>🎚️ Instrumentos</h2></code>
@@ -108,19 +104,26 @@ title: "{{ page.file }}"
{% for instrument in track.instruments %}
{% if instrument.instrument_name %}
<li style="margin-bottom: 0.4rem;">
<!-- Gerar link para filtro por instrumento -->
{% assign instrument_slug = instrument.instrument_name | replace: ' ', '+' %}
<a href="{{ '/instruments/?instrument=' | append: instrument_slug | relative_url }}" class="tag is-info is-light">
<code>{{ instrument.instrument_name }}</code>
</a>
{% if instrument.audiofileprocessor and instrument.audiofileprocessor.src %}
<!-- Exibir player de áudio para o instrumento -->
{% assign audio_filename_with_path = 'src/samples/' | append: instrument.audiofileprocessor.src %}
<div class="audio-player-section mt-3">
<audio controls>
<!-- O caminho, aparentemente, está correto. mas não está emitindo sons -->
<source src="{{ '/mmp/instruments/lmms/' | append: instrument.audiofileprocessor.src | relative_url }}" type="audio/wav">
<source src="{{ '/mmp/instruments/lmms/' | append: instrument.audiofileprocessor.src | relative_url }}" type="audio/ogg">
<source
src="{{ audio_filename_with_path | relative_url }}"
type="audio/ogg"
>
{% assign wav_path = audio_filename_with_path | replace: '.ogg', '.wav' %}
<source
src="{{ wav_path | relative_url }}"
type="audio/wav"
>
Seu navegador não suporta o elemento <code>audio</code>.
</audio>
</div>
@@ -137,6 +140,48 @@ title: "{{ page.file }}"
</div>
</section>
{% endif %}
</div> <div class="column is-one-third">
<div class="box p-4 has-background-info-light">
<h3 class="title is-6 has-text-info mb-3">🛠️ Abra na Criação Colaborativa</h3>
{% comment %}
CONSTRUÇÃO DA URL:
1. Usa o arquivo fonte (`page.file`) como parâmetro `project`.
2. Assume que a plataforma de criação está em `/creations/`.
3. Abre em uma nova aba (`target="_blank"`).
{% endcomment %}
{% assign creation_url = '/creations/?project=' | append: page.file | relative_url %}
<a
href="{{ creation_url }}"
target="_blank"
class="button is-info is-fullwidth is-medium mb-4"
>
<span class="icon"><i class="fa-solid fa-up-right-from-square"></i></span>
<span>Abrir no MMPCreator</span>
</a>
<p class="is-size-7 has-text-grey-dark">
O link abrirá o projeto em uma nova aba para edição.
<br>
Arquivo: <code>{{ page.file }}</code>
</p>
<div class="mt-4 is-hidden-mobile">
<h4 class="title is-7 has-text-grey-dark">Prévia (Embed)</h4>
{% comment %}
A prévia embutida é opcional e pode ser um pouco pesada.
Ajuste o caminho da URL e o estilo (altura) conforme necessário.
{% endcomment %}
<iframe
src="{{ creation_url }}"
title="Prévia do Projeto"
style="width: 100%; height: 300px; border: 1px solid #ccc; border-radius: 4px;"
>
</iframe>
</div>
</div>
</div> </div>
</div>
</main>
</div>
+16
View File
@@ -25,6 +25,22 @@ import { sendAction, joinRoom, setUserName } from "./socket.js";
const ROOM_NAME = new URLSearchParams(window.location.search).get("room");
window.ROOM_NAME = ROOM_NAME;
const PROJECT_NAME = new URLSearchParams(window.location.search).get("project");
if (PROJECT_NAME) {
// O nome do projeto deve corresponder ao arquivo no servidor, por ex: "mmp/nome-do-seu-projeto-salvo.mmp"
// O arquivo 'file.js' já espera que loadProjectFromServer receba apenas o nome
// do arquivo dentro da pasta 'mmp/' (ex: 'nome-do-projeto.mmp').
console.log(`[MAIN] Carregando projeto do servidor: ${PROJECT_NAME}`);
// Adicione a extensão se ela não estiver no link
const filename = PROJECT_NAME.endsWith('.mmp') || PROJECT_NAME.endsWith('.mmpz')
? PROJECT_NAME
: `${PROJECT_NAME}.mmp`;
// Chama a função de file.js para carregar (que já envia a ação 'LOAD_PROJECT')
loadProjectFromServer(filename);
}
// ✅ NOVO: se tem sala na URL, entra já na sala (independe do áudio)
if (ROOM_NAME) {
+21
View File
@@ -518,6 +518,27 @@ io.on("connection", (socket) => {
});
});
// --- ENDPOINT DE NOTIFICAÇÃO EXTERNA ---
app.post("/notify-update", express.json(), (req, res) => {
const { updateType } = req.body;
if (updateType === "samples") {
// 1. Emitir o evento para TODAS as salas/clientes.
// O evento deve ser algo que seu ui.js entenda.
io.emit("system_update", {
type: "RELOAD_SAMPLES",
message: "Novo Sample/Project adicionado. Recarregando o navegador de arquivos...",
});
console.log(
"[Notificação] Evento 'RELOAD_SAMPLES' emitido para todos os clientes."
);
return res.status(200).send({ success: true, message: "Notificação de Samples/Projetos enviada." });
}
res.status(400).send({ success: false, message: "updateType inválido." });
});
app.get("/", (req, res) => {
res.send(
"Servidor Backend V6 (Logs Dinâmicos) da DAW colaborativa está no ar!"
+12
View File
@@ -165,6 +165,18 @@ socket.on("connect_error", (err) => {
);
});
socket.on("system_update", (data) => {
if (data.type === "RELOAD_SAMPLES") {
console.log(`[System Update] Recebida ordem para recarregar samples: ${data.message}`);
// Certifique-se de que esta função existe e faz o que é esperado:
// 1. Fetch dos novos manifestos (metadata/samples-manifest.json etc.)
// 2. Renderiza a interface do navegador de samples
loadAndRenderSampleBrowser();
}
// Se houver outras notificações, adicione-as aqui (ex: RELOAD_PROJECTS)
});
// -----------------------------------------------------------------------------
// RECEBER ESTADO SALVO DA SALA
// -----------------------------------------------------------------------------
+773
View File
@@ -0,0 +1,773 @@
{
"basses": {
"bass01.ogg": {
"_isFile": true
},
"bass_acid01.ogg": {
"_isFile": true
},
"bass_acid02.ogg": {
"_isFile": true
},
"bass_hard01.ogg": {
"_isFile": true
},
"bass_hard02.ogg": {
"_isFile": true
},
"bass_punch01.ogg": {
"_isFile": true
},
"horror_bass01.ogg": {
"_isFile": true
},
"matrix1000_pluck01.ogg": {
"_isFile": true
},
"rave_bass01.ogg": {
"_isFile": true
},
"rave_bass02.ogg": {
"_isFile": true
},
"rave_bass04.ogg": {
"_isFile": true
},
"synth_acid01.ogg": {
"_isFile": true
},
"synth_acid02.ogg": {
"_isFile": true
},
"techno_synth01.ogg": {
"_isFile": true
}
},
"bassloops": {
"briff01.ogg": {
"_isFile": true
},
"rave_bass01.ogg": {
"_isFile": true
},
"rave_bass02.ogg": {
"_isFile": true
},
"tb303_01.ogg": {
"_isFile": true
},
"techno_bass01.ogg": {
"_isFile": true
},
"techno_bass02.ogg": {
"_isFile": true
},
"techno_synth01.ogg": {
"_isFile": true
},
"techno_synth02.ogg": {
"_isFile": true
},
"techno_synth03.ogg": {
"_isFile": true
},
"techno_synth04.ogg": {
"_isFile": true
}
},
"beats": {
"909beat01.ogg": {
"_isFile": true
},
"break01.ogg": {
"_isFile": true
},
"break02.ogg": {
"_isFile": true
},
"break03.ogg": {
"_isFile": true
},
"electro_beat01.ogg": {
"_isFile": true
},
"electro_beat02.ogg": {
"_isFile": true
},
"house_loop01.ogg": {
"_isFile": true
},
"jungle01.ogg": {
"_isFile": true
},
"rave_hihat01.ogg": {
"_isFile": true
},
"rave_hihat02.ogg": {
"_isFile": true
},
"rave_kick01.ogg": {
"_isFile": true
},
"rave_kick02.ogg": {
"_isFile": true
},
"rave_snare01.ogg": {
"_isFile": true
}
},
"drums": {
"bassdrum01.ogg": {
"_isFile": true
},
"bassdrum02.ogg": {
"_isFile": true
},
"bassdrum03.ogg": {
"_isFile": true
},
"bassdrum04.ogg": {
"_isFile": true
},
"bassdrum_acoustic01.ogg": {
"_isFile": true
},
"bassdrum_acoustic02.ogg": {
"_isFile": true
},
"clap01.ogg": {
"_isFile": true
},
"clap02.ogg": {
"_isFile": true
},
"clap03.ogg": {
"_isFile": true
},
"clap04.ogg": {
"_isFile": true
},
"clav01.ogg": {
"_isFile": true
},
"clav02.ogg": {
"_isFile": true
},
"crash01.ogg": {
"_isFile": true
},
"crash02.ogg": {
"_isFile": true
},
"hihat_closed01.ogg": {
"_isFile": true
},
"hihat_closed02.ogg": {
"_isFile": true
},
"hihat_closed03.ogg": {
"_isFile": true
},
"hihat_closed04.ogg": {
"_isFile": true
},
"hihat_closed05.ogg": {
"_isFile": true
},
"hihat_foot_pedal01.ogg": {
"_isFile": true
},
"hihat_opened01.ogg": {
"_isFile": true
},
"hihat_opened02.ogg": {
"_isFile": true
},
"hihat_opened03.ogg": {
"_isFile": true
},
"kick01.ogg": {
"_isFile": true
},
"kick02.ogg": {
"_isFile": true
},
"kick03.ogg": {
"_isFile": true
},
"kick04.ogg": {
"_isFile": true
},
"kick_distorted01.ogg": {
"_isFile": true
},
"kick_hard01.ogg": {
"_isFile": true
},
"kick_hardcore01.ogg": {
"_isFile": true
},
"kick_hiphop01.ogg": {
"_isFile": true
},
"kick_long01.ogg": {
"_isFile": true
},
"kick_soft01.ogg": {
"_isFile": true
},
"kick_soft02.ogg": {
"_isFile": true
},
"nasty_bass01.ogg": {
"_isFile": true
},
"nasty_rim01.ogg": {
"_isFile": true
},
"nasty_snare01.ogg": {
"_isFile": true
},
"ride01.ogg": {
"_isFile": true
},
"ride02.ogg": {
"_isFile": true
},
"rim01.ogg": {
"_isFile": true
},
"shaker01.ogg": {
"_isFile": true
},
"shaker02.ogg": {
"_isFile": true
},
"shaker03.ogg": {
"_isFile": true
},
"sidestick01.ogg": {
"_isFile": true
},
"snare01.ogg": {
"_isFile": true
},
"snare02.ogg": {
"_isFile": true
},
"snare03.ogg": {
"_isFile": true
},
"snare04.ogg": {
"_isFile": true
},
"snare05.ogg": {
"_isFile": true
},
"snare06.ogg": {
"_isFile": true
},
"snare07.ogg": {
"_isFile": true
},
"snare_acoustic01.ogg": {
"_isFile": true
},
"snare_electro01.ogg": {
"_isFile": true
},
"snare_harsh01.ogg": {
"_isFile": true
},
"snare_hiphop01.ogg": {
"_isFile": true
},
"snare_hiphop02.ogg": {
"_isFile": true
},
"snare_muffled01.ogg": {
"_isFile": true
},
"snare_muffled02.ogg": {
"_isFile": true
},
"snare_rim01.ogg": {
"_isFile": true
},
"snare_short01.ogg": {
"_isFile": true
},
"tom01.ogg": {
"_isFile": true
},
"tom02.ogg": {
"_isFile": true
},
"tom03.ogg": {
"_isFile": true
},
"tom04.ogg": {
"_isFile": true
},
"tom05.ogg": {
"_isFile": true
},
"tom_hi01.ogg": {
"_isFile": true
},
"tom_low01.ogg": {
"_isFile": true
},
"tom_mid01.ogg": {
"_isFile": true
},
"wood01.ogg": {
"_isFile": true
},
"zap01.ogg": {
"_isFile": true
},
"zap02.ogg": {
"_isFile": true
},
"zap03.ogg": {
"_isFile": true
}
},
"drumsynth": {
"acoustic": {},
"cr78": {},
"cr8000": {},
"effects": {},
"electro": {},
"farfisa": {},
"ferraro": {},
"instrument": {},
"jorgensohn": {},
"latin": {},
"linn": {},
"magnetboy": {},
"misc": {},
"misc_bass": {},
"misc_claps": {},
"misc_electro": {},
"misc_fx": {},
"misc_hats": {},
"misc_perc": {},
"misc_synth": {},
"r_n_b": {},
"tr606": {},
"tr77": {},
"tr808": {},
"tr909": {}
},
"effects": {
"chroma_sound_effect01.ogg": {
"_isFile": true
},
"explode01.ogg": {
"_isFile": true
},
"filter_sweep01.ogg": {
"_isFile": true
},
"scratch01.ogg": {
"_isFile": true
},
"start01.ogg": {
"_isFile": true
},
"start02.ogg": {
"_isFile": true
},
"stop01.ogg": {
"_isFile": true
},
"warp01.ogg": {
"_isFile": true
},
"warp02.ogg": {
"_isFile": true
},
"wind_chimes01.ogg": {
"_isFile": true
}
},
"instruments": {
"bassslap01.ogg": {
"_isFile": true
},
"bassslap02.ogg": {
"_isFile": true
},
"cello01.ogg": {
"_isFile": true
},
"church_organ01.ogg": {
"_isFile": true
},
"church_organ02.ogg": {
"_isFile": true
},
"church_organ03.ogg": {
"_isFile": true
},
"church_organ04.ogg": {
"_isFile": true
},
"e_organ01.ogg": {
"_isFile": true
},
"e_piano_accord01.ogg": {
"_isFile": true
},
"e_piano_accord02.ogg": {
"_isFile": true
},
"flute01.ogg": {
"_isFile": true
},
"harpsichord01.ogg": {
"_isFile": true
},
"piano01.ogg": {
"_isFile": true
},
"piano02.ogg": {
"_isFile": true
},
"steel_guitar01.ogg": {
"_isFile": true
},
"steel_guitar_heavy_distorted01.ogg": {
"_isFile": true
},
"steel_guitar_medium_distorted01.ogg": {
"_isFile": true
},
"steel_guitar_slight_distorted01.ogg": {
"_isFile": true
},
"trumpet01.ogg": {
"_isFile": true
},
"violin_double_stop01.ogg": {
"_isFile": true
},
"violin_fingered01.ogg": {
"_isFile": true
},
"violin_pizzicato01.ogg": {
"_isFile": true
}
},
"latin": {
"latin_brass01.ogg": {
"_isFile": true
},
"latin_guitar01.ogg": {
"_isFile": true
},
"latin_guitar02.ogg": {
"_isFile": true
},
"latin_guitar03.ogg": {
"_isFile": true
}
},
"misc": {
"applause01.ogg": {
"_isFile": true
},
"bass!.ogg": {
"_isFile": true
},
"breath01.ogg": {
"_isFile": true
},
"buzz!.ogg": {
"_isFile": true
},
"dong01.ogg": {
"_isFile": true
},
"dong02.ogg": {
"_isFile": true
},
"dong03.ogg": {
"_isFile": true
},
"electric_ping01.ogg": {
"_isFile": true
},
"hit01.ogg": {
"_isFile": true
},
"hit02.ogg": {
"_isFile": true
},
"metalish_dong01.ogg": {
"_isFile": true
},
"metronome01.ogg": {
"_isFile": true
},
"metronome02.ogg": {
"_isFile": true
},
"raving_crowd01.ogg": {
"_isFile": true
},
"snaph01.ogg": {
"_isFile": true
},
"undefined01.ogg": {
"_isFile": true
}
},
"samples": {
"reuniao_funkwhale.wav": {
"_isFile": true
}
},
"shapes": {
"additive.wav": {
"_isFile": true
},
"additive2.wav": {
"_isFile": true
},
"bunglist.wav": {
"_isFile": true
},
"bunglist2.wav": {
"_isFile": true
},
"bunglist3.wav": {
"_isFile": true
},
"bunglist4.wav": {
"_isFile": true
},
"bunglist5.wav": {
"_isFile": true
},
"bunglist6.wav": {
"_isFile": true
},
"bunglist7.wav": {
"_isFile": true
},
"bunglist8.wav": {
"_isFile": true
},
"bunglist9.wav": {
"_isFile": true
},
"harmonics.wav": {
"_isFile": true
},
"low_sine.wav": {
"_isFile": true
},
"micro.wav": {
"_isFile": true
},
"moog.wav": {
"_isFile": true
},
"roundbass.wav": {
"_isFile": true
},
"roundsaw.ogg": {
"_isFile": true
},
"saw+sine.wav": {
"_isFile": true
},
"sine_dist.ogg": {
"_isFile": true
},
"smooth_inv_saw.ogg": {
"_isFile": true
},
"smooth_inv_saw2.ogg": {
"_isFile": true
},
"smooth_inv_saw_dist.ogg": {
"_isFile": true
},
"technobass.wav": {
"_isFile": true
},
"technosynth1.wav": {
"_isFile": true
},
"technosynth2.wav": {
"_isFile": true
},
"technosynth3.wav": {
"_isFile": true
},
"technosynth4.wav": {
"_isFile": true
},
"technosynth5.wav": {
"_isFile": true
},
"technosynth6.wav": {
"_isFile": true
},
"vowel_a.wav": {
"_isFile": true
},
"vowel_u.wav": {
"_isFile": true
}
},
"stringsnpads": {
"bell_choir01.ogg": {
"_isFile": true
},
"bell_choir02.ogg": {
"_isFile": true
},
"chorus01.ogg": {
"_isFile": true
},
"chorus02.ogg": {
"_isFile": true
},
"heaven_strings01.ogg": {
"_isFile": true
},
"juno_pad01.ogg": {
"_isFile": true
},
"korg_poly6_drone01.ogg": {
"_isFile": true
},
"nord_ambient01.ogg": {
"_isFile": true
},
"orion_string01.ogg": {
"_isFile": true
},
"rave_choir01.ogg": {
"_isFile": true
},
"space_strings01.ogg": {
"_isFile": true
},
"space_strings02.ogg": {
"_isFile": true
},
"space_strings03.ogg": {
"_isFile": true
},
"strings01.ogg": {
"_isFile": true
}
},
"waveforms": {
"10saw.flac": {
"_isFile": true
},
"10sine.flac": {
"_isFile": true
},
"10sqr.flac": {
"_isFile": true
},
"10tri.flac": {
"_isFile": true
},
"analogsqr.flac": {
"_isFile": true
},
"fmsaw1.flac": {
"_isFile": true
},
"fmsine.flac": {
"_isFile": true
},
"fmsine2.flac": {
"_isFile": true
},
"halfsine.flac": {
"_isFile": true
},
"impulse.flac": {
"_isFile": true
},
"lfo_trancegate_quarter.flac": {
"_isFile": true
},
"lfo_trancegate_sawdecay_quarter.flac": {
"_isFile": true
},
"lfo_trancegate_sinedecay_quarter.flac": {
"_isFile": true
},
"lfo_trancegate_triplet_half.flac": {
"_isFile": true
},
"lfo_trancegate_triplet_half_2.flac": {
"_isFile": true
},
"lfo_trancegate_triplet_quarter.flac": {
"_isFile": true
},
"lfo_trancegate_whole.flac": {
"_isFile": true
},
"lfo_trancegate_whole_2.flac": {
"_isFile": true
},
"modsqr.flac": {
"_isFile": true
},
"saw1.flac": {
"_isFile": true
},
"saw2.flac": {
"_isFile": true
},
"sinesaw.flac": {
"_isFile": true
},
"w2_addsyn.flac": {
"_isFile": true
},
"w2_angrysaw.flac": {
"_isFile": true
},
"w2_hexagon.flac": {
"_isFile": true
},
"w2_invsine.flac": {
"_isFile": true
},
"w2_invsineabs.flac": {
"_isFile": true
},
"w2_invsinehalf.flac": {
"_isFile": true
},
"w2_noisy.flac": {
"_isFile": true
},
"w2_rad.flac": {
"_isFile": true
},
"w2_sawsine.flac": {
"_isFile": true
},
"w2_sharp.flac": {
"_isFile": true
},
"w2_w-wave.flac": {
"_isFile": true
}
}
}
+63 -20
View File
@@ -8,6 +8,9 @@ from werkzeug.utils import secure_filename
from flask_cors import CORS
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import requests
NODE_SERVER_URL = "https://127.0.0.1:33001"
# --- Configurações (sem alterações) ---
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
@@ -27,13 +30,16 @@ CONFIGS = [
}
]
UPLOAD_FOLDER = os.path.join(PROJECT_ROOT, "src", "samples", "samples")
WATCH_FOLDER = os.path.join(PROJECT_ROOT, "src", "samples") # Pasta a ser vigiada
UPLOAD_FOLDER_SAMPLE = os.path.join(PROJECT_ROOT, "src", "samples", "samples")
WATCH_FOLDER_SAMPLE = UPLOAD_FOLDER_SAMPLE # Pasta a ser vigiada
UPLOAD_FOLDER_PROJECT = os.path.join(PROJECT_ROOT, "src", "samples", "projects")
WATCH_FOLDER_PROJECT = os.path.join(PROJECT_ROOT, "src", "upload_projects") # Pasta a ser vigiada
ALLOWED_EXTENSIONS = {'wav', 'ogg', 'flac', 'mp3'}
app = Flask(__name__)
CORS(app, origins=["https://alice.ufsj.edu.br", "http://localhost:8000", "http://127.0.0.1:5500"])
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
CORS(app, origins=["https://alice.ufsj.edu.br", "https://alice.ufsj.edu.br:33002"])
app.config['UPLOAD_FOLDER_SAMPLE'] = UPLOAD_FOLDER_SAMPLE
# --- Funções do Gerador de Manifesto (sem alterações) ---
def scan_directory_tree(path):
@@ -75,9 +81,27 @@ def run_jekyll_build():
except Exception as e:
print(f"ERRO inesperado durante o Jekyll build: {e}")
def notify_node_server(update_type):
"""Envia uma notificação HTTP para o servidor Node.js."""
try:
url = f"{NODE_SERVER_URL}/notify-update"
headers = {'Content-Type': 'application/json'}
payload = {"updateType": update_type}
# O argumento 'verify=False' é geralmente necessário ao usar 127.0.0.1
# com certificados Let's Encrypt (ou autoassinados) para evitar erros de SSL.
response = requests.post(url, json=payload, headers=headers, verify=False)
if response.status_code == 200:
print(f"[Notificação] Sucesso ao notificar Node.js: {update_type}")
else:
print(f"[Notificação] ERRO ao notificar Node.js ({response.status_code}): {response.text}")
except Exception as e:
print(f"[Notificação] ERRO de conexão com o Node.js em {NODE_SERVER_URL}: {e}")
def generate_manifests():
print("\nIniciando geração de arquivos de manifesto...")
# ... (lógica interna sem alterações) ...
for config in CONFIGS:
source_dir_abs = os.path.join(PROJECT_ROOT, config["source_dir"])
output_file_abs = os.path.join(PROJECT_ROOT, config["output_file"])
@@ -93,8 +117,10 @@ def generate_manifests():
print(f"SUCESSO: Arquivo '{output_file_abs}' gerado!")
print("\nGeração de manifestos concluída.")
run_jekyll_build()
# 2. NOTIFICA O SERVIDOR NODE.JS (NOVO PASSO)
notify_node_server("samples")
# --- Lógica do Servidor Flask (sem alterações) ---
# --- Servidor Flask ---
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@@ -105,41 +131,58 @@ def upload_file():
if file.filename == '': return jsonify({"error": "Nenhum arquivo selecionado"}), 400
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
os.makedirs(app.config['UPLOAD_FOLDER_SAMPLE'], exist_ok=True)
save_path = os.path.join(app.config['UPLOAD_FOLDER_SAMPLE'], filename)
try:
file.save(save_path)
generate_manifests()
return jsonify({"success": True, "message": f"Arquivo '{filename}' salvo e site atualizado!"}), 200
return jsonify({"success": True, "message": f"Arquivo '{filename}' salvo!"}), 200
except Exception as e:
return jsonify({"error": str(e)}), 500
return jsonify({"error": "Tipo de arquivo não permitido"}), 400
# --- NOVO: LÓGICA DO "VIGIA" DE ARQUIVOS (WATCHDOG) ---
# --- LÓGICA DO "VIGIA" DE ARQUIVOS (WATCHDOG) ---
class ManifestEventHandler(FileSystemEventHandler):
"""Um manipulador de eventos que gera os manifestos quando um arquivo muda."""
def __init__(self):
# Inicializa o timestamp da última execução
self.last_triggered = 0
# Define o intervalo mínimo entre execuções (5 segundos)
self.debounce_interval = 5
def on_any_event(self, event):
# Desconsidera eventos em diretórios
if event.is_directory:
# Ignora eventos em diretórios ou no arquivo de manifesto de saída (evita loop)
if event.is_directory or "manifest" in event.src_path:
return
# Mecanismo simples para evitar múltiplas execuções seguidas (debounce)
current_time = time.time()
if current_time - self.last_triggered > 5: # Espera 5 segundos entre as atualizações
# Verifica se passou tempo suficiente desde o último acionamento
if current_time - self.last_triggered > self.debounce_interval:
print(f"\n[VIGIA] Mudança detectada: {event.src_path}")
generate_manifests()
# Executa a geração de manifestos (e o Jekyll build, se descomentado)
# É crucial que generate_manifests() seja rápido ou executado em outra thread separada
# para não bloquear o Watchdog por muito tempo.
Thread(target=generate_manifests).start()
self.last_triggered = current_time
else:
print(f"[VIGIA] Mudança ignorada devido ao debounce (Intervalo de {self.debounce_interval}s).")
def start_file_watcher():
"""Inicia o observador de arquivos em uma thread separada."""
print(f"\n[VIGIA] Iniciando monitoramento da pasta: {WATCH_FOLDER}")
"""Inicia o observador de arquivos para ambas as pastas em uma thread separada."""
event_handler = ManifestEventHandler()
observer = Observer()
observer.schedule(event_handler, WATCH_FOLDER, recursive=True)
# 1. Monitorar a pasta de Samples
print(f"\n[VIGIA] Monitorando Samples: {WATCH_FOLDER_SAMPLE}")
observer.schedule(event_handler, WATCH_FOLDER_SAMPLE, recursive=True)
# 2. Monitorar a pasta de Projetos
print(f"[VIGIA] Monitorando Projetos: {WATCH_FOLDER_PROJECT}")
observer.schedule(event_handler, WATCH_FOLDER_PROJECT, recursive=True)
observer.start()
try:
while True:
@@ -160,4 +203,4 @@ if __name__ == '__main__':
# Inicia o servidor Flask (thread principal)
print("\n[FLASK] Iniciando servidor de upload...")
app.run(host='0.0.0.0', port=5000, debug=False) # Debug mode deve ser False para evitar que rode duas vezes
app.run(host='0.0.0.0', port=33002, debug=True) # Debug mode deve ser False para evitar que rode duas vezes