mirror of
https://github.com/simon987/sist2.git
synced 2025-12-11 14:38:54 +00:00
Add thumbnail-count option
This commit is contained in:
33
sist2-vue/dist/index.html
vendored
33
sist2-vue/dist/index.html
vendored
@@ -1,32 +1,3 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'/>
|
||||
|
||||
<title>sist2</title>
|
||||
<link href="js/chunk-vendors.js" rel="preload" as="script"><link href="js/index.js" rel="preload" as="script"></head>
|
||||
<body>
|
||||
<noscript>
|
||||
<style>
|
||||
body {
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>sist2</title><link href="css/chunk-vendors.css" rel="preload" as="style"><link href="css/index.css" rel="preload" as="style"><link href="js/chunk-vendors.js" rel="preload" as="script"><link href="js/index.js" rel="preload" as="script"><link href="css/chunk-vendors.css" rel="stylesheet"><link href="css/index.css" rel="stylesheet"></head><body><noscript><style>body {
|
||||
height: initial;
|
||||
}
|
||||
</style>
|
||||
<div style="text-align: center; margin-top: 100px">
|
||||
<strong>
|
||||
We're sorry but sist2 doesn't work properly without JavaScript enabled.
|
||||
Please enable it to continue.
|
||||
</strong>
|
||||
<br/>
|
||||
<strong>
|
||||
Nous sommes désolés mais sist2 ne fonctionne pas correctement
|
||||
si JavaScript est activé.
|
||||
Veuillez l'activer pour continuer.
|
||||
</strong>
|
||||
</div>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<script type="text/javascript" src="js/chunk-vendors.js"></script><script type="text/javascript" src="js/index.js"></script></body>
|
||||
</html>
|
||||
}</style><div style="text-align: center; margin-top: 100px"><strong>We're sorry but sist2 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong><br><strong>Nous sommes désolés mais sist2 ne fonctionne pas correctement si JavaScript est activé. Veuillez l'activer pour continuer.</strong></div></noscript><div id="app"></div><script src="js/chunk-vendors.js"></script><script src="js/index.js"></script></body></html>
|
||||
19899
sist2-vue/dist/js/chunk-vendors.js
vendored
19899
sist2-vue/dist/js/chunk-vendors.js
vendored
File diff suppressed because one or more lines are too long
3820
sist2-vue/dist/js/index.js
vendored
3820
sist2-vue/dist/js/index.js
vendored
File diff suppressed because one or more lines are too long
@@ -62,8 +62,9 @@ export interface EsHit {
|
||||
isPlayableImage: boolean
|
||||
isAudio: boolean
|
||||
hasThumbnail: boolean
|
||||
tnW: number
|
||||
tnH: number
|
||||
hasVidPreview: boolean
|
||||
/** Number of thumbnails available */
|
||||
tnNum: number
|
||||
}
|
||||
highlight: {
|
||||
name: string[] | undefined,
|
||||
@@ -134,8 +135,15 @@ class Sist2Api {
|
||||
|
||||
if ("thumbnail" in hit._source) {
|
||||
hit._props.hasThumbnail = true;
|
||||
hit._props.tnW = Number(hit._source.thumbnail.split(",")[0]);
|
||||
hit._props.tnH = Number(hit._source.thumbnail.split(",")[1]);
|
||||
|
||||
if (Number.isNaN(Number(hit._source.thumbnail))) {
|
||||
// Backwards compatibility
|
||||
hit._props.tnNum = 1;
|
||||
hit._props.hasVidPreview = false;
|
||||
} else {
|
||||
hit._props.tnNum = Number(hit._source.thumbnail);
|
||||
hit._props.hasVidPreview = hit._props.tnNum > 1;
|
||||
}
|
||||
}
|
||||
|
||||
switch (mimeCategory) {
|
||||
|
||||
@@ -30,6 +30,7 @@ export default {
|
||||
{key: "esIndex", value: this.$store.state.sist2Info.esIndex},
|
||||
{key: "tagline", value: this.$store.state.sist2Info.tagline},
|
||||
{key: "dev", value: this.$store.state.sist2Info.dev},
|
||||
{key: "mongooseVersion", value: this.$store.state.sist2Info.mongooseVersion},
|
||||
{key: "esVersion", value: this.$store.state.sist2Info.esVersion},
|
||||
{key: "esVersionSupported", value: this.$store.state.sist2Info.esVersionSupported},
|
||||
{key: "esVersionLegacy", value: this.$store.state.sist2Info.esVersionLegacy},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="doc-card" :class="{'sub-document': doc._props.isSubDocument}" :style="`width: ${width}px`">
|
||||
<div class="doc-card" :class="{'sub-document': doc._props.isSubDocument}" :style="`width: ${width}px`"
|
||||
@click="$store.commit('busTnTouchStart', null)">
|
||||
<b-card
|
||||
no-body
|
||||
img-top
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div v-if="doc._props.hasThumbnail" class="img-wrapper" @mouseenter="onTnEnter()" @mouseleave="onTnLeave()">
|
||||
<div v-if="doc._props.hasThumbnail" class="img-wrapper" @mouseenter="onTnEnter()" @mouseleave="onTnLeave()"
|
||||
@touchstart="onTouchStart()">
|
||||
<div v-if="doc._props.isAudio" class="card-img-overlay" :class="{'small-badge': smallBadge}">
|
||||
<span class="badge badge-resolution">{{ humanTime(doc._source.duration) }}</span>
|
||||
</div>
|
||||
@@ -25,26 +26,53 @@
|
||||
|
||||
<img ref="tn"
|
||||
v-if="doc._props.isPlayableImage || doc._props.isPlayableVideo"
|
||||
:src="(doc._props.isGif && hover) ? `f/${doc._id}` : `t/${doc._source.index}/${doc._id}`"
|
||||
:src="tnSrc"
|
||||
alt=""
|
||||
:style="{height: (doc._props.isGif && hover) ? `${tnHeight()}px` : undefined}"
|
||||
class="pointer fit card-img-top" @click="onThumbnailClick()">
|
||||
<img v-else :src="`t/${doc._source.index}/${doc._id}`" alt=""
|
||||
<img v-else :src="tnSrc" alt=""
|
||||
class="fit card-img-top">
|
||||
|
||||
<ThumbnailProgressBar v-if="hover && doc._props.hasVidPreview"
|
||||
:progress="(currentThumbnailNum + 1) / (doc._props.tnNum)"
|
||||
></ThumbnailProgressBar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {humanTime} from "@/util";
|
||||
import ThumbnailProgressBar from "@/components/ThumbnailProgressBar";
|
||||
|
||||
export default {
|
||||
name: "FullThumbnail",
|
||||
props: ["doc", "smallBadge"],
|
||||
components: {ThumbnailProgressBar},
|
||||
data() {
|
||||
return {
|
||||
hover: false
|
||||
hover: false,
|
||||
currentThumbnailNum: 0,
|
||||
timeoutId: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.subscribe((mutation) => {
|
||||
if (mutation.type === "busTnTouchStart" && mutation.payload !== this.doc._id) {
|
||||
this.onTnLeave();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
tnSrc() {
|
||||
const doc = this.doc;
|
||||
const props = doc._props;
|
||||
if (props.isGif && this.hover) {
|
||||
return `f/${doc._id}`;
|
||||
}
|
||||
return (this.currentThumbnailNum === 0)
|
||||
? `t/${doc._source.index}/${doc._id}`
|
||||
: `t/${doc._source.index}/${doc._id}${String(this.currentThumbnailNum).padStart(4, "0")}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
humanTime: humanTime,
|
||||
onThumbnailClick() {
|
||||
@@ -53,11 +81,43 @@ export default {
|
||||
tnHeight() {
|
||||
return this.$refs.tn.height;
|
||||
},
|
||||
tnWidth() {
|
||||
return this.$refs.tn.width;
|
||||
},
|
||||
onTnEnter() {
|
||||
this.hover = true;
|
||||
if (this.doc._props.hasVidPreview) {
|
||||
this.currentThumbnailNum += 1;
|
||||
this.scheduleNextTnNum();
|
||||
}
|
||||
},
|
||||
onTnLeave() {
|
||||
this.currentThumbnailNum = 0;
|
||||
this.hover = false;
|
||||
if (this.timeoutId !== null) {
|
||||
window.clearTimeout(this.timeoutId);
|
||||
this.timeoutId = null;
|
||||
}
|
||||
},
|
||||
scheduleNextTnNum() {
|
||||
const INTERVAL = this.$store.state.optVidPreviewInterval ?? 700;
|
||||
this.timeoutId = window.setTimeout(() => {
|
||||
if (!this.hover) {
|
||||
return;
|
||||
}
|
||||
this.scheduleNextTnNum();
|
||||
if (this.currentThumbnailNum === this.doc._props.tnNum - 1) {
|
||||
this.currentThumbnailNum = 0;
|
||||
} else {
|
||||
this.currentThumbnailNum += 1;
|
||||
}
|
||||
}, INTERVAL);
|
||||
},
|
||||
onTouchStart() {
|
||||
this.$store.commit("busTnTouchStart", this.doc._id);
|
||||
if (!this.hover) {
|
||||
this.onTnEnter()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
40
sist2-vue/src/components/ThumbnailProgressBar.vue
Normal file
40
sist2-vue/src/components/ThumbnailProgressBar.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="thumbnail-progress-bar" :style="{width: `${percentProgress}%`}"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ThumbnailProgressBar",
|
||||
props: ["doc", "progress"],
|
||||
computed: {
|
||||
percentProgress() {
|
||||
return Math.min(Math.max(this.progress * 100, 0), 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.thumbnail-progress-bar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
|
||||
height: 4px;
|
||||
background: #2196f3AA;
|
||||
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.theme-black .thumbnail-progress-bar {
|
||||
background: rgba(0, 188, 212, 0.95);
|
||||
}
|
||||
|
||||
.sub-document .thumbnail-progress-bar {
|
||||
max-width: calc(100% - 8px);
|
||||
left: 4px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -71,7 +71,8 @@ export default {
|
||||
hideDuplicates: "Hide duplicate results based on checksum",
|
||||
hideLegacy: "Hide the 'legacyES' Elasticsearch notice",
|
||||
updateMimeMap: "Update the Media Types tree in real time",
|
||||
useDatePicker: "Use a Date Picker component rather than a slider"
|
||||
useDatePicker: "Use a Date Picker component rather than a slider",
|
||||
vidPreviewInterval: "Video preview frame duration in ms"
|
||||
},
|
||||
queryMode: {
|
||||
simple: "Simple",
|
||||
@@ -237,7 +238,8 @@ export default {
|
||||
hideDuplicates: "Masquer les résultats en double",
|
||||
hideLegacy: "Masquer la notice 'legacyES' Elasticsearch",
|
||||
updateMimeMap: "Mettre à jour l'arbre de Types de médias en temps réel",
|
||||
useDatePicker: "Afficher un composant « Date Picker » plutôt qu'un slider"
|
||||
useDatePicker: "Afficher un composant « Date Picker » plutôt qu'un slider",
|
||||
vidPreviewInterval: "Durée des images d'aperçu video en millisecondes"
|
||||
},
|
||||
queryMode: {
|
||||
simple: "Simple",
|
||||
@@ -403,7 +405,8 @@ export default {
|
||||
hideDuplicates: "使用校验码隐藏重复结果",
|
||||
hideLegacy: "隐藏'legacyES' Elasticsearch 通知",
|
||||
updateMimeMap: "媒体类型树的实时更新",
|
||||
useDatePicker: "使用日期选择器组件而不是滑块"
|
||||
useDatePicker: "使用日期选择器组件而不是滑块",
|
||||
vidPreviewInterval: "视频预览帧的持续时间,以毫秒为单位"
|
||||
},
|
||||
queryMode: {
|
||||
simple: "简单",
|
||||
|
||||
@@ -50,6 +50,7 @@ export default new Vuex.Store({
|
||||
optHideLegacy: false,
|
||||
optUpdateMimeMap: false,
|
||||
optUseDatePicker: false,
|
||||
optVidPreviewInterval: 700,
|
||||
|
||||
_onLoadSelectedIndices: [] as string[],
|
||||
_onLoadSelectedMimeTypes: [] as string[],
|
||||
@@ -159,6 +160,7 @@ export default new Vuex.Store({
|
||||
setOptHideLegacy: (state, val) => state.optHideLegacy = val,
|
||||
setOptUpdateMimeMap: (state, val) => state.optUpdateMimeMap = val,
|
||||
setOptUseDatePicker: (state, val) => state.optUseDatePicker = val,
|
||||
setOptVidPreviewInterval: (state, val) => state.optVidPreviewInterval = val,
|
||||
|
||||
setOptLightboxLoadOnlyCurrent: (state, val) => state.optLightboxLoadOnlyCurrent = val,
|
||||
setOptLightboxSlideDuration: (state, val) => state.optLightboxSlideDuration = val,
|
||||
@@ -174,6 +176,12 @@ export default new Vuex.Store({
|
||||
busSearch: () => {
|
||||
// noop
|
||||
},
|
||||
busTouchEnd: () => {
|
||||
// noop
|
||||
},
|
||||
busTnTouchStart: (doc_id) => {
|
||||
// noop
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setSist2Info: (store, val) => {
|
||||
@@ -369,5 +377,6 @@ export default new Vuex.Store({
|
||||
optHideLegacy: state => state.optHideLegacy,
|
||||
optUpdateMimeMap: state => state.optUpdateMimeMap,
|
||||
optUseDatePicker: state => state.optUseDatePicker,
|
||||
optVidPreviewInterval: state => state.optVidPreviewInterval,
|
||||
}
|
||||
})
|
||||
@@ -85,6 +85,10 @@
|
||||
<label>{{ $t("opt.slideDuration") }}</label>
|
||||
<b-form-input :value="optLightboxSlideDuration" type="number" min="1"
|
||||
@input="setOptLightboxSlideDuration"></b-form-input>
|
||||
|
||||
<label>{{ $t("opt.vidPreviewInterval") }}</label>
|
||||
<b-form-input :value="optVidPreviewInterval" type="number" min="50"
|
||||
@input="setOptVidPreviewInterval"></b-form-input>
|
||||
</b-card>
|
||||
|
||||
<h4 class="mt-3">{{ $t("treemapOptions") }}</h4>
|
||||
@@ -234,6 +238,7 @@ export default {
|
||||
"optHideLegacy",
|
||||
"optUpdateMimeMap",
|
||||
"optUseDatePicker",
|
||||
"optVidPreviewInterval",
|
||||
]),
|
||||
clientWidth() {
|
||||
return window.innerWidth;
|
||||
@@ -279,6 +284,7 @@ export default {
|
||||
"setOptHideLegacy",
|
||||
"setOptUpdateMimeMap",
|
||||
"setOptUseDatePicker",
|
||||
"setOptVidPreviewInterval",
|
||||
]),
|
||||
onResetClick() {
|
||||
localStorage.removeItem("sist2_configuration");
|
||||
|
||||
@@ -100,6 +100,10 @@ export default Vue.extend({
|
||||
...mapGetters(["indices", "optDisplay"]),
|
||||
},
|
||||
mounted() {
|
||||
// Handle touch events
|
||||
window.ontouchend = () => this.$store.commit("busTouchEnd");
|
||||
window.ontouchcancel = this.$store.commit("busTouchEnd");
|
||||
|
||||
this.search = _debounce(async (clear: boolean) => {
|
||||
if (clear) {
|
||||
await this.clearResults();
|
||||
|
||||
Reference in New Issue
Block a user