mirror of
https://github.com/zoriya/Kyoo.git
synced 2026-04-25 02:20:02 -04:00
Create types/routes for fingerprinting
This commit is contained in:
parent
fbb995a3a6
commit
f09728a993
9
transcoder/migrations/000005_fingerprints.down.sql
Normal file
9
transcoder/migrations/000005_fingerprints.down.sql
Normal file
@ -0,0 +1,9 @@
|
||||
begin;
|
||||
|
||||
alter table gocoder.chapters drop column match_accuracy;
|
||||
alter table gocoder.chapters drop column fingerprint_id;
|
||||
drop table gocoder.chapterprints;
|
||||
drop table gocoder.fingerprints;
|
||||
alter table gocoder.info drop column ver_fingerprint;
|
||||
|
||||
commit;
|
||||
19
transcoder/migrations/000005_fingerprints.up.sql
Normal file
19
transcoder/migrations/000005_fingerprints.up.sql
Normal file
@ -0,0 +1,19 @@
|
||||
begin;
|
||||
|
||||
alter table gocoder.info add column ver_fingerprint integer not null default 0;
|
||||
|
||||
create table gocoder.fingerprints(
|
||||
id integer not null primary key references gocoder.info(id) on delete cascade,
|
||||
start_data text not null,
|
||||
end_data text not null
|
||||
);
|
||||
|
||||
create table gocoder.chapterprints(
|
||||
id serial primary key,
|
||||
data text not null
|
||||
);
|
||||
|
||||
alter table gocoder.chapters add column fingerprint_id integer references gocoder.chapterprints(id) on delete set null;
|
||||
alter table gocoder.chapters add column match_accuracy integer;
|
||||
|
||||
commit;
|
||||
@ -23,7 +23,7 @@ func RegisterMetadataHandlers(e *echo.Group, metadata *src.MetadataService) {
|
||||
h := mhandler{metadata}
|
||||
|
||||
e.GET("/:path/info", h.GetInfo)
|
||||
e.GET("/:path/prepare", h.Prepare)
|
||||
e.POST("/:path/prepare", h.Prepare)
|
||||
e.GET("/:path/subtitle/:name", h.GetSubtitle)
|
||||
e.GET("/:path/attachment/:name", h.GetAttachment)
|
||||
e.GET("/:path/thumbnails.png", h.GetThumbnails)
|
||||
@ -62,10 +62,11 @@ func (h *mhandler) GetInfo(c *echo.Context) error {
|
||||
Container: nil,
|
||||
MimeCodec: nil,
|
||||
Versions: src.Versions{
|
||||
Info: -1,
|
||||
Extract: 0,
|
||||
Thumbs: 0,
|
||||
Keyframes: 0,
|
||||
Info: -1,
|
||||
Extract: 0,
|
||||
Thumbs: 0,
|
||||
Keyframes: 0,
|
||||
Fingerprint: 0,
|
||||
},
|
||||
Videos: make([]src.Video, 0),
|
||||
Audios: make([]src.Audio, 0),
|
||||
@ -77,22 +78,34 @@ func (h *mhandler) GetInfo(c *echo.Context) error {
|
||||
return c.JSON(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
type PrepareRequest struct {
|
||||
// File path of the previous/next episodes (for audio fingerprinting).
|
||||
NearEpisodes *string `json:"nearEpisodes"`
|
||||
}
|
||||
|
||||
// @Summary Prepare metadata
|
||||
//
|
||||
// @Description Starts metadata preparation in background (info, extract, thumbs, keyframes).
|
||||
// @Description Starts metadata preparation in background (info, extract, thumbs, keyframes, chapter identification).
|
||||
//
|
||||
// @Tags metadata
|
||||
// @Param path path string true "Base64 of a video's path" format(base64) example(L3ZpZGVvL2J1YmJsZS5ta3YK)
|
||||
// @Param body body PrepareRequest false "Adjacent episode paths for chapter detection"
|
||||
//
|
||||
// @Success 202 "Preparation started"
|
||||
// @Router /:path/prepare [get]
|
||||
// @Router /:path/prepare [post]
|
||||
func (h *mhandler) Prepare(c *echo.Context) error {
|
||||
path, sha, err := getPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func(path string, sha string) {
|
||||
var req PrepareRequest
|
||||
err = c.Bind(&req)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error())
|
||||
}
|
||||
|
||||
go func() {
|
||||
bgCtx := context.Background()
|
||||
|
||||
info, err := h.metadata.GetMetadata(bgCtx, path, sha)
|
||||
@ -113,7 +126,9 @@ func (h *mhandler) Prepare(c *echo.Context) error {
|
||||
fmt.Printf("failed to extract audio keyframes for %s (stream %d): %v\n", path, audio.Index, err)
|
||||
}
|
||||
}
|
||||
}(path, sha)
|
||||
|
||||
h.metadata.IdentifyChapters(bgCtx, info, req.NearEpisodes)
|
||||
}()
|
||||
|
||||
return c.NoContent(http.StatusAccepted)
|
||||
}
|
||||
|
||||
@ -20,10 +20,11 @@ import (
|
||||
const InfoVersion = 4
|
||||
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type MediaInfo struct {
|
||||
@ -149,6 +150,10 @@ type Chapter struct {
|
||||
Name string `json:"name" db:"name"`
|
||||
/// The type value is used to mark special chapters (openning/credits...)
|
||||
Type ChapterType `json:"type" db:"type"`
|
||||
/// Reference to the chapterprint used for fingerprint matching.
|
||||
FingerprintId *int32 `json:"-" db:"fingerprint_id"`
|
||||
/// Accuracy of the fingerprint match (0-100).
|
||||
MatchAccuracy *int32 `json:"matchAccuracy,omitempty" db:"match_accuracy"`
|
||||
}
|
||||
|
||||
type ChapterType string
|
||||
@ -255,10 +260,11 @@ func RetriveMediaInfo(path string, sha string) (*MediaInfo, error) {
|
||||
Duration: mi.Format.DurationSeconds,
|
||||
Container: OrNull(mi.Format.FormatName),
|
||||
Versions: Versions{
|
||||
Info: InfoVersion,
|
||||
Extract: 0,
|
||||
Thumbs: 0,
|
||||
Keyframes: 0,
|
||||
Info: InfoVersion,
|
||||
Extract: 0,
|
||||
Thumbs: 0,
|
||||
Keyframes: 0,
|
||||
Fingerprint: 0,
|
||||
},
|
||||
Videos: MapStream(mi.Streams, ffprobe.StreamVideo, func(stream *ffprobe.Stream, i uint32) Video {
|
||||
lang, _ := language.Parse(stream.Tags.Language)
|
||||
|
||||
@ -180,6 +180,19 @@ func (s *MetadataService) GetMetadata(ctx context.Context, path string, sha stri
|
||||
}
|
||||
}
|
||||
|
||||
if ret.Versions.Fingerprint < FingerprintVersion && ret.Versions.Fingerprint != 0 {
|
||||
tx, err := s.Database.Begin(bgCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tx.Exec(bgCtx, `delete from gocoder.fingerprints where id = $1`, ret.Id)
|
||||
tx.Exec(bgCtx, `update gocoder.info set ver_fingerprint = 0 where id = $1`, ret.Id)
|
||||
err = tx.Commit(bgCtx)
|
||||
if err != nil {
|
||||
fmt.Printf("error deleting old fingerprints from database: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@ -192,7 +205,8 @@ func (s *MetadataService) getMetadata(ctx context.Context, path string, sha stri
|
||||
'info', i.ver_info,
|
||||
'extract', i.ver_extract,
|
||||
'thumbs', i.ver_thumbs,
|
||||
'keyframes', i.ver_keyframes
|
||||
'keyframes', i.ver_keyframes,
|
||||
'fingerprint', i.ver_fingerprint
|
||||
) as versions
|
||||
from gocoder.info as i
|
||||
where i.sha=$1 limit 1`,
|
||||
@ -286,13 +300,14 @@ func (s *MetadataService) storeFreshMetadata(ctx context.Context, path string, s
|
||||
err = tx.QueryRow(ctx,
|
||||
`
|
||||
insert into gocoder.info(sha, path, extension, mime_codec, size, duration, container,
|
||||
fonts, ver_info, ver_extract, ver_thumbs, ver_keyframes)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||
fonts, ver_info, ver_extract, ver_thumbs, ver_keyframes, ver_fingerprint)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
||||
returning id
|
||||
`,
|
||||
// on conflict do not update versions of extract/thumbs/keyframes
|
||||
ret.Sha, ret.Path, ret.Extension, ret.MimeCodec, ret.Size, ret.Duration, ret.Container,
|
||||
ret.Fonts, ret.Versions.Info, ret.Versions.Extract, ret.Versions.Thumbs, ret.Versions.Keyframes,
|
||||
ret.Versions.Fingerprint,
|
||||
).Scan(&ret.Id)
|
||||
if err != nil {
|
||||
return set(ret, fmt.Errorf("failed to insert info: %w", err))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user