diff --git a/package-lock.json b/package-lock.json index 470de18b..608b4a8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "@ffprobe-installer/ffprobe": "2.1.2", "@iharbeck/ngx-virtual-scroller": "17.0.2", "@ngx-translate/core": "15.0.0", - "@tweenjs/tween.js": "^25.0.0", "an-qrcode": "1.0.7", "async": "3.2.6", "body-parser": "1.20.3", @@ -46,6 +45,7 @@ "@angular/platform-browser": "18.2.8", "@angular/platform-browser-dynamic": "18.2.8", "@angular/router": "18.2.8", + "@tweenjs/tween.js": "25.0.0", "@types/node": "22.7.8", "@typescript-eslint/eslint-plugin": "8.11.0", "@typescript-eslint/parser": "8.11.0", @@ -5711,6 +5711,7 @@ "version": "25.0.0", "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-25.0.0.tgz", "integrity": "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==", + "dev": true, "license": "MIT" }, "node_modules/@types/body-parser": { diff --git a/src/app/app.component.html b/src/app/app.component.html index 66d11b44..9b9e097f 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,3 +1,22 @@ - +
+ + + + Found {{ duplicates.length }} duplicates + +
+ + +
+

Duplicate videos:

+ +
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5bd4a44f..652c186c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,21 +1,31 @@ -import { Component } from '@angular/core'; -import { ElectronService } from './providers/electron.service'; +import { Component, OnInit } from '@angular/core'; +import { ElectronService } from './providers/electron.service'; +import { ImageElementService } from './services/image-element.service'; +import type { ImageElement } from '../../interfaces/final-object.interface'; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) -export class AppComponent { +export class AppComponent implements OnInit { + duplicates: ImageElement[] = []; constructor( - public electronService: ElectronService - ) { + public electronService: ElectronService, + private imageElementService: ImageElementService + ) {} - if (electronService.isElectron()) { + ngOnInit(): void { + if (this.electronService.isElectron()) { console.log('Mode electron'); } else { console.log('Mode web'); } } + /** Called when the user clicks β€œFind Duplicates” */ + checkDuplicates(): void { + this.duplicates = this.imageElementService.findDuplicatesByTagsOrName(); + console.log('Found duplicates:', this.duplicates); + } } diff --git a/src/app/components/home.component.ts b/src/app/components/home.component.ts index 7169ebce..9f7b50af 100644 --- a/src/app/components/home.component.ts +++ b/src/app/components/home.component.ts @@ -758,15 +758,27 @@ export class HomeComponent implements OnInit, AfterViewInit { this.vhaFileHistory = (settingsObject.vhaFileHistory || []); this.restoreSettingsFromBefore(settingsObject); this.setOrRestoreLanguage(settingsObject.appState.language, locale); + if (this.appState.currentZoomLevel !== 1) { + // restore zoom this.electronService.webFrame.setZoomFactor(this.appState.currentZoomLevel); + + // β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + // Immediately recompute gallery layout + // β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + setTimeout(() => { + this.computePreviewWidth(); + this.virtualScroller.refresh(); + }, 0); } + if (settingsObject.appState.currentVhaFile) { this.loadThisVhaFile(settingsObject.appState.currentVhaFile); } else { this.wizard.showWizard = true; this.flickerReduceOverlay = false; } + if (settingsObject.shortcuts) { this.shortcutService.initializeFromSaved(settingsObject.shortcuts); } diff --git a/src/app/components/top/top.component.html b/src/app/components/top/top.component.html index 390f4d53..23747abc 100644 --- a/src/app/components/top/top.component.html +++ b/src/app/components/top/top.component.html @@ -3,7 +3,11 @@ class="folder-container" [ngClass]="{ 'dark-mode-override': darkMode }" > - + - + + + + + diff --git a/src/app/components/top/top.component.ts b/src/app/components/top/top.component.ts index 2d9d076d..a56d3179 100644 --- a/src/app/components/top/top.component.ts +++ b/src/app/components/top/top.component.ts @@ -1,10 +1,13 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { ImageElementService } from '../../services/image-element.service'; @Component({ selector: 'app-top-component', templateUrl: './top.component.html', - styleUrls: ['./top.component.scss', - '../../fonts/icons.scss'] + styleUrls: [ + './top.component.scss', + '../../fonts/icons.scss' + ] }) export class TopComponent { @@ -37,12 +40,13 @@ export class TopComponent { public folderNameArray: string[]; public fileNameArray: string[]; + constructor(private imageElementService: ImageElementService) {} + public folderWordClicked(item: string): void { this.onFolderWordClicked.emit(item.trim()); } public fileWordClicked(item: string): void { - // Strip away any of: {}()[]., const regex = /{|}|\(|\)|\[|\]|\.|\,/g; item = item.replace(regex, ''); this.onFileWordClicked.emit(item.trim()); @@ -52,4 +56,8 @@ export class TopComponent { this.onOpenInExplorer.emit(true); } + /** Called by the toolbar button to trigger duplicate detection */ + public checkDuplicates(): void { + this.imageElementService.findDuplicatesByTagsOrName(); + } } diff --git a/src/app/components/views/filmstrip/filmstrip.component.html b/src/app/components/views/filmstrip/filmstrip.component.html index 265bbc54..a04f54ed 100644 --- a/src/app/components/views/filmstrip/filmstrip.component.html +++ b/src/app/components/views/filmstrip/filmstrip.component.html @@ -14,7 +14,8 @@ height: imgHeight + 'px', 'background-image': 'url(' + fullFilePath + ')', 'background-position-x': '-' + filmXoffset + 'px', - 'background-size': 'auto ' + imgHeight + 'px' + 'background-size': 'auto ' + imgHeight + 'px', + 'padding': isVertical(video) ? '2px 4px' : '10px' }" > {{ video.durationDisplay }} diff --git a/src/app/components/views/filmstrip/filmstrip.component.ts b/src/app/components/views/filmstrip/filmstrip.component.ts index 7c6a0f52..3764d1f8 100644 --- a/src/app/components/views/filmstrip/filmstrip.component.ts +++ b/src/app/components/views/filmstrip/filmstrip.component.ts @@ -29,7 +29,6 @@ export class FilmstripComponent implements OnInit { @Output() rightClick = new EventEmitter(); @Input() video: ImageElement; - @Input() compactView: boolean; @Input() darkMode: boolean; @Input() elHeight: number; @@ -55,6 +54,14 @@ export class FilmstripComponent implements OnInit { this.fullFilePath = this.filePathService.createFilePath(this.folderPath, this.hubName, 'filmstrips', this.video.hash); } + /** + * Return true if this video is portrait (vertical) orientation + */ + public isVertical(video: ImageElement): boolean { + console.log('Video dimensions:', video.width, video.height); + return video.width < video.height; + } + updateFilmXoffset($event) { if (this.hoverScrub) { const imgWidth = this.imgHeight * (16 / 9); // hardcoded aspect ratio diff --git a/src/app/services/image-element.service.ts b/src/app/services/image-element.service.ts index 8507cb79..86895503 100644 --- a/src/app/services/image-element.service.ts +++ b/src/app/services/image-element.service.ts @@ -125,5 +125,27 @@ constructor() { } splice(this.imageElements[position].tags.indexOf(emission.tag), 1); } } + /** + * Find duplicates based on exact fileName match or shared tags + */ + public findDuplicatesByTagsOrName(): ImageElement[] { + // 1) Finds all filenames that occur more than once + const allNames = this.imageElements.map(el => el.fileName); + const dupNames = new Set( + allNames.filter((name, i, arr) => arr.indexOf(name) !== i) + ); + + // 2) Finds all tags that occur more than once + const allTags = this.imageElements.flatMap(el => el.tags || []); + const dupTags = new Set( + allTags.filter((tag, i, arr) => arr.indexOf(tag) !== i) + ); + + // 3) Returns every element whose name or whose tags hit one of those duplicates + return this.imageElements.filter(el => + dupNames.has(el.fileName) || + (el.tags ?? []).some(tag => dupTags.has(tag)) + ); +} }