Catalogue de médias pour Jellyfin

cooper

Chevalier Jedi
Membre Confirmé
28 Octobre 2022
202
70
78
48
Ile-De-France
Professionnel
Non
Hello,

Les vacances étant propices aux réflexions, j'ai pondu un script shell (comptabile POSIX, donc pouvant tourner sur des shells de type Bourne et avec des outils disponibles de base sur les distributions GNU/Linux et les *BSD) permettant de récupérer la totalité des médias servis par un Jellyfin local (actuellement seules les catégories de film et de séries TV ; toute ma musithèque étant déjà en FLAC) en stockant les informations dans une base SQL.

Ceci afin de visualiser et identifier facilement les métadonnées dans le but d'harmoniser et/ou optimiser les fichiers d'après ces dernières (optimisation de la taille des fichiers, des débits, des formats, des définitions, etc.).

Jellyfin comporte une API RESTful afin de récupérer quantité de métadonnées stockées dans sa base SQLite, donc avec une fonction inhérente à MySQL/MariaDB (LOAD DATA Statement), l'extraction et l'import est extrêmement rapide : ~9 secondes pour ~5 200 fichiers (y)

Pour automatiser tout ça, un cronjob tourne quotidiennement.

A la fin, une synthèse des outils utilisés par ce script.

Bash:
#!/bin/sh

# Before invoking this script,
#
# 1) From an admin account, create an API key on Jellyfin:
# Go to the dashboard of Jellyfin > API Keys
#
# 2) For ease and convenience when working with a local SQL server,
# configure MySQL to use --login-path:
# https://dev.mysql.com/doc/refman/8.4/en/mysql-config-editor.html
#
# NB. For MariaDB users, it is safer to use inplace of `mysql --login-path=local [...]`:
#
# mysql \
#    -h localhost \
#    -u SQL_USER \
#    -p"SQL_USER_PASSWORD"
#
# ... or set up a ~/.my.cnf
#
# More information:
# https://mariadb.com/docs/server/security/user-account-management/authentication-from-mariadb-10-4

set -e

JELLYFIN="http://localhost:8096"
API_KEY="REDACTED"

MYSQL_DB="video_catalog"

SECURE_FILE_PRIV=$(mysql --login-path=local -Nse "SELECT @@secure_file_priv;")
TMPFILE="${SECURE_FILE_PRIV%/}/jellyfin_catalog.tsv"

trap 'rm -f "$TMPFILE"' EXIT INT TERM HUP

DATE=$(date "+%Y-%m-%d %H:%M:%S")

curl -fsS \
"$JELLYFIN/Items?Recursive=true&IncludeItemTypes=Movie,Episode&Fields=Path,MediaStreams,ParentIndexNumber,IndexNumber,SeriesName&api_key=$API_KEY" |

jq -r --arg DATE "$DATE" '

.Items[]

| (
    [(.MediaStreams // [])[] | select(.Type=="Video")][0] // {}
) as $v

| (
    [(.MediaStreams // [])[] | select(.Type=="Audio")][0] // {}
) as $a

| [
    $DATE,
    (.Id // ""),
    (.Name // ""),
    (.Path // ""),
    (if .Type=="Movie" then "movie" else "episode" end),
    (.SeriesName // ""),
    (.ParentIndexNumber // 0),
    (.IndexNumber // 0),
    ($v.Codec // ""),
    ($v.Width // 0),
    ($v.Height // 0),
    ($v.BitRate // 0),
    ($a.Codec // ""),
    ($a.Channels // 0),
    ($a.Language // "")
]

| join("|")

' |

while IFS="|" read \
    scan_date \
    jellyfin_id \
    name \
    path \
    media_type \
    series_name \
    season \
    episode \
    codec_video \
    width \
    height \
    bitrate_video \
    audio_codec \
    audio_channels \
    audio_language
do

    if stat -f "%z" / >/dev/null 2>&1; then
        STAT_SIZE='stat -f %z'
    else
        STAT_SIZE='stat -c %s'
    fi
   
    if [ -f "$path" ]; then
        size=$($STAT_SIZE "$path")
    else
        echo "Fichier absent : $path" >&2
        size=0
    fi

    printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" \
        "$scan_date" \
        "$jellyfin_id" \
        "$name" \
        "$path" \
        "$media_type" \
        "$series_name" \
        "$season" \
        "$episode" \
        "$codec_video" \
        "$width" \
        "$height" \
        "$bitrate_video" \
        "$audio_codec" \
        "$audio_channels" \
        "$audio_language" \
        "$size"

done > "$TMPFILE"

mysql --login-path=local "$MYSQL_DB" <<EOF
TRUNCATE TABLE media;

LOAD DATA INFILE '$TMPFILE'
INTO TABLE media
FIELDS TERMINATED BY '\t'
LINES TERMINATED BY '\n'
(
    scan_date,
    jellyfin_id,
    name,
    path,
    media_type,
    series_name,
    season_number,
    episode_number,
    codec_video,
    width,
    height,
    bitrate_video,
    audio_codec,
    audio_channels,
    audio_language,
    size_bytes
);
EOF

exit 0

Pour avoir une vue synthétique de la table media dans la base video_catalog, on peut créer une vue SQL :

SQL:
CREATE VIEW media_readable AS
SELECT
    name,
    codec_video,
    CONCAT(width,'x',height) AS resolution,
    ROUND(bitrate_video/1000) AS bitrate_kbps,
    ROUND(size_bytes/1073741824,2) AS size_gb,
    path,
    media_type
FROM media;

OutilsDescriptionURL
curlRécupération des données exposées par l'API de Jellyfinhttps://curl.se/
jqFormattage des données pour importation SQLhttps://jqlang.org/
mysqlClient pour importation de données dans un serveur (My)SQLhttps://www.mysql.com/fr/
statRécupération de la taille précise des fichiershttps://pubs.opengroup.org/onlinepubs/009696799/functions/stat.html
shInterpréteurhttps://fr.wikipedia.org/wiki/Bourne_shell
 
Alors ca a l'air très cool ce que tu dis mais sinon pour les non dev ca donne quoi ? Non parce que la je comprend à peut pret le but mais je comprend pas comment le mettre en place/consulter ce que va sortir ton script.
 
@Anachron

Comme tout script shell, celui-ci est une fois rendu exécutable (chmod +x script.sh), se lance depuis la ligne de commande : le « shell » du système avec ./script.sh

Le script fait les choses suivantes :

  1. Défini des variables d'environnements locales à l'exécution du script ;
  2. Récupérer via curl avec l'aide de la clef API générée dans le tableau de bord de Jellyfin depuis un compte admin via le protocole HTTP sur le port local TCF/8096 (Jellyfin tournant localement sur le système) les données des médias (films et séries TV, catalogués en interne comme tel par Jellyfin) ;
  3. Produire et formatter les données en JSON avec jq et stocker tout ça dans un fichier temporaire défini par la variable d'environnement $TMPFILE ;
  4. Injecter les données contenues de $TMPFILE dans la table media de la base video_catalog SQL.
Ce script demande d'être familier avec le shell de ton système et l'installation d'un serveur SQL sur ce dernier.
 
En fait le sous entendu de mon premier message c'est " c'est quand même dommage de poster ca dans Tuto sans rien expliquer et qu'au final 80% des gens qui serait intéresser vont rien comprendre et passer leur chemin "


Par définition un tutoriel ca sert a expliquer pas à pas les choses et la je suis un peu déçu tu explique rien du coup ca a assez peu d'intérêt car les gens qui savent faire ce genre de chose l'ont surement déjà fait et ceux qui ne savent pas le faire seront pas plus avancer par ton poste.
 
En fait le sous entendu de mon premier message c'est " c'est quand même dommage de poster ca dans Tuto sans rien expliquer et qu'au final 80% des gens qui serait intéresser vont rien comprendre et passer leur chemin "


Par définition un tutoriel ca sert a expliquer pas à pas les choses et la je suis un peu déçu tu explique rien du coup ca a assez peu d'intérêt car les gens qui savent faire ce genre de chose l'ont surement déjà fait et ceux qui ne savent pas le faire seront pas plus avancer par ton poste.

Nous sommes sur un forum, donc si tu as d'autres questions n'hésites pas.
 
Nous sommes sur un forum, donc si tu as d'autres questions n'hésites pas.
Non mais Ok mais dans ce cas tu t'es tromper de section poste dans général car la tu a pas fait de tuto. Le prend pas mal hein mais pour moi ton poste n'a rien a faire dans cette section en l'état.
 
Non mais Ok mais dans ce cas tu t'es tromper de section poste dans général car la tu a pas fait de tuto. Le prend pas mal hein mais pour moi ton poste n'a rien a faire dans cette section en l'état.

Si ce post n'a pas sa place dans cette section, gageons que le nécessaire sera fait (y)