diff --git a/.env.dev-exemple b/.env.dev-exemple index e2d88c1b02..cb7b19c3f5 100644 --- a/.env.dev-exemple +++ b/.env.dev-exemple @@ -4,7 +4,7 @@ DJANGO_SUPERUSER_EMAIL= ### You can use internal registry ELASTICSEARCH_TAG=elasticsearch:8.17.3 NODE_TAG=node:23 -PYTHON_TAG=python:3.9-bookworm +PYTHON_TAG=python:3.12-bookworm REDIS_TAG=redis:alpine3.21 ### DOCKER_ENV: You can specify light or full. ### In case of value changing, you have to rebuild and restart your container. diff --git a/.github/workflows/pod_dev.yml b/.github/workflows/pod_dev.yml index 77ff1a5c0f..6f2e66c56b 100644 --- a/.github/workflows/pod_dev.yml +++ b/.github/workflows/pod_dev.yml @@ -14,14 +14,14 @@ on: workflow_dispatch: env: - PYTHON_VERSION: '3.9' + PYTHON_VERSION: '3.10' DJANGO_SUPERUSER_USERNAME: "admin" DJANGO_SUPERUSER_PASSWORD: "passwd" DJANGO_SUPERUSER_EMAIL: "noreply@uni.fr" ELASTICSEARCH_TAG: "elasticsearch:8.17.3" NODE_VERSION: "23" NODE_TAG: "node:23" - PYTHON_TAG: "python:3.9-bullseye" + PYTHON_TAG: "python:3.10-bookworm" REDIS_TAG: "redis:alpine3.21" DOCKER_ENV: "full-test" diff --git a/CONFIGURATION_FR.md b/CONFIGURATION_FR.md index de48f3d6ee..d4918a34bb 100644 --- a/CONFIGURATION_FR.md +++ b/CONFIGURATION_FR.md @@ -3,7 +3,9 @@ ## Informations générales La plateforme Esup-Pod se base sur le framework Django écrit en Python.
-Elle est compatible avec les versions 3.9, 3.10 et 3.12 de Python.
+Elle est compatible avec les versions 3.10 et 3.12 de Python.
+> Attention : à partir d’Esup-Pod version 4.2,
+ la version 3.9 de Python n’est plus supportée.
**Django Version : 4.2 LTS**
@@ -13,42 +15,42 @@ Elle est compatible avec les versions 3.9, 3.10 et 3.12 de Python.
Voici les configurations des applications tierces utilisées par Esup-Pod.
* `CAS` - > valeur par défaut : `1.5.3` + > default value: `1.5.3` >> Système d’authentification SSO_CAS
>> [kstateome/django-cas](https://github.com/kstateome/django-cas)
* `ModelTranslation` - > valeur par défaut : `0.19.11` + > default value: `0.19.11` >> L’application modeltranslation est utilisée pour traduire le contenu dynamique
>> des modèles Django existants
>> [django-modeltranslation.readthedocs.io](https://django-modeltranslation.readthedocs.io/en/latest/installation.html#configuration)
* `captcha` - > valeur par défaut : `0.6.0` + > default value: `0.6.0` >> Gestion du captcha du formulaire de contact
>> [django-simple-captcha.readthedocs.io](https://django-simple-captcha.readthedocs.io/en/latest/usage.html)
* `chunked_upload` - > valeur par défaut : `2.0.0` + > default value: `2.0.0` >> Envoi de fichier par morceaux // voir pour mettre à jour si nécessaire
>> [juliomalegria/django-chunked-upload](https://github.com/juliomalegria/django-chunked-upload)
* `ckeditor` - > valeur par défaut : `6.3.0` + > default value: `6.3.0` >> ATTENTION. django-ckeditor integre la version gratuite de CKEditor 4.22.1,
>> qui n'est plus prise en charge et qui présente des problèmes de sécurité non résolus,
>> voir par exemple .
* `django_select2` - > valeur par défaut : `latest` + > default value: `latest` >> Recherche et completion dans les formulaires
>> [django-select2.readthedocs.io](https://django-select2.readthedocs.io/en/latest/)
* `honeypot` - > valeur par défaut : `1.2.1` + > default value: `1.2.1` >> Utilisé pour le formulaire de contact de Pod -
>> ajoute un champ caché pour diminuer le spam
>> [jamesturk/django-honeypot](https://github.com/jamesturk/django-honeypot/)
* `mozilla_django_oidc` - > valeur par défaut : `4.0.1` + > default value: `4.0.1` >> Système d’authentification OpenID Connect
>> [mozilla-django-oidc.readthedocs.io](https://mozilla-django-oidc.readthedocs.io/en/stable/installation.html)
* `pwa` - > valeur par défaut : `2.0.1` + > default value: `2.0.1` >> Mise en place du mode PWA grâce à l’application Django-pwa
>> Voici la configuration par défaut pour Pod,
>> vous pouvez surcharger chaque variable dans votre fichier de configuration.
@@ -74,23 +76,23 @@ Voici les configurations des applications tierces utilisées par Esup-Pod.
>> >> Pour en savoir plus : [silviolleite/django-pwa](https://github.com/silviolleite/django-pwa)
* `rest_framework` - > valeur par défaut : `3.15.2` + > default value: `3.15.2` >> mise en place de l’API rest pour l’application
>> [django-rest-framework.org](https://www.django-rest-framework.org/)
* `shibboleth` - > valeur par défaut : `latest` + > default value: `latest` >> Système d’authentification Shibboleth
>> [Brown-University-Library/django-shibboleth-remoteuser](https://github.com/Brown-University-Library/django-shibboleth-remoteuser)
* `sorl.thumbnail` - > valeur par défaut : `12.11.0` + > default value: `12.11.0` >> Utilisée pour la génération de miniature des images
>> [sorl-thumbnail.readthedocs.io](https://sorl-thumbnail.readthedocs.io/en/latest/reference/settings.html)
* `tagging` - > valeur par défaut : `0.5.0` + > default value: `0.5.0` >> Gestion des mots-clés associés à une vidéo // voir pour référencer une nouvelle application
>> [django-tagging.readthedocs.io](https://django-tagging.readthedocs.io/en/develop/#settings)
* `tagulous` - > valeur par défaut : `2.1.0` + > default value: `2.1.0` >> Gestion des mots-clés associés à un objet Django.
>> [django-tagulous.readthedocs.io](https://django-tagulous.readthedocs.io)
@@ -99,7 +101,7 @@ Voici les configurations des applications tierces utilisées par Esup-Pod.
### Base de données * `DATABASES` - > valeur par défaut : + > default value: ```python { @@ -140,34 +142,34 @@ Voici les configurations des applications tierces utilisées par Esup-Pod.
### Courriel * `CONTACT_US_EMAIL` - > valeur par défaut : `` + > default value: `` >> Liste des adresses destinataires des courriels de contact
* `CUSTOM_CONTACT_US` - > valeur par défaut : `False` + > default value: `False` >> Si 'True', les e-mails de contacts seront adressés, selon le sujet,
>> soit au propriétaire de la vidéo soit au(x) manager(s) des vidéos Pod.
>> (voir `USER_CONTACT_EMAIL_CASE` et `USE_ESTABLISHMENT_FIELD`)
* `DEFAULT_FROM_EMAIL` - > valeur par défaut : `noreply` + > default value: `noreply` >> Expediteur par défaut pour les envois de courriel (contact, encodage etc.)
* `EMAIL_HOST` - > valeur par défaut : `smtp.univ.fr` + > default value: `smtp.univ.fr` >> nom du serveur smtp
>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#email-host)_
* `EMAIL_PORT` - > valeur par défaut : `25` + > default value: `25` >> Port d’écoute du serveur SMTP.
* `EMAIL_SUBJECT_PREFIX` - > valeur par défaut : `` + > default value: `` >> Préfixe par défaut pour l’objet des courriels.
* `NOTIFY_SENDER` - > valeur par défaut : `True` + > default value: `True` >> En mode non authentifié, lors de l'utilisation du formulaire de contact, envoie une copie du message à l'adresse saisie dans le formulaire.
* `SERVER_EMAIL` - > valeur par défaut : `noreply` + > default value: `noreply` >> Expediteur par défaut pour les envois automatique (erreur de code etc.)
* `SUBJECT_CHOICES` - > valeur par défaut : `()` + > default value: `()` >> Choix de sujet pour les courriels envoyés depuis la plateforme
>> >> ```python @@ -183,11 +185,11 @@ Voici les configurations des applications tierces utilisées par Esup-Pod.
>> ``` >> * `SUPPORT_EMAIL` - > valeur par défaut : `None` + > default value: `None` >> Liste de destinataire(s) pour les demandes d’assistance, si différent de `CONTACT_US_EMAIL`
>> i.e.: `SUPPORT_EMAIL = ["assistance_pod@univ.fr"]`
* `USER_CONTACT_EMAIL_CASE` - > valeur par défaut : `` + > default value: `` >> Une liste contenant les sujets de contact dont l’utilisateur
>> sera seul destinataire plutôt que le(s) manager(s).
>> Si la liste est vide, les mails de contact seront envoyés au(x) manager(s).
@@ -195,7 +197,7 @@ Voici les configurations des applications tierces utilisées par Esup-Pod.
>> `info`, `contribute`, `request_password`,
>> `inapropriate_content`, `bug`, `other`
* `USE_ESTABLISHMENT_FIELD` - > valeur par défaut : `False` + > default value: `False` >> Si valeur vaut 'True', rajoute un attribut 'establishment'
>> à l’utilisateur Pod, ce qui permet de gérer plus d’un établissement.
>> Dans ce cas, les emails de contact par exemple seront envoyés
@@ -209,140 +211,140 @@ Voici les configurations des applications tierces utilisées par Esup-Pod.
### Encodage * `FFMPEG_AUDIO_BITRATE` - > valeur par défaut : `192k` + > default value: `192k` >> * `FFMPEG_CMD` - > valeur par défaut : `ffmpeg` + > default value: `ffmpeg` >> * `FFMPEG_CREATE_THUMBNAIL` - > valeur par défaut : `-vf "fps=1/(%(duration)s/%(nb_thumbnail)s)" -vsync vfr "%(output)s_%%04d.png"` + > default value: `-vf "fps=1/(%(duration)s/%(nb_thumbnail)s)" -vsync vfr "%(output)s_%%04d.png"` >> * `FFMPEG_CRF` - > valeur par défaut : `20` + > default value: `20` >> * `FFMPEG_EXTRACT_SUBTITLE` - > valeur par défaut : `-map 0:%(index)s -f webvtt -y "%(output)s"` + > default value: `-map 0:%(index)s -f webvtt -y "%(output)s"` >> * `FFMPEG_EXTRACT_THUMBNAIL` - > valeur par défaut : `-map 0:%(index)s -an -c:v copy -y "%(output)s"` + > default value: `-map 0:%(index)s -an -c:v copy -y "%(output)s"` >> * `FFMPEG_HLS_COMMON_PARAMS` - > valeur par défaut : `-c:v %(libx)s -preset %(preset)s -profile:v %(profile)s -pix_fmt yuv420p -level %(level)s -crf %(crf)s -sc_threshold 0 -force_key_frames "expr:gte(t,n_forced*1)" -c:a aac -ar 48000 -max_muxing_queue_size 4000` + > default value: `-c:v %(libx)s -preset %(preset)s -profile:v %(profile)s -pix_fmt yuv420p -level %(level)s -crf %(crf)s -sc_threshold 0 -force_key_frames "expr:gte(t,n_forced*1)" -c:a aac -ar 48000 -max_muxing_queue_size 4000` >> * `FFMPEG_HLS_ENCODE_PARAMS` - > valeur par défaut : `-vf "scale=-2:%(height)s" -maxrate %(maxrate)s -bufsize %(bufsize)s -b:a:0 %(ba)s -hls_playlist_type vod -hls_time %(hls_time)s -hls_flags single_file -master_pl_name "livestream%(height)s.m3u8" -y "%(output)s"` + > default value: `-vf "scale=-2:%(height)s" -maxrate %(maxrate)s -bufsize %(bufsize)s -b:a:0 %(ba)s -hls_playlist_type vod -hls_time %(hls_time)s -hls_flags single_file -master_pl_name "livestream%(height)s.m3u8" -y "%(output)s"` >> * `FFMPEG_HLS_TIME` - > valeur par défaut : `2` + > default value: `2` >> * `FFMPEG_INPUT` - > valeur par défaut : `-hide_banner -threads %(nb_threads)s -i "%(input)s"` + > default value: `-hide_banner -threads %(nb_threads)s -i "%(input)s"` >> * `FFMPEG_LEVEL` - > valeur par défaut : `3` + > default value: `3` >> * `FFMPEG_LIBX` - > valeur par défaut : `libx264` + > default value: `libx264` >> * `FFMPEG_M4A_ENCODE` - > valeur par défaut : `-vn -c:a aac -b:a %(audio_bitrate)s "%(output)s"` + > default value: `-vn -c:a aac -b:a %(audio_bitrate)s "%(output)s"` >> * `FFMPEG_MP3_ENCODE` - > valeur par défaut : `-vn -codec:a libmp3lame -qscale:a 2 -y "%(output)s"` + > default value: `-vn -codec:a libmp3lame -qscale:a 2 -y "%(output)s"` >> * `FFMPEG_MP4_ENCODE` - > valeur par défaut : `-map 0:v:0 %(map_audio)s -c:v %(libx)s -vf "scale=-2:%(height)s" -preset %(preset)s -profile:v %(profile)s -pix_fmt yuv420p -level %(level)s -crf %(crf)s -maxrate %(maxrate)s -bufsize %(bufsize)s -sc_threshold 0 -force_key_frames "expr:gte(t,n_forced*1)" -max_muxing_queue_size 4000 -c:a aac -ar 48000 -b:a %(ba)s -movflags faststart -y -vsync 0 "%(output)s"` + > default value: `-map 0:v:0 %(map_audio)s -c:v %(libx)s -vf "scale=-2:%(height)s" -preset %(preset)s -profile:v %(profile)s -pix_fmt yuv420p -level %(level)s -crf %(crf)s -maxrate %(maxrate)s -bufsize %(bufsize)s -sc_threshold 0 -force_key_frames "expr:gte(t,n_forced*1)" -max_muxing_queue_size 4000 -c:a aac -ar 48000 -b:a %(ba)s -movflags faststart -y -vsync 0 "%(output)s"` >> * `FFMPEG_NB_THREADS` - > valeur par défaut : `0` + > default value: `0` >> * `FFMPEG_NB_THUMBNAIL` - > valeur par défaut : `3` + > default value: `3` >> * `FFMPEG_PRESET` - > valeur par défaut : `slow` + > default value: `slow` >> * `FFMPEG_PROFILE` - > valeur par défaut : `high` + > default value: `high` >> * `FFMPEG_STUDIO_COMMAND` - > valeur par défaut : `-hide_banner -threads %(nb_threads)s %(input)s %(subtime)s -c:a aac -ar 48000 -c:v h264 -profile:v high -pix_fmt yuv420p -crf %(crf)s -sc_threshold 0 -force_key_frames "expr:gte(t,n_forced*1)" -max_muxing_queue_size 4000 -deinterlace` + > default value: `-hide_banner -threads %(nb_threads)s %(input)s %(subtime)s -c:a aac -ar 48000 -c:v h264 -profile:v high -pix_fmt yuv420p -crf %(crf)s -sc_threshold 0 -force_key_frames "expr:gte(t,n_forced*1)" -max_muxing_queue_size 4000 -deinterlace` >> * `FFPROBE_CMD` - > valeur par défaut : `ffprobe` + > default value: `ffprobe` >> * `FFPROBE_GET_INFO` - > valeur par défaut : `%(ffprobe)s -v quiet -show_format -show_streams %(select_streams)s -print_format json -i %(source)s` + > default value: `%(ffprobe)s -v quiet -show_format -show_streams %(select_streams)s -print_format json -i %(source)s` >> * `FFMPEG_DRESSING_OUTPUT` - > valeur par défaut : ` -c:v libx264 -y -vsync 0 "%(output)s" ` + > default value: ` -c:v libx264 -y -vsync 0 "%(output)s" ` >> Spécifie les paramètres d'encodage de sortie FFmpeg pour générer le fichier vidéo temporaire d'habillage, utilisant le codec H.264 avec écrasement forcé et synchronisation de la sortie vidéo.
* `FFMPEG_DRESSING_INPUT` - > valeur par défaut : ` -i "%(input)s" ` + > default value: ` -i "%(input)s" ` >> Définit le fichier d'entrée pour le traitement FFmpeg de la vidéo intermédiaire d'habillage.
* `FFMPEG_DRESSING_FILTER_COMPLEX` - > valeur par défaut : ` -filter_complex "%(filter)s" ` + > default value: ` -filter_complex "%(filter)s" ` >> Applique des chaînes de filtres complexes à la vidéo intermédiaire d'habillage avec FFmpeg.
* `FFMPEG_DRESSING_WATERMARK` - > valeur par défaut : ` [1]format=rgba,colorchannelmixer=aa=%(opacity)s[logo]; [logo][vid]scale2ref=oh*mdar:ih*0.1[logo][video2]; [video2][logo]%(position)s%(name_out)s ` + > default value: ` [1]format=rgba,colorchannelmixer=aa=%(opacity)s[logo]; [logo][vid]scale2ref=oh*mdar:ih*0.1[logo][video2]; [video2][logo]%(position)s%(name_out)s ` >> Ajoute un filigrane à la vidéo intermédiaire d'habillage avec une opacité et une position personnalisables.
* `FFMPEG_DRESSING_SCALE` - > valeur par défaut : `[%(number)s]scale=w='if(gt(a,16/9),16/9*%(height)s,-2)':h='if(gt(a,16/9),-2,%(height)s)',pad=ceil(16/9*%(height)s):%(height)s:(ow-iw)/2:(oh-ih)/2[%(name)s]` + > default value: `[%(number)s]scale=w='if(gt(a,16/9),16/9*%(height)s,-2)':h='if(gt(a,16/9),-2,%(height)s)',pad=ceil(16/9*%(height)s):%(height)s:(ow-iw)/2:(oh-ih)/2[%(name)s]` >> Redimensionne la vidéo intermédiaire d'habillage pour maintenir un ratio d'aspect 16:9 avec ajout de bordures si nécessaire.
* `FFMPEG_DRESSING_CONCAT` - > valeur par défaut : `%(params)sconcat=n=%(number)s:v=1:a=1:unsafe=1[v][a]` + > default value: `%(params)sconcat=n=%(number)s:v=1:a=1:unsafe=1[v][a]` >> Concatène plusieurs flux vidéo et audio en une seule sortie de vidéo temporaire d'habillage.
* `FFMPEG_DRESSING_SILENT` - > valeur par défaut : ` -f lavfi -t %(duration)s -i anullsrc=r=44100:cl=stereo` + > default value: ` -f lavfi -t %(duration)s -i anullsrc=r=44100:cl=stereo` >> Génère un audio silencieux d'une durée spécifiée pour la vidéo temporaire d'habillage.
* `FFMPEG_DRESSING_AUDIO` - > valeur par défaut : `[%(param_in)s]anull[%(param_out)s]` + > default value: `[%(param_in)s]anull[%(param_out)s]` >> Traite l'audio sans modifications pour l'inclure dans la vidéo temporaire d'habillage.
### Gestion des fichiers * `FILES_DIR` - > valeur par défaut : `files` + > default value: `files` >> Nom du répertoire racine où les fichiers "complémentaires"
>> (hors vidéos etc.) sont téléversés. Notament utilisé par PODFILE
>> À modifier principalement pour indiquer dans LOCATION
>> votre serveur de cache si elle n’est pas sur la même machine que votre POD.
* `FILE_UPLOAD_TEMP_DIR` - > valeur par défaut : `/var/tmp` + > default value: `/var/tmp` >> Le répertoire dans lequel stocker temporairement les données
>> (typiquement pour les fichiers plus grands que `FILE_UPLOAD_MAX_MEMORY_SIZE`)
>> lors des téléversements de fichiers.
>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#file-upload-temp-dir)_
* `MEDIA_ROOT` - > valeur par défaut : `/pod/media` + > default value: `/pod/media` >> Chemin absolu du système de fichiers pointant vers le répertoire qui contiendra
>> les fichiers téléversés par les utilisateurs.

>> Attention, ce répertoire doit exister.

>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#std:setting-MEDIA_ROOT)_
* `MEDIA_URL` - > valeur par défaut : `/media/` + > default value: `/media/` >> prefix url utilisé pour accéder aux fichiers du répertoire media
* `STATICFILES_STORAGE` - > valeur par défaut : `` + > default value: `` >> Indique à django de compresser automatiquement les fichiers css/js
>> les plus gros lors du collectstatic pour optimiser les tailles de requetes.

>> À combiner avec un réglage webserver (`gzip_static on;` sur nginx)

>> _ref : [whs/django-static-compress](https://github.com/whs/django-static-compress)
* `STATIC_ROOT` - > valeur par défaut : `/pod/static` + > default value: `/pod/static` >> Le chemin absolu vers le répertoire dans lequel collectstatic rassemble
>> les fichiers statiques en vue du déploiement.
>> Ce chemin sera précisé dans le fichier de configurtation du vhost nginx.

>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#std:setting-STATIC_ROOT)_
* `STATIC_URL` - > valeur par défaut : `/static/` + > default value: `/static/` >> prefix url utilisé pour accèder aux fichiers static
* `USE_PODFILE` - > valeur par défaut : `False` + > default value: `False` >> Utiliser l’application de gestion de fichier fourni avec le projet.
>> Si False, chaque fichier envoyé ne pourra être utilisé qu’une seule fois.
* `VIDEOS_DIR` - > valeur par défaut : `videos` + > default value: `videos` >> Répertoire par défaut pour le téléversement des vidéos.
### Langue @@ -352,16 +354,16 @@ Vous pouvez tout à fait rajouter des langues comme vous le souhaitez.
Il faudra pour cela créer un fichier de langue et traduire chaque entrée.
* `LANGUAGES` - > valeur par défaut : `(('fr', 'Français'), ('en', 'English')))` + > default value: `(('fr', 'Français'), ('en', 'English')))` >> Langue disponible et traduite
* `LANGUAGE_CODE` - > valeur par défaut : `fr` + > default value: `fr` >> Langue par défaut si non détectée
### Divers * `ADMINS` - > valeur par défaut : `[("Name", "adminmail@univ.fr"),]` + > default value: `[("Name", "adminmail@univ.fr"),]` >> Une liste de toutes les personnes qui reçoivent les notifications d’erreurs dans le code.

>> Lorsque DEBUG=False et qu’une vue lève une exception,
>> Django envoie un courriel à ces personnes contenant les informations complètes de l’exception.

@@ -372,16 +374,16 @@ Il faudra pour cela créer un fichier de langue et traduire chaque entrée.
>> d’encodage ou de flux RSS si la variable `CONTACT_US_EMAIL` n’est pas renseignée.

>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#admins)_
* `ALLOWED_HOSTS` - > valeur par défaut : `['pod.localhost']` + > default value: `['pod.localhost']` >> Une liste de chaînes représentant des noms de domaine/d’hôte que ce site Django peut servir.

>> C’est une mesure de sécurité pour empêcher les attaques d’en-tête Host HTTP,
>> qui sont possibles même avec bien des configurations de serveur Web apparemment sécurisées.

>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#allowed-hosts)_
* `BASE_DIR` - > valeur par défaut : `os.path.dirname(os.path.dirname(os.path.abspath(__file__)))` + > default value: `os.path.dirname(os.path.dirname(os.path.abspath(__file__)))` >> répertoire de base
* `CACHES` - > valeur par défaut : `{}` + > default value: `{}` >> >> ```python >> CACHES = { @@ -408,28 +410,28 @@ Il faudra pour cela créer un fichier de langue et traduire chaque entrée.
>> ``` >> * `CSRF_COOKIE_SECURE` - > valeur par défaut : `not DEBUG` + > default value: `not DEBUG` >> Ces 3 variables servent à sécuriser la plateforme en passant
>> l’ensemble des requetes en https.
>> Idem pour les cookies de session et de cross-sites qui seront également sécurisés

>> Il faut les passer à False en cas d’usage du runserver (phase de développement / debugage)

>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#secure-ssl-redirect)_
* `DEBUG` - > valeur par défaut : `True` + > default value: `True` >> Une valeur booléenne qui active ou désactive le mode de débogage.

>> Ne déployez jamais de site en production avec le réglage DEBUG activé.

>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#debug)_
* `USE_DEBUG_TOOLBAR` - > valeur par défaut : `True` + > default value: `True` >> Une valeur booléenne qui active ou désactive l’outil de débogage.

>> Ne déployez jamais de site en production avec le réglage USE_DEBUG_TOOLBAR activé.

>> _ref : [django-debug-toolbar.readthedocs.io](https://django-debug-toolbar.readthedocs.io/en/latest/)_
* `LOGIN_URL` - > valeur par défaut : `/authentication_login/` + > default value: `/authentication_login/` >> url de redirection pour l’authentification de l’utilisateur
>> voir : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#login-url)
* `MANAGERS` - > valeur par défaut : `[]` + > default value: `[]` >> Dans Pod, les "managers" sont destinataires des courriels de fin d’encodage
>> (et ainsi des vidéos déposées sur la plateforme).

>> Le premier manager renseigné est également contact des flus RSS.

@@ -437,49 +439,49 @@ Il faudra pour cela créer un fichier de langue et traduire chaque entrée.
>> si la variable `CONTACT_US_EMAIL` n’est pas renseignée.

>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#managers)_
* `PROXY_HOST` - > valeur par défaut : `` + > default value: `` >> Utilisation du proxy - host
* `PROXY_PORT` - > valeur par défaut : `` + > default value: `` >> Utilisation du proxy - port
* `SECRET_KEY` - > valeur par défaut : `A_CHANGER` + > default value: `A_CHANGER` >> La clé secrète d’une installation Django.

>> Elle est utilisée dans le contexte de la signature cryptographique,
>> et doit être définie à une valeur unique et non prédictible.

>> Vous pouvez utiliser ce site pour en générer une : [djecrety.ir](https://djecrety.ir/)

>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#secret-key)_
* `SECURE_SSL_REDIRECT` - > valeur par défaut : `False` + > default value: `False` >> À moins que votre site ne doive être disponible sur des connexions SSL et non SSL,
>> vous souhaiterez probablement définir ce paramètre sur True ou configurer un
>> load balancer ou reverse-proxy pour rediriger toutes les connexions vers HTTPS.
* `SESSION_COOKIE_AGE` - > valeur par défaut : `14400` + > default value: `14400` >> L’âge des cookies de sessions, en secondes (4h par défaut).
* `SESSION_COOKIE_SAMESITE` - > valeur par défaut : `Lax` + > default value: `Lax` >> Cette option empêche le cookie d’être envoyé dans les requêtes inter-sites,
>> ce qui prévient les attaques CSRF et rend impossible
>> certaines méthodes de vol du cookie de session.
>> Voir [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#std-setting-SESSION_COOKIE_SAMESITE)
* `SESSION_COOKIE_SECURE` - > valeur par défaut : `not DEBUG` + > default value: `not DEBUG` >> * `SESSION_EXPIRE_AT_BROWSER_CLOSE` - > valeur par défaut : `True` + > default value: `True` >> Indique s’il faut que la session expire lorsque l’utilisateur ferme son navigateur.
* `SITE_ID` - > valeur par défaut : `1` + > default value: `1` >> L’identifiant (nombre entier) du site actuel.
>> Peut être utilisé pour mettre en place une instance multi-tenant
>> et ainsi gérer dans une même base de données du contenu pour plusieurs sites.

>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#site-id)_
* `TEST_SETTINGS` - > valeur par défaut : `False` + > default value: `False` >> Permet de vérifier si la configuration de la plateforme est en mode test.
* `THIRD_PARTY_APPS` - > valeur par défaut : `[]` + > default value: `[]` >> Liste des applications tierces accessibles.
>> >> ```python @@ -487,7 +489,7 @@ Il faudra pour cela créer un fichier de langue et traduire chaque entrée.
>> ``` >> * `TIME_ZONE` - > valeur par défaut : `UTC` + > default value: `UTC` >> Une chaîne représentant le fuseau horaire pour cette installation.

>> _ref : [docs.djangoproject.com](https://docs.djangoproject.com/fr/4.2/ref/settings/#std:setting-TIME_ZONE)_
>> Liste des adresses destinataires des courriels de contact
@@ -495,7 +497,7 @@ Il faudra pour cela créer un fichier de langue et traduire chaque entrée.
### Obsolescence * `ACCOMMODATION_YEARS` - > valeur par défaut : `{}` + > default value: `{}` >> Durée d’obsolescence personnalisée par Affiliation
>> >> ```python @@ -505,13 +507,13 @@ Il faudra pour cela créer un fichier de langue et traduire chaque entrée.
>> ``` >> * `ARCHIVE_OWNER_USERNAME` - > valeur par défaut : `"archive"` + > default value: `"archive"` >> Nom de l’utilisateur pour l’archivage des vidéos.
* `ARCHIVE_HOW_MANY_DAYS` - > valeur par défaut : `365` + > default value: `365` >> Délai avant qu'une vidéo archivée ne soit déplacée vers archive_ROOT.
* `POD_ARCHIVE_AFFILIATION` - > valeur par défaut : `[]` + > default value: `[]` >> Affiliations pour lesquelles on souhaite archiver la vidéo plutôt que de la supprimer.
>> Si l’affiliation du propriétaire est dans cette variable,
>> alors les vidéos sont affectées à un utilisateur précis
@@ -536,7 +538,7 @@ Il faudra pour cela créer un fichier de langue et traduire chaque entrée.
>> ``` >> * `WARN_DEADLINES` - > valeur par défaut : `[60, 30, 7]` + > default value: `[60, 30, 7]` >> Liste de jours de délais avant l’obsolescence de la vidéo.
>> À chaque délai, le propriétaire reçoit un mail d’avertissement
>> pour éventuellement changer la date d’obsolescence de sa vidéo.
@@ -544,86 +546,86 @@ Il faudra pour cela créer un fichier de langue et traduire chaque entrée.
### Modèle * `COOKIE_LEARN_MORE` - > valeur par défaut : `` + > default value: `` >> Ce paramètre permet d’afficher un lien "En savoir plus"
>> sur la boite de dialogue d’information sur l’usage des cookies dans Pod.
>> On peut préciser un lien vers les mentions légales ou page DPO.
* `DARKMODE_ENABLED` - > valeur par défaut : `True` + > default value: `True` >> Permet aux utilisateurs d’activer un mode sombre.
* `DYSLEXIAMODE_ENABLED` - > valeur par défaut : `True` + > default value: `True` >> Permet d’utiliser une police de caractères plus adaptée
>> aux personnes atteintes de dyslexie.
* `HIDE_CHANNEL_TAB` - > valeur par défaut : `False` + > default value: `False` >> Si True, permet de cacher l’onglet chaine dans la barre de menu du haut.
* `HIDE_CURSUS` - > valeur par défaut : `False` + > default value: `False` >> Si True, permet de ne pas afficher les cursus dans la colonne de droite.
* `HIDE_DISCIPLINES` - > valeur par défaut : `False` + > default value: `False` >> Si True, permet de ne pas afficher les disciplines dans la colonne de droite.
* `HIDE_LANGUAGE_SELECTOR` - > valeur par défaut : `False` + > default value: `False` >> Si True, permet de cacher le sélecteur de langue dans le menu du haut.
* `HIDE_SHARE` - > valeur par défaut : `False` + > default value: `False` >> Si True, permet de ne pas afficher les liens de partage
>> sur les réseaux sociaux dans la colonne de droite.
* `HIDE_TAGS` - > valeur par défaut : `False` + > default value: `False` >> Si True, permet de ne pas afficher le nuage de mots clés dans la colonne de droite.
* `HIDE_TYPES` - > valeur par défaut : `False` + > default value: `False` >> Si True, permet de ne pas afficher la liste des types dans la colonne de droite.
* `HIDE_TYPES_TAB` - > valeur par défaut : `False` + > default value: `False` >> Si True, permet de cacher l’entrée 'type' dans le menu de navigation.
* `HIDE_USERNAME` - > valeur par défaut : `False` + > default value: `False` >> Voir description dans authentification
>> Si valeur vaut 'True', le username de l’utilisateur ne sera pas visible et
>> si la valeur vaut 'False' le username sera affiché aux utilisateurs authentifiés.
>> (pour respecter le RGPD)
* `HIDE_USER_FILTER` - > valeur par défaut : `False` + > default value: `False` >> Si 'True', le filtre des vidéos par utilisateur ne sera plus visible
>> si 'False' le filtre ne sera visible qu’aux personnes authentifiées.
>> (pour respecter le RGPD)
* `HIDE_USER_TAB` - > valeur par défaut : `False` + > default value: `False` >> Si valeur vaut 'True', l’onglet Utilisateur ne sera pas visible
>> et si la valeur vaut 'False' l’onglet Utilisateur ne sera visible
>> qu’aux personnes authentifiées.
>> (pour respecter le RGPD)
* `HOMEPAGE_NB_VIDEOS` - > valeur par défaut : `12` + > default value: `12` >> Nombre de vidéos à afficher sur la page d’accueil.
* `HOMEPAGE_SHOWS_PASSWORDED` - > valeur par défaut : `False` + > default value: `False` >> Afficher les vidéos dont l’accès est protégé par mot de passe sur la page d’accueil.
* `HOMEPAGE_SHOWS_RESTRICTED` - > valeur par défaut : `False` + > default value: `False` >> Afficher les vidéos dont l’accès est protégé par authentification sur la page d’accueil.
* `MENUBAR_HIDE_INACTIVE_OWNERS` - > valeur par défaut : `True` + > default value: `True` >> Les utilisateurs inactifs ne sont plus affichés dans la barre de menu utilisateur.
* `MENUBAR_SHOW_STAFF_OWNERS_ONLY` - > valeur par défaut : `False` + > default value: `False` >> Les utilisateurs non staff ne sont plus affichés dans la barre de menu utilisateur.
* `SHIB_NAME` - > valeur par défaut : `Identify Federation` + > default value: `Identify Federation` >> Nom de la fédération d’identité utilisée
>> Affiché sur le bouton de connexion si l’authentification Shibboleth est utilisée.
* `SHOW_EVENTS_ON_HOMEPAGE` - > valeur par défaut : `False` + > default value: `False` >> Si True, affiche les prochains évènements sur la page d’accueil.
* `SHOW_ONLY_PARENT_THEMES` - > valeur par défaut : `False` + > default value: `False` >> Si True, affiche uniquement les thèmes de premier niveau dans l’onglet 'Chaîne'.
* `TEMPLATE_VISIBLE_SETTINGS` - > valeur par défaut : `{}` + > default value: `{}` >> >> ```python >> TEMPLATE_VISIBLE_SETTINGS = { @@ -696,10 +698,10 @@ Il faudra pour cela créer un fichier de langue et traduire chaque entrée.
### Transcodage * `TRANSCRIPTION_AUDIO_SPLIT_TIME` - > valeur par défaut : `600` + > default value: `600` >> Découpage de l’audio pour la transcription.
* `TRANSCRIPTION_MODEL_PARAM` - > valeur par défaut : `` + > default value: `` >> Paramétrage des modèles pour la transcription
>> Voir la documentation à cette adresse :
>> [esupportail.github.io](https://esupportail.github.io/Esup-Pod/4.x/Installation/optional/auto-transcription-install_fr)
@@ -728,26 +730,26 @@ Il faudra pour cela créer un fichier de langue et traduire chaque entrée.
>> ``` >> * `TRANSCRIPTION_NORMALIZE` - > valeur par défaut : `False` + > default value: `False` >> Activation de la normalisation de l’audio avant sa transcription.
* `TRANSCRIPTION_NORMALIZE_TARGET_LEVEL` - > valeur par défaut : `-16.0` + > default value: `-16.0` >> Niveau de normalisation de l’audio avant sa transcription.
* `TRANSCRIPTION_STT_SENTENCE_BLANK_SPLIT_TIME` - > valeur par défaut : `0.5` + > default value: `0.5` >> Temps maximum en secondes des blancs entre chaque mot
>> pour le decoupage des sous-titres avec l’outil STT.
* `TRANSCRIPTION_STT_SENTENCE_MAX_LENGTH` - > valeur par défaut : `2` + > default value: `2` >> Temps en secondes maximum pour une phrase lors de la transcription avec l’outil STT.
* `TRANSCRIPTION_TYPE` - > valeur par défaut : `WHISPER` + > default value: `WHISPER` >> Choix de l’outil pour la transcription : `VOSK`ou `WHISPER`.
* `TRANSCRIPT_VIDEO` - > valeur par défaut : `start_transcript` + > default value: `start_transcript` >> Fonction appelée pour lancer la transcription des vidéos.
* `USE_TRANSCRIPTION` - > valeur par défaut : `False` + > default value: `False` >> Activation de la transcription.
## Configuration des applications Esup_Pod @@ -758,137 +760,137 @@ Application AI Enhancement pour pouvoir utiliser les améliorations des vidéos Mettre `USE_AI_ENHANCEMENT` à True pour activer cette application.
* `AI_ENHANCEMENT_API_URL` - > valeur par défaut : `` + > default value: `` >> L’URL de l’API pour l’IA d’amélioration des vidéos.
>> Exemple : ''
>> Lien du projet :
* `AI_ENHANCEMENT_API_VERSION` - > valeur par défaut : `` + > default value: `` >> La version de l’API pour l’IA d’amélioration des vidéos.
* `AI_ENHANCEMENT_CGU_URL` - > valeur par défaut : `` + > default value: `` >> L’URL des conditions générales d’utilisation de l’API pour l’IA d’amélioration des vidéos.
>> Exemple : ''
>> Lien du projet :
* `AI_ENHANCEMENT_CLIENT_ID` - > valeur par défaut : `mocked_id` + > default value: `mocked_id` >> L’ID du client de l’IA d’amélioration des vidéos.
>> Exemple : 'v1'
* `AI_ENHANCEMENT_CLIENT_SECRET` - > valeur par défaut : `mocked_secret` + > default value: `mocked_secret` >> Le mot de passe secret du client de l’IA d’amélioration des vidéos.
* `AI_ENHANCEMENT_FIELDS_HELP_TEXT` - > valeur par défaut : `` + > default value: `` >> Ensemble des textes d’aide affichés avec le formulaire d'amélioration d'une vidéo avec l'IA d'Aristote.
* `USE_AI_ENHANCEMENT` - > valeur par défaut : `False` + > default value: `False` >> Activation des améliorations de l'intelligence artificielle. Permet aux utilisateurs de l'utiliser.
* `AI_ENHANCEMENT_PROXY_URL` - > valeur par défaut : `` + > default value: `` >> L’URL du serveur proxy pour les requêtes venant d'Aristote.
>> Exemple : ''
### Configuration de l’application authentification * `AFFILIATION` - > valeur par défaut : `` + > default value: `` >> Valeurs possibles pour l’affiliation du compte.
* `AFFILIATION_EVENT` - > valeur par défaut : `` + > default value: `` >> Groupes ou affiliations des personnes autorisées à créer un évènement.
* `AFFILIATION_STAFF` - > valeur par défaut : `` + > default value: `` >> Les personnes ayant pour affiliation les valeurs
>> renseignées dans cette variable ont automatiquement
>> la valeur staff de leur compte à True.
* `ALLOWED_SUPERUSER_IPS` - > valeur par défaut : `[]` + > default value: `[]` >> Liste d’IP et/ou de plages depuis lesquelles le statut 'superuser'
>> est autorisé.
>> Laissez vide pour autoriser toutes les sources.
* `AUTH_CAS_USER_SEARCH` - > valeur par défaut : `user` + > default value: `user` >> Variable utilisée pour trouver les informations de l’individu
>> connecté dans le fichier renvoyé par le CAS lors de l’authentification.
* `AUTH_LDAP_BIND_DN` - > valeur par défaut : `` + > default value: `` >> Identifiant (DN) du compte pour se connecter au serveur LDAP.
* `AUTH_LDAP_BIND_PASSWORD` - > valeur par défaut : `` + > default value: `` >> Mot de passe du compte pour se connecter au serveur LDAP.
* `AUTH_LDAP_USER_SEARCH` - > valeur par défaut : `` + > default value: `` >> Filtre LDAP permettant la recherche de l’individu dans le serveur LDAP.
* `AUTH_TYPE` - > valeur par défaut : `` + > default value: `` >> Type d’authentification possible sur votre instance.
>> Choix : local, CAS, OIDC, Shibboleth
* `CAS_ADMIN_AUTH` - > valeur par défaut : `False` + > default value: `False` >> Permet d’activer l’authentification CAS pour la partie admin
>> Voir : [pypi.org/project/django-cas-sso](https://pypi.org/project/django-cas-sso/)
* `CAS_FORCE_LOWERCASE_USERNAME` - > valeur par défaut : `False` + > default value: `False` >> Forcer le passage en minuscule du nom d’utilisateur CAS
>> (permet de prévenir des doubles créations de comptes dans certains cas).
>> OBSOLÈTE à partir de Pod 4.0. Utilisez `CAS_FORCE_CHANGE_USERNAME_CASE`
* `CAS_FORCE_CHANGE_USERNAME_CASE` - > valeur par défaut : `False` + > default value: `False` >> Forcer la casse (minuscules ou majuscules) du nom d’utilisateur CAS
>> (permet de prévenir des doubles créations de comptes dans certains cas).
>> Valeurs possibles : `lower`, `upper`, `False`.
* `CAS_GATEWAY` - > valeur par défaut : `False` + > default value: `False` >> Si True, authentifie automatiquement l’individu
>> si déjà authentifié sur le serveur CAS
>> OBSOLÈTE à partir de Pod 4.0
* `CAS_LOGOUT_COMPLETELY` - > valeur par défaut : `True` + > default value: `True` >> Voir [kstateome/django-cas](https://github.com/kstateome/django-cas)
* `CAS_SERVER_URL` - > valeur par défaut : `sso_cas` + > default value: `sso_cas` >> Url du serveur CAS de l’établissement. Format `http://url_cas`
* `CAS_MAP_AFFILIATIONS` - > valeur par défaut : `False` + > default value: `False` >> Si True, des `groupes` d’utilisateurs sont créés automatiquement
>> à partir des affiliations CAS des individus qui se connectent sur la plateforme
>> et l’individu qui se connecte est ajouté automatiquement à ces groupes.
* `CREATE_GROUP_FROM_AFFILIATION` - > valeur par défaut : `False` + > default value: `False` >> Si True, des `groupes d’accès` sont créés automatiquement
>> à partir des affiliations des individus qui se connectent sur la plateforme
>> et l’individu qui se connecte est ajouté automatiquement à ces groupes.
* `CREATE_GROUP_FROM_GROUPS` - > valeur par défaut : `False` + > default value: `False` >> Si True, des groupes sont créés automatiquement
>> à partir des groupes (attribut groups à memberOf)
>> des individus qui se connectent sur la plateforme
>> et l’individu qui se connecte est ajouté automatiquement à ces groupes
* `DEFAULT_AFFILIATION` - > valeur par défaut : `` + > default value: `` >> Affiliation par défaut d’un utilisateur authentifié par OIDC.
>> Ce contenu sera comparé à la liste AFFILIATION_STAFF
>> pour déterminer si l’utilisateur doit être admin Django
* `ESTABLISHMENTS` - > valeur par défaut : `` + > default value: `` >> [TODO] À compléter
* `GROUP_STAFF` - > valeur par défaut : `AFFILIATION_STAFF` + > default value: `AFFILIATION_STAFF` >> utilisé dans populatedCasbackend
* `HIDE_LOCAL_LOGIN` - > valeur par défaut : `False` + > default value: `False` >> Si True, masque l’authentification locale
* `HIDE_USERNAME` - > valeur par défaut : `False` + > default value: `False` >> Si valeur vaut `True`, le username de l’utilisateur
>> ne sera pas visible sur la plate-forme Pod
>> et si la valeur vaut `False` le username sera affiché aux utilisateurs authentifiés.
>> (pour respecter le RGPD)
* `LDAP` - > valeur par défaut : `` + > default value: `` >> Interroge le serveur LDAP pour renseigner les champs.
* `LDAP_SERVER` - > valeur par défaut : `` + > default value: `` >> Information de connection au serveur LDAP.
>> Le champ url peut contenir une ou plusieurs url
>> pour ajouter des hôtes de référence, exemple :
@@ -897,94 +899,94 @@ Mettre `USE_AI_ENHANCEMENT` à True pour activer cette application.
>> Si plusieurs :
>> `{'url': ("ldap.univ.fr'',"ldap2.univ.fr"), 'port': 389, 'use_ssl': False}`
* `OIDC_CLAIM_FAMILY_NAME` - > valeur par défaut : `family_name` + > default value: `family_name` >> * `OIDC_CLAIM_PREFERRED_USERNAME` - > valeur par défaut : `preferred_username` + > default value: `preferred_username` >> Noms des Claim permettant de récupérer
>> l’attribut login mais dépendant de l’attribut du client dans l’IDP.
* `OIDC_CLAIM_GIVEN_NAME` - > valeur par défaut : `given_name` + > default value: `given_name` >> Noms des Claim permettant de récupérer les attributs nom, prénom, email
* `OIDC_DEFAULT_ACCESS_GROUP_CODE_NAMES` - > valeur par défaut : `[]` + > default value: `[]` >> Groupes d’accès attribués par défaut à un nouvel utilisateur authentifié par OIDC
* `OIDC_DEFAULT_AFFILIATION` - > valeur par défaut : `` + > default value: `` >> Affiliation par défaut d’un utilisateur authentifié par OIDC.
>> Ce contenu sera comparé à la liste AFFILIATION_STAFF
>> pour déterminer si l’utilisateur doit être admin Django.
* `OIDC_NAME` - > valeur par défaut : `` + > default value: `` >> Nom du Service Provider OIDC
* `OIDC_OP_AUTHORIZATION_ENDPOINT` - > valeur par défaut : `https` + > default value: `https` >> * `OIDC_OP_JWKS_ENDPOINT` - > valeur par défaut : `https` + > default value: `https` >> Différents paramètres pour OIDC
>> tant que `mozilla_django_oidc` n’accepte pas le mécanisme de discovery
>> _ref : [mozilla/mozilla-django-oidc](https://github.com/mozilla/mozilla-django-oidc/pull/309)_
* `OIDC_OP_TOKEN_ENDPOINT` - > valeur par défaut : `https` + > default value: `https` >> * `OIDC_OP_USER_ENDPOINT` - > valeur par défaut : `https` + > default value: `https` >> * `OIDC_RP_CLIENT_ID` - > valeur par défaut : `os.environ` + > default value: `os.environ` >> * `OIDC_RP_CLIENT_SECRET` - > valeur par défaut : `os.environ` + > default value: `os.environ` >> `CLIENT_ID` et `CLIENT_SECRET` de OIDC sont plutôt à positionner
>> à travers des variables d’environnement.
* `OIDC_RP_SIGN_ALGO` - > valeur par défaut : `` + > default value: `` >> * `POPULATE_USER` - > valeur par défaut : `None` + > default value: `None` >> Si utilisation de la connection CAS, renseigne les champs du compte
>> de la personne depuis une source externe.
>> Valeurs possibles :
>> * None (pas de renseignement),
>> * CAS (renseigne les champs depuis les informations renvoyées par le CAS),
* `REMOTE_USER_HEADER` - > valeur par défaut : `REMOTE_USER` + > default value: `REMOTE_USER` >> Nom de l’attribut dans les headers qui sert à identifier
>> l’utilisateur connecté avec Shibboleth.
* `SHIBBOLETH_ATTRIBUTE_MAP` - > valeur par défaut : `` + > default value: `` >> Mapping des attributs entre Shibboleth et la classe utilisateur
* `SHIBBOLETH_STAFF_ALLOWED_DOMAINS` - > valeur par défaut : `` + > default value: `` >> Permettre à l’utilisateur d’un domaine d’être membre du personnel.
>> Si vide, tous les domaines seront autorisés.
* `SHIB_LOGOUT_URL` - > valeur par défaut : `` + > default value: `` >> URL de déconnexion à votre instance Shibboleth
* `SHIB_NAME` - > valeur par défaut : `` + > default value: `` >> Nom de la fédération d’identité utilisée.
* `SHIB_URL` - > valeur par défaut : `` + > default value: `` >> URL de connexion à votre instance Shibboleth.
* `USER_CAS_MAPPING_ATTRIBUTES` - > valeur par défaut : `` + > default value: `` >> Liste de correspondance entre les champs d’un compte de Pod
>> et les champs renvoyés par le CAS.
>> OBSOLÈTE. Utilisez désormais `CAS_RENAME_ATTRIBUTES`.
* `USER_LDAP_MAPPING_ATTRIBUTES` - > valeur par défaut : `` + > default value: `` >> Liste de correspondance entre les champs d’un compte de Pod
>> et les champs renvoyés par le LDAP.
* `USE_CAS` - > valeur par défaut : `False` + > default value: `False` >> Activation de l’authentification CAS en plus de l’authentification locale.
* `USE_OIDC` - > valeur par défaut : `False` + > default value: `False` >> Mettre à True pour utiliser l’authentification OpenID Connect.
* `USE_SHIB` - > valeur par défaut : `False` + > default value: `False` >> Mettre à True pour utiliser l’authentification Shibboleth.
### Configuration de l’application chapter @@ -993,41 +995,41 @@ Mettre `USE_AI_ENHANCEMENT` à True pour activer cette application.
### Configuration de l’application completion * `ACTIVE_MODEL_ENRICH` - > valeur par défaut : `False` + > default value: `False` >> Définissez à True pour activer la case à cocher dans l’édition des sous-titres.
* `ALL_LANG_CHOICES` - > valeur par défaut : `` + > default value: `` >> liste toutes les langues pour l’ajout de fichier de sous-titre
>> voir le fichier `pod/main/lang_settings.py`.
* `DEFAULT_LANG_TRACK` - > valeur par défaut : `fr` + > default value: `fr` >> langue par défaut pour l’ajout de piste à une vidéo.
* `KIND_CHOICES` - > valeur par défaut : `` + > default value: `` >> Liste de types de piste possibles pour une vidéo (sous-titre, légende etc.)
* `LANG_CHOICES` - > valeur par défaut : `` + > default value: `` >> Liste des langues proposées lors de l’ajout des vidéos.
>> Affichés en dessous d’une vidéo, les choix sont aussi utilisés pour affiner la recherche.
* `LINK_SUPERPOSITION` - > valeur par défaut : `False` + > default value: `False` >> Si valeur vaut 'True', les URLs contenues dans le texte de superposition
>> seront transformées, à la lecture de la vidéo, en liens cliquables.
* `MODEL_COMPILE_DIR` - > valeur par défaut : `/path/of/project/Esup-Pod/compile-model` + > default value: `/path/of/project/Esup-Pod/compile-model` >> Paramétrage des chemins du modèle pour la compilation
>> Pour télécharger les modèles : [alphacephei.com/vosk](https://alphacephei.com/vosk/lm#update-process)
>> Ajouter le modèle dans les sous-dossier de la langue correspondante
>> Exemple pour le français : `/path/of/project/Esup-Pod/compile-model/fr/`
* `PREF_LANG_CHOICES` - > valeur par défaut : `` + > default value: `` >> liste des langues à afficher en premier dans la liste des toutes les langues
>> voir le fichier `pod/main/lang_settings.py`
* `ROLE_CHOICES` - > valeur par défaut : `` + > default value: `` >> Liste de rôles possibles pour un contributeur.
* `USE_ENRICH_READY` - > valeur par défaut : `False` + > default value: `False` >> voir `ACTIVE_MODEL_ENRICH`
### Configuration de l’application Cut @@ -1036,7 +1038,7 @@ Application Cut permettant de découper des vidéos.
Mettre `USE_CUT` à True pour activer cette application.
* `USE_CUT` - > valeur par défaut : `False` + > default value: `False` >> Activation de l’application Cut
### Configuration de l’application dressing @@ -1045,7 +1047,7 @@ Application Dressing pour customiser une vidéo avec un filigrane et des crédit Mettre `USE_DRESSING` à True pour activer cette application.
* `USE_DRESSING` - > valeur par défaut : `False` + > default value: `False` >> Activation des habillages.
>> Permet aux utilisateurs de customiser une vidéo avec un filigrane et des crédits.
@@ -1055,7 +1057,7 @@ Application Duplicate pour créer une copie du formulaire d’une vidéo existan Mettre `USE_DUPLICATE` à True pour activer cette application.
* `USE_DUPLICATE` - > valeur par défaut : `False` + > default value: `False` >> Activation de duplicate.
>> Permet aux utilisateurs de dupliquer une vidéo
@@ -1065,7 +1067,7 @@ Application Liens permettant d'ajouter des liens à la vidéo.
Mettre `USE_HYPERLINKS` à True pour activer cette application.
* `USE_HYPERLINKS` - > valeur par défaut : `False` + > default value: `False` >> Activation de l’application Liens
### Configuration de l’application enrichment @@ -1077,10 +1079,10 @@ Application Intervenant permettant d'ajouter des intervenants à la vidéo.
Mettre `USE_SPEAKER` à True pour activer cette application.
* `USE_SPEAKER` - > valeur par défaut : `False` + > default value: `False` >> Activation de l’application Intervenant
* `REQUIRED_SPEAKER_FIRSTNAME` - > valeur par défaut : `True` + > default value: `True` >> Prénom obligatoire dans le formulaire d'ajout intervenant
### Configuration de l’application d’import vidéo @@ -1089,88 +1091,88 @@ Application Import_video permettant d’importer des vidéos externes dans Pod.< Mettre `USE_IMPORT_VIDEO` à True pour activer cette application.
* `MAX_UPLOAD_SIZE_ON_IMPORT` - > valeur par défaut : `4` + > default value: `4` >> Taille maximum en Go des fichiers vidéos qui peuvent être importés sur la plateforme
>> via l’application import_video (0 = pas de taille maximum).
* `RESTRICT_EDIT_IMPORT_VIDEO_ACCESS_TO_STAFF_ONLY` - > valeur par défaut : `True` + > default value: `True` >> Seuls les utilisateurs "staff" pourront importer des vidéos
* `USE_IMPORT_VIDEO` - > valeur par défaut : `False` + > default value: `False` >> Activation de l’application d’import des vidéos
* `USE_IMPORT_VIDEO_BBB_RECORDER` - > valeur par défaut : `False` + > default value: `False` >> Utilisation du plugin bbb-recorder pour le module import-vidéo;
>> utile pour convertir une présentation BigBlueButton en fichier vidéo.
* `IMPORT_VIDEO_BBB_RECORDER_PLUGIN` - > valeur par défaut : `/home/pod/bbb-recorder/` + > default value: `/home/pod/bbb-recorder/` >> Répertoire du plugin bbb-recorder (voir la documentation [jibon57/bbb-recorder](https://github.com/jibon57/bbb-recorder)).
>> bbb-recorder doit être installé dans ce répertoire, sur tous les serveurs d’encodage.
>> bbb-recorder crée un répertoire Downloads, au même niveau, qui nécessite de l’espace disque.
* `IMPORT_VIDEO_BBB_RECORDER_PATH` - > valeur par défaut : `/data/bbb-recorder/media/` + > default value: `/data/bbb-recorder/media/` >> Répertoire qui contiendra les fichiers vidéo générés par bbb-recorder.
### Configuration de l’application live * `AFFILIATION_EVENT` - > valeur par défaut : `['faculty', 'employee', 'staff']` + > default value: `['faculty', 'employee', 'staff']` >> Groupes ou affiliations des personnes autorisées à créer un évènement.
* `BROADCASTER_PILOTING_SOFTWARE` - > valeur par défaut : `[]` + > default value: `[]` >> Types de logiciel de serveur de streaming utilisés.
>> Actuellement disponible Wowza et SMP.
>> Il faut préciser cette valeur pour l’activer `['Wowza', 'SMP']`
>> Si vous utilisez une autre logiciel,
>> il faut développer une interface dans `pod/live/pilotingInterface.py`
* `DEFAULT_EVENT_PATH` - > valeur par défaut : `` + > default value: `` >> Chemin racine du répertoire où sont déposés temporairement
>> les enregistrements des évènements éffectués depuis POD
>> pour convertion en ressource vidéo (VOD)
* `DEFAULT_EVENT_THUMBNAIL` - > valeur par défaut : `/img/default-event.svg` + > default value: `/img/default-event.svg` >> Image par défaut affichée comme poster ou vignette, utilisée pour présenter l’évènement.
>> Cette image doit se situer dans le répertoire `static`.
* `DEFAULT_EVENT_TYPE_ID` - > valeur par défaut : `1` + > default value: `1` >> Type par défaut affecté à un évènement direct
>> (en général, le type ayant pour identifiant '1' est 'Other')
* `DEFAULT_THUMBNAIL` - > valeur par défaut : `img/default.svg` + > default value: `img/default.svg` >> Image par défaut affichée comme poster ou vignette, utilisée pour présenter la vidéo.
>> Cette image doit se situer dans le répertoire static.
* `EMAIL_ON_EVENT_SCHEDULING` - > valeur par défaut : `True` + > default value: `True` >> Si True, un courriel est envoyé aux managers et à l’auteur
>> (si DEBUG est à False) à la création/modification d’un event.
* `EVENT_ACTIVE_AUTO_START` - > valeur par défaut : `False` + > default value: `False` >> Permet de lancer automatiquement l’enregistrement sur l’interface utilisée
>> (wowza, ) sur le broadcaster et spécifié par `BROADCASTER_PILOTING_SOFTWARE`.
* `EVENT_CHECK_MAX_ATTEMPT` - > valeur par défaut : `10` + > default value: `10` >> Nombre de tentatives maximum pour vérifier la présence / taille d’un fichier sur le filesystem
* `EVENT_GROUP_ADMIN` - > valeur par défaut : `event admin` + > default value: `event admin` >> Permet de préciser le nom du groupe dans lequel les utilisateurs
>> peuvent planifier un évènement sur plusieurs jours.
* `HEARTBEAT_DELAY` - > valeur par défaut : `45` + > default value: `45` >> Temps (en secondes) entre deux envois d’un signal au serveur,
>> pour signaler la présence sur un live.
>> Peut être augmenté en cas de perte de performance,
>> mais au détriment de la qualité du comptage des valeurs.
* `LIVE_CELERY_TRANSCRIPTION` - > valeur par défaut : `False` + > default value: `False` >> >> Activer la transcription déportée sur une machine distante.
* `LIVE_TRANSCRIPTIONS_FOLDER` - > valeur par défaut : `` + > default value: `` >> >> Dossier contenat les fichiers de sous-titre au format vtt pour les directs
* `LIVE_VOSK_MODEL` - > valeur par défaut : `{}` + > default value: `{}` >> >> Paramétrage des modèles pour la transcription des directs
>> La documentation sera présente prochaînement
@@ -1185,29 +1187,29 @@ Mettre `USE_IMPORT_VIDEO` à True pour activer cette application.
>> ``` >> * `USE_BBB` - > valeur par défaut : `False` + > default value: `False` >> Utilisation de BigBlueButton
>> Retiré à partir de la version 3.8.2 de Pod (remplacé par le module des réunions)
* `USE_BBB_LIVE` - > valeur par défaut : `False` + > default value: `False` >> Utilisation du système de diffusion de Webinaires en lien avec BigBlueButton
>> Retiré à partir de la version 3.8.2 de Pod (remplacé par le module des réunions)
* `USE_LIVE_TRANSCRIPTION` - > valeur par défaut : `False` + > default value: `False` >> Activer l’auto-transcription pour les directs
>> * `VIEW_EXPIRATION_DELAY` - > valeur par défaut : `60` + > default value: `60` >> Délai (en seconde) selon lequel une vue est considérée comme expirée
>> si elle n’a pas renvoyé de signal depuis.
### Configuration de l’application LTI * `LTI_ENABLED` - > valeur par défaut : `False` + > default value: `False` >> Configuration / Activation du LTI voir pod/main/settings.py L.224
* `PYLTI_CONFIG` - > valeur par défaut : `{}` + > default value: `{}` >> Cette variable permet de configurer l’application cliente et le secret partagé
>> >> ```python @@ -1224,33 +1226,33 @@ Mettre `USE_IMPORT_VIDEO` à True pour activer cette application.
### Configuration de l’application main * `HOMEPAGE_VIEW_VIDEOS_FROM_NON_VISIBLE_CHANNELS` - > valeur par défaut : `False` + > default value: `False` >> Affiche les vidéos de chaines non visibles sur la page d’accueil
* `USE_BBB` - > valeur par défaut : `True` + > default value: `True` >> Utilisation de BigBlueButton
>> Module obsolète.
* `USE_BBB_LIVE` - > valeur par défaut : `False` + > default value: `False` >> Utilisation du système de diffusion de Webinaires en lien avec BigBlueButton
>> [TODO] À retirer dans les futures versions de Pod
* `USE_IMPORT_VIDEO` - > valeur par défaut : `False` + > default value: `False` >> Activation de l’application d’import des vidéos
* `USE_MEETING` - > valeur par défaut : `False` + > default value: `False` >> Activation de l’application meeting
* `USE_OPENCAST_STUDIO` - > valeur par défaut : `False` + > default value: `False` >> Activation du studio [Opencast](https://opencast.org/)
* `VERSION` - > valeur par défaut : `` + > default value: `` >> Version courante du projet
* `WEBTV_MODE` - > valeur par défaut : `False` + > default value: `False` >> Mode webtv permet de basculer POD en une application webtv ensupprimant les boutons de connexions par exemple
* `SOCIAL_SHARE` - > valeur par défaut : `['X', 'FACEBOOK', 'LINKEDIN', 'BLUESKY', 'MASTODON']` + > default value: `['X', 'FACEBOOK', 'LINKEDIN', 'BLUESKY', 'MASTODON']` >> Choix d'affichage des liens de partage des réseaux sociaux
### Configuration de l’application meeting @@ -1260,13 +1262,13 @@ Mettre `USE_MEETING` à True pour activer cette application.
`BBB_API_URL` et `BBB_SECRET_KEY` sont obligatoires pour faire fonctionner l’application
* `BBB_API_URL` - > valeur par défaut : `` + > default value: `` >> Indiquer l’URL API de BBB par ex `https://webconf.univ.fr/bigbluebutton/api`.
* `BBB_LOGOUT_URL` - > valeur par défaut : `` + > default value: `` >> Indiquer l’URL de retour au moment où vous quittez la réunion BBB. Ce champ est optionnel.
* `BBB_MEETING_INFO` - > valeur par défaut : `{}` + > default value: `{}` >> Dictionnaire de `clé:valeur` permettant d’afficher les informations
>> d’une session de réunion dans BBB
>> Voici la liste par défaut
@@ -1288,16 +1290,16 @@ Mettre `USE_MEETING` à True pour activer cette application.
>> ``` >> * `BBB_SECRET_KEY` - > valeur par défaut : `` + > default value: `` >> Clé de votre serveur BBB.
>> Vous pouvez récupérer cette clé à l’aide de la commande
>> `bbb-conf --secret` sur le serveur BBB.
* `DEFAULT_MEETING_THUMBNAIL` - > valeur par défaut : `/img/default-meeting.svg` + > default value: `/img/default-meeting.svg` >> Image par défaut affichée comme poster ou vignette, utilisée pour présenter la réunion.
>> Cette image doit se situer dans le répertoire `static`.
* `MEETING_DATE_FIELDS` - > valeur par défaut : `()` + > default value: `()` >> liste des champs du formulaire de creation d’une reunion
>> les champs sont regroupés dans un ensemble de champs
>> @@ -1311,12 +1313,12 @@ Mettre `USE_MEETING` à True pour activer cette application.
>> ``` >> * `MEETING_DISABLE_RECORD` - > valeur par défaut : `True` + > default value: `True` >> Mettre à True pour désactiver les enregistrements de réunion
>> Configuration de l’enregistrement des réunions.
>> Ce champ n’est pas pris en compte si `MEETING_DISABLE_RECORD = True`.
* `MEETING_MAIN_FIELDS` - > valeur par défaut : `()` + > default value: `()` >> Permet de définir les champs principaux du formulaire de création d’une réunion
>> les champs principaux sont affichés directement dans la page de formulaire d’une réunion
>> @@ -1333,18 +1335,18 @@ Mettre `USE_MEETING` à True pour activer cette application.
>> ``` >> * `MEETING_MAX_DURATION` - > valeur par défaut : `5` + > default value: `5` >> permet de définir la durée maximum pour une reunion
>> (en heure)
* `MEETING_PRE_UPLOAD_SLIDES` - > valeur par défaut : `` + > default value: `` >> >> Diaporama préchargé pour les réunions virtuelles.
>> Un utilisateur peut remplacer cette valeur en choisissant un diaporama
>> lors de la création d’une réunion virtuelle.
>> Doit se trouver dans le répertoire statique.
* `MEETING_RECORD_FIELDS` - > valeur par défaut : `()` + > default value: `()` >> ensemble des champs qui seront cachés si `MEETING_DISABLE_RECORD` est défini à true.
>> >> ```python @@ -1352,7 +1354,7 @@ Mettre `USE_MEETING` à True pour activer cette application.
>> ``` >> * `MEETING_RECURRING_FIELDS` - > valeur par défaut : `()` + > default value: `()` >> Liste de tous les champs permettant de définir la récurrence d’une reunion
>> tous ces champs sont regroupés dans un ensemble de champs affichés dans une modale
>> @@ -1369,21 +1371,21 @@ Mettre `USE_MEETING` à True pour activer cette application.
>> ``` >> * `RESTRICT_EDIT_MEETING_ACCESS_TO_STAFF_ONLY` - > valeur par défaut : `False` + > default value: `False` >> Seuls les utilisateurs "staff" pourront éditer les réunions
* `USE_MEETING_WEBINAR` - > valeur par défaut : `False` + > default value: `False` >> Activation du mode Webinaire pour le module des réunions
* `MEETING_WEBINAR_SIPMEDIAGW_URL` - > valeur par défaut : `` + > default value: `` >> URL du serveur SIPMediaGW qui gère les webinaires (Ex: `https://sipmediagw.univ.fr`)
>> Retiré à partir de la version 3.8.2 de Pod (remplacé par le module des réunions, cf. passerelle de live)
* `MEETING_WEBINAR_SIPMEDIAGW_TOKEN` - > valeur par défaut : `` + > default value: `` >> Jeton bearer du serveur SIPMediaGW qui gère les webinaires
>> Retiré à partir de la version 3.8.2 de Pod (cf. passerelle de live)
* `MEETING_WEBINAR_FIELDS` - > valeur par défaut : `("is_webinar", "enable_chat")` + > default value: `("is_webinar", "enable_chat")` >> Permet de définir les champs complémentaires du formulaire de création d’un webinaire
>> ces champs complémentaires sont affichés directement dans la page de formulaire d’un webinaire
>> @@ -1396,13 +1398,13 @@ Mettre `USE_MEETING` à True pour activer cette application.
>> ``` >> * `MEETING_WEBINAR_AFFILIATION` - > valeur par défaut : `['faculty', 'employee', 'staff']` + > default value: `['faculty', 'employee', 'staff']` >> Groupes d’accès ou affiliations des personnes autorisées à créer un webinaire
* `MEETING_WEBINAR_GROUP_ADMIN` - > valeur par défaut : `webinar admin` + > default value: `webinar admin` >> Groupe des personnes autorisées à créer un webinaire
* `USE_MEETING` - > valeur par défaut : `False` + > default value: `False` >> Activer l’application meeting
### Configuration de l’application playlist @@ -1411,57 +1413,57 @@ Application Playlist pour la gestion des playlists.
Mettre `USE_PLAYLIST` à True pour activer cette application.
* `COUNTDOWN_PLAYLIST_PLAYER` - > valeur par défaut : `0` + > default value: `0` >> Compte à rebours utilisé entre chaque vidéo lors de
>> la lecture d’une playlist en lecture automatique.
>> Le compte à rebours n’est pas présent s’il est à 0.
* `DEFAULT_PLAYLIST_THUMBNAIL` - > valeur par défaut : `/static/playlist/img/default-playlist.svg` + > default value: `/static/playlist/img/default-playlist.svg` >> Image par défaut affichée comme poster ou vignette, utilisée pour présenter la playlist.
>> Cette image doit se situer dans le répertoire `static`.
* `RESTRICT_PROMOTED_PLAYLIST_ACCESS_TO_STAFF_ONLY` - > valeur par défaut : `True` + > default value: `True` >> Restreindre l’accès à la création de listes de lecture promues
>> au staff uniquement.
* `USE_FAVORITES` - > valeur par défaut : `False` + > default value: `False` >> Activation des vidéos favorites.
>> Permet aux utilisateurs d’ajouter des vidéos dans leurs favoris.
* `USE_PLAYLIST` - > valeur par défaut : `False` + > default value: `False` >> Activation des playlist. Permet aux utilisateurs d’ajouter des vidéos dans une playlist.
* `USE_PROMOTED_PLAYLIST` - > valeur par défaut : `False` + > default value: `False` >> Activation des playlist promues.
>> Permet aux utilisateurs d'utiliser les listes de lecture promues.
### Configuration de l’application podfile * `FILES_DIR` - > valeur par défaut : `files` + > default value: `files` >> Nom du répertoire racine où les fichiers "complémentaires"
>> (hors vidéos etc.) sont téléversés. Notament utilisé par PODFILE
>> À modifier principalement pour indiquer dans LOCATION votre serveur
>> de cache si elle n’est pas sur la même machine que votre POD.
* `FILE_ALLOWED_EXTENSIONS` - > valeur par défaut : `('doc', 'docx', 'odt', 'pdf', 'xls', 'xlsx', 'ods', 'ppt', 'pptx', 'txt', 'html', 'htm', 'vtt', 'srt')` + > default value: `('doc', 'docx', 'odt', 'pdf', 'xls', 'xlsx', 'ods', 'ppt', 'pptx', 'txt', 'html', 'htm', 'vtt', 'srt')` >> Extensions autorisées pour les documents téléversés
>> dans le gestionnaire de fichier (en minuscules).
* `FILE_MAX_UPLOAD_SIZE` - > valeur par défaut : `10` + > default value: `10` >> Poids maximum en Mo par fichier téléversé dans le gestionnaire de fichier
* `IMAGE_ALLOWED_EXTENSIONS` - > valeur par défaut : `('jpg', 'jpeg', 'bmp', 'png', 'gif', 'tiff', 'webp')` + > default value: `('jpg', 'jpeg', 'bmp', 'png', 'gif', 'tiff', 'webp')` >> Extensions autorisées pour les images téléversées
>> dans le gestionnaire de fichier. (en minuscules)
### Configuration de l’application progressive_web_app * `USE_NOTIFICATIONS` - > valeur par défaut : `False` + > default value: `False` >> Activation des notifications, attention, elles sont actives par défaut.
* `WEBPUSH_SETTINGS` - > valeur par défaut : + > default value: ```python { @@ -1480,34 +1482,34 @@ Application Quiz pour ajouter des questions sur les vidéos.
Mettre `USE_QUIZ` à True pour activer cette application.
* `USE_QUIZ` - > valeur par défaut : `False` + > default value: `False` >> Activation des quiz. Permet aux utilisateurs de créer, répondre et utiliser des quiz dans les vidéos.
### Configuration de l’application recorder * `ALLOW_MANUAL_RECORDING_CLAIMING` - > valeur par défaut : `False` + > default value: `False` >> Si True, active un lien dans le menu de l’utilisateur permettant de réclamer un enregistrement.
* `ALLOW_RECORDER_MANAGER_CHOICE_VID_OWNER` - > valeur par défaut : `True` + > default value: `True` >> Si True, le manager de l’enregistreur pourra choisir un propriétaire de l’enregistrement.
* `DEFAULT_RECORDER_ID` - > valeur par défaut : `1` + > default value: `1` >> Ajoute un enregistreur par défaut à un enregistrement non identifiable
>> (mauvais chemin dans le dépôt FTP).
* `DEFAULT_RECORDER_PATH` - > valeur par défaut : `/data/ftp-pod/ftp/` + > default value: `/data/ftp-pod/ftp/` >> Chemin racine du répertoire où sont déposés les enregistrements
>> (chemin du serveur FTP).
* `DEFAULT_RECORDER_TYPE_ID` - > valeur par défaut : `1` + > default value: `1` >> Identifiant du type de vidéo par défaut (si non spécifié).
>> (Exemple : 3 pour Colloque/conférence, 4 pour Cours…)
* `DEFAULT_RECORDER_USER_ID` - > valeur par défaut : `1` + > default value: `1` >> Identifiant du propriétaire par défaut (si non spécifié) des enregistrements déposés.
* `OPENCAST_DEFAULT_PRESENTER` - > valeur par défaut : `mid` + > default value: `mid` >> Permet de spécifier la valeur par défaut du placement de la vidéo du
>> presenteur par rapport à la vidéo de présentation (écran)
>> les valeurs possibles sont :
@@ -1518,10 +1520,10 @@ Mettre `USE_QUIZ` à True pour activer cette application.
>> Ce fichier va contenir toutes les spécificités de l’enregistrement
>> (source, cutting, title, presenter etc.)
* `OPENCAST_FILES_DIR` - > valeur par défaut : `opencast-files` + > default value: `opencast-files` >> Permet de spécifier le dossier de stockage des enregistrements du studio avant traitement.
* `OPENCAST_MEDIAPACKAGE` - > valeur par défaut : `-> see xml content` + > default value: `-> see xml content` >> Contenu par défaut du fichier xml pour créer le mediapackage pour le studio.
>> Ce fichier va contenir toutes les spécificités de l’enregistrement
>> (source, cutting, title, presenter etc.)
@@ -1537,11 +1539,11 @@ Mettre `USE_QUIZ` à True pour activer cette application.
>> ``` >> * `PUBLIC_RECORD_DIR` - > valeur par défaut : `records` + > default value: `records` >> Chemin d’accès web (public) au répertoire de dépot des enregistrements (`DEFAULT_RECORDER_PATH`).
>> Attention : penser à modifier la conf de NGINX.
* `RECORDER_ADDITIONAL_FIELDS` - > valeur par défaut : `()` + > default value: `()` >> Liste des champs supplémentaires pour le formulaire des enregistreurs.
>> Cette liste reprend le nom des champs correspondants aux paramètres d’édition d’une vidéo
>> (Discipline, Chaine, Theme, mots clés...).
@@ -1550,50 +1552,50 @@ Mettre `USE_QUIZ` à True pour activer cette application.
>> Les vidéos seront alors générées avec les valeurs des champs supplémentaires
>> telles que définies dans leur enregistreur.
* `RECORDER_ALLOW_INSECURE_REQUESTS` - > valeur par défaut : `False` + > default value: `False` >> Autorise la requête sur l’application en elle-même sans vérifier le certificat SSL
* `RECORDER_BASE_URL` - > valeur par défaut : `https://pod.univ.fr` + > default value: `https://pod.univ.fr` >> url racine de l’instance permettant l’envoi de notification lors de la réception d’enregistrement.
* `RECORDER_SELF_REQUESTS_PROXIES` - > valeur par défaut : `{"http": None, "https": None}` + > default value: `{"http": None, "https": None}` >> Précise les proxy à utiliser pour une requête vers l’application elle même
>> dans le cadre d’enregistrement par défaut force la non utilisation de proxy.
* `RECORDER_SKIP_FIRST_IMAGE` - > valeur par défaut : `False` + > default value: `False` >> Si True, permet de ne pas prendre en compte la 1ère image lors du traitement
>> d’un fichier d’enregistrement de type AudioVideoCast.
* `RECORDER_TYPE` - > valeur par défaut : `(('video', _('Video')), ('audiovideocast', _('Audiovideocast')), ('studio', _('Studio')))` + > default value: `(('video', _('Video')), ('audiovideocast', _('Audiovideocast')), ('studio', _('Studio')))` >> Type d’enregistrement géré par la plateforme.
>> Un enregistreur ne peut déposer que des fichiers de type proposé par la plateforme.
>> Le traitement se fait en fonction du type de fichier déposé.
* `USE_OPENCAST_STUDIO` - > valeur par défaut : `False` + > default value: `False` >> Activer l’utilisation du studio Opencast.
* `USE_RECORD_PREVIEW` - > valeur par défaut : `False` + > default value: `False` >> Si True, affiche l’icone de prévisualisation des vidéos dans la page "Revendiquer un enregistrement".
### Configuration de l’application vidéo * `ACTIVE_VIDEO_COMMENT` - > valeur par défaut : `False` + > default value: `False` >> Activer les commentaires au niveau de la plateforme
* `CACHE_VIDEO_DEFAULT_TIMEOUT` - > valeur par défaut : `600` + > default value: `600` >> >> Temps en seconde de conservation des données de l’application video
* `CHANNEL_FORM_FIELDS_HELP_TEXT` - > valeur par défaut : `` + > default value: `` >> Ensemble des textes d’aide affichés avec le formulaire d’édition de chaine.
>> voir pod/video/forms.py
* `CHUNK_SIZE` - > valeur par défaut : `1000000` + > default value: `1000000` >> Taille d’un fragment lors de l’envoi d’une vidéo
>> le fichier sera mis en ligne par fragment de cette taille.
* `CURSUS_CODES` - > valeur par défaut : `()` + > default value: `()` >> Liste des cursus proposés lors de l’ajout des vidéos.
>> Affichés en dessous d’une vidéos, ils sont aussi utilisés pour affiner la recherche.
>> @@ -1608,32 +1610,32 @@ Mettre `USE_QUIZ` à True pour activer cette application.
>> ``` >> * `DEFAULT_DC_COVERAGE` - > valeur par défaut : `TITLE_ETB + " - Town - Country"` + > default value: `TITLE_ETB + " - Town - Country"` >> couverture du droit pour chaque vidéo
* `DEFAULT_DC_RIGHTS` - > valeur par défaut : `BY-NC-SA` + > default value: `BY-NC-SA` >> droit par défaut affichés dans le flux RSS si non renseigné
* `DEFAULT_THUMBNAIL` - > valeur par défaut : `img/default.svg` + > default value: `img/default.svg` >> Image par défaut affichée comme poster ou vignette, utilisée pour présenter la vidéo.
>> Cette image doit se situer dans le répertoire static.
* `DEFAULT_TYPE_ID` - > valeur par défaut : `1` + > default value: `1` >> Les vidéos créées sans type (par importation par exemple)
>> seront affectées au type par défaut
>> (en général, le type ayant pour identifiant '1' est 'Other')
* `DEFAULT_YEAR_DATE_DELETE` - > valeur par défaut : `2` + > default value: `2` >> Durée d’obsolescence par défaut (en années après la date d’ajout).
* `FORCE_LOWERCASE_TAGS` - > valeur par défaut : `True` + > default value: `True` >> Les mots clés saisis lors de l’ajout de vidéo sont convertis automatiquement en minuscule.
* `LANG_CHOICES` - > valeur par défaut : `` + > default value: `` >> Liste des langues proposées lors de l’ajout des vidéos.
>> Affichés en dessous d’une vidéos, les choix sont aussi utilisés pour affiner la recherche.
* `LICENCE_CHOICES` - > valeur par défaut : `()` + > default value: `()` >> Licence proposées pour les vidéos en creative commons :
>> >> ```python @@ -1652,18 +1654,18 @@ Mettre `USE_QUIZ` à True pour activer cette application.
>> ``` >> * `MAX_DURATION_DATE_DELETE` - > valeur par défaut : `10` + > default value: `10` >> Fixe une durée maximale que la date de suppression d’une vidéo ne peut dépasser.
>> Par défaut : 10 (Année courante + 10 ans).
* `MAX_TAG_LENGTH` - > valeur par défaut : `50` + > default value: `50` >> Les mots-clés saisis lors de l’ajout de vidéo ne peuvent dépasser cette longueur.
* `NUMBER_TAGS_CLOUD` - > valeur par défaut : `20` + > default value: `20` >> Nombre de mots-clés les plus importants affichés dans le nuage de la page d'accueil.
>> Les paramètres TAGULOUS_WEIGHT_MIN et TAGULOUS_WEIGHT_MAX ne sont pas utilisés.
* `NOTES_STATUS` - > valeur par défaut : `()` + > default value: `()` >> Valeurs possible pour l’accès à une note.
>> >> ```python @@ -1675,17 +1677,17 @@ Mettre `USE_QUIZ` à True pour activer cette application.
>> ``` >> * `OEMBED` - > valeur par défaut : `False` + > default value: `False` >> Permettre l’usage du oembed, partage dans Moodle, Facebook, Twitter etc.
* `ORGANIZE_BY_THEME` - > valeur par défaut : `False` + > default value: `False` >> Affichage uniquement des vidéos de la chaîne ou du thème actuel(le).
>> Affichage des sous-thèmes directs de la chaîne ou du thème actuel(le)
* `RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY` - > valeur par défaut : `False` + > default value: `False` >> Si True, seule les personnes "Staff" peuvent déposer des vidéos
* `THEME_FORM_FIELDS_HELP_TEXT` - > valeur par défaut : `""` + > default value: `""` >> Ensemble des textes d’aide affichés avec le formulaire d’édition de theme.
>> voir pod/video/forms.py
>> @@ -1720,17 +1722,17 @@ Mettre `USE_QUIZ` à True pour activer cette application.
>> ``` >> * `USER_VIDEO_CATEGORY` - > valeur par défaut : `False` + > default value: `False` >> Permet d’activer le fonctionnement de categorie au niveau de ses vidéos.
>> Vous pouvez créer des catégories pour pouvoir ranger vos propres vidéos.
>> Les catégories sont liées à l’utilisateur.
* `USE_OBSOLESCENCE` - > valeur par défaut : `False` + > default value: `False` >> Activation de l’obsolescence des video.
>> Permet d’afficher la date de suppression de la video
>> dans le formulaire d’edition et dans la partie admin.
* `USE_STATS_VIEW` - > valeur par défaut : `False` + > default value: `False` >> Permet d’activer la possibilité de voir en details le nombre de visualisation
>> d’une vidéo durant un jour donné ou mois,
>> année ou encore le nombre de vue total depuis la création de la vidéo.
@@ -1738,7 +1740,7 @@ Mettre `USE_QUIZ` à True pour activer cette application.
>> un lien est rajouté dans la page de visualisation d’une chaîne ou un theme
>> ou encore toutes les vidéos présentes sur la plateforme.
* `USE_VIDEO_EVENT_TRACKING` - > valeur par défaut : `False` + > default value: `False` >> Ce paramètre permet d’activer l’envoi d’évènements sur le lecteur vidéo à Matomo.
>> N’est utile que si le code piwik / matomo est présent dans l’instance de Esup-Pod.
>> Les évènements envoyés sont :
@@ -1749,12 +1751,12 @@ Mettre `USE_QUIZ` à True pour activer cette application.
>> de préciser dans la variable `TEMPLATE_VISIBLE_SETTINGS`:
>> `'TRACKING_TEMPLATE': 'custom/tracking.html'`
* `USE_XAPI_VIDEO` - > valeur par défaut : `False` + > default value: `False` >> >> Active l‘envoi d’instructions xAPI pour le lecteur vidéo.
>> Attention, il faut mettre USE_XAPI à True pour que les instructions soient envoyées.
* `VIDEO_ALLOWED_EXTENSIONS` - > valeur par défaut : `()` + > default value: `()` >> Extensions autorisées pour le téléversement vidéo sur la plateforme (en minuscules).
>> >> ```python @@ -1782,14 +1784,14 @@ Mettre `USE_QUIZ` à True pour activer cette application.
>> ``` >> * `VIDEO_FEED_NB_ITEMS` - > valeur par défaut : `100` + > default value: `100` >> >> nombre d’item renvoyé par le flux rss
* `VIDEO_FORM_FIELDS` - > valeur par défaut : `__all__` + > default value: `__all__` >> Liste des champs du formulaire d’édition de vidéos affichées.
* `VIDEO_FORM_FIELDS_HELP_TEXT` - > valeur par défaut : `` + > default value: `` >> Ensemble des textes d’aide affichés avec le formulaire d’envoi de vidéo.
>> >> ```python @@ -2016,61 +2018,73 @@ Mettre `USE_QUIZ` à True pour activer cette application.
>> ``` >> * `VIDEO_MAX_UPLOAD_SIZE` - > valeur par défaut : `1` + > default value: `1` >> Taille maximum en Go des fichiers téléversés sur la plateforme.
* `VIDEO_PLAYBACKRATES` - > valeur par défaut : `[0.5, 1, 1.5, 2]` + > default value: `[0.5, 1, 1.5, 2]` >> Configuration des choix de vitesse de lecture pour le lecteur vidéo.
* `VIDEO_RECENT_VIEWCOUNT` - > valeur par défaut : `180` + > default value: `180` >> Durée (en nombre de jours) sur laquelle on souhaite compter le nombre de vues récentes.
* `VIDEO_REQUIRED_FIELDS` - > valeur par défaut : `[]` + > default value: `[]` >> Permet d’ajouter l’attribut obligatoire dans
>> le formulaire d’edition et d’ajout d’une video :
>> Exemple de valeur : `["discipline", "tags"]`
>> NB : les champs cachés et suivant ne sont pas pris en compte :
>> `(video, title, type, owner, date_added, cursus, main_lang)`
* `VIEW_STATS_AUTH` - > valeur par défaut : `False` + > default value: `False` >> Réserve l’accès aux statistiques des vidéos aux personnes authentifiées.
### Configuration de l’application encodage et transcription de vidéo Application pour l’encodage et la transcription de vidéo.
Il est possible d’encoder en local ou en distant.
-Attention, il faut configurer Celery pour l’envoi des instructions pour l’encodage distant.
- +Pour l’encodage distant, il est préférable d’utiliser le système d’externalisation des encodages et transcriptions,
+Esup-Runner, avec utilisation de runner managers. Sinon, il est aussi possible d’utiliser Celery dans ce cas là.
+ +* `USE_RUNNER_MANAGER` + > default value: `False` + >> Si True, Pod utilise le système d’externalisation des encodages et transcriptions,
+ >> Esup-Runner, avec utilisation de runner managers. Les encodages et transcriptions
+ >> sont totalement délégués à ce système. Cf. https://github.com/EsupPortail/esup-runner.
+ >> Dans ce cas là, il n'est plus utile de configurer Celery pour l’encodage et la transcription à distance.
+* `RM_TASKS_DELETED_AFTER_DAYS` + > default value: `0` + >> Paramètre utilisé seulement quand USE_RUNNER_MANAGER = True.
+ >> Il correspond au nombre de jours après lesquels les tâches terminées sont supprimées
+ >> dans la base de données (0 signifiant que les tâches terminées sont conservées indéfiniment).
* `CAPTIONS_STRICT_ACCESSIBILITY` - > valeur par défaut : `False` + > default value: `False` >> Si True, les sous-titres seront générés en respectant strictement les normes
>> d’accessibilité. L'apparition d'un message d’avertissement sera affiché si les
>> sous-titres ne respectent pas ces normes, même si la valeur est à False.
* `CELERY_BROKER_URL` - > valeur par défaut : `redis://redis.localhost:6379/5` + > default value: `redis://redis.localhost:6379/5` >> URL du courtier de messages où Celery stocke les ordres d’encodage et de transcription.
* `CELERY_TO_ENCODE` - > valeur par défaut : `False` + > default value: `False` >> Utilisation de Celery pour la gestion des taches d’encodage
* `DEFAULT_LANG_TRACK` - > valeur par défaut : `fr` + > default value: `fr` >> langue par défaut pour l’ajout de piste à une vidéo.
* `EMAIL_ON_ENCODING_COMPLETION` - > valeur par défaut : `True` + > default value: `True` >> Si True, un courriel est envoyé aux managers
>> et à l’auteur (si DEBUG est à False) à la fin de l’encodage.
* `EMAIL_ON_TRANSCRIPTING_COMPLETION` - > valeur par défaut : `True` + > default value: `True` >> Si True, un courriel est envoyé aux managers
>> et à l’auteur (si DEBUG est à False) à la fin de la transcription.
* `ENCODE_STUDIO` - > valeur par défaut : `start_encode_studio` + > default value: `start_encode_studio` >> Fonction appelée pour lancer l’encodage du studio (merge and cut).
* `ENCODE_VIDEO` - > valeur par défaut : `start_encode` + > default value: `start_encode` >> Fonction appelée pour lancer l’encodage des vidéos direct par thread ou distant par celery
* `ENCODING_CHOICES` - > valeur par défaut : `()` + > default value: `()` >> Encodage possible sur la plateforme. Associé à un rendu dans le cas d’une vidéo.
>> >> ```python @@ -2085,13 +2099,13 @@ Attention, il faut configurer Celery pour l’envoi des instructions pour l’en >> ``` >> * `ENCODING_TRANSCODING_CELERY_BROKER_URL` - > valeur par défaut : `False` + > default value: `False` >> >> Il faut renseigner l’url du redis sur lequel Celery
>> va chercher les ordres d’encodage et de transcription
>> par exemple : "redis://redis.localhost:6379/7"
* `FORMAT_CHOICES` - > valeur par défaut : `()` + > default value: `()` >> Format d’encodage réalisé sur la plateforme.
>> >> ```python @@ -2106,21 +2120,21 @@ Attention, il faut configurer Celery pour l’envoi des instructions pour l’en >> ``` >> * `USE_REMOTE_ENCODING_TRANSCODING` - > valeur par défaut : `False` + > default value: `False` >> >> Si True, active l’encodage et la transcription sur un environnement distant via redis+celery
* `POD_API_URL` - > valeur par défaut : `` + > default value: `` >> Adresse de l’API rest à appeler en fin d’encodage
>> distant ou de transcription à distance.
>> Exemple : `https://pod.univ.fr/rest/`
* `POD_API_TOKEN` - > valeur par défaut : `` + > default value: `` >> Token d’authentification utilisé pour l’appel
>> en fin d’encodage distant ou de transcription à distance.
>> Pour le créer, il faut aller dans la partie Admin > Jeton d’authentification > token.
* `VIDEO_RENDITIONS` - > valeur par défaut : `[]` + > default value: `[]` >> Rendu serializé pour l’encodage des videos.
>> Cela permet de pouvoir encoder les vidéos sans l’environnement de Pod.
>> @@ -2161,20 +2175,20 @@ Attention, il faut configurer Celery pour l’envoi des instructions pour l’en ### Configuration de l’application search * `ES_INDEX` - > valeur par défaut : `pod` + > default value: `pod` >> Valeur pour l’index de ElasticSearch
* `ES_MAX_RETRIES` - > valeur par défaut : `10` + > default value: `10` >> Valeur max de tentatives pour ElasticSearch.
* `ES_TIMEOUT` - > valeur par défaut : `30` + > default value: `30` >> Valeur de timeout pour ElasticSearch.
* `ES_URL` - > valeur par défaut : `["http://elasticsearch.localhost:9200/"]` + > default value: `["http://elasticsearch.localhost:9200/"]` >> Adresse du ou des instances d’Elasticsearch utilisées pour
>> l’indexation et la recherche de vidéo.
* `ES_VERSION` - > valeur par défaut : `8` + > default value: `8` >> Version d’ElasticSearch.
>> valeurs possibles : `8`, correspondant à la version du serveur Elasticsearch utilisé.
>> Attention, le paquet elasticsearch-py doit correspondre à la version du serveur.
@@ -2182,7 +2196,7 @@ Attention, il faut configurer Celery pour l’envoi des instructions pour l’en >> Voir [elasticsearch-py.readthedocs.io](https://elasticsearch-py.readthedocs.io/)
>> pour plus d’information.
* `ES_OPTIONS` - > valeur par défaut : `{}` + > default value: `{}` >> Options d’ElasticSearch, notamment utilisées pour ES8 en SSL et avec un user en paramètre
>> Voir [www.elastic.co](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/config.html)
>> pour plus d’informations.
@@ -2194,23 +2208,23 @@ Aucune instruction ne persiste dans Pod, elles sont toutes envoyées au LRS para Attention, il faut configurer Celery pour l’envoi des instructions.
* `USE_XAPI` - > valeur par défaut : `False` + > default value: `False` >> >> Activation de l’application xAPI
* `XAPI_ANONYMIZE_ACTOR` - > valeur par défaut : `True` + > default value: `True` >> >> Si False, le nom de l’utilisateur sera stocké en clair dans les statements xAPI,
>> si True, son nom d’utilisateur sera anonymisé
* `XAPI_LRS_LOGIN` - > valeur par défaut : `` + > default value: `` >> >> identifiant de connexion du LRS pour l’envoi des statements
* `XAPI_LRS_PWD` - > valeur par défaut : `` + > default value: `` >> >> mot de passe de connexion du LRS pour l’envoi des statements
* `XAPI_LRS_URL` - > valeur par défaut : `` + > default value: `` >> >> URL de destination pour l’envoi des statements. I.E. : `https://ralph.univ.fr/xAPI/statements`
diff --git a/dockerfile-dev-with-volumes/README.adoc b/dockerfile-dev-with-volumes/README.adoc index 69c0cdaab7..37ff735344 100755 --- a/dockerfile-dev-with-volumes/README.adoc +++ b/dockerfile-dev-with-volumes/README.adoc @@ -52,7 +52,7 @@ DJANGO_SUPERUSER_PASSWORD= DJANGO_SUPERUSER_EMAIL= ELASTICSEARCH_TAG=elasticsearch:8.17.3 NODE_TAG=node:23 -PYTHON_TAG=python:3.9-bookworm +PYTHON_TAG=python:3.12-bookworm REDIS_TAG=redis:alpine3.21 DOCKER_ENV=light ---- diff --git a/dockerfile-dev-with-volumes/pod-back/Dockerfile b/dockerfile-dev-with-volumes/pod-back/Dockerfile index 5309c95fa1..2364b10281 100755 --- a/dockerfile-dev-with-volumes/pod-back/Dockerfile +++ b/dockerfile-dev-with-volumes/pod-back/Dockerfile @@ -5,7 +5,7 @@ #------------------------------------------------------------------------------------------------------------------------------ # Conteneur node ARG NODE_VERSION=23 -ARG PYTHON_VERSION=3.9 +ARG PYTHON_VERSION=3.12 FROM $NODE_VERSION AS source-build-js WORKDIR /tmp/pod diff --git a/dockerfile-dev-with-volumes/pod-encode/Dockerfile b/dockerfile-dev-with-volumes/pod-encode/Dockerfile index b280e5a173..f6365fec5a 100755 --- a/dockerfile-dev-with-volumes/pod-encode/Dockerfile +++ b/dockerfile-dev-with-volumes/pod-encode/Dockerfile @@ -4,7 +4,7 @@ # (")_(") #------------------------------------------------------------------------------------------------------------------------------ -ARG PYTHON_VERSION=3.9 +ARG PYTHON_VERSION=3.12 #------------------------------------------------------------------------------------------------------------------------------ # Conteneur python diff --git a/dockerfile-dev-with-volumes/pod-transcript/Dockerfile b/dockerfile-dev-with-volumes/pod-transcript/Dockerfile index 355be67e21..defef2725e 100755 --- a/dockerfile-dev-with-volumes/pod-transcript/Dockerfile +++ b/dockerfile-dev-with-volumes/pod-transcript/Dockerfile @@ -4,7 +4,7 @@ # (")_(") #------------------------------------------------------------------------------------------------------------------------------ # Conteneur node -ARG PYTHON_VERSION=3.9 +ARG PYTHON_VERSION=3.12 #------------------------------------------------------------------------------------------------------------------------------ # Conteneur python diff --git a/dockerfile-dev-with-volumes/pod-xapi/Dockerfile b/dockerfile-dev-with-volumes/pod-xapi/Dockerfile index f8b0f5a4b5..87285e9c1f 100755 --- a/dockerfile-dev-with-volumes/pod-xapi/Dockerfile +++ b/dockerfile-dev-with-volumes/pod-xapi/Dockerfile @@ -4,7 +4,7 @@ # (")_(") #------------------------------------------------------------------------------------------------------------------------------ # Conteneur node -ARG PYTHON_VERSION=3.9 +ARG PYTHON_VERSION=3.12 #------------------------------------------------------------------------------------------------------------------------------ # Conteneur python diff --git a/dockerfile-dev-with-volumes/pod/Dockerfile b/dockerfile-dev-with-volumes/pod/Dockerfile index b997956c2d..0db4832ffd 100755 --- a/dockerfile-dev-with-volumes/pod/Dockerfile +++ b/dockerfile-dev-with-volumes/pod/Dockerfile @@ -5,7 +5,7 @@ #------------------------------------------------------------------------------------------------------------------------------ # Conteneur node ARG NODE_VERSION=23 -ARG PYTHON_VERSION=3.9 +ARG PYTHON_VERSION=3.12 FROM $NODE_VERSION AS source-build-js WORKDIR /tmp/pod diff --git a/pod/ai_enhancement/forms.py b/pod/ai_enhancement/forms.py index b90981ec3a..c0f3f5df5c 100644 --- a/pod/ai_enhancement/forms.py +++ b/pod/ai_enhancement/forms.py @@ -85,12 +85,10 @@ class Meta: ) tags = TagField( - help_text=_( - """ + help_text=_(""" Please choose tags for your video. Separate tags with spaces, enclose the tags consist of several words in quotation marks. - """ - ), + """), verbose_name=_("Tags"), ) diff --git a/pod/ai_enhancement/templatetags/ai_enhancement_template_tags.py b/pod/ai_enhancement/templatetags/ai_enhancement_template_tags.py index 743e064e75..eeea78f381 100644 --- a/pod/ai_enhancement/templatetags/ai_enhancement_template_tags.py +++ b/pod/ai_enhancement/templatetags/ai_enhancement_template_tags.py @@ -10,7 +10,6 @@ ) from pod.video.models import Video - USE_AI_ENHANCEMENT = getattr(settings, "USE_AI_ENHANCEMENT", False) AI_ENHANCEMENT_TO_STAFF_ONLY = getattr(settings, "AI_ENHANCEMENT_TO_STAFF_ONLY", True) diff --git a/pod/authentication/signals.py b/pod/authentication/signals.py index abae16a571..74a08d3e7b 100644 --- a/pod/authentication/signals.py +++ b/pod/authentication/signals.py @@ -37,11 +37,8 @@ def cas_user_logout_callback(sender, **kwargs) -> None: """Callback for CAS user logout signal.""" args = {} args.update(kwargs) - print( - """cas_user_logout_callback: + print("""cas_user_logout_callback: user: %s session: %s ticket: %s - """ - % (args.get("user"), args.get("session"), args.get("ticket")) - ) + """ % (args.get("user"), args.get("session"), args.get("ticket"))) diff --git a/pod/authentication/tests/test_views.py b/pod/authentication/tests/test_views.py index 816e2caa95..5b3b64f70e 100644 --- a/pod/authentication/tests/test_views.py +++ b/pod/authentication/tests/test_views.py @@ -49,10 +49,8 @@ def test_authentication_logout(self) -> None: response = self.client.post("/authentication_logout/") self.assertRedirects(response, "/accounts/logout/?next=/", target_status_code=302) - print( - " ---> test_authentication_logout \ - of authenticationViewsTestCase: OK!" - ) + print(" ---> test_authentication_logout \ + of authenticationViewsTestCase: OK!") def test_userpicture(self) -> None: self.client = Client() diff --git a/pod/cut/views.py b/pod/cut/views.py index 5ddb985d45..00f5b8380d 100644 --- a/pod/cut/views.py +++ b/pod/cut/views.py @@ -1,27 +1,23 @@ """Esup-Pod video cutting app views.""" -from django.shortcuts import render -from pod.main.views import in_maintenance -from django.shortcuts import redirect -from django.urls import reverse -from django.shortcuts import get_object_or_404 -from django.contrib.sites.shortcuts import get_current_site -from django.contrib.auth.decorators import login_required -from django.utils.translation import gettext as _ +from django.conf import settings from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import PermissionDenied from django.http import QueryDict +from django.shortcuts import get_object_or_404, redirect, render +from django.urls import reverse +from django.utils.translation import gettext as _ from django.views.decorators.csrf import csrf_protect -from pod.video_encode_transcript.encode import start_encode +from pod.main.views import in_maintenance from pod.video.models import Video -from .models import CutVideo from .forms import CutVideoForm +from .models import CutVideo from .utils import clean_database -from django.conf import settings - RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY = getattr( settings, "RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY", False ) @@ -77,7 +73,8 @@ def cut_video(request, slug): clean_database(video.id) - start_encode(video.id) + video.launch_encode = True + video.save() messages.add_message(request, messages.SUCCESS, _("The cut was made.")) return redirect(reverse("video:dashboard")) diff --git a/pod/dressing/utils.py b/pod/dressing/utils.py index c5340e0014..757e053e7c 100644 --- a/pod/dressing/utils.py +++ b/pod/dressing/utils.py @@ -3,7 +3,7 @@ import os from .models import Dressing from django.conf import settings -from django.db.models import Q +from django.db.models import Q, QuerySet from django.core.handlers.wsgi import WSGIRequest @@ -34,7 +34,7 @@ def get_dressing_input(dressing: Dressing, ffmpeg_dressing_input: str) -> str: return command -def get_dressings(user, accessgroup_set) -> list: +def get_dressings(user, accessgroup_set) -> QuerySet[Dressing]: """ Return the list of dressings that the user can use. diff --git a/pod/enrichment/tests/test_views.py b/pod/enrichment/tests/test_views.py index f2761bb927..466833217d 100644 --- a/pod/enrichment/tests/test_views.py +++ b/pod/enrichment/tests/test_views.py @@ -1,6 +1,7 @@ # gitguardian:ignore """Esup-Pod unit tests for enrichment views.""" + from django.test import TestCase from django.contrib.auth import authenticate from django.contrib.auth.models import User diff --git a/pod/import_video/views.py b/pod/import_video/views.py index 986697ccb5..05b20aa037 100644 --- a/pod/import_video/views.py +++ b/pod/import_video/views.py @@ -50,7 +50,6 @@ from pod.main.tasks import task_start_bbb_presentation_encode_and_upload_to_pod from pod.main.tasks import task_start_bbb_presentation_encode_and_move_to_destination - RESTRICT_EDIT_IMPORT_VIDEO_ACCESS_TO_STAFF_ONLY = getattr( settings, "RESTRICT_EDIT_IMPORT_VIDEO_ACCESS_TO_STAFF_ONLY", True ) diff --git a/pod/live/templates/live/event-iframe.html b/pod/live/templates/live/event-iframe.html index d8c218b891..25f7a03de4 100644 --- a/pod/live/templates/live/event-iframe.html +++ b/pod/live/templates/live/event-iframe.html @@ -3,175 +3,181 @@ - - + + - {{ TITLE_SITE }} - {% block page_title %}{{ event.title }}{% endblock page_title %} - + {{ TITLE_SITE }} - {% block page_title %}{{ event.title }}{% endblock page_title %} + - + - {% block page_extra_head %} - {% include 'videos/video-header.html' %} - {% endblock page_extra_head %} + {% block page_extra_head %} + {% include 'videos/video-header.html' %} + {% endblock page_extra_head %} - {% if event.broadcaster.enable_viewer_count %} - + {% if event.broadcaster.enable_viewer_count %} + - {% if event.is_current %} - - {% endif %} - {% endif %} + {% if event.is_current %} + + {% endif %} + {% endif %} - + - - - + + + + - - + + {% block page_content %} - {% csrf_token %} -

 {{ event.title|title }}

- {% if form %} - {% include 'live/event-form.html' %} - {% else %} -
- {% if event.is_past %} -
- {% trans "Event is finished at:" %} {{ event.end_date }} -
-
- {% elif event.is_coming %} -
- {% blocktrans with start_date=event.start_date end_date=event.end_date %}The event is scheduled from {{start_date}} to {{end_date}}{% endblocktrans %} -
- {% if event.video_on_hold.is_video %} -
- -
-
-

{{ event.title }}

- -
-
- {% include 'live/event-info.html' %} -
-
-
- {# event thumbnail #} - {% elif event.thumbnail %} - {{ event.title }} - {# broadcaster thumbnail #} - {% elif event.broadcaster.poster %} - {{ event.title }} - {% endif %} - - {% elif event.is_current %} -
-
-
- -
- {# ajouter ici l'event.aside_iframe_url au besoin #} - {# ajouter ici le chatBBB au besoin #} -
-
- {# ajouter ici l'event.iframe_url au besoin #} - - - -
-
-

{{ event.title }}

- -
-
- {% include 'live/event-info.html' %} -
-
- {% endif %} + {% csrf_token %} +

+  {{ event.title|title }} +

+ {% if form %} + {% include 'live/event-form.html' %} + {% else %} +
+ {% if event.is_past %} +
+ {% trans "Event is finished at:" %} {{ event.end_date }} +
- {% endif %} + {% elif event.is_coming %} +
+ {% blocktrans with start_date=event.start_date end_date=event.end_date %}The event is scheduled from {{start_date}} to {{end_date}}{% endblocktrans %} +
+ {% if event.video_on_hold.is_video %} +
+ +
+
+

{{ event.title }}

+ +
+
+ {% include 'live/event-info.html' %} +
+
+
+ {# event thumbnail #} + {% elif event.thumbnail %} + {{ event.title }} + {# broadcaster thumbnail #} + {% elif event.broadcaster.poster %} + {{ event.title }} + {% endif %} + + {% elif event.is_current %} +
+
+
+ +
+ {# ajouter ici l'event.aside_iframe_url au besoin #} + {# ajouter ici le chatBBB au besoin #} +
+
+ {# ajouter ici l'event.iframe_url au besoin #} + + + + + {% endif %} +
+ {% endif %} + + {% block more_script %} + {% include 'live/event-script.html' %} - {% block more_script %} - {% include 'live/event-script.html' %} - - {# affichage du bouton d'info sur le player #} - {% if event.is_coming and event.video_on_hold.is_video or event.is_current %} - - + {# affichage du bouton d'info sur le player #} + {% if event.is_coming and event.video_on_hold.is_video or event.is_current %} + + + {% endif %} + + {% endblock more_script %} + {% if TRACKING_TEMPLATE %}{% include TRACKING_TEMPLATE %}{% endif %} {% endblock page_content %} diff --git a/pod/live/templates/live/event-info.html b/pod/live/templates/live/event-info.html index 3fcf073189..9c7b60ce7e 100644 --- a/pod/live/templates/live/event-info.html +++ b/pod/live/templates/live/event-info.html @@ -13,10 +13,13 @@
-

 {% trans 'Infos' %}

+

{% trans 'Infos' %}

@@ -55,7 +69,8 @@

 {%

diff --git a/pod/locale/fr/LC_MESSAGES/django.mo b/pod/locale/fr/LC_MESSAGES/django.mo deleted file mode 100644 index 64599674eb..0000000000 Binary files a/pod/locale/fr/LC_MESSAGES/django.mo and /dev/null differ diff --git a/pod/locale/fr/LC_MESSAGES/django.po b/pod/locale/fr/LC_MESSAGES/django.po index 5b3832b628..d875661a18 100644 --- a/pod/locale/fr/LC_MESSAGES/django.po +++ b/pod/locale/fr/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: Pod\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-12-16 23:10+0000\n" +"POT-Creation-Date: 2026-02-27 14:05+0100\n" "PO-Revision-Date: \n" "Last-Translator: obado \n" "Language-Team: Pod Team cotech-esup-pod@esup-portail.org\n" @@ -193,7 +193,8 @@ msgstr "Enrichissements AI" #: pod/quiz/templates/quiz/create_edit_quiz.html #: pod/quiz/templates/quiz/video_quiz.html pod/recorder/models.py #: pod/video/models.py pod/video/templates/channel/channel.html -#: pod/video/templates/videos/videos.html pod/video_encode_transcript/models.py +#: pod/video/templates/videos/videos.html pod/video_encode_transcript/admin.py +#: pod/video_encode_transcript/models.py msgid "Video" msgstr "Vidéo" @@ -234,8 +235,8 @@ msgid "Enter the ID of the enhancement in Aristote" msgstr "Entrez l’identifiant de l’enrichissement dans Aristote" #: pod/ai_enhancement/templates/choose_video_element.html -#: pod/dressing/models.py pod/video/apps.py pod/video/models.py -#: pod/video/templates/videos/video_breadcrumbs.html +#: pod/dressing/models.py pod/video/admin.py pod/video/apps.py +#: pod/video/models.py pod/video/templates/videos/video_breadcrumbs.html #: pod/video/templates/videos/videos.html msgid "Videos" msgstr "Vidéos" @@ -404,7 +405,8 @@ msgstr "" #: pod/main/views.py pod/playlist/views.py pod/video/feeds.py #: pod/video/management/commands/check_obsolete_videos.py pod/video/models.py #: pod/video/templates/videos/video-script.html pod/video/utils.py -#: pod/video/views.py pod/video_encode_transcript/utils.py +#: pod/video/views.py pod/video_encode_transcript/runner_manager.py +#: pod/video_encode_transcript/utils.py msgid "Home" msgstr "Accueil" @@ -433,7 +435,7 @@ msgid "" "for improvement can’t be available on %(site_title)s." msgstr "" "Une erreur s’est produite sur le traitement par l’IA de " -"« %(content_title)s ». Les suggestions d’amélioration ne peuvent pas être " +"« %(content_title)s ». Les suggestions d’amélioration ne peuvent pas être " "disponibles sur %(site_title)s." #: pod/ai_enhancement/utils.py @@ -457,7 +459,7 @@ msgid "" "%(site_title)s." msgstr "" "Une erreur s’est produite sur l’amélioration par l’IA de " -"« %(content_title)s » sur %(site_title)s." +"« %(content_title)s » sur %(site_title)s." #: pod/ai_enhancement/utils.py pod/live/utils.py pod/meeting/utils.py #: pod/video_encode_transcript/utils.py @@ -758,8 +760,8 @@ msgid "The file must be in VTT format." msgstr "Le fichier doit être au format VTT." #: pod/chapter/models.py pod/completion/models.py pod/enrichment/models.py -#: pod/playlist/templates/playlist/playlist_card.html pod/video/models.py -#: pod/video_encode_transcript/utils.py +#: pod/playlist/templates/playlist/playlist_card.html pod/video/admin.py +#: pod/video/models.py pod/video_encode_transcript/utils.py msgid "video" msgstr "vidéo" @@ -1351,6 +1353,12 @@ msgstr "" "Il y a un chevauchement avec la superposition {0}, veuillez changer son " "temps de début et/ou de fin." +#: pod/completion/permissions/video.py +#, python-brace-format +msgid "{func.__name__}: Permission denied for user {current_user.pk}." +msgstr "" +"{func.__name__} : Permission refusée pour l’utilisateur {current_user.pk}." + #: pod/completion/templates/contributor/form_contributor.html #: pod/completion/templates/document/form_document.html #: pod/completion/templates/overlay/form_overlay.html @@ -1728,8 +1736,8 @@ msgstr "" #: pod/completion/templates/video_completion.html msgid "Subtitle and/or caption file(s) must be in “.vtt” format." msgstr "" -"Les fichiers de sous-titres et/ou de légendes doivent être au format " -"« .vtt »." +"Les fichiers de sous-titres et/ou de légendes doivent être au format « ." +"vtt »." #: pod/completion/templates/video_completion.html msgid "You can use" @@ -2626,7 +2634,7 @@ msgid "User who create this recording" msgstr "Utilisateur qui a créé cet enregistrement" #: pod/import_video/models.py pod/meeting/models.py pod/playlist/models.py -#: pod/video/models.py +#: pod/video/models.py pod/video_encode_transcript/models.py msgid "Site" msgstr "Site" @@ -6042,7 +6050,7 @@ msgstr "Journal des sessions des réunions" msgid "Meeting session logs" msgstr "Journaux des sessions des réunions" -#: pod/meeting/models.py +#: pod/meeting/models.py pod/video_encode_transcript/admin.py msgid "Recording ID" msgstr "Identifiant de l’enregistrement" @@ -6073,7 +6081,7 @@ msgstr "Exemple de format : %(url)s" #: pod/meeting/models.py msgid "Bearer token for the SIPMediaGW server." -msgstr "Jeton Bearer pour le serveur SIPMediaGW." +msgstr "Jeton porteur pour le serveur SIPMediaGW." #: pod/meeting/models.py msgid "Example format: 1234" @@ -6185,6 +6193,7 @@ msgstr "" "Pour supprimer la réunion, veuillez cocher la case et cliquer sur supprimer." #: pod/meeting/templates/meeting/filter_aside.html pod/video/views.py +#: pod/video_encode_transcript/admin.py msgid "Status" msgstr "Statut" @@ -6578,6 +6587,7 @@ msgid "Has user joined?" msgstr "Utilisateurs connectés ?" #: pod/meeting/views.py pod/recorder/models.py +#: pod/video_encode_transcript/admin.py pod/video_encode_transcript/models.py msgid "Recording" msgstr "Enregistrement" @@ -6984,6 +6994,15 @@ msgstr "Protégé par un mot de passe" msgid "Private" msgstr "Privé" +#: pod/playlist/models.py +#, python-brace-format +msgid "" +"Please choose a title between 1 and {__MAX_LENGTH_FOR_PLAYLIST_NAME__} " +"characters." +msgstr "" +"Veuillez entrer un titre contenant entre 1 et " +"{__MAX_LENGTH_FOR_PLAYLIST_NAME__} caractères." + #: pod/playlist/models.py msgid "" "Selecting this setting causes your playlist to be promoted on the page " @@ -7382,10 +7401,6 @@ msgstr "Vous ne pouvez pas accéder à cette liste de lecture." msgid "The playlist has been deleted." msgstr "La liste de lecture a été supprimée." -#: pod/playlist/views.py -msgid "Delete the playlist" -msgstr "Supprimer la liste de lecture" - #: pod/playlist/views.py #, python-format msgid "Edit the playlist “%(pname)s”" @@ -7786,7 +7801,7 @@ msgstr "Veuillez choisir si les réponses correctes seront affichées ou non." msgid "Quizzes" msgstr "Quiz" -#: pod/quiz/models.py pod/quiz/tests/test_models.py +#: pod/quiz/models.py msgid "Quiz of video" msgstr "Quiz de la vidéo" @@ -8106,6 +8121,27 @@ msgstr "Vous ne pouvez pas éditer ce quiz." msgid "Quiz edition for the video “%s”" msgstr "Modification du quiz pour la vidéo « %s »" +#: pod/recorder/admin.py +msgid "Encode selected recordings and create new video" +msgstr "Encoder les enregistrements sélectionnés et créer une nouvelle vidéo" + +#: pod/recorder/admin.py +#, python-brace-format +msgid "Studio recording {item.id} encoding started" +msgstr "Encodage de l’enregistrement en studio {item.id} démarré" + +#: pod/recorder/admin.py +#, python-brace-format +msgid "Recording {item.id} is not a studio recording and can’t be encoded" +msgstr "" +"L’enregistrement {item.id} n’est pas un enregistrement studio et ne peut pas " +"être encodé." + +#: pod/recorder/admin.py +#, python-brace-format +msgid "Error for {item}: {e}" +msgstr "Erreur pour {item} : {e}" + #: pod/recorder/admin.py msgid "Delete selected Recording file treatments + source files" msgstr "" @@ -8376,6 +8412,7 @@ msgstr "Taille du fichier" #: pod/video/templates/videos/dashboard.html #: pod/video/templates/videos/video_row_select.html #: pod/video/templates/videos/video_sort_select.html +#: pod/video_encode_transcript/models.py msgid "Date added" msgstr "Date d’ajout" @@ -9881,6 +9918,7 @@ msgid "Clear all filters" msgstr "Effacer tous les filtres" #: pod/video/templates/videos/dashboard.html +#: pod/video/templates/videos/filter_aside_category.html msgid "Clear filters" msgstr "Effacer les filtres" @@ -10049,8 +10087,8 @@ msgid "Original version" msgstr "Version originale" #: pod/video/templates/videos/link_video_dropdown_menu.html -msgid "Enhance the video with AI" -msgstr "Améliorer la vidéo avec l’IA" +msgid "Complete the video with AI" +msgstr "Compléter la vidéo avec l’IA" #: pod/video/templates/videos/link_video_dropdown_menu.html msgid "Complete the video" @@ -10592,6 +10630,14 @@ msgstr "La vidéo est actuellement en attente du traitement par l’IA Aristote. msgid "The form does not contain a video." msgstr "La fiche ne contient pas de vidéo." +#: pod/video/templates/videos/video_queue_runner_manager.html +msgid "Queue rank:" +msgstr "Rang dans la file d’attente :" + +#: pod/video/templates/videos/video_queue_runner_manager.html +msgid "Total number of videos in the queue:" +msgstr "Nombre total de vidéos dans la file d’attente :" + #: pod/video/templates/videos/video_row_select.html msgid "Selected" msgstr "Sélectionné" @@ -10950,14 +10996,94 @@ msgstr "Erreur serveur lors de la récupération des vidéos de l’utilisateur. msgid "Server error while processing filter." msgstr "Erreur serveur lors du traitement du filtre." +#: pod/video_encode_transcript/admin.py pod/video_encode_transcript/models.py +msgid "resolution" +msgstr "résolution" + +#: pod/video_encode_transcript/admin.py pod/video_encode_transcript/models.py +msgid "Active" +msgstr "Actif" + +#: pod/video_encode_transcript/admin.py +msgid "Inactive" +msgstr "Inactif" + +#: pod/video_encode_transcript/admin.py +#: pod/video_encode_transcript/templates/admin_test_connection.html +msgid "Runner administration" +msgstr "Administration des runners" + +#: pod/video_encode_transcript/admin.py +msgid "Open runner administration" +msgstr "Ouvrir l’administration des runners" + +#: pod/video_encode_transcript/admin.py +msgid "Runner manager not found." +msgstr "Runner manager non trouvé." + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "" +"Unable to reach runner manager '%(name)s' at %(url)s. Check the URL and " +"network access. Error: %(error)s" +msgstr "" +"Impossible de joindre le runner manager « %(name)s » à l’adresse %(url)s. " +"Vérifiez l’URL et l’accès au réseau. Erreur : %(error)s" + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "" +"Runner manager '%(name)s' responded but rejected authentication (HTTP " +"%(status)s). Check the bearer token." +msgstr "" +"Le runner manager « %(name)s » a répondu mais a rejeté l’authentification " +"(HTTP %(status)s). Vérifiez le jeton porteur." + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "Connection to runner manager '%(name)s' succeeded (HTTP %(status)s)." +msgstr "Connexion au runner manager « %(name)s » réussie (HTTP %(status)s)." + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "" +"Runner manager '%(name)s' is reachable but endpoint %(url)s was not found " +"(HTTP 404). Check the configured URL." +msgstr "" +"Le runner manager « %(name)s » est accessible, mais le point de terminaison " +"%(url)s est introuvable (HTTP 404). Vérifiez l’URL configurée." + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "" +"Runner manager '%(name)s' is reachable but returned an unexpected response " +"(HTTP %(status)s)." +msgstr "" +"Le runner manager « %(name)s » est accessible, mais a renvoyé une réponse " +"inattendue (HTTP %(status)s)." + +#: pod/video_encode_transcript/admin.py +msgid "Video ID" +msgstr "Identifiant de la vidéo" + +#: pod/video_encode_transcript/admin.py +msgid "Restart selected tasks" +msgstr "Redémarrer les tâches sélectionnées" + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "%(count)s task(s) relaunched immediately." +msgstr "%(count)s tâche(s) relancée(s) immédiatement." + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "%(count)s task(s) skipped (duplicate or missing source)." +msgstr "%(count)s tâche(s) ignorée(s) (duplicata ou source manquante)." + #: pod/video_encode_transcript/apps.py msgid "Video encoding and transcription" msgstr "Encodage et transcription de vidéos" -#: pod/video_encode_transcript/models.py -msgid "resolution" -msgstr "résolution" - #: pod/video_encode_transcript/models.py msgid "" "Please use the only format x. i.e.: 640x360 or 1280x720 or " @@ -11058,6 +11184,142 @@ msgstr "Liste de lecture" msgid "Video Playlists" msgstr "Listes de lecture" +#: pod/video_encode_transcript/models.py +msgid "Runner manager name" +msgstr "Nom du runner manager" + +#: pod/video_encode_transcript/models.py +msgid "Priority" +msgstr "Priorité" + +#: pod/video_encode_transcript/models.py +msgid "Priority of the runner manager. Lower values indicate higher priority." +msgstr "" +"Priorité du runner manager. Les valeurs les plus basses indiquent une " +"priorité plus élevée." + +#: pod/video_encode_transcript/models.py +msgid "If checked, this runner manager can be used to process tasks." +msgstr "" +"Si cette option est cochée, ce runner manager peut être utilisé pour traiter " +"des tâches." + +#: pod/video_encode_transcript/models.py +msgid "URL of the runner manager" +msgstr "URL du runner manager" + +#: pod/video_encode_transcript/models.py +msgid "Example format: https://manager.univ.fr:port/" +msgstr "Exemple de format : https://manager.univ.fr:port/" + +#: pod/video_encode_transcript/models.py +msgid "Bearer token for the runner manager." +msgstr "Jeton porteur pour le runner manager." + +#: pod/video_encode_transcript/models.py +msgid "Example format: 6YqG_73xt-9s8v5aBz" +msgstr "Exemple de format : 6YqG_73xt-9s8v5aBz" + +#: pod/video_encode_transcript/models.py +msgid "Runner manager" +msgstr "Runner manager" + +#: pod/video_encode_transcript/models.py +msgid "Runner managers" +msgstr "Runner managers" + +#: pod/video_encode_transcript/models.py +msgid "Encoding task" +msgstr "Tâche d’encodage" + +#: pod/video_encode_transcript/models.py +msgid "Studio task" +msgstr "Tâche Studio" + +#: pod/video_encode_transcript/models.py +msgid "Transcription task" +msgstr "Tâche de transcription" + +#: pod/video_encode_transcript/models.py +msgid "Task type" +msgstr "Type de tâche" + +#: pod/video_encode_transcript/models.py +msgid "Task pending" +msgstr "Tâche en attente" + +#: pod/video_encode_transcript/models.py +msgid "Task in progress" +msgstr "Tâche en cours" + +#: pod/video_encode_transcript/models.py +msgid "Task completed" +msgstr "Tâche terminée" + +#: pod/video_encode_transcript/models.py +msgid "Task failed" +msgstr "Tâche échouée" + +#: pod/video_encode_transcript/models.py +msgid "Task timeouted" +msgstr "Tâche expirée" + +#: pod/video_encode_transcript/models.py +msgid "Task status" +msgstr "Statut de la tâche" + +#: pod/video_encode_transcript/models.py +msgid "Task identifier from runner manager" +msgstr "Identifiant de tâche du runner manager" + +#: pod/video_encode_transcript/models.py +msgid "Identifier of the task provided by the runner manager" +msgstr "Identifiant de la tâche fourni par le runner manager" + +#: pod/video_encode_transcript/models.py +msgid "Video associated to the task" +msgstr "Vidéo associée à la tâche" + +#: pod/video_encode_transcript/models.py +msgid "Studio recording associated to the task" +msgstr "Enregistrement studio associé à la tâche" + +#: pod/video_encode_transcript/models.py +msgid "Runner manager that manages this task" +msgstr "Runner manager qui gère cette tâche" + +#: pod/video_encode_transcript/models.py +msgid "Runner manager that achieves this task" +msgstr "Runner manager qui accomplit cette tâche" + +#: pod/video_encode_transcript/models.py +msgid "Queue rank" +msgstr "Rang dans la file d’attente" + +#: pod/video_encode_transcript/models.py +msgid "Rank of the task in the pending queue" +msgstr "Rang de la tâche dans la file d’attente" + +#: pod/video_encode_transcript/models.py +msgid "Script output" +msgstr "Sortie du script" + +#: pod/video_encode_transcript/models.py +msgid "Output from the runner manager script" +msgstr "Sortie du script du runner manager" + +#: pod/video_encode_transcript/models.py +msgid "Task" +msgstr "Tâche" + +#: pod/video_encode_transcript/models.py +msgid "Tasks" +msgstr "Tâches" + +#: pod/video_encode_transcript/templates/admin_test_connection.html +msgid "Test connection" +msgstr "Tester la connexion" + #: pod/video_encode_transcript/utils.py msgid "Encoding" msgstr "Encodage" @@ -11127,9 +11389,9 @@ msgstr "Résultats de la recherche" msgid "Esup-Pod xAPI" msgstr "xAPI Esup-Pod" -#, fuzzy, python-format -#~| msgid "%(counter)s video found" -#~| msgid_plural "%(counter)s videos found" +#~ msgid "Delete the playlist" +#~ msgstr "Supprimer la liste de lecture" + #~ msgid "" #~ "\n" #~ " %(counter)s video found\n" @@ -11147,13 +11409,6 @@ msgstr "xAPI Esup-Pod" #~ msgid "Search by title" #~ msgstr "Rechercher par titre" -#~ msgid "" -#~ "Please choose a title between 1 and {__MAX_LENGTH_FOR_PLAYLIST_NAME__} " -#~ "characters." -#~ msgstr "" -#~ "Veuillez entrer un titre contenant entre 1 et " -#~ "{__MAX_LENGTH_FOR_PLAYLIST_NAME__} caractères." - #~ msgid "Clear selection" #~ msgstr "Effacer la sélection" diff --git a/pod/locale/fr/LC_MESSAGES/djangojs.mo b/pod/locale/fr/LC_MESSAGES/djangojs.mo deleted file mode 100644 index 04a0ee62a5..0000000000 Binary files a/pod/locale/fr/LC_MESSAGES/djangojs.mo and /dev/null differ diff --git a/pod/locale/nl/LC_MESSAGES/django.po b/pod/locale/nl/LC_MESSAGES/django.po index a1156ef2f5..e4746d614c 100644 --- a/pod/locale/nl/LC_MESSAGES/django.po +++ b/pod/locale/nl/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: Pod\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-12-16 23:10+0000\n" +"POT-Creation-Date: 2026-02-13 09:52+0100\n" "PO-Revision-Date: 2025-10-09 14:19+0200\n" "Last-Translator: obado \n" "Language-Team: \n" @@ -391,7 +391,8 @@ msgstr "" #: pod/main/views.py pod/playlist/views.py pod/video/feeds.py #: pod/video/management/commands/check_obsolete_videos.py pod/video/models.py #: pod/video/templates/videos/video-script.html pod/video/utils.py -#: pod/video/views.py pod/video_encode_transcript/utils.py +#: pod/video/views.py pod/video_encode_transcript/runner_manager.py +#: pod/video_encode_transcript/utils.py msgid "Home" msgstr "" @@ -2514,7 +2515,7 @@ msgid "User who create this recording" msgstr "" #: pod/import_video/models.py pod/meeting/models.py pod/playlist/models.py -#: pod/video/models.py +#: pod/video/models.py pod/video_encode_transcript/models.py msgid "Site" msgstr "" @@ -6212,6 +6213,7 @@ msgid "Has user joined?" msgstr "" #: pod/meeting/views.py pod/recorder/models.py +#: pod/video_encode_transcript/models.py msgid "Recording" msgstr "" @@ -6755,6 +6757,7 @@ msgstr "" #: pod/playlist/templates/playlist/playlist.html #: pod/playlist/templates/playlist/playlist_content.html #: pod/video/templates/channel/channel.html +#: pod/video/templates/videos/dashboard.html #: pod/video/templates/videos/videos.html #: pod/video_search/templates/search/search.html #, python-format @@ -7608,6 +7611,25 @@ msgstr "" msgid "Quiz edition for the video “%s”" msgstr "" +#: pod/recorder/admin.py +msgid "Encode selected recordings and create new video" +msgstr "" + +#: pod/recorder/admin.py +#, python-brace-format +msgid "Studio recording {item.id} encoding started" +msgstr "" + +#: pod/recorder/admin.py +#, python-brace-format +msgid "Recording {item.id} is not a studio recording and can't be encoded" +msgstr "" + +#: pod/recorder/admin.py +#, python-brace-format +msgid "Error for {item}: {e}" +msgstr "" + #: pod/recorder/admin.py msgid "Delete selected Recording file treatments + source files" msgstr "" @@ -7856,6 +7878,7 @@ msgstr "" #: pod/video/templates/videos/dashboard.html #: pod/video/templates/videos/video_row_select.html #: pod/video/templates/videos/video_sort_select.html +#: pod/video_encode_transcript/models.py msgid "Date added" msgstr "" @@ -9195,7 +9218,7 @@ msgstr "" #: pod/video/templates/videos/change_video_owner.html msgid "Filter by title" -msgstr "Filter op titel" +msgstr "" #: pod/video/templates/videos/change_video_owner.html msgid "Select video(s) to edit" @@ -9211,10 +9234,8 @@ msgid "Submit changes" msgstr "" #: pod/video/templates/videos/dashboard.html -#, fuzzy -#| msgid "Filter by title" msgid "Enter video title" -msgstr "Filter op titel" +msgstr "" #: pod/video/templates/videos/dashboard.html msgid "Use the search field to find a video by its title in your dashboard" @@ -9314,19 +9335,6 @@ msgstr "" msgid "Sort videos" msgstr "" -#: pod/video/templates/videos/dashboard.html -#, python-format -msgid "" -"\n" -" %(counter)s video found\n" -" " -msgid_plural "" -"\n" -" %(counter)s videos found\n" -" " -msgstr[0] "" -msgstr[1] "" - #: pod/video/templates/videos/filter_aside.html msgid "" "The videos on the left are automatically sorted according to the filters " @@ -9912,6 +9920,14 @@ msgstr "" msgid "The form does not contain a video." msgstr "" +#: pod/video/templates/videos/video_queue_runner_manager.html +msgid "Queue rank:" +msgstr "" + +#: pod/video/templates/videos/video_queue_runner_manager.html +msgid "Total number of videos in the queue:" +msgstr "" + #: pod/video/templates/videos/video_row_select.html msgid "Selected" msgstr "" @@ -10259,12 +10275,63 @@ msgstr "" msgid "Server error while processing filter." msgstr "" -#: pod/video_encode_transcript/apps.py -msgid "Video encoding and transcription" +#: pod/video_encode_transcript/admin.py pod/video_encode_transcript/models.py +msgid "resolution" msgstr "" -#: pod/video_encode_transcript/models.py -msgid "resolution" +#: pod/video_encode_transcript/admin.py +msgid "Runner manager not found." +msgstr "" + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "" +"Unable to reach runner manager '%(name)s' at %(url)s. Check the URL and " +"network access. Error: %(error)s" +msgstr "" + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "" +"Runner manager '%(name)s' responded but rejected authentication (HTTP " +"%(status)s). Check the bearer token." +msgstr "" + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "Connection to runner manager '%(name)s' succeeded (HTTP %(status)s)." +msgstr "" + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "" +"Runner manager '%(name)s' is reachable but endpoint %(url)s was not found " +"(HTTP 404). Check the configured URL." +msgstr "" + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "" +"Runner manager '%(name)s' is reachable but returned an unexpected response " +"(HTTP %(status)s)." +msgstr "" + +#: pod/video_encode_transcript/admin.py +msgid "Restart selected tasks" +msgstr "" + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "%(count)s task(s) relaunched immediately." +msgstr "" + +#: pod/video_encode_transcript/admin.py +#, python-format +msgid "%(count)s task(s) skipped (duplicate or missing source)." +msgstr "" + +#: pod/video_encode_transcript/apps.py +msgid "Video encoding and transcription" msgstr "" #: pod/video_encode_transcript/models.py @@ -10363,6 +10430,134 @@ msgstr "" msgid "Video Playlists" msgstr "" +#: pod/video_encode_transcript/models.py +msgid "Runner manager name" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Priority" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Priority of the runner manager. Lower values indicate higher priority." +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "URL of the runner manager" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Example format: https://manager.univ.fr:port/" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Bearer token for the runner manager." +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Example format: 6YqG_73xt-9s8v5aBz" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Runner manager" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Runner managers" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Encoding task" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Studio task" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Transcription task" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Task type" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Task pending" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Task in progress" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Task completed" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Task failed" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Task timeouted" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Task status" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Task identifier from runner manager" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Identifier of the task provided by the runner manager" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Video associated to the task" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Studio recording associated to the task" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Runner manager that manages this task" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Runner manager that achieves this task" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Queue rank" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Rank of the task in the pending queue" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Script output" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Output from the runner manager script" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Task" +msgstr "" + +#: pod/video_encode_transcript/models.py +msgid "Tasks" +msgstr "" + +#: pod/video_encode_transcript/templates/admin_test_connection.html +msgid "Test connection" +msgstr "" + #: pod/video_encode_transcript/utils.py msgid "Encoding" msgstr "Videocodering" diff --git a/pod/main/admin.py b/pod/main/admin.py index 9d1ac78ce8..72f8085010 100644 --- a/pod/main/admin.py +++ b/pod/main/admin.py @@ -14,7 +14,6 @@ from pod.main.models import AdditionalChannelTab from pod.main.models import Block - SITE_ID = getattr(settings, "SITE_ID", 1) content_widget = {} for key, value in settings.LANGUAGES: diff --git a/pod/main/configuration.json b/pod/main/configuration.json index d3d23548ea..abee22c6b6 100644 --- a/pod/main/configuration.json +++ b/pod/main/configuration.json @@ -3436,15 +3436,56 @@ "video_encode_transcript": { "description": { "en": [ - "" + "Application for video encoding and transcription.", + "It is possible to encode locally or remotely.", + "For remote encoding, it is preferable to use the encoding and transcription outsourcing system,", + "Esup-Runner, with the use of runner managers. Otherwise, it is also possible to use Celery in this case." ], "fr": [ "Application pour l’encodage et la transcription de vidéo.", "Il est possible d’encoder en local ou en distant.", - "Attention, il faut configurer Celery pour l’envoi des instructions pour l’encodage distant." + "Pour l’encodage distant, il est préférable d’utiliser le système d’externalisation des encodages et transcriptions, ", + "Esup-Runner, avec utilisation de runner managers. Sinon, il est aussi possible d’utiliser Celery dans ce cas là." ] }, "settings": { + "USE_RUNNER_MANAGER": { + "default_value": false, + "description": { + "en": [ + "If True, Pod uses an outsourcing system for encoding and transcription, ", + "Esup-Runner, with the use of runner managers. Encoding and transcription ", + "are entirely delegated to this system. See https://github.com/EsupPortail/esup-runner.", + "In this case, it is no longer necessary to configure Celery for remote encoding and transcription." + + ], + "fr": [ + "Si True, Pod utilise le système d’externalisation des encodages et transcriptions, ", + "Esup-Runner, avec utilisation de runner managers. Les encodages et transcriptions ", + "sont totalement délégués à ce système. Cf. https://github.com/EsupPortail/esup-runner.", + "Dans ce cas là, il n'est plus utile de configurer Celery pour l’encodage et la transcription à distance." + ] + }, + "pod_version_end": "", + "pod_version_init": "4.2.0" + }, + "RM_TASKS_DELETED_AFTER_DAYS": { + "default_value": 0, + "description": { + "en": [ + "Parameter used only when USE_RUNNER_MANAGER = True.", + "It corresponds to the number of days after which completed tasks are deleted ", + "from the database (0 means that completed tasks are kept indefinitely)." + ], + "fr": [ + "Paramètre utilisé seulement quand USE_RUNNER_MANAGER = True.", + "Il correspond au nombre de jours après lesquels les tâches terminées sont supprimées ", + "dans la base de données (0 signifiant que les tâches terminées sont conservées indéfiniment)." + ] + }, + "pod_version_end": "", + "pod_version_init": "4.2.0" + }, "CAPTIONS_STRICT_ACCESSIBILITY": { "default_value": false, "description": { @@ -5765,7 +5806,9 @@ "fr": [ "", "La plateforme Esup-Pod se base sur le framework Django écrit en Python.", - "Elle est compatible avec les versions 3.9, 3.10 et 3.12 de Python.", + "Elle est compatible avec les versions 3.10 et 3.12 de Python.", + "> Attention : à partir d’Esup-Pod version 4.2,", + " la version 3.9 de Python n’est plus supportée.", "", "**Django Version : 4.2 LTS**", "", diff --git a/pod/main/models.py b/pod/main/models.py index 55584299fe..9b203daadf 100644 --- a/pod/main/models.py +++ b/pod/main/models.py @@ -14,7 +14,6 @@ import mimetypes from tinymce.models import HTMLField - FILES_DIR = getattr(settings, "FILES_DIR", "files") diff --git a/pod/main/settings.py b/pod/main/settings.py index f995cc92eb..8ecb763896 100644 --- a/pod/main/settings.py +++ b/pod/main/settings.py @@ -321,7 +321,7 @@ SOCIAL_SHARE = ["X", "FACEBOOK", "LINKEDIN", "BLUESKY", "MASTODON"] # Disable TinyMCE relative_urls to avoid auto replace self domain urls -# i.e : http://pod.localhost:8000/video/0001-podmp4/ become ../../0001-podmp4/ +# i.e : http://pod.localhost/video/0001-podmp4/ become ../../0001-podmp4/ TINYMCE_DEFAULT_CONFIG = { "theme": "silver", "height": 500, diff --git a/pod/main/static/css/pod.css b/pod/main/static/css/pod.css index 5e286906eb..e2f996bc8a 100755 --- a/pod/main/static/css/pod.css +++ b/pod/main/static/css/pod.css @@ -1336,10 +1336,8 @@ main { } .video-info_duration-view-share { - display: flex; font-size: 90%; gap: 0.75rem; - align-items: center; justify-content: flex-start; border-bottom: 1px solid var(--pod-background-neutre1-bloc); padding-bottom: 0.75rem; @@ -1383,10 +1381,6 @@ main { margin-left: auto; } -.pod-info-video__buttons { - margin-left: auto; -} - .pod-btn-share i, .pod-info-video__signal i { transition: all 0.2s ease-in; diff --git a/pod/main/static/js/videojs-info-controlbar.js b/pod/main/static/js/videojs-info-controlbar.js index 7ac15c5690..574319bfbd 100644 --- a/pod/main/static/js/videojs-info-controlbar.js +++ b/pod/main/static/js/videojs-info-controlbar.js @@ -48,7 +48,7 @@ function showInfoVideo() { * Resize responsively the Pod video Information panel */ function resizeInfo() { - const ihead = document.querySelector("#info-video-wrapper > .iframe-header"); + const ihead = document.querySelector("#info-video-wrapper .iframe-header"); const ph = player.el().offsetHeight; const pb = parseInt(player.el().style.top, 10) + ph - 30; const pw = ph * hdres; // ('#podvideoplayer .vjs-poster').width() diff --git a/pod/main/views.py b/pod/main/views.py index 17c06c34ee..6e7e5cbaa2 100644 --- a/pod/main/views.py +++ b/pod/main/views.py @@ -45,7 +45,6 @@ from .utils import is_ajax from honeypot.decorators import check_honeypot - ## # Settings exposed in templates # diff --git a/pod/meeting/forms.py b/pod/meeting/forms.py index 74edf69b8a..1a6bb569f8 100644 --- a/pod/meeting/forms.py +++ b/pod/meeting/forms.py @@ -20,7 +20,6 @@ from pod.main.forms_utils import OwnerWidget, AddOwnerWidget from pod.meeting.webinar import start_webinar, stop_webinar, toggle_rtmp_gateway - __FILEPICKER__ = False if getattr(settings, "USE_PODFILE", False): __FILEPICKER__ = True diff --git a/pod/meeting/models.py b/pod/meeting/models.py index de3bb3def1..b06543f549 100644 --- a/pod/meeting/models.py +++ b/pod/meeting/models.py @@ -277,14 +277,12 @@ class Meeting(models.Model): null=True, blank=True, verbose_name=_("Slides"), - help_text=_( - """ + help_text=_(""" BigBlueButton will accept Office documents (.doc .docx .pptx), text documents(.txt), images (.png ,.jpg) and Adobe Acrobat documents (.pdf); we recommend converting documents to .pdf prior to uploading for best results. Maximum size is 30 MB or 150 pages per document. - """ - ), + """), on_delete=models.CASCADE, ) site = models.ForeignKey(Site, verbose_name=_("Site"), on_delete=models.CASCADE) @@ -787,9 +785,7 @@ def get_join_url(self, fullname, role, userID=""): """ if role not in ["MODERATOR", "VIEWER"]: msg = {} - msg[ - "error" - ] = """ + msg["error"] = """ Define user role for the meeting. Valid values are MODERATOR or VIEWER """ msg["returncode"] = "" diff --git a/pod/meeting/tests/test_views.py b/pod/meeting/tests/test_views.py index dca40d9141..364bf215ff 100644 --- a/pod/meeting/tests/test_views.py +++ b/pod/meeting/tests/test_views.py @@ -20,7 +20,6 @@ from pod.meeting.models import LiveGateway from pod.video.models import Type - VIDEO_TEST = getattr(settings, "VIDEO_TEST", "pod/main/static/video_test/pod.mp4") ROOT_URLCONF = getattr(settings, "ROOT_URLCONF", "http://testserver") diff --git a/pod/meeting/views.py b/pod/meeting/views.py index f6b8fea0c3..cf797b6744 100644 --- a/pod/meeting/views.py +++ b/pod/meeting/views.py @@ -49,7 +49,6 @@ from pod.live.models import Event from pod.live.views import can_manage_event - RESTRICT_EDIT_MEETING_ACCESS_TO_STAFF_ONLY = getattr( settings, "RESTRICT_EDIT_MEETING_ACCESS_TO_STAFF_ONLY", False ) @@ -1037,8 +1036,7 @@ def get_html_content(request: WSGIRequest, meeting: Meeting) -> str: ) if meeting.recurrence: html_content = ( - _( - """ + _("""

Hello,

%(owner)s invites you to a recurring meeting %(meeting_title)s.

Start date: %(start_date_time)s

@@ -1047,8 +1045,7 @@ def get_html_content(request: WSGIRequest, meeting: Meeting) -> str:

Here is the link to join the meeting: %(join_link)s

You need this password to enter: %(password)s

Regards

- """ - ) + """) % { "owner": full_name, "meeting_title": meeting.name, @@ -1063,16 +1060,14 @@ def get_html_content(request: WSGIRequest, meeting: Meeting) -> str: else: if meeting.is_personal: html_content = ( - _( - """ + _("""

Hello,

%(owner)s invites you to the meeting %(meeting_title)s.

here the link to join the meeting: %(join_link)s

You need this password to enter: %(password)s

Regards

- """ - ) + """) % { "owner": full_name, "meeting_title": meeting.name, @@ -1086,8 +1081,7 @@ def get_html_content(request: WSGIRequest, meeting: Meeting) -> str: ) else: html_content = ( - _( - """ + _("""

Hello,

%(owner)s invites you to the meeting %(meeting_title)s.

Start date: %(start_date_time)s

@@ -1096,8 +1090,7 @@ def get_html_content(request: WSGIRequest, meeting: Meeting) -> str: %(join_link)s

You need this password to enter: %(password)s

Regards

- """ - ) + """) % { "owner": full_name, "meeting_title": meeting.name, @@ -1121,15 +1114,10 @@ def create_ics(request: WSGIRequest, meeting: Meeting) -> str: "owner": meeting.owner.get_full_name(), "meeting_title": meeting.name, } - description = ( - _( - """ + description = _(""" Here is the link to join the meeting: %(join_link)s You need this password to enter: %(password)s - """ - ) - % {"join_link": join_link, "password": meeting.attendee_password} - ) + """) % {"join_link": join_link, "password": meeting.attendee_password} event_description = "\\n".join( line for line in description.replace(" ", "").split("\n") ) diff --git a/pod/package.json b/pod/package.json index 82bfed699c..e6fbd5a570 100644 --- a/pod/package.json +++ b/pod/package.json @@ -24,5 +24,6 @@ }, "engines": { "yarn": ">= 1.0.0" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/pod/playlist/apps.py b/pod/playlist/apps.py index 8d9732dc18..5fcfe08617 100644 --- a/pod/playlist/apps.py +++ b/pod/playlist/apps.py @@ -46,13 +46,11 @@ def save_favorites(self) -> None: """Save previous data from favorites table.""" try: with connection.cursor() as c: - c.execute( - """ + c.execute(""" SELECT owner_id, date_added, rank, video_id FROM favorite_favorite ORDER BY owner_id - """ - ) + """) results = c.fetchall() for res in results: owner_id = res[0] diff --git a/pod/playlist/models.py b/pod/playlist/models.py index 9c5c5256d1..ad177dc88b 100644 --- a/pod/playlist/models.py +++ b/pod/playlist/models.py @@ -14,7 +14,6 @@ from pod.video.models import Video from pod.video.utils import sort_videos_list - SITE_ID = getattr(settings, "SITE_ID") __MAX_LENGTH_FOR_PLAYLIST_NAME__ = 200 diff --git a/pod/playlist/templatetags/favorites_playlist.py b/pod/playlist/templatetags/favorites_playlist.py index 76878017ce..c1f569805c 100644 --- a/pod/playlist/templatetags/favorites_playlist.py +++ b/pod/playlist/templatetags/favorites_playlist.py @@ -13,7 +13,6 @@ get_favorite_playlist_for_user, ) - register = Library() diff --git a/pod/playlist/tests/test_forms.py b/pod/playlist/tests/test_forms.py index 9a285af7d6..566625c4ff 100644 --- a/pod/playlist/tests/test_forms.py +++ b/pod/playlist/tests/test_forms.py @@ -6,7 +6,6 @@ from ...playlist.forms import PlaylistForm, PlaylistRemoveForm, PlaylistPasswordForm from ...playlist.apps import FAVORITE_PLAYLIST_NAME - FIELD_REQUIRED_ERROR_MESSAGE = _("This field is required.") # ggignore-start diff --git a/pod/playlist/views.py b/pod/playlist/views.py index f6245f8f0f..e4252e7275 100644 --- a/pod/playlist/views.py +++ b/pod/playlist/views.py @@ -50,7 +50,6 @@ import json import hashlib - TEMPLATE_VISIBLE_SETTINGS = getattr( settings, "TEMPLATE_VISIBLE_SETTINGS", diff --git a/pod/podfile/forms.py b/pod/podfile/forms.py index c130e8e194..cf3cc308d2 100644 --- a/pod/podfile/forms.py +++ b/pod/podfile/forms.py @@ -12,7 +12,6 @@ from .models import CustomFileModel from .models import CustomImageModel - FILE_ALLOWED_EXTENSIONS = getattr( settings, "FILE_ALLOWED_EXTENSIONS", diff --git a/pod/podfile/views.py b/pod/podfile/views.py index 29f68147e9..6c1f13926d 100644 --- a/pod/podfile/views.py +++ b/pod/podfile/views.py @@ -30,7 +30,6 @@ from pod.main.utils import is_ajax from .utils import update_shared_user - __FOLDER_FILE_TYPE__ = ["image", "file"] diff --git a/pod/progressive_web_app/static/js/serviceworker.js b/pod/progressive_web_app/static/js/serviceworker.js index f48e338144..2ac241f241 100644 --- a/pod/progressive_web_app/static/js/serviceworker.js +++ b/pod/progressive_web_app/static/js/serviceworker.js @@ -32,6 +32,12 @@ self.addEventListener("activate", (event) => { // Serve from Cache self.addEventListener("fetch", (event) => { + const url = new URL(event.request.url); + // In the context of using the recordings to be claimed, it allows bypassing mp4 video files + if (event.request.destination === "video" || url.pathname.endsWith(".mp4")) { + return; + } + event.respondWith( caches .match(event.request) diff --git a/pod/progressive_web_app/utils.py b/pod/progressive_web_app/utils.py index e0ff6cb516..6f1b00dcb2 100644 --- a/pod/progressive_web_app/utils.py +++ b/pod/progressive_web_app/utils.py @@ -3,7 +3,6 @@ from webpush import send_user_notification from django.templatetags.static import static - DEFAULT_ICON = static("img/icon_x1024.png") diff --git a/pod/quiz/admin.py b/pod/quiz/admin.py index fe763dc69e..173b5bc8aa 100644 --- a/pod/quiz/admin.py +++ b/pod/quiz/admin.py @@ -9,7 +9,6 @@ SingleChoiceQuestion, ) - # Questions types diff --git a/pod/quiz/templatetags/video_quiz.py b/pod/quiz/templatetags/video_quiz.py index e67e2815d0..e2d05d6831 100644 --- a/pod/quiz/templatetags/video_quiz.py +++ b/pod/quiz/templatetags/video_quiz.py @@ -6,7 +6,6 @@ from pod.video.models import Video - register = Library() diff --git a/pod/quiz/views.py b/pod/quiz/views.py index ef9476004b..695d4ff8f6 100644 --- a/pod/quiz/views.py +++ b/pod/quiz/views.py @@ -396,7 +396,7 @@ def video_quiz(request: WSGIRequest, video_slug: str) -> HttpResponse: return redirect("%s?referrer=%s" % (settings.LOGIN_URL, request.get_full_path())) if request.method == "POST": - (percentage_score, questions_stats, questions_answers, questions_form_errors) = ( + percentage_score, questions_stats, questions_answers, questions_form_errors = ( process_quiz_submission(request, quiz) ) form_submitted = True diff --git a/pod/recorder/admin.py b/pod/recorder/admin.py index 33572e34df..13c0ecf609 100644 --- a/pod/recorder/admin.py +++ b/pod/recorder/admin.py @@ -1,20 +1,25 @@ """Esup-Pod recorder administration.""" import os + from django.conf import settings -from django.contrib import admin +from django.contrib import admin, messages +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.contrib.sites.shortcuts import get_current_site +from django.db.models.query import QuerySet +from django.http import HttpRequest from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ -from .models import Recording, Recorder, RecordingFile -from .models import RecordingFileTreatment -from django.contrib.sites.shortcuts import get_current_site -from django.contrib.sites.models import Site -from django.contrib.auth.models import User + from pod.video.models import Type +from .models import Recorder, Recording, RecordingFile, RecordingFileTreatment + # Register your models here. RECORDER_ADDITIONAL_FIELDS = getattr(settings, "RECORDER_ADDITIONAL_FIELDS", ()) +USE_RUNNER_MANAGER = getattr(settings, "USE_RUNNER_MANAGER", False) @admin.register(Recording) @@ -23,6 +28,8 @@ class RecordingAdmin(admin.ModelAdmin): list_display_links = ("title",) list_filter = ("type",) autocomplete_fields = ["recorder", "user"] + if USE_RUNNER_MANAGER: + actions = ["encode_recording"] def formfield_for_foreignkey(self, db_field, request, **kwargs): if (db_field.name) == "recorder": @@ -39,6 +46,47 @@ def get_queryset(self, request): qs = qs.filter(recorder__sites=get_current_site(request)) return qs + @admin.action(description=_("Encode selected recordings and create new video")) + def encode_recording( + self, request: HttpRequest, queryset: QuerySet[Recording] + ) -> None: + """Encode selected studio recordings through Runner Manager. + + When Runner Manager is enabled, this admin action iterates over the + selected recordings and starts encoding only for items with type + ``studio``. It reports success, warnings for unsupported recording + types, and processing errors through Django admin messages. + """ + if USE_RUNNER_MANAGER: + # Import here to avoid circular import + from pod.video_encode_transcript.runner_manager import ( + encode_studio_recording, + ) + + for item in queryset: + try: + if item.type == "studio": + self.message_user( + request, + _(f"Studio recording {item.id} encoding started"), + messages.SUCCESS, + ) + # Encode studio recording via Runner Manager + encode_studio_recording(item.id) + else: + # Display a message to the admin user + self.message_user( + request, + _( + f"Recording {item.id} is not a studio recording and can’t be encoded" + ), + messages.WARNING, + ) + except Exception as e: + self.message_user( + request, _(f"Error for {item}: {e}"), messages.ERROR + ) + @admin.register(RecordingFileTreatment) class RecordingFileTreatmentAdmin(admin.ModelAdmin): diff --git a/pod/recorder/forms.py b/pod/recorder/forms.py index b9e538315f..242843a380 100644 --- a/pod/recorder/forms.py +++ b/pod/recorder/forms.py @@ -7,7 +7,6 @@ from pod.main.forms_utils import add_placeholder_and_asterisk from django_select2 import forms as s2forms - DEFAULT_RECORDER_PATH = getattr(settings, "DEFAULT_RECORDER_PATH", "/data/ftp-pod/ftp/") ALLOW_RECORDER_MANAGER_CHOICE_VID_OWNER = getattr( settings, "ALLOW_RECORDER_MANAGER_CHOICE_VID_OWNER", True diff --git a/pod/recorder/plugins/type_video.py b/pod/recorder/plugins/type_video.py index 3ed24476c4..9465eafffb 100644 --- a/pod/recorder/plugins/type_video.py +++ b/pod/recorder/plugins/type_video.py @@ -1,11 +1,12 @@ # video type process -import threading -import logging import datetime +import logging import os import shutil +import threading from django.conf import settings + from pod.video.models import Video, get_storage_path_video from pod.video_encode_transcript import encode @@ -15,7 +16,7 @@ def process(recording): - log.info("START PROCESS OF RECORDING %s" % recording) + log.info("START PROCESS OF RECORDING (type_video) %s" % recording) t = threading.Thread(target=encode_recording, args=[recording]) t.daemon = True t.start() diff --git a/pod/recorder/tests/test_models.py b/pod/recorder/tests/test_models.py index 9bb134bcbb..1f1a43663b 100644 --- a/pod/recorder/tests/test_models.py +++ b/pod/recorder/tests/test_models.py @@ -156,10 +156,8 @@ def test_verifying_attributs_fst_cases(self): recording.source_file = "" recording.save() self.assertEqual(2, len(recording.verify_attributs())) - print( - " ---> test_verifying_attributs_fst_cases \ - of RecordingTestCase: OK!" - ) + print(" ---> test_verifying_attributs_fst_cases \ + of RecordingTestCase: OK!") # Testing the two elif cases of verify_attibuts method def test_verifying_attributs_snd_cases(self): @@ -169,10 +167,8 @@ def test_verifying_attributs_snd_cases(self): recording.source_file = "/home/pod/files/somefile.mp4" recording.save() self.assertEqual(2, len(recording.verify_attributs())) - print( - " ---> test_verifying_attributs_snd_cases \ - of RecordingTestCase: OK!" - ) + print(" ---> test_verifying_attributs_snd_cases \ + of RecordingTestCase: OK!") def test_clean_raise_exception(self): """Test method clean().""" diff --git a/pod/recorder/views.py b/pod/recorder/views.py index afe574da24..2013391e8b 100644 --- a/pod/recorder/views.py +++ b/pod/recorder/views.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- """Esup-pod recorder views.""" + import hashlib import logging import os diff --git a/pod/settings.py b/pod/settings.py index 06df8509ea..c68f7c4f37 100644 --- a/pod/settings.py +++ b/pod/settings.py @@ -19,7 +19,7 @@ ## # Version of the project # -VERSION = "4.1.0" +VERSION = "4.2.0" ## # Installed applications list diff --git a/pod/speaker/templatetags/speaker_template_tags.py b/pod/speaker/templatetags/speaker_template_tags.py index 079a294660..c12d146ce7 100644 --- a/pod/speaker/templatetags/speaker_template_tags.py +++ b/pod/speaker/templatetags/speaker_template_tags.py @@ -6,7 +6,6 @@ from pod.speaker.utils import get_video_speakers, get_video_speakers_grouped from pod.video.models import Video - register = template.Library() diff --git a/pod/urls.py b/pod/urls.py index 29ff4bb438..3fcf8ce4d3 100644 --- a/pod/urls.py +++ b/pod/urls.py @@ -1,27 +1,26 @@ """Esup-pod URL configuration.""" +import importlib.util + from django.conf import settings -from django.urls import include -from django.urls import path, re_path from django.conf.urls.static import static from django.contrib import admin from django.contrib.auth import views as auth_views -from django.views.i18n import JavaScriptCatalog +from django.urls import include, path, re_path from django.utils.translation import gettext_lazy as _ +from django.views.i18n import JavaScriptCatalog -import importlib.util - +from pod.main.rest_router import urlpatterns as rest_urlpatterns from pod.main.views import ( contact_us, download_file, - user_autocomplete, + info_pod, maintenance, robots_txt, - info_pod, - userpicture, set_notifications, + user_autocomplete, + userpicture, ) -from pod.main.rest_router import urlpatterns as rest_urlpatterns USE_CAS = getattr(settings, "USE_CAS", False) USE_SHIB = getattr(settings, "USE_SHIB", False) @@ -41,6 +40,7 @@ WEBTV_MODE = getattr(settings, "WEBTV_MODE", False) USE_DUPLICATE = getattr(settings, "USE_DUPLICATE", False) USE_HYPERLINKS = getattr(settings, "USE_HYPERLINKS", False) +USE_RUNNER_MANAGER = getattr(settings, "USE_RUNNER_MANAGER", False) if USE_CAS: from django_cas_ng import views as cas_views @@ -210,6 +210,17 @@ path("duplicate/", include("pod.duplicate.urls", namespace="duplicate")), ] +# RUNNER_MANAGER +if USE_RUNNER_MANAGER: + urlpatterns += [ + path( + "runner/", + include( + "pod.video_encode_transcript.urls", namespace="video_encode_transcript" + ), + ), + ] + if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if importlib.util.find_spec("debug_toolbar") is not None: diff --git a/pod/video/admin.py b/pod/video/admin.py index dd01936249..b28b5379ea 100644 --- a/pod/video/admin.py +++ b/pod/video/admin.py @@ -642,7 +642,7 @@ class VideoToDeleteAdmin(admin.ModelAdmin): list_filter = ["date_deletion"] autocomplete_fields = ["video"] - @admin.display(description="video") + @admin.display(description=_("video")) def get_videos(self, obj): return obj.video.count() @@ -667,7 +667,7 @@ class CategoryAdmin(admin.ModelAdmin): readonly_fields = ("slug",) # list_filter = ["owner"] - @admin.display(description="Videos") + @admin.display(description=_("Videos")) def videos_count(self, obj) -> int: return obj.video.all().count() diff --git a/pod/video/feeds.py b/pod/video/feeds.py index 4a1a7a099f..5993eeee63 100644 --- a/pod/video/feeds.py +++ b/pod/video/feeds.py @@ -19,7 +19,6 @@ import re import os - ## # Settings exposed in templates # diff --git a/pod/video/forms.py b/pod/video/forms.py index 3c1f9e54c2..a2385283a9 100644 --- a/pod/video/forms.py +++ b/pod/video/forms.py @@ -1384,9 +1384,7 @@ def __init__(self, *args, **kwargs) -> None: super(NoteCommentsForm, self).__init__(*args, **kwargs) # self.fields["user"].widget = forms.HiddenInput() # self.fields["note"].widget = forms.HiddenInput() - self.fields["comment"].widget.attrs[ - "class" - ] = "form-control \ + self.fields["comment"].widget.attrs["class"] = "form-control \ input_comment" self.fields["comment"].widget.attrs["autocomplete"] = "off" self.fields["comment"].widget.attrs["rows"] = 3 diff --git a/pod/video/management/commands/check_obsolete_videos.py b/pod/video/management/commands/check_obsolete_videos.py index 22e093f6b0..02936fef56 100644 --- a/pod/video/management/commands/check_obsolete_videos.py +++ b/pod/video/management/commands/check_obsolete_videos.py @@ -21,7 +21,6 @@ from datetime import date, timedelta - USE_OBSOLESCENCE = getattr(settings, "USE_OBSOLESCENCE", False) USE_ESTABLISHMENT = getattr(settings, "USE_ESTABLISHMENT_FIELD", False) MANAGERS = getattr(settings, "MANAGERS", []) diff --git a/pod/video/management/commands/create_archive_package.py b/pod/video/management/commands/create_archive_package.py index 42ca028165..3cd08fc10a 100644 --- a/pod/video/management/commands/create_archive_package.py +++ b/pod/video/management/commands/create_archive_package.py @@ -24,7 +24,6 @@ from pod.enrichment.models import Enrichment from pod.main.utils import sizeof_fmt - """CUSTOM PARAMETERS.""" LANGUAGE_CODE = getattr(settings, "LANGUAGE_CODE", "fr") ARCHIVE_ROOT = getattr(settings, "ARCHIVE_ROOT", "/video_archiving") diff --git a/pod/video/management/commands/import_encoded_recording.py b/pod/video/management/commands/import_encoded_recording.py index bd5430a0ee..f9d563e535 100644 --- a/pod/video/management/commands/import_encoded_recording.py +++ b/pod/video/management/commands/import_encoded_recording.py @@ -9,7 +9,6 @@ from pod.recorder.models import Recording from pod.video.remote_encode import store_remote_encoding_studio - LANGUAGE_CODE = getattr(settings, "LANGUAGE_CODE", "fr") diff --git a/pod/video/management/commands/import_transcripted_video.py b/pod/video/management/commands/import_transcripted_video.py index a5a33ede2d..edb1beb350 100755 --- a/pod/video/management/commands/import_transcripted_video.py +++ b/pod/video/management/commands/import_transcripted_video.py @@ -9,7 +9,6 @@ from pod.video.models import Video from pod.video.remote_transcript import store_remote_transcripting_video - LANGUAGE_CODE = getattr(settings, "LANGUAGE_CODE", "fr") diff --git a/pod/video/models.py b/pod/video/models.py index 21a093a775..370ecc6c78 100644 --- a/pod/video/models.py +++ b/pod/video/models.py @@ -1083,11 +1083,8 @@ def get_thumbnail_card(self) -> str: # height="{{ im.height }}" loading="lazy"> else: thumbnail_url = static(DEFAULT_THUMBNAIL) - return ( - '%s' - % (thumbnail_url, self.title) - ) + return '%s' % (thumbnail_url, self.title) @property def duration_in_time(self) -> str: diff --git a/pod/video/rest_views.py b/pod/video/rest_views.py index 7205ade7bd..1e492b0daa 100644 --- a/pod/video/rest_views.py +++ b/pod/video/rest_views.py @@ -1,20 +1,22 @@ """Esup-Pod REST views.""" -from rest_framework import serializers, viewsets, renderers -from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework.decorators import action +import json +from django.db.models import Q from django.template.loader import render_to_string +from rest_framework import renderers, serializers, viewsets +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.views import APIView -from .models import Channel, Theme, Type, Discipline, Video, ViewCount -from .context_processors import get_available_videos from pod.main.utils import remove_trailing_spaces +from .context_processors import get_available_videos +from .models import Channel, Discipline, Theme, Type, Video, ViewCount + # commented for v3 # from .remote_encode import start_store_remote_encoding_video -import json # Serializers define the API representation. @@ -210,8 +212,12 @@ class VideoViewSet(viewsets.ModelViewSet): @action(detail=False, methods=["get"]) def user_videos(self, request): - user_videos = self.filter_queryset(self.get_queryset()).filter( - owner__username=request.GET.get("username") + # Manage additional_owners filtering + username = request.GET.get("username") + user_videos = ( + self.filter_queryset(self.get_queryset()) + .filter(Q(owner__username=username) | Q(additional_owners__username=username)) + .distinct() ) if request.GET.get("encoded") and request.GET.get("encoded") == "true": user_videos = user_videos.exclude( diff --git a/pod/video/static/css/video-iframe.css b/pod/video/static/css/video-iframe.css index 708cbb86e7..584e4bcccb 100644 --- a/pod/video/static/css/video-iframe.css +++ b/pod/video/static/css/video-iframe.css @@ -24,44 +24,18 @@ body { margin-right: auto; } -#info-video-wrapper { - overflow: hidden; - position: absolute; - top: 0; - left: 0; - bottom: 30px; - width: 100%; - border-radius: 0; - padding: 0; - margin: 0; - background-color: rgb(250 250 250 / 90%); - z-index: 10; -} - -#info-video { - padding: 1em; - box-sizing: border-box; - overflow: auto; - position: absolute; - - /* top: 3em; */ - width: 100%; - bottom: 0; - z-index: 1000; - border-radius: 0; - margin: 0; +.video-info_duration-view-share { + font-size: 90%; + gap: 0.75rem; + justify-content: flex-start; + border-bottom: 1px solid #ddd; + padding-bottom: 0.75rem; } .tab-title { font-size: 1.5em; } -/* -.meta-title { - font-size: 1.2rem; -} -*/ - .list-group-item { background-color: rgb(250 250 250 / 90%) !important; } @@ -79,31 +53,6 @@ body { padding: 0.25em 5em 0 0; } -.iframe-header .close { - /* - position: absolute; - top: .5em; - right: .5em; - margin: .395em 0 0 .75em; - padding: 0 .25em .15em; - overflow: hidden; - opacity: 1; - font-size: 1.1em; - */ - float: right; - border: 1px solid var(--bs-light); - color: var(--bs-light); - background-color: var(--bs-dark); - border-radius: 2px; -} - -.iframe-header .close:hover { - background-color: var(--bs-light); - color: var(--bs-dark); - - /* opacity: 1; */ -} - #div-video-wrapper #overlay-footer { /* bottom: 0; */ position: absolute; @@ -158,24 +107,6 @@ body { width: 1px; } -/* -#overlay-footer .vjs-info-button::before, -.video-js .vjs-info-button::before { - content: ''; - display: block;width: 100%;height: 100%; - background-color: #FFF; - mask-size: 20px; - -webkit-mask-size: 20px; -} -*/ - -/* -%3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-info-circle" viewBox="0 0 16 16"%3E%3Cpath d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/%3E %3Cpath d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/%3E%3C/svg%3E -*/ -#div-video-wrapper #info-video-wrapper { - display: none; -} - #div-video-wrapper.overlay.info #info-video-wrapper, #div-video-wrapper.overlay #overlay-footer, #div-video-wrapper.vjs-not-started #overlay-footer { diff --git a/pod/video/static/js/FilterManager.js b/pod/video/static/js/FilterManager.js index f1513c904f..05aeb33074 100644 --- a/pod/video/static/js/FilterManager.js +++ b/pod/video/static/js/FilterManager.js @@ -72,6 +72,7 @@ class FilterManager { addFilter({ name, param, searchCallback, itemLabel, itemKey }) { try { this.filters[param] = { + param, name, searchCallback, itemLabel, @@ -201,16 +202,7 @@ class FilterManager { */ removeFilter(currentFilter, key) { if (currentFilter) { - const filterElement = document.getElementById(`${slugify(key)}-tag`); - const closeButton = document.getElementById( - `remove-filter-${slugify(key)}`, - ); - if (closeButton) { - const tooltip = bootstrap.Tooltip.getInstance(closeButton); - if (tooltip) tooltip.dispose(); - } - const data = currentFilter.selectedItems.get(key); - if (filterElement) filterElement.remove(); + this._removeFilterTag(key); currentFilter.selectedItems.delete(key); sessionStorage.setItem( `filter-${currentFilter.param}`, @@ -224,10 +216,29 @@ class FilterManager { * Initializes the filters from the session. */ async initializeFilters() { + let shouldRefresh = false; + for (const param of Object.keys(this.filters)) { const filter = this.filters[param]; + const selectedKeysFromSession = ( + JSON.parse(sessionStorage.getItem(`filter-${param}`)) || [] + ) + .map((value) => slugify(String(value))) + .filter((value) => value !== ""); + const selectedKeysFromUrl = this.getFilterValuesFromUrl(param).map( + (value) => slugify(String(value)), + ); const selectedKeys = - JSON.parse(sessionStorage.getItem(`filter-${param}`)) || []; + selectedKeysFromUrl.length > 0 + ? selectedKeysFromUrl + : selectedKeysFromSession; + + // Refresh only when we apply filters coming from sessionStorage + // that are not already present in the URL/server-rendered page. + if (selectedKeysFromUrl.length === 0 && selectedKeys.length > 0) { + shouldRefresh = true; + } + let allItems = []; try { allItems = await filter.searchCallback(""); @@ -251,7 +262,136 @@ class FilterManager { } }); } - this.update(); + this.update({ refresh: shouldRefresh }); + } + + /** + * Returns selected values from URL for one filter key. + * Supports repeated and comma-separated values. + * @param {string} param - Parameter key. + * @returns {Array} + */ + getFilterValuesFromUrl(param) { + const searchParams = new URLSearchParams(window.location.search); + return searchParams + .getAll(param) + .flatMap((value) => value.split(",")) + .map((value) => value.trim()) + .filter((value) => value !== ""); + } + + /** + * Synchronize one filter with an explicit list of values. + * @param {string} param - Parameter key. + * @param {Array} values - Values to set. + * @param {Object} [options] + * @param {boolean} [options.refresh=true] - Trigger videos refresh. + * @param {boolean} [options.emitEvent=true] - Emit dashboard filters event. + * @param {boolean} [options.updateUrl=true] - Update browser URL. + */ + syncFilterSelection( + param, + values = [], + { refresh = true, emitEvent = true, updateUrl = true } = {}, + ) { + const filter = this.filters[param]; + if (!filter) return; + + Array.from(filter.selectedItems.keys()).forEach((key) => { + this._removeFilterTag(key); + }); + filter.selectedItems.clear(); + + const availableItems = this.currentResults[param] || []; + const uniqueEntries = []; + const seenKeys = new Set(); + + values.forEach((entry) => { + const rawValue = + typeof entry === "object" && entry !== null ? entry.value : entry; + if (!rawValue) return; + + const value = String(rawValue).trim(); + if (!value) return; + + const key = slugify(value); + if (seenKeys.has(key)) return; + seenKeys.add(key); + + const providedLabel = + typeof entry === "object" && entry !== null ? entry.label : null; + const matchedItem = availableItems.find( + (item) => slugify(filter.itemKey(item)) === key, + ); + const label = + providedLabel || (matchedItem ? filter.itemLabel(matchedItem) : value); + + uniqueEntries.push({ key, value, label }); + }); + + uniqueEntries.forEach(({ key, value, label }) => { + filter.selectedItems.set(key, { value, label }); + this.renderActiveFilter(filter, key); + }); + + const listContainer = document.getElementById( + `collapseFilter${capitalize(param)}`, + ); + if (listContainer) { + this.createCheckboxesForFilter(param, this.currentResults[param] || []); + } + + this.update({ refresh, emitEvent, updateUrl }); + } + + /** + * Synchronize one filter from URL query values. + * @param {string} param - Parameter key. + * @param {Object} [options] + * @param {boolean} [options.refresh=true] - Trigger videos refresh. + * @param {boolean} [options.emitEvent=true] - Emit dashboard filters event. + * @param {boolean} [options.updateUrl=true] - Update browser URL. + */ + syncFilterSelectionFromUrl( + param, + { refresh = true, emitEvent = true, updateUrl = true } = {}, + ) { + this.syncFilterSelection(param, this.getFilterValuesFromUrl(param), { + refresh, + emitEvent, + updateUrl, + }); + } + + /** + * Returns a snapshot of selected filters. + * @returns {Object>} + */ + getSelectedFiltersSnapshot() { + const selectedFilters = {}; + Object.keys(this.filters).forEach((param) => { + selectedFilters[param] = Array.from( + this.filters[param].selectedItems.keys(), + ); + }); + return selectedFilters; + } + + /** + * Removes one filter tag from active filters UI. + * @param {string} key - Selected key. + */ + _removeFilterTag(key) { + const normalizedKey = slugify(key); + const filterElement = document.getElementById(`${normalizedKey}-tag`); + const closeButton = document.getElementById( + `remove-filter-${normalizedKey}`, + ); + if (closeButton) { + const tooltip = bootstrap.Tooltip.getInstance(closeButton); + if (tooltip) tooltip.dispose(); + } + if (filterElement) filterElement.remove(); } /** @@ -401,8 +541,8 @@ class FilterManager { /** * Updates the sessionStorage and URL with the selected filters. */ - update() { - const query = new URLSearchParams(); + update({ refresh = true, emitEvent = true, updateUrl = true } = {}) { + const query = new URLSearchParams(window.location.search); Object.keys(this.filters).forEach((param) => { const filter = this.filters[param]; @@ -415,10 +555,27 @@ class FilterManager { sessionStorage.removeItem(`filter-${param}`); } }); - const newUrl = `${window.location.pathname}?${query.toString()}`; - window.history.replaceState(null, "", newUrl); + query.delete("page"); + if (updateUrl) { + const queryString = query.toString(); + const newUrl = queryString + ? `${window.location.pathname}?${queryString}` + : window.location.pathname; + window.history.replaceState(null, "", newUrl); + } this._syncResetLink(); - refreshVideosSearch(); + if (emitEvent) { + document.dispatchEvent( + new CustomEvent("pod:dashboard-filters-updated", { + detail: { + selectedFilters: this.getSelectedFiltersSnapshot(), + }, + }), + ); + } + if (refresh) { + refreshVideosSearch(); + } } /** diff --git a/pod/video/static/js/filter.js b/pod/video/static/js/filter.js index 29dbf295be..4037dda73c 100644 --- a/pod/video/static/js/filter.js +++ b/pod/video/static/js/filter.js @@ -52,6 +52,7 @@ const filtersConfig = [ itemKey: (categories) => categories.value, }, ]; +globalThis.filtersConfig = filtersConfig; /** * Retrieves the list of video owners based on a search term. @@ -89,7 +90,29 @@ const filterManager = new FilterManager({ // Inject filter configuration into the manager filtersConfig.forEach((cfg) => filterManager.addFilter(cfg)); -filterManager.initializeFilters(); +// Wait for async filter initialization before applying URL params to avoid a race condition +filterManager.initializeFilters().then(() => { + const searchParams = new URLSearchParams(window.location.search); + if (searchParams.has("categories")) { + filterManager.syncFilterSelectionFromUrl("categories", { + refresh: false, + emitEvent: false, + updateUrl: false, + }); + } +}); + +document.addEventListener("pod:aside-category-filter-updated", (event) => { + filterManager.syncFilterSelection( + "categories", + event.detail?.categories || [], + { + refresh: false, + emitEvent: false, + updateUrl: false, + }, + ); +}); /** * Initializes filters by fetching data from the statistics API and preparing @@ -152,7 +175,7 @@ async function fetchSingleFilter(filterName, searchTerm = "") { // Map the received data to the { label, value } format expected by FilterManager return data[key].map((item) => ({ label: item.title || item.name || item.label || "unknown", - value: item.id || item.slug || item.value || "unknown", + value: item.slug || item.id || item.value || "unknown", })); } catch (error) { console.error( diff --git a/pod/video/static/js/filter_aside_video_list_refresh.js b/pod/video/static/js/filter_aside_video_list_refresh.js index 5c52d53dec..0b21f2dbe1 100644 --- a/pod/video/static/js/filter_aside_video_list_refresh.js +++ b/pod/video/static/js/filter_aside_video_list_refresh.js @@ -20,17 +20,25 @@ var infinite; let infiniteLoading = document.querySelector(".infinite-loading"); function onBeforePageLoad() { - infiniteLoading.style.display = "block"; + if (infiniteLoading) { + infiniteLoading.style.display = "block"; + } } function onAfterPageLoad() { if ( + typeof urlVideos !== "undefined" && urlVideos === "/video/dashboard/" && + typeof selectedVideos !== "undefined" && + typeof videosListContainerId !== "undefined" && + typeof setSelectedVideos === "function" && selectedVideos[videosListContainerId] && selectedVideos[videosListContainerId].length !== 0 ) { setSelectedVideos(videosListContainerId); } - infiniteLoading.style.display = "none"; + if (infiniteLoading) { + infiniteLoading.style.display = "none"; + } let footer = document.querySelector("footer.static-pod"); if (!footer) return; footer.classList.add("small"); @@ -43,12 +51,12 @@ function onAfterPageLoad() { document.scrollHeight, document.offsetHeight, ); - document.querySelector("footer.static-pod .hidden-pod").style.display = - "none"; + const hiddenFooter = document.querySelector("footer.static-pod .hidden-pod"); + if (!hiddenFooter) return; + hiddenFooter.style.display = "none"; window.addEventListener("scroll", function () { - if (window.innerHeight + window.scrollTop() === docHeight) { - document.querySelector("footer.static-pod .hidden-pod").style.display = - "block"; + if (window.innerHeight + window.scrollY >= docHeight) { + hiddenFooter.style.display = "block"; footer.setAttribute("style", "height:auto;"); footer.classList.remove("fixed-bottom"); } @@ -61,6 +69,7 @@ function onAfterPageLoad() { * @param nextPage */ function refreshInfiniteLoader(url, nextPage) { + if (typeof InfiniteLoader !== "function") return; if (infinite !== undefined) { infinite.removeLoader(); } @@ -84,8 +93,13 @@ function replaceCountVideos(newCount) { newCount, ); videoFoundStr = interpolate(videoFoundStr, { count: newCount }, true); - document.getElementById("video_count").textContent = videoFoundStr; - resetDashboardElements(); + const videoCount = document.getElementById("video_count"); + if (videoCount) { + videoCount.textContent = videoFoundStr; + } + if (typeof resetDashboardElements === "function") { + resetDashboardElements(); + } } /** @@ -110,7 +124,7 @@ function handleSearch(e) { refreshVideosSearch(); } -document.getElementById("searchForm").addEventListener("submit", handleSearch); +document.getElementById("searchForm")?.addEventListener("submit", handleSearch); document .getElementById("titleSearchBtn") @@ -167,7 +181,11 @@ function refreshVideosSearch() { refreshInfiniteLoader(url, pageNext); } if ( + typeof urlVideos !== "undefined" && urlVideos === "/video/dashboard/" && + typeof selectedVideos !== "undefined" && + typeof videosListContainerId !== "undefined" && + typeof setSelectedVideos === "function" && selectedVideos[videosListContainerId] && selectedVideos[videosListContainerId].length !== 0 ) { @@ -210,8 +228,11 @@ function getUrlForRefresh() { let urlParams = new URLSearchParams(window.location.search); // Normalize multi-value parameters + const safeFiltersConfig = Array.isArray(globalThis.filtersConfig) + ? globalThis.filtersConfig + : []; const multiFilters = [ - ...new Set(filtersConfig.map((filter) => filter.param)), + ...new Set(safeFiltersConfig.map((filter) => filter.param)), ]; urlParams = normalizeMultiValues(urlParams, multiFilters); @@ -274,17 +295,23 @@ function disabledInputs(value) { } // First launch of the infinite scroll -infinite = new InfiniteLoader( - getUrlForRefresh(), - onBeforePageLoad, - onAfterPageLoad, - nextPage, - (page = 2), -); +if (typeof InfiniteLoader === "function") { + infinite = new InfiniteLoader( + getUrlForRefresh(), + onBeforePageLoad, + onAfterPageLoad, + typeof nextPage !== "undefined" ? nextPage : true, + 2, + ); +} // Check and clean url to avoid owner parameter if not authorized var urlParams = new URLSearchParams(window.location.search); -if (urlParams.has("owner") && !ownerFilter) { +if ( + urlParams.has("owner") && + typeof ownerFilter !== "undefined" && + !ownerFilter +) { urlParams.delete("owner"); window.history.pushState( null, diff --git a/pod/video/static/js/video_category.js b/pod/video/static/js/video_category.js index a8bee5d462..f65ae73e97 100644 --- a/pod/video/static/js/video_category.js +++ b/pod/video/static/js/video_category.js @@ -45,11 +45,17 @@ function manageCategoriesLinks() { * Manage search category input in filter aside */ let searchCategoriesInput = document.getElementById("search-categories-input"); +const clearCategoryFilterButton = document.getElementById( + "clear-category-filter-btn", +); if (searchCategoriesInput) { searchCategoriesInput.addEventListener("input", () => { manageSearchCategories(searchCategoriesInput.value.trim()); }); } +if (clearCategoryFilterButton) { + clearCategoryFilterButton.addEventListener("click", clearCategoryFilter); +} /** * Manage search category input to display chosen ones @@ -80,10 +86,107 @@ function manageSearchCategories(search) { * @param {HTMLElement} el - Category link clicked */ function toggleCategoryLink(el) { - el.parentNode.classList.toggle("active"); + const categorySlug = el.dataset.slug; + // Keep category filter as a single selection from the aside list. + updateCategoriesQueryParam([categorySlug]); + syncCategoryLinksWithUrl(); + dispatchAsideCategoryFilterUpdated([ + { + value: categorySlug, + label: el.textContent.trim(), + }, + ]); refreshVideosSearch(); } +/** + * Clear selected category filter. + */ +function clearCategoryFilter() { + updateCategoriesQueryParam([]); + syncCategoryLinksWithUrl(); + dispatchAsideCategoryFilterUpdated([]); + refreshVideosSearch(); +} + +/** + * Notify dashboard filter manager that category selection changed from aside. + * + * @param {Array<{value: string, label: string}>} categories - Selected categories. + */ +function dispatchAsideCategoryFilterUpdated(categories) { + document.dispatchEvent( + new CustomEvent("pod:aside-category-filter-updated", { + detail: { categories }, + }), + ); +} + +/** + * Return selected category slugs from current URL query. + * Supports repeated params and comma-separated values. + * + * @returns {Array} - selected category slugs + */ +function getSelectedCategoriesFromUrl() { + const searchParams = new URLSearchParams(window.location.search); + return searchParams + .getAll("categories") + .flatMap((value) => value.split(",")) + .map((value) => value.trim()) + .filter((value) => value !== ""); +} + +/** + * Update categories query parameter in browser URL. + * + * @param {Array} selectedCategories - categories to persist in URL + */ +function updateCategoriesQueryParam(selectedCategories) { + const currentParams = new URLSearchParams(window.location.search); + const orderedParams = new URLSearchParams(); + + selectedCategories.forEach((slug) => { + orderedParams.append("categories", slug); + }); + + currentParams.forEach((value, key) => { + if (key === "categories" || key === "page") return; + orderedParams.append(key, value); + }); + + const queryString = orderedParams.toString(); + const newUrl = queryString + ? `${window.location.pathname}?${queryString}` + : window.location.pathname; + window.history.replaceState({}, "", newUrl); +} + +/** + * Sync active category links in aside from current URL. + */ +function syncCategoryLinksWithUrl() { + const selectedCategories = new Set(getSelectedCategoriesFromUrl()); + + document.querySelectorAll(".categories-list-item").forEach((item) => { + const button = item.querySelector(".cat-title"); + if (!button) return; + + if (selectedCategories.has(button.dataset.slug)) { + item.classList.add("active"); + } else { + item.classList.remove("active"); + } + }); + + if (clearCategoryFilterButton) { + clearCategoryFilterButton.classList.toggle( + "d-none", + selectedCategories.size === 0, + ); + } +} + /** * Build and return url for Get or Post categories methods * @@ -295,6 +398,7 @@ function refreshCategoriesLinks() { let html = parser.parseFromString(data, "text/html").body; categoriesListContainer.innerHTML = html.innerHTML; manageCategoriesLinks(); + syncCategoryLinksWithUrl(); }) .catch(() => { showalert( @@ -305,5 +409,10 @@ function refreshCategoriesLinks() { }); } +document.addEventListener("pod:dashboard-filters-updated", () => { + syncCategoryLinksWithUrl(); +}); + // Add event listeners on categories list buttons for the first time manageCategoriesLinks(); +syncCategoryLinksWithUrl(); diff --git a/pod/video/templates/videos/dashboard.html b/pod/video/templates/videos/dashboard.html index d5bb049ce3..359177f972 100644 --- a/pod/video/templates/videos/dashboard.html +++ b/pod/video/templates/videos/dashboard.html @@ -82,8 +82,8 @@

{% trans 'Multiple actions' %}

{% if request.user.is_superuser %} - {% endif %} + {% if request.user.is_superuser %} diff --git a/pod/video/templates/videos/filter_aside_category.html b/pod/video/templates/videos/filter_aside_category.html index ad0069e4ce..ebf320998f 100644 --- a/pod/video/templates/videos/filter_aside_category.html +++ b/pod/video/templates/videos/filter_aside_category.html @@ -13,6 +13,11 @@
+
+ +
- - -{% if video.is_draft == False or video.owner == request.user or request.user in video.additional_owners.all%} -