アニメーション画像を無効にする際、サーバーサイドではなくクライアントサイドでURLを変更するように
This commit is contained in:
		
							parent
							
								
									f014b7ae0e
								
							
						
					
					
						commit
						861302f0fd
					
				
					 14 changed files with 40 additions and 56 deletions
				
			
		
							
								
								
									
										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">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +48,11 @@ export default Vue.extend({
 | 
			
		|||
				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 {
 | 
			
		||||
			return {
 | 
			
		||||
				backgroundColor: this.lightmode
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +60,7 @@ export default Vue.extend({
 | 
			
		|||
					: this.user.avatarColor && this.user.avatarColor.length == 3
 | 
			
		||||
						? `rgb(${this.user.avatarColor.join(',')})`
 | 
			
		||||
						: null,
 | 
			
		||||
				backgroundImage: this.lightmode ? null : `url(${this.user.avatarUrl})`,
 | 
			
		||||
				backgroundImage: this.lightmode ? null : `url(${this.url})`,
 | 
			
		||||
				borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@
 | 
			
		|||
import Vue from 'vue';
 | 
			
		||||
// スクリプトサイズがデカい
 | 
			
		||||
//import { lib } from 'emojilib';
 | 
			
		||||
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +55,9 @@ export default Vue.extend({
 | 
			
		|||
			const customEmoji = this.customEmojis.find(x => x.name == this.name);
 | 
			
		||||
			if (customEmoji) {
 | 
			
		||||
				this.customEmoji = customEmoji;
 | 
			
		||||
				this.url = customEmoji.url;
 | 
			
		||||
				this.url = this.$store.state.device.doNotAutoplayAnimation
 | 
			
		||||
					? getStaticImageUrl(customEmoji.url)
 | 
			
		||||
					: customEmoji.url;
 | 
			
		||||
			} else {
 | 
			
		||||
				//const emoji = lib[this.name];
 | 
			
		||||
				//if (emoji) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@
 | 
			
		|||
import Vue from 'vue';
 | 
			
		||||
import i18n from '../../../i18n';
 | 
			
		||||
import ImageViewer from './image-viewer.vue';
 | 
			
		||||
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	i18n: i18n('common/views/components/media-image.vue'),
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +37,11 @@ export default Vue.extend({
 | 
			
		|||
	}
 | 
			
		||||
	computed: {
 | 
			
		||||
		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) {
 | 
			
		||||
				url = null;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -518,8 +518,8 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
 | 
			
		||||
		doNotAutoplayAnimation: {
 | 
			
		||||
			get() { return !!this.$store.state.settings.doNotAutoplayAnimation; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'doNotAutoplayAnimation', value }); }
 | 
			
		||||
			get() { return this.$store.state.device.doNotAutoplayAnimation; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'doNotAutoplayAnimation', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		remainDeletedNote: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -315,8 +315,8 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
 | 
			
		||||
		doNotAutoplayAnimation: {
 | 
			
		||||
			get() { return !!this.$store.state.settings.doNotAutoplayAnimation; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'doNotAutoplayAnimation', value }); }
 | 
			
		||||
			get() { return this.$store.state.device.doNotAutoplayAnimation; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'doNotAutoplayAnimation', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showReplyTarget: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,6 @@ import createPersistedState from 'vuex-persistedstate';
 | 
			
		|||
import * as nestedProperty from 'nested-property';
 | 
			
		||||
 | 
			
		||||
import MiOS from './mios';
 | 
			
		||||
import { hostname } from './config';
 | 
			
		||||
import { erase } from '../../prelude/array';
 | 
			
		||||
import getNoteSummary from '../../misc/get-note-summary';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +69,8 @@ const defaultDeviceSettings = {
 | 
			
		|||
	mobileNotificationPosition: 'bottom',
 | 
			
		||||
	deckTemporaryColumn: null,
 | 
			
		||||
	deckDefault: false,
 | 
			
		||||
	useOsDefaultEmojis: false
 | 
			
		||||
	useOsDefaultEmojis: false,
 | 
			
		||||
	doNotAutoplayAnimation: false
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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 deepcopy from 'deepcopy';
 | 
			
		||||
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 isObjectId from '../misc/is-objectid';
 | 
			
		||||
import getDriveFileUrl, { getOriginalUrl } from '../misc/get-drive-file-url';
 | 
			
		||||
import wrapUrl from '../misc/wrap-url';
 | 
			
		||||
 | 
			
		||||
const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
 | 
			
		||||
DriveFile.createIndex('md5');
 | 
			
		||||
| 
						 | 
				
			
			@ -134,7 +133,6 @@ export const packMany = (
 | 
			
		|||
		detail?: boolean
 | 
			
		||||
		self?: boolean,
 | 
			
		||||
		withUser?: boolean,
 | 
			
		||||
		me?: string | mongo.ObjectID | IUser,
 | 
			
		||||
	}
 | 
			
		||||
) => {
 | 
			
		||||
	return Promise.all(files.map(f => pack(f, options)));
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +147,6 @@ export const pack = (
 | 
			
		|||
		detail?: boolean,
 | 
			
		||||
		self?: boolean,
 | 
			
		||||
		withUser?: boolean,
 | 
			
		||||
		me?: string | mongo.ObjectID | IUser,
 | 
			
		||||
	}
 | 
			
		||||
) => new Promise<any>(async (resolve, reject) => {
 | 
			
		||||
	const opts = Object.assign({
 | 
			
		||||
| 
						 | 
				
			
			@ -192,11 +189,6 @@ export const pack = (
 | 
			
		|||
 | 
			
		||||
	_target.url = getDriveFileUrl(_file);
 | 
			
		||||
	_target.thumbnailUrl = getDriveFileUrl(_file, true);
 | 
			
		||||
 | 
			
		||||
	if (_target.thumbnailUrl != null) {
 | 
			
		||||
		_target.thumbnailUrl = wrapUrl(_target.thumbnailUrl, options.me);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_target.isRemote = _file.metadata.isRemote;
 | 
			
		||||
 | 
			
		||||
	if (_target.properties == null) _target.properties = {};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,6 @@ import Reaction from './note-reaction';
 | 
			
		|||
import { packMany as packFileMany, IDriveFile } from './drive-file';
 | 
			
		||||
import Following from './following';
 | 
			
		||||
import Emoji from './emoji';
 | 
			
		||||
import wrapUrl from '../misc/wrap-url';
 | 
			
		||||
 | 
			
		||||
const Note = db.get<INote>('notes');
 | 
			
		||||
Note.createIndex('uri', { sparse: true, unique: true });
 | 
			
		||||
| 
						 | 
				
			
			@ -248,14 +247,11 @@ export const pack = async (
 | 
			
		|||
				fields: { _id: false }
 | 
			
		||||
			});
 | 
			
		||||
		} else {
 | 
			
		||||
			_note.emojis = (await Emoji.find({
 | 
			
		||||
			_note.emojis = Emoji.find({
 | 
			
		||||
				name: { $in: _note.emojis },
 | 
			
		||||
				host: host
 | 
			
		||||
			}, {
 | 
			
		||||
				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;
 | 
			
		||||
 | 
			
		||||
	// Populate user
 | 
			
		||||
	_note.user = packUser(_note.userId, me);
 | 
			
		||||
	_note.user = packUser(_note.userId, meId);
 | 
			
		||||
 | 
			
		||||
	// Populate app
 | 
			
		||||
	if (_note.appId) {
 | 
			
		||||
| 
						 | 
				
			
			@ -286,7 +282,7 @@ export const pack = async (
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// Populate files
 | 
			
		||||
	_note.files = packFileMany(_note.fileIds || [], { me });
 | 
			
		||||
	_note.files = packFileMany(_note.fileIds || []);
 | 
			
		||||
 | 
			
		||||
	// Some counts
 | 
			
		||||
	_note.renoteCount = _note.renoteCount || 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,6 @@ import config from '../config';
 | 
			
		|||
import FollowRequest from './follow-request';
 | 
			
		||||
import fetchMeta from '../misc/fetch-meta';
 | 
			
		||||
import Emoji from './emoji';
 | 
			
		||||
import wrapUrl from '../misc/wrap-url';
 | 
			
		||||
 | 
			
		||||
const User = db.get<IUser>('users');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -345,8 +344,6 @@ export const pack = (
 | 
			
		|||
 | 
			
		||||
	if (_user.avatarUrl == null) {
 | 
			
		||||
		_user.avatarUrl = `${config.drive_url}/default-avatar.jpg`;
 | 
			
		||||
	} else {
 | 
			
		||||
		_user.avatarUrl = wrapUrl(_user.avatarUrl, me);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!meId || !meId.equals(_user.id) || !opts.detail) {
 | 
			
		||||
| 
						 | 
				
			
			@ -371,7 +368,7 @@ export const pack = (
 | 
			
		|||
	if (opts.detail) {
 | 
			
		||||
		if (_user.pinnedNoteIds) {
 | 
			
		||||
			// Populate pinned notes
 | 
			
		||||
			_user.pinnedNotes = packNoteMany(_user.pinnedNoteIds, me, {
 | 
			
		||||
			_user.pinnedNotes = packNoteMany(_user.pinnedNoteIds, meId, {
 | 
			
		||||
				detail: true
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -400,14 +397,11 @@ export const pack = (
 | 
			
		|||
 | 
			
		||||
	// カスタム絵文字添付
 | 
			
		||||
	if (_user.emojis) {
 | 
			
		||||
		_user.emojis = (await Emoji.find({
 | 
			
		||||
		_user.emojis = Emoji.find({
 | 
			
		||||
			name: { $in: _user.emojis },
 | 
			
		||||
			host: _user.host
 | 
			
		||||
		}, {
 | 
			
		||||
			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
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
	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) {
 | 
			
		||||
	const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url;
 | 
			
		||||
	console.log(url);
 | 
			
		||||
 | 
			
		||||
	// Create temp file
 | 
			
		||||
	const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue