Isaac105 commited on
Commit
a47fcf7
·
1 Parent(s): 4e586b0

deploy from github actions

Browse files
Dockerfile CHANGED
@@ -2,6 +2,7 @@ FROM python:3.10-slim
2
 
3
  # Install system dependencies
4
  RUN apt-get update && apt-get install -y \
 
5
  ffmpeg \
6
  git \
7
  build-essential \
@@ -16,11 +17,15 @@ RUN pip install --upgrade pip "setuptools<70" wheel && \
16
  pip install --no-build-isolation madmom && \
17
  pip install --no-cache-dir --upgrade -r /code/requirements.txt
18
 
 
 
 
 
19
  # Copy application files
20
  COPY . .
21
 
22
  # Create writable folders with permissions for Hugging Face user 1000
23
- RUN mkdir -p /code/processed /code/uploads /code/logs && chmod -R 777 /code
24
 
25
  # Grant execution rights to start script
26
  RUN chmod +x /code/start.sh
 
2
 
3
  # Install system dependencies
4
  RUN apt-get update && apt-get install -y \
5
+ curl \
6
  ffmpeg \
7
  git \
8
  build-essential \
 
17
  pip install --no-build-isolation madmom && \
18
  pip install --no-cache-dir --upgrade -r /code/requirements.txt
19
 
20
+ # Pre-descargar pesos del modelo BTC de acordes en la imagen
21
+ RUN mkdir -p /code/models && \
22
+ curl -L -o /code/models/btc_chord.pth https://huggingface.co/amaai-lab/music2emo/resolve/main/inference/data/btc_model.pt
23
+
24
  # Copy application files
25
  COPY . .
26
 
27
  # Create writable folders with permissions for Hugging Face user 1000
28
+ RUN mkdir -p /code/processed /code/uploads /code/logs /code/models && chmod -R 777 /code
29
 
30
  # Grant execution rights to start script
31
  RUN chmod +x /code/start.sh
btc_toolkit/inference.py CHANGED
@@ -144,7 +144,25 @@ class BTCChordRecognizer:
144
  # ==========================================
145
  # EXTRACCIÓN DE CARACTERÍSTICAS CQT
146
  # ==========================================
147
- y, sr = librosa.load(audio_path, sr=SAMPLE_RATE, mono=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
  # Transformada de Q constante (CQT) - representación frecuencial de alta resolución
150
  # NOTA: Se ha omitido librosa.effects.harmonic por su altísimo costo en CPU
 
144
  # ==========================================
145
  # EXTRACCIÓN DE CARACTERÍSTICAS CQT
146
  # ==========================================
147
+ try:
148
+ y, sr = librosa.load(audio_path, sr=SAMPLE_RATE, mono=True)
149
+ except Exception as load_err:
150
+ logger.warning(f"[BTC] librosa.load falló ({load_err}), usando fallback soundfile/ffmpeg...")
151
+ try:
152
+ import soundfile as sf
153
+ y, orig_sr = sf.read(audio_path, dtype='float32')
154
+ if y.ndim > 1:
155
+ y = np.mean(y, axis=1)
156
+ if orig_sr != SAMPLE_RATE:
157
+ try:
158
+ import soxr
159
+ y = soxr.resample(y, orig_sr, SAMPLE_RATE)
160
+ except Exception:
161
+ y = librosa.resample(y, orig_sr=orig_sr, target_sr=SAMPLE_RATE)
162
+ sr = SAMPLE_RATE
163
+ except Exception:
164
+ from app.utils.harmony import _load_audio_via_ffmpeg
165
+ y, sr = _load_audio_via_ffmpeg(audio_path, sr=SAMPLE_RATE)
166
 
167
  # Transformada de Q constante (CQT) - representación frecuencial de alta resolución
168
  # NOTA: Se ha omitido librosa.effects.harmonic por su altísimo costo en CPU
btc_toolkit/weights_manager.py CHANGED
@@ -73,16 +73,28 @@ def ensure_weights_available() -> str:
73
  logger.info(f"[BTC] Destino: {weights_path}")
74
 
75
  try:
76
- def reporthook(count, block_size, total_size):
77
- """Callback de progreso durante la descarga."""
78
- if total_size > 0:
79
- downloaded = count * block_size
80
- percent = min(100, int(downloaded * 100 / total_size))
81
- mb_downloaded = downloaded / (1024 * 1024)
82
- mb_total = total_size / (1024 * 1024)
83
- logger.debug(f"[BTC] Descargando: {percent}% ({mb_downloaded:.1f} MB / {mb_total:.1f} MB)")
84
-
85
- urllib.request.urlretrieve(BTC_WEIGHTS_URL, weights_path, reporthook)
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
  # Verificar que el archivo se descargó correctamente
88
  if not os.path.exists(weights_path) or os.path.getsize(weights_path) == 0:
 
73
  logger.info(f"[BTC] Destino: {weights_path}")
74
 
75
  try:
76
+ headers = {
77
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
78
+ }
79
+ hf_token = os.getenv("HF_TOKEN")
80
+ if hf_token:
81
+ headers["Authorization"] = f"Bearer {hf_token}"
82
+
83
+ req = urllib.request.Request(BTC_WEIGHTS_URL, headers=headers)
84
+ with urllib.request.urlopen(req, timeout=60) as response, open(weights_path, 'wb') as out_file:
85
+ total_size = int(response.headers.get('content-length', 0))
86
+ downloaded = 0
87
+ block_size = 65536
88
+ while True:
89
+ chunk = response.read(block_size)
90
+ if not chunk:
91
+ break
92
+ out_file.write(chunk)
93
+ downloaded += len(chunk)
94
+ if total_size > 0:
95
+ percent = min(100, int(downloaded * 100 / total_size))
96
+ if percent % 25 == 0:
97
+ logger.debug(f"[BTC] Descargando: {percent}% ({downloaded / (1024*1024):.1f} MB / {total_size / (1024*1024):.1f} MB)")
98
 
99
  # Verificar que el archivo se descargó correctamente
100
  if not os.path.exists(weights_path) or os.path.getsize(weights_path) == 0:
requirements.txt CHANGED
@@ -50,7 +50,7 @@ curl-cffi>=0.7.0 # Permite a yt-dlp impersonar Chrome (bypass bot-ch
50
  # Nota: ffmpeg debe estar instalado en el sistema
51
 
52
  # --- UTILIDADES ---
53
- setuptools
54
  aiofiles==23.2.1
55
  pydantic[email]>=2.5.3
56
  pydantic-settings==2.1.0
 
50
  # Nota: ffmpeg debe estar instalado en el sistema
51
 
52
  # --- UTILIDADES ---
53
+ setuptools<70.0.0
54
  aiofiles==23.2.1
55
  pydantic[email]>=2.5.3
56
  pydantic-settings==2.1.0
tasks.py CHANGED
@@ -431,7 +431,29 @@ def detectar_y_corregir_afinacion_440(audio_path: str, output_base: str):
431
  return tuning_info
432
 
433
  # Cargar los primeros 45s para estimación rápida y precisa
434
- y, sr = librosa.load(audio_path, sr=22050, duration=45.0, mono=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
  if len(y) == 0:
436
  return tuning_info
437
 
@@ -646,47 +668,39 @@ def extraer_acordes_pro(audio_path, bpm=None, beats=None):
646
  Intenta usar primero madmom, y si no está disponible o falla, utiliza
647
  el fallback a BTCChordRecognizer (PyTorch Transformer).
648
  """
649
- # Intentar madmom primero
650
  acordes = extraer_acordes_madmom(audio_path)
651
- if acordes is not None:
652
- logger.info("[Acordes] Utilizando resultado exitoso de Madmom.")
653
  return acordes
654
 
655
  # Fallback a BTC Transformer
656
- logger.info("[Acordes] Madmom no disponible o falló. Usando fallback a BTC Transformer...")
657
  if BTCChordRecognizer is None or torch is None:
658
  logger.warning("[BTC] Modelo de acordes o PyTorch no disponible (no importado).")
659
  return []
660
 
661
  try:
662
- import warnings
663
- import logging as _logging
664
- # Silenciar todos los logs internos de BTCChordRecognizer
665
- _logging.getLogger('btc_toolkit').setLevel(_logging.CRITICAL)
666
- _logging.getLogger('urllib3').setLevel(_logging.CRITICAL)
667
- with warnings.catch_warnings():
668
- warnings.simplefilter("ignore")
669
- device = "cuda" if torch.cuda.is_available() else "cpu"
670
- recognizer = BTCChordRecognizer(device=device)
671
- btc_acordes = recognizer.recognize(audio_path, bpm=bpm, beats=beats)
672
-
673
- # Normalizar los acordes obtenidos de BTC por si acaso
674
- acordes_normalizados = []
675
- prev_chord = None
676
- for item in btc_acordes:
677
- mapped_chord = normalize_chord_to_maj_min(item["ch"])
678
- if mapped_chord != 'N' and mapped_chord != prev_chord:
679
- acordes_normalizados.append({"t": item["t"], "ch": mapped_chord})
680
- prev_chord = mapped_chord
681
-
682
- if acordes_normalizados and acordes_normalizados[0]["t"] > 0.5:
683
- acordes_normalizados.insert(0, {"t": 0.0, "ch": acordes_normalizados[0]["ch"]})
684
 
685
- logger.info(f"[BTC] Acordes detectados tras normalización: {len(acordes_normalizados)}")
686
- return acordes_normalizados
 
 
 
687
  except Exception as e:
688
- logger.warning(f"[BTC] Falló el fallback de acordes a BTC: {e}")
689
- logger.exception("Error en fallback a BTC:")
690
  return []
691
 
692
 
 
431
  return tuning_info
432
 
433
  # Cargar los primeros 45s para estimación rápida y precisa
434
+ try:
435
+ y, sr = librosa.load(audio_path, sr=22050, duration=45.0, mono=True)
436
+ except Exception:
437
+ try:
438
+ y, orig_sr = sf.read(audio_path, dtype='float32')
439
+ if y.ndim > 1:
440
+ y = np.mean(y, axis=1)
441
+ max_samples = int(orig_sr * 45.0)
442
+ if len(y) > max_samples:
443
+ y = y[:max_samples]
444
+ if orig_sr != 22050:
445
+ try:
446
+ import soxr
447
+ y = soxr.resample(y, orig_sr, 22050)
448
+ except Exception:
449
+ y = librosa.resample(y, orig_sr=orig_sr, target_sr=22050)
450
+ sr = 22050
451
+ except Exception:
452
+ from app.utils.harmony import _load_audio_via_ffmpeg
453
+ y, sr = _load_audio_via_ffmpeg(audio_path, sr=22050)
454
+ if len(y) > 22050 * 45:
455
+ y = y[:22050 * 45]
456
+
457
  if len(y) == 0:
458
  return tuning_info
459
 
 
668
  Intenta usar primero madmom, y si no está disponible o falla, utiliza
669
  el fallback a BTCChordRecognizer (PyTorch Transformer).
670
  """
671
+ # Intentar madmom primero si está disponible
672
  acordes = extraer_acordes_madmom(audio_path)
673
+ if acordes is not None and len(acordes) > 0:
674
+ logger.info(f"[Acordes] Utilizando resultado exitoso de Madmom ({len(acordes)} acordes).")
675
  return acordes
676
 
677
  # Fallback a BTC Transformer
678
+ logger.info("[Acordes] Madmom no disponible o no retornó acordes. Usando BTC Transformer...")
679
  if BTCChordRecognizer is None or torch is None:
680
  logger.warning("[BTC] Modelo de acordes o PyTorch no disponible (no importado).")
681
  return []
682
 
683
  try:
684
+ device = "cuda" if torch.cuda.is_available() else "cpu"
685
+ recognizer = BTCChordRecognizer(device=device)
686
+ btc_acordes = recognizer.recognize(audio_path, bpm=bpm, beats=beats)
687
+
688
+ # Normalizar los acordes obtenidos de BTC por si acaso
689
+ acordes_normalizados = []
690
+ prev_chord = None
691
+ for item in btc_acordes:
692
+ mapped_chord = normalize_chord_to_maj_min(item["ch"])
693
+ if mapped_chord != 'N' and mapped_chord != prev_chord:
694
+ acordes_normalizados.append({"t": item["t"], "ch": mapped_chord})
695
+ prev_chord = mapped_chord
 
 
 
 
 
 
 
 
 
 
696
 
697
+ if acordes_normalizados and acordes_normalizados[0]["t"] > 0.5:
698
+ acordes_normalizados.insert(0, {"t": 0.0, "ch": acordes_normalizados[0]["ch"]})
699
+
700
+ logger.info(f"[BTC] ✅ Acordes detectados tras normalización: {len(acordes_normalizados)}")
701
+ return acordes_normalizados
702
  except Exception as e:
703
+ logger.error(f"[BTC] Error en fallback de acordes a BTC: {e}", exc_info=True)
 
704
  return []
705
 
706