diff --git a/go.sum b/go.sum index 72b222b94..fb5948561 100644 --- a/go.sum +++ b/go.sum @@ -187,8 +187,6 @@ github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/feederbox826/gosx-notifier v0.2.1 h1:47FdsdfVQUOkAlHdp9qevviVaGnNT152uMcgY91MiGs= -github.com/feederbox826/gosx-notifier v0.2.1/go.mod h1:R6rqw7VuwuiCuvsr7EOONmWq++CRA5Ijmkmx75/C3Fs= github.com/feederbox826/gosx-notifier v0.2.2 h1:26NkaJZ8Wzptx82R46c9pkVAcFwGSU7kxWrOKmRWlC0= github.com/feederbox826/gosx-notifier v0.2.2/go.mod h1:R6rqw7VuwuiCuvsr7EOONmWq++CRA5Ijmkmx75/C3Fs= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= diff --git a/pkg/sqlite/audio_filter.go b/pkg/sqlite/audio_filter.go index 572fec44d..bfe8a6e75 100644 --- a/pkg/sqlite/audio_filter.go +++ b/pkg/sqlite/audio_filter.go @@ -62,8 +62,12 @@ func (qb *audioFilterHandler) criterionHandler() criterionHandler { stringCriterionHandler(audioFilter.Details, "audios.details"), criterionHandlerFunc(func(ctx context.Context, f *filterBuilder) { if audioFilter.Oshash != nil { - qb.addAudioFilesTable(f) - f.addLeftJoin(fingerprintTable, "fingerprints_oshash", "audios_files.file_id = fingerprints_oshash.file_id AND fingerprints_oshash.type = 'oshash'") + joinType := joinTypeInner + if audioFilter.Oshash.Modifier == models.CriterionModifierIsNull { + joinType = joinTypeLeft + } + qb.addAudioFilesTable(f, joinType) + f.addJoin(joinType, fingerprintTable, "fingerprints_oshash", "audios_files.file_id = fingerprints_oshash.file_id AND fingerprints_oshash.type = 'oshash'") } stringCriterionHandler(audioFilter.Oshash, "fingerprints_oshash.fingerprint")(ctx, f) @@ -71,8 +75,12 @@ func (qb *audioFilterHandler) criterionHandler() criterionHandler { criterionHandlerFunc(func(ctx context.Context, f *filterBuilder) { if audioFilter.Checksum != nil { - qb.addAudioFilesTable(f) - f.addLeftJoin(fingerprintTable, "fingerprints_md5", "audios_files.file_id = fingerprints_md5.file_id AND fingerprints_md5.type = 'md5'") + joinType := joinTypeInner + if audioFilter.Checksum.Modifier == models.CriterionModifierIsNull { + joinType = joinTypeLeft + } + qb.addAudioFilesTable(f, joinType) + f.addJoin(joinType, fingerprintTable, "fingerprints_md5", "audios_files.file_id = fingerprints_md5.file_id AND fingerprints_md5.type = 'md5'") } stringCriterionHandler(audioFilter.Checksum, "fingerprints_md5.fingerprint")(ctx, f) @@ -138,6 +146,12 @@ func (qb *audioFilterHandler) criterionHandler() criterionHandler { }, }, + &relatedFilterHandler{ + relatedIDCol: "audios.studio_id", + relatedRepo: studioRepository.repository, + relatedHandler: &studioFilterHandler{audioFilter.StudiosFilter}, + }, + &relatedFilterHandler{ relatedIDCol: "audio_tag.tag_id", relatedRepo: tagRepository.repository, @@ -164,8 +178,8 @@ func (qb *audioFilterHandler) criterionHandler() criterionHandler { isRelated: true, }, joinFn: func(f *filterBuilder) { - qb.addFilesTable(f) - qb.addFoldersTable(f) + qb.addFilesTable(f, joinTypeInner) + qb.addFoldersTable(f, joinTypeInner) }, // don't use a subquery; join directly directJoin: true, @@ -173,18 +187,18 @@ func (qb *audioFilterHandler) criterionHandler() criterionHandler { } } -func (qb *audioFilterHandler) addAudioFilesTable(f *filterBuilder) { - f.addLeftJoin(audiosFilesTable, "", "audios_files.audio_id = audios.id") +func (qb *audioFilterHandler) addAudioFilesTable(f *filterBuilder, joinType joinType) { + f.addJoin(joinType, audiosFilesTable, "", "audios_files.audio_id = audios.id") } -func (qb *audioFilterHandler) addFilesTable(f *filterBuilder) { - qb.addAudioFilesTable(f) - f.addLeftJoin(fileTable, "", "audios_files.file_id = files.id") +func (qb *audioFilterHandler) addFilesTable(f *filterBuilder, joinType joinType) { + qb.addAudioFilesTable(f, joinType) + f.addJoin(joinType, fileTable, "", "audios_files.file_id = files.id") } -func (qb *audioFilterHandler) addFoldersTable(f *filterBuilder) { - qb.addFilesTable(f) - f.addLeftJoin(folderTable, "", "files.parent_folder_id = folders.id") +func (qb *audioFilterHandler) addFoldersTable(f *filterBuilder, joinType joinType) { + qb.addFilesTable(f, joinType) + f.addJoin(joinType, folderTable, "", "files.parent_folder_id = folders.id") } func (qb *audioFilterHandler) playCountCriterionHandler(count *models.IntCriterionInput) criterionHandlerFunc { @@ -217,11 +231,15 @@ func (qb *audioFilterHandler) fileCountCriterionHandler(fileCount *models.IntCri return h.handler(fileCount) } -func (qb *audioFilterHandler) codecCriterionHandler(codec *models.StringCriterionInput, codecColumn string, addJoinFn func(f *filterBuilder)) criterionHandlerFunc { +func (qb *audioFilterHandler) codecCriterionHandler(codec *models.StringCriterionInput, codecColumn string, addJoinFn func(f *filterBuilder, joinType joinType)) criterionHandlerFunc { return func(ctx context.Context, f *filterBuilder) { if codec != nil { if addJoinFn != nil { - addJoinFn(f) + joinType := joinTypeInner + if codec.Modifier == models.CriterionModifierIsNull { + joinType = joinTypeLeft + } + addJoinFn(f, joinType) } stringCriterionHandler(codec, codecColumn)(ctx, f) @@ -234,20 +252,20 @@ func (qb *audioFilterHandler) isMissingCriterionHandler(isMissing *string) crite if isMissing != nil && *isMissing != "" { switch *isMissing { case "url": - audiosURLsTableMgr.join(f, "", "audios.id") + audiosURLsTableMgr.leftJoin(f, "", "audios.id") f.addWhere("audio_urls.url IS NULL") case "studio": f.addWhere("audios.studio_id IS NULL") case "movie", "group": - audioRepository.groups.join(f, "groups_join", "audios.id") + audioRepository.groups.leftJoin(f, "groups_join", "audios.id") f.addWhere("groups_join.audio_id IS NULL") case "performers": - audioRepository.performers.join(f, "performers_join", "audios.id") + audioRepository.performers.leftJoin(f, "performers_join", "audios.id") f.addWhere("performers_join.audio_id IS NULL") case "date": f.addWhere(`audios.date IS NULL OR audios.date IS ""`) case "tags": - audioRepository.tags.join(f, "tags_join", "audios.id") + audioRepository.tags.leftJoin(f, "tags_join", "audios.id") f.addWhere("tags_join.audio_id IS NULL") default: if err := validateIsMissing(*isMissing, []string{ @@ -268,8 +286,8 @@ func (qb *audioFilterHandler) urlsCriterionHandler(url *models.StringCriterionIn primaryFK: audioIDColumn, joinTable: audiosURLsTable, stringColumn: audioURLColumn, - addJoinTable: func(f *filterBuilder) { - audiosURLsTableMgr.join(f, "", "audios.id") + addJoinTable: func(f *filterBuilder, joinType joinType) { + audiosURLsTableMgr.join(f, joinType, "", "audios.id") }, } @@ -282,9 +300,9 @@ func (qb *audioFilterHandler) captionCriterionHandler(captions *models.StringCri primaryFK: audioIDColumn, joinTable: videoCaptionsTable, stringColumn: captionCodeColumn, - addJoinTable: func(f *filterBuilder) { - qb.addAudioFilesTable(f) - f.addLeftJoin(videoCaptionsTable, "", "video_captions.file_id = audios_files.file_id") + addJoinTable: func(f *filterBuilder, joinType joinType) { + qb.addAudioFilesTable(f, joinTypeLeft) + f.addJoin(joinType, videoCaptionsTable, "", "video_captions.file_id = audios_files.file_id") }, excludeHandler: func(f *filterBuilder, criterion *models.StringCriterionInput) { excludeClause := `audios.id NOT IN ( @@ -334,8 +352,8 @@ func (qb *audioFilterHandler) performersCriterionHandler(performers *models.Mult primaryFK: audioIDColumn, foreignFK: performerIDColumn, - addJoinTable: func(f *filterBuilder) { - audioRepository.performers.join(f, "performers_join", "audios.id") + addJoinTable: func(f *filterBuilder, joinType joinType) { + audioRepository.performers.join(f, joinType, "performers_join", "audios.id") }, } diff --git a/pkg/sqlite/file_filter.go b/pkg/sqlite/file_filter.go index 018da49c1..7a37ad149 100644 --- a/pkg/sqlite/file_filter.go +++ b/pkg/sqlite/file_filter.go @@ -377,20 +377,25 @@ func (qb *audioFileFilterHandler) criterionHandler() criterionHandler { intCriterionHandler(audioFileFilter.SampleRate, "audio_files.sample_rate", qb.addAudioFilesTable), intCriterionHandler(audioFileFilter.Bitrate, "audio_files.bit_rate", qb.addAudioFilesTable), qb.codecCriterionHandler(audioFileFilter.AudioCodec, "audio_files.audio_codec", qb.addAudioFilesTable), + qb.codecCriterionHandler(audioFileFilter.AudioCodec, "audio_files.audio_codec", qb.addAudioFilesTable), qb.captionCriterionHandler(audioFileFilter.Captions), } } -func (qb *audioFileFilterHandler) addAudioFilesTable(f *filterBuilder) { - f.addLeftJoin(audioFileTable, "", "audio_files.file_id = files.id") +func (qb *audioFileFilterHandler) addAudioFilesTable(f *filterBuilder, joinType joinType) { + f.addJoin(joinType, audioFileTable, "", "audio_files.file_id = files.id") } -func (qb *audioFileFilterHandler) codecCriterionHandler(codec *models.StringCriterionInput, codecColumn string, addJoinFn func(f *filterBuilder)) criterionHandlerFunc { +func (qb *audioFileFilterHandler) codecCriterionHandler(codec *models.StringCriterionInput, codecColumn string, addJoinFn func(f *filterBuilder, joinType joinType)) criterionHandlerFunc { return func(ctx context.Context, f *filterBuilder) { if codec != nil { if addJoinFn != nil { - addJoinFn(f) + joinType := joinTypeInner + if codec.Modifier == models.CriterionModifierIsNull || codec.Modifier == models.CriterionModifierNotMatchesRegex { + joinType = joinTypeLeft + } + addJoinFn(f, joinType) } stringCriterionHandler(codec, codecColumn)(ctx, f) @@ -404,14 +409,14 @@ func (qb *audioFileFilterHandler) captionCriterionHandler(captions *models.Strin primaryFK: sceneIDColumn, joinTable: videoCaptionsTable, stringColumn: captionCodeColumn, - addJoinTable: func(f *filterBuilder) { - f.addLeftJoin(videoCaptionsTable, "", "audio_captions.file_id = files.id") + addJoinTable: func(f *filterBuilder, joinType joinType) { + f.addJoin(joinType, videoCaptionsTable, "", "video_captions.file_id = files.id") }, excludeHandler: func(f *filterBuilder, criterion *models.StringCriterionInput) { excludeClause := `files.id NOT IN ( SELECT files.id from files - INNER JOIN audio_captions on audio_captions.file_id = files.id - WHERE audio_captions.language_code LIKE ? + INNER JOIN video_captions on video_captions.file_id = files.id + WHERE video_captions.language_code LIKE ? )` f.addWhere(excludeClause, criterion.Value)