mmpSearch/upload_server.py

207 lines
8.5 KiB
Python

import os
import json
import subprocess
import time
from threading import Thread
from flask import Flask, request, jsonify
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__))
print(f"Raiz do projeto detectada em: {PROJECT_ROOT}")
CONFIGS = [
{
"source_dir": "src/samples",
"output_file": "metadata/samples-manifest.json",
"scan_type": "tree"
},
{
"source_dir": "mmp",
"output_file": "metadata/mmp-manifest.json",
"scan_type": "list",
"extensions": ['.mmp', '.mmpz']
}
]
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", "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):
tree = {}
if not os.path.isdir(path): return tree
for item in sorted(os.listdir(path)):
full_path = os.path.join(path, item)
if os.path.isdir(full_path):
tree[item] = scan_directory_tree(full_path)
else:
if item.lower().endswith(('.wav', '.ogg', '.flac', '.mp3')):
tree[item] = {"_isFile": True}
return tree
def scan_directory_list(path, allowed_extensions):
file_list = []
if not os.path.isdir(path): return file_list
for item in os.listdir(path):
full_path = os.path.join(path, item)
if os.path.isfile(full_path) and any(item.lower().endswith(ext) for ext in allowed_extensions):
file_list.append(item)
return sorted(file_list)
def run_jekyll_build():
print("\nExecutando o comando de build do Jekyll...")
destination_path = "/nethome/jotachina/public_html/mmpSearch/"
jekyll_args = ["--destination", destination_path, "--force"]
try:
command = ["bundle", "exec", "jekyll", "build"] + jekyll_args
result = subprocess.run(command, cwd=PROJECT_ROOT, capture_output=True, text=True, check=False)
if result.returncode != 0:
print("Falha com 'bundle', tentando 'jekyll build' diretamente...")
command = ["jekyll", "build"] + jekyll_args
result = subprocess.run(command, cwd=PROJECT_ROOT, capture_output=True, text=True, check=False)
if result.returncode == 0:
print("SUCESSO: Jekyll build concluído.")
else:
print(f"ERRO no Jekyll build: {result.stderr}")
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...")
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"])
if not os.path.isdir(source_dir_abs): continue
result_data = None
if config["scan_type"] == "tree": result_data = scan_directory_tree(source_dir_abs)
elif config["scan_type"] == "list": result_data = scan_directory_list(source_dir_abs, config.get("extensions", []))
if result_data is not None:
output_dir = os.path.dirname(output_file_abs)
os.makedirs(output_dir, exist_ok=True)
with open(output_file_abs, 'w', encoding='utf-8') as f:
json.dump(result_data, f, indent=2, ensure_ascii=False)
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")
# --- Servidor Flask ---
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/upload-sample', methods=['POST'])
def upload_file():
if 'sampleFile' not in request.files: return jsonify({"error": "Nenhum arquivo enviado"}), 400
file = request.files['sampleFile']
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_SAMPLE'], exist_ok=True)
save_path = os.path.join(app.config['UPLOAD_FOLDER_SAMPLE'], filename)
try:
file.save(save_path)
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
# --- 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):
# 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
current_time = time.time()
# 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}")
# 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 para ambas as pastas em uma thread separada."""
event_handler = ManifestEventHandler()
observer = Observer()
# 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:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
# --- ATUALIZAÇÃO: INICIALIZAÇÃO PRINCIPAL ---
if __name__ == '__main__':
# Gera os manifestos uma vez ao iniciar o servidor
generate_manifests()
# Inicia o "vigia" de arquivos em uma thread separada (em segundo plano)
watcher_thread = Thread(target=start_file_watcher, daemon=True)
watcher_thread.start()
# Inicia o servidor Flask (thread principal)
print("\n[FLASK] Iniciando servidor de upload...")
app.run(host='0.0.0.0', port=33002, debug=True) # Debug mode deve ser False para evitar que rode duas vezes