feat(library): detección de intro/créditos post-scan (skip segments)
Some checks failed
CI / Test (push) Failing after 6m18s
CI / Build (push) Successful in 1m32s
CI / Build-1 (push) Successful in 1m55s
CI / Build-2 (push) Successful in 1m33s
CI / Build-3 (push) Successful in 1m32s
CI / Build-4 (push) Successful in 1m35s
CI / Build-5 (push) Successful in 1m33s
CI / Lint (push) Failing after 2m50s
CI / Coverage (push) Successful in 2m58s
CI / Vet (push) Successful in 2m7s
Some checks failed
CI / Test (push) Failing after 6m18s
CI / Build (push) Successful in 1m32s
CI / Build-1 (push) Successful in 1m55s
CI / Build-2 (push) Successful in 1m33s
CI / Build-3 (push) Successful in 1m32s
CI / Build-4 (push) Successful in 1m35s
CI / Build-5 (push) Successful in 1m33s
CI / Lint (push) Failing after 2m50s
CI / Coverage (push) Successful in 2m58s
CI / Vet (push) Successful in 2m7s
Tras cada scan, localiza la intro (OP) y los créditos (ED) comparando fingerprints chromaprint entre episodios de la misma temporada — reimplementación limpia del enfoque de Intro Skipper: índice invertido de uint32, alineamiento por shifts, Hamming ≤6/32, región contigua más larga (15-120s intro / 15-450s créditos). Películas: inicio de créditos por rachas de blackframe (solo keyframes, -skip_frame nokey) que llegan al final del fichero. - fpcalc se auto-descarga de las releases estáticas de acoustid (linux/macos/windows, ~2MB) con el mismo patrón que ffmpeg/ffprobe. - Resultados cacheados como sidecar .skipseg.json (mtime + versión de algoritmo); solo los ficheros nuevos trabajan. - Submit a /api/internal/agent/skip-segments DESPUÉS del library-sync, en dos fases (episodios primero, películas después) para que la fase rápida no espere a los blackframe lentos sobre NAS. - Agrupación por (dir + título-pre-SxxEyy + season): los títulos parseados arrastran nombre de episodio y tags de release. - Gotcha cazado en vivo: fpcalc -length sale sin drenar el pipe; hay que cerrar nuestro read-end o ffmpeg queda bloqueado para siempre. - config: library.skip_detect (default true, backfill) y scan_interval default 24h → 1h (estilo Plex).
This commit is contained in:
parent
59da949a53
commit
a710bc1626
11 changed files with 1223 additions and 5 deletions
|
|
@ -512,3 +512,14 @@ func (c *Client) handleResponse(resp *http.Response, dst any) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubmitSkipSegments uploads detected intro/credits segments after a library
|
||||
// scan. Must run AFTER SyncLibrary — the server resolves file paths against
|
||||
// the freshly-synced library_item rows.
|
||||
func (c *Client) SubmitSkipSegments(ctx context.Context, req SkipSegmentsRequest) (*SkipSegmentsResponse, error) {
|
||||
var resp SkipSegmentsResponse
|
||||
if err := c.doPostWith(ctx, c.librarySyncClient, "/api/internal/agent/skip-segments", req, &resp); err != nil {
|
||||
return nil, fmt.Errorf("skip segments: %w", err)
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -592,3 +592,38 @@ type WatchProgressUpdate struct {
|
|||
type WatchProgressResponse struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Skip-segment types (intro/credits detection — see library/skipdetect.go)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// SkipSegmentRange is one detected skippable range inside a media file.
|
||||
type SkipSegmentRange struct {
|
||||
Category string `json:"category"` // "intro" | "credits"
|
||||
StartSec float64 `json:"startSec"`
|
||||
EndSec float64 `json:"endSec"`
|
||||
}
|
||||
|
||||
// SkipSegmentItem carries the detected segments of one library file. The
|
||||
// server resolves FilePath against the user's library_item rows (synced just
|
||||
// before) to attach the segments to a content identity.
|
||||
type SkipSegmentItem struct {
|
||||
FilePath string `json:"filePath"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Season int `json:"season,omitempty"`
|
||||
Episode int `json:"episode,omitempty"`
|
||||
DurationSec float64 `json:"durationSec"`
|
||||
Segments []SkipSegmentRange `json:"segments"`
|
||||
}
|
||||
|
||||
// SkipSegmentsRequest submits detected skip segments after a library scan.
|
||||
type SkipSegmentsRequest struct {
|
||||
AgentID string `json:"agentId,omitempty"`
|
||||
Items []SkipSegmentItem `json:"items"`
|
||||
}
|
||||
|
||||
// SkipSegmentsResponse reports how many segments the server stored.
|
||||
type SkipSegmentsResponse struct {
|
||||
Stored int `json:"stored"`
|
||||
Unmatched int `json:"unmatched"`
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue