/
githubmirror
/
photoprism
Обзор
Документация
Войти
/
githubmirror
/
photoprism
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
internal/entity/query/file_selection.go
220 строк
7 KB
Michael Mayer
Logging: Report backend internals on the console-only system log
25 июл 2026, 10:02
25 июл 2026, 10:02
656dc31
Код
Авторство
О чём код?
package query import ( "errors" "fmt" "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/dsn" "github.com/photoprism/photoprism/pkg/fs" "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/entity/search" "github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/pkg/media" ) // MiB represents one mebibyte in bytes. const MiB = 1024 * 1024 // FileSelection represents a selection filter to include/exclude certain files. type FileSelection struct { MaxSize int Media []string OmitMedia []string Types []string OmitTypes []string Primary bool Originals bool Hidden bool Private bool Archived bool } // DownloadSelection selects files to download. func DownloadSelection(mediaRaw, mediaSidecar, originals bool) FileSelection { omitMedia := make([]string, 0, 2) if !mediaRaw { omitMedia = append(omitMedia, media.Raw.String()) } if !mediaSidecar { omitMedia = append(omitMedia, media.Sidecar.String()) } return FileSelection{ OmitMedia: omitMedia, Originals: originals, Private: true, Archived: true, Hidden: true, } } // AlbumDownloadSelection selects an album's files for a zip download. It keeps archived and hidden // pictures out of the archive (they are not part of the visible album) and defers private-picture // visibility to the session scope applied by SelectedFilesForSession; when allowPrivate is false // (an unidentified session, e.g. the instance-default token) private pictures are excluded outright. func AlbumDownloadSelection(mediaRaw, mediaSidecar, originals, allowPrivate bool) FileSelection { sel := DownloadSelection(mediaRaw, mediaSidecar, originals) sel.Archived = false sel.Hidden = false sel.Private = allowPrivate return sel } // ShareSelection selects files to share, for example for upload via WebDAV. func ShareSelection(originals bool) FileSelection { var omitMedia []string var omitTypes []string if !originals { omitMedia = []string{ media.Unknown.String(), media.Raw.String(), media.Sidecar.String(), } omitTypes = []string{ fs.ImagePng.String(), fs.ImageWebp.String(), fs.ImageTiff.String(), fs.ImageAvif.String(), fs.ImageHeic.String(), fs.ImageBmp.String(), fs.ImageGif.String(), } } return FileSelection{ Originals: originals, OmitMedia: omitMedia, OmitTypes: omitTypes, Hidden: false, Private: false, Archived: false, MaxSize: 1024 * MiB, } } // SelectedFiles finds files based on the given selection form, e.g. for downloading or sharing. func SelectedFiles(frm form.Selection, o FileSelection) (results entity.Files, err error) { return selectedFiles(frm, o, nil) } // SelectedFilesForSession works like SelectedFiles but limits the result to the session's shared // scope. Full library and admin sessions are not limited, so this adds no overhead for them. func SelectedFilesForSession(frm form.Selection, o FileSelection, sess *entity.Session) (results entity.Files, err error) { return selectedFiles(frm, o, sess) } // selectedFiles finds files based on the given selection form, optionally limited to the content // the session may access when sess is not nil. func selectedFiles(frm form.Selection, o FileSelection, sess *entity.Session) (results entity.Files, err error) { if frm.Empty() { return results, errors.New("no items selected") } // Resolve photos in smart albums. if photoIds, err := AlbumsPhotoUIDs(frm.Albums, false, o.Private); err != nil { log.Warnf("query: failed to resolve smart album members for selection (%s)", clean.Error(err)) } else if len(photoIds) > 0 { frm.Photos = append(frm.Photos, photoIds...) } var concat string switch DbDialect() { case dsn.DriverMySQL: concat = "CONCAT(a.path, '/%')" case dsn.DriverSQLite3: concat = "a.path || '/%'" default: return results, fmt.Errorf("unknown sql dialect: %s", DbDialect()) } // Search condition. where := fmt.Sprintf(`photos.photo_uid IN (?) OR photos.place_id IN (?) OR photos.photo_uid IN (SELECT photo_uid FROM files WHERE file_uid IN (?)) OR photos.photo_path IN ( SELECT a.path FROM folders a WHERE a.folder_uid IN (?) UNION SELECT b.path FROM folders a JOIN folders b ON b.path LIKE %s WHERE a.folder_uid IN (?)) OR photos.photo_uid IN (SELECT photo_uid FROM photos_albums WHERE hidden = 0 AND album_uid IN (?)) OR files.file_uid IN (SELECT file_uid FROM %s m WHERE m.subj_uid IN (?)) OR photos.id IN (SELECT pl.photo_id FROM photos_labels pl JOIN labels l ON pl.label_id = l.id AND pl.uncertainty < 100 AND l.deleted_at IS NULL WHERE l.label_uid IN (?)) OR photos.id IN (SELECT pl.photo_id FROM photos_labels pl JOIN categories c ON c.label_id = pl.label_id AND pl.uncertainty < 100 JOIN labels lc ON lc.id = c.category_id AND lc.deleted_at IS NULL WHERE lc.label_uid IN (?))`, concat, entity.Marker{}.TableName()) // Build search query. s := UnscopedDb().Table("files"). Select("files.*"). Joins("JOIN photos ON photos.id = files.photo_id"). Where("files.file_missing = 0 AND files.file_name <> '' AND files.file_hash <> ''"). Where(where, frm.Photos, frm.Places, frm.Files, frm.Files, frm.Files, frm.Albums, frm.Subjects, frm.Labels, frm.Labels). Group("files.id") // File size limit? if o.MaxSize > 0 { s = s.Where("files.file_size < ?", o.MaxSize) } // Specific media types only? if len(o.Media) > 0 { s = s.Where("files.media_type IN (?)", o.Media) } // Exclude media types? if len(o.OmitMedia) > 0 { s = s.Where("files.media_type NOT IN (?)", o.OmitMedia) } // Specific file types only? if len(o.Types) > 0 { s = s.Where("files.file_type IN (?)", o.Types) } // Exclude file types? if len(o.OmitTypes) > 0 { s = s.Where("files.file_type NOT IN (?)", o.OmitTypes) } // Previews files only? if o.Primary { s = s.Where("files.file_primary = 1") } // Files in originals only? if o.Originals { s = s.Where("files.file_root = '/'") } // Exclude private? if !o.Private { s = s.Where("photos.photo_private <> 1") } // Exclude hidden photos? if !o.Hidden { s = s.Where("photos.photo_quality > -1") } // Exclude archived photos? if !o.Archived { s = s.Where("photos.deleted_at IS NULL") } // Limit the selection to the session's shared scope (no-op for full-access sessions). The selected // photo UIDs are passed so pictures shared only through a filter-based smart album stay downloadable. if sess != nil { s = search.ScopeVisibleSelection(s, sess, frm.Photos) } // Find and return. if result := s.Scan(&results); result.Error != nil { return results, result.Error } return results, nil }