Merge branch 'develop'
This commit is contained in:
commit
9fcb205364
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "12.118.1-simkey-v2",
|
||||
"version": "12.118.1-simkey-v3",
|
||||
"codename": "indigo",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -910,6 +910,7 @@ onMounted(() => {
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
min-height: 90px;
|
||||
height: 250px;
|
||||
|
||||
&.withCw {
|
||||
padding-top: 8px;
|
||||
|
@ -76,12 +76,13 @@ function onMousedown(evt: Event) {
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), height 0.2s ease-out, transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
transform-origin: left top;
|
||||
}
|
||||
|
||||
.fade-enter-from, .fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
height: 1px;
|
||||
}
|
||||
</style>
|
||||
|
@ -101,11 +101,12 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popup-enter-active, .popup-leave-active {
|
||||
transition: opacity 0.3s, transform 0.3s !important;
|
||||
transition: opacity 0.3s, height 0.2s, transform 0.3s !important;
|
||||
}
|
||||
.popup-enter-from, .popup-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.fxxzrfni {
|
||||
|
@ -54,122 +54,108 @@
|
||||
</FormInput>
|
||||
</FormSection>
|
||||
|
||||
<FormButton primary class="_formBlock" @click="emojiGenerate">{{ $ts.emojiGenerate }}</FormButton>
|
||||
<FormButton primary class="_formBlock" @click="preview">{{ $ts.emojiGenerate }}</FormButton>
|
||||
|
||||
<FormSection>
|
||||
<template #label>{{ $ts.preview }}</template>
|
||||
<p><img :src="emojiUrl" class="img" :alt="emojiName"/></p>
|
||||
<MkLink v-if="emojiUrl" :url="emojiUrl">{{ emojiUrl }}</MkLink>
|
||||
<p><img :src="previewUrl" class="img" :alt="emojiName"/></p>
|
||||
</FormSection>
|
||||
<FormButton primary class="_formBlock" @click="emojiApproval">{{ $ts.emojiApproval }}</FormButton>
|
||||
<FormButton primary class="_formBlock" @click="uploadEmoji">{{ $ts.emojiApproval }}</FormButton>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormTextarea from '@/components/form/textarea.vue';
|
||||
import FormRadios from '@/components/form/radios.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import MkLink from '@/components/link.vue';
|
||||
<script lang="ts" setup>
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { computed, defineAsyncComponent, ref } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
import { defaultStore } from '@/store';
|
||||
import { stream } from '@/stream';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormButton from '@/components/ui/button.vue';
|
||||
import FormRadios from '@/components/form/radios.vue';
|
||||
import FormTextarea from '@/components/form/textarea.vue';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
definePageMetadata(computed(() => ({
|
||||
title: i18n.ts.emojiGen,
|
||||
icon: 'fas fa-laugh',
|
||||
})));
|
||||
const font = ref('rounded-x-mplus-1p-black');
|
||||
const text = ref('');
|
||||
const emojiName = ref('');
|
||||
const emojiAlign = ref('center');
|
||||
const emojiSizeFixed = ref(false);
|
||||
const emojiStretch = ref(true);
|
||||
const emojiColor = ref('90ee90');
|
||||
const previewUrl = ref('');
|
||||
const accentColors = [
|
||||
'#ff00ff',
|
||||
'#ff0080',
|
||||
'#D55353',
|
||||
'#ff8080',
|
||||
'#ff8040',
|
||||
'#ffff80',
|
||||
'#80ff80',
|
||||
'#90ee90',
|
||||
'#80ffff',
|
||||
'#0080ff',
|
||||
'#8080ff',
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormInput,
|
||||
FormTextarea,
|
||||
FormRadios,
|
||||
FormSection,
|
||||
FormSwitch,
|
||||
FormButton,
|
||||
MkLink,
|
||||
},
|
||||
const makeUrl = (): string => {
|
||||
const API_URL = 'https://emoji-gen.ninja/emoji';
|
||||
|
||||
emits: ['info'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
emojiName: '',
|
||||
text: '',
|
||||
emojiAlign: 'center',
|
||||
emojiSizeFixed: false,
|
||||
emojiStretch: false,
|
||||
font: 'notosans-mono-bold',
|
||||
emojiColor: '38BA91',
|
||||
emojiUrl: '',
|
||||
accentColors: ['#e36749', '#f29924', '#98c934', '#34c9a9', '#34a1c9', '#606df7', '#8d34c9', '#e84d83'],
|
||||
const query = {
|
||||
text: encodeURI(text.value),
|
||||
color: emojiColor.value + 'FF',
|
||||
back_color: '00000000',
|
||||
font: font.value,
|
||||
size_fixed: !emojiSizeFixed.value ? 'true' : 'false',
|
||||
align: 'center',
|
||||
stretch: !emojiStretch.value ? 'true' : 'false',
|
||||
public_fg: 'false',
|
||||
locale: 'ja',
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
},
|
||||
return API_URL + '?' + Object.entries(query).map(([key, value]) => `${key}=${value}`).join('&');
|
||||
};
|
||||
|
||||
emojiGenerate() {
|
||||
//https://emoji-gen.ninja/result?text=%E7%B5%B5%E6%96%87%0A%E5%AD%97%E3%80%82&color=EC71A1FF&back_color=00000000&font=notosans-mono-bold&size_fixed=false&align=center&stretch=true&public_fg=true&locale=ja
|
||||
const preview = (): void => {
|
||||
previewUrl.value = makeUrl();
|
||||
};
|
||||
|
||||
const apiUrl = 'https://emoji-gen.ninja/emoji';
|
||||
let query = { 'text': encodeURI(this.text), 'color': this.emojiColor.replace('#','') + 'FF', 'back_color': '00000000', 'font': this.font, 'size_fixed': this.emojiSizeFixed, 'align': this.emojiAlign, 'stretch': !this.emojiStretch, 'public_fg': 'false', 'locale': 'ja' };
|
||||
const setAccentColor = (color) => {
|
||||
emojiColor.value = color.replace('#', '');
|
||||
};
|
||||
|
||||
this.emojiUrl = apiUrl + '?' + Object.entries(query).map((e) => `${e[0]}=${e[1]}`).join('&');
|
||||
},
|
||||
const uploadFileFromUrlWithId = (url: string) => new Promise<string>(async resolve => {
|
||||
const marker = uuid();
|
||||
|
||||
emojiApproval: function () {
|
||||
//emojiUploadでは、絵文字をdrive/files/upload-from-urlでアップロードしたあと、emojiAddでリネーム、登録をして、emojiAddの戻り値(絵文字のid)を返す
|
||||
const emojiUpload = () => new Promise(async resolve => {
|
||||
const marker = Math.random().toString(); // TODO: UUIDとか使う
|
||||
|
||||
//先にコネクションを貼って監視する
|
||||
// 先にコネクションを貼り、アップロードが完了したかどうかを監視する
|
||||
const connection = stream.useChannel('main');
|
||||
connection.on('urlUploadFinished', async data => {
|
||||
if (data.marker === marker) {
|
||||
const fileId = await emojiAdd(data.file.id);
|
||||
resolve(fileId);
|
||||
|
||||
connection.on('urlUploadFinished', async response => {
|
||||
if (response.marker === marker) {
|
||||
resolve(response.file.id);
|
||||
connection.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
// 絵文字をアップロード
|
||||
await os.api('drive/files/upload-from-url', {
|
||||
url: this.emojiUrl,
|
||||
url,
|
||||
folderId: defaultStore.state.uploadFolder,
|
||||
marker,
|
||||
});
|
||||
});
|
||||
|
||||
//リネーム→登録→登録されたIDを返す
|
||||
const emojiAdd = (fileId) => new Promise<Record<string, any> | null>(async resolve => {
|
||||
await os.api('drive/files/update', {
|
||||
fileId,
|
||||
name: this.emojiName + '.png',
|
||||
});
|
||||
|
||||
const response = await os.api('admin/emoji/add', {
|
||||
fileId,
|
||||
});
|
||||
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
|
||||
//emoji関数 admin/emoji/listでは、idによる検索ができないため、自分のidをuntilIdに入れて1つ前のidを取得してからそれをsinceIdに指定して、絵文字情報をlist→objectで取得する
|
||||
const emoji = (emojiId) => new Promise<Record<string, any> | null>(async resolve => {
|
||||
const getEmojiObject = emojiId => new Promise<Record<string, any> | null>(async resolve => {
|
||||
const sinceId = await os.api('admin/emoji/list', {
|
||||
limit: 1,
|
||||
untilId: emojiId.id,
|
||||
});
|
||||
|
||||
if (!sinceId) {
|
||||
if (!sinceId || !sinceId[0] || !sinceId[0].id) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
@ -179,38 +165,47 @@ export default defineComponent({
|
||||
sinceId: sinceId[0].id,
|
||||
});
|
||||
|
||||
if (!id) {
|
||||
if (!id || !id[0]) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(id[0]);
|
||||
});
|
||||
|
||||
//edit関数には、emojiのobjectを渡す
|
||||
const edit = (emoji) => {
|
||||
os.popup(import('./emoji-edit-dialog.vue'), {
|
||||
emoji: emoji,
|
||||
});
|
||||
};
|
||||
|
||||
(async () => {
|
||||
await this.emojiGenerate();
|
||||
const emojiId = await emojiUpload();//emojiIdはファイルID emojiUploadはファイルIDを返す
|
||||
const emojiObj = await emoji(emojiId);//emojiObjはemojiオブジェクト emoji関数はemojiIdを引数に受け取りemojiオブジェクトを返す
|
||||
edit(emojiObj);
|
||||
})();
|
||||
},
|
||||
|
||||
setAccentColor(color) {
|
||||
this.emojiColor = color.replace('#', '');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const uploadEmoji = async () => {
|
||||
const emojiUrl = makeUrl();
|
||||
const fileId = await uploadFileFromUrlWithId(emojiUrl);
|
||||
|
||||
// ドライブにアップロードされたファイルをリネーム
|
||||
await os.api('drive/files/update', {
|
||||
fileId,
|
||||
name: uuid() + '.png',
|
||||
});
|
||||
|
||||
const emojiId = await os.api('admin/emoji/add', { fileId });
|
||||
const emoji = await getEmojiObject(emojiId);
|
||||
|
||||
if (!emoji) {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: i18n.ts.failedToUploadEmoji,
|
||||
});
|
||||
}
|
||||
|
||||
os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { emoji });
|
||||
};
|
||||
|
||||
definePageMetadata(computed(() => ({
|
||||
title: i18n.ts.emojiGen,
|
||||
icon: 'fas fa-kiss-wink-heart',
|
||||
})));
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.preview-img {
|
||||
height: 128px;
|
||||
}
|
||||
.cwepdizn {
|
||||
::v-deep(.cwepdizn-colors) {
|
||||
text-align: center;
|
||||
@ -263,4 +258,3 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -308,6 +308,10 @@ export const routes = [{
|
||||
path: '/emojis',
|
||||
name: 'emojis',
|
||||
component: page(() => import('./pages/admin/emojis.vue')),
|
||||
}, {
|
||||
path: '/emojigen',
|
||||
name: 'emojigen',
|
||||
component: page(() => import('./pages/admin/emojigen.vue')),
|
||||
}, {
|
||||
path: '/queue',
|
||||
name: 'queue',
|
||||
|
@ -1,9 +1,7 @@
|
||||
import { ref } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import { stream } from '@/stream';
|
||||
import { i18n } from '@/i18n';
|
||||
import { defaultStore } from '@/store';
|
||||
import { DriveFile } from 'misskey-js/built/entities';
|
||||
import { defaultStore } from '@/store';
|
||||
import { uploadFile } from '@/scripts/upload';
|
||||
|
||||
function select(src: any, label: string | null, multiple: boolean): Promise<DriveFile | DriveFile[]> {
|
||||
return new Promise((res, rej) => {
|
||||
@ -12,126 +10,17 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv
|
||||
const chooseFileFromPc = () => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'video/mp4';
|
||||
input.multiple = multiple;
|
||||
input.onchange = () => {
|
||||
const promises = Array.from(input.files).map(file => uploadFile(file, defaultStore.state.uploadFolder, undefined, keepOriginal.value));
|
||||
|
||||
//ここから
|
||||
// @ts-ignore
|
||||
function VideoConverter (videoFileData, targetFormat) {
|
||||
try {
|
||||
targetFormat = targetFormat.toLowerCase();
|
||||
let reader = new FileReader();
|
||||
return new Promise(resolve => {
|
||||
reader.onload = function (event) {
|
||||
let contentType = 'video/'+targetFormat;
|
||||
|
||||
// @ts-ignore
|
||||
let fileData: ArrayBuffer = event.target.result.split(',');
|
||||
let b64Data = fileData[1];
|
||||
let blob = getBlobFromBase64Data(b64Data, contentType);
|
||||
let blobUrl = URL.createObjectURL(blob);
|
||||
|
||||
let convertedVideo: any = {
|
||||
name: videoFileData.name.substring(0, videoFileData.name.lastIndexOf(".")),
|
||||
format: targetFormat,
|
||||
data: blob
|
||||
}
|
||||
// console.log("convertedVideo: ", convertedVideo);
|
||||
resolve(convertedVideo);
|
||||
}
|
||||
reader.readAsDataURL(videoFileData);
|
||||
Promise.all(promises).then(driveFiles => {
|
||||
res(multiple ? driveFiles : driveFiles[0]);
|
||||
}).catch(err => {
|
||||
// アップロードのエラーは uploadFile 内でハンドリングされているためアラートダイアログを出したりはしてはいけない
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
console.log("Error occurred while converting : ", e);
|
||||
}
|
||||
}
|
||||
|
||||
function getBlobFromBase64Data(b64Data, contentType, sliceSize=512) {
|
||||
const byteCharacters = atob(b64Data);
|
||||
const byteArrays = [];
|
||||
|
||||
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
||||
const slice = byteCharacters.slice(offset, offset + sliceSize);
|
||||
|
||||
const byteNumbers = new Array(slice.length);
|
||||
for (let i = 0; i < slice.length; i++) {
|
||||
byteNumbers[i] = slice.charCodeAt(i);
|
||||
}
|
||||
|
||||
const byteArray = new Uint8Array(byteNumbers);
|
||||
// @ts-ignore
|
||||
byteArrays.push(byteArray);
|
||||
}
|
||||
|
||||
const blob = new Blob(byteArrays, {type: contentType});
|
||||
return blob;
|
||||
}
|
||||
|
||||
//ここまで
|
||||
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Suvro Debnath
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
Array.from(input.files).map(async file => {
|
||||
if (file.type.indexOf('video/mp4') === 1) {
|
||||
os.toast('It is mp4 file');
|
||||
os.upload(file, defaultStore.state.uploadFolder).then(driveFiles => {
|
||||
res(driveFiles);
|
||||
}).catch(e => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: e
|
||||
});
|
||||
});
|
||||
return
|
||||
}
|
||||
|
||||
console.log('convert video');
|
||||
os.toast('Try convert to mp4');
|
||||
//convertVideo(file);
|
||||
let sourceVideoFile = file;
|
||||
let targetVideoFormat = 'mp4'
|
||||
console.log('start convert video');
|
||||
let convertedVideoDataObj = await VideoConverter(sourceVideoFile, targetVideoFormat);
|
||||
console.log('end convert video');
|
||||
|
||||
// @ts-ignore
|
||||
let convertedVideoFile = await new File([convertedVideoDataObj.data], convertedVideoDataObj.name + "." + convertedVideoDataObj.format);
|
||||
|
||||
os.upload(convertedVideoFile, defaultStore.state.uploadFolder).then(driveFiles => {
|
||||
res(driveFiles);
|
||||
}).catch(e => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: e
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// 一応廃棄
|
||||
(window as any).__misskey_input_ref__ = null;
|
||||
};
|
||||
@ -143,21 +32,10 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv
|
||||
input.click();
|
||||
};
|
||||
|
||||
os.popupMenu([label ? {
|
||||
text: label,
|
||||
type: 'label'
|
||||
} : undefined, {
|
||||
text: i18n.ts.upload,
|
||||
icon: 'fas fa-upload',
|
||||
action: chooseFileFromPc
|
||||
}], src);
|
||||
chooseFileFromPc();
|
||||
});
|
||||
}
|
||||
|
||||
export function selectFileEnc(src: any, label: string | null = null): Promise<DriveFile> {
|
||||
return select(src, label, false) as Promise<DriveFile>;
|
||||
}
|
||||
|
||||
export function selectFilesEnc(src: any, label: string | null = null): Promise<DriveFile[]> {
|
||||
return select(src, label, true) as Promise<DriveFile[]>;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user