diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index c5ec3fd588..c2a8110d92 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -278,6 +278,10 @@ common/views/components/connect-failed.troubleshooter.vue:
   flush: "キャッシュの削除"
   set-version: "バージョン指定"
 
+common/views/components/media-banner.vue:
+  sensitive: "閲覧注意"
+  click-to-show: "クリックして表示"
+
 common/views/components/cw-button.vue:
   hide: "隠す"
   show: "もっと見る"
diff --git a/src/client/app/common/views/components/media-banner.vue b/src/client/app/common/views/components/media-banner.vue
new file mode 100644
index 0000000000..0693143f02
--- /dev/null
+++ b/src/client/app/common/views/components/media-banner.vue
@@ -0,0 +1,89 @@
+<template>
+<div class="mk-media-banner">
+	<div class="sensitive" v-if="media.isSensitive && hide" @click="hide = false">
+		<span class="icon">%fa:exclamation-triangle%</span>
+		<b>%i18n:@sensitive%</b>
+		<span>%i18n:@click-to-show%</span>
+	</div>
+	<div class="audio" v-else-if="media.type.startsWith('audio')">
+		<audio class="audio"
+			:src="media.url"
+			:title="media.name"
+			controls
+			ref="audio"
+			preload="metadata" />
+	</div>
+	<a class="download" v-else
+		:href="media.url"
+		:title="media.name"
+		:download="media.name"
+	>
+		<span class="icon">%fa:download%</span>
+		<b>{{ media.name }}</b>
+	</a>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+
+export default Vue.extend({
+	props: {
+		media: {
+			type: Object,
+			required: true
+		},
+		hide: {
+			type: Boolean,
+			default: true
+		}
+	}
+})
+</script>
+
+<style lang="stylus" scoped>
+root(isDark)
+	width 100%
+	border-radius 4px
+	margin-top 4px
+	overflow hidden
+
+	> .download,
+	> .sensitive
+		display flex
+		align-items center
+		font-size 12px
+		padding 8px 12px
+		white-space nowrap
+
+		> *
+			display block
+
+		> b
+			overflow hidden
+			text-overflow ellipsis
+
+		> *:not(:last-child)
+			margin-right .2em
+
+		> .icon
+			font-size 1.6em
+
+	> .download
+		background isDark ? #21242d : #f7f7f7
+
+	> .sensitive
+		background #111
+		color #fff
+
+	> .audio
+		.audio
+			display block
+			width 100%
+
+.mk-media-banner[data-darkmode]
+	root(true)
+
+.mk-media-banner:not([data-darkmode])
+	root(false)
+</style>
diff --git a/src/client/app/common/views/components/media-list.vue b/src/client/app/common/views/components/media-list.vue
index cdfc2c8d3c..d83d6f85cd 100644
--- a/src/client/app/common/views/components/media-list.vue
+++ b/src/client/app/common/views/components/media-list.vue
@@ -1,18 +1,27 @@
 <template>
 <div class="mk-media-list">
-	<div :data-count="mediaList.length" ref="grid">
-		<template v-for="media in mediaList">
-			<mk-media-video :video="media" :key="media.id" v-if="media.type.startsWith('video')" :inline-playable="mediaList.length === 1"/>
-			<mk-media-image :image="media" :key="media.id" v-else :raw="raw"/>
-		</template>
+	<template v-for="media in mediaList.filter(media => !previewable(media))">
+		<x-banner :media="media" :key="media.id"/>
+	</template>
+	<div v-if="mediaList.filter(media => previewable(media)).length > 0" class="gird-container">
+		<div :data-count="mediaList.filter(media => previewable(media)).length" ref="grid">
+			<template v-for="media in mediaList">
+				<mk-media-video :video="media" :key="media.id" v-if="media.type.startsWith('video')"/>
+				<mk-media-image :image="media" :key="media.id" v-else-if="media.type.startsWith('image')" :raw="raw"/>
+			</template>
+		</div>
 	</div>
 </div>
 </template>
 
 <script lang="ts">
 import Vue from 'vue';
+import XBanner from './media-banner.vue';
 
 export default Vue.extend({
+	components: {
+		XBanner
+	},
 	props: {
 		mediaList: {
 			required: true
@@ -22,70 +31,80 @@ export default Vue.extend({
 		}
 	},
 	mounted() {
-		// for Safari bug
-		this.$refs.grid.style.height = this.$refs.grid.clientHeight ? `${this.$refs.grid.clientHeight}px` : '128px';
+		//#region for Safari bug
+		if (this.$refs.grid) {
+			this.$refs.grid.style.height = this.$refs.grid.clientHeight ? `${this.$refs.grid.clientHeight}px` : '128px';
+		}
+		//#endregion
+	},
+	methods: {
+		previewable(file) {
+			return file.type.startsWith('video') || file.type.startsWith('image');
+		}
 	}
 });
 </script>
 
 <style lang="stylus" scoped>
 .mk-media-list
-	width 100%
+	> .gird-container
+		width 100%
+		margin-top 4px
 
-	&:before
-		content ''
-		display block
-		padding-top 56.25% // 16:9
+		&:before
+			content ''
+			display block
+			padding-top 56.25% // 16:9
 
-	> div
-		position absolute
-		top 0
-		right 0
-		bottom 0
-		left 0
-		display grid
-		grid-gap 4px
+		> div
+			position absolute
+			top 0
+			right 0
+			bottom 0
+			left 0
+			display grid
+			grid-gap 4px
 
-		> *
-			overflow hidden
-			border-radius 4px
+			> *
+				overflow hidden
+				border-radius 4px
 
-		&[data-count="1"]
-			grid-template-rows 1fr
+			&[data-count="1"]
+				grid-template-rows 1fr
 
-		&[data-count="2"]
-			grid-template-columns 1fr 1fr
-			grid-template-rows 1fr
+			&[data-count="2"]
+				grid-template-columns 1fr 1fr
+				grid-template-rows 1fr
 
-		&[data-count="3"]
-			grid-template-columns 1fr 0.5fr
-			grid-template-rows 1fr 1fr
+			&[data-count="3"]
+				grid-template-columns 1fr 0.5fr
+				grid-template-rows 1fr 1fr
+
+				> *:nth-child(1)
+					grid-row 1 / 3
+
+				> *:nth-child(3)
+					grid-column 2 / 3
+					grid-row 2 / 3
+
+			&[data-count="4"]
+				grid-template-columns 1fr 1fr
+				grid-template-rows 1fr 1fr
 
 			> *:nth-child(1)
-				grid-row 1 / 3
+				grid-column 1 / 2
+				grid-row 1 / 2
+
+			> *:nth-child(2)
+				grid-column 2 / 3
+				grid-row 1 / 2
 
 			> *:nth-child(3)
+				grid-column 1 / 2
+				grid-row 2 / 3
+
+			> *:nth-child(4)
 				grid-column 2 / 3
 				grid-row 2 / 3
 
-		&[data-count="4"]
-			grid-template-columns 1fr 1fr
-			grid-template-rows 1fr 1fr
-
-		> *:nth-child(1)
-			grid-column 1 / 2
-			grid-row 1 / 2
-
-		> *:nth-child(2)
-			grid-column 2 / 3
-			grid-row 1 / 2
-
-		> *:nth-child(3)
-			grid-column 1 / 2
-			grid-row 2 / 3
-
-		> *:nth-child(4)
-			grid-column 2 / 3
-			grid-row 2 / 3
-
 </style>
diff --git a/src/client/app/desktop/views/components/media-image.vue b/src/client/app/desktop/views/components/media-image.vue
index 66ac389f64..3cff8cfc04 100644
--- a/src/client/app/desktop/views/components/media-image.vue
+++ b/src/client/app/desktop/views/components/media-image.vue
@@ -89,7 +89,7 @@ export default Vue.extend({
 		text-align center
 		font-size 12px
 
-		> b
+		> *
 			display block
 
 </style>
diff --git a/src/client/app/desktop/views/components/media-video.vue b/src/client/app/desktop/views/components/media-video.vue
index 6c60f2da96..d2752b0c95 100644
--- a/src/client/app/desktop/views/components/media-video.vue
+++ b/src/client/app/desktop/views/components/media-video.vue
@@ -79,7 +79,6 @@ export default Vue.extend({
 		justify-content center
 		align-items center
 		font-size 3.5em
-
 		cursor zoom-in
 		overflow hidden
 		background-position center
@@ -101,5 +100,4 @@ export default Vue.extend({
 
 		> b
 			display block
-
 </style>
diff --git a/src/client/app/mobile/views/components/media-image.vue b/src/client/app/mobile/views/components/media-image.vue
index 65ea6cf8ea..e38cef06ba 100644
--- a/src/client/app/mobile/views/components/media-image.vue
+++ b/src/client/app/mobile/views/components/media-image.vue
@@ -65,7 +65,7 @@ export default Vue.extend({
 		text-align center
 		font-size 12px
 
-		> b
+		> *
 			display block
 
 </style>