Skip to content

Repository files navigation

PodcastTranscribe API

A .NET 9 cloud backend that discovers podcast episodes, prepares audio with FFmpeg, submits asynchronous jobs to Azure AI Speech, and persists workflow state in Azure Cosmos DB.

PodcastTranscribe is an independent backend engineering project built around a multi-stage media workflow rather than a single synchronous API call. The service coordinates external search, bounded audio download, local transcoding, object storage, batch speech recognition, status polling, and durable result retrieval behind an ASP.NET Core REST API.

1. Engineering Profile

Area Implementation
API and application layer C# · .NET 9 · ASP.NET Core controllers · dependency injection · Swagger/OpenAPI
Workflow orchestration Asynchronous submission · explicit transcription states · idempotent episode checks · polling-based synchronization
Cloud services Azure AI Speech Batch Transcription · Azure Blob Storage · Azure Cosmos DB
Media pipeline HTTP range download · streamed file I/O · FFmpeg transcoding · deterministic blob naming
External integration Listen Notes episode search · Azure SDKs · typed configuration through IOptions<T>
Runtime Multi-stage Docker build · ASP.NET 9 runtime · system FFmpeg · structured ILogger<T> logging

2. System Architecture

REST Client
    |
    v
EpisodeController
    |
    v
EpisodeService ---------------------------> CosmosDbService ---> Azure Cosmos DB
    |                         |
    |                         +-----------> Listen Notes API
    |
    +---> TranscriptionSubmissionService
              |          |            |
              |          |            +---> Azure AI Speech
              |          +----------------> Azure Blob Storage
              +---------------------------> Audio host + FFmpeg

The application is a modular monolith: HTTP concerns remain in the controller, use-case decisions live in EpisodeService, and provider-specific behavior is isolated in dedicated services. Interfaces separate high-level orchestration from Blob Storage, Speech, external search, and media-processing details.

3. End-to-End Workflow

3.1 Discover and cache episodes

GET /api/Episode?title=... searches Cosmos DB first. On a cache miss, the service queries Listen Notes, maps provider data into Episode aggregates, persists previously unseen episodes, and returns reduced EpisodeSummary responses.

3.2 Submit transcription work

POST /api/Episode/{id}/transcription checks the persisted state before starting work. Completed or active episodes are not submitted again; a NotStarted episode enters the asynchronous media pipeline and returns immediately to the caller.

3.3 Prepare and hand off media

The submission service:

  1. Transitions the episode to Processing.
  2. Reuses the deterministic {episodeId}_audio.mp3 blob when available.
  3. Otherwise downloads at most 10 MiB using streamed HTTP range reads.
  4. Converts the audio to a 16 kbps, 22,050 Hz mono MP3 with FFmpeg.
  5. Uploads the processed stream to Azure Blob Storage.
  6. Submits the Blob URL to Azure AI Speech Batch Transcription.
  7. Persists the provider job URI and state checkpoint in Cosmos DB.
  8. Removes temporary files in a finally block.

3.4 Synchronize status and results

GET /api/Episode/{id}/transcription polls the Speech job, maps provider states into the domain state machine, retrieves the completed result, and persists display-ready transcript text before returning it.

NotStarted -> Processing -> TranscriptionSubmitted -> TranscriptionRunning -> TranscriptionSucceeded
                    \                    |                    /
                     +-------------------+-------------------> Failed

4. Backend Engineering Highlights

  • Explicit workflow state: transcription progress is represented by a persisted enum rather than inferred from nullable fields or provider responses.
  • Idempotent submission decisions: active and completed episodes do not create duplicate work, while deterministic Blob names allow processed audio to be reused.
  • Durable checkpoints: Blob URI, Speech job URI, provider status, and transcript text are persisted after externally meaningful steps.
  • Bounded media handling: HTTP response streaming, a fixed download limit, reduced bitrate, mono output, and temporary-file cleanup bound local resource use.
  • Provider isolation: Listen Notes, Blob Storage, Cosmos DB, Azure Speech, and FFmpeg behavior remain behind focused service boundaries.
  • Dependency-injected composition: service lifetimes and typed cloud configuration are defined centrally in Program.cs.
  • Containerized runtime: the Docker image separates SDK build and ASP.NET runtime stages and installs the native FFmpeg dependency explicitly.

5. REST API

Method Route Purpose
GET /api/Episode?title={title} Search the local catalog, then Listen Notes on a cache miss
POST /api/Episode/{id}/transcription Start transcription when the episode state permits it
GET /api/Episode/{id}/transcription Synchronize and return the current state or transcript
GET / Basic process health response

6. Code Map

Controllers/
  EpisodeController.cs                 HTTP routes, validation, and response shaping

Services/
  EpisodeService.cs                    Use-case orchestration and state decisions
  TranscriptionSubmissionService.cs    Download, FFmpeg, Blob, and Speech submission pipeline
  AzureSpeechHandlerSeervice.cs        Azure batch job submission, polling, and result parsing
  AzureBlobStorageService.cs           Blob initialization and media operations
  CosmosDbService.cs                   Episode persistence and query operations
  ExternalPodcastSearchService.cs      Listen Notes integration and catalog population

Models/
  Episode.cs                           Persisted aggregate and transcription state machine
  EpisodeSummary.cs                    Search response projection

Configuration/                         Typed cloud-service settings
Program.cs                             Dependency injection and application composition
Dockerfile                             Reproducible .NET + FFmpeg runtime

7. Run Locally

7.1 Prerequisites

  • .NET 9 SDK
  • FFmpeg
  • Azure Cosmos DB database and container partitioned by /id
  • Azure Blob Storage container
  • Azure AI Speech resource
  • Listen Notes API access

7.2 Configuration

The application reads these environment variables, with .env support for local development:

COSMOS_CONNECTION_STRING=
COSMOS_DB_NAME=
COSMOS_CONTAINER_NAME=
BLOB_CONNECTION_STRING=
BLOB_CONTAINER_NAME=
AZURE_SPEECH_KEY=
AZURE_SPEECH_REGION=
AZURE_SPEECH_API_VERSION=
LISTENNOTES_API_KEY=
LISTENNOTES_END_POINT=

7.3 Start the API

dotnet restore
dotnet run

The application listens on http://localhost:5050. Swagger UI is enabled in the Development environment.

7.4 Build the container

docker build -t podcast-transcribe-api .
docker run --env-file .env -p 5050:5050 podcast-transcribe-api

8. Project Context

PodcastTranscribe was built outside work to practice conventional backend engineering across API design, dependency injection, asynchronous workflows, media processing, cloud persistence, external-service integration, and containerized deployment.

About

.NET 9 cloud backend for asynchronous podcast transcription with ASP.NET Core, FFmpeg, Azure AI Speech, Blob Storage, and Cosmos DB.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages