mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Update media info retrival with db schema
This commit is contained in:
parent
c5047babca
commit
5047364ad3
@ -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(
|
||||
|
@ -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 {
|
||||
|
@ -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 == "" {
|
||||
|
Loading…
x
Reference in New Issue
Block a user