mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
FFMPEG auto download
This commit is contained in:
24
README.md
24
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://travis-ci.org/stashapp/stash)
|
||||
|
||||
**Stash is a rails app which organizes and serves your porn.**
|
||||
**Stash is a Go app which organizes and serves your porn.**
|
||||
|
||||
See a demo [here](https://vimeo.com/275537038) (password is stashapp).
|
||||
|
||||
@@ -15,18 +15,26 @@ TODO: This is not final. There is more work to be done to ease this process.
|
||||
### OSX / Linux
|
||||
|
||||
1. `mkdir ~/.stash` && `cd ~/.stash`
|
||||
2. Download FFMPEG ([macOS](https://ffmpeg.zeranoe.com/builds/macos64/static/ffmpeg-4.0-macos64-static.zip), [Linux](https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.0.3-64bit-static.tar.xz)) and extract so that just `ffmpeg` and `ffprobe` are in `~/.stash`
|
||||
3. Create a `config.json` file (see below).
|
||||
4. Run stash with `./stash` and visit `http://localhost:9998` or `https://localhost:9999`
|
||||
2. Create a `config.json` file (see below).
|
||||
3. Run stash with `./stash` and visit `http://localhost:9998` or `https://localhost:9999`
|
||||
|
||||
### Windows
|
||||
|
||||
1. Create a new folder at `C:\Users\YourUsername\.stash`
|
||||
2. Download [FFMPEG](https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-4.0-win64-static.zip) and extract so that just `ffmpeg.exe` and `ffprobe.exe` are in `C:\Users\YourUsername\.stash`
|
||||
3. Create a `config.json` file (see below)
|
||||
4. Run stash with `./stash` and visit `http://localhost:9998` or `https://localhost:9999`
|
||||
2. Create a `config.json` file (see below)
|
||||
3. Run stash with `./stash` and visit `http://localhost:9998` or `https://localhost:9999`
|
||||
|
||||
### Config.json
|
||||
#### FFMPEG
|
||||
|
||||
If stash is unable to find or download FFMPEG then download it yourself from the link for your platform:
|
||||
|
||||
* [macOS](https://ffmpeg.zeranoe.com/builds/macos64/static/ffmpeg-4.0-macos64-static.zip)
|
||||
* [Windows](https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-4.0-win64-static.zip)
|
||||
* [Linux](https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz)
|
||||
|
||||
The `ffmpeg(.exe)` and `ffprobe(.exe)` files should be placed in `~/.stash` on macOS / Linux or `C:\Users\YourUsername\.stash` on Windows.
|
||||
|
||||
#### Config.json
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
168
ffmpeg/downloader.go
Normal file
168
ffmpeg/downloader.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package ffmpeg
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"github.com/stashapp/stash/utils"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetPaths(configDirectory string) (string, string) {
|
||||
var ffmpegPath, ffprobePath string
|
||||
|
||||
// Check if ffmpeg exists in the PATH
|
||||
if pathBinaryHasCorrectFlags() {
|
||||
ffmpegPath, _ = exec.LookPath("ffmpeg")
|
||||
ffprobePath, _ = exec.LookPath("ffprobe")
|
||||
}
|
||||
|
||||
// Check if ffmpeg exists in the config directory
|
||||
ffmpegConfigPath := filepath.Join(configDirectory, getFFMPEGFilename())
|
||||
ffprobeConfigPath := filepath.Join(configDirectory, getFFProbeFilename())
|
||||
ffmpegConfigExists, _ := utils.FileExists(ffmpegConfigPath)
|
||||
ffprobeConfigExists, _ := utils.FileExists(ffprobeConfigPath)
|
||||
if ffmpegPath == "" && ffmpegConfigExists {
|
||||
ffmpegPath = ffmpegConfigPath
|
||||
}
|
||||
if ffprobePath == "" && ffprobeConfigExists {
|
||||
ffprobePath = ffprobeConfigPath
|
||||
}
|
||||
|
||||
return ffmpegPath, ffprobePath
|
||||
}
|
||||
|
||||
func Download(configDirectory string) error {
|
||||
url := getFFMPEGURL()
|
||||
if url == "" {
|
||||
return fmt.Errorf("no ffmpeg url for this platform")
|
||||
}
|
||||
|
||||
// Configure where we want to download the archive
|
||||
urlExt := path.Ext(url)
|
||||
archivePath := filepath.Join(configDirectory, "ffmpeg"+urlExt)
|
||||
_ = os.Remove(archivePath) // remove archive if it already exists
|
||||
out, err := os.Create(archivePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// Make the HTTP request
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Check server response
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("bad status: %s", resp.Status)
|
||||
}
|
||||
|
||||
// Write the response to the archive file location
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if urlExt == ".zip" {
|
||||
if err := unzip(archivePath, configDirectory); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("FFMPeg was downloaded to %s. ")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFFMPEGURL() string {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
return "https://ffmpeg.zeranoe.com/builds/macos64/static/ffmpeg-4.1-macos64-static.zip"
|
||||
case "linux":
|
||||
// TODO: untar this
|
||||
//return "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz"
|
||||
return ""
|
||||
case "windows":
|
||||
return "https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-4.1-win64-static.zip"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func getFFMPEGFilename() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return "ffmpeg.exe"
|
||||
} else {
|
||||
return "ffmpeg"
|
||||
}
|
||||
}
|
||||
|
||||
func getFFProbeFilename() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return "ffprobe.exe"
|
||||
} else {
|
||||
return "ffprobe"
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if FFMPEG in the path has the correct flags
|
||||
func pathBinaryHasCorrectFlags() bool {
|
||||
ffmpegPath, err := exec.LookPath("ffmpeg")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
bytes, _ := exec.Command(ffmpegPath).CombinedOutput()
|
||||
output := string(bytes)
|
||||
hasOpus := strings.Contains(output, "--enable-libopus")
|
||||
hasVpx := strings.Contains(output, "--enable-libvpx")
|
||||
hasX264 := strings.Contains(output, "--enable-libx264")
|
||||
hasX265 := strings.Contains(output, "--enable-libx265")
|
||||
hasWebp := strings.Contains(output, "--enable-libwebp")
|
||||
return hasOpus && hasVpx && hasX264 && hasX265 && hasWebp
|
||||
}
|
||||
|
||||
func unzip(src, configDirectory string) error {
|
||||
zipReader, err := zip.OpenReader(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zipReader.Close()
|
||||
|
||||
for _, f := range zipReader.File {
|
||||
if f.FileInfo().IsDir() {
|
||||
continue
|
||||
}
|
||||
filename := f.FileInfo().Name()
|
||||
if filename != "ffprobe" && filename != "ffmpeg" && filename != "ffprobe.exe" && filename != "ffmpeg.exe" {
|
||||
continue
|
||||
}
|
||||
|
||||
rc, err := f.Open()
|
||||
|
||||
unzippedPath := filepath.Join(configDirectory, filename)
|
||||
unzippedOutput, err := os.Create(unzippedPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(unzippedOutput, rc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := unzippedOutput.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
2
main.go
2
main.go
@@ -18,7 +18,7 @@ func main() {
|
||||
//fmt.Println("hello world")
|
||||
|
||||
managerInstance := manager.Initialize()
|
||||
database.Initialize(managerInstance.Paths.FixedPaths.DatabaseFile)
|
||||
database.Initialize(managerInstance.StaticPaths.DatabaseFile)
|
||||
|
||||
api.Start()
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func NewPreviewGenerator(videoFile ffmpeg.VideoFile, videoFilename string, image
|
||||
func (g *PreviewGenerator) Generate() error {
|
||||
instance.Paths.Generated.EmptyTmpDir()
|
||||
logger.Infof("[generator] generating scene preview for %s", g.Info.VideoFile.Path)
|
||||
encoder := ffmpeg.NewEncoder(instance.Paths.FixedPaths.FFMPEG)
|
||||
encoder := ffmpeg.NewEncoder(instance.StaticPaths.FFMPEG)
|
||||
|
||||
if err := g.generateConcatFile(); err != nil {
|
||||
return err
|
||||
|
||||
@@ -49,7 +49,7 @@ func NewSpriteGenerator(videoFile ffmpeg.VideoFile, imageOutputPath string, vttO
|
||||
|
||||
func (g *SpriteGenerator) Generate() error {
|
||||
instance.Paths.Generated.EmptyTmpDir()
|
||||
encoder := ffmpeg.NewEncoder(instance.Paths.FixedPaths.FFMPEG)
|
||||
encoder := ffmpeg.NewEncoder(instance.StaticPaths.FFMPEG)
|
||||
|
||||
if err := g.generateSpriteImage(&encoder); err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"github.com/bmatcuk/doublestar"
|
||||
"github.com/stashapp/stash/ffmpeg"
|
||||
"github.com/stashapp/stash/logger"
|
||||
"github.com/stashapp/stash/manager/paths"
|
||||
"github.com/stashapp/stash/models"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type singleton struct {
|
||||
Status JobStatus
|
||||
Paths *paths.Paths
|
||||
StaticPaths *paths.StaticPathsType
|
||||
JSON *jsonUtils
|
||||
}
|
||||
|
||||
@@ -28,126 +27,29 @@ func Initialize() *singleton {
|
||||
instance = &singleton{
|
||||
Status: Idle,
|
||||
Paths: paths.RefreshPaths(),
|
||||
StaticPaths: &paths.StaticPaths,
|
||||
JSON: &jsonUtils{},
|
||||
}
|
||||
|
||||
initFFMPEG()
|
||||
})
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
func (s *singleton) Scan() {
|
||||
if s.Status != Idle { return }
|
||||
s.Status = Scan
|
||||
func initFFMPEG() {
|
||||
ffmpegPath, ffprobePath := ffmpeg.GetPaths(instance.StaticPaths.ConfigDirectory)
|
||||
if ffmpegPath == "" || ffprobePath == "" {
|
||||
logger.Infof("couldn't find FFMPEG, attempting to download it")
|
||||
if err := ffmpeg.Download(instance.StaticPaths.ConfigDirectory); err != nil {
|
||||
msg := `Unable to locate / automatically download FFMPEG
|
||||
|
||||
go func() {
|
||||
defer s.returnToIdleState()
|
||||
Check the readme for download links.
|
||||
The FFMPEG and FFProbe binaries should be placed in %s
|
||||
|
||||
globPath := filepath.Join(s.Paths.Config.Stash, "**/*.{zip,m4v,mp4,mov,wmv}")
|
||||
globResults, _ := doublestar.Glob(globPath)
|
||||
logger.Infof("Starting scan of %d files", len(globResults))
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, path := range globResults {
|
||||
wg.Add(1)
|
||||
task := ScanTask{FilePath: path}
|
||||
go task.Start(&wg)
|
||||
wg.Wait()
|
||||
The error was: %s
|
||||
`
|
||||
logger.Fatalf(msg, instance.StaticPaths.ConfigDirectory, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *singleton) Import() {
|
||||
if s.Status != Idle { return }
|
||||
s.Status = Import
|
||||
|
||||
go func() {
|
||||
defer s.returnToIdleState()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
task := ImportTask{}
|
||||
go task.Start(&wg)
|
||||
wg.Wait()
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *singleton) Export() {
|
||||
if s.Status != Idle { return }
|
||||
s.Status = Export
|
||||
|
||||
go func() {
|
||||
defer s.returnToIdleState()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
task := ExportTask{}
|
||||
go task.Start(&wg)
|
||||
wg.Wait()
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *singleton) Generate(sprites bool, previews bool, markers bool, transcodes bool) {
|
||||
if s.Status != Idle { return }
|
||||
s.Status = Generate
|
||||
|
||||
qb := models.NewSceneQueryBuilder()
|
||||
//this.job.total = await ObjectionUtils.getCount(Scene);
|
||||
instance.Paths.Generated.EnsureTmpDir()
|
||||
|
||||
go func() {
|
||||
defer s.returnToIdleState()
|
||||
|
||||
scenes, err := qb.All()
|
||||
if err != nil {
|
||||
logger.Errorf("failed to get scenes for generate")
|
||||
return
|
||||
}
|
||||
|
||||
delta := btoi(sprites) + btoi(previews) + btoi(markers) + btoi(transcodes)
|
||||
var wg sync.WaitGroup
|
||||
for _, scene := range scenes {
|
||||
wg.Add(delta)
|
||||
|
||||
if sprites {
|
||||
task := GenerateSpriteTask{Scene: scene}
|
||||
go task.Start(&wg)
|
||||
}
|
||||
|
||||
if previews {
|
||||
task := GeneratePreviewTask{Scene: scene}
|
||||
go task.Start(&wg)
|
||||
}
|
||||
|
||||
if markers {
|
||||
task := GenerateMarkersTask{Scene: scene}
|
||||
go task.Start(&wg)
|
||||
}
|
||||
|
||||
if transcodes {
|
||||
go func() {
|
||||
wg.Done() // TODO
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *singleton) returnToIdleState() {
|
||||
if r := recover(); r!= nil {
|
||||
logger.Info("recovered from ", r)
|
||||
}
|
||||
|
||||
if s.Status == Generate {
|
||||
instance.Paths.Generated.RemoveTmpDir()
|
||||
}
|
||||
s.Status = Idle
|
||||
}
|
||||
|
||||
func btoi(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
120
manager/manager_tasks.go
Normal file
120
manager/manager_tasks.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"github.com/bmatcuk/doublestar"
|
||||
"github.com/stashapp/stash/logger"
|
||||
"github.com/stashapp/stash/models"
|
||||
"github.com/stashapp/stash/utils"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func (s *singleton) Scan() {
|
||||
if s.Status != Idle { return }
|
||||
s.Status = Scan
|
||||
|
||||
go func() {
|
||||
defer s.returnToIdleState()
|
||||
|
||||
globPath := filepath.Join(s.Paths.Config.Stash, "**/*.{zip,m4v,mp4,mov,wmv}")
|
||||
globResults, _ := doublestar.Glob(globPath)
|
||||
logger.Infof("Starting scan of %d files", len(globResults))
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, path := range globResults {
|
||||
wg.Add(1)
|
||||
task := ScanTask{FilePath: path}
|
||||
go task.Start(&wg)
|
||||
wg.Wait()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *singleton) Import() {
|
||||
if s.Status != Idle { return }
|
||||
s.Status = Import
|
||||
|
||||
go func() {
|
||||
defer s.returnToIdleState()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
task := ImportTask{}
|
||||
go task.Start(&wg)
|
||||
wg.Wait()
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *singleton) Export() {
|
||||
if s.Status != Idle { return }
|
||||
s.Status = Export
|
||||
|
||||
go func() {
|
||||
defer s.returnToIdleState()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
task := ExportTask{}
|
||||
go task.Start(&wg)
|
||||
wg.Wait()
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *singleton) Generate(sprites bool, previews bool, markers bool, transcodes bool) {
|
||||
if s.Status != Idle { return }
|
||||
s.Status = Generate
|
||||
|
||||
qb := models.NewSceneQueryBuilder()
|
||||
//this.job.total = await ObjectionUtils.getCount(Scene);
|
||||
instance.Paths.Generated.EnsureTmpDir()
|
||||
|
||||
go func() {
|
||||
defer s.returnToIdleState()
|
||||
|
||||
scenes, err := qb.All()
|
||||
if err != nil {
|
||||
logger.Errorf("failed to get scenes for generate")
|
||||
return
|
||||
}
|
||||
|
||||
delta := utils.Btoi(sprites) + utils.Btoi(previews) + utils.Btoi(markers) + utils.Btoi(transcodes)
|
||||
var wg sync.WaitGroup
|
||||
for _, scene := range scenes {
|
||||
wg.Add(delta)
|
||||
|
||||
if sprites {
|
||||
task := GenerateSpriteTask{Scene: scene}
|
||||
go task.Start(&wg)
|
||||
}
|
||||
|
||||
if previews {
|
||||
task := GeneratePreviewTask{Scene: scene}
|
||||
go task.Start(&wg)
|
||||
}
|
||||
|
||||
if markers {
|
||||
task := GenerateMarkersTask{Scene: scene}
|
||||
go task.Start(&wg)
|
||||
}
|
||||
|
||||
if transcodes {
|
||||
go func() {
|
||||
wg.Done() // TODO
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *singleton) returnToIdleState() {
|
||||
if r := recover(); r!= nil {
|
||||
logger.Info("recovered from ", r)
|
||||
}
|
||||
|
||||
if s.Status == Generate {
|
||||
instance.Paths.Generated.RemoveTmpDir()
|
||||
}
|
||||
s.Status = Idle
|
||||
}
|
||||
@@ -3,13 +3,9 @@ package paths
|
||||
import (
|
||||
"github.com/stashapp/stash/manager/jsonschema"
|
||||
"github.com/stashapp/stash/utils"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Paths struct {
|
||||
FixedPaths *fixedPaths
|
||||
Config *jsonschema.Config
|
||||
Generated *generatedPaths
|
||||
JSON *jsonPaths
|
||||
@@ -20,15 +16,13 @@ type Paths struct {
|
||||
}
|
||||
|
||||
func RefreshPaths() *Paths {
|
||||
fp := newFixedPaths()
|
||||
ensureConfigFile(fp)
|
||||
return newPaths(fp)
|
||||
ensureConfigFile()
|
||||
return newPaths()
|
||||
}
|
||||
|
||||
func newPaths(fp *fixedPaths) *Paths {
|
||||
func newPaths() *Paths {
|
||||
p := Paths{}
|
||||
p.FixedPaths = fp
|
||||
p.Config = jsonschema.LoadConfigFile(p.FixedPaths.ConfigFile)
|
||||
p.Config = jsonschema.LoadConfigFile(StaticPaths.ConfigFile)
|
||||
p.Generated = newGeneratedPaths(p)
|
||||
p.JSON = newJSONPaths(p)
|
||||
|
||||
@@ -38,24 +32,8 @@ func newPaths(fp *fixedPaths) *Paths {
|
||||
return &p
|
||||
}
|
||||
|
||||
func getExecutionDirectory() string {
|
||||
ex, err := os.Executable()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return filepath.Dir(ex)
|
||||
}
|
||||
|
||||
func getHomeDirectory() string {
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return currentUser.HomeDir
|
||||
}
|
||||
|
||||
func ensureConfigFile(fp *fixedPaths) {
|
||||
configFileExists, _ := utils.FileExists(fp.ConfigFile) // TODO: Verify JSON is correct. Pass verified
|
||||
func ensureConfigFile() {
|
||||
configFileExists, _ := utils.FileExists(StaticPaths.ConfigFile) // TODO: Verify JSON is correct. Pass verified
|
||||
if configFileExists {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package paths
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stashapp/stash/utils"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type fixedPaths struct {
|
||||
ExecutionDirectory string
|
||||
ConfigDirectory string
|
||||
ConfigFile string
|
||||
DatabaseFile string
|
||||
|
||||
FFMPEG string
|
||||
FFProbe string
|
||||
}
|
||||
|
||||
func newFixedPaths() *fixedPaths {
|
||||
fp := fixedPaths{}
|
||||
fp.ExecutionDirectory = getExecutionDirectory()
|
||||
fp.ConfigDirectory = filepath.Join(getHomeDirectory(), ".stash")
|
||||
fp.ConfigFile = filepath.Join(fp.ConfigDirectory, "config.json")
|
||||
fp.DatabaseFile = filepath.Join(fp.ConfigDirectory, "stash-go.sqlite")
|
||||
|
||||
ffmpegDirectories := []string{fp.ExecutionDirectory, fp.ConfigDirectory}
|
||||
ffmpegFileName := func() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return "ffmpeg.exe"
|
||||
} else {
|
||||
return "ffmpeg"
|
||||
}
|
||||
}()
|
||||
ffprobeFileName := func() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return "ffprobe.exe"
|
||||
} else {
|
||||
return "ffprobe"
|
||||
}
|
||||
}()
|
||||
for _, directory := range ffmpegDirectories {
|
||||
ffmpegPath := filepath.Join(directory, ffmpegFileName)
|
||||
ffprobePath := filepath.Join(directory, ffprobeFileName)
|
||||
if exists, _ := utils.FileExists(ffmpegPath); exists {
|
||||
fp.FFMPEG = ffmpegPath
|
||||
}
|
||||
if exists, _ := utils.FileExists(ffprobePath); exists {
|
||||
fp.FFProbe = ffprobePath
|
||||
}
|
||||
}
|
||||
|
||||
errorText := fmt.Sprintf(
|
||||
"FFMPEG or FFProbe not found. Place it in one of the following folders:\n\n%s",
|
||||
strings.Join(ffmpegDirectories, ","),
|
||||
)
|
||||
if exists, _ := utils.FileExists(fp.FFMPEG); !exists {
|
||||
panic(errorText)
|
||||
}
|
||||
if exists, _ := utils.FileExists(fp.FFProbe); !exists {
|
||||
panic(errorText)
|
||||
}
|
||||
|
||||
return &fp
|
||||
}
|
||||
44
manager/paths/paths_static.go
Normal file
44
manager/paths/paths_static.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package paths
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type StaticPathsType struct {
|
||||
ExecutionDirectory string
|
||||
ConfigDirectory string
|
||||
ConfigFile string
|
||||
DatabaseFile string
|
||||
|
||||
FFMPEG string
|
||||
FFProbe string
|
||||
}
|
||||
|
||||
var StaticPaths = StaticPathsType{
|
||||
ExecutionDirectory: getExecutionDirectory(),
|
||||
ConfigDirectory: getConfigDirectory(),
|
||||
ConfigFile: filepath.Join(getConfigDirectory(), "config.json"),
|
||||
DatabaseFile: filepath.Join(getConfigDirectory(), "stash-go.sqlite"),
|
||||
}
|
||||
|
||||
func getExecutionDirectory() string {
|
||||
ex, err := os.Executable()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return filepath.Dir(ex)
|
||||
}
|
||||
|
||||
func getHomeDirectory() string {
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return currentUser.HomeDir
|
||||
}
|
||||
|
||||
func getConfigDirectory() string {
|
||||
return filepath.Join(getHomeDirectory(), ".stash")
|
||||
}
|
||||
@@ -25,7 +25,7 @@ func (t *GenerateMarkersTask) Start(wg *sync.WaitGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
videoFile, err := ffmpeg.NewVideoFile(instance.Paths.FixedPaths.FFProbe, t.Scene.Path)
|
||||
videoFile, err := ffmpeg.NewVideoFile(instance.StaticPaths.FFProbe, t.Scene.Path)
|
||||
if err != nil {
|
||||
logger.Errorf("error reading video file: %s", err.Error())
|
||||
return
|
||||
@@ -35,7 +35,7 @@ func (t *GenerateMarkersTask) Start(wg *sync.WaitGroup) {
|
||||
markersFolder := filepath.Join(instance.Paths.Generated.Markers, t.Scene.Checksum)
|
||||
_ = utils.EnsureDir(markersFolder)
|
||||
|
||||
encoder := ffmpeg.NewEncoder(instance.Paths.FixedPaths.FFMPEG)
|
||||
encoder := ffmpeg.NewEncoder(instance.StaticPaths.FFMPEG)
|
||||
for i, sceneMarker := range sceneMarkers {
|
||||
index := i + 1
|
||||
logger.Progressf("[generator] <%s> scene marker %d of %d", t.Scene.Checksum, index, len(sceneMarkers))
|
||||
|
||||
@@ -21,7 +21,7 @@ func (t *GeneratePreviewTask) Start(wg *sync.WaitGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
videoFile, err := ffmpeg.NewVideoFile(instance.Paths.FixedPaths.FFProbe, t.Scene.Path)
|
||||
videoFile, err := ffmpeg.NewVideoFile(instance.StaticPaths.FFProbe, t.Scene.Path)
|
||||
if err != nil {
|
||||
logger.Errorf("error reading video file: %s", err.Error())
|
||||
return
|
||||
|
||||
@@ -19,7 +19,7 @@ func (t *GenerateSpriteTask) Start(wg *sync.WaitGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
videoFile, err := ffmpeg.NewVideoFile(instance.Paths.FixedPaths.FFProbe, t.Scene.Path)
|
||||
videoFile, err := ffmpeg.NewVideoFile(instance.StaticPaths.FFProbe, t.Scene.Path)
|
||||
if err != nil {
|
||||
logger.Errorf("error reading video file: %s", err.Error())
|
||||
return
|
||||
|
||||
@@ -33,7 +33,7 @@ func (t *ImportTask) Start(wg *sync.WaitGroup) {
|
||||
}
|
||||
t.Scraped = scraped
|
||||
|
||||
database.Reset(instance.Paths.FixedPaths.DatabaseFile)
|
||||
database.Reset(instance.StaticPaths.DatabaseFile)
|
||||
|
||||
ctx := context.TODO()
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ func (t *ScanTask) scanGallery() {
|
||||
}
|
||||
|
||||
func (t *ScanTask) scanScene() {
|
||||
videoFile, err := ffmpeg.NewVideoFile(instance.Paths.FixedPaths.FFProbe, t.FilePath)
|
||||
videoFile, err := ffmpeg.NewVideoFile(instance.StaticPaths.FFProbe, t.FilePath)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return
|
||||
@@ -142,7 +142,7 @@ func (t *ScanTask) makeScreenshots(probeResult ffmpeg.VideoFile, checksum string
|
||||
}
|
||||
|
||||
func (t *ScanTask) makeScreenshot(probeResult ffmpeg.VideoFile, outputPath string, quality int, width int) {
|
||||
encoder := ffmpeg.NewEncoder(instance.Paths.FixedPaths.FFMPEG)
|
||||
encoder := ffmpeg.NewEncoder(instance.StaticPaths.FFMPEG)
|
||||
options := ffmpeg.ScreenshotOptions{
|
||||
OutputPath: outputPath,
|
||||
Quality: quality,
|
||||
|
||||
8
utils/boolean.go
Normal file
8
utils/boolean.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package utils
|
||||
|
||||
func Btoi(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user