2016-11-06 19:28:37 +01:00
|
|
|
/* eslint-env browser */
|
|
|
|
|
import statusPosterService from '../../services/status_poster/status_poster.service.js'
|
2018-12-10 09:50:04 +03:00
|
|
|
import fileSizeFormatService from '../../services/file_size_format/file_size_format.js'
|
|
|
|
|
|
2020-10-19 19:38:49 +03:00
|
|
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
|
|
|
|
import { faUpload, faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
|
|
|
|
|
|
|
|
|
library.add(
|
|
|
|
|
faUpload,
|
|
|
|
|
faCircleNotch
|
|
|
|
|
)
|
|
|
|
|
|
2016-11-06 19:28:37 +01:00
|
|
|
const mediaUpload = {
|
2017-02-21 15:13:19 +02:00
|
|
|
data () {
|
|
|
|
|
return {
|
2020-06-08 18:22:17 +02:00
|
|
|
uploadCount: 0,
|
2019-02-04 10:45:26 -05:00
|
|
|
uploadReady: true
|
2017-02-21 15:13:19 +02:00
|
|
|
}
|
|
|
|
|
},
|
2020-06-08 18:22:17 +02:00
|
|
|
computed: {
|
|
|
|
|
uploading () {
|
|
|
|
|
return this.uploadCount > 0
|
|
|
|
|
}
|
|
|
|
|
},
|
2017-02-21 15:13:19 +02:00
|
|
|
methods: {
|
2023-04-24 21:57:31 +03:00
|
|
|
onClick () {
|
|
|
|
|
if (this.uploadReady) {
|
|
|
|
|
this.$refs.input.click()
|
|
|
|
|
}
|
|
|
|
|
},
|
2025-01-17 16:33:26 +04:00
|
|
|
async resizeImage (file) {
|
|
|
|
|
// Skip if not an image or if it's a GIF
|
|
|
|
|
if (!file.type.startsWith('image/') || file.type === 'image/gif') {
|
|
|
|
|
return file
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-21 09:24:18 +04:00
|
|
|
// Skip if image compression is disabled
|
|
|
|
|
if (!this.$store.getters.mergedConfig.imageCompression) {
|
|
|
|
|
return file
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-17 16:33:26 +04:00
|
|
|
// For PNGs, check if animated
|
|
|
|
|
if (file.type === 'image/png') {
|
|
|
|
|
const isAnimated = await this.isAnimatedPng(file)
|
|
|
|
|
if (isAnimated) {
|
|
|
|
|
return file
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
const img = new Image()
|
|
|
|
|
img.onload = () => {
|
|
|
|
|
// Calculate new dimensions
|
|
|
|
|
let width = img.width
|
|
|
|
|
let height = img.height
|
|
|
|
|
const maxSize = 2048
|
|
|
|
|
|
|
|
|
|
if (width > maxSize || height > maxSize) {
|
|
|
|
|
if (width > height) {
|
|
|
|
|
height = Math.round((height * maxSize) / width)
|
|
|
|
|
width = maxSize
|
|
|
|
|
} else {
|
|
|
|
|
width = Math.round((width * maxSize) / height)
|
|
|
|
|
height = maxSize
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create canvas and resize
|
|
|
|
|
const canvas = document.createElement('canvas')
|
|
|
|
|
canvas.width = width
|
|
|
|
|
canvas.height = height
|
|
|
|
|
const ctx = canvas.getContext('2d')
|
|
|
|
|
ctx.drawImage(img, 0, 0, width, height)
|
|
|
|
|
|
|
|
|
|
// Check WebP support by trying to create a WebP canvas
|
|
|
|
|
const testCanvas = document.createElement('canvas')
|
|
|
|
|
const supportsWebP = testCanvas.toDataURL('image/webp').startsWith('data:image/webp')
|
|
|
|
|
|
2025-01-29 18:09:13 +04:00
|
|
|
// Convert to WebP if supported and alwaysUseJpeg is false, otherwise JPEG
|
|
|
|
|
const type = (!this.$store.getters.mergedConfig.alwaysUseJpeg && supportsWebP) ? 'image/webp' : 'image/jpeg'
|
|
|
|
|
const extension = type === 'image/webp' ? '.webp' : '.jpg'
|
2025-01-17 16:33:26 +04:00
|
|
|
|
|
|
|
|
// Remove the original extension and add new one
|
|
|
|
|
const newFileName = file.name.replace(/\.[^/.]+$/, '') + extension
|
|
|
|
|
|
|
|
|
|
canvas.toBlob((blob) => {
|
|
|
|
|
resolve(new File([blob], newFileName, {
|
2025-01-20 13:43:15 +02:00
|
|
|
type,
|
2025-01-17 16:33:26 +04:00
|
|
|
lastModified: Date.now()
|
|
|
|
|
}))
|
|
|
|
|
}, type, 0.85)
|
|
|
|
|
}
|
|
|
|
|
img.src = URL.createObjectURL(file)
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
async isAnimatedPng (file) {
|
|
|
|
|
const buffer = await file.arrayBuffer()
|
|
|
|
|
const view = new Uint8Array(buffer)
|
|
|
|
|
// Look for animated PNG chunks (acTL)
|
|
|
|
|
for (let i = 0; i < view.length - 8; i++) {
|
|
|
|
|
if (view[i] === 0x61 && // a
|
|
|
|
|
view[i + 1] === 0x63 && // c
|
|
|
|
|
view[i + 2] === 0x54 && // T
|
|
|
|
|
view[i + 3] === 0x4C) { // L
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
},
|
|
|
|
|
async uploadFile (file) {
|
2017-02-21 15:13:19 +02:00
|
|
|
const self = this
|
|
|
|
|
const store = this.$store
|
2018-12-08 18:23:21 +03:00
|
|
|
if (file.size > store.state.instance.uploadlimit) {
|
2018-12-10 17:06:32 +03:00
|
|
|
const filesize = fileSizeFormatService.fileSizeFormat(file.size)
|
|
|
|
|
const allowedsize = fileSizeFormatService.fileSizeFormat(store.state.instance.uploadlimit)
|
2019-07-05 10:02:14 +03:00
|
|
|
self.$emit('upload-failed', 'file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit })
|
2018-12-08 18:23:21 +03:00
|
|
|
return
|
|
|
|
|
}
|
2025-01-17 16:33:26 +04:00
|
|
|
|
|
|
|
|
// Resize image if needed
|
|
|
|
|
const processedFile = await this.resizeImage(file)
|
2016-11-07 15:04:27 +01:00
|
|
|
const formData = new FormData()
|
2025-01-17 16:33:26 +04:00
|
|
|
formData.append('file', processedFile)
|
2016-11-13 18:26:10 +01:00
|
|
|
|
|
|
|
|
self.$emit('uploading')
|
2020-06-08 18:22:17 +02:00
|
|
|
self.uploadCount++
|
2016-11-13 18:26:10 +01:00
|
|
|
|
2016-11-06 19:28:37 +01:00
|
|
|
statusPosterService.uploadMedia({ store, formData })
|
|
|
|
|
.then((fileData) => {
|
|
|
|
|
self.$emit('uploaded', fileData)
|
2020-06-08 18:22:17 +02:00
|
|
|
self.decreaseUploadCount()
|
2022-07-31 12:35:48 +03:00
|
|
|
}, (error) => {
|
|
|
|
|
console.error('Error uploading file', error)
|
2018-12-12 16:38:01 +03:00
|
|
|
self.$emit('upload-failed', 'default')
|
2020-06-08 18:22:17 +02:00
|
|
|
self.decreaseUploadCount()
|
2016-11-06 19:28:37 +01:00
|
|
|
})
|
2017-02-22 14:53:05 +02:00
|
|
|
},
|
2020-06-08 18:26:16 +02:00
|
|
|
decreaseUploadCount () {
|
2020-06-08 18:22:17 +02:00
|
|
|
this.uploadCount--
|
|
|
|
|
if (this.uploadCount === 0) {
|
|
|
|
|
this.$emit('all-uploaded')
|
|
|
|
|
}
|
|
|
|
|
},
|
2019-02-04 10:45:26 -05:00
|
|
|
clearFile () {
|
|
|
|
|
this.uploadReady = false
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.uploadReady = true
|
|
|
|
|
})
|
|
|
|
|
},
|
2020-06-08 18:22:17 +02:00
|
|
|
multiUpload (files) {
|
|
|
|
|
for (const file of files) {
|
2019-02-04 10:45:26 -05:00
|
|
|
this.uploadFile(file)
|
|
|
|
|
}
|
2020-06-08 18:22:17 +02:00
|
|
|
},
|
|
|
|
|
change ({ target }) {
|
|
|
|
|
this.multiUpload(target.files)
|
2017-02-21 15:13:19 +02:00
|
|
|
}
|
2016-11-13 18:26:10 +01:00
|
|
|
},
|
2023-04-24 21:57:31 +03:00
|
|
|
props: {
|
|
|
|
|
dropFiles: Object,
|
|
|
|
|
disabled: Boolean,
|
|
|
|
|
normalButton: Boolean,
|
|
|
|
|
acceptTypes: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: '*/*'
|
|
|
|
|
}
|
|
|
|
|
},
|
2017-02-21 15:13:19 +02:00
|
|
|
watch: {
|
2022-07-31 12:35:48 +03:00
|
|
|
dropFiles: function (fileInfos) {
|
2017-02-21 21:48:48 +01:00
|
|
|
if (!this.uploading) {
|
2020-06-08 18:22:17 +02:00
|
|
|
this.multiUpload(fileInfos)
|
2017-02-21 21:48:48 +01:00
|
|
|
}
|
2016-11-13 18:26:10 +01:00
|
|
|
}
|
2016-11-06 19:28:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default mediaUpload
|