55 lines
1.5 KiB
TypeScript
55 lines
1.5 KiB
TypeScript
import { t } from '@/i18n'
|
|
import type { MediaItem } from '@/composables/useMedia'
|
|
import { formatDimensions, formatSize } from '@/lib/mediaFormat'
|
|
|
|
export interface DuplicateGroup {
|
|
hash: string
|
|
copies: MediaItem[]
|
|
suggested: MediaItem
|
|
header: string
|
|
}
|
|
|
|
function byAge(first: MediaItem, second: MediaItem) {
|
|
return Date.parse(first.created_at) - Date.parse(second.created_at) || first.id - second.id
|
|
}
|
|
|
|
function byPreference(first: MediaItem, second: MediaItem) {
|
|
return second.used_in - first.used_in || byAge(first, second)
|
|
}
|
|
|
|
export function groupHeader(copies: MediaItem[]) {
|
|
const [first] = copies
|
|
return t('{0} identical images · {1} each · {2}', [
|
|
copies.length,
|
|
formatSize(first!.size),
|
|
formatDimensions(first!.width, first!.height),
|
|
])
|
|
}
|
|
|
|
export function groupDuplicates(items: MediaItem[]): DuplicateGroup[] {
|
|
const byHash = new Map<string, MediaItem[]>()
|
|
for (const item of items) {
|
|
byHash.set(item.sha256, [...(byHash.get(item.sha256) ?? []), item])
|
|
}
|
|
return [...byHash.entries()]
|
|
.filter(([, copies]) => copies.length > 1)
|
|
.map(([hash, copies]) => {
|
|
const sorted = [...copies].sort(byPreference)
|
|
return {
|
|
hash,
|
|
copies: sorted,
|
|
suggested: sorted[0]!,
|
|
get header() {
|
|
return groupHeader(sorted)
|
|
},
|
|
}
|
|
})
|
|
.sort((first, second) => byAge(first.suggested, second.suggested))
|
|
}
|
|
|
|
export function mergeArticleCount(copies: MediaItem[], keepId: number) {
|
|
return copies
|
|
.filter((copy) => copy.id !== keepId)
|
|
.reduce((total, copy) => total + copy.used_in, 0)
|
|
}
|