Documentation
¶
Overview ¶
Package waxtap provides the public WaxTap API for acquiring and processing audio.
WaxTap can take audio from YouTube or from a local file. Processing stages such as transcoding, cutting, SponsorBlock removal, loudness measurement, and loudness normalization are opt-in. By default, downloads keep the selected source stream and do not re-encode it.
Client.Download and Client.Stream handle one video. Client.DownloadPlaylist downloads playlist entries with bounded concurrency, optional pacing, and an optional limit on download attempts. Options configures per-host request rates and post-rate-limit cooldowns.
The default client chain returns playable audio for public videos with no PO token. WEB-family clients need a POTokenProvider and remain experimental. For byte-exact session coherence with a token minter, Options.Session / Options.SessionProvider adopt an externally supplied guest visitorData and cookies verbatim instead of bootstrapping; adoption requires a uniform client chain (Options.Client or a single-family profile override) and resolves once per Client.
This top-level package is the stable public surface. The youtube package and packages below it are YouTube-specific implementation surfaces; they are exported where the facade needs them, but external callers should prefer this package.
Type ownership ¶
To keep the dependency graph acyclic, contract types are defined in the package that owns the behavior and re-exported here for convenience:
- audio formats and selectors: package format
- the PO-token provider contract: package potoken
- the SponsorBlock category vocabulary: package sponsorblock
- the error taxonomy: package waxerr
- extraction models (Video, Playlist): package youtube
Callers can work entirely through waxtap, using names such as BestAudio, ErrVideoUnavailable, and POTokenProvider.
Identity anchors ¶
[Video.ID] (the 11-character video ID) and [Video.ChannelID] (the UC channel ID) are the canonical, stable YouTube identifiers. Callers that persist or deduplicate should key on these rather than on titles or URLs. [Video.URL] is the canonical watch URL derived from [Video.ID].
Availability errors: skip vs. fail ¶
Info and Download return typed availability sentinels for videos that exist but cannot be delivered. A consumer iterating a feed should treat these as "skip this item and continue," not as a hard failure, because retrying or updating the tool will not help:
- ErrLiveContent: the stream is currently live (retry after it ends)
- ErrLiveNotStarted: an upcoming premiere or offline stream (retry later)
- ErrLoginRequired: sign-in or an interactive confirm gate
- ErrAgeRestricted: age-gated (rare, since the default client bypasses age-gating)
- ErrVideoRestricted: private (maps a consumer's ErrPrivate)
- ErrMembersOnly: channel-members only
- ErrGeoBlocked: blocked in the request IP's region
- ErrVideoUnavailable: removed or generic-unavailable (maps a consumer's ErrRemoved)
- ErrNoAudioFormats: no audio rendition exists
Everything else is a hard error the consumer should surface: extraction and cipher maintenance signals (ErrExtractionFailed, ErrCipherSolve, ErrPlaylistParse), rate limiting (ErrRateLimited), incomplete delivery (ErrIncompleteStream, ErrURLExpired), token preconditions (ErrNeedsPOToken), and network or I/O failures. Match sentinels with errors.Is; a PlayabilityError (via errors.As / errors.AsType) carries YouTube's Status and Reason for finer classification. Members-only and geo-blocked matching is best-effort under the default en/US locale.
Example (Errors) ¶
Example_errors shows the two ways to inspect a failure from Download or Info: errors.Is for the sentinel category, and errors.As for YouTube's structured detail. The re-exported error types satisfy error on their pointer type, so errors.As takes a double-pointer target (var pe *PlayabilityError; &pe).
package main
import (
"errors"
"fmt"
"github.com/colespringer/waxtap/v2"
)
func main() {
// In real use this comes from client.Download/Info; constructed here for a
// self-contained example.
var err error = &waxtap.PlayabilityError{
Status: "UNPLAYABLE",
Reason: "This video is unavailable",
Sentinel: waxtap.ErrVideoUnavailable,
}
if errors.Is(err, waxtap.ErrVideoUnavailable) {
fmt.Println("category: video unavailable")
}
var pe *waxtap.PlayabilityError
if errors.As(err, &pe) {
fmt.Printf("status: %s\n", pe.Status)
}
}
Output: category: video unavailable status: UNPLAYABLE
Index ¶
- Constants
- Variables
- func BestForTarget(candidates []Format, policy SourcePolicy, target Target) (int, error)
- func ParseNetscapeCookies(path string) ([]*http.Cookie, error)
- func ValidateProcessSpec(s ProcessSpec) error
- type AlbumLoudnessResult
- type AlbumProcessResult
- type AlbumTrack
- type AudioQualityTier
- type AudioSelector
- type AudioTrack
- type Availability
- type Category
- type ChannelLayout
- type Chapter
- type Client
- func (c *Client) Download(ctx context.Context, req Request) (res *Result, err error)
- func (c *Client) DownloadPlaylist(ctx context.Context, url string, o PlaylistDownloadOptions) (*PlaylistRunResult, error)
- func (c *Client) Enumerate(ctx context.Context, url string, opts EnumerateOptions) (*Playlist, error)
- func (c *Client) Info(ctx context.Context, url string, depth InfoDepth, opts ...ReadOption) (*Video, error)
- func (c *Client) InfoResult(ctx context.Context, url string, depth InfoDepth, opts ...ReadOption) (*InfoResult, error)
- func (c *Client) Measure(ctx context.Context, path string) (LoudnessInfo, error)
- func (c *Client) MeasureAlbum(ctx context.Context, paths []string) (*AlbumLoudnessResult, error)
- func (c *Client) ProbeCodec(ctx context.Context, path string) (string, error)
- func (c *Client) Process(ctx context.Context, req ProcessRequest) (res *Result, err error)
- func (c *Client) ProcessAlbum(ctx context.Context, tracks []AlbumTrack, target float64, spec TranscodeSpec) (*AlbumProcessResult, error)
- func (c *Client) Resolve(ctx context.Context, url string, sel AudioSelector, opts ...ReadOption) (ResolvedStream, error)
- func (c *Client) SponsorBlockSegments(ctx context.Context, videoURL string, categories []Category) ([]Segment, error)
- func (c *Client) Stream(ctx context.Context, req Request) (rc io.ReadCloser, info StreamInfo, err error)
- type Concurrency
- type CutMode
- type CutSpec
- type EnumerateOptions
- type Event
- type ExtractionError
- type Format
- type HTTPStatusError
- type InfoDepth
- type InfoResult
- type LiveStatus
- type Locale
- type LoudnessInfo
- type LoudnessMode
- type LoudnessResult
- type LoudnessSpec
- type Options
- type Output
- type POTokenFailure
- type POTokenProvider
- type POTokenProviderFunc
- type POTokenRequest
- type POTokenResponse
- type POTokenScope
- type POTokenSession
- type POTokenSessionProvider
- type PlayabilityError
- type PlayerContext
- type PlayerContextFormat
- type PlayerContextProvider
- type PlayerContextProviderFunc
- type Playlist
- type PlaylistDownloadOptions
- type PlaylistEntry
- type PlaylistItemOutcome
- type PlaylistRunResult
- type PlaylistUnavailableError
- type Politeness
- type ProcessRequest
- type ProcessSpec
- type ProviderError
- type RateLimitError
- type ReadOption
- type Request
- type RequestedFormatError
- type ResolvedStream
- type Result
- type RetryPolicy
- type Segment
- type SidecarError
- type SidecarOption
- type SidecarResponseError
- type SourceKind
- type SourcePolicy
- type SponsorBlockErrorPolicy
- type SponsorBlockOptions
- type Stage
- type StreamInfo
- type Target
- type Thumbnail
- type TimeRange
- type Timeouts
- type TranscodeFormat
- type TranscodeSpec
- type Tri
- type Video
- type VideoMetadata
- type Warning
- type WarningCode
Examples ¶
Constants ¶
Tri values.
const ( QualityUnknown = format.QualityUnknown QualityUltraLow = format.QualityUltraLow QualityLow = format.QualityLow QualityMedium = format.QualityMedium QualityHigh = format.QualityHigh )
Audio quality tiers reported by YouTube.
const ( LayoutAny = format.LayoutAny LayoutMono = format.LayoutMono LayoutStereo = format.LayoutStereo LayoutSurround = format.LayoutSurround )
Channel layouts used by AudioSelector.WithChannels and ProcessSpec.Channels. LayoutAny is the neutral zero value.
const ( CategorySponsor = sponsorblock.CategorySponsor CategorySelfPromo = sponsorblock.CategorySelfPromo CategoryInteraction = sponsorblock.CategoryInteraction CategoryIntro = sponsorblock.CategoryIntro CategoryOutro = sponsorblock.CategoryOutro CategoryPreview = sponsorblock.CategoryPreview CategoryFiller = sponsorblock.CategoryFiller CategoryMusicOffTopic = sponsorblock.CategoryMusicOffTopic )
SponsorBlock categories. Values match the SponsorBlock API wire strings.
const ( LiveNone = youtube.LiveNone LiveUpcoming = youtube.LiveUpcoming LiveNow = youtube.LiveNow LiveWasLive = youtube.LiveWasLive )
LiveStatus values.
const ( AvailabilityUnknown = youtube.AvailabilityUnknown AvailabilityPublic = youtube.AvailabilityPublic AvailabilityUnlisted = youtube.AvailabilityUnlisted )
Availability values.
const ( ScopeNone = potoken.ScopeNone // no token scope ScopePlayer = potoken.ScopePlayer // /player request body ScopeGVS = potoken.ScopeGVS // googlevideo media URL ScopeSubtitles = potoken.ScopeSubtitles // subtitle or timed-text URL )
PO-token scopes identify where a token will be used. Tokens are not interchangeable across scopes.
Variables ¶
var ( // YouTube extraction maintenance signals. ErrExtractionFailed = waxerr.ErrExtractionFailed ErrCipherSolve = waxerr.ErrCipherSolve ErrNeedsPOToken = waxerr.ErrNeedsPOToken ErrURLExpired = waxerr.ErrURLExpired // ErrIncompleteStream indicates that a client returned a detectably truncated // stream. Another client may still deliver the complete stream. ErrIncompleteStream = waxerr.ErrIncompleteStream // Availability failures. ErrVideoRestricted = waxerr.ErrVideoRestricted ErrLoginRequired = waxerr.ErrLoginRequired // ErrLiveContent indicates a stream that is currently live; ErrLiveNotStarted // indicates an upcoming/premiere or offline stream that may become available. ErrLiveContent = waxerr.ErrLiveContent ErrLiveNotStarted = waxerr.ErrLiveNotStarted // ErrAgeRestricted, ErrMembersOnly, and ErrGeoBlocked are specific availability // verdicts. The default ANDROID_VR client bypasses age-gating, so // ErrAgeRestricted is near-unreachable unless a stricter client is forced. ErrAgeRestricted = waxerr.ErrAgeRestricted ErrMembersOnly = waxerr.ErrMembersOnly ErrGeoBlocked = waxerr.ErrGeoBlocked ErrNoAudioFormats = waxerr.ErrNoAudioFormats // matched none of the available audio formats. It is distinct from // ErrNoAudioFormats, which means no usable audio formats exist. ErrRequestedFormatUnavailable = waxerr.ErrRequestedFormatUnavailable // Throttling. ErrRateLimited = waxerr.ErrRateLimited // Input / routing. ErrIsPlaylist = waxerr.ErrIsPlaylist // ErrIsChannel indicates a channel URL was passed where a single video is // required. ErrIsChannel = waxerr.ErrIsChannel ErrInvalidVideoID = waxerr.ErrInvalidVideoID // ErrVideoIDTooShort and ErrVideoIDTooLong indicate an all-ID-character token // of the wrong length (a video ID is exactly 11 characters). ErrVideoIDTooShort = waxerr.ErrVideoIDTooShort ErrVideoIDTooLong = waxerr.ErrVideoIDTooLong ErrInvalidPlaylistID = waxerr.ErrInvalidPlaylistID // ErrPlaylistParse is a maintenance signal, not a bad input: the playlist // response parsed but matched no known shape. ErrPlaylistParse = waxerr.ErrPlaylistParse // otherwise inaccessible. See [PlaylistUnavailableError]. ErrPlaylistUnavailable = waxerr.ErrPlaylistUnavailable // ErrPlaylistEmpty indicates that a valid playlist contains no videos. ErrPlaylistEmpty = waxerr.ErrPlaylistEmpty // ErrShortsPlaylist indicates that WaxTap cannot enumerate a channel's Shorts // shelf playlist. It wraps [ErrUnsupportedInput]. ErrShortsPlaylist = waxerr.ErrShortsPlaylist // Processing / local files. ErrIncompatibleSpec = waxerr.ErrIncompatibleSpec ErrUnsupportedInput = waxerr.ErrUnsupportedInput ErrFFmpegNotFound = waxerr.ErrFFmpegNotFound // ErrInvalidConfig indicates invalid or conflicting library configuration. ErrInvalidConfig = waxerr.ErrInvalidConfig )
Re-exported sentinel errors. The canonical definitions live in package waxerr; match them with errors.Is.
var DefaultCategories = sponsorblock.DefaultCategories
DefaultCategories contains the categories used when CutSpec.SponsorBlock is a non-nil empty slice.
var ErrNoMatch = format.ErrNoMatch
ErrNoMatch reports that audio selection found no candidate satisfying the request. Download/Process translate it to ErrNoAudioFormats; it is re-exported for callers using BestForTarget directly.
Functions ¶
func BestForTarget ¶
func BestForTarget(candidates []Format, policy SourcePolicy, target Target) (int, error)
BestForTarget chooses the best source audio index for a transcode target under a SourcePolicy. It is the selection BestAudio uses; exposed for callers that resolve formats themselves.
func ParseNetscapeCookies ¶
ParseNetscapeCookies reads a Netscape/Mozilla cookies.txt file (the format yt-dlp and curl use) into http.Cookies. The returned slice matches POTokenSession.Cookies, so a caller can adopt a static session from a browser-exported cookies.txt without reimplementing the format.
Each data line is seven tab-separated fields: domain, include-subdomains flag, path, secure, expiry (unix seconds; 0 = session), name, value. Some exporters drop the trailing tab when the value is empty, leaving six fields; such a line is read with an empty value rather than skipped. The "#HttpOnly_" domain prefix is checked before comment skipping, since those lines are real cookies marked HttpOnly, not comments. Blank lines, ordinary "#" comments, and malformed (under-six-field) lines are skipped.
func ValidateProcessSpec ¶
func ValidateProcessSpec(s ProcessSpec) error
ValidateProcessSpec checks a ProcessSpec without acquiring or processing media. Invalid specs return an error that wraps ErrIncompatibleSpec. Client.Download, Client.Stream, and Client.Process call it automatically; callers may use it to fail before starting batch work.
Types ¶
type AlbumLoudnessResult ¶
type AlbumLoudnessResult struct {
Album LoudnessInfo // loudness measured across the complete album
PerTrack []LoudnessInfo // measurements in input order
}
AlbumLoudnessResult reports a group loudness measurement plus per-track measurements, in input order. The album value is a true group EBU R128 measurement, not a mean of the per-track LUFS.
type AlbumProcessResult ¶
type AlbumProcessResult struct {
Album LoudnessInfo // loudness measured across the complete album
GainDB float64 // Target - album integrated LUFS, applied to every track (0 for a silent album)
PerTrack []LoudnessInfo // input measurements in track order
Outputs []string // completed output paths in track order
}
AlbumProcessResult reports the album loudness, the gain applied to every track, the input measurements, and the output paths.
type AlbumTrack ¶
AlbumTrack names one album input and where its processed output should be written.
type AudioQualityTier ¶
type AudioQualityTier = format.AudioQualityTier
Audio format model and selectors (package format).
type AudioSelector ¶
type AudioSelector = format.AudioSelector
Audio format model and selectors (package format).
func BestAudio ¶
func BestAudio() AudioSelector
BestAudio selects the best audio stream. It prefers the original track, non-DRC audio, higher reported quality tiers, Opus within a tier, and finally higher effective bitrate.
The selector itself imposes no channel preference, but the Download, Info, and Resolve facades apply a stereo default to it, so client.Download(Request{URL}) yields stereo. Call WithChannels(LayoutSurround) for surround, or WithChannels(LayoutAny) to let a surround track rank highest.
func Codec ¶
func Codec(codec string) AudioSelector
Codec selects the best stream whose codec matches (e.g. "opus", "aac").
type AudioTrack ¶
type AudioTrack = format.AudioTrack
Audio format model and selectors (package format).
type Availability ¶
type Availability = youtube.Availability
Availability reports whether a video is publicly listed. It is set only when a watch-page metadata pass runs (see WithFullMetadata), else AvailabilityUnknown.
type Category ¶
type Category = sponsorblock.Category
Category identifies a SponsorBlock segment category.
type ChannelLayout ¶
type ChannelLayout = format.ChannelLayout
Audio format model and selectors (package format).
type Chapter ¶
Extraction models (package youtube). Part of the volatile surface; may evolve pre-1.0.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the main WaxTap entry point for library callers and the CLI. It is safe for concurrent use after construction.
The same httpx.Client backs extraction, media download, and SponsorBlock. Its per-host limiter is shared by all request paths, while each host keeps its own schedule. The ffmpeg Runner is created lazily on first use, so metadata calls and keep-source downloads work without ffmpeg installed.
func (*Client) Download ¶
Download acquires and processes a single YouTube video to the configured sink. It is strictly single-video: a playlist URL returns ErrIsPlaylist (use Enumerate and loop).
Audio selection defaults to stereo, so a bare Request (zero Audio) yields the best stereo track rather than a surround one. Set Audio: BestAudio().WithChannels(LayoutSurround) for surround, or WithChannels(LayoutAny) to rank purely by fidelity.
When no processing is requested (a nil ProcessSpec) it downloads the selected source stream straight to the sink with no ffmpeg and no temp file: the bytes are byte-identical to what YouTube served, so Result.SourceBytes == Result.OutputBytes, Result.OutputFormat == Result.SourceFormat, and Result.Transcoded is false. A TranscodeSpec with FormatCopy is different: it stream-copies through ffmpeg to remux into the target container, so no re-encode happens but the bytes and container may change. When a cut, transcode, or loudness stage is requested it stages the source to a temp file, runs the fused pipeline, and finalizes to the sink.
Example ¶
ExampleClient_Download downloads the best audio stream to a file. With no processing requested, WaxTap keeps the source encoding.
package main
import (
"context"
"fmt"
"log"
"github.com/colespringer/waxtap/v2"
)
func main() {
client, err := waxtap.New(waxtap.Options{})
if err != nil {
log.Fatal(err)
}
res, err := client.Download(context.Background(), waxtap.Request{
URL: "https://www.youtube.com/watch?v=VIDEO_ID_01",
ProcessSpec: waxtap.ProcessSpec{
Output: waxtap.ToFile("track.opus"),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s -> %s (%d bytes)\n", res.VideoID, res.OutputPath, res.OutputBytes)
}
Output:
Example (TranscodeAndSponsorBlock) ¶
ExampleClient_Download_transcodeAndSponsorBlock downloads a video, removes SponsorBlock "music_offtopic" segments, and transcodes to FLAC in one ffmpeg pass. SourcePolicy defaults to MinimizeLoss.
package main
import (
"context"
"fmt"
"log"
"github.com/colespringer/waxtap/v2"
)
func main() {
client, err := waxtap.New(waxtap.Options{})
if err != nil {
log.Fatal(err)
}
res, err := client.Download(context.Background(), waxtap.Request{
URL: "https://www.youtube.com/watch?v=VIDEO_ID_01",
ProcessSpec: waxtap.ProcessSpec{
Transcode: &waxtap.TranscodeSpec{Format: waxtap.FormatFLAC},
Cut: &waxtap.CutSpec{
SponsorBlock: []waxtap.Category{waxtap.CategoryMusicOffTopic},
OnError: waxtap.ProceedUncut,
},
Output: waxtap.ToFile("track.flac"),
Events: func(e waxtap.Event) {
if e.Stage == waxtap.StageWarning && e.Warning != nil {
log.Printf("warning: %s", e.Warning.Detail)
}
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("cut=%v transcoded=%v\n", res.CutApplied, res.Transcoded)
}
Output:
func (*Client) DownloadPlaylist ¶
func (c *Client) DownloadPlaylist(ctx context.Context, url string, o PlaylistDownloadOptions) (*PlaylistRunResult, error)
DownloadPlaylist enumerates a playlist URL and downloads its entries with bounded concurrency, optional pacing, and an optional attempt limit.
An enumeration failure returns an error and a nil result. Item-level enumeration errors do not stop downloads and are returned in EnumErrors. After enumeration succeeds, cancellation returns a partial result with ctx.Err().
Example ¶
ExampleClient_DownloadPlaylist downloads up to ten playlist entries one at a time, waiting between downloads. BuildRequest prepares or skips each entry; OnItem receives the outcome for every entry the run reaches.
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/colespringer/waxtap/v2"
)
func main() {
client, err := waxtap.New(waxtap.Options{})
if err != nil {
log.Fatal(err)
}
res, err := client.DownloadPlaylist(context.Background(),
"https://www.youtube.com/playlist?list=UUSMOQeBJ2RAnuFungnQOxLg",
waxtap.PlaylistDownloadOptions{
Concurrency: 1, // serialize downloads
SleepInterval: 5 * time.Second, // pause between them
MaxDownloads: 10, // stop after 10 attempts
BuildRequest: func(_ context.Context, e waxtap.PlaylistEntry) (waxtap.Request, string, error) {
return waxtap.Request{
URL: e.VideoID,
ProcessSpec: waxtap.ProcessSpec{Output: waxtap.ToFile(e.VideoID + ".opus")},
}, "", nil
},
OnItem: func(o waxtap.PlaylistItemOutcome) {
switch {
case o.Err != nil:
log.Printf("%s: %v", o.Entry.VideoID, o.Err)
case o.SkipReason != "":
log.Printf("%s: skipped (%s)", o.Entry.VideoID, o.SkipReason)
}
},
},
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%d downloaded, %d remaining (cap reached: %v)\n",
res.Downloaded, res.Remaining, res.CapReached)
}
Output:
func (*Client) Enumerate ¶
func (c *Client) Enumerate(ctx context.Context, url string, opts EnumerateOptions) (*Playlist, error)
Enumerate expands a playlist or channel URL into entries without downloading media. A channel reference (a bare UC ID, or a /channel/, /@handle, /c/, or /user/ URL, with any trailing tab stripped) resolves to the channel's uploads feed, which is newest-first and lists Shorts and past live streams as ordinary entries. EnumerateOptions.MaxItems caps the listing, and Skip/Stop drive an archive cursor. With Enrich set, InfoBasic calls refresh entries at bounded concurrency. Successful calls update their entries; item-level failures are added to Playlist.Errors.
Example ¶
ExampleClient_Enumerate lists a playlist without downloading any audio.
package main
import (
"context"
"fmt"
"log"
"github.com/colespringer/waxtap/v2"
)
func main() {
client, err := waxtap.New(waxtap.Options{})
if err != nil {
log.Fatal(err)
}
pl, err := client.Enumerate(context.Background(),
"https://www.youtube.com/playlist?list=UUSMOQeBJ2RAnuFungnQOxLg",
waxtap.EnumerateOptions{MaxItems: 50},
)
if err != nil {
log.Fatal(err)
}
for _, entry := range pl.Entries {
// The video ID is the stable key WaxTap uses for deduplication.
fmt.Printf("%d. %s (%s)\n", entry.Index, entry.Title, entry.VideoID)
}
}
Output:
func (*Client) Info ¶
func (c *Client) Info(ctx context.Context, url string, depth InfoDepth, opts ...ReadOption) (*Video, error)
Info returns video metadata and candidate audio formats at the requested depth, without downloading.
Example ¶
ExampleClient_Info fetches metadata and candidate audio formats without downloading.
package main
import (
"context"
"fmt"
"log"
"github.com/colespringer/waxtap/v2"
)
func main() {
client, err := waxtap.New(waxtap.Options{})
if err != nil {
log.Fatal(err)
}
video, err := client.Info(context.Background(), "VIDEO_ID_01", waxtap.InfoBasic)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s by %s (%d formats)\n", video.Title, video.Author, len(video.Formats))
}
Output:
func (*Client) InfoResult ¶
func (c *Client) InfoResult(ctx context.Context, url string, depth InfoDepth, opts ...ReadOption) (*InfoResult, error)
InfoResult returns video metadata, candidate audio formats, and the extraction client at the requested depth, without downloading.
InfoBasic returns extracted metadata and candidate formats. InfoResolved additionally resolves the best-audio format, surfacing resolution errors (such as ErrNeedsPOToken) and filling in its content length. InfoProbe additionally runs ffprobe on that resolved stream and fills its authoritative sample rate, channel count, bitrate, and duration (network-expensive, and requires ffmpeg). The signed stream URLs themselves are not returned through Video; use Download or Stream to fetch bytes.
func (*Client) Measure ¶
Measure reports EBU R128 integrated loudness for a single local audio file. It uses Process with a measure-only spec and no Output, so no output or scratch file is created.
It requires ffmpeg. Use MeasureAlbum to measure several files as one album, or Process with a LoudnessApply spec to normalize and write audio.
Example ¶
ExampleClient_Measure reports the loudness of a single local file without writing any output.
package main
import (
"context"
"fmt"
"log"
"github.com/colespringer/waxtap/v2"
)
func main() {
client, err := waxtap.New(waxtap.Options{})
if err != nil {
log.Fatal(err)
}
loud, err := client.Measure(context.Background(), "song.flac")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%.1f LUFS\n", loud.IntegratedLUFS)
}
Output:
func (*Client) MeasureAlbum ¶
MeasureAlbum measures local audio files as one album and also returns each track's loudness. It does not write output files; callers can use the album value for ReplayGain tags or playback gain.
It requires ffmpeg. Use ProcessAlbum to measure the album and write normalized tracks. Callers that manage ffmpeg directly can build the gain filter with normalize.AlbumGainFilter.
Example ¶
ExampleClient_MeasureAlbum measures several files as one album, useful for ReplayGain-style album tags without rewriting the files.
package main
import (
"context"
"fmt"
"log"
"github.com/colespringer/waxtap/v2"
)
func main() {
client, err := waxtap.New(waxtap.Options{})
if err != nil {
log.Fatal(err)
}
album, err := client.MeasureAlbum(context.Background(), []string{
"01.flac", "02.flac", "03.flac",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("album: %.2f LUFS\n", album.Album.IntegratedLUFS)
for i, tr := range album.PerTrack {
fmt.Printf("track %d: %.2f LUFS\n", i+1, tr.IntegratedLUFS)
}
}
Output:
func (*Client) ProbeCodec ¶
ProbeCodec reports the codec name of the first audio stream in a local file, such as "opus" or "aac". It returns ErrUnsupportedInput when the file has no audio stream.
func (*Client) Process ¶
Process runs the transcode/cut/normalize pipeline on a local file, with no YouTube access, through the same source-agnostic pipeline as Download. SponsorBlock is not used here: it is keyed by video ID, which a local file does not have, so only explicit Cut.Ranges apply.
The input is validated up front (ffprobe); a corrupt or non-audio file fails with ErrUnsupportedInput. Writing the output over the input is rejected unless the caller targets a different path.
Callers may omit Output only for pure loudness measurement: LoudnessMeasureOnly with no transcode, downmix, or cut. Client.Measure wraps that case.
Example ¶
ExampleClient_Process transcodes a local file and normalizes its loudness to -14 LUFS, fused into the same encode. No YouTube access occurs.
package main
import (
"context"
"fmt"
"log"
"github.com/colespringer/waxtap/v2"
)
func main() {
client, err := waxtap.New(waxtap.Options{})
if err != nil {
log.Fatal(err)
}
res, err := client.Process(context.Background(), waxtap.ProcessRequest{
Input: "song.wav",
ProcessSpec: waxtap.ProcessSpec{
Transcode: &waxtap.TranscodeSpec{Format: waxtap.FormatMP3},
Loudness: &waxtap.LoudnessSpec{Mode: waxtap.LoudnessApply, Target: -14},
Output: waxtap.ToFile("song.mp3"),
},
})
if err != nil {
log.Fatal(err)
}
if res.Loudness != nil && res.Loudness.Output != nil {
fmt.Printf("normalized to %.1f LUFS\n", res.Loudness.Output.IntegratedLUFS)
}
}
Output:
func (*Client) ProcessAlbum ¶
func (c *Client) ProcessAlbum(ctx context.Context, tracks []AlbumTrack, target float64, spec TranscodeSpec) (*AlbumProcessResult, error)
ProcessAlbum measures local files as one album, then applies the same gain to every track. The shared offset preserves track-to-track loudness differences; per-track normalization would flatten them.
Album processing requires ffmpeg and a non-copy transcode format. A silent album applies a no-op gain, leaving each track unchanged apart from re-encoding.
func (*Client) Resolve ¶
func (c *Client) Resolve(ctx context.Context, url string, sel AudioSelector, opts ...ReadOption) (ResolvedStream, error)
Resolve selects and resolves an audio stream without downloading it. The zero AudioSelector means best audio, defaulting to stereo like Download; pass BestAudio().WithChannels(LayoutSurround) for surround or WithChannels(LayoutAny) for any-fidelity. Direct streams include a temporary googlevideo URL and its request metadata. SABR streams set IsSABR and leave URL empty.
It is exposed for diagnostics: the CLI's info --show-url and doctor. Most callers use Download or Stream, which never expose the raw URL.
func (*Client) SponsorBlockSegments ¶
func (c *Client) SponsorBlockSegments(ctx context.Context, videoURL string, categories []Category) ([]Segment, error)
SponsorBlockSegments returns skip segments for videoURL using the client's SponsorBlock settings and shared HTTP client. An empty categories slice uses DefaultCategories. The method does not cut or download media.
func (*Client) Stream ¶
func (c *Client) Stream(ctx context.Context, req Request) (rc io.ReadCloser, info StreamInfo, err error)
Stream acquires a single YouTube video and returns a reader for source-style delivery (pipe to disk or object storage). When processing is requested it stages and processes to a temp file first, then streams the result. Final byte counts are known only after the reader is drained and closed.
Example ¶
ExampleClient_Stream pipes the audio to an arbitrary writer (here a file) without staging to a temp file when no processing is requested.
package main
import (
"context"
"io"
"log"
"os"
"github.com/colespringer/waxtap/v2"
)
func main() {
client, err := waxtap.New(waxtap.Options{})
if err != nil {
log.Fatal(err)
}
rc, info, err := client.Stream(context.Background(), waxtap.Request{
URL: "https://youtu.be/VIDEO_ID_01",
})
if err != nil {
log.Fatal(err)
}
defer rc.Close()
out, err := os.Create("track" + "." + info.Format.Extension)
if err != nil {
log.Fatal(err)
}
defer out.Close()
if _, err := io.Copy(out, rc); err != nil {
log.Fatal(err)
}
}
Output:
type Concurrency ¶
type Concurrency struct {
// Downloads is the max simultaneous downloads (e.g. across a playlist run).
Downloads int
// Chunks is the max parallel ranged chunks within a single download. Kept
// low by default, especially for CLI playlist runs.
Chunks int
// FFmpeg limits concurrent ffmpeg/ffprobe processes, guarding local CPU
// independently from network parallelism. Zero selects a conservative default
// (GOMAXPROCS); a negative value disables the limit.
FFmpeg int
}
Concurrency bounds parallel work. Zero values select conservative defaults at New time.
type CutMode ¶
type CutMode uint8
CutMode selects how cuts are rendered.
const ( // CutSmart copies when cutting alone (lossless, frame-boundary) and fuses the // cut into the transcode when one is requested. It avoids cut-then-transcode // workflows that would encode twice. CutSmart CutMode = iota // CutCopy forces stream-copy; it errors with ErrIncompatibleSpec when copy is // unsafe for the codec/container. CutCopy // CutAccurate decodes, cuts sample-exactly, and re-encodes. CutAccurate )
type CutSpec ¶
type CutSpec struct {
// Ranges are explicit [Start, End) removals (optional). They are clamped to the
// media duration. A request whose ranges all lie outside the media returns
// ErrIncompatibleSpec; partial overlaps remain valid.
Ranges []TimeRange
// SponsorBlock lists categories to fetch and remove. Nil disables
// SponsorBlock; a non-nil empty slice uses [DefaultCategories].
SponsorBlock []Category
// Mode selects copy/accurate/smart rendering.
Mode CutMode
// Crossfade, when > 0, applies a click-free crossfade at splice points. It
// is OFF by default and orthogonal to Mode (accurate does not imply it).
Crossfade time.Duration
// OnError governs the SponsorBlock fetch only.
OnError SponsorBlockErrorPolicy
// Timeout is a strict cap on the SponsorBlock fetch.
Timeout time.Duration
}
CutSpec describes time-range removal and/or SponsorBlock-driven cuts.
type EnumerateOptions ¶
type EnumerateOptions struct {
// MaxItems caps the number of entries returned (0 = all).
MaxItems int
// Enrich refreshes entries with InfoBasic calls made at bounded concurrency.
// Successful calls update their entries; failures are added to Playlist.Errors.
Enrich bool
// Skip omits entries whose video ID it matches while continuing to page, for an
// archive cursor. The consumer owns persistence: pass a predicate that reads
// your store. It is safe on any playlist (seen items may be interspersed) and
// runs before MaxItems, so the cap counts unseen entries. Skipped entries still
// advance PlaylistEntry.Index, which stays the true playlist position.
Skip func(id string) bool
// Stop halts pagination at the first entry it matches (excluding that entry and
// everything after) and leaves Playlist.Continuation empty. It is only correct
// on an append-only newest-first feed such as a channel uploads playlist, where
// a subscription poll stops at the first already-seen ID instead of paging the
// whole feed. On a curated playlist, which can insert entries anywhere, Stop
// drops items; use Skip there. Stop is checked before Skip.
Stop func(id string) bool
// OnProgress reports the running entry count after each playlist page. It is
// optional and never triggers downloads.
OnProgress func(items int)
// OnEnrichProgress reports each completed InfoBasic refresh when Enrich is set.
// Calls are serialized in increasing done-count order. The final call reaches
// (total, total) unless context cancellation stops enrichment early.
OnEnrichProgress func(done, total int)
}
EnumerateOptions tunes playlist enumeration. Enumeration never downloads.
type Event ¶
type Event struct {
Stage Stage // current pipeline stage
VideoID string // empty for local-file processing
// Downloading progress.
Bytes int64
Total int64 // 0 if unknown
// CLI playlist expansion.
ItemIndex int
ItemCount int // total playlist entries, or 0 when unknown
Warning *Warning // set when Stage == StageWarning
Err error // set when Stage == StageFailed
Message string // optional human-readable detail
}
Event is a best-effort progress signal. Callbacks are invoked synchronously from the worker and are panic-recovered. A terminal event always fires: StageDone on success or StageFailed with Err. For Stream, the terminal event is emitted when the returned reader is closed.
type ExtractionError ¶
type ExtractionError = waxerr.ExtractionError
Re-exported structured error types. Inspect them with errors.AsType, or with errors.As using a double-pointer target: each satisfies error on its pointer type (like *os.PathError), so the value form errors.As(err, &PlayabilityError{}) panics. Use:
var pe *PlayabilityError
if errors.As(err, &pe) { /* pe.Status, pe.Reason */ }
equivalently errors.AsType[*PlayabilityError](err).
type HTTPStatusError ¶
type HTTPStatusError = waxerr.HTTPStatusError
Re-exported structured error types. Inspect them with errors.AsType, or with errors.As using a double-pointer target: each satisfies error on its pointer type (like *os.PathError), so the value form errors.As(err, &PlayabilityError{}) panics. Use:
var pe *PlayabilityError
if errors.As(err, &pe) { /* pe.Status, pe.Reason */ }
equivalently errors.AsType[*PlayabilityError](err).
type InfoDepth ¶
type InfoDepth uint8
InfoDepth selects how much work Info does. Callers do not pay for what they do not request.
const ( // InfoBasic returns metadata and candidate formats (the default). InfoBasic InfoDepth = iota // InfoResolved additionally resolves the best-audio stream URL and expiry. // These signed googlevideo URLs are temporary and sensitive; the CLI omits // them from human output unless --show-url is given. InfoResolved // InfoProbe additionally runs ffprobe on the selected format only. This is // network-expensive (it reads the remote signed URL) and is never run on // every candidate. InfoProbe )
type InfoResult ¶
type InfoResult struct {
Video *Video // extracted metadata and candidate formats
Client string // YouTube client that produced the metadata
// SubstitutedFrom names a forced non-WEB client, such as WEB_EMBEDDED, that
// the watch-page fallback replaced. When set, the metadata came from WEB
// rather than the requested client.
SubstitutedFrom string
// ViaWatchPage reports that the primary metadata came from the watch-page
// fallback. For a forced WEB client this read needs no PO token, unlike a forced
// WEB stream; SubstitutedFrom stays empty because WEB is not substituted for
// itself.
ViaWatchPage bool
// FullMetadata reports that watch-page enrichment (PublishDate, Chapters, and
// Availability) was populated, either by the opt-in WithFullMetadata pass or
// because the primary extraction already scraped the watch page (ViaWatchPage).
// This is a different axis from ViaWatchPage, which reports where the base
// metadata came from. When false, an empty Chapters slice or Unknown
// Availability means enrichment did not run, not that the video has none.
FullMetadata bool
// Probed reports that InfoProbe ran ffprobe on the resolved best-audio stream,
// so that row's sample rate, channels, bitrate, and duration are authoritative.
// It is false for SABR streams, which have no direct URL to probe.
Probed bool
// BestIndex is the index into Video.Formats that InfoResolved/InfoProbe resolved
// (and, for InfoProbe, probed in place). It is -1 at InfoBasic depth or when no
// audio could be selected. A probe mutates that row, so callers should display
// BestIndex rather than re-running selection on the mutated slice.
BestIndex int
}
InfoResult contains extracted video metadata and the client that produced it. A later Resolve call may use a different client.
type LiveStatus ¶
type LiveStatus = youtube.LiveStatus
LiveStatus reports a video's live-broadcast state. On a Video from Info it is LiveNone or LiveWasLive; live/upcoming videos surface as error sentinels.
type Locale ¶
type Locale struct {
HL string // host language, e.g. "en", "de", "ja"
GL string // content region, e.g. "US", "DE", "JP"
}
Locale sets InnerTube localization hints. The zero value uses en / US.
HL affects localized UI text YouTube returns, including some error reasons. Availability classification of members-only and geo-blocked videos matches those reason strings and is tuned for English, so a non-English HL may report ErrVideoUnavailable instead of ErrMembersOnly or ErrGeoBlocked (both are still skip-class verdicts). GL is a content-region hint; it does not change the request IP or bypass geo restrictions. Titles and descriptions are usually returned as-authored.
type LoudnessInfo ¶
type LoudnessInfo struct {
IntegratedLUFS float64 // integrated loudness, LUFS
TruePeakDBTP float64 // true peak, dBTP
LRA float64 // loudness range, LU
Threshold float64 // relative gating threshold, LUFS
}
LoudnessInfo holds an EBU R128 measurement.
func (LoudnessInfo) MarshalJSON ¶
func (l LoudnessInfo) MarshalJSON() ([]byte, error)
MarshalJSON encodes non-finite measurements as JSON null because encoding/json rejects NaN and Inf. Silent tracks can produce -Inf; using null keeps LoudnessInfo JSON-friendly for Measure and MeasureAlbum callers. Field names stay the exported struct names.
type LoudnessMode ¶
type LoudnessMode uint8
LoudnessMode selects measurement vs. normalization.
const ( // LoudnessMeasureOnly returns measurements without altering the audio. LoudnessMeasureOnly LoudnessMode = iota // LoudnessApply normalizes to Target, fused into the transcode pass. It // requires an encode, so it is rejected with FormatCopy or no transcode unless // an explicit output codec is given (ErrIncompatibleSpec). LoudnessApply )
type LoudnessResult ¶
type LoudnessResult struct {
Input *LoudnessInfo // measured input loudness (post-cut)
Output *LoudnessInfo // post-apply loudness; set only when Mode == LoudnessApply
Target float64 // requested integrated loudness in LUFS
}
LoudnessResult reports loudness measurements. WaxTap returns LUFS/true-peak measurements, not ReplayGain tag values.
type LoudnessSpec ¶
type LoudnessSpec struct {
// Mode selects measurement or applied normalization.
Mode LoudnessMode
// Target is the target integrated loudness in LUFS for Apply (e.g. -14). The
// value is the caller's policy; WaxTap does not impose one.
Target float64
}
LoudnessSpec requests loudness measurement or normalization (EBU R128).
type Options ¶
type Options struct {
// HTTPClient is used for all requests. It should set a DialContext and a
// conservative Timeout, or rely on the per-operation context deadlines
// WaxTap applies (see Timeouts). A no-timeout client on a dead proxy cannot
// leak goroutines because WaxTap still bounds each operation by context. If
// nil, a default client is used.
HTTPClient *http.Client
// Logger receives structured logs. If nil, logging is discarded; the CLI
// installs its own handler.
Logger *slog.Logger
// Locale sets the InnerTube host language (hl) and content region (gl). The
// zero value defaults to en / US.
Locale Locale
// CacheDir is the base directory for the on-disk player cache. Empty selects
// os.UserCacheDir()/waxtap.
CacheDir string
// DisableDiskCache turns off on-disk player cache reads and writes.
DisableDiskCache bool
// TempDir is where intermediate/staging files are written; empty uses the OS
// temp dir. MaxTempBytes optionally guards total staging size (0 =
// unlimited). This is useful in constrained containers.
TempDir string
// MaxTempBytes limits temporary staging bytes. Zero disables the limit.
MaxTempBytes int64
Concurrency Concurrency // limits parallel network and ffmpeg work
Timeouts Timeouts // sets per-operation deadlines
Retry RetryPolicy // tunes HTTP retries and backoff
Politeness Politeness // limits request rate and applies cooldowns
// ProfileOverridePath points at a strict JSON file that replaces the built-in
// YouTube client profile chain at startup. Use it to refresh client versions,
// user agents, or device fingerprints without rebuilding.
ProfileOverridePath string
// ChromeMajor overrides the emulated Chrome major for the built-in WEB-family
// client identities. Zero selects the built-in default.
//
// The value applies to the default profile chain and to built-in WEB requests
// used for discovery and fallbacks. It does not modify profiles loaded from
// ProfileOverridePath, so the two options cannot be combined. New rejects
// values outside 0..999.
ChromeMajor int
SponsorBlock SponsorBlockOptions // configures SponsorBlock API access
// POTokenProvider supplies PO tokens for profiles that require them. WaxTap may
// call it during extraction for a player-scope token and during resolution for
// a GVS-scope stream token. When a download refresh follows a 403, WaxTap
// passes the failure details in the request. Nil means no provider is
// configured.
POTokenProvider POTokenProvider
// PlayerContextProvider enables the opt-in WEB SABR audio path: it supplies an
// attested /player streaming context (from an external attesting browser such
// as WaxSeal) that WaxTap streams Go-side. When set, WaxTap tries the WEB
// context first (even over a forced Client, whose chain stays the fallback)
// and falls back to the normal extraction chain on a context failure. It
// needs a GVS PO-token provider too (the stream binds a GVS token to the
// context's visitorData); New rejects a configuration without one, because
// the token mint happens at SABR setup, past the fallback boundary. Nil
// leaves WaxTap on its default chain.
PlayerContextProvider PlayerContextProvider
// Client, when non-empty, forces a single built-in client as the whole
// strategy chain instead of the default multi-client fallback. Valid values
// are "web", "ios", "android_vr", and "web_embedded". It applies the built-in
// WEB-family User-Agent / ChromeMajor treatment. It is mutually exclusive with
// ProfileOverridePath. A configured PlayerContextProvider is tried before
// this chain; the forced client serves as its fallback.
Client string
// Session is an externally supplied guest identity (visitorData + cookies)
// WaxTap adopts verbatim instead of bootstrapping its own, for byte-exact
// session coherence with a PO-token minter. Session.VisitorData must be the
// browser's exact X-Goog-Visitor-Id literal (the URL-escaped form in
// ytcfg.VISITOR_DATA); it is re-sent with no escape/unescape.
//
// Adoption requires a uniform client chain (set Client, or a ProfileOverridePath
// whose profiles are all one InnerTube client); the default multi-client chain
// is rejected so an adopted session is never routed through a different client.
// If resolution fails, extraction aborts rather than falling back to a random
// synthetic visitorData. Adopted cookies need an HTTPClient with a cookie jar;
// login cookies are dropped (adoption assumes a guest session). The adopted
// session is resolved once per Client, so long-running services should recreate
// the Client per task. Mutually exclusive with SessionProvider.
Session *POTokenSession
// SessionProvider resolves the adopted guest identity lazily, at most once per
// Client (cached on success). It is the pull-based form of Session and shares
// its uniform-chain requirement. Mutually exclusive with Session.
SessionProvider POTokenSessionProvider
}
Options configures a Client. The zero value is usable; New fills in defaults for timeouts, retry policy, and limits. All fields are read once during New.
type Output ¶
type Output struct {
// contains filtered or unexported fields
}
Output is a delivery sink: either a file path or a writer. The zero value is unset. Construct it with ToFile or ToWriter.
The library writes the exact path given to ToFile (only a temp suffix and an atomic rename); filename templating, sanitization, and collision handling are the CLI's job, not the library's.
type POTokenFailure ¶
type POTokenFailure = potoken.HTTPFailure
POTokenFailure describes the HTTP failure that triggered a token refresh. POTokenRequest.Failure points to a POTokenFailure when one is available.
type POTokenProvider ¶
PO-token provider contract (package potoken).
func NewSidecarPOTokenProvider ¶
func NewSidecarPOTokenProvider(baseURL string, opts ...SidecarOption) (POTokenProvider, error)
NewSidecarPOTokenProvider returns a POTokenProvider that mints PO tokens from a bgutil-wire endpoint (the protocol bgutil-ytdlp-pot-provider and WaxSeal's token server speak). It POSTs a content_binding to <baseURL>/get_pot; baseURL may be a base such as "http://127.0.0.1:4416" or a full endpoint, and the default path is appended only when absent. A bad URL returns an error. Plug the result into Options.POTokenProvider.
The provider uses a dedicated 30s-timeout, no-redirect client that ignores Options.HTTPClient: a PO token is IP-bound, so the mint and the stream must share egress, and no-redirect pins credentials to the endpoint.
Example ¶
ExampleNewSidecarPOTokenProvider wires a running WaxSeal sidecar into a client for full WEB SABR audio: a PO-token provider plus an attested player-context provider, both pointed at the same host so the token mint and the stream share egress. NewSidecarSessionProvider (adopted /session) is the alternative to the player-context handoff. See MAINTENANCE.md for the sidecar wire contracts.
package main
import (
"context"
"fmt"
"log"
"github.com/colespringer/waxtap/v2"
)
func main() {
// The PO-token and player-context endpoints must share a host with the
// download so the IP-bound token stays valid. Each accepts a base URL or a
// full endpoint; add WithSidecarAPIKey for an authenticated sidecar.
poToken, err := waxtap.NewSidecarPOTokenProvider("http://127.0.0.1:4416")
if err != nil {
log.Fatal(err)
}
playerContext, err := waxtap.NewSidecarPlayerContextProvider("http://127.0.0.1:4416")
if err != nil {
log.Fatal(err)
}
client, err := waxtap.New(waxtap.Options{
POTokenProvider: poToken,
PlayerContextProvider: playerContext,
})
if err != nil {
log.Fatal(err)
}
res, err := client.Download(context.Background(), waxtap.Request{
URL: "https://www.youtube.com/watch?v=VIDEO_ID_01",
ProcessSpec: waxtap.ProcessSpec{Output: waxtap.ToFile("track.opus")},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("delivered %d bytes via %s\n", res.OutputBytes, res.Client)
}
Output:
type POTokenProviderFunc ¶
type POTokenProviderFunc = potoken.ProviderFunc
POTokenProviderFunc adapts a closure to POTokenProvider.
type POTokenRequest ¶
PO-token provider contract (package potoken).
type POTokenResponse ¶
PO-token provider contract (package potoken).
type POTokenSession ¶
External guest-session adoption (package potoken). A POTokenSession lets WaxTap adopt an externally supplied visitorData + cookies verbatim instead of bootstrapping its own, for byte-exact session coherence with a PO-token minter. POTokenSessionProvider is its pull-based form.
type POTokenSessionProvider ¶
type POTokenSessionProvider = potoken.SessionProvider
External guest-session adoption (package potoken). A POTokenSession lets WaxTap adopt an externally supplied visitorData + cookies verbatim instead of bootstrapping its own, for byte-exact session coherence with a PO-token minter. POTokenSessionProvider is its pull-based form.
func NewSidecarSessionProvider ¶
func NewSidecarSessionProvider(baseURL string, opts ...SidecarOption) (POTokenSessionProvider, error)
NewSidecarSessionProvider returns a POTokenSessionProvider that adopts a guest identity ({visitor_data, cookies}) from a <baseURL>/session endpoint (base or full endpoint accepted), so WaxTap streams under the same session the token was attested in. A bad URL returns an error. Plug the result into Options.SessionProvider.
The provider uses a dedicated 30s-timeout, no-redirect client that ignores Options.HTTPClient: full WEB validation requires the session host and the downloads to share an egress IP.
type PlayabilityError ¶
type PlayabilityError = waxerr.PlayabilityError
Re-exported structured error types. Inspect them with errors.AsType, or with errors.As using a double-pointer target: each satisfies error on its pointer type (like *os.PathError), so the value form errors.As(err, &PlayabilityError{}) panics. Use:
var pe *PlayabilityError
if errors.As(err, &pe) { /* pe.Status, pe.Reason */ }
equivalently errors.AsType[*PlayabilityError](err).
type PlayerContext ¶
type PlayerContext = potoken.PlayerContext
Attested WEB player-context handoff (package potoken). A PlayerContextProvider supplies an attested /player streaming context (serverAbrStreamingUrl, ustreamer config, visitorData, and audio formats) that WaxTap streams Go-side, enabling the opt-in WEB SABR audio path.
type PlayerContextFormat ¶
type PlayerContextFormat = potoken.PlayerContextFormat
Attested WEB player-context handoff (package potoken). A PlayerContextProvider supplies an attested /player streaming context (serverAbrStreamingUrl, ustreamer config, visitorData, and audio formats) that WaxTap streams Go-side, enabling the opt-in WEB SABR audio path.
type PlayerContextProvider ¶
type PlayerContextProvider = potoken.PlayerContextProvider
Attested WEB player-context handoff (package potoken). A PlayerContextProvider supplies an attested /player streaming context (serverAbrStreamingUrl, ustreamer config, visitorData, and audio formats) that WaxTap streams Go-side, enabling the opt-in WEB SABR audio path.
func NewSidecarPlayerContextProvider ¶
func NewSidecarPlayerContextProvider(baseURL string, opts ...SidecarOption) (PlayerContextProvider, error)
NewSidecarPlayerContextProvider returns a PlayerContextProvider that fetches an attested WEB /player streaming context from a WaxSeal-style endpoint, enabling the opt-in WEB SABR audio path. It POSTs video_id to <baseURL>/player-context (base or full endpoint accepted). A bad URL returns an error. Plug the result into Options.PlayerContextProvider; New requires a POTokenProvider alongside it because the WEB stream binds a GVS PO token to the context's visitorData.
The client imposes no timeout of its own; calls rely on Timeouts.WebContext. Like the token provider it ignores Options.HTTPClient and is never proxied.
type PlayerContextProviderFunc ¶
type PlayerContextProviderFunc = potoken.PlayerContextProviderFunc
Attested WEB player-context handoff (package potoken). A PlayerContextProvider supplies an attested /player streaming context (serverAbrStreamingUrl, ustreamer config, visitorData, and audio formats) that WaxTap streams Go-side, enabling the opt-in WEB SABR audio path.
type Playlist ¶
Extraction models (package youtube). Part of the volatile surface; may evolve pre-1.0.
type PlaylistDownloadOptions ¶
type PlaylistDownloadOptions struct {
// MaxItems limits playlist enumeration. Zero includes all entries.
MaxItems int
// Concurrency limits parallel downloads. Zero uses the client's download
// concurrency, then a default of 2. BuildRequest calls remain serial and may
// overlap a download even when Concurrency is 1.
Concurrency int
// MaxDownloads limits download attempts. Zero is unlimited. Skipped entries
// and BuildRequest errors do not count.
MaxDownloads int
// SleepInterval is the minimum delay before each download start after the
// first. Zero disables pacing. With Concurrency set to 1, the delay falls
// between completed downloads; at higher concurrency, it spaces starts.
SleepInterval time.Duration
// MaxSleepInterval, when greater than SleepInterval, randomizes the delay
// uniformly within that range. It requires a non-zero SleepInterval.
MaxSleepInterval time.Duration
// Skip and Stop drive an archive cursor during enumeration, matching
// EnumerateOptions. Skip omits matching entries but keeps paging (safe on any
// playlist); Stop halts paging at the first match (only correct on a newest-first
// channel uploads feed). Using Stop for a subscription poll avoids paging the
// whole feed each run instead of skipping only at BuildRequest time.
Skip func(id string) bool
Stop func(id string) bool
// BuildRequest prepares a download request for each entry. Calls are serial
// and follow playlist order. Returning a non-empty skip reason or an error
// prevents the download and does not count toward MaxDownloads. Panics are
// recovered and recorded as BuildRequest errors.
BuildRequest func(ctx context.Context, e PlaylistEntry) (req Request, skip string, err error)
// OnItem receives each attempted, skipped, or failed entry. It is not called
// for entries left in Remaining. Calls may be concurrent and out of playlist
// order. Panics are recovered and ignored.
OnItem func(PlaylistItemOutcome)
}
PlaylistDownloadOptions configures Client.DownloadPlaylist. BuildRequest is required. Counts and durations must not be negative.
type PlaylistEntry ¶
type PlaylistEntry = youtube.PlaylistEntry
Extraction models (package youtube). Part of the volatile surface; may evolve pre-1.0.
type PlaylistItemOutcome ¶
type PlaylistItemOutcome struct {
Entry PlaylistEntry // playlist entry associated with this outcome
Attempted bool // whether Download was called and counted against MaxDownloads
Result *Result // set only after a successful download
SkipReason string // set when BuildRequest skipped the entry
Err error // from BuildRequest when not Attempted, otherwise from Download
}
PlaylistItemOutcome describes the result for one playlist entry.
type PlaylistRunResult ¶
type PlaylistRunResult struct {
Enumerated int // entries returned by playlist enumeration
Downloaded int // successful downloads
Skipped int // entries skipped by BuildRequest
BuildRequestFailed int // entries whose BuildRequest call failed
DownloadFailed int // entries whose Download call failed
Remaining int // entries not reached because of a limit or cancellation
CapReached bool // whether MaxDownloads stopped the run
EnumErrors []error // item errors returned by playlist enumeration
Outcomes []PlaylistItemOutcome // reached entries, in playlist order
}
PlaylistRunResult summarizes a playlist download.
Invariant: Downloaded + Skipped + BuildRequestFailed + DownloadFailed + Remaining equals Enumerated. MaxDownloads counts Downloaded and DownloadFailed.
type PlaylistUnavailableError ¶
type PlaylistUnavailableError = waxerr.PlaylistUnavailableError
PlaylistUnavailableError reports why YouTube considers a playlist inaccessible.
type Politeness ¶
type Politeness struct {
// PerHostQPS throttles requests per host (0 = unlimited). youtube.com and
// googlevideo.com are limited independently.
PerHostQPS float64
// Cooldown pauses requests to a host after HTTP 429, or after HTTP 503/403
// with a Retry-After header. A longer Retry-After value takes precedence, up
// to RetryPolicy.MaxRetryWait. Zero disables the cooldown.
Cooldown time.Duration
}
Politeness governs request volume and backoff. The posture is to be a well-behaved client (reduce load, honor backoff, stop when limited), not to evade detection.
type ProcessRequest ¶
type ProcessRequest struct {
// Input is the local file path. Reader-based inputs will use a separate
// request type; non-seekable inputs are staged before processing.
Input string
ProcessSpec
}
ProcessRequest processes a local audio file through the same pipeline as a YouTube download (transcode/cut/normalize), with no YouTube access.
type ProcessSpec ¶
type ProcessSpec struct {
Transcode *TranscodeSpec // nil = keep source, no re-encode
Cut *CutSpec // nil = no cut
Loudness *LoudnessSpec // nil = no loudness work
// Channels is the Downmix target layout, applied after probing. When Downmix is
// set it must be LayoutMono or LayoutStereo; pairing Downmix with LayoutAny is a
// hard error. LayoutAny, the zero value, means no downmix target, so Downmix must
// be false. For YouTube requests, prefer setting the layout on Audio with
// WithChannels to pick a native track; audio selection already defaults to stereo,
// so downmix is only needed when a caller opts into a surround source.
Channels ChannelLayout
// Downmix reduces a source with more channels to Channels after probing. It
// never adds channels and does nothing when the source already fits the
// requested layout. Channels must be LayoutMono or LayoutStereo. When
// Transcode is nil, the encoder is chosen from the source codec and destination
// container.
Downmix bool
// Output is the sink. For source-style delivery (an io.ReadCloser to pipe
// elsewhere) use Client.Stream instead of setting Output.
Output Output
// Events receives best-effort, synchronous, panic-recovered stage events.
// It may be nil. A slow callback backpressures the worker, so keep it fast.
Events func(Event)
// SkipIfExists skips work when the exact output path already exists. This is
// only a path check; callers remain responsible for library-level deduping.
SkipIfExists bool
// IncludeMetadata attaches extended video metadata to Result.Metadata for
// YouTube downloads. It has no effect on local-file processing.
IncludeMetadata bool
// Threads limits ffmpeg's worker threads for processing operations. Zero lets
// ffmpeg choose.
Threads int
}
ProcessSpec is the processing pipeline shared by YouTube and local-file requests. Each stage is opt-in: a nil pointer means that stage is skipped, so the default path keeps the selected source stream unchanged.
type ProviderError ¶
type ProviderError = waxerr.ProviderError
ProviderError reports a failed player-context or session provider call.
type RateLimitError ¶
type RateLimitError = waxerr.RateLimitError
Re-exported structured error types. Inspect them with errors.AsType, or with errors.As using a double-pointer target: each satisfies error on its pointer type (like *os.PathError), so the value form errors.As(err, &PlayabilityError{}) panics. Use:
var pe *PlayabilityError
if errors.As(err, &pe) { /* pe.Status, pe.Reason */ }
equivalently errors.AsType[*PlayabilityError](err).
type ReadOption ¶
type ReadOption func(*readOptions)
ReadOption configures Info, InfoResult, and Resolve. WithNoFallback applies to all three; WithChannels only affects the best-audio row Info and InfoResult pick (Resolve takes an explicit AudioSelector and ignores it).
func WithChannels ¶
func WithChannels(layout ChannelLayout) ReadOption
WithChannels sets the channel preference Info and InfoResult use to pick the best-audio row they resolve and probe, matching the row a default download would select. The facade defaults to stereo; pass WithChannels(LayoutSurround) for surround or WithChannels(LayoutAny) to rank purely by fidelity with no channel preference (a surround track may then rank highest). Resolve takes an explicit AudioSelector and ignores this option.
func WithFullMetadata ¶
func WithFullMetadata() ReadOption
WithFullMetadata makes Info and InfoResult run a token-free watch-page pass that backfills PublishDate (when the primary client omitted it), Chapters, and Availability. The default ANDROID_VR client omits these, so this is what makes them reliable across clients. InfoResult.FullMetadata reports whether the data was populated.
It costs one extra HTTP request unless the primary extraction already scraped the watch page (InfoResult.ViaWatchPage), in which case the data is already present and no extra fetch runs. Enrichment is best-effort: a parse failure leaves the fields zero/Unknown rather than failing the call. WithFullMetadata is a no-op when combined with WithNoFallback, which forbids the watch page.
func WithNoFallback ¶
func WithNoFallback() ReadOption
WithNoFallback prevents Info, InfoResult, and Resolve from falling back to watch-page extraction. Request.NoFallback provides the same behavior for Download and Stream.
type Request ¶
type Request struct {
// URL is a YouTube video URL or bare video ID.
URL string
// Audio selects which audio stream to take. The zero value is BestAudio, which
// the facade defaults to stereo; use BestAudio().WithChannels(LayoutSurround) for
// surround or WithChannels(LayoutAny) to rank purely by fidelity.
Audio AudioSelector
// SourcePolicy controls source selection when transcoding. The zero value is
// MinimizeLoss.
SourcePolicy SourcePolicy
// NoFallback prevents fallback from a WEB player context to the configured
// client chain, disables watch-page extraction, and prevents retrying another
// client after an incomplete download. The configured extraction chain may
// still select a working client. Set Options.Client to force a single client.
// Read methods use WithNoFallback for the same behavior.
NoFallback bool
// FullMetadata runs a token-free watch-page pass during the download and fills
// Result.Metadata with the PublishDate and Chapters that the default /player
// client omits, so an ingest that needs them is one call instead of a separate
// Info(..., WithFullMetadata()) plus Download. It requires IncludeMetadata and is
// a no-op with NoFallback (which forbids the watch page). The extra fetch is
// skipped when extraction already scraped the watch page. Enrichment is
// best-effort: a failure leaves the base metadata and never fails the download.
FullMetadata bool
ProcessSpec
}
Request is a YouTube acquisition + processing request.
type RequestedFormatError ¶
type RequestedFormatError = waxerr.RequestedFormatError
RequestedFormatError reports that an explicit itag/codec selector matched no available audio format and lists the available alternatives.
type ResolvedStream ¶
type ResolvedStream = youtube.ResolvedStream
Extraction models (package youtube). Part of the volatile surface; may evolve pre-1.0.
type Result ¶
type Result struct {
SourceKind SourceKind // identifies a YouTube or local-file source
VideoID string // empty for local files
Title string // empty for local files
InputPath string // set for local files
OutputPath string // empty for ToWriter delivery
Client string // YouTube client used, such as "ANDROID_VR"; empty for local files
SourceFormat Format // input/source format
OutputFormat Format // after transcode (== source when copy/keep)
SourceBytes int64 // bytes read from the acquired or local source
OutputBytes int64 // bytes delivered to the output sink
Transcoded bool // audio was re-encoded (not stream-copied); a copy/remux stays false
CutApplied bool // at least one time range was removed
SponsorBlockApplied bool // SponsorBlock contributed a removed range
LoudnessMeasured bool // measured != normalized
LoudnessApplied bool // normalization was applied
Loudness *LoudnessResult // nil unless measured
Warnings []Warning // non-fatal conditions encountered during processing
// Metadata contains extended video metadata when ProcessSpec.IncludeMetadata
// is set. It is nil otherwise.
Metadata *VideoMetadata
}
Result reports the outcome of a Download or Process. Boolean flags describe completed effects, not requested work. For example, a SponsorBlock request that matches no segments leaves SponsorBlockApplied and CutApplied false.
type RetryPolicy ¶
type RetryPolicy struct {
MaxRetries int // additional attempts after the first
BaseBackoff time.Duration // base of the exponential backoff
MaxBackoff time.Duration // cap on a single backoff sleep
// MaxRetryWait caps an honored Retry-After. Beyond it WaxTap fails fast with
// a *RateLimitError instead of sleeping a goroutine. Some Retry-After values
// can be hours long.
MaxRetryWait time.Duration
}
RetryPolicy tunes HTTP retry/backoff.
type SidecarError ¶
type SidecarError struct {
Label string // provider name, such as "bgutil PO-token server"
Endpoint string // configured endpoint (redacted in the Error string)
Err error // underlying transport error; Unwrap returns it
}
SidecarError reports a connection failure to a configured sidecar endpoint. Its Error string self-redacts the endpoint; SidecarResponseError is the counterpart for a reachable endpoint that returned a non-OK status or unusable response.
func (*SidecarError) Error ¶
func (e *SidecarError) Error() string
func (*SidecarError) Unwrap ¶
func (e *SidecarError) Unwrap() error
type SidecarOption ¶
type SidecarOption func(*sidecarConfig)
SidecarOption configures a sidecar provider built by NewSidecarPOTokenProvider, NewSidecarPlayerContextProvider, or NewSidecarSessionProvider.
func WithSidecarAPIKey ¶
func WithSidecarAPIKey(key string) SidecarOption
WithSidecarAPIKey sends key as the X-API-Key header on every sidecar request. An empty key (the default) sends no header. Use HTTPS for a remote sidecar.
type SidecarResponseError ¶
type SidecarResponseError struct {
Label string // provider name, such as "session endpoint"
Endpoint string // configured endpoint (redacted in the Error string)
StatusCode int // HTTP status, or 0 when a 200 carried invalid content
Reason string // short, sanitized reason; never raw response bytes
}
SidecarResponseError reports a non-OK status or an invalid response from a configured sidecar. StatusCode is zero when an HTTP 200 response had invalid content. SidecarError is reserved for connection failures. Its Error string self-redacts the endpoint.
func (*SidecarResponseError) Error ¶
func (e *SidecarResponseError) Error() string
type SourceKind ¶
type SourceKind uint8
SourceKind distinguishes a YouTube download from local-file processing.
const ( SourceYouTube SourceKind = iota // media acquired from YouTube SourceLocalFile // media read from a local file )
func (SourceKind) String ¶
func (k SourceKind) String() string
type SourcePolicy ¶
type SourcePolicy = format.SourcePolicy
Audio format model and selectors (package format).
func BestNative ¶
func BestNative() SourcePolicy
BestNative ignores target codec matching and uses normal best-audio ranking.
func MinimizeLoss ¶
func MinimizeLoss() SourcePolicy
MinimizeLoss prefers a source in the target codec family, avoiding a cross-codec transcode when possible.
func PreferCodec ¶
func PreferCodec(codec string) SourcePolicy
PreferCodec prefers a source in the named codec family when policy is active.
type SponsorBlockErrorPolicy ¶
type SponsorBlockErrorPolicy uint8
SponsorBlockErrorPolicy governs SponsorBlock fetch failures only (ffmpeg cut/transcode failures are always hard errors).
const ( // ProceedUncut logs a warning and delivers the full, uncut audio when the // SponsorBlock fetch fails or times out (the default). ProceedUncut SponsorBlockErrorPolicy = iota // FailDownload fails the whole request when the SponsorBlock fetch fails. FailDownload )
type SponsorBlockOptions ¶
type SponsorBlockOptions struct {
// BaseURL overrides the SponsorBlock API base URL (empty = public default).
BaseURL string
// Timeout is a strict per-fetch timeout; if set it takes precedence over
// Timeouts.SponsorBlock.
Timeout time.Duration
}
SponsorBlockOptions configures the SponsorBlock client.
type Stage ¶
type Stage uint8
Stage identifies a pipeline stage in an Event.
const ( StageExtracting Stage = iota // fetching and parsing source metadata StageResolving // resolving the selected media stream StageDownloading // transferring source bytes StageStaging // preparing a local working file StageProbing // inspecting media with ffprobe StageAnalyzing // measuring loudness StageCutting // removing time ranges StageNormalizing // applying loudness normalization StageTranscoding // encoding or remuxing audio StageFinalizing // delivering the completed output StageSkipped // skipping work because output already exists StageWarning // reporting a non-fatal warning StageDone // reporting successful completion StageFailed // reporting terminal failure )
type StreamInfo ¶
type StreamInfo struct {
VideoID string // resolved YouTube video ID
Title string // extracted video title
Format Format // selected source format
ContentLength int64 // 0 if unknown
Client string // YouTube client used, such as "ANDROID_VR"
}
StreamInfo is the initial metadata returned by Client.Stream alongside the stream reader. Final byte counts are known only after read-to-EOF/Close.
type Target ¶
Target describes a transcode output for source selection. The facade maps a TranscodeSpec onto it; most callers do not construct one directly.
type Thumbnail ¶
Extraction models (package youtube). Part of the volatile surface; may evolve pre-1.0.
type TimeRange ¶
type TimeRange struct {
Start time.Duration // inclusive start offset
End time.Duration // exclusive end offset
}
TimeRange is a half-open [Start, End) span. End must be greater than Start.
type Timeouts ¶
type Timeouts struct {
Extraction time.Duration // player-response fetch + parse
Resolve time.Duration // stream-URL resolution (incl. cipher JS)
WebContext time.Duration // per attested /player-context fetch, mid-stream re-fetches included
SponsorBlock time.Duration // SponsorBlock fetch (see also SponsorBlock.Timeout)
ChunkRetry time.Duration // per-chunk deadline for ranged downloads
FFmpegShutdown time.Duration // grace period before killing ffmpeg on cancel
}
Timeouts are per-operation deadlines applied through context. There is no single global download cap; each operation gets its own budget. A zero field means WaxTap adds no extra deadline for that operation.
type TranscodeFormat ¶
type TranscodeFormat uint8
TranscodeFormat names an output preset. FormatCopy is the only no-re-encode path. FLAC, ALAC, and WAV preserve the decoded samples, but they are still decode-and-encode passes when the source is YouTube audio.
const ( FormatCopy TranscodeFormat = iota // remux / stream-copy (no re-encode) FormatFLAC // FLAC lossless audio FormatALAC // Apple Lossless audio FormatWAV // uncompressed PCM in a WAV container FormatMP3 // MP3 audio FormatAAC // delivered in an .m4a container FormatOpus // Opus audio FormatVorbis // Vorbis audio )
type TranscodeSpec ¶
type TranscodeSpec struct {
// Format selects the output preset.
Format TranscodeFormat
// Bitrate is the target bits per second for lossy presets (e.g. 256000).
// Zero selects the preset default. Ignored by lossless presets.
Bitrate int
}
TranscodeSpec requests ffmpeg processing. An explicit FormatCopy stream-copies through ffmpeg to remux into the destination container; a nil TranscodeSpec keeps the selected source bytes untouched.
type VideoMetadata ¶
type VideoMetadata struct {
Author string // channel / uploader name
ChannelID string // YouTube channel ID (canonical UC identity anchor)
Duration time.Duration // video duration, 0 if unknown
PublishDate time.Time // publication date, zero if unknown
Description string // video description
// Availability is the listing state. It is Public or Unlisted only when
// Request.FullMetadata ran the watch-page pass that determines it; otherwise
// AvailabilityUnknown.
Availability Availability
// Chapters are the video's chapter markers. They are populated only when
// Request.FullMetadata ran the watch-page pass (the default /player response
// omits them); nil otherwise.
Chapters []Chapter
Formats []Format // full candidate audio (and incidental video) formats
}
VideoMetadata contains optional YouTube metadata that is not stored directly on Result.
type Warning ¶
type Warning struct {
Code WarningCode // stable machine-readable identifier
Detail string // human-readable context
}
Warning is a typed, non-fatal signal. It is both delivered as a StageWarning Event and accumulated in Result.Warnings.
type WarningCode ¶
type WarningCode uint8
WarningCode is a stable, machine-readable warning identifier. Warning.Detail is intended for people.
const ( WarnProceedUncut WarningCode = iota // SponsorBlock fetch failed; delivered uncut WarnFallbackProfile // a fallback client profile was used WarnURLReResolved // an expired stream URL was re-resolved WarnPlaylistEntryFailed // one playlist entry failed (others returned) WarnRateLimitedRetried // a request was retried after a 429 WarnSponsorBlockEmpty // SponsorBlock matched no segments WarnRangesEmpty // SponsorBlock segments all fell outside the media WarnThrottled // a limiter/cooldown is active WarnWebContextFallback // WEB player-context failed; fell back to the configured chain WarnIncompleteFallback // a client returned an incomplete stream; switched clients WarnWebContextRetry // WEB player-context was capped (status 2); retried once with a fresh context )
func (WarningCode) String ¶
func (w WarningCode) String() string
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
waxtap
command
Command waxtap provides the WaxTap CLI for YouTube audio downloads and local audio processing.
|
Command waxtap provides the WaxTap CLI for YouTube audio downloads and local audio processing. |
|
Package cut removes time ranges from an audio file.
|
Package cut removes time ranges from an audio file. |
|
Package download transfers resolved media streams to files, writers, or callers that want an io.ReadCloser.
|
Package download transfers resolved media streams to files, writers, or callers that want an io.ReadCloser. |
|
Package format defines WaxTap's stream-format model and the rules for picking an audio source from a candidate list.
|
Package format defines WaxTap's stream-format model and the rules for picking an audio source from a candidate list. |
|
internal
|
|
|
cache
Package cache provides a thread-safe LRU cache with TTL expiry, schema versioning, and singleflight de-duplication of concurrent loads.
|
Package cache provides a thread-safe LRU cache with TTL expiry, schema versioning, and singleflight de-duplication of concurrent loads. |
|
clientident
Package clientident defines the built-in browser identity used by WaxTap's WEB-family YouTube clients.
|
Package clientident defines the built-in browser identity used by WaxTap's WEB-family YouTube clients. |
|
diskcache
Package diskcache provides a small on-disk blob cache for data that is expensive to fetch and safe to lose.
|
Package diskcache provides a small on-disk blob cache for data that is expensive to fetch and safe to lose. |
|
dumpfile
Package dumpfile writes timestamped diagnostic artifacts.
|
Package dumpfile writes timestamped diagnostic artifacts. |
|
httpx
Package httpx is WaxTap's internal HTTP client wrapper.
|
Package httpx is WaxTap's internal HTTP client wrapper. |
|
iox
Package iox holds small io helpers shared across WaxTap.
|
Package iox holds small io helpers shared across WaxTap. |
|
pipeline
Package pipeline runs WaxTap's source-agnostic audio processing on a staged local file: it cuts time ranges, normalizes loudness, and transcodes, fusing whatever is requested into a single ffmpeg encode.
|
Package pipeline runs WaxTap's source-agnostic audio processing on a staged local file: it cuts time ranges, normalizes loudness, and transcodes, fusing whatever is requested into a single ffmpeg encode. |
|
tempfile
Package tempfile stages output in the destination directory and publishes it with an atomic rename.
|
Package tempfile stages output in the destination directory and publishes it with an atomic rename. |
|
Package normalize measures EBU R128 loudness with ffmpeg's loudnorm filter and builds the matching apply filter for a later encode.
|
Package normalize measures EBU R128 loudness with ffmpeg's loudnorm filter and builds the matching apply filter for a later encode. |
|
Package potoken defines the PO-token provider contract and the related browser-attested handoff contracts (Session and PlayerContext) that let WaxTap adopt an external attesting browser's identity and streaming context.
|
Package potoken defines the PO-token provider contract and the related browser-attested handoff contracts (Session and PlayerContext) that let WaxTap adopt an external attesting browser's identity and streaming context. |
|
Package sponsorblock defines the SponsorBlock category vocabulary used by WaxTap cut requests.
|
Package sponsorblock defines the SponsorBlock category vocabulary used by WaxTap cut requests. |
|
Package transcode wraps ffmpeg and ffprobe for local audio files.
|
Package transcode wraps ffmpeg and ffprobe for local audio files. |
|
Package waxerr defines the sentinel and structured errors shared by WaxTap's extraction, download, and processing packages.
|
Package waxerr defines the sentinel and structured errors shared by WaxTap's extraction, download, and processing packages. |
|
Package youtube performs YouTube extraction: it turns a URL into video metadata and candidate audio formats, and resolves those formats into playable, signed stream URLs.
|
Package youtube performs YouTube extraction: it turns a URL into video metadata and candidate audio formats, and resolves those formats into playable, signed stream URLs. |
|
internal/resolver
Package resolver isolates YouTube's volatile player JavaScript.
|
Package resolver isolates YouTube's volatile player JavaScript. |
|
internal/sabr
Package sabr streams YouTube SABR audio over UMP.
|
Package sabr streams YouTube SABR audio over UMP. |