Source code for bundle.youtube.track
# Copyright 2026 HorusElohim
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import hashlib
import re
from pathlib import Path
from typing import Literal
from ..core import data
[docs]
def sanitize_string(input_string: str) -> str:
invalid_chars = r'[<>:"/\\|?*\0\n\r&]'
sanitized_string = re.sub(invalid_chars, "", input_string)
sanitized_string = sanitized_string.replace(" ", " ")
return sanitized_string
[docs]
def get_identifier(filename: str) -> str:
return hashlib.sha256(filename.encode("utf-8")).hexdigest()
[docs]
class TrackData(data.Data):
title: str = data.Field(default_factory=str)
author: str = data.Field(default_factory=str)
duration: int = data.Field(default_factory=int)
identifier: str = data.Field(default_factory=str)
filename: str = data.Field(default_factory=str)
[docs]
@data.model_validator(mode="after")
def post_init(self):
self.author = sanitize_string(self.author)
self.title = sanitize_string(self.title)
self.filename = f"{self.author}-{self.title}"
self.identifier = get_identifier(self.filename)
return self
[docs]
class YoutubeTrackData(TrackData):
audio_url: str = data.Field(default_factory=str)
video_url: str = data.Field(default_factory=str)
thumbnail_url: str = data.Field(default_factory=str)
audio_mime_type: str = data.Field(default_factory=str)
video_mime_type: str = data.Field(default_factory=str)
audio_streams: list["YoutubeStreamOption"] = data.Field(default_factory=list)
video_streams: list["YoutubeStreamOption"] = data.Field(default_factory=list)
[docs]
def is_resolved(self) -> bool:
"""Return True when the resolver filled the stream URLs."""
return bool(self.video_url.strip() or self.audio_url.strip() or self.video_streams or self.audio_streams)
[docs]
class YoutubeStreamOption(data.Data):
itag: int
kind: Literal["audio", "video"]
url: str = data.Field(default_factory=str)
resolution: str = data.Field(default_factory=str)
abr: str = data.Field(default_factory=str)
fps: int = 0
mime_type: str = data.Field(default_factory=str)
progressive: bool = False
filesize: int = 0
[docs]
class YoutubeResolveOptions(data.Data):
select_video_itag: int | None = None
select_audio_itag: int | None = None
best: bool = True
[docs]
class MP3TrackData(TrackData):
path: Path
[docs]
class MP4TrackData(TrackData):
path: Path