Documentation
¶
Index ¶
- Constants
- Variables
- func Abs(x int32) int32
- func CompressFingerprint(fp []uint32) (string, error)
- func ComputeSha(path string) (string, error)
- func ConvertSubtitle(format string, stream io.ReadCloser, outFmt string, out io.WriteCloser) error
- func DecompressFingerprint(compressed string) ([]uint32, error)
- func GetEnvOr(env string, def string) string
- func GetMimeCodec(stream *ffprobe.Stream) *string
- func Map[T, U any](ts []T, f func(T, int) U) []U
- func MapStream[T any](streams []*ffprobe.Stream, kind ffprobe.StreamType, ...) []T
- func NewStream(file *FileStream, keyframes *Keyframe, handle StreamHandle, ret *Stream)
- func NullIfUnd(str string) *string
- func OrNull(str string) *string
- func ParseFloat(str string) float32
- func ParseInt64(str string) int64
- func ParseUint(str string) uint32
- type Audio
- type AudioKey
- type AudioQuality
- type AudioStream
- type CMap
- type Chapter
- type ChapterType
- type ClientInfo
- type ClientStatus
- type FileStream
- func (fs *FileStream) Destroy(ctx context.Context)
- func (fs *FileStream) GetAudioIndex(ctx context.Context, idx uint32, quality AudioQuality, client string) (string, error)
- func (fs *FileStream) GetAudioSegment(ctx context.Context, idx uint32, quality AudioQuality, segment int32) (string, error)
- func (fs *FileStream) GetMaster(ctx context.Context, client string) string
- func (fs *FileStream) GetVideoIndex(ctx context.Context, idx uint32, quality VideoQuality, client string) (string, error)
- func (fs *FileStream) GetVideoSegment(ctx context.Context, idx uint32, quality VideoQuality, segment int32) (string, error)
- func (fs *FileStream) Kill()
- type Fingerprint
- type Flags
- type Head
- type HeadRange
- type HwAccelT
- type Keyframe
- type KeyframeInfo
- type KeyframeKey
- type Match
- type MediaInfo
- type MetadataService
- func (s *MetadataService) Close() error
- func (s *MetadataService) ComputeFingerprint(ctx context.Context, info *MediaInfo) (*Fingerprint, error)
- func (s *MetadataService) ExtractSubs(ctx context.Context, info *MediaInfo) (any, error)
- func (s *MetadataService) ExtractThumbs(ctx context.Context, path string, sha string) (interface{}, error)
- func (s *MetadataService) GetAttachment(ctx context.Context, sha string, name string) (io.ReadCloser, error)
- func (s *MetadataService) GetKeyframes(ctx context.Context, info *MediaInfo, isVideo bool, idx uint32) (*Keyframe, error)
- func (s *MetadataService) GetMetadata(ctx context.Context, path string, sha string) (*MediaInfo, error)
- func (s *MetadataService) GetSubtitle(ctx context.Context, path string, sha string, name string) (io.ReadCloser, error)
- func (s *MetadataService) GetThumbSprite(ctx context.Context, path string, sha string) (io.ReadCloser, error)
- func (s *MetadataService) GetThumbVtt(ctx context.Context, path string, sha string) (io.ReadCloser, error)
- func (s *MetadataService) IdentifyChapters(ctx context.Context, info *MediaInfo, prev string, next string) error
- func (s *MetadataService) StoreFingerprint(ctx context.Context, infoID int32, fingerprint *Fingerprint) error
- type Overlap
- type Result
- type RunLock
- type Segment
- type SettingsT
- type Stream
- type StreamHandle
- type StreamStatus
- type Subtitle
- type Task
- type Thumbnail
- type Tracker
- func (t *Tracker) DestroyStreamIfOld(sha string)
- func (t *Tracker) KillAudioIfDead(sha string, path string, audio AudioKey) bool
- func (t *Tracker) KillOrphanedHeads(sha string, video *VideoKey, audio *AudioKey)
- func (t *Tracker) KillStreamIfDead(sha string, path string) bool
- func (t *Tracker) KillVideoIfDead(sha string, path string, video VideoKey) bool
- func (t *Tracker) SnapshotClients() map[string]ClientInfo
- type TranscodeStatus
- type Transcoder
- func (t *Transcoder) GetAudioIndex(ctx context.Context, path string, audio uint32, quality AudioQuality, ...) (string, error)
- func (t *Transcoder) GetAudioSegment(ctx context.Context, path string, audio uint32, quality AudioQuality, ...) (string, error)
- func (t *Transcoder) GetMaster(ctx context.Context, path string, client string, profileId *string, ...) (string, error)
- func (t *Transcoder) GetVideoIndex(ctx context.Context, path string, video uint32, quality VideoQuality, ...) (string, error)
- func (t *Transcoder) GetVideoSegment(ctx context.Context, path string, video uint32, quality VideoQuality, ...) (string, error)
- func (t *Transcoder) ListRunningStreams() []StreamStatus
- func (t *Transcoder) NewAudioStream(ctx context.Context, file *FileStream, idx uint32, quality AudioQuality) (*AudioStream, error)
- func (t *Transcoder) NewVideoStream(ctx context.Context, file *FileStream, idx uint32, quality VideoQuality) (*VideoStream, error)
- type Versions
- type Video
- type VideoKey
- type VideoQuality
- type VideoStream
- type ViewerTrack
Constants ¶
const ( FingerprintVersion = 1 FpStartPercent = 0.20 FpStartDuration = 10 * 60 FpEndDuration = 5 * 60 )
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 )
const ExtractVersion = 1
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.
const InfoVersion = 4
const (
KeyframeVersion = 2
)
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 )
const ThumbsVersion = 1
Variables ¶
var AudioQualities = []AudioQuality{K128, K192, K256, K320, K512}
var DeletedHead = Head{ // contains filtered or unexported fields }
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(), }
var SubtitleExtensions = map[string]string{
"subrip": "srt",
"ass": "ass",
"vtt": "vtt",
"hdmv_pgs_subtitle": "sup",
}
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 CompressFingerprint ¶
func ComputeSha ¶
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 GetMimeCodec ¶
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 NewStream ¶
func NewStream(file *FileStream, keyframes *Keyframe, handle StreamHandle, ret *Stream)
func ParseFloat ¶
func ParseInt64 ¶
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 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]) GetAndRemove ¶
func (*CMap[K, V]) GetOrCreate ¶
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 ¶
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 HwAccelT ¶
type HwAccelT struct {
Name string
DecodeFlags []string
EncodeFlags []string
NoResizeFilter string
ScaleFilter string
}
func DetectHardwareAccel ¶
func DetectHardwareAccel() HwAccelT
type Keyframe ¶
func (*Keyframe) AddListener ¶
type KeyframeInfo ¶
type KeyframeInfo struct {
// contains filtered or unexported fields
}
type KeyframeKey ¶
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 (*MediaInfo) SearchExternalSubtitles ¶
type MetadataService ¶
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 (*MetadataService) ExtractThumbs ¶
func (*MetadataService) GetAttachment ¶
func (s *MetadataService) GetAttachment(ctx context.Context, sha string, name string) (io.ReadCloser, error)
func (*MetadataService) GetKeyframes ¶
func (*MetadataService) GetMetadata ¶
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 (*MetadataService) StoreFingerprint ¶
func (s *MetadataService) StoreFingerprint(ctx context.Context, infoID int32, fingerprint *Fingerprint) error
type Overlap ¶
func FpFindOverlap ¶
FpFindOverlap finds all similar segments (like shared intro music) between two chromaprint fingerprints.
- 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)
- Align the fingerprints at that offset.
- Divide the aligned region into ~2-second blocks and compute correlation per block using the AcoustID scoring formula.
- Find contiguous runs of high-correlation blocks that are at least MinOverlapDuration long.
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]
type Stream ¶
type Stream struct {
// contains filtered or unexported fields
}
func (*Stream) GetSegment ¶
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 Tracker ¶
type Tracker struct {
// contains filtered or unexported fields
}
func NewTracker ¶
func NewTracker(t *Transcoder) *Tracker
func (*Tracker) DestroyStreamIfOld ¶
func (*Tracker) KillAudioIfDead ¶
func (*Tracker) KillOrphanedHeads ¶
func (*Tracker) KillVideoIfDead ¶
func (*Tracker) SnapshotClients ¶
func (t *Tracker) SnapshotClients() map[string]ClientInfo
type TranscodeStatus ¶
type Transcoder ¶
type Transcoder struct {
// contains filtered or unexported fields
}
func NewTranscoder ¶
func NewTranscoder(metadata *MetadataService) (*Transcoder, error)
func (*Transcoder) GetAudioIndex ¶
func (*Transcoder) GetAudioSegment ¶
func (*Transcoder) GetVideoIndex ¶
func (*Transcoder) GetVideoSegment ¶
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 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 ¶
Source Files
¶
- audioquality.go
- audiostream.go
- chapters.go
- cmap.go
- codec.go
- extract.go
- filestream.go
- fingerprints.go
- fingerprints_compare.go
- fingerprints_utils.go
- hwaccel.go
- info.go
- keyframes.go
- metadata.go
- runlock.go
- settings.go
- stream.go
- subtitles.go
- thumbnails.go
- tracker.go
- transcode_status.go
- transcoder.go
- videoquality.go
- videostream.go