yb

yb — streamline media publishing: prepare once, publish to YouTube or podcast.

Layered by separation of concerns:

  • core (always available): yb.content — build a platform-neutral PublicationContent (title, description, keywords, chapters, captions, thumbnail) from a media file, delegating transcription, chapter detection, and thumbnails to the mixing package.

  • adapters (optional extras): - yb.youtube (pip install 'yb[youtube]') — upload and edit videos. - yb.music (pip install 'yb[music]') — turn songs into music videos

    (rendering via muvid) and publish them, including a whole folder as an album. Uploading also needs yb[youtube].

    • yb.podcast (pip install 'yb[podcast]') — show notes, chapter markers, cover-over-audio video (via muvid), RSS episode item.

    • yb.download (pip install 'yb[download]') — fetch videos/metadata.

The audio→video rendering itself lives in muvid.visualize (yb is the publication layer); yb.music is the thin publish-facing facade over it.

The most common callables are re-exported here for convenience; the ones that live in optional extras are imported lazily, so import yb never fails for a missing extra — the error surfaces only when you actually use that feature.

Examples

>>> import yb
>>> r = yb.download_youtube_video("https://youtu.be/PRa9ciOe-us")  # needs yb[download]
>>> content = yb.prepare_content(r.path, language="English", language_code="en")
>>> yb.prepare_and_publish(r.path, privacy_status="unlisted")        # needs yb[youtube]
class yb.ContentMetadata(title: str, description: str, keywords: list[str] = <factory>)[source]

Platform-neutral copy: a title, a description, and keywords.

class yb.PublicationContent(media: Path, title: str = '', description: str = '', keywords: list[str] = <factory>, chapters: list[Chapter] = <factory>, language: str | None = None, audio_language: str | None = None, srt_path: Path | None = None, thumbnail: Path | None = None, duration: float | None = None)[source]

Everything needed to publish a piece of media, destination-agnostic.

media

Path to the audio/video file.

Type:

pathlib.Path

title

Headline/title.

Type:

str

description

Long-form description / show notes body.

Type:

str

keywords

Topical keywords/tags.

Type:

list[str]

chapters

Ordered chapter markers (mixing.chapters.Chapter).

Type:

list[mixing.chapters.Chapter]

language

BCP-47 code of the metadata text (e.g. "en").

Type:

str | None

audio_language

BCP-47 code of the spoken audio (e.g. "fr").

Type:

str | None

srt_path

Path to the SRT subtitle/caption file, if any.

Type:

pathlib.Path | None

thumbnail

Path to a thumbnail/cover image, if any.

Type:

pathlib.Path | None

duration

Media duration in seconds, if known.

Type:

float | None

description_with_chapters() str[source]

Description with a chapters block appended (if any chapters).

class yb.YbConfig(privacy_status: str = 'unlisted', playlist: str | None = None, create_playlist_if_missing: bool = True, playlist_privacy_status: str = 'private')[source]

Resolved publishing defaults.

privacy_status

Privacy a new upload gets (unlisted | private | public) when the call doesn’t override it.

Type:

str

playlist

Title of a playlist every upload is added to (None = none).

Type:

str | None

create_playlist_if_missing

Create playlist if no playlist of that title exists yet, rather than erroring.

Type:

bool

playlist_privacy_status

Privacy for an auto-created playlist.

Type:

str

classmethod from_mapping(mapping: dict | None) YbConfig[source]

Build a config from a mapping, ignoring unknown keys.

yb.add_video_to_playlist(video_id: str, title: str, *, create: bool = True, privacy_status: str = 'private', skip_if_present: bool = True, service=None, **cred_kwargs) dict[source]

Find-or-create the playlist named title and append video_id.

Idempotent: with skip_if_present (default), a video already in the playlist is left alone instead of duplicated.

Returns:

{"playlist_id", "playlist_title", "added", "created"} where added is False if the video was already present and created is True if the playlist had to be made.

Raises:

KeyError – the playlist is absent and create=False.

yb.default_config_file() Path[source]

Config file location ($XDG_CONFIG_HOME or ~/.config).

yb.download_youtube_audio(url: str, *, download_dir: str | Path | None = None, audio_format: str | None = None, bitrate: str | None = None, keep_original: bool = False, on_error: Literal['raise', 'warn'] = 'raise', fmt: str = 'bestaudio/best', filename_template: str = '%(title)s (%(id)s).%(ext)s', write_info_json: bool = False, write_thumbnail: bool = False, write_description: bool = False, write_subtitles: bool = False, write_auto_subtitles: bool = False, subtitle_langs: tuple[str, ...] = ('en',), quiet: bool = True, extra_opts: dict | None = None) DownloadResult[source]

Download only a video’s audio (no video stream).

Simplest use: download_youtube_audio(url) → the best audio stream, kept in whatever format the source offers (YouTube’s is usually Opus in a .webm container), named Title (video_id).webm in ~/Downloads.

Pass audio_format to get a specific format instead:

>>> download_youtube_audio(url, audio_format="mp3")
Parameters:
  • url – The video URL (or id).

  • download_dir – Destination directory. Defaults to default_download_dir().

  • audio_format – Target format as an extension ("mp3", ".wav", …). The default None means no conversion — keep the downloaded bytes exactly as they came, which needs no ffmpeg and avoids re-encoding a lossy stream into another lossy format.

  • bitrate – Bitrate for lossy targets (e.g. "320k"). Ignored when audio_format is lossless or None.

  • keep_original – When converting, also keep the originally downloaded file (by default it is removed once converted).

  • on_error – What to do when conversion fails (ffmpeg missing or erroring). "raise" (default) propagates AudioConversionError; "warn" emits a warning and returns the unconverted download. Either way the downloaded audio is left on disk — a failed conversion never costs you the download.

  • fmt – yt-dlp format selector (default best audio-only stream).

  • filename_template – yt-dlp output template (default "%(title)s (%(id)s).%(ext)s").

  • write_info_json – Also save the full metadata as *.info.json.

  • write_thumbnail – Also save the thumbnail image.

  • write_description – Also save the description as *.description.

  • write_subtitles – Also save uploaded subtitles for subtitle_langs.

  • write_auto_subtitles – Also save auto-generated subtitles.

  • subtitle_langs – Subtitle languages to fetch when subtitles are enabled.

  • quiet – Suppress yt-dlp console output.

  • extra_opts – Any additional raw yt-dlp options (merged last, so they win).

Returns:

A DownloadResult whose path is the audio file — converted when audio_format was given and conversion succeeded.

Raises:
  • ValueError – If on_error is not "raise" or "warn".

  • AudioConversionError – If conversion fails and on_error="raise".

yb.download_youtube_video(url: str, *, download_dir: str | Path | None = None, fmt: str = 'bestvideo+bestaudio/best', merge_to: str | None = 'mp4', filename_template: str = '%(title)s (%(id)s).%(ext)s', write_info_json: bool = False, write_thumbnail: bool = False, write_description: bool = False, write_subtitles: bool = False, write_auto_subtitles: bool = False, subtitle_langs: tuple[str, ...] = ('en',), quiet: bool = True, extra_opts: dict | None = None) DownloadResult[source]

Download a YouTube video to download_dir (default ~/Downloads).

Simplest use: download_youtube_video(url) → best quality merged to mp4, named Title (video_id).mp4 in ~/Downloads.

Parameters:
  • url – The video URL (or id).

  • download_dir – Destination directory. Defaults to default_download_dir().

  • fmt – yt-dlp format selector (default best video + best audio).

  • merge_to – Container to merge video+audio into (default "mp4"; requires ffmpeg). Set None to keep yt-dlp’s default.

  • filename_template – yt-dlp output template (default "%(title)s (%(id)s).%(ext)s").

  • write_info_json – Also save the full metadata as *.info.json.

  • write_thumbnail – Also save the thumbnail image.

  • write_description – Also save the description as *.description.

  • write_subtitles – Also save uploaded subtitles for subtitle_langs.

  • write_auto_subtitles – Also save auto-generated subtitles.

  • subtitle_langs – Subtitle languages to fetch when subtitles are enabled.

  • quiet – Suppress yt-dlp console output.

  • extra_opts – Any additional raw yt-dlp options (merged last, so they win).

Returns:

A DownloadResult with the media path, trimmed info, and any sidecars written.

yb.format_chapter_lines(chapters: Sequence[Chapter]) str[source]

Render chapters as M:SS Title lines (the shared text convention).

Uses H:MM:SS when any chapter is at or beyond one hour. This is the text format both YouTube descriptions and podcast show notes accept.

yb.generate_metadata(transcript: str, *, language: str = 'English', brand: str | None = None, extra_context: str | None = None, model: str | None = None) ContentMetadata[source]

Generate platform-neutral title/description/keywords from a transcript.

LLM-backed via aix. Pass a ready ContentMetadata to prepare_content() instead if you want to skip generation.

Raises:

ImportErroraix is not importable.

yb.load_config(config_file: str | Path | None = None, **overrides) YbConfig[source]

Load publishing defaults from config_file (or the default location).

A missing file yields the built-in defaults. overrides (typically the explicit keyword arguments a caller passed) take precedence when not None, so call-site arguments always win over the file.

yb.prepare_and_publish(media: str | Path, *, language: str = 'English', language_code: str | None = None, audio_language_code: str | None = None, brand: str | None = None, extra_context: str | None = None, with_chapters: bool = True, with_thumbnail: bool = True, privacy_status: str | None = None, playlist=<object object>, category_id: str = '28', config: YbConfig | None = None, client_secrets_file: str | Path | None = None, token_file: str | Path | None = None, progress: bool = True) dict[source]

One call: prepare publication content from media and upload it.

Transcribes (persisting the SRT next to the media) if needed, writes LLM metadata, detects chapters, renders a thumbnail, then uploads with captions + thumbnail + chapters and adds the video to the configured playlist. privacy_status and playlist default to your yb config (see publish_content()).

yb.prepare_content(media: str | Path, *, language: str = 'English', language_code: str | None = None, audio_language_code: str | None = None, brand: str | None = None, extra_context: str | None = None, transcript: str | None = None, with_chapters: bool = True, with_thumbnail: bool = False, thumbnail_text: str | None = None, metadata: ContentMetadata | None = None, model: str | None = None, transcribe_kwargs: dict | None = None, chapters_kwargs: dict | None = None) PublicationContent[source]

Build a PublicationContent for media (destination-agnostic).

Ensures a persisted SRT next to the media (transcribing once via mixing if needed), writes LLM metadata, detects chapters (default on, auto-skipped for clips too short to host them), and optionally renders a thumbnail. Any precomputed pieces (transcript, metadata) are reused instead of recomputed.

Parameters:
  • media – Audio or video file.

  • language – Human-readable language to write metadata in.

  • audio_language_code (language_code /) – BCP-47 codes for the metadata text and the spoken audio.

  • brand – Product/brand name to keep verbatim in the copy.

  • extra_context – Extra guidance for the copywriter (audience, CTA…).

  • transcript – SRT text to use as-is (skips transcription).

  • with_chapters – Detect chapters (default True).

  • with_thumbnail – Render a thumbnail image (default False).

  • thumbnail_text – Overlay text for the thumbnail (defaults to the title).

  • metadata – Precomputed ContentMetadata to reuse.

  • model – LLM model override.

  • chapters_kwargs (transcribe_kwargs /) – Forwarded to the mixing calls.

Returns:

A populated PublicationContent.

yb.prepare_podcast_episode(media: str | Path, output_dir: str | Path, *, content: PublicationContent | None = None, audio: str | Path | None = None, cover_image: str | Path | None = None, make_cover_video: bool = False, ken_burns: bool = False, embed_chapters: bool = True, language: str = 'English', brand: str | None = None, extra_context: str | None = None, audio_bitrate: str = '192k') PodcastEpisode[source]

Build a podcast episode bundle from media into output_dir.

Parameters:
  • media – Source audio or video.

  • output_dir – Directory to write the episode assets into.

  • content – Precomputed PublicationContent; built via yb.content.prepare_content() when omitted.

  • audio – Explicit episode audio. When omitted, media is used if it is audio, else its audio track is extracted to MP3.

  • cover_image – Cover art (required for make_cover_video).

  • make_cover_video – Also render a cover-over-audio mp4 (e.g. for YouTube).

  • ken_burns – Apply a Ken Burns pan/zoom to the cover video.

  • embed_chapters – Embed ID3 chapter frames into the episode MP3.

  • extra_context (language / brand /) – Forwarded to prepare_content.

  • audio_bitrate – Bitrate for extracted MP3 audio.

Returns:

A PodcastEpisode with the asset paths.

yb.publish_content(content: PublicationContent, *, privacy_status: str | None = None, playlist=<object object>, category_id: str = '28', with_chapters: bool = True, attach_caption: bool = True, set_thumb: bool = True, config: YbConfig | None = None, client_secrets_file: str | Path | None = None, token_file: str | Path | None = None, progress: bool = True, service=None) dict[source]

Upload a prepared PublicationContent to YouTube.

Maps the content to a YouTube snippet (chapters embedded in the description when present), uploads, attaches the SRT caption track (language = content.audio_language or content.language) and the thumbnail when available, and adds the video to the configured playlist.

privacy_status and playlist default to your yb config (see yb.config): unset means “use the config value”, so privacy falls back to unlisted and the video joins your configured playlist. Pass playlist=None to skip the playlist for one call, or playlist="Name" to override the target.

Returns:

{"video_id", "url", "studio_url", "privacy_status", "captions", "thumbnail", "playlist"}. privacy_status reflects what YouTube actually set (an unaudited project may force "private"); playlist is the add_video_to_playlist() result or None.

yb.set_chapters(video_id: str, chapters: Sequence[Chapter], *, header: str = 'Chapters:', service=None, **cred_kwargs) dict[source]

Insert/replace a chapters block in the video’s description.

Strips any prior block under header and appends the new one. YouTube renders interactive chapters when the first timestamp is 0:00.

yb.update_video_fields(video_id: str, *, title: str | None = None, description: str | None = None, tags: Sequence[str] | None = None, category_id: str | None = None, default_language: str | None = None, default_audio_language: str | None = None, service=None, **cred_kwargs) dict[source]

Patch selected snippet fields, preserving the rest.

Fetches the current snippet, overlays the provided fields, and updates. categoryId must be present (kept from the existing snippet if not given).

yb.upsert_caption(video_id: str, track: CaptionTrack | str | Path, *, language: str | None = None, name: str = '', is_draft: bool = False, replace: bool = True, service=None, **cred_kwargs) dict[source]

Insert a caption track, or update the existing same-language one.

Parameters:
  • video_id – Target video.

  • track – A CaptionTrack, or a path (then language required).

  • replace – When an uploaded track in the same language exists, update it (True, default) rather than inserting a duplicate.

Returns:

The inserted/updated caption resource.

yb.youtube_video_info(url: str, *, extra_opts: dict | None = None) dict[str, Any][source]

Fetch a video’s metadata without downloading it.

Returns the trimmed info dict (see _INFO_FIELDS). Useful to preview the title/duration/chapters before deciding to download.