Files
ohmj2/api/README.md
T

9.4 KiB

OHMJ API Documentation

API REST pour la gestion des partitions de l'Harmonie de Montpellier-Jacou.

Installation

Prérequis : PHP 8.5 avec les extensions gd, zlib, curl.

cd api
php composer.phar install   # vendor/ est déjà présent dans le dépôt
cp .env.example .env        # puis définir JWT_SECRET

Dépendances (composer.json) :

Package Rôle
setasign/fpdi import de pages PDF (watermark)
setasign/fpdf génération PDF de base
tecnickcom/tcpdf boîte à outils PDF

Base URL

http://localhost:8000

Authentification

L'API utilise JWT (JSON Web Token) pour l'authentification.

Login

POST /login
Content-Type: application/json

Corps de la requête :

{
  "username": "admin",
  "password": "password"
}

Réponse :

{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "username": "admin",
    "role": "admin"
  }
}

Rôles disponibles :

  • admin - Accès complet
  • user - Accès lecture seule

Utilisation du token

Inclure le token dans l'en-tête Authorization :

Authorization: Bearer <token>

Endpoints

GET /scores

Liste toutes les partitions.

GET /scores

Réponse :

{
  "success": true,
  "scores": [
    {
      "id": "102",
      "name": "A Legend from Yao",
      "compositor": "Yeh Shu-Han",
      "ressource": "https://youtube.com/watch?v=xxx"
    },
    {
      "id": "390",
      "name": "La part d Euterpe",
      "compositor": "Michael CUVILLON"
    }
  ]
}

GET /scores/:id

Détails d'une partition. Pour les partitions multi-morceaux (ex: 390), retourne tous les instruments de toutes les pièces.

GET /scores/102
GET /scores/390

Réponse (score simple) :

{
  "success": true,
  "score": {
    "id": "102",
    "name": "A Legend from Yao",
    "compositor": "Yeh Shu-Han",
    "ressource": "https://youtube.com/watch?v=xxx",
    "instruments": [
      {
        "id": "cla",
        "title": "Clarinette",
        "piece": "1",
        "parts": [
          {
            "id": "1",
            "files": [
              {
                "name": "clarinette-sib-1",
                "filename": "clarinette-sib-1.pdf",
                "path": "102/1/cla/1/clarinette-sib-1.pdf",
                "part": "1",
                "key": "sib",
                "clef": null,
                "variant": null
              }
            ]
          }
        ]
      }
    ]
  }
}

Réponse (partition multi-morceaux comme 390) :

{
  "success": true,
  "score": {
    "id": "390",
    "name": "La part d Euterpe",
    "compositor": "Michael CUVILLON",
    "instruments": [
      {
        "id": "cla",
        "title": "Clarinette",
        "piece": "1",
        "parts": [...]
      },
      {
        "id": "sax",
        "title": "Sax Alto",
        "piece": "2",
        "parts": [...]
      }
    ]
  }
}

GET /scores/:id/instruments

Liste les instruments d'une partition. Pour les partitions multi-morceaux (ex: 390), peut filtrer par pièce.

GET /scores/102/instruments
GET /scores/390/instruments?piece=1

Paramètre optionnel :

  • piece - Numéro de la pièce (pour partitions multi-morceaux)

Réponse :

{
  "success": true,
  "instruments": [
    {
      "id": "cla",
      "title": "Clarinette",
      "piece": "1",
      "parts": [
        {
          "id": "1",
          "files": [
            {
              "name": "clarinette-sib-1",
              "filename": "clarinette-sib-1.pdf",
              "path": "102/1/cla/1/clarinette-sib-1.pdf",
              "part": "1",
              "key": "sib",
              "clef": null,
              "variant": null
            }
          ]
        }
      ]
    }
  ]
}

Champs des fichiers :

  • name - Nom du fichier sans extension
  • filename - Nom du fichier avec extension
  • path - Chemin relatif pour download
  • part - Numéro de partie (ex: "1", "2_3")
  • key - Tonalité (ex: "sib", "mib", "fa")
  • clef - Clé (ex: "clesol", "clefa")
  • variant - Variante (ex: "solo", "default")

GET /pieces/:scoreId

Liste les pièces d'une partition.

GET /pieces/102

Réponse :

{
  "success": true,
  "pieces": [
    {
      "id": 1,
      "name": "Pièce 1"
    }
  ]
}

GET /download/:path

Télécharge un fichier PDF.

GET /download/102/1/cla/1/clarinette-sib-1.pdf

Réponse : Fichier PDF (Content-Type: application/pdf)

Structure des chemins :

NUM/PIECE/INSTRUMENT/VERSION/PARTIE.pdf

Instruments disponibles :

Code Instrument
dir Direction
pic Piccolo
flu Flûte
cla Clarinette
clb Clarinette basse
sax Saxophone alto
sab Saxophone baryton
sat Saxophone ténor
coa Cor anglais
cba Contrebasse
cor Cor
trp Trompette
trb Trombone
tub Tuba
htb Hautbois
bas Basson
per Percussion
crn Cornet
eup Euphonium
har Harpe
pia Piano
sup Parties combinées ("supérieur")
par Parties combinées

Routes Admin

POST /admin/scores

Créer une nouvelle partition.

POST /admin/scores
Authorization: Bearer <token_admin>
Content-Type: application/json

Corps de la requête :

{
  "id": "200",
  "name": "Nouvelle partition",
  "compositor": "Compositeur"
}

PUT /admin/scores/:id

Mettre à jour une partition.

PUT /admin/scores/102
Authorization: Bearer <token_admin>
Content-Type: application/json

Corps de la requête :

{
  "name": "Nouveau nom",
  "compositor": "Nouveau compositeur",
  "ressource": "https://youtube.com/watch?v=xxx"
}

Paramètres optionnels :

  • name - Nom du morceau
  • compositor - Nom du compositeur
  • ressource - Lien externe (YouTube, site éditeur, etc.)

Bug connu : ressource n'est actuellement pas persisté par cette route (ScoreScanner::updateScore() n'accepte pas le paramètre et la reconstruction de score.ini ne réécrit pas ressource). À corriger côté backend.


DELETE /admin/scores/:id

Supprimer une partition.

DELETE /admin/scores/102
Authorization: Bearer <token_admin>

GET /admin/scores/:id/files

Récupérer l'arborescence des fichiers d'une partition.

GET /admin/scores/001/files
Authorization: Bearer <token_admin>

Réponse :

{
  "success": true,
  "files": [
    {
      "name": "1",
      "path": "1",
      "type": "folder",
      "children": [
        {
          "name": "cla",
          "path": "1/cla",
          "type": "folder",
          "children": [
            {
              "name": "1",
              "path": "1/cla/1",
              "type": "folder",
              "children": [
                {
                  "name": "clarinette_sib_1.pdf",
                  "path": "1/cla/1/clarinette_sib_1.pdf",
                  "type": "file"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

DELETE /admin/scores/:id/files

Supprimer un fichier spécifique.

DELETE /admin/scores/001/files?path=1/cla/1/clarinette_sib_1.pdf
Authorization: Bearer <token_admin>

Paramètres :

  • path - Chemin relatif du fichier (requis)

POST /admin/scores/:id/upload

Uploader un fichier PDF pour une partition.

POST /admin/scores/001/upload
Authorization: Bearer <token_admin>
Content-Type: multipart/form-data

Corps de la requête :

file: <fichier_pdf>
piece: 1
instrument: cla
version: 1
key: sib (optionnel)
clef: clesol (optionnel)
variant: solo (optionnel)
part: 1 (optionnel, défaut: 1)
watermark: true (optionnel)          # ajoute le numéro de partition sur le PDF
watermarkPosition: left|right (optionnel, défaut: left)

Codes d'erreur

Code Description
200 Succès
400 Requête invalide (champ manquant, payload incorrect)
401 Non authentifié
403 Accès interdit (rôle insuffisant)
404 Ressource non trouvée
413 Fichier trop volumineux
429 Trop de requêtes (rate limiting)
500 Erreur serveur

Lancer les tests

cd api
php tests.php

Les tests vérifient :

  • Auth : Login, mauvais mots de passe, token manquant/invalide
  • Scores : CRUD, gestion d'erreurs pour ressources inexistantes
  • Create Score with Pieces : Création avec plusieurs parties, vérification score.ini
  • Files : Get files tree, suppression de fichiers

Lancer le serveur

cd api
php -c php-upload.ini -S localhost:8000 router.php

-c php-upload.ini est requis pour autoriser l'upload de PDF (64M max). ini_set() ne peut pas modifier upload_max_filesize/post_max_size à l'exécution. L'API répond sur http://localhost:8000 (sans préfixe /api).


Structure des fichiers

legacy/Scores/
├── 102/
│   ├── 1/
│   │   ├── cla/
│   │   │   ├── 1/
│   │   │   │   ├── clarinette-sib-1.pdf
│   │   │   │   ├── clarinette-sib-2.pdf
│   │   │   │   └── clarinette-mib-1.pdf
│   │   │   └── 2/
│   │   │       └── clarinette-sib-1.pdf
│   │   ├── flu/
│   │   └── sax/
│   └── 2/
└── score.ini