アニメーション画像を無効にする際、サーバーサイドではなくクライアントサイドでURLを変更するように
This commit is contained in:
parent
f014b7ae0e
commit
861302f0fd
14 changed files with 40 additions and 56 deletions
|
@ -121,7 +121,7 @@ common:
|
||||||
use-avatar-reversi-stones: "リバーシの石にアバターを使う"
|
use-avatar-reversi-stones: "リバーシの石にアバターを使う"
|
||||||
verified-user: "公式アカウント"
|
verified-user: "公式アカウント"
|
||||||
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
|
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
|
||||||
do-not-autoplay-animation: "アニメーションを自動再生しない"
|
do-not-autoplay-animation: "アニメーション画像を再生しない"
|
||||||
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
|
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
|
||||||
always-show-nsfw: "常に閲覧注意のメディアを表示する"
|
always-show-nsfw: "常に閲覧注意のメディアを表示する"
|
||||||
always-mark-nsfw: "常にメディアを閲覧注意として投稿"
|
always-mark-nsfw: "常にメディアを閲覧注意として投稿"
|
||||||
|
|
9
src/client/app/common/scripts/get-static-image-url.ts
Normal file
9
src/client/app/common/scripts/get-static-image-url.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { url as instanceUrl } from '../../config';
|
||||||
|
|
||||||
|
export function getStaticImageUrl(url: string): string {
|
||||||
|
const u = new URL(url);
|
||||||
|
const dummy = `${u.host}${u.pathname}`; // 拡張子がないとキャッシュしてくれないCDNがあるので
|
||||||
|
let result = `${instanceUrl}/proxy/${dummy}?url=${encodeURIComponent(u.href)}`;
|
||||||
|
result += '&static=1';
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: {
|
props: {
|
||||||
|
@ -47,6 +48,11 @@ export default Vue.extend({
|
||||||
borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
|
borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
url(): string {
|
||||||
|
return this.$store.state.device.doNotAutoplayAnimation
|
||||||
|
? getStaticImageUrl(this.user.avatarUrl)
|
||||||
|
: this.user.avatarUrl;
|
||||||
|
},
|
||||||
icon(): any {
|
icon(): any {
|
||||||
return {
|
return {
|
||||||
backgroundColor: this.lightmode
|
backgroundColor: this.lightmode
|
||||||
|
@ -54,7 +60,7 @@ export default Vue.extend({
|
||||||
: this.user.avatarColor && this.user.avatarColor.length == 3
|
: this.user.avatarColor && this.user.avatarColor.length == 3
|
||||||
? `rgb(${this.user.avatarColor.join(',')})`
|
? `rgb(${this.user.avatarColor.join(',')})`
|
||||||
: null,
|
: null,
|
||||||
backgroundImage: this.lightmode ? null : `url(${this.user.avatarUrl})`,
|
backgroundImage: this.lightmode ? null : `url(${this.url})`,
|
||||||
borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
|
borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
// スクリプトサイズがデカい
|
// スクリプトサイズがデカい
|
||||||
//import { lib } from 'emojilib';
|
//import { lib } from 'emojilib';
|
||||||
|
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: {
|
props: {
|
||||||
|
@ -54,7 +55,9 @@ export default Vue.extend({
|
||||||
const customEmoji = this.customEmojis.find(x => x.name == this.name);
|
const customEmoji = this.customEmojis.find(x => x.name == this.name);
|
||||||
if (customEmoji) {
|
if (customEmoji) {
|
||||||
this.customEmoji = customEmoji;
|
this.customEmoji = customEmoji;
|
||||||
this.url = customEmoji.url;
|
this.url = this.$store.state.device.doNotAutoplayAnimation
|
||||||
|
? getStaticImageUrl(customEmoji.url)
|
||||||
|
: customEmoji.url;
|
||||||
} else {
|
} else {
|
||||||
//const emoji = lib[this.name];
|
//const emoji = lib[this.name];
|
||||||
//if (emoji) {
|
//if (emoji) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
import ImageViewer from './image-viewer.vue';
|
import ImageViewer from './image-viewer.vue';
|
||||||
|
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('common/views/components/media-image.vue'),
|
i18n: i18n('common/views/components/media-image.vue'),
|
||||||
|
@ -36,7 +37,11 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
computed: {
|
computed: {
|
||||||
style(): any {
|
style(): any {
|
||||||
let url = `url(${this.image.thumbnailUrl})`;
|
let url = `url(${
|
||||||
|
this.$store.state.device.doNotAutoplayAnimation
|
||||||
|
? getStaticImageUrl(this.image.thumbnailUrl)
|
||||||
|
: this.image.thumbnailUrl
|
||||||
|
})`;
|
||||||
|
|
||||||
if (this.$store.state.device.loadRemoteMedia || this.$store.state.device.lightmode) {
|
if (this.$store.state.device.loadRemoteMedia || this.$store.state.device.lightmode) {
|
||||||
url = null;
|
url = null;
|
||||||
|
|
|
@ -518,8 +518,8 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
doNotAutoplayAnimation: {
|
doNotAutoplayAnimation: {
|
||||||
get() { return !!this.$store.state.settings.doNotAutoplayAnimation; },
|
get() { return this.$store.state.device.doNotAutoplayAnimation; },
|
||||||
set(value) { this.$store.dispatch('settings/set', { key: 'doNotAutoplayAnimation', value }); }
|
set(value) { this.$store.commit('device/set', { key: 'doNotAutoplayAnimation', value }); }
|
||||||
},
|
},
|
||||||
|
|
||||||
remainDeletedNote: {
|
remainDeletedNote: {
|
||||||
|
|
|
@ -315,8 +315,8 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
doNotAutoplayAnimation: {
|
doNotAutoplayAnimation: {
|
||||||
get() { return !!this.$store.state.settings.doNotAutoplayAnimation; },
|
get() { return this.$store.state.device.doNotAutoplayAnimation; },
|
||||||
set(value) { this.$store.dispatch('settings/set', { key: 'doNotAutoplayAnimation', value }); }
|
set(value) { this.$store.commit('device/set', { key: 'doNotAutoplayAnimation', value }); }
|
||||||
},
|
},
|
||||||
|
|
||||||
showReplyTarget: {
|
showReplyTarget: {
|
||||||
|
|
|
@ -3,7 +3,6 @@ import createPersistedState from 'vuex-persistedstate';
|
||||||
import * as nestedProperty from 'nested-property';
|
import * as nestedProperty from 'nested-property';
|
||||||
|
|
||||||
import MiOS from './mios';
|
import MiOS from './mios';
|
||||||
import { hostname } from './config';
|
|
||||||
import { erase } from '../../prelude/array';
|
import { erase } from '../../prelude/array';
|
||||||
import getNoteSummary from '../../misc/get-note-summary';
|
import getNoteSummary from '../../misc/get-note-summary';
|
||||||
|
|
||||||
|
@ -70,7 +69,8 @@ const defaultDeviceSettings = {
|
||||||
mobileNotificationPosition: 'bottom',
|
mobileNotificationPosition: 'bottom',
|
||||||
deckTemporaryColumn: null,
|
deckTemporaryColumn: null,
|
||||||
deckDefault: false,
|
deckDefault: false,
|
||||||
useOsDefaultEmojis: false
|
useOsDefaultEmojis: false,
|
||||||
|
doNotAutoplayAnimation: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export default (os: MiOS) => new Vuex.Store({
|
export default (os: MiOS) => new Vuex.Store({
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
import { URL } from 'url';
|
|
||||||
import config from '../config';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* avatar, thumbnail, custom-emoji 等のURLをクライアント設定等によって置き換える
|
|
||||||
*/
|
|
||||||
export default function(url: string, me: any) {
|
|
||||||
if (url == null) return url;
|
|
||||||
|
|
||||||
// アニメーション再生無効
|
|
||||||
if (me && me.clientSettings && me.clientSettings.doNotAutoplayAnimation) {
|
|
||||||
const u = new URL(url);
|
|
||||||
const dummy = `${u.host}${u.pathname}`; // 拡張子がないとキャッシュしてくれないCDNがあるので
|
|
||||||
let result = `${config.url}/proxy/${dummy}?url=${encodeURI(u.href)}`;
|
|
||||||
result += '&static=1';
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
|
|
@ -1,11 +1,10 @@
|
||||||
import * as mongo from 'mongodb';
|
import * as mongo from 'mongodb';
|
||||||
import * as deepcopy from 'deepcopy';
|
import * as deepcopy from 'deepcopy';
|
||||||
import { pack as packFolder } from './drive-folder';
|
import { pack as packFolder } from './drive-folder';
|
||||||
import { pack as packUser, IUser } from './user';
|
import { pack as packUser } from './user';
|
||||||
import monkDb, { nativeDbConn, dbLogger } from '../db/mongodb';
|
import monkDb, { nativeDbConn, dbLogger } from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
import isObjectId from '../misc/is-objectid';
|
||||||
import getDriveFileUrl, { getOriginalUrl } from '../misc/get-drive-file-url';
|
import getDriveFileUrl, { getOriginalUrl } from '../misc/get-drive-file-url';
|
||||||
import wrapUrl from '../misc/wrap-url';
|
|
||||||
|
|
||||||
const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
|
const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
|
||||||
DriveFile.createIndex('md5');
|
DriveFile.createIndex('md5');
|
||||||
|
@ -134,7 +133,6 @@ export const packMany = (
|
||||||
detail?: boolean
|
detail?: boolean
|
||||||
self?: boolean,
|
self?: boolean,
|
||||||
withUser?: boolean,
|
withUser?: boolean,
|
||||||
me?: string | mongo.ObjectID | IUser,
|
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
return Promise.all(files.map(f => pack(f, options)));
|
return Promise.all(files.map(f => pack(f, options)));
|
||||||
|
@ -149,7 +147,6 @@ export const pack = (
|
||||||
detail?: boolean,
|
detail?: boolean,
|
||||||
self?: boolean,
|
self?: boolean,
|
||||||
withUser?: boolean,
|
withUser?: boolean,
|
||||||
me?: string | mongo.ObjectID | IUser,
|
|
||||||
}
|
}
|
||||||
) => new Promise<any>(async (resolve, reject) => {
|
) => new Promise<any>(async (resolve, reject) => {
|
||||||
const opts = Object.assign({
|
const opts = Object.assign({
|
||||||
|
@ -192,11 +189,6 @@ export const pack = (
|
||||||
|
|
||||||
_target.url = getDriveFileUrl(_file);
|
_target.url = getDriveFileUrl(_file);
|
||||||
_target.thumbnailUrl = getDriveFileUrl(_file, true);
|
_target.thumbnailUrl = getDriveFileUrl(_file, true);
|
||||||
|
|
||||||
if (_target.thumbnailUrl != null) {
|
|
||||||
_target.thumbnailUrl = wrapUrl(_target.thumbnailUrl, options.me);
|
|
||||||
}
|
|
||||||
|
|
||||||
_target.isRemote = _file.metadata.isRemote;
|
_target.isRemote = _file.metadata.isRemote;
|
||||||
|
|
||||||
if (_target.properties == null) _target.properties = {};
|
if (_target.properties == null) _target.properties = {};
|
||||||
|
|
|
@ -11,7 +11,6 @@ import Reaction from './note-reaction';
|
||||||
import { packMany as packFileMany, IDriveFile } from './drive-file';
|
import { packMany as packFileMany, IDriveFile } from './drive-file';
|
||||||
import Following from './following';
|
import Following from './following';
|
||||||
import Emoji from './emoji';
|
import Emoji from './emoji';
|
||||||
import wrapUrl from '../misc/wrap-url';
|
|
||||||
|
|
||||||
const Note = db.get<INote>('notes');
|
const Note = db.get<INote>('notes');
|
||||||
Note.createIndex('uri', { sparse: true, unique: true });
|
Note.createIndex('uri', { sparse: true, unique: true });
|
||||||
|
@ -248,14 +247,11 @@ export const pack = async (
|
||||||
fields: { _id: false }
|
fields: { _id: false }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
_note.emojis = (await Emoji.find({
|
_note.emojis = Emoji.find({
|
||||||
name: { $in: _note.emojis },
|
name: { $in: _note.emojis },
|
||||||
host: host
|
host: host
|
||||||
}, {
|
}, {
|
||||||
fields: { _id: false }
|
fields: { _id: false }
|
||||||
})).map(emoji => async () => {
|
|
||||||
emoji.url = await wrapUrl(emoji.url, me);
|
|
||||||
return emoji;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,7 +274,7 @@ export const pack = async (
|
||||||
if (_note.geo) delete _note.geo.type;
|
if (_note.geo) delete _note.geo.type;
|
||||||
|
|
||||||
// Populate user
|
// Populate user
|
||||||
_note.user = packUser(_note.userId, me);
|
_note.user = packUser(_note.userId, meId);
|
||||||
|
|
||||||
// Populate app
|
// Populate app
|
||||||
if (_note.appId) {
|
if (_note.appId) {
|
||||||
|
@ -286,7 +282,7 @@ export const pack = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate files
|
// Populate files
|
||||||
_note.files = packFileMany(_note.fileIds || [], { me });
|
_note.files = packFileMany(_note.fileIds || []);
|
||||||
|
|
||||||
// Some counts
|
// Some counts
|
||||||
_note.renoteCount = _note.renoteCount || 0;
|
_note.renoteCount = _note.renoteCount || 0;
|
||||||
|
|
|
@ -12,7 +12,6 @@ import config from '../config';
|
||||||
import FollowRequest from './follow-request';
|
import FollowRequest from './follow-request';
|
||||||
import fetchMeta from '../misc/fetch-meta';
|
import fetchMeta from '../misc/fetch-meta';
|
||||||
import Emoji from './emoji';
|
import Emoji from './emoji';
|
||||||
import wrapUrl from '../misc/wrap-url';
|
|
||||||
|
|
||||||
const User = db.get<IUser>('users');
|
const User = db.get<IUser>('users');
|
||||||
|
|
||||||
|
@ -345,8 +344,6 @@ export const pack = (
|
||||||
|
|
||||||
if (_user.avatarUrl == null) {
|
if (_user.avatarUrl == null) {
|
||||||
_user.avatarUrl = `${config.drive_url}/default-avatar.jpg`;
|
_user.avatarUrl = `${config.drive_url}/default-avatar.jpg`;
|
||||||
} else {
|
|
||||||
_user.avatarUrl = wrapUrl(_user.avatarUrl, me);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!meId || !meId.equals(_user.id) || !opts.detail) {
|
if (!meId || !meId.equals(_user.id) || !opts.detail) {
|
||||||
|
@ -371,7 +368,7 @@ export const pack = (
|
||||||
if (opts.detail) {
|
if (opts.detail) {
|
||||||
if (_user.pinnedNoteIds) {
|
if (_user.pinnedNoteIds) {
|
||||||
// Populate pinned notes
|
// Populate pinned notes
|
||||||
_user.pinnedNotes = packNoteMany(_user.pinnedNoteIds, me, {
|
_user.pinnedNotes = packNoteMany(_user.pinnedNoteIds, meId, {
|
||||||
detail: true
|
detail: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -400,14 +397,11 @@ export const pack = (
|
||||||
|
|
||||||
// カスタム絵文字添付
|
// カスタム絵文字添付
|
||||||
if (_user.emojis) {
|
if (_user.emojis) {
|
||||||
_user.emojis = (await Emoji.find({
|
_user.emojis = Emoji.find({
|
||||||
name: { $in: _user.emojis },
|
name: { $in: _user.emojis },
|
||||||
host: _user.host
|
host: _user.host
|
||||||
}, {
|
}, {
|
||||||
fields: { _id: false }
|
fields: { _id: false }
|
||||||
})).map(emoji => {
|
|
||||||
emoji.url = wrapUrl(emoji.url, me);
|
|
||||||
return emoji;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,5 +65,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
|
||||||
sort: sort
|
sort: sort
|
||||||
});
|
});
|
||||||
|
|
||||||
res(await packMany(files, { self: true, me: user }));
|
res(await packMany(files, { self: true }));
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -11,7 +11,6 @@ import { IImage, ConvertToPng } from '../../services/drive/image-processor';
|
||||||
|
|
||||||
export async function proxyMedia(ctx: Koa.BaseContext) {
|
export async function proxyMedia(ctx: Koa.BaseContext) {
|
||||||
const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url;
|
const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url;
|
||||||
console.log(url);
|
|
||||||
|
|
||||||
// Create temp file
|
// Create temp file
|
||||||
const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
|
const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
|
||||||
|
|
Loading…
Reference in a new issue