diff --git a/src/common/QueryParams.ts b/src/common/QueryParams.ts
index 80a9413c..9ad32c4d 100644
--- a/src/common/QueryParams.ts
+++ b/src/common/QueryParams.ts
@@ -14,6 +14,7 @@ export const QueryParams = {
query: 'qs',
},
photo: 'p',
+ playback: 'play',
sharingKey_query: 'sk',
sharingKey_params: 'sharingKey',
directory: 'directory',
diff --git a/src/frontend/app/model/query.service.ts b/src/frontend/app/model/query.service.ts
index 9a71f1e8..8035723b 100644
--- a/src/frontend/app/model/query.service.ts
+++ b/src/frontend/app/model/query.service.ts
@@ -10,32 +10,35 @@ import {ContentLoaderService} from '../ui/gallery/contentLoader.service';
@Injectable()
export class QueryService {
constructor(
- private shareService: ShareService,
- private galleryService: ContentLoaderService
+ private shareService: ShareService,
+ private galleryService: ContentLoaderService
) {
}
getMediaStringId(media: MediaDTO): string {
if (this.galleryService.isSearchResult()) {
return Utils.concatUrls(
- media.directory.path,
- media.directory.name,
- media.name
+ media.directory.path,
+ media.directory.name,
+ media.name
);
} else {
return media.name;
}
}
- getParams(media?: MediaDTO): { [key: string]: string } {
+ getParams(lightbox?: { media?: MediaDTO, playing?: boolean }): { [key: string]: string } {
const query: { [key: string]: string } = {};
- if (media) {
- query[QueryParams.gallery.photo] = this.getMediaStringId(media);
+ if (lightbox?.media) {
+ query[QueryParams.gallery.photo] = this.getMediaStringId(lightbox?.media);
+ }
+ if (lightbox?.playing) {
+ query[QueryParams.gallery.playback] = 'true';
}
if (Config.Sharing.enabled === true) {
if (this.shareService.isSharing()) {
query[QueryParams.gallery.sharingKey_query] =
- this.shareService.getSharingKey();
+ this.shareService.getSharingKey();
}
}
return query;
@@ -48,14 +51,14 @@ export class QueryService {
if (Config.Sharing.enabled === true) {
if (this.shareService.isSharing()) {
params[QueryParams.gallery.sharingKey_query] =
- this.shareService.getSharingKey();
+ this.shareService.getSharingKey();
}
}
if (
- directory &&
- directory.lastModified &&
- directory.lastScanned &&
- !directory.isPartial
+ directory &&
+ directory.lastModified &&
+ directory.lastScanned &&
+ !directory.isPartial
) {
params[QueryParams.gallery.knownLastModified] = directory.lastModified;
params[QueryParams.gallery.knownLastScanned] = directory.lastScanned;
diff --git a/src/frontend/app/ui/gallery/grid/grid.gallery.component.ts b/src/frontend/app/ui/gallery/grid/grid.gallery.component.ts
index fa93b692..972d3db5 100644
--- a/src/frontend/app/ui/gallery/grid/grid.gallery.component.ts
+++ b/src/frontend/app/ui/gallery/grid/grid.gallery.component.ts
@@ -192,7 +192,7 @@ export class GalleryGridComponent
photoClicked(media: MediaDTO): void {
this.router.navigate([], {
- queryParams: this.queryService.getParams(media),
+ queryParams: this.queryService.getParams({media}),
});
}
diff --git a/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.html b/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.html
index b93f119f..2f45201d 100644
--- a/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.html
+++ b/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.html
@@ -180,7 +180,7 @@
(click)="nextMediaManuallyTriggered()">
@@ -211,17 +211,17 @@
*ngIf="zoom == 1 && activePhoto && activePhoto.gridMedia.isPhoto()">
diff --git a/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.ts b/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.ts
index 171b48c3..1fe5309e 100644
--- a/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.ts
+++ b/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.ts
@@ -16,11 +16,6 @@ import {FileSizePipe} from '../../../../pipes/FileSizePipe';
import {DatePipe} from '@angular/common';
import {LightBoxTitleTexts} from '../../../../../../common/config/public/ClientConfig';
-export enum PlayBackStates {
- Paused = 1,
- Play = 2,
-}
-
@Component({
selector: 'app-lightbox-controls',
@@ -39,17 +34,17 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
@Output() toggleFullScreen = new EventEmitter();
@Output() nextPhoto = new EventEmitter();
@Output() previousPhoto = new EventEmitter();
+ @Output() togglePlayback = new EventEmitter();
@Input() navigation = {hasPrev: true, hasNext: true};
@Input() activePhoto: GalleryPhotoComponent;
@Input() mediaElement: GalleryLightboxMediaComponent;
@Input() photoFrameDim = {width: 1, height: 1, aspect: 1};
+ @Input() slideShowRunning: boolean;
public readonly facesEnabled = Config.Faces.enabled;
public zoom = 1;
- public playBackState: PlayBackStates = PlayBackStates.Paused;
- public PlayBackStates = PlayBackStates;
public playBackDurations = [1, 2, 5, 10, 15, 20, 30, 60];
public selectedSlideshowSpeed: number = null;
public controllersDimmed = false;
@@ -94,7 +89,7 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
if (this.zoom === zoom) {
return;
}
- this.pause();
+ this.stopSlideShow();
this.drag.x = (this.drag.x / this.zoom) * zoom;
this.drag.y = (this.drag.y / this.zoom) * zoom;
this.prevDrag.x = this.drag.x;
@@ -117,15 +112,19 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
}
ngOnDestroy(): void {
- this.pause();
+ this.stopSlideShow();
if (this.visibilityTimer != null) {
clearTimeout(this.visibilityTimer);
+ this.visibilityTimer = null;
}
}
ngOnChanges(): void {
this.updateFaceContainerDim();
+ if (this.slideShowRunning) {
+ this.runSlideShow();
+ }
}
pan($event: { deltaY: number; deltaX: number; isFinal: boolean }): void {
@@ -312,14 +311,19 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
this.ctx.stroke();
}
- resetSlideshowTimer(): void {
- if (this.playBackState == PlayBackStates.Play) {
- this.play();
+ private resetSlideshowTimer(): void {
+ if (this.slideShowRunning === true) {
+ this.stopSlideShow();
+ this.runSlideShow();
}
}
- public play(): void {
- this.pause();
+ public runSlideShow(): void {
+ //timer already running, do not reset it.
+ if (this.timerSub) {
+ return;
+ }
+ this.stopSlideShow();
this.drawSliderProgress(0);
this.timerSub = interval(100)
.pipe(filter((t) => {
@@ -328,7 +332,6 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
}))
.pipe(skip(1)) // do not skip to next photo right away
.subscribe(this.showNextMedia);
- this.playBackState = PlayBackStates.Play;
}
public slideshowSpeedChanged() {
@@ -340,12 +343,20 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
this.showControls();
}
- public pause(): void {
+ public stopSlideShow(): void {
if (this.timerSub != null) {
this.timerSub.unsubscribe();
+ this.timerSub = null;
}
this.ctx = null;
- this.playBackState = PlayBackStates.Paused;
+ }
+
+ playClicked() {
+ this.togglePlayback.emit(true);
+ }
+
+ pauseClicked() {
+ this.togglePlayback.emit(false);
}
resetZoom(): void {
@@ -520,6 +531,7 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
get BottomLeftTitle(): string {
return this.getText(Config.Gallery.Lightbox.Titles.bottomLeftTitle);
}
+
get BottomLeftSubtitle(): string {
return this.getText(Config.Gallery.Lightbox.Titles.bottomLeftSubtitle);
}
diff --git a/src/frontend/app/ui/gallery/lightbox/lightbox.gallery.component.html b/src/frontend/app/ui/gallery/lightbox/lightbox.gallery.component.html
index 251cee36..fc0dadae 100644
--- a/src/frontend/app/ui/gallery/lightbox/lightbox.gallery.component.html
+++ b/src/frontend/app/ui/gallery/lightbox/lightbox.gallery.component.html
@@ -37,12 +37,14 @@
*ngIf="isOpen()"
#controls
[activePhoto]="activePhoto"
+ [slideShowRunning]="slideShowRunning"
(closed)="hide()"
[navigation]="navigation"
(nextPhoto)="nextImage()"
(previousPhoto)="prevImage()"
(toggleInfoPanel)="toggleInfoPanel()"
(toggleFullScreen)="toggleFullscreen()"
+ (togglePlayback)="togglePlayback($event)"
[photoFrameDim]="photoFrameDim"
[mediaElement]="mediaElement">
diff --git a/src/frontend/app/ui/gallery/lightbox/lightbox.gallery.component.ts b/src/frontend/app/ui/gallery/lightbox/lightbox.gallery.component.ts
index 261c1e9c..a3623b96 100644
--- a/src/frontend/app/ui/gallery/lightbox/lightbox.gallery.component.ts
+++ b/src/frontend/app/ui/gallery/lightbox/lightbox.gallery.component.ts
@@ -65,6 +65,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
photosChange: null,
route: null,
};
+ slideShowRunning: boolean;
constructor(
public fullScreenService: FullScreenService,
@@ -105,10 +106,18 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
this.updatePhotoFrameDim();
this.subscription.route = this.route.queryParams.subscribe(
(params: Params) => {
+ const validPhoto = params[QueryParams.gallery.photo] &&
+ params[QueryParams.gallery.photo] !== '';
+
+
+ if (params[QueryParams.gallery.playback]) {
+ this.runSlideShow();
+ } else {
+ this.stopSlideShow();
+ }
+
this.delayedMediaShow = null;
- if (
- params[QueryParams.gallery.photo] &&
- params[QueryParams.gallery.photo] !== ''
+ if (validPhoto
) {
this.delayedMediaShow = params[QueryParams.gallery.photo];
// photos are not yet available to show
@@ -120,13 +129,28 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
this.delayedMediaShow = null;
this.hideLightbox();
}
+
+
}
);
}
+ private runSlideShow() {
+ if (!this.activePhoto && this.gridPhotoQL?.length > 0) {
+ this.navigateToPhoto(0);
+ }
+ this.slideShowRunning = true;
+ this.controls?.runSlideShow();
+ }
+
+ private stopSlideShow() {
+ this.slideShowRunning = false;
+ this.controls?.stopSlideShow();
+ }
+
ngOnDestroy(): void {
if (this.controls) {
- this.controls.pause();
+ this.controls.stopSlideShow();
}
if (this.subscription.photosChange != null) {
this.subscription.photosChange.unsubscribe();
@@ -186,12 +210,18 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
if (this.delayedMediaShow) {
this.onNavigateTo(this.delayedMediaShow);
}
+ if (this.slideShowRunning) {
+ this.runSlideShow();
+ }
}
);
if (this.delayedMediaShow) {
this.onNavigateTo(this.delayedMediaShow);
}
+ if (this.slideShowRunning) {
+ this.runSlideShow();
+ }
}
@HostListener('window:resize', ['$event'])
@@ -213,7 +243,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
public prevImage(): void {
if (this.controls) {
- this.controls.pause();
+ this.controls.stopSlideShow();
}
if (this.activePhotoId > 0) {
this.navigateToPhoto(this.activePhotoId - 1);
@@ -410,14 +440,13 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
this.router
.navigate([], {
queryParams: this.queryService.getParams(
- this.gridPhotoQL.get(photoIndex).gridMedia.media
+ {media: this.gridPhotoQL.get(photoIndex).gridMedia.media, playing: this.slideShowRunning}
),
})
.then(() => {
this.piTitleService.setMediaTitle(this.gridPhotoQL.get(photoIndex).gridMedia);
})
.catch(console.error);
-
}
private showPhoto(photoIndex: number, resize = true): void {
@@ -434,7 +463,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
this.fullScreenService.exitFullScreen();
if (this.controls) {
- this.controls.pause();
+ this.controls.stopSlideShow();
}
this.animating = true;
@@ -531,5 +560,14 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
}
return null;
}
+
+ togglePlayback(value: boolean): void {
+ if (this.slideShowRunning === value) {
+ return;
+ }
+ this.slideShowRunning = value;
+ // resets query. This side effect is to assign playback = true to the url
+ this.navigateToPhoto(this.activePhotoId);
+ }
}