src

package
v0.0.0-...-1d73c28 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 9, 2026 License: GPL-3.0 Imports: 48 Imported by: 0

Documentation

Index

Constants

View Source
const (
	FingerprintVersion = 1
	FpStartPercent     = 0.20
	FpStartDuration    = 10 * 60
	FpEndDuration      = 5 * 60
)
View Source
const (
	MinOverlapDuration = 15.0

	// Correlation threshold (0.0-1.0) above which a match is considered valid.
	// Uses the AcoustID-style formula: 1.0 - 2.0 * biterror / (32 * length),
	// where random noise scores ~0.0 and identical audio scores 1.0.
	MatchThreshold = 0.1

	// Number of most-significant bits used as a hash key for offset voting.
	// Matches AcoustID's MATCH_BITS. The top bits of a chromaprint value are
	// the most discriminative (classifiers are ordered by importance).
	MatchBits = 14

	// Chromaprint encodes silence as this specific value.
	// We skip it during offset voting to avoid false matches.
	SilenceValue = 627964279

	// Number of samples per correlation block (~2 seconds at 7.8125 samples/s).
	// Segments are evaluated in blocks of this size to find contiguous matching runs.
	CorrBlockSize = 16
)
View Source
const ExtractVersion = 1
View Source
const FingerprintSampleRate = 7.8125

Number of fingerprint items per second (chromaprint default sample rate). Chromaprint uses ~8000 Hz sample rate with 4096-sample frames and 4096/3 overlap, producing roughly 7.8 items/s. We use the conventional approximation.

View Source
const InfoVersion = 4
View Source
const (
	KeyframeVersion = 2
)
View Source
const (
	// MergeWindowSec is the maximum gap (in seconds) between a detected chapter
	// boundary and an existing chapter for them to be merged.
	MergeWindowSec float32 = 3.0
)
View Source
const ThumbsVersion = 1

Variables

View Source
var AudioQualities = []AudioQuality{K128, K192, K256, K320, K512}
View Source
var DeletedHead = Head{
	// contains filtered or unexported fields
}
View Source
var Settings = SettingsT{

	Outpath:  path.Join(GetEnvOr("GOCODER_CACHE_ROOT", "/cache"), "kyoo_cache"),
	SafePath: GetEnvOr("GOCODER_SAFE_PATH", "/video"),
	JwksUrl:  os.Getenv("JWKS_URL"),
	HwAccel:  DetectHardwareAccel(),
}
View Source
var SubtitleExtensions = map[string]string{
	"subrip":            "srt",
	"ass":               "ass",
	"vtt":               "vtt",
	"hdmv_pgs_subtitle": "sup",
}
View Source
var VideoQualities = []VideoQuality{P240, P360, P480, P720, P1080, P1440, P4k, P8k}

Purposfully removing Original from this list (since it require special treatments anyways)

Functions

func Abs

func Abs(x int32) int32

func CompressFingerprint

func CompressFingerprint(fp []uint32) (string, error)

func ComputeSha

func ComputeSha(path string) (string, error)

ComputeSha computes a SHA1 hash of the file path and its modification time. This is used as a cache key to detect when a file has changed.

func ConvertSubtitle

func ConvertSubtitle(
	format string,
	stream io.ReadCloser,
	outFmt string,
	out io.WriteCloser,
) error

func DecompressFingerprint

func DecompressFingerprint(compressed string) ([]uint32, error)

func GetEnvOr

func GetEnvOr(env string, def string) string

func GetMimeCodec

func GetMimeCodec(stream *ffprobe.Stream) *string

convert mediainfo to RFC 6381, waiting for either of those tickets to be resolved:

https://sourceforge.net/p/mediainfo/feature-requests/499
https://trac.ffmpeg.org/ticket/6617

this code is addapted from https://github.com/jellyfin/jellyfin/blob/master/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs and https://git.ffmpeg.org/gitweb/ffmpeg.git/blob/HEAD%3a/libavformat/hlsenc.c#l344

func Map

func Map[T, U any](ts []T, f func(T, int) U) []U

func MapStream

func MapStream[T any](streams []*ffprobe.Stream, kind ffprobe.StreamType, mapper func(*ffprobe.Stream, uint32) T) []T

func NewStream

func NewStream(file *FileStream, keyframes *Keyframe, handle StreamHandle, ret *Stream)

func NullIfUnd

func NullIfUnd(str string) *string

func OrNull

func OrNull(str string) *string

func ParseFloat

func ParseFloat(str string) float32

func ParseInt64

func ParseInt64(str string) int64

func ParseUint

func ParseUint(str string) uint32

Types

type Audio

type Audio struct {
	Id int32 `json:"-" db:"id"`

	/// The index of this track on the media.
	Index uint32 `json:"index" db:"idx"`
	/// The title of the stream.
	Title *string `json:"title" db:"title"`
	/// The language of this stream (as a IETF-BCP-47 language code)
	Language *string `json:"language" db:"language"`
	/// The human readable codec name.
	Codec string `json:"codec" db:"codec"`
	/// The codec of this stream (defined as the RFC 6381).
	MimeCodec *string `json:"mimeCodec" db:"mime_codec"`
	/// The number of channels that stream has.
	Channels int `json:"channels" db:"channels"`
	/// The average bitrate of the audio in bytes/s
	Bitrate uint32 `json:"bitrate" db:"bitrate"`
	/// Is this stream the default one of it's type?
	IsDefault bool `json:"isDefault" db:"is_default"`

	/// Keyframes of this video
	Keyframes *Keyframe `json:"-"`
}

func (*Audio) Quality

func (audio *Audio) Quality() AudioQuality

type AudioKey

type AudioKey struct {
	// contains filtered or unexported fields
}

type AudioQuality

type AudioQuality string
const (
	K128      AudioQuality = "128k"
	K192      AudioQuality = "192k"
	K256      AudioQuality = "256k"
	K320      AudioQuality = "320k"
	K512      AudioQuality = "512k"
	AOriginal AudioQuality = "original"
)

func AudioQualityFromString

func AudioQualityFromString(str string) (AudioQuality, error)

func (AudioQuality) Bitrate

func (a AudioQuality) Bitrate() uint32

type AudioStream

type AudioStream struct {
	Stream
	// contains filtered or unexported fields
}

type CMap

type CMap[K comparable, V any] struct {
	// contains filtered or unexported fields
}

func NewCMap

func NewCMap[K comparable, V any]() CMap[K, V]

func (*CMap[K, V]) Get

func (m *CMap[K, V]) Get(key K) (V, bool)

func (*CMap[K, V]) GetAndRemove

func (m *CMap[K, V]) GetAndRemove(key K) (V, bool)

func (*CMap[K, V]) GetOrCreate

func (m *CMap[K, V]) GetOrCreate(key K, create func() V) (V, bool)

func (*CMap[K, V]) GetOrSet

func (m *CMap[K, V]) GetOrSet(key K, val V) (V, bool)

func (*CMap[K, V]) Remove

func (m *CMap[K, V]) Remove(key K)

func (*CMap[K, V]) Set

func (m *CMap[K, V]) Set(key K, val V)

type Chapter

type Chapter struct {
	Id int32 `json:"-" db:"id"`

	/// The start time of the chapter (in second from the start of the episode).
	StartTime float32 `json:"startTime" db:"start_time"`
	/// The end time of the chapter (in second from the start of the episode).
	EndTime float32 `json:"endTime" db:"end_time"`
	/// The name of this chapter. This should be a human-readable name that could be presented to the user.
	Name string `json:"name" db:"name"`
	/// The type value is used to mark special chapters (opening/credits...)
	Type ChapterType `json:"type" db:"type"`
	// true only for introductions where the audio track is new (first time we'we heard this one in the serie)
	FirstAppearance *bool `json:"firstAppearance,omitempty" db:"first_appearance"`
	/// Accuracy of the fingerprint match (0-100).
	MatchAccuracy *int32 `json:"matchAccuracy,omitempty" db:"match_accuracy"`
}

type ChapterType

type ChapterType string
const (
	Content ChapterType = "content"
	Recap   ChapterType = "recap"
	Intro   ChapterType = "intro"
	Credits ChapterType = "credits"
	Preview ChapterType = "preview"
)

type ClientInfo

type ClientInfo struct {
	// contains filtered or unexported fields
}

type ClientStatus

type ClientStatus struct {
	ClientId  string       `json:"clientId"`
	ProfileId *string      `json:"profileId"`
	SessionId *string      `json:"sessionId"`
	Video     *ViewerTrack `json:"video"`
	Audio     *ViewerTrack `json:"audio"`
}

type FileStream

type FileStream struct {
	Out  string
	Info *MediaInfo
	// contains filtered or unexported fields
}

func (*FileStream) Destroy

func (fs *FileStream) Destroy(ctx context.Context)

func (*FileStream) GetAudioIndex

func (fs *FileStream) GetAudioIndex(ctx context.Context, idx uint32, quality AudioQuality, client string) (string, error)

func (*FileStream) GetAudioSegment

func (fs *FileStream) GetAudioSegment(ctx context.Context, idx uint32, quality AudioQuality, segment int32) (string, error)

func (*FileStream) GetMaster

func (fs *FileStream) GetMaster(ctx context.Context, client string) string

func (*FileStream) GetVideoIndex

func (fs *FileStream) GetVideoIndex(ctx context.Context, idx uint32, quality VideoQuality, client string) (string, error)

func (*FileStream) GetVideoSegment

func (fs *FileStream) GetVideoSegment(ctx context.Context, idx uint32, quality VideoQuality, segment int32) (string, error)

func (*FileStream) Kill

func (fs *FileStream) Kill()

type Fingerprint

type Fingerprint struct {
	Start []uint32
	End   []uint32
}

type Flags

type Flags int32
const (
	AudioF   Flags = 1 << 0
	VideoF   Flags = 1 << 1
	CopyF    Flags = 1 << 2
	Transmux Flags = 1 << 3
)
type Head struct {
	// contains filtered or unexported fields
}

type HeadRange

type HeadRange struct {
	Start     float64 `json:"start"`
	End       float64 `json:"end"`
	StartHead int32   `json:"startHead"`
	EndHead   int32   `json:"endHead"`
	IsRunning bool    `json:"isRunning"`
}

type HwAccelT

type HwAccelT struct {
	Name           string
	DecodeFlags    []string
	EncodeFlags    []string
	NoResizeFilter string
	ScaleFilter    string
}

func DetectHardwareAccel

func DetectHardwareAccel() HwAccelT

type Keyframe

type Keyframe struct {
	Keyframes []float64
	IsDone    bool
	// contains filtered or unexported fields
}

func (*Keyframe) AddListener

func (kf *Keyframe) AddListener(callback func(keyframesLen int))

func (*Keyframe) Get

func (kf *Keyframe) Get(idx int32) float64

func (*Keyframe) Length

func (kf *Keyframe) Length() (int32, bool)

func (*Keyframe) Scan

func (kf *Keyframe) Scan(src any) error

func (*Keyframe) Slice

func (kf *Keyframe) Slice(start int32, end int32) []float64

type KeyframeInfo

type KeyframeInfo struct {
	// contains filtered or unexported fields
}

type KeyframeKey

type KeyframeKey struct {
	Sha     string
	IsVideo bool
	Index   uint32
}

type Match

type Match struct {
	Start    float64
	Duration float64
	Accuracy int
}

type MediaInfo

type MediaInfo struct {
	// Auto-increment id used as foreign key for related tables.
	Id int32 `json:"id" db:"id"`
	// The sha1 of the video file.
	Sha string `json:"sha" db:"sha"`
	/// The internal path of the video file.
	Path string `json:"path" db:"path"`
	/// The extension currently used to store this video file
	Extension string `json:"extension" db:"extension"`
	/// The whole mimetype (defined as the RFC 6381). ex: `video/mp4; codecs=\"avc1.640028, mp4a.40.2\"`
	MimeCodec *string `json:"mimeCodec" db:"mime_codec"`
	/// The file size of the video file.
	Size int64 `json:"size" db:"size"`
	/// The length of the media in seconds.
	Duration float64 `json:"duration" db:"duration"`
	/// The container of the video file of this episode.
	Container *string `json:"container" db:"container"`
	/// Version of the metadata. This can be used to invalidate older metadata from db if the extraction code has changed.
	Versions Versions `json:"versions" db:"versions"`

	/// The list of videos if there are multiples.
	Videos []Video `json:"videos" db:"-"`
	/// The list of audio tracks.
	Audios []Audio `json:"audios" db:"-"`
	/// The list of subtitles tracks.
	Subtitles []Subtitle `json:"subtitles" db:"-"`
	/// The list of fonts that can be used to display subtitles.
	Fonts []string `json:"fonts" db:"fonts"`
	/// The list of chapters. See Chapter for more information.
	Chapters []Chapter `json:"chapters" db:"-"`
	// contains filtered or unexported fields
}

func RetriveMediaInfo

func RetriveMediaInfo(ctx context.Context, path string, sha string) (*MediaInfo, error)

func (*MediaInfo) SearchExternalSubtitles

func (mi *MediaInfo) SearchExternalSubtitles() error

type MetadataService

type MetadataService struct {
	Database *pgxpool.Pool
	// contains filtered or unexported fields
}

func NewMetadataService

func NewMetadataService() (*MetadataService, error)

func (*MetadataService) Close

func (s *MetadataService) Close() error

func (*MetadataService) ComputeFingerprint

func (s *MetadataService) ComputeFingerprint(ctx context.Context, info *MediaInfo) (*Fingerprint, error)

func (*MetadataService) ExtractSubs

func (s *MetadataService) ExtractSubs(ctx context.Context, info *MediaInfo) (any, error)

func (*MetadataService) ExtractThumbs

func (s *MetadataService) ExtractThumbs(ctx context.Context, path string, sha string) (interface{}, error)

func (*MetadataService) GetAttachment

func (s *MetadataService) GetAttachment(ctx context.Context, sha string, name string) (io.ReadCloser, error)

func (*MetadataService) GetKeyframes

func (s *MetadataService) GetKeyframes(ctx context.Context, info *MediaInfo, isVideo bool, idx uint32) (*Keyframe, error)

func (*MetadataService) GetMetadata

func (s *MetadataService) GetMetadata(ctx context.Context, path string, sha string) (*MediaInfo, error)

func (*MetadataService) GetSubtitle

func (s *MetadataService) GetSubtitle(
	ctx context.Context,
	path string,
	sha string,
	name string,
) (io.ReadCloser, error)

func (*MetadataService) GetThumbSprite

func (s *MetadataService) GetThumbSprite(ctx context.Context, path string, sha string) (io.ReadCloser, error)

func (*MetadataService) GetThumbVtt

func (s *MetadataService) GetThumbVtt(ctx context.Context, path string, sha string) (io.ReadCloser, error)

func (*MetadataService) IdentifyChapters

func (s *MetadataService) IdentifyChapters(
	ctx context.Context,
	info *MediaInfo,
	prev string,
	next string,
) error

func (*MetadataService) StoreFingerprint

func (s *MetadataService) StoreFingerprint(ctx context.Context, infoID int32, fingerprint *Fingerprint) error

type Overlap

type Overlap struct {
	StartFirst  float64
	StartSecond float64
	Duration    float64
	Accuracy    int
}

func FpFindOverlap

func FpFindOverlap(ctx context.Context, fp1 []uint32, fp2 []uint32) ([]Overlap, error)

FpFindOverlap finds all similar segments (like shared intro music) between two chromaprint fingerprints.

  1. Hash each fingerprint value by its top 14 bits to find the best time-offset alignment between the two fingerprints (like AcoustID's match_fingerprints2)
  2. Align the fingerprints at that offset.
  3. Divide the aligned region into ~2-second blocks and compute correlation per block using the AcoustID scoring formula.
  4. Find contiguous runs of high-correlation blocks that are at least MinOverlapDuration long.

type Result

type Result[V any] struct {
	// contains filtered or unexported fields
}

type RunLock

type RunLock[K comparable, V any] struct {
	// contains filtered or unexported fields
}

func NewRunLock

func NewRunLock[K comparable, V any]() RunLock[K, V]

func (*RunLock[K, V]) Start

func (r *RunLock[K, V]) Start(key K) (func() (V, error), func(val V, err error) (V, error))

func (*RunLock[K, V]) WaitFor

func (r *RunLock[K, V]) WaitFor(key K) (V, error)

type Segment

type Segment struct {
	// contains filtered or unexported fields
}

type SettingsT

type SettingsT struct {
	Outpath  string
	SafePath string
	JwksUrl  string
	HwAccel  HwAccelT
}

type Stream

type Stream struct {
	// contains filtered or unexported fields
}

func (*Stream) GetIndex

func (ts *Stream) GetIndex(_ context.Context, client string) (string, error)

func (*Stream) GetSegment

func (ts *Stream) GetSegment(ctx context.Context, segment int32) (string, error)

func (*Stream) Kill

func (ts *Stream) Kill()

func (*Stream) KillHead

func (ts *Stream) KillHead(encoder_id int)

Stream assume to be locked

type StreamHandle

type StreamHandle interface {
	// contains filtered or unexported methods
}

type StreamStatus

type StreamStatus struct {
	Path     string            `json:"path"`
	Sha      string            `json:"sha"`
	Duration float64           `json:"duration"`
	Videos   []TranscodeStatus `json:"videos"`
	Audios   []TranscodeStatus `json:"audios"`
	Viewers  []ClientStatus    `json:"viewers"`
}

type Subtitle

type Subtitle struct {
	Id int32 `json:"-" db:"id"`

	/// The index of this track on the media.
	Index *uint32 `json:"index" db:"idx"`
	/// The title of the stream.
	Title *string `json:"title" db:"title"`
	/// The language of this stream (as a IETF-BCP-47 language code)
	Language *string `json:"language" db:"language"`
	/// The codec of this stream.
	Codec string `json:"codec" db:"codec"`
	/// The extension for the codec.
	Extension *string `json:"extension" db:"extension"`
	/// Is this stream the default one of it's type?
	IsDefault bool `json:"isDefault" db:"is_default"`
	/// Is this stream tagged as forced?
	IsForced bool `json:"isForced" db:"is_forced"`
	/// Is this stream tagged as hearing impaired?
	IsHearingImpaired bool `json:"isHearingImpaired" db:"is_hearing_impaired"`
	/// Is this an external subtitle (as in stored in a different file)
	IsExternal bool `json:"isExternal" db:"-"`
	/// Where the subtitle is stored (null if stored inside the video)
	Path *string `json:"path" db:"-"`
	/// The link to access this subtitle.
	Link *string `json:"link" db:"-"`
}

type Task

type Task[V any] struct {
	// contains filtered or unexported fields
}

type Thumbnail

type Thumbnail struct {
	// contains filtered or unexported fields
}

type Tracker

type Tracker struct {
	// contains filtered or unexported fields
}

func NewTracker

func NewTracker(t *Transcoder) *Tracker

func (*Tracker) DestroyStreamIfOld

func (t *Tracker) DestroyStreamIfOld(sha string)

func (*Tracker) KillAudioIfDead

func (t *Tracker) KillAudioIfDead(sha string, path string, audio AudioKey) bool

func (*Tracker) KillOrphanedHeads

func (t *Tracker) KillOrphanedHeads(sha string, video *VideoKey, audio *AudioKey)

func (*Tracker) KillStreamIfDead

func (t *Tracker) KillStreamIfDead(sha string, path string) bool

func (*Tracker) KillVideoIfDead

func (t *Tracker) KillVideoIfDead(sha string, path string, video VideoKey) bool

func (*Tracker) SnapshotClients

func (t *Tracker) SnapshotClients() map[string]ClientInfo

type TranscodeStatus

type TranscodeStatus struct {
	Index   uint32      `json:"index"`
	Quality string      `json:"quality"`
	Heads   []HeadRange `json:"heads"`
}

type Transcoder

type Transcoder struct {
	// contains filtered or unexported fields
}

func NewTranscoder

func NewTranscoder(metadata *MetadataService) (*Transcoder, error)

func (*Transcoder) GetAudioIndex

func (t *Transcoder) GetAudioIndex(
	ctx context.Context,
	path string,
	audio uint32,
	quality AudioQuality,
	client string,
	profileId *string,
	sessionId *string,
	sha string,
) (string, error)

func (*Transcoder) GetAudioSegment

func (t *Transcoder) GetAudioSegment(
	ctx context.Context,
	path string,
	audio uint32,
	quality AudioQuality,
	segment int32,
	client string,
	profileId *string,
	sessionId *string,
	sha string,
) (string, error)

func (*Transcoder) GetMaster

func (t *Transcoder) GetMaster(ctx context.Context, path string, client string, profileId *string, sessionId *string, sha string) (string, error)

func (*Transcoder) GetVideoIndex

func (t *Transcoder) GetVideoIndex(
	ctx context.Context,
	path string,
	video uint32,
	quality VideoQuality,
	client string,
	profileId *string,
	sessionId *string,
	sha string,
) (string, error)

func (*Transcoder) GetVideoSegment

func (t *Transcoder) GetVideoSegment(
	ctx context.Context,
	path string,
	video uint32,
	quality VideoQuality,
	segment int32,
	client string,
	profileId *string,
	sessionId *string,
	sha string,
) (string, error)

func (*Transcoder) ListRunningStreams

func (t *Transcoder) ListRunningStreams() []StreamStatus

func (*Transcoder) NewAudioStream

func (t *Transcoder) NewAudioStream(ctx context.Context, file *FileStream, idx uint32, quality AudioQuality) (*AudioStream, error)

func (*Transcoder) NewVideoStream

func (t *Transcoder) NewVideoStream(ctx context.Context, file *FileStream, idx uint32, quality VideoQuality) (*VideoStream, error)

type Versions

type Versions struct {
	Info        int32 `json:"info" db:"ver_info"`
	Extract     int32 `json:"extract" db:"ver_extract"`
	Thumbs      int32 `json:"thumbs" db:"ver_thumbs"`
	Keyframes   int32 `json:"keyframes" db:"ver_keyframes"`
	Fingerprint int32 `json:"fingerprint" db:"ver_fingerprint"`
	/// List of sha this was fingerprinted with
	FpWith []string `json:"fpWith" db:"ver_fp_with"`
}

type Video

type Video struct {
	Id int32 `json:"-" db:"id"`

	/// The index of this track on the media.
	Index uint32 `json:"index" db:"idx"`
	/// The title of the stream.
	Title *string `json:"title" db:"title"`
	/// The language of this stream (as a ISO-639-2 language code)
	Language *string `json:"language" db:"language"`
	/// The human readable codec name.
	Codec string `json:"codec" db:"codec"`
	/// The codec of this stream (defined as the RFC 6381).
	MimeCodec *string `json:"mimeCodec" db:"mime_codec"`
	/// The width of the video stream
	Width uint32 `json:"width" db:"width"`
	/// The height of the video stream
	Height uint32 `json:"height" db:"height"`
	/// The average bitrate of the video in bytes/s
	Bitrate uint32 `json:"bitrate" db:"bitrate"`
	/// Is this stream the default one of it's type?
	IsDefault bool `json:"isDefault" db:"is_default"`

	/// Keyframes of this video
	Keyframes *Keyframe `json:"-"`
}

func (*Video) Quality

func (video *Video) Quality() VideoQuality

type VideoKey

type VideoKey struct {
	// contains filtered or unexported fields
}

type VideoQuality

type VideoQuality string
const (
	P240     VideoQuality = "240p"
	P360     VideoQuality = "360p"
	P480     VideoQuality = "480p"
	P720     VideoQuality = "720p"
	P1080    VideoQuality = "1080p"
	P1440    VideoQuality = "1440p"
	P4k      VideoQuality = "4k"
	P8k      VideoQuality = "8k"
	NoResize VideoQuality = "transcode"
	Original VideoQuality = "original"
)

func VideoQualityFromString

func VideoQualityFromString(str string) (VideoQuality, error)

func (VideoQuality) AverageBitrate

func (v VideoQuality) AverageBitrate() uint32

I'm not entierly sure about the values for bitrates. Double checking would be nice.

func (VideoQuality) Height

func (q VideoQuality) Height() uint32

func (VideoQuality) MaxBitrate

func (v VideoQuality) MaxBitrate() uint32

type VideoStream

type VideoStream struct {
	Stream
	// contains filtered or unexported fields
}

type ViewerTrack

type ViewerTrack struct {
	Index   uint32 `json:"index"`
	Quality string `json:"quality"`
	Head    int32  `json:"head"`
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL