mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Strip file extension from scene title when scanning (#1022)
This commit is contained in:
@@ -34,6 +34,8 @@ input ScanMetadataInput {
|
|||||||
paths: [String!]
|
paths: [String!]
|
||||||
"""Set name, date, details from metadata (if present)"""
|
"""Set name, date, details from metadata (if present)"""
|
||||||
useFileMetadata: Boolean!
|
useFileMetadata: Boolean!
|
||||||
|
"""Strip file extension from title"""
|
||||||
|
stripFileExtension: Boolean!
|
||||||
"""Generate previews during scan"""
|
"""Generate previews during scan"""
|
||||||
scanGeneratePreviews: Boolean!
|
scanGeneratePreviews: Boolean!
|
||||||
"""Generate image previews during scan"""
|
"""Generate image previews during scan"""
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func getSceneFileContainer(scene *models.Scene) ffmpeg.Container {
|
|||||||
container = ffmpeg.Container(scene.Format.String)
|
container = ffmpeg.Container(scene.Format.String)
|
||||||
} else { // container isn't in the DB
|
} else { // container isn't in the DB
|
||||||
// shouldn't happen, fallback to ffprobe
|
// shouldn't happen, fallback to ffprobe
|
||||||
tmpVideoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path)
|
tmpVideoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("[transcode] error reading video file: %s", err.Error())
|
logger.Errorf("[transcode] error reading video file: %s", err.Error())
|
||||||
return ffmpeg.Container("")
|
return ffmpeg.Container("")
|
||||||
@@ -100,7 +100,7 @@ func (rs sceneRoutes) StreamMp4(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (rs sceneRoutes) StreamHLS(w http.ResponseWriter, r *http.Request) {
|
func (rs sceneRoutes) StreamHLS(w http.ResponseWriter, r *http.Request) {
|
||||||
scene := r.Context().Value(sceneKey).(*models.Scene)
|
scene := r.Context().Value(sceneKey).(*models.Scene)
|
||||||
|
|
||||||
videoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path)
|
videoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("[stream] error reading video file: %s", err.Error())
|
logger.Errorf("[stream] error reading video file: %s", err.Error())
|
||||||
return
|
return
|
||||||
@@ -136,7 +136,7 @@ func (rs sceneRoutes) streamTranscode(w http.ResponseWriter, r *http.Request, vi
|
|||||||
|
|
||||||
// needs to be transcoded
|
// needs to be transcoded
|
||||||
|
|
||||||
videoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path)
|
videoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("[stream] error reading video file: %s", err.Error())
|
logger.Errorf("[stream] error reading video file: %s", err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ type VideoFile struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute exec command and bind result to struct.
|
// Execute exec command and bind result to struct.
|
||||||
func NewVideoFile(ffprobePath string, videoPath string) (*VideoFile, error) {
|
func NewVideoFile(ffprobePath string, videoPath string, stripExt bool) (*VideoFile, error) {
|
||||||
args := []string{"-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", "-show_error", videoPath}
|
args := []string{"-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", "-show_error", videoPath}
|
||||||
//// Extremely slow on windows for some reason
|
//// Extremely slow on windows for some reason
|
||||||
//if runtime.GOOS != "windows" {
|
//if runtime.GOOS != "windows" {
|
||||||
@@ -239,10 +239,10 @@ func NewVideoFile(ffprobePath string, videoPath string) (*VideoFile, error) {
|
|||||||
return nil, fmt.Errorf("Error unmarshalling video data for <%s>: %s", videoPath, err.Error())
|
return nil, fmt.Errorf("Error unmarshalling video data for <%s>: %s", videoPath, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return parse(videoPath, probeJSON)
|
return parse(videoPath, probeJSON, stripExt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(filePath string, probeJSON *FFProbeJSON) (*VideoFile, error) {
|
func parse(filePath string, probeJSON *FFProbeJSON, stripExt bool) (*VideoFile, error) {
|
||||||
if probeJSON == nil {
|
if probeJSON == nil {
|
||||||
return nil, fmt.Errorf("failed to get ffprobe json for <%s>", filePath)
|
return nil, fmt.Errorf("failed to get ffprobe json for <%s>", filePath)
|
||||||
}
|
}
|
||||||
@@ -262,7 +262,7 @@ func parse(filePath string, probeJSON *FFProbeJSON) (*VideoFile, error) {
|
|||||||
|
|
||||||
if result.Title == "" {
|
if result.Title == "" {
|
||||||
// default title to filename
|
// default title to filename
|
||||||
result.SetTitleFromPath()
|
result.SetTitleFromPath(stripExt)
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Comment = probeJSON.Format.Tags.Comment
|
result.Comment = probeJSON.Format.Tags.Comment
|
||||||
@@ -339,6 +339,11 @@ func (v *VideoFile) getStreamIndex(fileType string, probeJSON FFProbeJSON) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VideoFile) SetTitleFromPath() {
|
func (v *VideoFile) SetTitleFromPath(stripExtension bool) {
|
||||||
v.Title = filepath.Base(v.Path)
|
v.Title = filepath.Base(v.Path)
|
||||||
|
if stripExtension {
|
||||||
|
ext := filepath.Ext(v.Title)
|
||||||
|
v.Title = strings.TrimSuffix(v.Title, ext)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ func (s *singleton) Scan(input models.ScanMetadataInput) {
|
|||||||
instance.Paths.Generated.EnsureTmpDir()
|
instance.Paths.Generated.EnsureTmpDir()
|
||||||
|
|
||||||
wg.Add()
|
wg.Add()
|
||||||
task := ScanTask{FilePath: path, UseFileMetadata: input.UseFileMetadata, fileNamingAlgorithm: fileNamingAlgo, calculateMD5: calculateMD5, GeneratePreview: input.ScanGeneratePreviews, GenerateImagePreview: input.ScanGenerateImagePreviews, GenerateSprite: input.ScanGenerateSprites}
|
task := ScanTask{FilePath: path, UseFileMetadata: input.UseFileMetadata, StripFileExtension: input.StripFileExtension, fileNamingAlgorithm: fileNamingAlgo, calculateMD5: calculateMD5, GeneratePreview: input.ScanGeneratePreviews, GenerateImagePreview: input.ScanGenerateImagePreviews, GenerateSprite: input.ScanGenerateSprites}
|
||||||
go task.Start(&wg)
|
go task.Start(&wg)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ func GetSceneFileContainer(scene *models.Scene) (ffmpeg.Container, error) {
|
|||||||
container = ffmpeg.Container(scene.Format.String)
|
container = ffmpeg.Container(scene.Format.String)
|
||||||
} else { // container isn't in the DB
|
} else { // container isn't in the DB
|
||||||
// shouldn't happen, fallback to ffprobe
|
// shouldn't happen, fallback to ffprobe
|
||||||
tmpVideoFile, err := ffmpeg.NewVideoFile(GetInstance().FFProbePath, scene.Path)
|
tmpVideoFile, err := ffmpeg.NewVideoFile(GetInstance().FFProbePath, scene.Path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ffmpeg.Container(""), fmt.Errorf("error reading video file: %s", err.Error())
|
return ffmpeg.Container(""), fmt.Errorf("error reading video file: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func (t *GenerateMarkersTask) Start(wg *sizedwaitgroup.SizedWaitGroup) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path)
|
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("error reading video file: %s", err.Error())
|
logger.Errorf("error reading video file: %s", err.Error())
|
||||||
return
|
return
|
||||||
@@ -51,7 +51,7 @@ func (t *GenerateMarkersTask) generateSceneMarkers() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path)
|
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("error reading video file: %s", err.Error())
|
logger.Errorf("error reading video file: %s", err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func (t *GeneratePreviewTask) Start(wg *sizedwaitgroup.SizedWaitGroup) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path)
|
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("error reading video file: %s", err.Error())
|
logger.Errorf("error reading video file: %s", err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func (t *GenerateScreenshotTask) Start(wg *sync.WaitGroup) {
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
scenePath := t.Scene.Path
|
scenePath := t.Scene.Path
|
||||||
probeResult, err := ffmpeg.NewVideoFile(instance.FFProbePath, scenePath)
|
probeResult, err := ffmpeg.NewVideoFile(instance.FFProbePath, scenePath, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err.Error())
|
logger.Error(err.Error())
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func (t *GenerateSpriteTask) Start(wg *sizedwaitgroup.SizedWaitGroup) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path)
|
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("error reading video file: %s", err.Error())
|
logger.Errorf("error reading video file: %s", err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
type ScanTask struct {
|
type ScanTask struct {
|
||||||
FilePath string
|
FilePath string
|
||||||
UseFileMetadata bool
|
UseFileMetadata bool
|
||||||
|
StripFileExtension bool
|
||||||
calculateMD5 bool
|
calculateMD5 bool
|
||||||
fileNamingAlgorithm models.HashAlgorithm
|
fileNamingAlgorithm models.HashAlgorithm
|
||||||
GenerateSprite bool
|
GenerateSprite bool
|
||||||
@@ -374,7 +375,7 @@ func (t *ScanTask) scanScene() *models.Scene {
|
|||||||
|
|
||||||
// check for container
|
// check for container
|
||||||
if !scene.Format.Valid {
|
if !scene.Format.Valid {
|
||||||
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.FilePath)
|
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.FilePath, t.StripFileExtension)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err.Error())
|
logger.Error(err.Error())
|
||||||
return nil
|
return nil
|
||||||
@@ -455,7 +456,7 @@ func (t *ScanTask) scanScene() *models.Scene {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.FilePath)
|
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.FilePath, t.StripFileExtension)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err.Error())
|
logger.Error(err.Error())
|
||||||
return nil
|
return nil
|
||||||
@@ -464,7 +465,7 @@ func (t *ScanTask) scanScene() *models.Scene {
|
|||||||
|
|
||||||
// Override title to be filename if UseFileMetadata is false
|
// Override title to be filename if UseFileMetadata is false
|
||||||
if !t.UseFileMetadata {
|
if !t.UseFileMetadata {
|
||||||
videoFile.SetTitleFromPath()
|
videoFile.SetTitleFromPath(t.StripFileExtension)
|
||||||
}
|
}
|
||||||
|
|
||||||
var checksum string
|
var checksum string
|
||||||
@@ -588,7 +589,7 @@ func (t *ScanTask) rescanScene(scene *models.Scene, fileModTime time.Time) (*mod
|
|||||||
}
|
}
|
||||||
|
|
||||||
// regenerate the file details as well
|
// regenerate the file details as well
|
||||||
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.FilePath)
|
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.FilePath, t.StripFileExtension)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -648,7 +649,7 @@ func (t *ScanTask) makeScreenshots(probeResult *ffmpeg.VideoFile, checksum strin
|
|||||||
|
|
||||||
if probeResult == nil {
|
if probeResult == nil {
|
||||||
var err error
|
var err error
|
||||||
probeResult, err = ffmpeg.NewVideoFile(instance.FFProbePath, t.FilePath)
|
probeResult, err = ffmpeg.NewVideoFile(instance.FFProbePath, t.FilePath, t.StripFileExtension)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err.Error())
|
logger.Error(err.Error())
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func (t *GenerateTranscodeTask) Start(wg *sizedwaitgroup.SizedWaitGroup) {
|
|||||||
container = ffmpeg.Container(t.Scene.Format.String)
|
container = ffmpeg.Container(t.Scene.Format.String)
|
||||||
} else { // container isn't in the DB
|
} else { // container isn't in the DB
|
||||||
// shouldn't happen unless user hasn't scanned after updating to PR#384+ version
|
// shouldn't happen unless user hasn't scanned after updating to PR#384+ version
|
||||||
tmpVideoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path)
|
tmpVideoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("[transcode] error reading video file: %s", err.Error())
|
logger.Errorf("[transcode] error reading video file: %s", err.Error())
|
||||||
return
|
return
|
||||||
@@ -49,7 +49,7 @@ func (t *GenerateTranscodeTask) Start(wg *sizedwaitgroup.SizedWaitGroup) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path)
|
videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("[transcode] error reading video file: %s", err.Error())
|
logger.Errorf("[transcode] error reading video file: %s", err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
* Allow configuration of visible navbar items.
|
* Allow configuration of visible navbar items.
|
||||||
|
|
||||||
### 🎨 Improvements
|
### 🎨 Improvements
|
||||||
|
* Add option to strip file extension from scene title when populating from scanning task.
|
||||||
* Pagination support and general improvements for image lightbox.
|
* Pagination support and general improvements for image lightbox.
|
||||||
* Add mouse click support for CDP scrapers.
|
* Add mouse click support for CDP scrapers.
|
||||||
* Add gallery tabs to performer and studio pages.
|
* Add gallery tabs to performer and studio pages.
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export const SettingsTasksPanel: React.FC = () => {
|
|||||||
const [isImportDialogOpen, setIsImportDialogOpen] = useState<boolean>(false);
|
const [isImportDialogOpen, setIsImportDialogOpen] = useState<boolean>(false);
|
||||||
const [isScanDialogOpen, setIsScanDialogOpen] = useState<boolean>(false);
|
const [isScanDialogOpen, setIsScanDialogOpen] = useState<boolean>(false);
|
||||||
const [useFileMetadata, setUseFileMetadata] = useState<boolean>(false);
|
const [useFileMetadata, setUseFileMetadata] = useState<boolean>(false);
|
||||||
|
const [stripFileExtension, setStripFileExtension] = useState<boolean>(false);
|
||||||
const [scanGeneratePreviews, setScanGeneratePreviews] = useState<boolean>(
|
const [scanGeneratePreviews, setScanGeneratePreviews] = useState<boolean>(
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@@ -178,6 +179,7 @@ export const SettingsTasksPanel: React.FC = () => {
|
|||||||
await mutateMetadataScan({
|
await mutateMetadataScan({
|
||||||
paths,
|
paths,
|
||||||
useFileMetadata,
|
useFileMetadata,
|
||||||
|
stripFileExtension,
|
||||||
scanGeneratePreviews,
|
scanGeneratePreviews,
|
||||||
scanGenerateImagePreviews,
|
scanGenerateImagePreviews,
|
||||||
scanGenerateSprites,
|
scanGenerateSprites,
|
||||||
@@ -317,6 +319,12 @@ export const SettingsTasksPanel: React.FC = () => {
|
|||||||
label="Set name, date, details from metadata (if present)"
|
label="Set name, date, details from metadata (if present)"
|
||||||
onChange={() => setUseFileMetadata(!useFileMetadata)}
|
onChange={() => setUseFileMetadata(!useFileMetadata)}
|
||||||
/>
|
/>
|
||||||
|
<Form.Check
|
||||||
|
id="strip-file-extension"
|
||||||
|
checked={stripFileExtension}
|
||||||
|
label="Don't include file extension as part of the title"
|
||||||
|
onChange={() => setStripFileExtension(!stripFileExtension)}
|
||||||
|
/>
|
||||||
<Form.Check
|
<Form.Check
|
||||||
id="scan-generate-previews"
|
id="scan-generate-previews"
|
||||||
checked={scanGeneratePreviews}
|
checked={scanGeneratePreviews}
|
||||||
|
|||||||
Reference in New Issue
Block a user