Files
RenewCMS/internal/infrastructure/useCases/ListMedia.go
T

223 lines
5.9 KiB
Go

package useCases
import (
"RenewCMS/internal/domain/article"
"RenewCMS/internal/domain/author"
"RenewCMS/internal/domain/image"
"RenewCMS/internal/domain/site"
"RenewCMS/internal/domain/user"
"strings"
)
type MediaItem struct {
Image image.Image
UploadedBy string
UsedIn int
Duplicate bool
VariantCount int
VariantSize int64
}
type MediaCounts struct {
All int
Unused int
Duplicates int
}
type MediaPage struct {
Items []MediaItem
Total int
Page int
Size int
Counts MediaCounts
}
type ListMediaUseCase struct {
imageRepository image.Repository
articleRepository article.Repository
userRepository user.Repository
variantFiles image.VariantFileRepository
sites site.Repository
authors author.Repository
}
func NewListMediaUseCase(imageRepository image.Repository, articleRepository article.Repository, userRepository user.Repository, variantFiles image.VariantFileRepository, sites site.Repository, authors author.Repository) *ListMediaUseCase {
return &ListMediaUseCase{imageRepository: imageRepository, articleRepository: articleRepository, userRepository: userRepository, variantFiles: variantFiles, sites: sites, authors: authors}
}
func (l *ListMediaUseCase) List(query image.ListQuery) (MediaPage, error) {
if err := query.Validate(); err != nil {
return MediaPage{}, err
}
usage, err := l.usageCounter()
if err != nil {
return MediaPage{}, err
}
duplicateHashes, err := l.duplicateHashes()
if err != nil {
return MediaPage{}, err
}
counts, err := l.counts(usage)
if err != nil {
return MediaPage{}, err
}
variantTotals, err := l.variantFiles.TotalsBySource()
if err != nil {
return MediaPage{}, err
}
images, err := l.imageRepository.List(image.SearchQuery{Search: strings.TrimSpace(query.Search), Hash: query.Hash})
if err != nil {
return MediaPage{}, err
}
sortImages(images, query.Sort, variantTotals)
matching := make([]image.Image, 0, len(images))
for _, img := range images {
if matchesVariants(query.Variants, variantTotals[image.StoredName(img.Path)].Files) && matchesFilter(query.Filter, usage(img), duplicateHashes[img.SHA256] && img.SHA256 != "") {
matching = append(matching, img)
}
}
offset := (query.Page - 1) * query.Size
pageStart := min(offset, len(matching))
pageEnd := min(pageStart+query.Size, len(matching))
names := newUploaderNames(l.userRepository)
items := []MediaItem{}
for _, img := range matching[pageStart:pageEnd] {
uploadedBy, err := names.nameOf(img.UploadedByID)
if err != nil {
return MediaPage{}, err
}
own := variantTotals[image.StoredName(img.Path)]
items = append(items, MediaItem{
Image: img,
UploadedBy: uploadedBy,
UsedIn: usage(img),
Duplicate: img.SHA256 != "" && duplicateHashes[img.SHA256],
VariantCount: own.Files,
VariantSize: own.Bytes,
})
}
return MediaPage{Items: items, Total: len(matching), Page: query.Page, Size: query.Size, Counts: counts}, nil
}
func (l *ListMediaUseCase) Describe(img image.Image) (MediaItem, error) {
articles, err := l.articleRepository.FindByBodyContaining(image.StoredName(img.Path))
if err != nil {
return MediaItem{}, err
}
siteUsage, err := imageUsageCount(l.sites, l.authors)
if err != nil {
return MediaItem{}, err
}
uploadedBy, err := newUploaderNames(l.userRepository).nameOf(img.UploadedByID)
if err != nil {
return MediaItem{}, err
}
duplicate := false
if img.SHA256 != "" {
sameContent, err := l.imageRepository.FindByHash(img.SHA256)
if err != nil {
return MediaItem{}, err
}
duplicate = len(sameContent) > 1
}
rows, err := l.variantFiles.ListBySource(image.StoredName(img.Path))
if err != nil {
return MediaItem{}, err
}
var variantSize int64
for _, row := range rows {
variantSize += row.Size
}
return MediaItem{Image: img, UploadedBy: uploadedBy, UsedIn: len(articles) + siteUsage[img.ID], Duplicate: duplicate, VariantCount: len(rows), VariantSize: variantSize}, nil
}
func (l *ListMediaUseCase) usageCounter() (func(image.Image) int, error) {
articles, err := l.articleRepository.FindByFilters(article.Filters{})
if err != nil {
return nil, err
}
siteUsage, err := imageUsageCount(l.sites, l.authors)
if err != nil {
return nil, err
}
return func(img image.Image) int {
storedName := image.StoredName(img.Path)
used := siteUsage[img.ID]
for _, a := range articles {
if strings.Contains(a.Body, storedName) {
used++
}
}
return used
}, nil
}
func (l *ListMediaUseCase) duplicateHashes() (map[string]bool, error) {
duplicates, err := l.imageRepository.ListDuplicates()
if err != nil {
return nil, err
}
hashes := make(map[string]bool, len(duplicates))
for _, img := range duplicates {
hashes[img.SHA256] = true
}
return hashes, nil
}
func (l *ListMediaUseCase) counts(usage func(image.Image) int) (MediaCounts, error) {
libraryCounts, err := l.imageRepository.Counts()
if err != nil {
return MediaCounts{}, err
}
everything, err := l.imageRepository.List(image.SearchQuery{})
if err != nil {
return MediaCounts{}, err
}
unused := 0
for _, img := range everything {
if usage(img) == 0 {
unused++
}
}
return MediaCounts{All: libraryCounts.All, Unused: unused, Duplicates: libraryCounts.DuplicateGroups}, nil
}
func sortImages(images []image.Image, order image.Sort, totals map[string]image.VariantFileTotals) {
if order == image.SortTotalSize {
image.SortImagesByTotalSize(images, totals)
return
}
image.SortImages(images, order)
}
func matchesVariants(filter image.VariantsFilter, variantFiles int) bool {
switch filter {
case image.VariantsWith:
return variantFiles > 0
case image.VariantsWithout:
return variantFiles == 0
default:
return true
}
}
func matchesFilter(filter image.Filter, usedIn int, duplicate bool) bool {
switch filter {
case image.FilterUnused:
return usedIn == 0
case image.FilterDuplicates:
return duplicate
default:
return true
}
}