feat: Youtube window player (#9095)
* wip: feat: Youtube Player Window * fix: player fill window * fix: improve design * fix: disable at mobile and creanup code * fix: tailing comma * fix: delete debug output * fix: eslint * fix: switch to component * fix(backend): add missing dependency Fix #9101 Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
		
							parent
							
								
									e6077b03c3
								
							
						
					
					
						commit
						bcbda6940a
					
				
					 3 changed files with 88 additions and 2 deletions
				
			
		|  | @ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; | |||
| import type { UsersRepository } from '@/models/index.js'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	tags: ['admin'], | ||||
|  | @ -45,6 +46,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | |||
| 	constructor( | ||||
| 		@Inject(DI.usersRepository) | ||||
| 		private usersRepository: UsersRepository, | ||||
| 
 | ||||
| 		private userEntityService: UserEntityService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const query = this.usersRepository.createQueryBuilder('user'); | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ | |||
| 	<transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in"> | ||||
| 		<component :is="self ? 'MkA' : 'a'" v-if="!fetching" class="link" :class="{ compact }" :[attr]="self ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url"> | ||||
| 			<div v-if="thumbnail" class="thumbnail" :style="`background-image: url('${thumbnail}')`"> | ||||
| 				<button v-if="!playerEnabled && player.url" class="_button" :title="i18n.ts.enablePlayer" @click.prevent="playerEnabled = true"><i class="fas fa-play-circle"></i></button> | ||||
| 				<button v-if="!playerEnabled && player.url" class="_button" :title="i18n.ts.enablePlayer" @click.prevent="isMobile? playerEnabled = true : openPlayer()"><i class="fas fa-play-circle"></i></button> | ||||
| 			</div> | ||||
| 			<article> | ||||
| 				<header> | ||||
|  | @ -33,9 +33,11 @@ | |||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { onMounted, onUnmounted } from 'vue'; | ||||
| import { defineAsyncComponent, onMounted, onUnmounted } from 'vue'; | ||||
| import { url as local, lang } from '@/config'; | ||||
| import { i18n } from '@/i18n'; | ||||
| import * as os from '@/os'; | ||||
| import { deviceKind } from '@/scripts/device-kind'; | ||||
| 
 | ||||
| const props = withDefaults(defineProps<{ | ||||
| 	url: string; | ||||
|  | @ -46,6 +48,9 @@ const props = withDefaults(defineProps<{ | |||
| 	compact: false, | ||||
| }); | ||||
| 
 | ||||
| const MOBILE_THRESHOLD = 500; | ||||
| const isMobile = $ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); | ||||
| 
 | ||||
| const self = props.url.startsWith(local); | ||||
| const attr = self ? 'to' : 'href'; | ||||
| const target = self ? null : '_blank'; | ||||
|  | @ -103,6 +108,12 @@ function adjustTweetHeight(message: any) { | |||
| 	if (height) tweetHeight = height; | ||||
| } | ||||
| 
 | ||||
| const openPlayer = (): void => { | ||||
| 	os.popup(defineAsyncComponent(() => import('@/components/MkYoutubePlayer.vue')), { | ||||
| 		url: requestUrl.href, | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| (window as any).addEventListener('message', adjustTweetHeight); | ||||
| 
 | ||||
| onUnmounted(() => { | ||||
|  |  | |||
							
								
								
									
										72
									
								
								packages/client/src/components/MkYoutubePlayer.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								packages/client/src/components/MkYoutubePlayer.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,72 @@ | |||
| <template> | ||||
| <XWindow :initial-width="640" :initial-height="402" :can-resize="true" :close-button="true"> | ||||
| 	<template #header> | ||||
| 		<i class="icon fa-brands fa-youtube" style="margin-right: 0.5em;"></i> | ||||
| 		<span>{{ title ?? 'Youtube Player' }}</span> | ||||
| 	</template> | ||||
| 
 | ||||
| 	<div class="poamfof"> | ||||
| 		<transition :name="$store.state.animation ? 'fade' : ''" mode="out-in"> | ||||
| 			<div v-if="player.url" class="player"> | ||||
| 				<iframe v-if="!fetching" :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen/> | ||||
| 			</div> | ||||
| 		</transition> | ||||
| 		<MkLoading v-if="fetching"/> | ||||
| 		<MkError v-else-if="!player.url" @retry="ytFetch()"/> | ||||
| 	</div> | ||||
| </XWindow> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import XWindow from '@/components/MkWindow.vue'; | ||||
| import { lang } from '@/config'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
| 	url: string; | ||||
| }>(); | ||||
| 
 | ||||
| const requestUrl = new URL(props.url); | ||||
| 
 | ||||
| let fetching = $ref(true); | ||||
| let title = $ref<string | null>(null); | ||||
| let player = $ref({ | ||||
| 	url: null, | ||||
| 	width: null, | ||||
| 	height: null, | ||||
| }); | ||||
| 
 | ||||
| const requestLang = (lang ?? 'ja-JP').replace('ja-KS', 'ja-JP'); | ||||
| 
 | ||||
| const ytFetch = (): void => { | ||||
| 	fetching = true; | ||||
| 	fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${requestLang}`).then(res => { | ||||
| 		res.json().then(info => { | ||||
| 			if (info.url == null) return; | ||||
| 			title = info.title; | ||||
| 			fetching = false; | ||||
| 			player = info.player; | ||||
| 		}); | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| ytFetch(); | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss"> | ||||
| .poamfof { | ||||
| 	position: relative; | ||||
| 	overflow: hidden; | ||||
| 	height: 100%; | ||||
| 
 | ||||
| 	.player { | ||||
| 		position: absolute; | ||||
| 		inset: 0; | ||||
| 
 | ||||
| 		iframe { | ||||
| 			width: 100%; | ||||
| 			height: 100%; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue