Compare commits

...

9 Commits

Author SHA1 Message Date
a0cfe9ef2b Version 1.3.0-rc3 - Modification du changelog 2025-10-08 15:13:17 +02:00
5d10c258c8 Version 1.3.1-rc2 - Correctif sur Stream 2025-10-06 23:41:15 +02:00
ccb73807c0 Version 1.3.1-rc1 - Modification 2025-10-06 23:30:41 +02:00
adfa2b1710 Version 1.3.1 - Modification du Bug des medias 2025-10-06 23:24:28 +02:00
dc072f36bd Version 1.3.0-rc2 - Modification du container 2025-10-06 23:11:35 +02:00
1a25e26829 Version 1.3.0-rc2 - Modif Docker 2025-10-04 17:59:07 +02:00
4340d876a3 Version 1.3.0-rc1 - Modification de la methode ; Error 2025-10-04 17:30:54 +02:00
0f0f263c98 Version 1.3.0 - Ajout de Docker
Some checks failed
Deployment Pipeline / deploy (push) Has been cancelled
2025-10-04 17:28:58 +02:00
ca4a20a1b2 Version 1.3.0 - Ajout de Docker 2025-10-04 17:28:50 +02:00
10 changed files with 2437 additions and 1506 deletions

View File

@@ -1,86 +0,0 @@
name: Deployment Pipeline
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup SSH
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa
ssh-keyscan git.raphix.fr >> ~/.ssh/known_hosts
- name: Deploy Subsonics as gitlab-ci
run: |
ssh -A -o StrictHostKeyChecking=no raphix@alpha.raphix.fr << 'EOF'
sudo su - gitlab-ci -c '
set -e
# Variables PM2 et npm
export PM2_HOME=/home/gitlab-ci/.pm2
export NPM_CONFIG_CACHE=/home/gitlab-ci/.npm
mkdir -p $PM2_HOME $NPM_CONFIG_CACHE
chown -R gitlab-ci:gitlab-ci $PM2_HOME $NPM_CONFIG_CACHE
echo "[Subsonics-Deploy] - Stage - Déploiement - START"
echo "[Subsonics-Deploy] - Arrêt de Subsonics : Processing"
cd /home/gitlab-ci
pm2 stop "Subsonics - Backend" || true
pm2 delete "Subsonics - Backend" || true
echo "[Subsonics-Deploy] - Arrêt de Subsonics : Success"
# Préparer tempdata
if [ ! -d "/home/gitlab-ci/backend/data" ]; then
mkdir -p /home/gitlab-ci/backend/data
fi
mv /home/gitlab-ci/backend/data/ /home/gitlab-ci/tempdata || true
echo "[Subsonics-Deploy] - Suppression de Subsonics : Processing"
rm -rf ./backend
echo "[Subsonics-Deploy] - Suppression de Subsonics : Success"
echo "[Subsonics-Deploy] - Installation de Subsonics : Processing"
git clone https://git.raphix.fr/subsonics/chopin backend
echo "[Subsonics-Deploy] - Installation de Subsonics : Success"
echo "[Subsonics-Deploy] - Installation des dépendances : Processing"
cd /home/gitlab-ci/backend
# Nettoyage node_modules et tempdata
rm -rf node_modules
if [ -d "/home/gitlab-ci/tempdata" ]; then
mv /home/gitlab-ci/tempdata/ ./data
fi
# Assurer la propriété gitlab-ci
chown -R gitlab-ci:gitlab-ci /home/gitlab-ci/backend
mkdir -p $NPM_CONFIG_CACHE
chown -R gitlab-ci:gitlab-ci $NPM_CONFIG_CACHE
npm install --omit=dev
echo "[Subsonics-Deploy] - Installation des dépendances : Success"
echo "[Subsonics-Deploy] - Démarrage de Subsonics : Processing"
cd /home/gitlab-ci
pm2 start subsonic.config.js
echo "[Subsonics-Deploy] - Démarrage de Subsonics : Success"
echo "[Subsonics-Deploy] - Stage - Déploiement - END"
'
EOF

View File

@@ -1,4 +1,12 @@
<div class="changelog-version changelog-actual"> <div class="changelog-version changelog-actual">
<h2>Chopin - Version /*1.3.0*/</h2>
<p class="changelog-date">*_Date de sortie_*: *-07/10/2025-*</p>
<ul>
<li>/#[FIX][BACKEND]#/ Fix de l'erreur sur les fichiers médias importés qui ne fonctionnaient pas</li>
<li>/#[BACKEND]#/ Migration du serveur vers une nouvelle architecture et Docker (instabilité prévu)</li>
</ul>
</div>
<div class="changelog-version">
<h2>Chopin - Version /*1.2.0*/</h2> <h2>Chopin - Version /*1.2.0*/</h2>
<p class="changelog-date">*_Date de sortie_*: *-07/09/2025-*</p> <p class="changelog-date">*_Date de sortie_*: *-07/09/2025-*</p>
<ul> <ul>

14
Dockerfile Normal file
View File

@@ -0,0 +1,14 @@
FROM node:lts-alpine
WORKDIR /app
# Installer bash et autres outils utiles
RUN apk add --no-cache bash
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 4000
CMD ["npm", "run", "start"]

16
docker-compose.yml Normal file
View File

@@ -0,0 +1,16 @@
version: "3.9"
services:
subsonics-backend:
build:
context: . # dossier backend
dockerfile: Dockerfile
container_name: subsonics-backend
ports:
- "4000:4000"
volumes:
- subsonics-backend-data:/app/data
restart: unless-stopped
volumes:
subsonics-backend-data:

3707
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "chopin-backend", "name": "chopin-backend",
"version": "1.2.0", "version": "1.3.1",
"description": "Discord Bot for music - Fetching everywhere !", "description": "Discord Bot for music - Fetching everywhere !",
"main": "src/main.js", "main": "src/main.js",
"nodemonConfig": { "nodemonConfig": {
@@ -30,16 +30,19 @@
"ffprobe-static": "^3.1.0", "ffprobe-static": "^3.1.0",
"fluent-ffmpeg": "^2.1.3", "fluent-ffmpeg": "^2.1.3",
"googleapis": "^149.0.0", "googleapis": "^149.0.0",
"https-proxy-agent": "^7.0.6",
"libsodium-wrappers": "^0.7.15", "libsodium-wrappers": "^0.7.15",
"loguix": "^1.4.2", "loguix": "^1.4.2",
"mime-types": "^3.0.1", "mime-types": "^3.0.1",
"nodemon": "^3.1.10", "nodemon": "^3.1.10",
"pm2": "^5.4.3", "pm2": "^6.0.11",
"socket.io": "^4.8.1", "socket.io": "^4.8.1",
"soundcloud.ts": "^0.6.3", "soundcloud.ts": "^0.6.3",
"spotify-web-api-node": "^5.0.2", "spotify-web-api-node": "^5.0.2",
"undici": "^7.16.0",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"webmetrik": "^0.1.4", "webmetrik": "^0.1.4",
"youtubei.js": "^15.1.1",
"yt-search": "^2.13.1", "yt-search": "^2.13.1",
"ytfps": "^1.2.0" "ytfps": "^1.2.0"
} }

View File

@@ -2,56 +2,105 @@ const ffprobe = require('ffprobe');
const ffprobeStatic = require('ffprobe-static'); const ffprobeStatic = require('ffprobe-static');
const { getReadableDuration } = require('../utils/TimeConverter'); const { getReadableDuration } = require('../utils/TimeConverter');
const clog = require("loguix").getInstance("Song") const clog = require("loguix").getInstance("Song")
const path = require('path');
const fs = require('fs');
async function getMediaInformation(instance, media, provider) { async function getMediaInformation(instance, media, provider) {
let tmpFile = null;
try { try {
const info = await ffprobe(media.attachment.url, { path: ffprobeStatic.path }); // 1. Vérifier si ./tmp existe, sinon le créer
if (info.streams?.[0]?.duration_ts) { const tmpDir = path.resolve("./tmp");
instance.duration = info.streams[0].duration; if (!fs.existsSync(tmpDir)) {
instance.readduration = getReadableDuration(instance.duration) fs.mkdirSync(tmpDir, { recursive: true });
} }
// Vérification pour éviter une erreur si `streams[0]` ou `tags` n'existe pas // 2. Télécharger le fichier depuis l'URL
instance.thumbnail = info.streams?.[0]?.tags?.thumbnail ?? const url = media.attachment.url;
"https://radomisol.fr/wp-content/uploads/2016/08/cropped-note-radomisol-musique.png"; const res = await fetch(url);
if (!res.ok) throw new Error(`Erreur HTTP ${res.status}`);
// Obtenir le titre (sinon utiliser le nom du fichier) const buffer = Buffer.from(await res.arrayBuffer());
instance.title = info.streams?.[0]?.tags?.title ?? media.attachment.name; tmpFile = path.join(tmpDir, `${Date.now()}-${media.attachment.name}`);
fs.writeFileSync(tmpFile, buffer);
// Obtenir l'auteur (s'il existe) // 3. Lancer ffprobe sur le fichier local
instance.author = info.streams?.[0]?.tags?.artist ?? instance.author; const info = await ffprobe(tmpFile, { path: ffprobeStatic.path });
return true;
} catch (err) { // 4. Récupérer les informations
clog.error("Impossible de récupérer les informations de la musique : " + media.attachment.name) if (info.streams?.[0]?.duration_ts) {
clog.error(err) instance.duration = info.streams[0].duration;
return null; instance.readduration = getReadableDuration(instance.duration);
} }
}
instance.thumbnail = info.streams?.[0]?.tags?.thumbnail ??
"https://radomisol.fr/wp-content/uploads/2016/08/cropped-note-radomisol-musique.png";
instance.title = info.streams?.[0]?.tags?.title ?? media.attachment.name;
instance.author = info.streams?.[0]?.tags?.artist ?? instance.author;
return true;
} catch (err) {
clog.error("Impossible de récupérer les informations de la musique : " + media.attachment.name);
console.error(err);
return null;
} finally {
// 5. Nettoyage : supprimer le fichier temporaire
if (tmpFile && fs.existsSync(tmpFile)) {
try {
fs.unlinkSync(tmpFile);
} catch (e) {
console.error("Erreur lors du unlink:", e);
}
}
}
}
async function getMediaInformationFromUrl(instance, url) {
let tmpFile = null;
try {
// 1. Vérifier si ./tmp existe, sinon le créer
const tmpDir = path.resolve("./tmp");
if (!fs.existsSync(tmpDir)) {
fs.mkdirSync(tmpDir, { recursive: true });
}
// 2. Télécharger le fichier en mémoire et lécrire en sync
const res = await fetch(url);
if (!res.ok) throw new Error(`Erreur HTTP ${res.status}`);
const buffer = Buffer.from(await res.arrayBuffer());
tmpFile = path.join(tmpDir, `${Date.now()}.mp3`);
fs.writeFileSync(tmpFile, buffer);
// 3. Lancer ffprobe sur le fichier local
const info = await ffprobe(tmpFile, { path: ffprobeStatic.path });
async function getMediaInformationFromUrl(instance, url) {
try {
const info = await ffprobe(url, { path: ffprobeStatic.path });
if (info.streams?.[0]?.duration_ts) { if (info.streams?.[0]?.duration_ts) {
instance.duration = info.streams[0].duration; instance.duration = info.streams[0].duration;
instance.readduration = getReadableDuration(instance.duration); instance.readduration = getReadableDuration(instance.duration);
} }
// Vérification pour éviter une erreur si `streams[0]` ou `tags` n'existe pas
instance.thumbnail = info.streams?.[0]?.tags?.thumbnail ?? instance.thumbnail = info.streams?.[0]?.tags?.thumbnail ??
"https://radomisol.fr/wp-content/uploads/2016/08/cropped-note-radomisol-musique.png"; "https://radomisol.fr/wp-content/uploads/2016/08/cropped-note-radomisol-musique.png";
// Obtenir le titre (sinon utiliser le nom du fichier)
instance.title = info.streams?.[0]?.tags?.title ?? "Titre inconnu"; instance.title = info.streams?.[0]?.tags?.title ?? "Titre inconnu";
// Obtenir l'auteur (s'il existe)
instance.author = info.streams?.[0]?.tags?.artist ?? "Auteur inconnu"; instance.author = info.streams?.[0]?.tags?.artist ?? "Auteur inconnu";
return true; return true;
} catch (err) { } catch (err) {
clog.error("Impossible de récupérer les informations de la musique depuis l'URL : " + url); clog.error("Impossible de récupérer les informations de la musique depuis l'URL : " + url);
console.log(err) console.error(err);
clog.error(err);
return null; return null;
} finally {
// 4. Nettoyage : supprimer le fichier temporaire
if (tmpFile && fs.existsSync(tmpFile)) {
try {
fs.unlinkSync(tmpFile);
} catch (e) {
console.error("Erreur lors du unlink:", e);
}
}
} }
} }

View File

@@ -6,10 +6,8 @@ const ffmpeg = require('fluent-ffmpeg')
async function getStream(song) { async function getStream(song) {
try { try {
const stream = await fetch(song.url).then(res => res.body);
return song.url; return stream;
} catch(e) { } catch(e) {
clog.error("Erreur lors de la lecture de la musique : " + song.title) clog.error("Erreur lors de la lecture de la musique : " + song.title)
clog.error(e) clog.error(e)

View File

@@ -283,6 +283,7 @@ class Player {
} }
async setDuration(duration) { async setDuration(duration) {
//FIXME: SET DURATION FONCTIONNE TRES LENTEMENT //FIXME: SET DURATION FONCTIONNE TRES LENTEMENT
if (this.checkConnection()) return; if (this.checkConnection()) return;
if (this.queue.current == null) return; if (this.queue.current == null) return;

View File

@@ -447,7 +447,6 @@ async function setFullBan(id) {
} }
} }
function deleteAccount(id) { function deleteAccount(id) {
const user = getUserById(id); const user = getUserById(id);
if (user) { if (user) {