アニメーション画像を無効にする際、サーバーサイドではなくクライアントサイドでURLを変更するように
This commit is contained in:
		
							parent
							
								
									f014b7ae0e
								
							
						
					
					
						commit
						861302f0fd
					
				|  | @ -121,7 +121,7 @@ common: | |||
|   use-avatar-reversi-stones: "リバーシの石にアバターを使う" | ||||
|   verified-user: "公式アカウント" | ||||
|   disable-animated-mfm: "投稿内の動きのあるテキストを無効にする" | ||||
|   do-not-autoplay-animation: "アニメーションを自動再生しない" | ||||
|   do-not-autoplay-animation: "アニメーション画像を再生しない" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   always-show-nsfw: "常に閲覧注意のメディアを表示する" | ||||
|   always-mark-nsfw: "常にメディアを閲覧注意として投稿" | ||||
|  |  | |||
|  | @ -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…
	
		Reference in New Issue