Recherche avancée

Médias (0)

Mot : - Tags -/auteurs

Aucun média correspondant à vos critères n’est disponible sur le site.

Autres articles (27)

  • List of compatible distributions

    26 avril 2011, par

    The table below is the list of Linux distributions compatible with the automated installation script of MediaSPIP. Distribution nameVersion nameVersion number Debian Squeeze 6.x.x Debian Weezy 7.x.x Debian Jessie 8.x.x Ubuntu The Precise Pangolin 12.04 LTS Ubuntu The Trusty Tahr 14.04
    If you want to help us improve this list, you can provide us access to a machine whose distribution is not mentioned above or send the necessary fixes to add (...)

  • ANNEXE : Les plugins utilisés spécifiquement pour la ferme

    5 mars 2010, par

    Le site central/maître de la ferme a besoin d’utiliser plusieurs plugins supplémentaires vis à vis des canaux pour son bon fonctionnement. le plugin Gestion de la mutualisation ; le plugin inscription3 pour gérer les inscriptions et les demandes de création d’instance de mutualisation dès l’inscription des utilisateurs ; le plugin verifier qui fournit une API de vérification des champs (utilisé par inscription3) ; le plugin champs extras v2 nécessité par inscription3 (...)

  • Ajouter notes et légendes aux images

    7 février 2011, par

    Pour pouvoir ajouter notes et légendes aux images, la première étape est d’installer le plugin "Légendes".
    Une fois le plugin activé, vous pouvez le configurer dans l’espace de configuration afin de modifier les droits de création / modification et de suppression des notes. Par défaut seuls les administrateurs du site peuvent ajouter des notes aux images.
    Modification lors de l’ajout d’un média
    Lors de l’ajout d’un média de type "image" un nouveau bouton apparait au dessus de la prévisualisation (...)

Sur d’autres sites (4437)

  • Find a great Google Tag Manager alternative in Matomo Tag Manager

    29 avril 2020, par Joselyn Khor — Analytics Tips, Development, Marketing, Plugins

    If you’re looking for a tag management system that rivals Google’s, then Matomo Tag Manager is a great Google Tag Manager alternative that takes your tracking to the next level.

    What’s a tag manager ?

    If you’re not familiar with Google Tag Manager or Matomo Tag Manager – they’re both free tag management systems that let you manage all your website code snippets (tags) in one place. 

    Tags are typically JavaScript code or HTML that lets you integrate various features into your site in just a few clicks. For example : analytics codes, conversion tracking codes, exit popups and surveys, remarketing codes, social widgets, affiliates, and ads. With a tag manager, you get to easily look into and manage these different tracking codes.

    Why use a tag manager ?

    Tag management systems are game changers because they let you track important data more effectively by easily adding code snippets (tags) to your website. 

    By not needing to hard code each individual code you also save time. Rather than waiting for someone to make tag changes and to deploy your website, you can make the changes yourself without needing the technical expertise of a developer.

    Why is Matomo Tag Manager a great Google Tag Manager alternative ?

     Matomo Tag Manager is a great Google Tag Manager alternative. Not only does it let you manage all your tracking and marketing tags in one place, it also offers less complexity and more flexibility. 

    By tagging your website and using Matomo Tag Manager alongside Matomo Analytics, you can collect much more data than you’d be able to otherwise. 

    A bonus to using Matomo is the privacy and data ownership aspect. With Matomo you also get the added peace of mind that comes with 100% data ownership and privacy protection. You will never be left wondering what’s happening to your data. Rest assured knowing you’re doing the best to protect user privacy, while getting useful insights to improve your website. 

    And since Matomo Tag Manager is the one of the best alternatives to Google Tag Manager, you’ll gain more than you lose by having full confidence that your data is yours to own.

    Three key benefits of using Matomo Tag Manager :

    • Empowers you to deploy and manage your own tags
      This takes the hassle out of needing a web developer to hard code and edit every tag on your website. Now you can deploy tracking code on chosen pages and track various data yourself. 
    • Open up endless possibilities on data tracking
      Dig a lot deeper to track analytics, conversions, and more. Now you can implement advanced tracking solutions without needing to pay an external source. 
    • Save time and create your own impact
      With limited resources you certainly don’t want to be wasting any time having to go back and forth with an external party over what tags to add or take away. An over-dependence on web developers or agencies carrying out tag management for you, stalls growth and experimentation opportunities. With a tag management system you have the convenience of inserting your own tags and getting to a desired outcome faster. You won’t have to forgo tracking opportunities because now it’s in your hands.
  • Play video using mse (media source extension) in google chrome

    23 août 2019, par liyuqihxc

    I’m working on a project that convert rtsp stream (ffmpeg) and play it on the web page (signalr + mse).

    So far it works pretty much as I expected on the latest version of edge and firefox, but not chrome.

    here’s the code

    public class WebmMediaStreamContext
    {
       private Process _ffProcess;
       private readonly string _cmd;
       private byte[] _initSegment;
       private Task _readMediaStreamTask;
       private CancellationTokenSource _cancellationTokenSource;

       private const string _CmdTemplate = "-i {0} -c:v libvpx -tile-columns 4 -frame-parallel 1 -keyint_min 90 -g 90 -f webm -dash 1 pipe:";

       public static readonly byte[] ClusterStart = { 0x1F, 0x43, 0xB6, 0x75, 0x01, 0x00, 0x00, 0x00 };

       public event EventHandler<clusterreadyeventargs> ClusterReadyEvent;

       public WebmMediaStreamContext(string rtspFeed)
       {
           _cmd = string.Format(_CmdTemplate, rtspFeed);
       }

       public async Task StartConverting()
       {
           if (_ffProcess != null)
               throw new InvalidOperationException();

           _ffProcess = new Process();
           _ffProcess.StartInfo = new ProcessStartInfo
           {
               FileName = "ffmpeg/ffmpeg.exe",
               Arguments = _cmd,
               UseShellExecute = false,
               CreateNoWindow = true,
               RedirectStandardOutput = true
           };
           _ffProcess.Start();

           _initSegment = await ParseInitSegmentAndStartReadMediaStream();
       }

       public byte[] GetInitSegment()
       {
           return _initSegment;
       }

       // Find the first cluster, and everything before it is the InitSegment
       private async Task ParseInitSegmentAndStartReadMediaStream()
       {
           Memory<byte> buffer = new byte[10 * 1024];
           int length = 0;
           while (length != buffer.Length)
           {
               length += await _ffProcess.StandardOutput.BaseStream.ReadAsync(buffer.Slice(length));
               int cluster = buffer.Span.IndexOf(ClusterStart);
               if (cluster >= 0)
               {
                   _cancellationTokenSource = new CancellationTokenSource();
                   _readMediaStreamTask = new Task(() => ReadMediaStreamProc(buffer.Slice(cluster, length - cluster).ToArray(), _cancellationTokenSource.Token), _cancellationTokenSource.Token, TaskCreationOptions.LongRunning);
                   _readMediaStreamTask.Start();
                   return buffer.Slice(0, cluster).ToArray();
               }
           }

           throw new InvalidOperationException();
       }

       private void ReadMoreBytes(Span<byte> buffer)
       {
           int size = buffer.Length;
           while (size > 0)
           {
               int len = _ffProcess.StandardOutput.BaseStream.Read(buffer.Slice(buffer.Length - size));
               size -= len;
           }
       }

       // Parse every single cluster and fire ClusterReadyEvent
       private void ReadMediaStreamProc(byte[] bytesRead, CancellationToken cancel)
       {
           Span<byte> buffer = new byte[5 * 1024 * 1024];
           bytesRead.CopyTo(buffer);
           int bufferEmptyIndex = bytesRead.Length;

           do
           {
               if (bufferEmptyIndex &lt; ClusterStart.Length + 4)
               {
                   ReadMoreBytes(buffer.Slice(bufferEmptyIndex, 1024));
                   bufferEmptyIndex += 1024;
               }

               int clusterDataSize = BitConverter.ToInt32(
                   buffer.Slice(ClusterStart.Length, 4)
                   .ToArray()
                   .Reverse()
                   .ToArray()
               );
               int clusterSize = ClusterStart.Length + 4 + clusterDataSize;
               if (clusterSize > buffer.Length)
               {
                   byte[] newBuffer = new byte[clusterSize];
                   buffer.Slice(0, bufferEmptyIndex).CopyTo(newBuffer);
                   buffer = newBuffer;
               }

               if (bufferEmptyIndex &lt; clusterSize)
               {
                   ReadMoreBytes(buffer.Slice(bufferEmptyIndex, clusterSize - bufferEmptyIndex));
                   bufferEmptyIndex = clusterSize;
               }

               ClusterReadyEvent?.Invoke(this, new ClusterReadyEventArgs(buffer.Slice(0, bufferEmptyIndex).ToArray()));

               bufferEmptyIndex = 0;
           } while (!cancel.IsCancellationRequested);
       }
    }
    </byte></byte></byte></clusterreadyeventargs>

    I use ffmpeg to convert the rtsp stream to vp8 WEBM byte stream and parse it to "Init Segment" (ebml head、info、tracks...) and "Media Segment" (cluster), then send it to browser via signalR

    $(function () {

       var mediaSource = new MediaSource();
       var mimeCodec = 'video/webm; codecs="vp8"';

       var video = document.getElementById('video');

       mediaSource.addEventListener('sourceopen', callback, false);
       function callback(e) {
           var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
           var queue = [];

           sourceBuffer.addEventListener('updateend', function () {
               if (queue.length === 0) {
                   return;
               }

               var base64 = queue[0];
               if (base64.length === 0) {
                   mediaSource.endOfStream();
                   queue.shift();
                   return;
               } else {
                   var buffer = new Uint8Array(atob(base64).split("").map(function (c) {
                       return c.charCodeAt(0);
                   }));
                   sourceBuffer.appendBuffer(buffer);
                   queue.shift();
               }
           }, false);

           var connection = new signalR.HubConnectionBuilder()
               .withUrl("/signalr-video")
               .configureLogging(signalR.LogLevel.Information)
               .build();
           connection.start().then(function () {
               connection.stream("InitVideoReceive")
                   .subscribe({
                       next: function(item) {
                           if (queue.length === 0 &amp;&amp; !!!sourceBuffer.updating) {
                               var buffer = new Uint8Array(atob(item).split("").map(function (c) {
                                   return c.charCodeAt(0);
                               }));
                               sourceBuffer.appendBuffer(buffer);
                               console.log(blockindex++ + " : " + buffer.byteLength);
                           } else {
                               queue.push(item);
                           }
                       },
                       complete: function () {
                           queue.push('');
                       },
                       error: function (err) {
                           console.error(err);
                       }
                   });
           });
       }
       video.src = window.URL.createObjectURL(mediaSource);
    })

    chrome just play the video for 3 5 seconds and then stop for buffering, even though there are plenty of cluster transfered and inserted into SourceBuffer.

    here’s the information in chrome ://media-internals/

    Player Properties :

    render_id: 217
    player_id: 1
    origin_url: http://localhost:52531/
    frame_url: http://localhost:52531/
    frame_title: Home Page
    url: blob:http://localhost:52531/dcb25d89-9830-40a5-ba88-33c13b5c03eb
    info: Selected FFmpegVideoDecoder for video decoding, config: codec: vp8 format: 1 profile: vp8 coded size: [1280,720] visible rect: [0,0,1280,720] natural size: [1280,720] has extra data? false encryption scheme: Unencrypted rotation: 0°
    pipeline_state: kSuspended
    found_video_stream: true
    video_codec_name: vp8
    video_dds: false
    video_decoder: FFmpegVideoDecoder
    duration: unknown
    height: 720
    width: 1280
    video_buffering_state: BUFFERING_HAVE_NOTHING
    for_suspended_start: false
    pipeline_buffering_state: BUFFERING_HAVE_NOTHING
    event: PAUSE

    Log

    Timestamp       Property            Value
    00:00:00 00     origin_url          http://localhost:52531/
    00:00:00 00     frame_url           http://localhost:52531/
    00:00:00 00     frame_title         Home Page
    00:00:00 00     url                 blob:http://localhost:52531/dcb25d89-9830-40a5-ba88-33c13b5c03eb
    00:00:00 00     info                ChunkDemuxer: buffering by DTS
    00:00:00 35     pipeline_state      kStarting
    00:00:15 213    found_video_stream  true
    00:00:15 213    video_codec_name    vp8
    00:00:15 216    video_dds           false
    00:00:15 216    video_decoder       FFmpegVideoDecoder
    00:00:15 216    info                Selected FFmpegVideoDecoder for video decoding, config: codec: vp8 format: 1 profile: vp8 coded size: [1280,720] visible rect: [0,0,1280,720] natural size: [1280,720] has extra data? false encryption scheme: Unencrypted rotation: 0°
    00:00:15 216    pipeline_state      kPlaying
    00:00:15 213    duration            unknown
    00:00:16 661    height              720
    00:00:16 661    width               1280
    00:00:16 665    video_buffering_state       BUFFERING_HAVE_ENOUGH
    00:00:16 665    for_suspended_start         false
    00:00:16 665    pipeline_buffering_state    BUFFERING_HAVE_ENOUGH
    00:00:16 667    pipeline_state      kSuspending
    00:00:16 670    pipeline_state      kSuspended
    00:00:52 759    info                Effective playback rate changed from 0 to 1
    00:00:52 759    event               PLAY
    00:00:52 759    pipeline_state      kResuming
    00:00:52 760    video_dds           false
    00:00:52 760    video_decoder       FFmpegVideoDecoder
    00:00:52 760    info                Selected FFmpegVideoDecoder for video decoding, config: codec: vp8 format: 1 profile: vp8 coded size: [1280,720] visible rect: [0,0,1280,720] natural size: [1280,720] has extra data? false encryption scheme: Unencrypted rotation: 0°
    00:00:52 760    pipeline_state      kPlaying
    00:00:52 793    height              720
    00:00:52 793    width               1280
    00:00:52 798    video_buffering_state       BUFFERING_HAVE_ENOUGH
    00:00:52 798    for_suspended_start         false
    00:00:52 798    pipeline_buffering_state    BUFFERING_HAVE_ENOUGH
    00:00:56 278    video_buffering_state       BUFFERING_HAVE_NOTHING
    00:00:56 295    for_suspended_start         false
    00:00:56 295    pipeline_buffering_state    BUFFERING_HAVE_NOTHING
    00:01:20 717    event               PAUSE
    00:01:33 538    event               PLAY
    00:01:35 94     event               PAUSE
    00:01:55 561    pipeline_state      kSuspending
    00:01:55 563    pipeline_state      kSuspended

    Can someone tell me what’s wrong with my code, or dose chrome require some magic configuration to work ?

    Thanks 

    Please excuse my english :)

  • How to Stream Audio from Google Cloud Storage in Chunks and Convert Each Chunk to WAV for Whisper Transcription

    14 novembre 2024, par Douglas Landvik

    I'm working on a project where I need to transcribe audio stored in a Google Cloud Storage bucket using OpenAI's Whisper model. The audio is stored in WebM format with Opus encoding, and due to the file size, I'm streaming the audio in 30-second chunks.

    &#xA;

    To convert each chunk to WAV (16 kHz, mono, 16-bit PCM) compatible with Whisper, I'm using FFmpeg. The first chunk converts successfully, but subsequent chunks fail to convert. I suspect this is because each chunk lacks the WebM container's header, which FFmpeg needs to interpret the Opus codec correctly.

    &#xA;

    Here’s a simplified version of my approach :

    &#xA;

    Download Chunk : I download each chunk from GCS as bytes.&#xA;Convert with FFmpeg : I pass the bytes to FFmpeg to convert each chunk from WebM/Opus to WAV.

    &#xA;

    async def handle_transcription_and_notify(&#xA;    consultation_service: ConsultationService,&#xA;    consultation_processor: ConsultationProcessor,&#xA;    consultation: Consultation,&#xA;    language: str,&#xA;    notes: str,&#xA;    clinic_id: str,&#xA;    vet_email: str,&#xA;    trace_id: str,&#xA;    blob_path: str,&#xA;    max_retries: int = 3,&#xA;    retry_delay: int = 5,&#xA;    max_concurrent_tasks: int = 3&#xA;):&#xA;    """&#xA;    Handles the transcription process by streaming the file from GCS, converting to a compatible format, &#xA;    and notifying the client via WebSocket.&#xA;    """&#xA;    chunk_duration_sec = 30  # 30 seconds per chunk&#xA;    logger.info(f"Starting transcription process for consultation {consultation.consultation_id}",&#xA;                extra={&#x27;trace_id&#x27;: trace_id})&#xA;&#xA;    # Initialize GCS client&#xA;    service_account_key = os.environ.get(&#x27;SERVICE_ACCOUNT_KEY_BACKEND&#x27;)&#xA;    if not service_account_key:&#xA;        logger.error("Service account key not found in environment variables", extra={&#x27;trace_id&#x27;: trace_id})&#xA;        await send_discord_alert(&#xA;            f"Service account key not found for consultation {consultation.consultation_id}.\nTrace ID: {trace_id}"&#xA;        )&#xA;        return&#xA;&#xA;    try:&#xA;        service_account_info = json.loads(service_account_key)&#xA;        credentials = service_account.Credentials.from_service_account_info(service_account_info)&#xA;    except Exception as e:&#xA;        logger.error(f"Error loading service account credentials: {str(e)}", extra={&#x27;trace_id&#x27;: trace_id})&#xA;        await send_discord_alert(&#xA;            f"Error loading service account credentials for consultation {consultation.consultation_id}.\nError: {str(e)}\nTrace ID: {trace_id}"&#xA;        )&#xA;        return&#xA;&#xA;    # Initialize GCS client&#xA;    service_account_key = os.environ.get(&#x27;SERVICE_ACCOUNT_KEY_BACKEND&#x27;)&#xA;    if not service_account_key:&#xA;        logger.error("Service account key not found in environment variables", extra={&#x27;trace_id&#x27;: trace_id})&#xA;        await send_discord_alert(&#xA;            f"Service account key not found for consultation {consultation.consultation_id}.\nTrace ID: {trace_id}"&#xA;        )&#xA;        return&#xA;&#xA;    try:&#xA;        service_account_info = json.loads(service_account_key)&#xA;        credentials = service_account.Credentials.from_service_account_info(service_account_info)&#xA;    except Exception as e:&#xA;        logger.error(f"Error loading service account credentials: {str(e)}", extra={&#x27;trace_id&#x27;: trace_id})&#xA;        await send_discord_alert(&#xA;            f"Error loading service account credentials for consultation {consultation.consultation_id}.\nError: {str(e)}\nTrace ID: {trace_id}"&#xA;        )&#xA;        return&#xA;&#xA;    storage_client = storage.Client(credentials=credentials)&#xA;    bucket_name = &#x27;vetz_consultations&#x27;&#xA;    blob = storage_client.bucket(bucket_name).get_blob(blob_path)&#xA;    bytes_per_second = 16000 * 2  # 32,000 bytes per second&#xA;    chunk_size_bytes = 30 * bytes_per_second&#xA;    size = blob.size&#xA;&#xA;    async def stream_blob_in_chunks(blob, chunk_size):&#xA;        loop = asyncio.get_running_loop()&#xA;        start = 0&#xA;        size = blob.size&#xA;        while start &lt; size:&#xA;            end = min(start &#x2B; chunk_size - 1, size - 1)&#xA;            try:&#xA;                logger.info(f"Requesting chunk from {start} to {end}", extra={&#x27;trace_id&#x27;: trace_id})&#xA;                chunk = await loop.run_in_executor(&#xA;                    None, lambda: blob.download_as_bytes(start=start, end=end)&#xA;                )&#xA;                if not chunk:&#xA;                    break&#xA;                logger.info(f"Yielding chunk from {start} to {end}, size: {len(chunk)} bytes",&#xA;                            extra={&#x27;trace_id&#x27;: trace_id})&#xA;                yield chunk&#xA;                start &#x2B;= chunk_size&#xA;            except Exception as e:&#xA;                logger.error(f"Error downloading chunk from {start} to {end}: {str(e)}", exc_info=True,&#xA;                             extra={&#x27;trace_id&#x27;: trace_id})&#xA;                raise e&#xA;&#xA;    async def convert_to_wav(chunk_bytes, chunk_idx):&#xA;        """&#xA;        Convert audio chunk to WAV format compatible with Whisper, ensuring it&#x27;s 16 kHz, mono, and 16-bit PCM.&#xA;        """&#xA;        try:&#xA;            logger.debug(f"Processing chunk {chunk_idx}: size = {len(chunk_bytes)} bytes")&#xA;&#xA;            detected_format = await detect_audio_format(chunk_bytes)&#xA;            logger.info(f"Detected audio format for chunk {chunk_idx}: {detected_format}")&#xA;            input_io = io.BytesIO(chunk_bytes)&#xA;            output_io = io.BytesIO()&#xA;&#xA;            # ffmpeg command to convert webm/opus to WAV with 16 kHz, mono, and 16-bit PCM&#xA;&#xA;            # ffmpeg command with debug information&#xA;            ffmpeg_command = [&#xA;                "ffmpeg",&#xA;                "-loglevel", "debug",&#xA;                "-f", "s16le",            # Treat input as raw PCM data&#xA;                "-ar", "48000",           # Set input sample rate&#xA;                "-ac", "1",               # Set input to mono&#xA;                "-i", "pipe:0",&#xA;                "-ar", "16000",           # Set output sample rate to 16 kHz&#xA;                "-ac", "1",               # Ensure mono output&#xA;                "-sample_fmt", "s16",     # Set output format to 16-bit PCM&#xA;                "-f", "wav",              # Output as WAV format&#xA;                "pipe:1"&#xA;            ]&#xA;&#xA;            process = subprocess.Popen(&#xA;                ffmpeg_command,&#xA;                stdin=subprocess.PIPE,&#xA;                stdout=subprocess.PIPE,&#xA;                stderr=subprocess.PIPE&#xA;            )&#xA;&#xA;            stdout, stderr = process.communicate(input=input_io.read())&#xA;&#xA;            if process.returncode == 0:&#xA;                logger.info(f"FFmpeg conversion completed successfully for chunk {chunk_idx}")&#xA;                output_io.write(stdout)&#xA;                output_io.seek(0)&#xA;&#xA;                # Save the WAV file locally for listening&#xA;                output_dir = "converted_chunks"&#xA;                os.makedirs(output_dir, exist_ok=True)&#xA;                file_path = os.path.join(output_dir, f"chunk_{chunk_idx}.wav")&#xA;&#xA;                with open(file_path, "wb") as f:&#xA;                    f.write(stdout)&#xA;                logger.info(f"Chunk {chunk_idx} saved to {file_path}")&#xA;&#xA;                return output_io&#xA;            else:&#xA;                logger.error(f"FFmpeg failed for chunk {chunk_idx} with return code {process.returncode}")&#xA;                logger.error(f"Chunk {chunk_idx} - FFmpeg stderr: {stderr.decode()}")&#xA;                return None&#xA;&#xA;        except Exception as e:&#xA;            logger.error(f"Unexpected error in FFmpeg conversion for chunk {chunk_idx}: {str(e)}")&#xA;            return None&#xA;&#xA;    async def transcribe_chunk(idx, chunk_bytes):&#xA;        for attempt in range(1, max_retries &#x2B; 1):&#xA;            try:&#xA;                logger.info(f"Transcribing chunk {idx &#x2B; 1} (attempt {attempt}).", extra={&#x27;trace_id&#x27;: trace_id})&#xA;&#xA;                # Convert to WAV format&#xA;                wav_io = await convert_to_wav(chunk_bytes, idx)&#xA;                if not wav_io:&#xA;                    logger.error(f"Failed to convert chunk {idx &#x2B; 1} to WAV format.")&#xA;                    return ""&#xA;&#xA;                wav_io.name = "chunk.wav"&#xA;                chunk_transcription = await consultation_processor.transcribe_audio_whisper(wav_io)&#xA;                logger.info(f"Chunk {idx &#x2B; 1} transcribed successfully.", extra={&#x27;trace_id&#x27;: trace_id})&#xA;                return chunk_transcription&#xA;            except Exception as e:&#xA;                logger.error(f"Error transcribing chunk {idx &#x2B; 1} (attempt {attempt}): {str(e)}", exc_info=True,&#xA;                             extra={&#x27;trace_id&#x27;: trace_id})&#xA;                if attempt &lt; max_retries:&#xA;                    await asyncio.sleep(retry_delay)&#xA;                else:&#xA;                    await send_discord_alert(&#xA;                        f"Max retries reached for chunk {idx &#x2B; 1} in consultation {consultation.consultation_id}.\nError: {str(e)}\nTrace ID: {trace_id}"&#xA;                    )&#xA;                    return ""  # Return empty string for failed chunk&#xA;&#xA;    await notification_manager.send_personal_message(&#xA;        f"Consultation {consultation.consultation_id} is being transcribed.", vet_email&#xA;    )&#xA;&#xA;    try:&#xA;        idx = 0&#xA;        full_transcription = []&#xA;        async for chunk in stream_blob_in_chunks(blob, chunk_size_bytes):&#xA;            transcription = await transcribe_chunk(idx, chunk)&#xA;            if transcription:&#xA;                full_transcription.append(transcription)&#xA;            idx &#x2B;= 1&#xA;&#xA;        combined_transcription = " ".join(full_transcription)&#xA;        consultation.full_transcript = (consultation.full_transcript or "") &#x2B; " " &#x2B; combined_transcription&#xA;        consultation_service.save_consultation(clinic_id, vet_email, consultation)&#xA;        logger.info(f"Transcription saved for consultation {consultation.consultation_id}.",&#xA;                    extra={&#x27;trace_id&#x27;: trace_id})&#xA;&#xA;    except Exception as e:&#xA;        logger.error(f"Error during transcription process: {str(e)}", exc_info=True, extra={&#x27;trace_id&#x27;: trace_id})&#xA;        await send_discord_alert(&#xA;            f"Error during transcription process for consultation {consultation.consultation_id}.\nError: {str(e)}\nTrace ID: {trace_id}"&#xA;        )&#xA;        return&#xA;&#xA;    await notification_manager.send_personal_message(&#xA;        f"Consultation {consultation.consultation_id} has been transcribed.", vet_email&#xA;    )&#xA;&#xA;    try:&#xA;        template_service = TemplateService()&#xA;        medical_record_template = template_service.get_template_by_name(&#xA;            consultation.medical_record_template_id).sections&#xA;&#xA;        sections = await consultation_processor.extract_structured_sections(&#xA;            transcription=consultation.full_transcript,&#xA;            notes=notes,&#xA;            language=language,&#xA;            template=medical_record_template,&#xA;        )&#xA;        consultation.sections = sections&#xA;        consultation_service.save_consultation(clinic_id, vet_email, consultation)&#xA;        logger.info(f"Sections processed for consultation {consultation.consultation_id}.",&#xA;                    extra={&#x27;trace_id&#x27;: trace_id})&#xA;    except Exception as e:&#xA;        logger.error(f"Error processing sections for consultation {consultation.consultation_id}: {str(e)}",&#xA;                     exc_info=True, extra={&#x27;trace_id&#x27;: trace_id})&#xA;        await send_discord_alert(&#xA;            f"Error processing sections for consultation {consultation.consultation_id}.\nError: {str(e)}\nTrace ID: {trace_id}"&#xA;        )&#xA;        raise e&#xA;&#xA;    await notification_manager.send_personal_message(&#xA;        f"Consultation {consultation.consultation_id} is fully processed.", vet_email&#xA;    )&#xA;    logger.info(f"Successfully processed consultation {consultation.consultation_id}.",&#xA;                extra={&#x27;trace_id&#x27;: trace_id})&#xA;&#xA;

    &#xA;