354 lines
9.0 KiB
Vue
354 lines
9.0 KiB
Vue
<template>
|
|
<div
|
|
id="previewer"
|
|
@mousemove="toggleNavigation"
|
|
@touchstart="toggleNavigation"
|
|
>
|
|
<header-bar>
|
|
<action icon="close" :label="$t('buttons.close')" @action="close()" />
|
|
<title>{{ name }}</title>
|
|
<action
|
|
:disabled="loading"
|
|
v-if="isResizeEnabled && req.type === 'image'"
|
|
:icon="fullSize ? 'photo_size_select_large' : 'hd'"
|
|
@action="toggleSize"
|
|
/>
|
|
|
|
<template #actions>
|
|
<action
|
|
:disabled="loading"
|
|
v-if="user.perm.rename"
|
|
icon="mode_edit"
|
|
:label="$t('buttons.rename')"
|
|
show="rename"
|
|
/>
|
|
<action
|
|
:disabled="loading"
|
|
v-if="user.perm.delete"
|
|
icon="delete"
|
|
:label="$t('buttons.delete')"
|
|
@action="deleteFile"
|
|
id="delete-button"
|
|
/>
|
|
<action
|
|
:disabled="loading"
|
|
v-if="user.perm.download"
|
|
icon="file_download"
|
|
:label="$t('buttons.download')"
|
|
@action="download"
|
|
/>
|
|
<action
|
|
:disabled="loading"
|
|
icon="info"
|
|
:label="$t('buttons.info')"
|
|
show="info"
|
|
/>
|
|
</template>
|
|
</header-bar>
|
|
|
|
<div class="loading delayed" v-if="loading">
|
|
<div class="spinner">
|
|
<div class="bounce1"></div>
|
|
<div class="bounce2"></div>
|
|
<div class="bounce3"></div>
|
|
</div>
|
|
</div>
|
|
<template v-else>
|
|
<div class="preview">
|
|
<ExtendedImage v-if="req.type == 'image'" :src="raw"></ExtendedImage>
|
|
<audio
|
|
v-else-if="req.type == 'audio'"
|
|
ref="player"
|
|
:src="raw"
|
|
controls
|
|
:autoplay="autoPlay"
|
|
@play="autoPlay = true"
|
|
></audio>
|
|
<video
|
|
v-else-if="req.type == 'video'"
|
|
ref="player"
|
|
:src="raw"
|
|
controls
|
|
:autoplay="autoPlay"
|
|
@play="autoPlay = true"
|
|
>
|
|
<track
|
|
kind="captions"
|
|
v-for="(sub, index) in subtitles"
|
|
:key="index"
|
|
:src="sub"
|
|
:label="'Subtitle ' + index"
|
|
:default="index === 0"
|
|
/>
|
|
Sorry, your browser doesn't support embedded videos, but don't worry,
|
|
you can <a :href="downloadUrl">download it</a>
|
|
and watch it with your favorite video player!
|
|
</video>
|
|
<object
|
|
v-else-if="req.extension.toLowerCase() == '.pdf'"
|
|
class="pdf"
|
|
:data="raw"
|
|
></object>
|
|
<div v-else-if="req.type == 'blob'" class="info">
|
|
<div class="title">
|
|
<i class="material-icons">feedback</i>
|
|
{{ $t("files.noPreview") }}
|
|
</div>
|
|
<div>
|
|
<a target="_blank" :href="downloadUrl" class="button button--flat">
|
|
<div>
|
|
<i class="material-icons">file_download</i
|
|
>{{ $t("buttons.download") }}
|
|
</div>
|
|
</a>
|
|
<a
|
|
target="_blank"
|
|
:href="raw"
|
|
class="button button--flat"
|
|
v-if="!req.isDir"
|
|
>
|
|
<div>
|
|
<i class="material-icons">open_in_new</i
|
|
>{{ $t("buttons.openFile") }}
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<button
|
|
@click="prev"
|
|
@mouseover="hoverNav = true"
|
|
@mouseleave="hoverNav = false"
|
|
:class="{ hidden: !hasPrevious || !showNav }"
|
|
:aria-label="$t('buttons.previous')"
|
|
:title="$t('buttons.previous')"
|
|
>
|
|
<i class="material-icons">chevron_left</i>
|
|
</button>
|
|
<button
|
|
@click="next"
|
|
@mouseover="hoverNav = true"
|
|
@mouseleave="hoverNav = false"
|
|
:class="{ hidden: !hasNext || !showNav }"
|
|
:aria-label="$t('buttons.next')"
|
|
:title="$t('buttons.next')"
|
|
>
|
|
<i class="material-icons">chevron_right</i>
|
|
</button>
|
|
<link rel="prefetch" :href="previousRaw" />
|
|
<link rel="prefetch" :href="nextRaw" />
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapState } from "vuex";
|
|
import { files as api } from "@/api";
|
|
import { resizePreview } from "@/utils/constants";
|
|
import url from "@/utils/url";
|
|
import throttle from "lodash.throttle";
|
|
import HeaderBar from "@/components/header/HeaderBar";
|
|
import Action from "@/components/header/Action";
|
|
import ExtendedImage from "@/components/files/ExtendedImage";
|
|
|
|
const mediaTypes = ["image", "video", "audio", "blob"];
|
|
|
|
export default {
|
|
name: "preview",
|
|
components: {
|
|
HeaderBar,
|
|
Action,
|
|
ExtendedImage,
|
|
},
|
|
data: function () {
|
|
return {
|
|
previousLink: "",
|
|
nextLink: "",
|
|
listing: null,
|
|
name: "",
|
|
subtitles: [],
|
|
fullSize: false,
|
|
showNav: true,
|
|
navTimeout: null,
|
|
hoverNav: false,
|
|
autoPlay: false,
|
|
previousRaw: "",
|
|
nextRaw: "",
|
|
};
|
|
},
|
|
computed: {
|
|
...mapState(["req", "user", "oldReq", "jwt", "loading", "show"]),
|
|
hasPrevious() {
|
|
return this.previousLink !== "";
|
|
},
|
|
hasNext() {
|
|
return this.nextLink !== "";
|
|
},
|
|
downloadUrl() {
|
|
return api.getDownloadURL(this.req);
|
|
},
|
|
raw() {
|
|
if (this.req.type === "image" && !this.fullSize) {
|
|
return api.getPreviewURL(this.req, "big");
|
|
}
|
|
|
|
return api.getDownloadURL(this.req, true);
|
|
},
|
|
showMore() {
|
|
return this.$store.state.show === "more";
|
|
},
|
|
isResizeEnabled() {
|
|
return resizePreview;
|
|
},
|
|
},
|
|
watch: {
|
|
$route: function () {
|
|
this.updatePreview();
|
|
this.toggleNavigation();
|
|
},
|
|
},
|
|
async mounted() {
|
|
window.addEventListener("keydown", this.key);
|
|
this.listing = this.oldReq.items;
|
|
this.updatePreview();
|
|
},
|
|
beforeDestroy() {
|
|
window.removeEventListener("keydown", this.key);
|
|
},
|
|
methods: {
|
|
deleteFile() {
|
|
this.$store.commit("showHover", {
|
|
prompt: "delete",
|
|
confirm: () => {
|
|
this.listing = this.listing.filter((item) => item.name !== this.name);
|
|
|
|
if (this.hasNext) {
|
|
this.next();
|
|
} else if (!this.hasPrevious && !this.hasNext) {
|
|
this.close();
|
|
} else {
|
|
this.prev();
|
|
}
|
|
},
|
|
});
|
|
},
|
|
prev() {
|
|
this.hoverNav = false;
|
|
this.$router.replace({ path: this.previousLink });
|
|
},
|
|
next() {
|
|
this.hoverNav = false;
|
|
this.$router.replace({ path: this.nextLink });
|
|
},
|
|
key(event) {
|
|
if (this.show !== null) {
|
|
return;
|
|
}
|
|
|
|
if (event.which === 13 || event.which === 39) {
|
|
// right arrow
|
|
if (this.hasNext) this.next();
|
|
} else if (event.which === 37) {
|
|
// left arrow
|
|
if (this.hasPrevious) this.prev();
|
|
} else if (event.which === 27) {
|
|
// esc
|
|
this.close();
|
|
}
|
|
},
|
|
async updatePreview() {
|
|
if (
|
|
this.$refs.player &&
|
|
this.$refs.player.paused &&
|
|
!this.$refs.player.ended
|
|
) {
|
|
this.autoPlay = false;
|
|
}
|
|
|
|
if (this.req.subtitles) {
|
|
this.subtitles = api.getSubtitlesURL(this.req);
|
|
}
|
|
|
|
let dirs = this.$route.fullPath.split("/");
|
|
this.name = decodeURIComponent(dirs[dirs.length - 1]);
|
|
|
|
if (!this.listing) {
|
|
try {
|
|
const path = url.removeLastDir(this.$route.path);
|
|
const res = await api.fetch(path);
|
|
this.listing = res.items;
|
|
} catch (e) {
|
|
this.$showError(e);
|
|
}
|
|
}
|
|
|
|
this.previousLink = "";
|
|
this.nextLink = "";
|
|
|
|
for (let i = 0; i < this.listing.length; i++) {
|
|
if (this.listing[i].name !== this.name) {
|
|
continue;
|
|
}
|
|
|
|
for (let j = i - 1; j >= 0; j--) {
|
|
if (mediaTypes.includes(this.listing[j].type)) {
|
|
this.previousLink = this.listing[j].url;
|
|
this.previousRaw = this.prefetchUrl(this.listing[j]);
|
|
break;
|
|
}
|
|
}
|
|
for (let j = i + 1; j < this.listing.length; j++) {
|
|
if (mediaTypes.includes(this.listing[j].type)) {
|
|
this.nextLink = this.listing[j].url;
|
|
this.nextRaw = this.prefetchUrl(this.listing[j]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
},
|
|
prefetchUrl(item) {
|
|
if (item.type !== "image") {
|
|
return "";
|
|
}
|
|
|
|
return this.fullSize
|
|
? api.getDownloadURL(item, true)
|
|
: api.getPreviewURL(item, "big");
|
|
},
|
|
openMore() {
|
|
this.$store.commit("showHover", "more");
|
|
},
|
|
resetPrompts() {
|
|
this.$store.commit("closeHovers");
|
|
},
|
|
toggleSize() {
|
|
this.fullSize = !this.fullSize;
|
|
},
|
|
toggleNavigation: throttle(function () {
|
|
this.showNav = true;
|
|
|
|
if (this.navTimeout) {
|
|
clearTimeout(this.navTimeout);
|
|
}
|
|
|
|
this.navTimeout = setTimeout(() => {
|
|
this.showNav = false || this.hoverNav;
|
|
this.navTimeout = null;
|
|
}, 1500);
|
|
}, 500),
|
|
close() {
|
|
this.$store.commit("updateRequest", {});
|
|
|
|
let uri = url.removeLastDir(this.$route.path) + "/";
|
|
this.$router.push({ path: uri });
|
|
},
|
|
download() {
|
|
window.open(this.downloadUrl);
|
|
},
|
|
},
|
|
};
|
|
</script>
|