Tous les articles
8 min de lecture

Comment transcrire automatiquement un flux RSS de podcast

Un flux RSS de podcast contient déjà les informations dont votre worker de transcription a besoin : titres, dates de publication, GUID et URLs audio. Le travail consiste à transformer chaque nouvel enclosure en transcription, une seule fois, puis à l’indexer.

Pour un épisode isolé, une transcription manuelle suffit. Pour une émission, un réseau ou un catalogue historique, il faut un petit pipeline d’ingestion.

La forme utile reste simple :

Flux RSS

Détection des nouveaux épisodes

Envoi de l’URL audio à l’API de transcription

Polling des jobs

Stockage du transcript et des horodatages

Techtuel expose une API de transcription de podcast et une API de transcription RSS pour ce workflow.

1. Lire le flux RSS

La plupart des flux podcast exposent chaque épisode dans un <item>. L’URL audio se trouve généralement dans la balise <enclosure>.

Stockez au minimum :

  • GUID RSS ;
  • titre de l’épisode ;
  • date de publication ;
  • URL de la page épisode ;
  • URL audio de l’enclosure ;
  • identifiant de job Techtuel ;
  • statut du job.

Le GUID est souvent le meilleur identifiant externe. S’il reste stable chez l’éditeur, il évite les jobs de transcription en double.

2. Détecter les épisodes non indexés

import Parser from 'rss-parser'
 
const parser = new Parser()
 
type StoredEpisode = {
  guid: string
}
 
export async function findNewEpisodes(
  feedUrl: string,
  storedEpisodes: StoredEpisode[],
) {
  const feed = await parser.parseURL(feedUrl)
  const knownGuids = new Set(storedEpisodes.map((episode) => episode.guid))
 
  return feed.items
    .filter((item) => item.guid && !knownGuids.has(item.guid))
    .map((item) => ({
      guid: item.guid!,
      title: item.title ?? 'Épisode sans titre',
      publishedAt: item.isoDate ?? null,
      episodeUrl: item.link ?? null,
      audioUrl: item.enclosure?.url ?? null,
    }))
    .filter((episode) => episode.audioUrl)
}

En production, dédupliquez aussi sur l’URL audio normalisée. Certains flux changent leurs GUID lors de migrations.

3. Soumettre chaque URL audio

Pour une ingestion de flux, utilisez l’endpoint asynchrone. Les épisodes sont longs, et un rattrapage de catalogue ne doit pas dépendre d’une requête HTTP maintenue ouverte.

curl -X POST https://api.techtuel.com/v1/transcriptions \
  -H "Authorization: Bearer $TECHTUEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "audio_url": "https://cdn.example.com/podcast/episode-42.mp3"
  }'

Réponse :

{
  "id": "job_9f2c",
  "status": "processing"
}

Persistez immédiatement cet identifiant à côté de la ligne épisode.

4. Interroger le job jusqu’à son statut final

Il n’y a pas de webhook. Le polling garde l’intégration prévisible et évite d’exposer un endpoint de callback public.

const TERMINAL = new Set(['completed', 'failed'])
 
export async function waitForTranscript(jobId: string) {
  while (true) {
    const response = await fetch(
      `https://api.techtuel.com/v1/transcriptions/${jobId}`,
      { headers: { Authorization: `Bearer ${process.env.TECHTUEL_API_KEY}` } },
    )
 
    const job = await response.json()
    if (TERMINAL.has(job.status)) return job
 
    await new Promise((resolve) => setTimeout(resolve, 15000))
  }
}

Traitez claimed comme processing : un worker a pris le job, mais la transcription n’est pas prête.

5. Stocker la transcription

Le job terminé renvoie le transcript complet et les segments horodatés :

{
  "id": "job_9f2c",
  "status": "completed",
  "title": "La recherche hybride en production",
  "language": "fr",
  "minutes": 54.7,
  "transcript": "Bienvenue dans cet épisode...",
  "segments": [
    {
      "start_seconds": 0,
      "text": "Bienvenue dans cet épisode."
    },
    {
      "start_seconds": 5.8,
      "text": "Aujourd’hui nous parlons de systèmes de recherche."
    }
  ]
}

Stockez le transcript complet et les segments. Le transcript sert aux exports et aux résumés. Les segments servent à la recherche, aux citations et au RAG.

6. Rendre le worker idempotent

L’ingestion de podcast échoue de façons banales :

  • flux temporairement indisponible ;
  • URL d’enclosure expirée ;
  • changement de CDN ;
  • métadonnées modifiées ;
  • épisode présent deux fois ;
  • job de transcription en échec.

Gardez un workflow reprenable :

  • persistez l’identifiant du job avant de poller ;
  • ne soumettez pas un nouveau job si un job est déjà en cours ;
  • ne relancez que les jobs échoués ;
  • stockez le GUID RSS et l’URL audio ;
  • séparez votre statut d’ingestion du statut du job Techtuel.

Si un job Techtuel échoue après un problème transitoire de source, relancez-le avec POST /v1/transcriptions/{id}/retry.

7. Indexer pour la recherche ou le RAG

Une fois stockée, découpez la transcription en chunks avec les métadonnées de l’épisode :

  • titre du podcast ;
  • titre de l’épisode ;
  • URL de l’épisode ;
  • date de publication ;
  • instant de départ ;
  • texte ;
  • identifiant de job Techtuel.

Votre résultat de recherche peut alors citer le passage exact et renvoyer l’auditeur au bon moment.

Pour un guide plus complet sur l’indexation, lisez Transformer vos épisodes de podcast en documents pour le RAG.

Commencer petit

Démarrez avec un flux, un worker et une table. Ajoutez de la concurrence après avoir mesuré le nombre de nouveaux épisodes par jour et la durée réelle de transcription de votre catalogue.

Lire la documentation de l’API

Essayer Techtuel sans carte