Update media info retrival with db schema

This commit is contained in:
Zoe Roux 2024-07-25 21:50:21 +02:00
parent c5047babca
commit 5047364ad3
3 changed files with 70 additions and 36 deletions

View File

@ -8,11 +8,11 @@ create table info(
size bigint not null,
duration real not null,
container varchar(256),
fonts text[],
ver_ffprobe integer,
ver_extract integer,
ver_thumbs integer,
ver_keyframes integer
fonts text[] not null,
ver_info integer not null,
ver_extract integer not null,
ver_thumbs integer not null,
ver_keyframes integer not null
);
create table videos(

View File

@ -20,6 +20,15 @@ import (
"gopkg.in/vansante/go-ffprobe.v2"
)
const InfoVersion = 1
type Versions struct {
Info int32 `db:"ver_info"`
Extract int32 `db:"ver_extract"`
Thumbs int32 `db:"ver_thumbs"`
Keyframes int32 `db:"ver_keyframes"`
}
type MediaInfo struct {
// The sha1 of the video file.
Sha string `json:"sha"`
@ -28,15 +37,19 @@ type MediaInfo struct {
/// The extension currently used to store this video file
Extension string `json:"extension"`
/// The whole mimetype (defined as the RFC 6381). ex: `video/mp4; codecs="avc1.640028, mp4a.40.2"`
MimeCodec *string `json:"mimeCodec"`
MimeCodec *string `json:"mimeCodec" db:"mime_codec"`
/// The file size of the video file.
Size int64 `json:"size"`
/// The length of the media in seconds.
Duration float32 `json:"duration"`
/// The container of the video file of this episode.
Container *string `json:"container"`
/// The video codec and informations.
Video *Video `json:"video"`
/// Version of the metadata. This can be used to invalidate older metadata from db if the extraction code has changed.
Versions Versions
// TODO: remove this
Video *Video
/// The list of videos if there are multiples.
Videos []Video `json:"videos"`
/// The list of audio tracks.
@ -50,14 +63,16 @@ type MediaInfo struct {
}
type Video struct {
/// The human readable codec name.
Codec string `json:"codec"`
/// The codec of this stream (defined as the RFC 6381).
MimeCodec *string `json:"mimeCodec"`
/// The index of this track on the media.
Index uint32 `json:"index" db:"idx"`
/// The title of the stream.
Title *string `json:"title"`
/// The language of this stream (as a ISO-639-2 language code)
Language *string `json:"language"`
/// The human readable codec name.
Codec string `json:"codec"`
/// The codec of this stream (defined as the RFC 6381).
MimeCodec *string `json:"mimeCodec" db:"mime_codec"`
/// The max quality of this video track.
Quality Quality `json:"quality"`
/// The width of the video stream
@ -70,7 +85,7 @@ type Video struct {
type Audio struct {
/// The index of this track on the media.
Index uint32 `json:"index"`
Index uint32 `json:"index" db:"idx"`
/// The title of the stream.
Title *string `json:"title"`
/// The language of this stream (as a IETF-BCP-47 language code)
@ -78,16 +93,14 @@ type Audio struct {
/// The human readable codec name.
Codec string `json:"codec"`
/// The codec of this stream (defined as the RFC 6381).
MimeCodec *string `json:"mimeCodec"`
MimeCodec *string `json:"mimeCodec" db:"mime_codec"`
/// Is this stream the default one of it's type?
IsDefault bool `json:"isDefault"`
/// Is this stream tagged as forced? (useful only for subtitles)
IsForced bool `json:"isForced"`
IsDefault bool `json:"isDefault" db:"is_default"`
}
type Subtitle struct {
/// The index of this track on the media.
Index uint32 `json:"index"`
Index uint32 `json:"index" db:"idx"`
/// The title of the stream.
Title *string `json:"title"`
/// The language of this stream (as a IETF-BCP-47 language code)
@ -97,23 +110,38 @@ type Subtitle struct {
/// The extension for the codec.
Extension *string `json:"extension"`
/// Is this stream the default one of it's type?
IsDefault bool `json:"isDefault"`
/// Is this stream tagged as forced? (useful only for subtitles)
IsForced bool `json:"isForced"`
IsDefault bool `json:"isDefault" db:"is_default"`
/// Is this stream tagged as forced?
IsForced bool `json:"isForced" db:"is_forced"`
/// Is this an external subtitle (as in stored in a different file)
IsExternal bool `json:"isExternal" db:"is_external"`
/// Where the subtitle is stored (either in library if IsExternal is true or in transcoder cache if false)
/// Null if the subtitle can't be extracted (unsupported format)
Path *string `json:"path"`
/// The link to access this subtitle.
Link *string `json:"link"`
}
type Chapter struct {
/// The start time of the chapter (in second from the start of the episode).
StartTime float32 `json:"startTime"`
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"`
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"`
// TODO: add a type field for Opening, Credits...
/// The type value is used to mark special chapters (openning/credits...)
Type ChapterType
}
type ChapterType string
const (
Content ChapterType = "content"
Recap ChapterType = "recap"
Intro ChapterType = "intro"
Credits ChapterType = "credits"
)
func ParseFloat(str string) float32 {
f, err := strconv.ParseFloat(str, 32)
if err != nil {
@ -209,7 +237,7 @@ func GetInfo(path string, sha string) (*MediaInfo, error) {
}
var val *MediaInfo
val, err = getInfo(path)
val, err = RetriveMediaInfo(path, sha)
if err == nil {
*mi.info = *val
mi.info.Sha = sha
@ -247,7 +275,7 @@ func saveInfo[T any](save_path string, mi *T) error {
return os.WriteFile(save_path, content, 0o644)
}
func getInfo(path string) (*MediaInfo, error) {
func RetriveMediaInfo(path string, sha string) (*MediaInfo, error) {
defer printExecTime("mediainfo for %s", path)()
ctx, cancelFn := context.WithTimeout(context.Background(), 30*time.Second)
@ -259,15 +287,23 @@ func getInfo(path string) (*MediaInfo, error) {
}
ret := MediaInfo{
Sha: sha,
Path: path,
// Remove leading .
Extension: filepath.Ext(path)[1:],
Size: ParseInt64(mi.Format.Size),
Duration: float32(mi.Format.DurationSeconds),
Container: OrNull(mi.Format.FormatName),
Versions: Versions{
Info: InfoVersion,
Extract: 0,
Thumbs: 0,
Keyframes: 0,
},
Videos: MapStream(mi.Streams, ffprobe.StreamVideo, func(stream *ffprobe.Stream, i uint32) Video {
lang, _ := language.Parse(stream.Tags.Language)
return Video{
Index: i,
Codec: stream.CodecName,
MimeCodec: GetMimeCodec(stream),
Title: OrNull(stream.Tags.Title),
@ -289,15 +325,15 @@ func getInfo(path string) (*MediaInfo, error) {
Codec: stream.CodecName,
MimeCodec: GetMimeCodec(stream),
IsDefault: stream.Disposition.Default != 0,
IsForced: stream.Disposition.Forced != 0,
}
}),
Subtitles: MapStream(mi.Streams, ffprobe.StreamSubtitle, func(stream *ffprobe.Stream, i uint32) Subtitle {
extension := OrNull(SubtitleExtensions[stream.CodecName])
var link *string
var link string
var path string
if extension != nil {
x := fmt.Sprintf("%s/%s/subtitle/%d.%s", Settings.RoutePrefix, base64.StdEncoding.EncodeToString([]byte(path)), i, *extension)
link = &x
link = fmt.Sprintf("%s/%s/subtitle/%d.%s", Settings.RoutePrefix, base64.StdEncoding.EncodeToString([]byte(path)), i, *extension)
path = fmt.Sprintf("%s/%s/sub/%d.%s", Settings.Metadata, sha, i, extension)
}
lang, _ := language.Parse(stream.Tags.Language)
return Subtitle{
@ -308,7 +344,8 @@ func getInfo(path string) (*MediaInfo, error) {
Extension: extension,
IsDefault: stream.Disposition.Default != 0,
IsForced: stream.Disposition.Forced != 0,
Link: link,
Link: &link,
Path: &path,
}
}),
Chapters: Map(mi.Chapters, func(c *ffprobe.Chapter, _ int) Chapter {
@ -316,6 +353,8 @@ func getInfo(path string) (*MediaInfo, error) {
Name: c.Title(),
StartTime: float32(c.StartTimeSeconds),
EndTime: float32(c.EndTimeSeconds),
// TODO: detect content type
Type: Content,
}
}),
Fonts: MapStream(mi.Streams, ffprobe.StreamAttachment, func(stream *ffprobe.Stream, i uint32) string {

View File

@ -14,11 +14,6 @@ import (
"github.com/zoriya/kyoo/transcoder/src"
)
// Encode the version in the hash path to update cached values.
// Older versions won't be deleted (needed to allow multiples versions of the transcoder to run at the same time)
// If the version changes a lot, we might want to automatically delete older versions.
var version = "v3-"
func GetPath(c echo.Context) (string, string, error) {
key := c.Param("path")
if key == "" {