diff --git a/cli/clean-cached-remote-files.js b/cli/clean-cached-remote-files.js
index e4db37ef9..a9c38a4cd 100644
--- a/cli/clean-cached-remote-files.js
+++ b/cli/clean-cached-remote-files.js
@@ -8,7 +8,8 @@ const { default: User } = require('../built/models/user');
const q = {
'metadata._user.host': {
$ne: null
- }
+ },
+ 'metadata.isMetaOnly': false
};
async function main() {
@@ -56,8 +57,7 @@ async function main() {
DriveFile.update({ _id: file._id }, {
$set: {
- 'metadata.deletedAt': new Date(),
- 'metadata.isExpired': true
+ 'metadata.isMetaOnly': true
}
})
]).then(async () => {
diff --git a/locales/ja.yml b/locales/ja.yml
index 7d818193b..49046d26e 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -855,6 +855,8 @@ mobile/views/pages/settings.vue:
behavior: "動作"
fetch-on-scroll: "スクロールで自動読み込み"
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
+ load-raw-images: "添付された画像を高画質で表示する"
+ load-remote-media: "リモートサーバーのメディアを表示する"
twitter: "Twitter連携"
twitter-connect: "Twitterアカウントに接続する"
twitter-reconnect: "再接続する"
diff --git a/src/client/app/mobile/views/components/media-image.vue b/src/client/app/mobile/views/components/media-image.vue
index c4622b01a..c2f9c66e8 100644
--- a/src/client/app/mobile/views/components/media-image.vue
+++ b/src/client/app/mobile/views/components/media-image.vue
@@ -16,13 +16,18 @@ export default Vue.extend({
}
},
computed: {
- lightmode(): boolean {
- return this.$store.state.device.lightmode;
- },
style(): any {
+ let url = `url(${this.image.url}?thumbnail)`;
+
+ if (this.$store.state.device.loadRemoteMedia || this.$store.state.device.lightmode) {
+ url = null;
+ } else if (this.raw || this.$store.state.device.loadRawImages) {
+ url = `url(${this.image.url})`;
+ }
+
return {
'background-color': this.image.properties.avgColor && this.image.properties.avgColor.length == 3 ? `rgb(${this.image.properties.avgColor.join(',')})` : 'transparent',
- 'background-image': this.lightmode ? null : this.raw ? `url(${this.image.url})` : `url(${this.image.url}?thumbnail&size=512)`
+ 'background-image': url
};
}
}
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 270c9f405..3bb25f88f 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -59,6 +59,14 @@
%i18n:@disable-via-mobile%
+
+ %i18n:@load-raw-images%
+
+
+
+ %i18n:@load-remote-media%
+
+
%i18n:@i-am-under-limited-internet%
@@ -166,6 +174,11 @@ export default Vue.extend({
set(value) { this.$store.commit('device/set', { key: 'lightmode', value }); }
},
+ loadRawImages: {
+ get() { return this.$store.state.device.loadRawImages; },
+ set(value) { this.$store.commit('device/set', { key: 'loadRawImages', value }); }
+ },
+
lang: {
get() { return this.$store.state.device.lang; },
set(value) { this.$store.commit('device/set', { key: 'lang', value }); }
@@ -195,6 +208,13 @@ export default Vue.extend({
});
},
+ onChangeLoadRemoteMedia(v) {
+ this.$store.dispatch('settings/set', {
+ key: 'loadRemoteMedia',
+ value: v
+ });
+ },
+
onChangeCircleIcons(v) {
this.$store.dispatch('settings/set', {
key: 'circleIcons',
diff --git a/src/client/app/store.ts b/src/client/app/store.ts
index 74a5852c1..e300d31d8 100644
--- a/src/client/app/store.ts
+++ b/src/client/app/store.ts
@@ -13,7 +13,9 @@ const defaultSettings = {
gradientWindowHeader: false,
showReplyTarget: true,
showMyRenotes: true,
- showRenotedMyNotes: true
+ showRenotedMyNotes: true,
+ loadRemoteMedia: true,
+ disableViaMobile: false
};
const defaultDeviceSettings = {
@@ -26,6 +28,7 @@ const defaultDeviceSettings = {
preventUpdate: false,
debug: false,
lightmode: false,
+ loadRawImages: false,
postStyle: 'standard'
};
diff --git a/src/config/types.ts b/src/config/types.ts
index dff3f7d37..910c03c2c 100644
--- a/src/config/types.ts
+++ b/src/config/types.ts
@@ -41,6 +41,8 @@ export type Source = {
secret_key: string;
};
+ preventCacheRemoteFiles: boolean;
+
/**
* ゴーストアカウントのID
*/
diff --git a/src/models/drive-file.ts b/src/models/drive-file.ts
index 8a18567dc..a3a567038 100644
--- a/src/models/drive-file.ts
+++ b/src/models/drive-file.ts
@@ -32,7 +32,7 @@ export type IMetadata = {
uri?: string;
url?: string;
deletedAt?: Date;
- isExpired?: boolean;
+ isMetaOnly?: boolean;
};
export type IDriveFile = {
@@ -155,7 +155,8 @@ export const pack = (
_target = Object.assign(_target, _file.metadata);
_target.src = _file.metadata.url;
- _target.url = `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`;
+ _target.url = _file.metadata.isMetaOnly ? _file.metadata.url : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`;
+ _target.isRemote = _file.metadata.isMetaOnly;
if (_target.properties == null) _target.properties = {};
diff --git a/src/server/file/send-drive-file.ts b/src/server/file/send-drive-file.ts
index d613a3aa5..e04400317 100644
--- a/src/server/file/send-drive-file.ts
+++ b/src/server/file/send-drive-file.ts
@@ -33,11 +33,12 @@ export default async function(ctx: Koa.Context) {
if (file.metadata.deletedAt) {
ctx.status = 410;
- if (file.metadata.isExpired) {
- await send(ctx, '/cache-expired.png', { root: assets });
- } else {
- await send(ctx, '/tombstone.png', { root: assets });
- }
+ await send(ctx, '/tombstone.png', { root: assets });
+ return;
+ }
+
+ if (file.metadata.isMetaOnly) {
+ ctx.status = 204;
return;
}
diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts
index 8b1d3eef0..413c91417 100644
--- a/src/services/drive/add-file.ts
+++ b/src/services/drive/add-file.ts
@@ -104,6 +104,7 @@ export default async function(
comment: string = null,
folderId: mongodb.ObjectID = null,
force: boolean = false,
+ metaOnly: boolean = false,
url: string = null,
uri: string = null
): Promise {
@@ -170,38 +171,40 @@ export default async function(
}
//#region Check drive usage
- const usage = await DriveFile
- .aggregate([{
- $match: {
- 'metadata.userId': user._id,
- 'metadata.deletedAt': { $exists: false }
- }
- }, {
- $project: {
- length: true
- }
- }, {
- $group: {
- _id: null,
- usage: { $sum: '$length' }
- }
- }])
- .then((aggregates: any[]) => {
- if (aggregates.length > 0) {
- return aggregates[0].usage;
- }
- return 0;
- });
+ if (!metaOnly) {
+ const usage = await DriveFile
+ .aggregate([{
+ $match: {
+ 'metadata.userId': user._id,
+ 'metadata.deletedAt': { $exists: false }
+ }
+ }, {
+ $project: {
+ length: true
+ }
+ }, {
+ $group: {
+ _id: null,
+ usage: { $sum: '$length' }
+ }
+ }])
+ .then((aggregates: any[]) => {
+ if (aggregates.length > 0) {
+ return aggregates[0].usage;
+ }
+ return 0;
+ });
- log(`drive usage is ${usage}`);
+ log(`drive usage is ${usage}`);
- // If usage limit exceeded
- if (usage + size > user.driveCapacity) {
- if (isLocalUser(user)) {
- throw 'no-free-space';
- } else {
- // (アバターまたはバナーを含まず)最も古いファイルを削除する
- deleteOldFile(user);
+ // If usage limit exceeded
+ if (usage + size > user.driveCapacity) {
+ if (isLocalUser(user)) {
+ throw 'no-free-space';
+ } else {
+ // (アバターまたはバナーを含まず)最も古いファイルを削除する
+ deleteOldFile(user);
+ }
}
}
//#endregion
@@ -270,8 +273,6 @@ export default async function(
const [folder] = await Promise.all([fetchFolder(), propPromises]);
- const readable = fs.createReadStream(path);
-
const metadata = {
userId: user._id,
_user: {
@@ -279,7 +280,8 @@ export default async function(
},
folderId: folder !== null ? folder._id : null,
comment: comment,
- properties: properties
+ properties: properties,
+ isMetaOnly: metaOnly
} as IMetadata;
if (url !== null) {
@@ -290,7 +292,16 @@ export default async function(
metadata.uri = uri;
}
- const driveFile = await (writeChunks(detectedName, readable, mime, metadata) as Promise);
+ const driveFile = metaOnly
+ ? await DriveFile.insert({
+ length: 0,
+ uploadDate: new Date(),
+ md5: hash,
+ filename: detectedName,
+ metadata: metadata,
+ contentType: mime
+ })
+ : await (writeChunks(detectedName, fs.createReadStream(path), mime, metadata) as Promise);
log(`drive file has been created ${driveFile._id}`);
@@ -300,13 +311,15 @@ export default async function(
publishDriveStream(user._id, 'file_created', packedFile);
});
- try {
- const thumb = await genThumbnail(driveFile);
- if (thumb) {
- await writeThumbnailChunks(detectedName, thumb, driveFile._id);
+ if (!metaOnly) {
+ try {
+ const thumb = await genThumbnail(driveFile);
+ if (thumb) {
+ await writeThumbnailChunks(detectedName, thumb, driveFile._id);
+ }
+ } catch (e) {
+ // noop
}
- } catch (e) {
- // noop
}
return driveFile;
diff --git a/src/services/drive/upload-from-url.ts b/src/services/drive/upload-from-url.ts
index ad2620c03..e216ca603 100644
--- a/src/services/drive/upload-from-url.ts
+++ b/src/services/drive/upload-from-url.ts
@@ -1,14 +1,17 @@
+import * as fs from 'fs';
import * as URL from 'url';
-import { IDriveFile, validateFileName } from '../../models/drive-file';
-import create from './add-file';
+
import * as debug from 'debug';
import * as tmp from 'tmp';
-import * as fs from 'fs';
import * as request from 'request';
+import { IDriveFile, validateFileName } from '../../models/drive-file';
+import create from './add-file';
+import config from '../../config';
+
const log = debug('misskey:drive:upload-from-url');
-export default async (url, user, folderId = null, uri = null): Promise => {
+export default async (url: string, user, folderId = null, uri: string = null): Promise => {
log(`REQUESTED: ${url}`);
let name = URL.parse(url).pathname.split('/').pop();
@@ -43,7 +46,7 @@ export default async (url, user, folderId = null, uri = null): Promise