feat:Support compress image view
This commit is contained in:
parent
b8300b7121
commit
8af40d28a7
@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="item"
|
<div
|
||||||
|
class="item"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
@ -11,9 +12,12 @@
|
|||||||
@touchstart="touchstart"
|
@touchstart="touchstart"
|
||||||
:data-dir="isDir"
|
:data-dir="isDir"
|
||||||
:aria-label="name"
|
:aria-label="name"
|
||||||
:aria-selected="isSelected">
|
:aria-selected="isSelected"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<i class="material-icons">{{ icon }}</i>
|
<img v-if="type==='image'" :src="compressUrl" />
|
||||||
|
<i v-else class="material-icons">{{ icon }}</i>
|
||||||
|
<!-- <i class="material-icons">{{ icon }}</i> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -30,140 +34,146 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations, mapGetters, mapState } from 'vuex'
|
import { mapMutations, mapGetters, mapState } from "vuex";
|
||||||
import filesize from 'filesize'
|
import { baseURL } from "@/utils/constants";
|
||||||
import moment from 'moment'
|
import filesize from "filesize";
|
||||||
import { files as api } from '@/api'
|
import moment from "moment";
|
||||||
|
import { files as api } from "@/api";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'item',
|
name: "item",
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
touches: 0
|
touches: 0
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
props: ['name', 'isDir', 'url', 'type', 'size', 'modified', 'index'],
|
props: ["name", "isDir", "url", "type", "size", "modified", "index"],
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['selected', 'req']),
|
...mapState(["selected", "req", "jwt"]),
|
||||||
...mapGetters(['selectedCount']),
|
...mapGetters(["selectedCount"]),
|
||||||
isSelected() {
|
isSelected() {
|
||||||
return (this.selected.indexOf(this.index) !== -1)
|
return this.selected.indexOf(this.index) !== -1;
|
||||||
},
|
},
|
||||||
icon() {
|
icon() {
|
||||||
if (this.isDir) return 'folder'
|
if (this.isDir) return "folder";
|
||||||
if (this.type === 'image') return 'insert_photo'
|
if (this.type === "image") return "insert_photo";
|
||||||
if (this.type === 'audio') return 'volume_up'
|
if (this.type === "audio") return "volume_up";
|
||||||
if (this.type === 'video') return 'movie'
|
if (this.type === "video") return "movie";
|
||||||
return 'insert_drive_file'
|
return "insert_drive_file";
|
||||||
},
|
},
|
||||||
canDrop() {
|
canDrop() {
|
||||||
if (!this.isDir) return false
|
if (!this.isDir) return false;
|
||||||
|
|
||||||
for (let i of this.selected) {
|
for (let i of this.selected) {
|
||||||
if (this.req.items[i].url === this.url) {
|
if (this.req.items[i].url === this.url) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true;
|
||||||
|
},
|
||||||
|
compressUrl() {
|
||||||
|
const path = this.url.replace(/^\/files\//, "");
|
||||||
|
return `${baseURL}/api/compress/${path}?auth=${this.jwt}&inline=true`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['addSelected', 'removeSelected', 'resetSelected']),
|
...mapMutations(["addSelected", "removeSelected", "resetSelected"]),
|
||||||
humanSize: function() {
|
humanSize: function() {
|
||||||
return filesize(this.size)
|
return filesize(this.size);
|
||||||
},
|
},
|
||||||
humanTime: function() {
|
humanTime: function() {
|
||||||
return moment(this.modified).fromNow()
|
return moment(this.modified).fromNow();
|
||||||
},
|
},
|
||||||
dragStart: function() {
|
dragStart: function() {
|
||||||
if (this.selectedCount === 0) {
|
if (this.selectedCount === 0) {
|
||||||
this.addSelected(this.index)
|
this.addSelected(this.index);
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isSelected) {
|
if (!this.isSelected) {
|
||||||
this.resetSelected()
|
this.resetSelected();
|
||||||
this.addSelected(this.index)
|
this.addSelected(this.index);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dragOver: function(event) {
|
dragOver: function(event) {
|
||||||
if (!this.canDrop) return
|
if (!this.canDrop) return;
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
let el = event.target
|
let el = event.target;
|
||||||
|
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
if (!el.classList.contains('item')) {
|
if (!el.classList.contains("item")) {
|
||||||
el = el.parentElement
|
el = el.parentElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
el.style.opacity = 1
|
el.style.opacity = 1;
|
||||||
},
|
},
|
||||||
drop: function(event) {
|
drop: function(event) {
|
||||||
if (!this.canDrop) return
|
if (!this.canDrop) return;
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
|
|
||||||
if (this.selectedCount === 0) return
|
if (this.selectedCount === 0) return;
|
||||||
|
|
||||||
let items = []
|
let items = [];
|
||||||
|
|
||||||
for (let i of this.selected) {
|
for (let i of this.selected) {
|
||||||
items.push({
|
items.push({
|
||||||
from: this.req.items[i].url,
|
from: this.req.items[i].url,
|
||||||
to: this.url + this.req.items[i].name
|
to: this.url + this.req.items[i].name
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
api.move(items)
|
api
|
||||||
|
.move(items)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$store.commit('setReload', true)
|
this.$store.commit("setReload", true);
|
||||||
})
|
})
|
||||||
.catch(this.$showError)
|
.catch(this.$showError);
|
||||||
},
|
},
|
||||||
click: function(event) {
|
click: function(event) {
|
||||||
if (this.selectedCount !== 0) event.preventDefault()
|
if (this.selectedCount !== 0) event.preventDefault();
|
||||||
if (this.$store.state.selected.indexOf(this.index) !== -1) {
|
if (this.$store.state.selected.indexOf(this.index) !== -1) {
|
||||||
this.removeSelected(this.index)
|
this.removeSelected(this.index);
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
let fi = 0
|
let fi = 0;
|
||||||
let la = 0
|
let la = 0;
|
||||||
|
|
||||||
if (this.index > this.selected[0]) {
|
if (this.index > this.selected[0]) {
|
||||||
fi = this.selected[0] + 1
|
fi = this.selected[0] + 1;
|
||||||
la = this.index
|
la = this.index;
|
||||||
} else {
|
} else {
|
||||||
fi = this.index
|
fi = this.index;
|
||||||
la = this.selected[0] - 1
|
la = this.selected[0] - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; fi <= la; fi++) {
|
for (; fi <= la; fi++) {
|
||||||
this.addSelected(fi)
|
this.addSelected(fi);
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected()
|
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected();
|
||||||
this.addSelected(this.index)
|
this.addSelected(this.index);
|
||||||
},
|
},
|
||||||
touchstart() {
|
touchstart() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.touches = 0
|
this.touches = 0;
|
||||||
}, 300)
|
}, 300);
|
||||||
|
|
||||||
this.touches++
|
this.touches++;
|
||||||
if (this.touches > 1) {
|
if (this.touches > 1) {
|
||||||
this.open()
|
this.open();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
open: function() {
|
open: function() {
|
||||||
this.$router.push({path: this.url})
|
this.$router.push({ path: this.url });
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="previewer">
|
<div id="previewer">
|
||||||
<div class="bar">
|
<div class="bar">
|
||||||
<button @click="back" class="action" :title="$t('files.closePreview')" :aria-label="$t('files.closePreview')" id="close">
|
<button
|
||||||
|
@click="back"
|
||||||
|
class="action"
|
||||||
|
:title="$t('files.closePreview')"
|
||||||
|
:aria-label="$t('files.closePreview')"
|
||||||
|
id="close"
|
||||||
|
>
|
||||||
<i class="material-icons">close</i>
|
<i class="material-icons">close</i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -11,10 +17,22 @@
|
|||||||
<info-button></info-button>
|
<info-button></info-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="action" @click="prev" v-show="hasPrevious" :aria-label="$t('buttons.previous')" :title="$t('buttons.previous')">
|
<button
|
||||||
|
class="action"
|
||||||
|
@click="prev"
|
||||||
|
v-show="hasPrevious"
|
||||||
|
:aria-label="$t('buttons.previous')"
|
||||||
|
:title="$t('buttons.previous')"
|
||||||
|
>
|
||||||
<i class="material-icons">chevron_left</i>
|
<i class="material-icons">chevron_left</i>
|
||||||
</button>
|
</button>
|
||||||
<button class="action" @click="next" v-show="hasNext" :aria-label="$t('buttons.next')" :title="$t('buttons.next')">
|
<button
|
||||||
|
class="action"
|
||||||
|
@click="next"
|
||||||
|
v-show="hasNext"
|
||||||
|
:aria-label="$t('buttons.next')"
|
||||||
|
:title="$t('buttons.next')"
|
||||||
|
>
|
||||||
<i class="material-icons">chevron_right</i>
|
<i class="material-icons">chevron_right</i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -27,39 +45,39 @@
|
|||||||
v-for="(sub, index) in subtitles"
|
v-for="(sub, index) in subtitles"
|
||||||
:key="index"
|
:key="index"
|
||||||
:src="sub"
|
:src="sub"
|
||||||
:label="'Subtitle ' + index" :default="index === 0">
|
:label="'Subtitle ' + index"
|
||||||
Sorry, your browser doesn't support embedded videos,
|
:default="index === 0"
|
||||||
but don't worry, you can <a :href="download">download it</a>
|
/>Sorry, your browser doesn't support embedded videos,
|
||||||
|
but don't worry, you can
|
||||||
|
<a :href="download">download it</a>
|
||||||
and watch it with your favorite video player!
|
and watch it with your favorite video player!
|
||||||
</video>
|
</video>
|
||||||
<object v-else-if="req.extension == '.pdf'" class="pdf" :data="raw"></object>
|
<object v-else-if="req.extension == '.pdf'" class="pdf" :data="raw"></object>
|
||||||
<a v-else-if="req.type == 'blob'" :href="download">
|
<a v-else-if="req.type == 'blob'" :href="download">
|
||||||
<h2 class="message">{{ $t('buttons.download') }} <i class="material-icons">file_download</i></h2>
|
<h2 class="message">
|
||||||
|
{{ $t('buttons.download') }}
|
||||||
|
<i class="material-icons">file_download</i>
|
||||||
|
</h2>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from "vuex";
|
||||||
import url from '@/utils/url'
|
import url from "@/utils/url";
|
||||||
import { baseURL } from '@/utils/constants'
|
import { baseURL } from "@/utils/constants";
|
||||||
import { files as api } from '@/api'
|
import { files as api } from "@/api";
|
||||||
import InfoButton from '@/components/buttons/Info'
|
import InfoButton from "@/components/buttons/Info";
|
||||||
import DeleteButton from '@/components/buttons/Delete'
|
import DeleteButton from "@/components/buttons/Delete";
|
||||||
import RenameButton from '@/components/buttons/Rename'
|
import RenameButton from "@/components/buttons/Rename";
|
||||||
import DownloadButton from '@/components/buttons/Download'
|
import DownloadButton from "@/components/buttons/Download";
|
||||||
import ExtendedImage from './ExtendedImage'
|
import ExtendedImage from "./ExtendedImage";
|
||||||
|
|
||||||
const mediaTypes = [
|
const mediaTypes = ["image", "video", "audio", "blob"];
|
||||||
"image",
|
|
||||||
"video",
|
|
||||||
"audio",
|
|
||||||
"blob"
|
|
||||||
]
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'preview',
|
name: "preview",
|
||||||
components: {
|
components: {
|
||||||
InfoButton,
|
InfoButton,
|
||||||
DeleteButton,
|
DeleteButton,
|
||||||
@ -69,92 +87,103 @@ export default {
|
|||||||
},
|
},
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
previousLink: '',
|
previousLink: "",
|
||||||
nextLink: '',
|
nextLink: "",
|
||||||
listing: null,
|
listing: null,
|
||||||
subtitles: []
|
subtitles: []
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['req', 'user', 'oldReq', 'jwt']),
|
...mapState(["req", "user", "oldReq", "jwt"]),
|
||||||
hasPrevious() {
|
hasPrevious() {
|
||||||
return (this.previousLink !== '')
|
return this.previousLink !== "";
|
||||||
},
|
},
|
||||||
hasNext() {
|
hasNext() {
|
||||||
return (this.nextLink !== '')
|
return this.nextLink !== "";
|
||||||
},
|
},
|
||||||
download() {
|
download() {
|
||||||
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`
|
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`;
|
||||||
|
},
|
||||||
|
compress() {
|
||||||
|
if (this.type === 'image') {
|
||||||
|
return `${baseURL}/api/compress${this.req.path}?auth=${this.jwt}`;
|
||||||
|
} else {
|
||||||
|
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
raw() {
|
raw() {
|
||||||
return `${this.download}&inline=true`
|
return `${this.compress}&inline=true`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
window.addEventListener('keyup', this.key)
|
window.addEventListener("keyup", this.key);
|
||||||
|
|
||||||
if (this.req.subtitles) {
|
if (this.req.subtitles) {
|
||||||
this.subtitles = this.req.subtitles.map(sub => `${baseURL}/api/raw${sub}?auth=${this.jwt}&inline=true`)
|
this.subtitles = this.req.subtitles.map(
|
||||||
|
sub => `${baseURL}/api/raw${sub}?auth=${this.jwt}&inline=true`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.oldReq.items) {
|
if (this.oldReq.items) {
|
||||||
this.updateLinks(this.oldReq.items)
|
this.updateLinks(this.oldReq.items);
|
||||||
} else {
|
} else {
|
||||||
const path = url.removeLastDir(this.$route.path)
|
const path = url.removeLastDir(this.$route.path);
|
||||||
const res = await api.fetch(path)
|
const res = await api.fetch(path);
|
||||||
this.updateLinks(res.items)
|
this.updateLinks(res.items);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$showError(e)
|
this.$showError(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
window.removeEventListener('keyup', this.key)
|
window.removeEventListener("keyup", this.key);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
back() {
|
back() {
|
||||||
let uri = url.removeLastDir(this.$route.path) + '/'
|
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||||
this.$router.push({ path: uri })
|
this.$router.push({ path: uri });
|
||||||
},
|
},
|
||||||
prev() {
|
prev() {
|
||||||
this.$router.push({ path: this.previousLink })
|
this.$router.push({ path: this.previousLink });
|
||||||
},
|
},
|
||||||
next() {
|
next() {
|
||||||
this.$router.push({ path: this.nextLink })
|
this.$router.push({ path: this.nextLink });
|
||||||
},
|
},
|
||||||
key(event) {
|
key(event) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
|
|
||||||
if (event.which === 13 || event.which === 39) { // right arrow
|
if (event.which === 13 || event.which === 39) {
|
||||||
if (this.hasNext) this.next()
|
// right arrow
|
||||||
} else if (event.which === 37) { // left arrow
|
if (this.hasNext) this.next();
|
||||||
if (this.hasPrevious) this.prev()
|
} else if (event.which === 37) {
|
||||||
|
// left arrow
|
||||||
|
if (this.hasPrevious) this.prev();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateLinks(items) {
|
updateLinks(items) {
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
if (items[i].name !== this.req.name) {
|
if (items[i].name !== this.req.name) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let j = i - 1; j >= 0; j--) {
|
for (let j = i - 1; j >= 0; j--) {
|
||||||
if (mediaTypes.includes(items[j].type)) {
|
if (mediaTypes.includes(items[j].type)) {
|
||||||
this.previousLink = items[j].url
|
this.previousLink = items[j].url;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let j = i + 1; j < items.length; j++) {
|
for (let j = i + 1; j < items.length; j++) {
|
||||||
if (mediaTypes.includes(items[j].type)) {
|
if (mediaTypes.includes(items[j].type)) {
|
||||||
this.nextLink = items[j].url
|
this.nextLink = items[j].url;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#listing h2 {
|
#listing h2 {
|
||||||
margin: 0 0 0 0.5em;
|
margin: 0 0 0 0.5em;
|
||||||
font-size: .9em;
|
font-size: 0.9em;
|
||||||
color: rgba(0, 0, 0, 0.38);
|
color: rgba(0, 0, 0, 0.38);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
@ -22,7 +22,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
color: #6f6f6f;
|
color: #6f6f6f;
|
||||||
transition: .1s ease background, .1s ease opacity;
|
transition: 0.1s ease background, 0.1s ease opacity;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@ -52,6 +52,13 @@
|
|||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#listing .item img {
|
||||||
|
width: 4em;
|
||||||
|
height: 4em;
|
||||||
|
margin-right: 0.1em;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
@ -64,7 +71,7 @@
|
|||||||
|
|
||||||
.message i {
|
.message i {
|
||||||
font-size: 2.5em;
|
font-size: 2.5em;
|
||||||
margin-bottom: .2em;
|
margin-bottom: 0.2em;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,14 +82,14 @@
|
|||||||
|
|
||||||
#listing.mosaic .item {
|
#listing.mosaic .item {
|
||||||
width: calc(33% - 1em);
|
width: calc(33% - 1em);
|
||||||
margin: .5em;
|
margin: 0.5em;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 1px 2px rgba(0, 0, 0, .12);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.mosaic .item:hover {
|
#listing.mosaic .item:hover {
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24) !important;
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.mosaic .header {
|
#listing.mosaic .header {
|
||||||
@ -116,7 +123,7 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing .item[aria-selected=true] {
|
#listing .item[aria-selected="true"] {
|
||||||
background: var(--blue) !important;
|
background: var(--blue) !important;
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
}
|
}
|
||||||
@ -129,6 +136,11 @@
|
|||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#listing.list .item div:first-of-type img {
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
#listing.list .item div:last-of-type {
|
#listing.list .item div:last-of-type {
|
||||||
width: calc(100% - 3em);
|
width: calc(100% - 3em);
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -151,14 +163,14 @@
|
|||||||
#listing.list .header i {
|
#listing.list .header i {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-left: .2em;
|
margin-left: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.list .item.header {
|
#listing.list .item.header {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
padding: .85em;
|
padding: 0.85em;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
@ -193,7 +205,7 @@
|
|||||||
|
|
||||||
#listing.list .header i {
|
#listing.list .header i {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: .1s ease all;
|
transition: 0.1s ease all;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.list .header p:hover i,
|
#listing.list .header p:hover i,
|
||||||
@ -215,7 +227,7 @@
|
|||||||
height: 4em;
|
height: 4em;
|
||||||
padding: 0.5em 0.5em 0.5em 1em;
|
padding: 0.5em 0.5em 0.5em 1em;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
transition: .2s ease bottom;
|
transition: 0.2s ease bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing #multiple-selection.active {
|
#listing #multiple-selection.active {
|
||||||
|
|||||||
1
go.mod
1
go.mod
@ -16,6 +16,7 @@ require (
|
|||||||
github.com/maruel/natural v0.0.0-20180416170133-dbcb3e2e8cf1
|
github.com/maruel/natural v0.0.0-20180416170133-dbcb3e2e8cf1
|
||||||
github.com/mholt/archiver v3.1.1+incompatible
|
github.com/mholt/archiver v3.1.1+incompatible
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
github.com/nwaples/rardecode v1.0.0 // indirect
|
github.com/nwaples/rardecode v1.0.0 // indirect
|
||||||
github.com/pelletier/go-toml v1.6.0
|
github.com/pelletier/go-toml v1.6.0
|
||||||
github.com/pierrec/lz4 v0.0.0-20190131084431-473cd7ce01a1 // indirect
|
github.com/pierrec/lz4 v0.0.0-20190131084431-473cd7ce01a1 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@ -141,6 +141,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
|||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||||
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg=
|
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg=
|
||||||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
|
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
|
||||||
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
|
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
|
||||||
|
|||||||
@ -59,6 +59,7 @@ func NewHandler(store *storage.Storage, server *settings.Server) (http.Handler,
|
|||||||
api.Handle("/settings", monkey(settingsPutHandler, "")).Methods("PUT")
|
api.Handle("/settings", monkey(settingsPutHandler, "")).Methods("PUT")
|
||||||
|
|
||||||
api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET")
|
api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET")
|
||||||
|
api.PathPrefix("/compress").Handler(monkey(compressHandler, "/api/compress")).Methods("GET")
|
||||||
api.PathPrefix("/command").Handler(monkey(commandsHandler, "/api/command")).Methods("GET")
|
api.PathPrefix("/command").Handler(monkey(commandsHandler, "/api/command")).Methods("GET")
|
||||||
api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET")
|
api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET")
|
||||||
|
|
||||||
|
|||||||
126
http/preview.go
Normal file
126
http/preview.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/files"
|
||||||
|
"github.com/nfnt/resize"
|
||||||
|
"image"
|
||||||
|
"image/gif"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
var compressHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
|
if !d.user.Perm.Download {
|
||||||
|
return http.StatusAccepted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := files.NewFileInfo(files.FileOptions{
|
||||||
|
Fs: d.user.Fs,
|
||||||
|
Path: r.URL.Path,
|
||||||
|
Modify: d.user.Perm.Modify,
|
||||||
|
Expand: true,
|
||||||
|
Checker: d,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errToStatus(err), err
|
||||||
|
}
|
||||||
|
|
||||||
|
if file.IsDir || file.Type != "image" {
|
||||||
|
return http.StatusNotFound, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return compressFileHandler(w, r, file)
|
||||||
|
})
|
||||||
|
|
||||||
|
func compressFileHandler(w http.ResponseWriter, r *http.Request, file *files.FileInfo) (int, error) {
|
||||||
|
fd, err := file.Fs.Open(file.Path)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
if r.URL.Query().Get("inline") == "true" {
|
||||||
|
w.Header().Set("Content-Disposition", "inline")
|
||||||
|
} else {
|
||||||
|
// As per RFC6266 section 4.3
|
||||||
|
w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(file.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := compressImageHandler(file, fd)
|
||||||
|
if err != nil {
|
||||||
|
return errToStatus(err), err
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Length", fmt.Sprintf("%d", buf.Len()))
|
||||||
|
w.Header().Add("Content-Type", mime.TypeByExtension(file.Extension))
|
||||||
|
io.Copy(w, buf)
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressImageHandler(file *files.FileInfo, fd io.Reader) (*bytes.Buffer, error) {
|
||||||
|
var (
|
||||||
|
buf *bytes.Buffer
|
||||||
|
m image.Image
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
switch file.Extension {
|
||||||
|
case ".jpg", ".jpeg":
|
||||||
|
buf, m, err = compressImage(jpeg.Decode, fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = jpeg.Encode(buf, m, nil)
|
||||||
|
break
|
||||||
|
case ".png":
|
||||||
|
buf, m, err = compressImage(png.Decode, fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = png.Encode(buf, m)
|
||||||
|
break
|
||||||
|
case ".gif":
|
||||||
|
buf, m, err = compressImage(gif.Decode, fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = gif.Encode(buf, m, nil)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return nil, errors.New("extension is not supported")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxSize = 1080
|
||||||
|
|
||||||
|
func compressImage(decode func(r io.Reader) (image.Image, error), fd io.Reader) (*bytes.Buffer, image.Image, error) {
|
||||||
|
img, err := decode(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
width := img.Bounds().Dx()
|
||||||
|
height := img.Bounds().Dy()
|
||||||
|
if width > maxSize && width > height {
|
||||||
|
width = maxSize
|
||||||
|
height = 0
|
||||||
|
} else if height > maxSize && height > width {
|
||||||
|
width = 0
|
||||||
|
height = maxSize
|
||||||
|
} else {
|
||||||
|
width = 0
|
||||||
|
height = 0
|
||||||
|
}
|
||||||
|
m := resize.Resize(uint(width), uint(height), img, resize.Lanczos3)
|
||||||
|
return buf, m, nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user