Add video support with unified media display.
All checks were successful
Docker / build (release) Successful in 9s

This commit is contained in:
krek0 2026-05-16 15:13:14 +02:00
parent a634cc88bd
commit f4052a3d99
16 changed files with 700 additions and 224 deletions

View file

@ -6,7 +6,11 @@
// Init gallery
lightGallery(document.getElementById('lightgallery'), {
plugins: [lgAdmin, lgHash, lgThumbnail, lgZoom],
selector: 'a.photo-item',
plugins: [lgAdmin, lgHash, lgThumbnail, lgZoom, lgVideo],
autoplayFirstVideo: true,
autoplayVideoOnSlide: true,
gotoNextSlideOnVideoEnd: false,
download: true,
customSlideName: true,
licenseKey: '94ED9732-30284A12-B88B0137-4FF9CEE6',
@ -66,7 +70,7 @@ lgContainer.addEventListener('lgAfterOpen', () => {
lgContainer.addEventListener('lgAfterOpen', () => {
// On cible le conteneur de la galerie qui vient de s'afficher
const lgOuter = document.querySelector('.lg-outer');
lgOuter.addEventListener('contextmenu', (e) => {
e.preventDefault();
}, false);

View file

@ -0,0 +1,144 @@
.lg-outer .lg-video-cont {
text-align: center;
display: inline-block;
vertical-align: middle;
position: relative;
}
.lg-outer .lg-video-cont .lg-object {
width: 100% !important;
height: 100% !important;
}
.lg-outer .lg-has-iframe .lg-video-cont {
-webkit-overflow-scrolling: touch;
overflow: auto;
}
.lg-outer .lg-video-object {
position: absolute;
left: 0;
right: 0;
width: 100%;
height: 100%;
top: 0;
bottom: 0;
z-index: 3;
}
.lg-outer .lg-video-poster {
z-index: 1;
}
.lg-outer .lg-has-video .lg-video-object {
opacity: 0;
will-change: opacity;
-webkit-transition: opacity 0.3s ease-in;
-o-transition: opacity 0.3s ease-in;
transition: opacity 0.3s ease-in;
}
.lg-outer .lg-has-video.lg-video-loaded .lg-video-poster,
.lg-outer .lg-has-video.lg-video-loaded .lg-video-play-button {
opacity: 0 !important;
}
.lg-outer .lg-has-video.lg-video-loaded .lg-video-object {
opacity: 1;
}
@keyframes lg-play-stroke {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -35px;
}
100% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -124px;
}
}
@keyframes lg-play-rotate {
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
.lg-video-play-button {
width: 18%;
max-width: 140px;
position: absolute;
top: 50%;
left: 50%;
z-index: 2;
cursor: pointer;
transform: translate(-50%, -50%) scale(1);
will-change: opacity, transform;
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0.17, 0.88, 0.32, 1.28), opacity 0.1s;
-moz-transition: -moz-transform 0.25s cubic-bezier(0.17, 0.88, 0.32, 1.28), opacity 0.1s;
-o-transition: -o-transform 0.25s cubic-bezier(0.17, 0.88, 0.32, 1.28), opacity 0.1s;
transition: transform 0.25s cubic-bezier(0.17, 0.88, 0.32, 1.28), opacity 0.1s;
}
.lg-video-play-button:hover .lg-video-play-icon-bg,
.lg-video-play-button:hover .lg-video-play-icon {
opacity: 1;
}
.lg-video-play-icon-bg {
fill: none;
stroke-width: 3%;
stroke: #fcfcfc;
opacity: 0.6;
will-change: opacity;
-webkit-transition: opacity 0.12s ease-in;
-o-transition: opacity 0.12s ease-in;
transition: opacity 0.12s ease-in;
}
.lg-video-play-icon-circle {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
fill: none;
stroke-width: 3%;
stroke: rgba(30, 30, 30, 0.9);
stroke-opacity: 1;
stroke-linecap: round;
stroke-dasharray: 200;
stroke-dashoffset: 200;
}
.lg-video-play-icon {
position: absolute;
width: 25%;
max-width: 120px;
left: 50%;
top: 50%;
transform: translate3d(-50%, -50%, 0);
opacity: 0.6;
will-change: opacity;
-webkit-transition: opacity 0.12s ease-in;
-o-transition: opacity 0.12s ease-in;
transition: opacity 0.12s ease-in;
}
.lg-video-play-icon .lg-video-play-icon-inner {
fill: #fcfcfc;
}
.lg-video-loading .lg-video-play-icon-circle {
animation: lg-play-rotate 2s linear 0.25s infinite, lg-play-stroke 1.5s ease-in-out 0.25s infinite;
}
.lg-video-loaded .lg-video-play-button {
opacity: 0;
transform: translate(-50%, -50%) scale(0.7);
}

View file

@ -15,6 +15,9 @@ class lgAdmin {
this.canResolveCensorship = document.querySelector('[name=can_resolve_censorship]').value === "true";
this.csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
this.photoId = 0;
this.deleteUrl = '';
this.adminUrl = '';
this.isVideo = false;
return this;
}
@ -49,14 +52,19 @@ class lgAdmin {
// Event called when showing a new slide
onAfterSlide(event) {
this.photoId = this.core.galleryItems[event.detail.index].slideName;
document.getElementById("lg-admin").href = `/admin/photologue/photo/${this.photoId}/change/`;
const el = document.querySelector(`[data-slide-name='${this.photoId}']`);
this.deleteUrl = el ? el.dataset.deleteUrl : '';
this.adminUrl = el ? el.dataset.adminUrl : '';
this.reportUrl = el ? el.dataset.reportUrl : '';
this.uncensorUrl = el ? el.dataset.uncensorUrl : '';
this.isVideo = el ? el.dataset.video !== undefined : false;
document.getElementById("lg-admin").href = this.adminUrl;
const ownerId = el ? el.dataset.ownerId : null;
const canDelete = this.isStaff || (ownerId && ownerId === this.userId);
document.getElementById("lg-delete").style.display = canDelete ? 'block' : 'none';
document.getElementById("lg-report").style.display = canDelete ? 'none' : 'block';
document.getElementById("lg-report").style.display = (!canDelete && this.reportUrl) ? 'block' : 'none';
const isCensored = el ? el.dataset.isPublic === 'false' : false;
document.getElementById("lg-restore").style.display = (this.canResolveCensorship && isCensored) ? 'block' : 'none';
document.getElementById("lg-restore").style.display = (this.canResolveCensorship && isCensored && this.uncensorUrl) ? 'block' : 'none';
}
// Event called when user clicks the restore (uncensor) button
@ -66,11 +74,12 @@ class lgAdmin {
const photoId = this.photoId;
let data = new FormData();
data.append('csrfmiddlewaretoken', this.csrfToken);
fetch(`/photo/${photoId}/uncensor/`, {
fetch(this.uncensorUrl, {
method: "POST",
body: data,
credentials: 'same-origin',
}).then(() => {
}).then(response => {
if (!response.ok) { console.error('Uncensor failed', response.status, this.uncensorUrl); return; }
const el = document.querySelector(`[data-slide-name='${photoId}']`);
if (el) {
el.dataset.isPublic = 'true';
@ -132,7 +141,7 @@ class lgAdmin {
const currentIndex = this.core.index;
let data = new FormData();
data.append('csrfmiddlewaretoken', this.csrfToken);
fetch(`/photo/${photoId}/delete/`, {
fetch(this.deleteUrl, {
method: "POST",
redirect: "manual", // do not load gallery again
body: data,
@ -150,7 +159,7 @@ class lgAdmin {
const currentIndex = this.core.index;
let data = new FormData();
data.append('csrfmiddlewaretoken', this.csrfToken);
fetch(`/photo/${photoId}/report/`, {
fetch(this.reportUrl, {
method: "POST",
redirect: "manual", // do not load gallery again
body: data,

File diff suppressed because one or more lines are too long