Skip to content

Commit

Permalink
Merge pull request #601 from da7a90-backup/thumbnails
Browse files Browse the repository at this point in the history
Feat: added screenshoting and setting the channel thumbnail
  • Loading branch information
gagansuie authored Jul 18, 2023
2 parents 63ba06c + d62773b commit 2a58ab9
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 31 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
"@playwright/test": "1.36.0",
"@playwright/test": "1.36.1",
"@sveltejs/adapter-cloudflare": "^2.2.0",
"@sveltejs/kit": "^1.10.0",
"@sveltejs/svelte-virtual-list": "^3.0.1",
Expand Down
12 changes: 12 additions & 0 deletions src/lib/assets/icons/chat/IconChatScreenshot.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-8 h-8">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />
</svg>
64 changes: 52 additions & 12 deletions src/lib/components/Channel/Chat/DrawerEditChannel.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<script lang="ts">
// import IconPhoto from '$lib/assets/icons/IconPhoto.svelte'
import { tags } from '$lib/stores/channelStore'
import { is_sharing_screen, is_sharing_webcam } from '$lib/stores/streamStore'
import { onDestroy, onMount } from 'svelte'
import Tags from 'svelte-tags-input'
import DrawerAddCategory from '$lib/components/Browse/DrawerAddCategory.svelte'
import { get } from '$lib/api'
import { enhance } from '$app/forms'
import { category_list } from '$lib/stores/channelStore'
import { emitChannelUpdate } from '$lib/websocket'
import IconChatScreenshot from '$lib/assets/icons/chat/IconChatScreenshot.svelte'
export let channel: any, showDrawer: boolean
Expand All @@ -16,7 +17,8 @@
showThumbnail = false,
showAddCategory = false,
maxTag = 3,
maxCategory = 4
maxCategory = 4,
imageSrc = ''
$: maxTagLabel = channel?.tags.length == maxTag ? 'max reached' : 'max ' + maxTag
$: maxCategoryLabel =
Expand Down Expand Up @@ -49,6 +51,37 @@
showThumbnail = false
}
const checkVideo = (e: any) => {
e.preventDefault()
showThumbnail = true
if (channel.videoItems.length > 0) {
let screenElement = document.getElementById(
`screen-${channel.videoItems[0]._id}`
) as HTMLVideoElement
let webcamElement = document.getElementById(
`webcam-${channel.videoItems[0]._id}`
) as HTMLVideoElement
let canvas = document.createElement('canvas')
canvas.width = 1920
canvas.height = 1080
let ctx = canvas.getContext('2d')
console.log(screenElement)
console.log(webcamElement)
if (ctx !== null && screenElement !== null && webcamElement !== null) {
ctx.drawImage(screenElement, 0, 0, canvas.width, canvas.height)
ctx.globalAlpha = 1
ctx.drawImage(webcamElement, 1400, 750, canvas.width - 1400, canvas.height - 750)
}
let screenshot = canvas.toDataURL('image/jpeg')
imageSrc = screenshot
//thumbnailRef.setAttribute('src', screenshot);
}
}
const addTag = (tagName: string) => {
tagName && channel.tags.length < maxTag ? channel.tags.push(tagName) : ''
channel = channel
Expand Down Expand Up @@ -101,25 +134,32 @@
Edit channel
</p>
<div class="flex flex-col p-3">
<p class="text-lg font-semibold">Please hide all sensitive data before going live.</p>

<!-- <div class="flex flex-row justify-center w-full">
<div class="card w-40 shadow-xl">
<div class="card-body items-center max-h-40 {showThumbnail ? '!p-3' : ''}">
{#if showThumbnail}
<img bind:this={thumbnailRef} src="" alt="Preview" class="rounded-lg h-full" />
<div class="flex flex-row justify-center w-full">
<div class="card w-60 shadow-xl" on:click={checkVideo}>
<div class="items-center max-h-40 {showThumbnail ? '!p-3' : ''}">
{#if imageSrc || channel.thumbnail}
<img
bind:this={thumbnailRef}
src={imageSrc || channel.thumbnail}
alt="Preview"
class="rounded-lg h-full" />
{:else}
<IconPhoto />
<div class="h-40 flex flex-col justify-center items-center">
<IconChatScreenshot />
Thumbnail
</div>
{/if}
</div>
</div>
</div>
<input type="hidden" name="imageSrc" value={imageSrc} />
<input type="hidden" name="channelId" value={channel._id} />
<input
bind:this={fileuploader}
on:change={fileupload}
type="file"
class="file-input file-input-bordered file-input-primary w-full mt-5" /> -->
name="thumbnail"
class="file-input file-input-bordered file-input-primary w-full mt-5" />
<input
bind:value={channel.title}
type="text"
Expand Down
14 changes: 12 additions & 2 deletions src/lib/components/Channel/Stream/VideoItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -331,15 +331,25 @@
{formattedTime}
</span>
{/if}
<video bind:this={screenElement} autoplay muted class="rounded-md w-full h-full" />
<video
bind:this={screenElement}
id={`screen-${video._id}`}
autoplay
muted
class="rounded-md w-full h-full" />
<div
use:draggable={{ bounds: 'parent' }}
on:mousedown={onMouseDown}
on:mouseup={onMouseUp}
class={animate +
' absolute ' +
(!isScreenLive ? 'w-full bottom-0 left-0 h-full' : 'w-1/4 bottom-0 right-0')}>
<video bind:this={webcamElement} autoplay muted class="rounded-md h-full w-full" />
<video
bind:this={webcamElement}
id={`webcam-${video._id}`}
autoplay
muted
class="rounded-md h-full w-full" />
</div>
<video bind:this={audioElement} autoplay muted class="rounded-md w-0 h-0" />
<div class="absolute left-2 bottom-2 rounded-md dropdown">
Expand Down
33 changes: 18 additions & 15 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ export const setRole = ({
channel,
currentUserId
}: {
userId: any
channel: any
userId: string
channel: { user: string; mods: string[] }
currentUserId: string
}): any => {
}): string => {
if (userId === 'AI') return '🤖 AI'
else if (userId === channel?.user) return 'Host'
else if (channel?.mods?.includes(userId)) return 'Mod'
Expand Down Expand Up @@ -165,9 +165,9 @@ export const cardCounts: { [key: number]: number[] } = {

export const timeSince = (date: string) => {
if (!date) return 'Date created unknown'
const created: any = new Date(date)
const currentDate: any = new Date(Date.now())
const seconds = Math.floor((currentDate - created) / 1000)
const created: Date = new Date(date)
const currentDate: Date = new Date(Date.now())
const seconds = Math.floor((currentDate.getTime() - created.getTime()) / 1000)
let interval = seconds / 31536000
if (interval > 1) {
return Math.floor(interval) + ' years ago'
Expand All @@ -191,15 +191,15 @@ export const timeSince = (date: string) => {
return Math.floor(seconds) + ' seconds ago'
}

export const getVideoGrids = (list: any, limit: number) => {
export const getVideoGrids = (list: object[], limit: number) => {
if (!list.length) return []

const numList = divideNumber(list.length, limit)
const result: any[] = []
const result: unknown[][] = []
let pointer = 0

for (let i = 0; i < numList.length; i++) {
const row: any = cardCounts[numList[i]]
const row: number[] = cardCounts[numList[i]]
for (let j = 0; j < row.length; j++) {
result[j] = []
for (let k = 0; k < row[j]; k++) {
Expand Down Expand Up @@ -253,17 +253,20 @@ export const getAudioIndicator = (

/** Dispatch event on click outside of node */

export const clickOutside = (element: any, callbackFunction: any) => {
const onClick = (event: any) => {
if (!element.contains(event.target)) {
export const clickOutside = (
element: HTMLElement,
callbackFunction: (event: MouseEvent) => void
) => {
const onClick = (event: MouseEvent) => {
if (!element.contains(event.target as Node)) {
callbackFunction(event)
}
}

document.body.addEventListener('click', onClick)

return {
update(newCallbackFunction: any) {
update(newCallbackFunction: (event: MouseEvent) => void) {
callbackFunction = newCallbackFunction
},
destroy() {
Expand All @@ -272,9 +275,9 @@ export const clickOutside = (element: any, callbackFunction: any) => {
}
}

export const createEffect = (...initialDeps: any[]) => {
export const createEffect = <T extends unknown[]>(...initialDeps: T) => {
let diff = JSON.stringify(initialDeps)
return (callback: () => void, deps?: any[], allowServerSide = false) => {
return (callback: () => void, deps?: T, allowServerSide = false) => {
if (JSON.stringify(deps) !== diff && ((!allowServerSide && browser) || allowServerSide)) {
diff = JSON.stringify(deps)
callback()
Expand Down
58 changes: 57 additions & 1 deletion src/routes/channel/[channelId]/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,63 @@
import { patch, putImage } from '$lib/api'
import type { Actions } from './$types'

const dataURLtoFile = (dataurl: string, filename: string) => {
const arr = dataurl.split(',')
const mime = (arr[0] && arr[0].match(/:(.*?);/)?.[1]) || ''
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, { type: mime })
}

export const actions = {
'edit-channel': async () => {
'edit-channel': async ({ request, locals }) => {
const data: FormData = await request.formData()
const newChannel = {}
addPropertyIfDefined(data, 'description', newChannel)
addPropertyIfDefined(data, 'title', newChannel)
addPropertyIfDefined(data, 'category', newChannel)
const thumbnail = data.get('thumbnail') as File
const imageSrc = data.get('imageSrc') as string
const channelId = data.get('channelId') as string
const file =
thumbnail !== null && thumbnail.size > 0
? thumbnail
: dataURLtoFile(imageSrc, 'thumbnail-image')
console.log(file)
if (file !== null && file.size > 0) {
const urlLocation = await putImage(
`channels/thumbnail?channelId=${channelId}&bucketName=thumbnails&originalName=${channelId}-thumbnail`,
file,
{
userId: locals.user.userId,
token: locals.user.token
}
)
console.log(urlLocation)
}

const updatedChannel = await patch(`channels?channelId=${channelId}`, newChannel, {
userId: locals.user.userId,
token: locals.user.token
})

console.log(updatedChannel)

return { success: true }
}
} satisfies Actions

const addPropertyIfDefined = (
data: FormData,
property: string,
newChannel: { [key: string]: unknown }
) => {
const propertyValue = data.get(property)
if (propertyValue !== null && propertyValue !== undefined) {
newChannel[property] = propertyValue
}
}

0 comments on commit 2a58ab9

Please sign in to comment.