アニメーション画像を無効にする際、サーバーサイドではなくクライアントサイドでURLを変更するように

This commit is contained in:
syuilo 2019-02-05 03:51:54 +09:00
parent f014b7ae0e
commit 861302f0fd
No known key found for this signature in database
GPG key ID: BDC4C49D06AB9D69
14 changed files with 40 additions and 56 deletions

View file

@ -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: "常にメディアを閲覧注意として投稿"

View 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;
}

View file

@ -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
}; };
} }

View file

@ -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) {

View file

@ -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;

View file

@ -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: {

View file

@ -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: {

View file

@ -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({

View file

@ -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;
}

View file

@ -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 = {};

View file

@ -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;

View file

@ -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;
}); });
} }

View file

@ -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 }));
})); }));

View file

@ -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) => {