Compare commits
9 Commits
96cd60912b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a0cfe9ef2b | |||
|
5d10c258c8
|
|||
|
ccb73807c0
|
|||
|
adfa2b1710
|
|||
|
dc072f36bd
|
|||
|
1a25e26829
|
|||
|
4340d876a3
|
|||
|
0f0f263c98
|
|||
|
ca4a20a1b2
|
@@ -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
|
|
||||||
@@ -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
14
Dockerfile
Normal 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
16
docker-compose.yml
Normal 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
3707
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user