Merge branch 'master' into stable24

old_stable24
Varun Patil 2022-11-11 07:02:29 -08:00
commit b2d8ce09a5
4 changed files with 225 additions and 200 deletions

View File

@ -264,14 +264,6 @@ class Index extends Command
return;
}
// skip 'IMDB' in path
if (false !== strpos($folderPath, 'IMDB')) {
$this->output->writeln('Skipping folder '.$folderPath.' because of IMDB');
$this->previousLineLength = 0;
return;
}
$nodes = $folder->getDirectoryListing();
foreach ($nodes as &$node) {

View File

@ -72,13 +72,21 @@ class TimelineQuery
} catch (\Throwable $e) {
}
$exif = [];
if (!$basic && !empty($row['exif'])) {
try {
$exif = json_decode($row['exif'], true);
} catch (\Throwable $e) {
}
}
return [
'fileid' => (int) $row['fileid'],
'dayid' => (int) $row['dayid'],
'datetaken' => $utcTs,
'w' => (int) $row['w'],
'h' => (int) $row['h'],
'exif' => $basic ? [] : json_decode($row['exif'], true),
'exif' => $exif,
];
}
}

View File

@ -8,74 +8,20 @@
'remove-gap': removeOuterGap,
}"
>
<NcAppNavigation v-if="showNavigation">
<NcAppNavigation v-if="showNavigation" ref="nav">
<template id="app-memories-navigation" #list>
<NcAppNavigationItem
:to="{ name: 'timeline' }"
:title="t('memories', 'Timeline')"
v-for="item in navItems"
:key="item.name"
:to="{ name: item.name }"
:title="item.title"
@click="linkClick"
exact
>
<ImageMultiple slot="icon" :size="20" />
</NcAppNavigationItem>
<NcAppNavigationItem
:to="{ name: 'folders' }"
:title="t('memories', 'Folders')"
>
<FolderIcon slot="icon" :size="20" />
</NcAppNavigationItem>
<NcAppNavigationItem
:to="{ name: 'favorites' }"
:title="t('memories', 'Favorites')"
>
<Star slot="icon" :size="20" />
</NcAppNavigationItem>
<NcAppNavigationItem
:to="{ name: 'videos' }"
:title="t('memories', 'Videos')"
>
<Video slot="icon" :size="20" />
</NcAppNavigationItem>
<NcAppNavigationItem
:to="{ name: 'albums' }"
:title="t('memories', 'Albums')"
v-if="showAlbums"
>
<AlbumIcon slot="icon" :size="20" />
</NcAppNavigationItem>
<NcAppNavigationItem
:to="{ name: 'people' }"
:title="t('memories', 'People')"
v-if="showPeople"
>
<PeopleIcon slot="icon" :size="20" />
</NcAppNavigationItem>
<NcAppNavigationItem
:to="{ name: 'archive' }"
:title="t('memories', 'Archive')"
>
<ArchiveIcon slot="icon" :size="20" />
</NcAppNavigationItem>
<NcAppNavigationItem
:to="{ name: 'thisday' }"
:title="t('memories', 'On this day')"
>
<CalendarIcon slot="icon" :size="20" />
</NcAppNavigationItem>
<NcAppNavigationItem
:to="{ name: 'tags' }"
v-if="config_tagsEnabled"
:title="t('memories', 'Tags')"
>
<TagsIcon slot="icon" :size="20" />
</NcAppNavigationItem>
<NcAppNavigationItem
:to="{ name: 'maps' }"
v-if="config_mapsEnabled"
:title="t('memories', 'Maps')"
>
<MapIcon slot="icon" :size="20" />
<component :is="item.icon" slot="icon" :size="20" />
</NcAppNavigationItem>
</template>
<template #footer>
<NcAppNavigationSettings :title="t('memories', 'Settings')">
<Settings />
@ -102,6 +48,7 @@ import {
} from "@nextcloud/vue";
import { generateUrl } from "@nextcloud/router";
import { getCurrentUser } from "@nextcloud/auth";
import { translate as t } from "@nextcloud/l10n";
import Timeline from "./components/Timeline.vue";
import Settings from "./components/Settings.vue";
@ -150,6 +97,65 @@ export default class App extends Mixins(GlobalMixin, UserConfig) {
private metadataComponent!: Metadata;
private readonly navItemsAll = [
{
name: "timeline",
icon: ImageMultiple,
title: t("memories", "Timeline"),
},
{
name: "folders",
icon: FolderIcon,
title: t("memories", "Folders"),
},
{
name: "favorites",
icon: Star,
title: t("memories", "Favorites"),
},
{
name: "videos",
icon: Video,
title: t("memories", "Videos"),
},
{
name: "albums",
icon: AlbumIcon,
title: t("memories", "Albums"),
if: (self: any) => self.showAlbums,
},
{
name: "people",
icon: PeopleIcon,
title: t("memories", "People"),
if: (self: any) => self.showPeople,
},
{
name: "archive",
icon: ArchiveIcon,
title: t("memories", "Archive"),
},
{
name: "thisday",
icon: CalendarIcon,
title: t("memories", "On this day"),
},
{
name: "tags",
icon: TagsIcon,
title: t("memories", "Tags"),
if: (self: any) => self.config_tagsEnabled,
},
{
name: "maps",
icon: MapIcon,
title: t("memories", "Maps"),
if: (self: any) => self.config_mapsEnabled,
},
];
private navItems = [];
get ncVersion() {
const version = (<any>window.OC).config.version.split(".");
return Number(version[0]);
@ -183,6 +189,11 @@ export default class App extends Mixins(GlobalMixin, UserConfig) {
mounted() {
this.doRouteChecks();
// Populate navigation
this.navItems = this.navItemsAll.filter(
(item) => !item.if || item.if(this)
);
// Store CSS variables modified
const root = document.documentElement;
const colorPrimary =
@ -241,6 +252,11 @@ export default class App extends Mixins(GlobalMixin, UserConfig) {
}
}
linkClick() {
const nav: any = this.$refs.nav;
if (window.innerWidth <= 1024) nav?.toggleNavigation(false);
}
doRouteChecks() {
if (this.$route.name === "folder-share") {
this.putFolderShareToken(this.$route.params.token);

View File

@ -98,82 +98,7 @@ class VideoContentSetup {
if (!e.slide.isActive) {
e.preventDefault();
} else if (content.videoElement) {
const fileid = content.data.photo.fileid;
// Create hls sources if enabled
let sources: any[] = [];
const baseUrl = generateUrl(
`/apps/memories/api/video/transcode/${clientId}/${fileid}`
);
if (!config_noTranscode) {
sources.push({
src: `${baseUrl}/index.m3u8`,
type: "application/x-mpegURL",
});
}
sources.push({
src: e.slide.data.src,
});
const overrideNative = !videojs.browser.IS_SAFARI;
content.videojs = videojs(content.videoElement, {
fill: true,
autoplay: true,
controls: true,
sources: sources,
preload: "metadata",
playbackRates: [0.5, 1, 1.5, 2],
responsive: true,
html5: {
vhs: {
overrideNative: overrideNative,
withCredentials: false,
},
nativeAudioTracks: !overrideNative,
nativeVideoTracks: !overrideNative,
},
});
content.videojs.on("error", () => {
if (content.videojs.error().code === 4) {
if (content.videojs.src().includes("m3u8")) {
// HLS could not be streamed
console.error("Video.js: HLS stream could not be opened.");
content.videojs.src({
src: e.slide.data.src,
});
this.updateRotation(content, 0);
}
}
});
content.videojs.qualityLevels();
content.videojs.hlsQualitySelector({
displayCurrentQuality: true,
});
setTimeout(() => {
content.videojs
.contentEl()
.querySelectorAll("button")
.forEach((b: HTMLButtonElement) => {
b.classList.add("button-vue");
});
}, 500);
// Get correct orientation
axios
.get<any>(
generateUrl("/apps/memories/api/image/info/{id}", {
id: content.data.photo.fileid,
})
)
.then((response) => {
content.data.exif = response.data?.exif;
this.updateRotation(content);
});
this.initVideo(content);
}
}
});
@ -189,15 +114,148 @@ class VideoContentSetup {
pswp.options.showHideAnimationType = "fade";
}
// pause video when closing
this.pauseVideo(pswp.currSlide.content);
// prevent more requests
this.destroyVideo(pswp.currSlide.content);
}
});
}
initVideo(content: any) {
if (!isVideoContent(content) || content.videojs) {
return;
}
content.videoElement = document.createElement("video");
content.videoElement.className = "video-js";
content.videoElement.setAttribute("poster", content.data.msrc);
if (this.options.videoAttributes) {
for (let key in this.options.videoAttributes) {
content.videoElement.setAttribute(
key,
this.options.videoAttributes[key] || ""
);
}
}
content.element.appendChild(content.videoElement);
// Pause / play on click on mobile
let touchAt = 0;
content.videoElement.addEventListener("touchstart", (e) => {
touchAt = e.timeStamp;
});
content.videoElement.addEventListener("touchend", (e) => {
if (touchAt && e.timeStamp - touchAt < 200) {
if (content.videojs.paused()) {
content.videojs.play();
} else {
content.videojs.pause();
}
}
touchAt = 0;
});
const fileid = content.data.photo.fileid;
// Create hls sources if enabled
let sources: any[] = [];
const baseUrl = generateUrl(
`/apps/memories/api/video/transcode/${clientId}/${fileid}`
);
if (!config_noTranscode) {
sources.push({
src: `${baseUrl}/index.m3u8`,
type: "application/x-mpegURL",
});
}
sources.push({
src: content.data.src,
});
const overrideNative = !videojs.browser.IS_SAFARI;
content.videojs = videojs(content.videoElement, {
fill: true,
autoplay: true,
controls: true,
sources: sources,
preload: "metadata",
playbackRates: [0.5, 1, 1.5, 2],
responsive: true,
html5: {
vhs: {
overrideNative: overrideNative,
withCredentials: false,
},
nativeAudioTracks: !overrideNative,
nativeVideoTracks: !overrideNative,
},
});
content.videojs.on("error", () => {
if (content.videojs.error().code === 4) {
if (content.videojs.src().includes("m3u8")) {
// HLS could not be streamed
console.error("Video.js: HLS stream could not be opened.");
content.videojs.src({
src: content.data.src,
});
this.updateRotation(content, 0);
}
}
});
content.videojs.qualityLevels();
content.videojs.hlsQualitySelector({
displayCurrentQuality: true,
});
setTimeout(() => {
content.videojs
.contentEl()
.querySelectorAll("button")
.forEach((b: HTMLButtonElement) => {
b.classList.add("button-vue");
});
// iOS needs this
content.videojs.play();
}, 500);
// Get correct orientation
axios
.get<any>(
generateUrl("/apps/memories/api/image/info/{id}", {
id: content.data.photo.fileid,
})
)
.then((response) => {
content.data.exif = response.data?.exif;
this.updateRotation(content);
});
}
destroyVideo(content: any) {
if (isVideoContent(content) && content.videojs) {
content.videojs.dispose();
content.videojs = null;
const elem: HTMLDivElement = content.element;
while (elem.lastElementChild) {
elem.removeChild(elem.lastElementChild);
}
content.videoElement = null;
}
}
updateRotation(content, val?: number) {
if (!content.videojs || !content.videoElement) {
return;
}
const rotation = val ?? Number(content.data.exif?.Rotation);
const shouldRotate = content.videojs?.src().includes("m3u8");
if (rotation && shouldRotate) {
let transform = `rotate(${rotation}deg)`;
@ -219,11 +277,6 @@ class VideoContentSetup {
onContentDestroy({ content }) {
if (isVideoContent(content)) {
if (content._videoPosterImg) {
content._videoPosterImg.onload = content._videoPosterImg.onerror = null;
content._videoPosterImg = null;
}
if (content.videojs) {
content.videojs.dispose();
content.videojs = null;
@ -271,15 +324,11 @@ class VideoContentSetup {
}
onContentActivate({ content }) {
if (isVideoContent(content) && this.options.autoplay) {
this.playVideo(content);
}
this.initVideo(content);
}
onContentDeactivate({ content }) {
if (isVideoContent(content)) {
this.pauseVideo(content);
}
this.destroyVideo(content);
}
onContentAppend(e) {
@ -319,19 +368,6 @@ class VideoContentSetup {
content.state = "loading";
content.type = "video"; // TODO: move this to pswp core?
content.videoElement = document.createElement("video");
content.videoElement.className = "video-js";
content.videoElement.setAttribute("controls", "true");
if (this.options.videoAttributes) {
for (let key in this.options.videoAttributes) {
content.videoElement.setAttribute(
key,
this.options.videoAttributes[key] || ""
);
}
}
content.element = document.createElement("div");
content.element.style.position = "absolute";
content.element.style.left = 0;
@ -339,34 +375,7 @@ class VideoContentSetup {
content.element.style.width = "100%";
content.element.style.height = "100%";
// content.videoElement.setAttribute("poster", content.data.msrc);
// this.preloadVideoPoster(content, content.data.msrc);
content.onLoaded();
content.element.appendChild(content.videoElement);
}
preloadVideoPoster(content, src) {
if (!content._videoPosterImg && src) {
content._videoPosterImg = new Image();
content._videoPosterImg.src = src;
if (content._videoPosterImg.complete) {
content.onLoaded();
} else {
content._videoPosterImg.onload = content._videoPosterImg.onerror =
() => {
content.onLoaded();
};
}
}
}
playVideo(content) {
content.videojs?.play();
}
pauseVideo(content) {
content.videojs?.pause();
}
useContentPlaceholder(usePlaceholder, content) {