diff --git a/pod/import_video/models.py b/pod/import_video/models.py index a724b77a0a..c6e4ec6228 100644 --- a/pod/import_video/models.py +++ b/pod/import_video/models.py @@ -1,4 +1,8 @@ """Models for the Import_video module.""" +import requests + +from urllib.parse import urlencode +import xml.etree.ElementTree as et from django.conf import settings from django.contrib.sites.models import Site @@ -9,7 +13,15 @@ from django.utils import timezone from django.utils.translation import ugettext_lazy as _ +from pod.meeting.utils import ( + api_call, + parseXmlToJson, + slash_join, +) + SITE_ID = getattr(settings, "SITE_ID", 1) +USE_MEETING = getattr(settings, "USE_MEETING", False) +BBB_API_URL = getattr(settings, "BBB_API_URL", "") class ExternalRecording(models.Model): @@ -121,6 +133,48 @@ class ExternalRecording(models.Model): help_text=_("User who uploaded to Pod the video file"), ) + def search_internal_recording(self, recording_id): + """Search for an internal recording that corresponds to recording_id. + + This function checks whether an external recording has been made on the BBB + environment used by internal recordings and the meetings module. + Typically, this function recovers the single-use token of a BBB session + performed on the ESR infrastructure. + """ + if USE_MEETING: + action = "getRecordings" + parameters = {} + parameters["recordID"] = recording_id + query = urlencode(parameters) + hashed = api_call(query, action) + if BBB_API_URL == "": + msg = {} + msg["error"] = _("Unable to call BBB server.") + msg["message"] = _("Parameter %s needs to be defined.") % "BBB_API_URL" + raise ValueError(msg) + url = slash_join(BBB_API_URL, action, "?%s" % hashed) + response = requests.get(url) + if response.status_code != 200: + msg = {} + msg["error"] = _("Unable to call BBB server.") + msg["returncode"] = response.status_code + msg["message"] = response.content.decode("utf-8") + raise ValueError(msg) + result = response.content.decode("utf-8") + xmldoc = et.fromstring(result) + recording_json = parseXmlToJson(xmldoc) + if recording_json.get("returncode", "") != "SUCCESS": + msg = {} + msg["error"] = _("Unable to get recording!") + msg["returncode"] = recording_json.get("returncode", "") + msg["messageKey"] = recording_json.get("messageKey", "") + msg["message"] = recording_json.get("message", "") + raise ValueError(msg) + else: + return recording_json + else: + return ValueError(_("Method not allowed")) + def __unicode__(self): return "%s - %s" % (self.id, self.name) diff --git a/pod/import_video/templates/import_video/list.html b/pod/import_video/templates/import_video/list.html index 2906f44c75..14d5434ed1 100644 --- a/pod/import_video/templates/import_video/list.html +++ b/pod/import_video/templates/import_video/list.html @@ -57,14 +57,28 @@ {{ record.type }} {% if record.presentationUrl != "" %} - - - + {% if record.recordingId == "" %} + + + + {% else %} + + + + {% endif %} {% endif %} {% if record.videoUrl != "" and record.videoUrl != record.presentationUrl %} - - - + {% if record.recordingId == "" %} + + + + {% else %} + + + + {% endif %} {% endif %} @@ -144,5 +158,42 @@ } return answer; } + + /** + * Function that allow to manage the link of recordings (video and presentation playback) to take into account the single-use token. + **/ + function manageTokenLink(type, element) { + // Get important information from element + let recordId = element.dataset.podRecordingId; + // AJAX Pod server to re-request the BBB server + urlGetRecording = "/import_video/recording_with_token/" + recordId + "/"; + + // Get infos from the recording (new token appears here) + fetch(urlGetRecording, { + method: "GET", + headers: { + "X-CSRFToken": "{{ csrf_token }}", + "X-Requested-With": "XMLHttpRequest", + }, + dataType: "html", + cache: "no-store", + }) + .then((response) => response.text()) + .then((data) => { + // Get new playback URL + let jsonData = JSON.parse(data); + var urlNew; + if (type == "presentation") { + urlNew = jsonData.presentationUrl; + } else { + urlNew = jsonData.videoUrl; + } + // Open the good URL in a new tab + window.open(urlNew, '_blank'); + }) + .catch((error) => { + console.debug(error); + }); + } {% endblock more_script %} diff --git a/pod/import_video/urls.py b/pod/import_video/urls.py index b53de34fe3..f101470917 100644 --- a/pod/import_video/urls.py +++ b/pod/import_video/urls.py @@ -28,4 +28,9 @@ views.delete_external_recording, name="delete_external_recording", ), + path( + "recording_with_token//", + views.recording_with_token, + name="recording_with_token", + ), ] diff --git a/pod/import_video/utils.py b/pod/import_video/utils.py index fe87b5a85f..28e335a3c1 100644 --- a/pod/import_video/utils.py +++ b/pod/import_video/utils.py @@ -11,6 +11,7 @@ from django.utils.html import mark_safe from django.utils.translation import gettext_lazy as _ from html.parser import HTMLParser +from pod.meeting.utils import slash_join from pod.video.models import Video from pod.video.models import Type from urllib.parse import parse_qs, urlparse @@ -48,6 +49,11 @@ VIDEOS_DIR = getattr(settings, "VIDEOS_DIR", "videos") +# The meeting application is activated? +USE_MEETING = getattr(settings, "USE_MEETING", False) +# BBB server API +BBB_API_URL = getattr(settings, "BBB_API_URL", "") + def secure_request_for_upload(request): """Check that the request is correct for uploading a recording. @@ -85,13 +91,8 @@ def parse_remote_file(session: Session, source_html_url: str): try: response = session.get(source_html_url) if response.status_code != 200: - msg = {} - msg["error"] = _( - "The HTML file for this recording was not found on the server." - ) - # If we want to display the 404/500... page to the user - # msg["message"] = response.content.decode("utf-8") - msg["message"] = _("Error number: %s") % response.status_code + # No more informations needed for common error + msg = '' raise ValueError(msg) # Parse the BBB video HTML file @@ -160,7 +161,7 @@ def manage_recording_url(source_url: str, video_file_add: str) -> str: if url.query: query = parse_qs(url.query, keep_blank_values=True) if query["token"][0]: - # 1st case (ex: ESR URL), URL likes (ex for ESR URL:) + # 1st case (ex: ESR URL), URL likes # https://_site_/recording/_recording_id/video?token=_token_ # Get recording unique identifier recording_id = url.path.split("/")[2] @@ -236,7 +237,7 @@ def download_video_file(session: Session, source_video_url: str, dest_file: str) # print(session.cookies.get_dict()) if response.status_code != 200: raise ValueError( - _("The video file for this recording " "was not found on the server.") + _("The video file for this recording was not found on the server.") ) with open(dest_file, "wb+") as file: @@ -361,7 +362,8 @@ def check_source_url(source_url: str): # noqa: C901 Platforms managed : - Mediacad platform (Médiathèque académique) : rewrite source URL if required and manage JSON API. - - Old BigBlueButton : source URL for old BBB presentation playback + - BBB ESR infrastructure : rewrite source URL if required + - Old BigBlueButton presentation : source URL for old BBB presentation playback """ base_url = "" media_id = "" @@ -428,6 +430,27 @@ def check_source_url(source_url: str): # noqa: C901 media_id, ) platform = "Mediacad" + elif source_url.find("bbb.numerique-esr.fr/video/") != -1: + # BBB ESR video link + # https://univ.scalelite.bbb.numerique-esr.fr/video/#id#/ + source_video_url = source_url + format = "m4v" + platform = "BBB_ESR" + elif source_url.find("bbb.numerique-esr.fr/playback/presentation/2.3/") != -1: + # BBB ESR presentation link : rewrite for video source URL + # https://univ.scalelite.bbb.numerique-esr.fr/playback/presentation/2.3/#id# + source_video_url = source_url.replace( + "/playback/presentation/2.3/", "/video/" + ) + "/" + format = "m4v" + platform = "BBB_ESR" + elif source_url.find("bbb.numerique-esr.fr/recording/") != -1: + # BBB ESR video or presentation link : rewrite for video source URL if pres + # https://univ.scalelite.bbb.numerique-esr.fr/recording/#id#/presentation? + # https://univ.scalelite.bbb.numerique-esr.fr/recording/#id#/video? + source_video_url = source_url.replace("/presentation", "/video") + format = "m4v" + platform = "BBB_ESR" elif source_url.find("/playback/presentation/2.0/playback.html?") != -1: # Old BBB 2.x (<2.3) presentation link # Conversion from @@ -454,8 +477,8 @@ def check_source_url(source_url: str): # noqa: C901 platform_type = TypeSourceURL( platform, source_video_url, format, url_api_video ) - if platform == "BBB_Presentation": - # Platform type: older BBB, format presentation + # Platform type: BBB ESR or older BBB, format presentation + if platform == "BBB_ESR" or platform == "BBB_Presentation": platform_type = TypeSourceURL(platform, source_video_url, format, "") return platform_type @@ -507,6 +530,79 @@ def move_file(source: str, destination: str): print(f"Error moving the file: {e}") +def check_url_format_presentation(source_url: str) -> bool: + """ Check if the URL looks like a BBB presentation.""" + presentation = False + # Management for old presentation URLs with BBB or Scalelite server + if source_url.find("/playback/presentation/2.0/playback.html?") != -1: + presentation = True + # Management for standard presentation URLs with BBB or Scalelite server + elif source_url.find("/playback/presentation/2.3/") != -1: + presentation = True + return presentation + + +def check_url_need_token(source_url: str) -> str: + """ Check if the URL is used by an infrastructure that need a token. + + Useful for generating the single-use token required to access video file. + 2 conditions : + - URL was created on the same BBB infrastructure as the meeting module, + - At present, only ESR's BBB infrastructure uses single-use tokens. + If both conditions are met, then returns the recording_id. + """ + if USE_MEETING: + # Calculate base URL for BBB meeting + meeting_base_url = slash_join(BBB_API_URL.replace("bigbluebutton/api", "")) + # If the URL was created on the same BBB ESR infrastructure as the meeting module + # Ex: + # https://univ.scalelite.bbb.numerique-esr.fr/video/##id##/ + # https://univ.scalelite.bbb.numerique-esr.fr/presentation/##id##/ + # https://univ.scalelite.bbb.numerique-esr.fr/recording/##id##/video?xxx + # https://univ.scalelite.bbb.numerique-esr.fr/recording/##id##/presentation?xxx + # https://univ.scalelite.bbb.numerique-esr.fr/playback/presentation/2.3/##id## + if ( + source_url.find(meeting_base_url) != -1 + and source_url.find("bbb.numerique-esr.fr/") != -1 + ): + # Get recording id + array_url = source_url.split("/") + recording_id = array_url[-2] + # Specific case + if recording_id == "2.3": + recording_id = array_url[-1] + return recording_id + else: + return "" + else: + return "" + + +def get_playbacks_urls_with_token(recording): + """ Get presentation and video source URL, with token, for a recording. + + Check if the recording was made by an infrastructure that need a token. + In such a case, request the BBB infrastructure to get playbacks with token. + """ + # Default values + presentation_url = "" + video_url = "" + # Check if the URL is used by an infrastructure that need a token + recording_id = check_url_need_token(recording.source_url) + if recording_id != "": + # Request the BBB infrastructure to get playbacks with token + recordings = recording.search_internal_recording(recording_id) + if type(recordings.get("recordings")) is dict: + for data in recordings["recordings"].values(): + for playback in data["playback"]: + if data["playback"][playback]["type"] == "video": + video_url = data["playback"][playback]["url"] + if data["playback"][playback]["type"] == "presentation": + presentation_url = data["playback"][playback]["url"] + # Return the 2 usefuls playbacks + return presentation_url, video_url + + class TypeSourceURL: """Manage external recording source URL. @@ -599,6 +695,8 @@ class StatelessRecording: presentationUrl = "" # Video playback URL, used as the source URL for the video file videoUrl = "" + # Recording id (BBB format), when created on the same BBB infra as meeting module + recordingId = "" def __init__(self, id, name, state): """Initiliaze.""" diff --git a/pod/import_video/views.py b/pod/import_video/views.py index 157ba5362b..c5f6d80df1 100644 --- a/pod/import_video/views.py +++ b/pod/import_video/views.py @@ -14,12 +14,13 @@ from .models import ExternalRecording from .forms import ExternalRecordingForm -from .utils import StatelessRecording, check_url_exists, download_video_file +from .utils import StatelessRecording, download_video_file from .utils import manage_recording_url, check_source_url, parse_remote_file from .utils import save_video, secure_request_for_upload from .utils import check_video_size, verify_video_exists_and_size from .utils import define_dest_file_and_path, check_file_exists, move_file -from .utils import TypeSourceURL +from .utils import check_url_format_presentation, check_url_need_token +from .utils import get_playbacks_urls_with_token, TypeSourceURL from datetime import datetime from django.conf import settings from django.contrib.auth.decorators import login_required @@ -27,6 +28,7 @@ from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import PermissionDenied from django.contrib import messages +from django.http import HttpResponse, HttpResponseBadRequest from django.shortcuts import render, redirect from django.shortcuts import get_object_or_404 from django.urls import reverse @@ -212,14 +214,12 @@ def external_recordings(request): owner__id=request.user.id, site=site ) # | request.user.owners_external_recordings.all().filter(site=site) - external_recordings = external_recordings.order_by("-id").distinct() + external_recordings = external_recordings.order_by("-start_at").distinct() recordings = [] for data in external_recordings: recording = get_stateless_recording(request, data) - recordings.append(recording) - return render( request, "import_video/list.html", @@ -467,6 +467,13 @@ def upload_video_recording_to_pod(request, record_id: int): # noqa: C901 return True else: return False + elif type_source_url is not None and type_source_url.type == "BBB_ESR": + # BBB ESR platform + # For BBB ESR infrastructure, there is always a video playback + return upload_bbb_esr_video_recording_to_pod( + record_id, + type_source_url.url + ) else: # Video file (or BBB video file, same process) source URL return upload_standard_video_recording_to_pod(record_id) @@ -507,8 +514,8 @@ def upload_standard_video_recording_to_pod(record_id: int) -> bool: recording = ExternalRecording.objects.get(id=record_id) # Step 1: Download and parse the remote HTML file if necessary - # Check if extension is a video extension source_url = recording.source_url + # Check if extension is a video extension extension = source_url.split(".")[-1].lower() # Name of the video file to add to the URL (if necessary) @@ -548,6 +555,59 @@ def upload_standard_video_recording_to_pod(record_id: int) -> bool: manage_standard_exception(exc) +def upload_bbb_esr_video_recording_to_pod(record_id: int, source_url: str) -> bool: + """Upload a BBB - from BBB ESR infrastructure - video file recording to Pod. + + Useful for generating the single-use token required to access the video file. + This is only possible for recordings made on the same BBB infrastructure used + by the meetings module. + """ + try: + # Session useful to achieve requests (and keep cookies between) + session = requests.Session() + + recording = ExternalRecording.objects.get(id=record_id) + + # Check if the URL is used by an infrastructure that need a token + # In such a case, request the BBB infrastructure to get playbacks with token + presentation_url, video_url = get_playbacks_urls_with_token(recording) + if video_url != "": + # Define the new source URL, with token + recording.source_url = video_url + source_url = video_url + + # Download and parse the remote HTML file (BBB specific) + video_file_add = parse_remote_file(session, source_url) + extension = video_file_add.split(".")[-1].lower() + + # Impossible to check simply that video exists + # and not oversized for BBB ESR infrastructure. + # This seems to be a technical constraint. + + # Step 2: Define destination source file + dest_file, dest_path = define_dest_file_and_path( + recording.owner, recording.id, extension + ) + + # Step 3: Download the video file + manage_download(session, source_url, video_file_add, dest_file) + + # Step 4: Save informations about the recording + save_external_recording(recording.owner, record_id) + + # Step 5: Save and encode Pod video + description = _( + 'This video has been uploaded to Pod and its source is the virtual ' + 'classroom solution of the French Ministry of Higher Education and Research.' + ) + + save_video(recording.owner, dest_path, recording.name, description) + + return True + except Exception as exc: + manage_standard_exception(exc) + + def upload_local_video_recording_to_pod(record_id: id, dest_file: str, dest_path: str): """Upload a local (typically in Pod filesystem) video file recording to Pod. Useful for video files that have been encoded following @@ -1078,32 +1138,33 @@ def get_stateless_recording(request, data: ExternalRecording): recording.state = get_status_recording(data) # Management of the external recording type - if data.type == "bigbluebutton": + if data.type == "bigbluebutton" or data.type == "video": # Manage BBB recording URL - # Management for old presentation URLs with BBB or Scalelite server - if data.source_url.find("/playback/presentation/2.0/playback.html?") != -1: - recording.presentationUrl = data.source_url - # Management for standard presentation URLs with BBB or Scalelite server - elif data.source_url.find("/playback/presentation/2.3/") != -1: + if check_url_format_presentation(data.source_url): + # Presentation format recording.presentationUrl = data.source_url - # Management of other situations else: + # Management of other situations recording.videoUrl = data.source_url + # Check if the URL is used by an infrastructure that need a token + recording.recordingId = check_url_need_token(data.source_url) + # If possible to encode BBB presentation # Useful for old BBB or BBB 2.6+ without video playback if USE_IMPORT_VIDEO_BBB_RECORDER and recording.presentationUrl != "": recording.canUpload = True recording.videoUrl = recording.presentationUrl - # In all case - if recording.videoUrl == "" or check_url_exists(recording.videoUrl) is False: + + # To optimize, do not check that url exists + if recording.videoUrl == "": recording.state = _( "No video file found. Upload to Pod as a video is not possible." ) recording.canUpload = False recording.videoUrl = "" else: - # For PeerTube, Video file, Youtube + # For PeerTube, Youtube recording.videoUrl = data.source_url # Display type label @@ -1116,8 +1177,39 @@ def get_status_recording(data: ExternalRecording) -> str: """Get the status of an external recording.""" if data.uploaded_to_pod_by is None and data.state is None: state = _("Video file not uploaded to Pod") - elif data.uploaded_to_pod_by is not None and data.state is None: + elif data.uploaded_to_pod_by is not None: state = _("Video file already uploaded to Pod") else: state = data.state return state + + +@csrf_protect +@ensure_csrf_cookie +@login_required(redirect_field_name="referrer") +def recording_with_token(request, id): + """Get for specific recording (recording created on BBB infrastructure that need a + token and used by meeting module), the presentation and video source URL, in JSON. + + Args: + request (Request): HTTP request + record_id (int): record id + + Raises: + PermissionDenied: if user not allowed + + Returns: + HTTPResponse: internal recording (JSON format) + """ + recording = ExternalRecording.objects.get(id=id) + + # Check if the URL is used by an infrastructure that need a token + # In such a case, request the BBB infrastructure to get playbacks with token + presentation_url, video_url = get_playbacks_urls_with_token(recording) + + # JSON format + data = '{"presentationUrl": "%s", "videoUrl": "%s"}' % (presentation_url, video_url) + if request.is_ajax(): + return HttpResponse(data, content_type="application/json") + else: + return HttpResponseBadRequest() diff --git a/pod/locale/fr/LC_MESSAGES/django.po b/pod/locale/fr/LC_MESSAGES/django.po index 4ee0860109..a4e9c88e11 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: 2024-02-29 10:01+0000\n" +"POT-Creation-Date: 2024-03-01 13:49+0100\n" "PO-Revision-Date: \n" "Last-Translator: obado \n" "Language-Team: Pod Team cotech-esup-pod@esup-portail.org\n" @@ -2545,6 +2545,23 @@ msgstr "Etat de l’enregistrement" msgid "User who uploaded to Pod the video file" msgstr "Utilisateur qui a téléversé le fichier vidéo sur Pod" +#: pod/import_video/models.py pod/meeting/models.py +msgid "Unable to call BBB server." +msgstr "Impossible de joindre le serveur BBB." + +#: pod/import_video/models.py +#, python-format +msgid "Parameter %s needs to be defined." +msgstr "Le paramètre %s doit être défini." + +#: pod/import_video/models.py pod/meeting/models.py +msgid "Unable to get recording!" +msgstr "Impossible d’obtenir l’enregistrement !" + +#: pod/import_video/models.py +msgid "Method not allowed" +msgstr "Méthode non autorisée" + #: pod/import_video/models.py msgid "External recordings" msgstr "Enregistrements externes" @@ -2855,15 +2872,6 @@ msgstr "" "Essayez de modifier le type d’enregistrement ou l’adresse de cet " "enregistrement" -#: pod/import_video/utils.py -msgid "The HTML file for this recording was not found on the server." -msgstr "Le fichier HTML de cet enregistrement est introuvable sur le serveur." - -#: pod/import_video/utils.py -#, python-format -msgid "Error number: %s" -msgstr "Numéro d’erreur : %s" - #: pod/import_video/utils.py msgid "The video file for this recording was not found in the HTML file." msgstr "" @@ -2947,8 +2955,17 @@ msgid "" "This video was uploaded to Pod; its origin is %(type)s: %(url)s" msgstr "" -"Cette vidéo a été téléversée sur Pod ; son origine est %(type)s : %(url)s" +"Cette vidéo a été téléversée sur Pod ; son origine est %(type)s : %(url)s" + +#: pod/import_video/views.py +msgid "" +"This video has been uploaded to Pod and its source is the virtual classroom " +"solution of the French Ministry of Higher Education and Research." +msgstr "" +"Cette vidéo a été téléversée sur Pod et sa source est la solution de classe " +"virtuelle du Ministère Français de l'Enseignement Supérieur et de la " +"Recherche." #: pod/import_video/views.py #, python-format @@ -5594,10 +5611,6 @@ msgstr "Impossible de terminer la réunion !" msgid "Unable to get meeting recordings!" msgstr "Impossible d’obtenir les enregistrements de la réunion !" -#: pod/meeting/models.py -msgid "Unable to get recording!" -msgstr "Impossible d’obtenir l’enregistrement !" - #: pod/meeting/models.py msgid "Unable to delete recording!" msgstr "Impossible de supprimer l’enregistrement !" @@ -9265,6 +9278,14 @@ msgstr "Résultats de la recherche" msgid "Esup-Pod xAPI" msgstr "xAPI Esup-Pod" +#~ msgid "The HTML file for this recording was not found on the server." +#~ msgstr "" +#~ "Le fichier HTML de cet enregistrement est introuvable sur le serveur." + +#, python-format +#~ msgid "Error number: %s" +#~ msgstr "Numéro d’erreur : %s" + #~ msgid "Playlist image" #~ msgstr "Image de la playlist" diff --git a/pod/locale/fr/LC_MESSAGES/djangojs.po b/pod/locale/fr/LC_MESSAGES/djangojs.po index ffae9cd7d0..11a9ebb8a1 100644 --- a/pod/locale/fr/LC_MESSAGES/djangojs.po +++ b/pod/locale/fr/LC_MESSAGES/djangojs.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: Esup-Pod\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-29 10:01+0000\n" +"POT-Creation-Date: 2024-03-01 13:49+0100\n" "PO-Revision-Date: \n" "Last-Translator: obado \n" "Language-Team: \n" diff --git a/pod/locale/nl/LC_MESSAGES/django.po b/pod/locale/nl/LC_MESSAGES/django.po index 7acb5d6772..a2d63bde3c 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: 2024-02-29 10:01+0000\n" +"POT-Creation-Date: 2024-03-01 13:49+0100\n" "PO-Revision-Date: 2023-06-08 14:37+0200\n" "Last-Translator: obado \n" "Language-Team: \n" @@ -2421,6 +2421,23 @@ msgstr "" msgid "User who uploaded to Pod the video file" msgstr "" +#: pod/import_video/models.py pod/meeting/models.py +msgid "Unable to call BBB server." +msgstr "" + +#: pod/import_video/models.py +#, python-format +msgid "Parameter %s needs to be defined." +msgstr "" + +#: pod/import_video/models.py pod/meeting/models.py +msgid "Unable to get recording!" +msgstr "" + +#: pod/import_video/models.py +msgid "Method not allowed" +msgstr "" + #: pod/import_video/models.py msgid "External recordings" msgstr "" @@ -2684,15 +2701,6 @@ msgstr "" msgid "Try changing the record type or address for this recording" msgstr "" -#: pod/import_video/utils.py -msgid "The HTML file for this recording was not found on the server." -msgstr "" - -#: pod/import_video/utils.py -#, python-format -msgid "Error number: %s" -msgstr "" - #: pod/import_video/utils.py msgid "The video file for this recording was not found in the HTML file." msgstr "" @@ -2770,6 +2778,12 @@ msgid "" "target=\"_blank\">%(url)s" msgstr "" +#: pod/import_video/views.py +msgid "" +"This video has been uploaded to Pod and its source is the virtual classroom " +"solution of the French Ministry of Higher Education and Research." +msgstr "" + #: pod/import_video/views.py #, python-format msgid "" @@ -5299,10 +5313,6 @@ msgstr "" msgid "Unable to get meeting recordings!" msgstr "" -#: pod/meeting/models.py -msgid "Unable to get recording!" -msgstr "" - #: pod/meeting/models.py msgid "Unable to delete recording!" msgstr "" diff --git a/pod/locale/nl/LC_MESSAGES/djangojs.po b/pod/locale/nl/LC_MESSAGES/djangojs.po index ca7c586d33..af89d1a839 100644 --- a/pod/locale/nl/LC_MESSAGES/djangojs.po +++ b/pod/locale/nl/LC_MESSAGES/djangojs.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: Esup-Pod\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-29 10:01+0000\n" +"POT-Creation-Date: 2024-03-01 13:49+0100\n" "PO-Revision-Date: 2023-02-08 15:22+0100\n" "Last-Translator: obado \n" "Language-Team: \n" diff --git a/pod/meeting/models.py b/pod/meeting/models.py index bb45dfc512..7ee879af89 100644 --- a/pod/meeting/models.py +++ b/pod/meeting/models.py @@ -635,7 +635,7 @@ def create(self, request=None): response = self.get_create_response(url) if response.status_code != 200: msg = {} - msg["error"] = "Unable to call BBB server." + msg["error"] = _("Unable to call BBB server.") msg["returncode"] = response.status_code msg["message"] = response.content.decode("utf-8") raise ValueError(msg) @@ -774,7 +774,7 @@ def get_is_meeting_running(self): response = requests.get(url) if response.status_code != 200: msg = {} - msg["error"] = "Unable to call BBB server." + msg["error"] = _("Unable to call BBB server.") msg["returncode"] = response.status_code msg["message"] = response.content.decode("utf-8") raise ValueError(msg) @@ -810,7 +810,7 @@ def get_meeting_info(self): response = requests.get(url) if response.status_code != 200: msg = {} - msg["error"] = "Unable to call BBB server." + msg["error"] = _("Unable to call BBB server.") msg["returncode"] = response.status_code msg["message"] = response.content.decode("utf-8") raise ValueError(msg) @@ -838,7 +838,7 @@ def end(self): response = requests.get(url) if response.status_code != 200: msg = {} - msg["error"] = "Unable to call BBB server." + msg["error"] = _("Unable to call BBB server.") msg["returncode"] = response.status_code msg["message"] = response.content.decode("utf-8") raise ValueError(msg) @@ -868,7 +868,7 @@ def get_recordings(self): response = requests.get(url) if response.status_code != 200: msg = {} - msg["error"] = "Unable to call BBB server." + msg["error"] = _("Unable to call BBB server.") msg["returncode"] = response.status_code msg["message"] = response.content.decode("utf-8") raise ValueError(msg) @@ -897,7 +897,7 @@ def get_recording(self, record_id): response = requests.get(url) if response.status_code != 200: msg = {} - msg["error"] = "Unable to call BBB server." + msg["error"] = _("Unable to call BBB server.") msg["returncode"] = response.status_code msg["message"] = response.content.decode("utf-8") raise ValueError(msg) @@ -925,7 +925,7 @@ def delete_recording(self, record_id): response = requests.get(url) if response.status_code != 200: msg = {} - msg["error"] = "Unable to call BBB server." + msg["error"] = _("Unable to call BBB server.") msg["returncode"] = response.status_code msg["message"] = response.content.decode("utf-8") raise ValueError(msg) @@ -955,7 +955,7 @@ def get_all_meetings(): response = requests.get(url) if response.status_code != 200: msg = {} - msg["error"] = "Unable to call BBB server." + msg["error"] = _("Unable to call BBB server.") msg["returncode"] = response.status_code msg["message"] = response.content.decode("utf-8") raise ValueError(msg)