mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
159 lines
4.3 KiB
Go
159 lines
4.3 KiB
Go
package src
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
|
|
"github.com/zoriya/kyoo/transcoder/src/storage"
|
|
"github.com/zoriya/kyoo/transcoder/src/utils"
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
const ExtractVersion = 1
|
|
|
|
func (s *MetadataService) ExtractSubs(ctx context.Context, info *MediaInfo) (interface{}, error) {
|
|
get_running, set := s.extractLock.Start(info.Sha)
|
|
if get_running != nil {
|
|
return get_running()
|
|
}
|
|
|
|
err := s.extractSubs(ctx, info)
|
|
if err != nil {
|
|
return set(nil, err)
|
|
}
|
|
_, err = s.database.Exec(`update info set ver_extract = $2 where sha = $1`, info.Sha, ExtractVersion)
|
|
return set(nil, err)
|
|
}
|
|
|
|
func (s *MetadataService) GetAttachment(ctx context.Context, sha string, name string) (io.ReadCloser, error) {
|
|
_, err := s.extractLock.WaitFor(sha)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
itemPath := fmt.Sprintf("%s/%s/%s", sha, "att", name)
|
|
|
|
item, err := s.storage.GetItem(ctx, itemPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get attachment with path %q: %w", itemPath, err)
|
|
}
|
|
return item, nil
|
|
}
|
|
|
|
func (s *MetadataService) GetSubtitle(ctx context.Context, sha string, name string) (io.ReadCloser, error) {
|
|
_, err := s.extractLock.WaitFor(sha)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
itemPath := fmt.Sprintf("%s/%s/%s", sha, "sub", name)
|
|
|
|
item, err := s.storage.GetItem(ctx, itemPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get subtitle with path %q: %w", itemPath, err)
|
|
}
|
|
return item, nil
|
|
}
|
|
|
|
func (s *MetadataService) extractSubs(ctx context.Context, info *MediaInfo) (err error) {
|
|
defer utils.PrintExecTime("extraction of %s", info.Path)()
|
|
|
|
// If there are no supported, embedded subtitles, there is nothing to extract.
|
|
hasSupportedSubtitle := false
|
|
for _, sub := range info.Subtitles {
|
|
if !sub.IsExternal && sub.Extension != nil {
|
|
hasSupportedSubtitle = true
|
|
break
|
|
}
|
|
}
|
|
if !hasSupportedSubtitle {
|
|
return nil
|
|
}
|
|
|
|
// Create a temporary directory for writing attachments
|
|
// TODO if the transcoder ever uses the ffmpeg library directly, remove this
|
|
// and write directly to storage via stream instead
|
|
tempWorkingDirectory := filepath.Join(os.TempDir(), info.Sha)
|
|
if err := os.MkdirAll(tempWorkingDirectory, 0660); err != nil {
|
|
return fmt.Errorf("failed to create temporary directory: %w", err)
|
|
}
|
|
|
|
attachmentWorkingDirectory := filepath.Join(tempWorkingDirectory, "att")
|
|
if err := os.MkdirAll(attachmentWorkingDirectory, 0660); err != nil {
|
|
return fmt.Errorf("failed to create attachment directory: %w", err)
|
|
}
|
|
|
|
subtitlesWorkingDirectory := filepath.Join(tempWorkingDirectory, "sub")
|
|
if err := os.MkdirAll(subtitlesWorkingDirectory, 0660); err != nil {
|
|
return fmt.Errorf("failed to create subtitles directory: %w", err)
|
|
}
|
|
|
|
defer utils.CleanupWithErr(
|
|
&err, func() error {
|
|
return os.RemoveAll(attachmentWorkingDirectory)
|
|
},
|
|
"failed to remove attachment working directory",
|
|
)
|
|
|
|
// Dump the attachments and subtitles
|
|
cmd := exec.Command(
|
|
"ffmpeg",
|
|
"-dump_attachment:t", "",
|
|
// override old attachments
|
|
"-y",
|
|
"-i", info.Path,
|
|
)
|
|
cmd.Dir = attachmentWorkingDirectory
|
|
|
|
for _, sub := range info.Subtitles {
|
|
if ext := sub.Extension; ext != nil {
|
|
if sub.IsExternal {
|
|
// skip extraction of external subtitles
|
|
continue
|
|
}
|
|
cmd.Args = append(
|
|
cmd.Args,
|
|
"-map", fmt.Sprintf("0:s:%d", *sub.Index),
|
|
"-c:s", "copy",
|
|
fmt.Sprintf("%s/%d.%s", subtitlesWorkingDirectory, *sub.Index, *ext),
|
|
)
|
|
}
|
|
}
|
|
log.Printf("Starting extraction with the command: %s", cmd)
|
|
cmd.Stdout = nil
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Println("Error starting ffmpeg extract:", err)
|
|
return err
|
|
}
|
|
|
|
// Move attachments and subtitles to the storage backend
|
|
var saveGroup errgroup.Group
|
|
saveGroup.Go(func() error {
|
|
err := storage.SaveFilesToBackend(ctx, s.storage, attachmentWorkingDirectory, filepath.Join(info.Sha, "att"))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to save attachments to backend: %w", err)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
saveGroup.Go(func() error {
|
|
err := storage.SaveFilesToBackend(ctx, s.storage, subtitlesWorkingDirectory, filepath.Join(info.Sha, "sub"))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to save subtitles to backend: %w", err)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if err := saveGroup.Wait(); err != nil {
|
|
return fmt.Errorf("failed while saving files to backend: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|