From 87d7e5752b92d068f3d291771427bc1984f53325 Mon Sep 17 00:00:00 2001 From: Izan Gil <66965250+SrIzan10@users.noreply.github.com> Date: Sat, 5 Apr 2025 14:05:32 +0200 Subject: [PATCH] feat: thumbnail poc --- apps/web/Dockerfile | 2 +- apps/web/package.json | 2 + apps/web/src/instrumentation.ts | 22 +++++- .../src/lib/instrumentation/getLiveThumb.ts | 21 ++++++ apps/web/src/lib/srizan2.jpg | Bin 0 -> 24941 bytes apps/web/src/lib/workers/index.ts | 22 +++++- apps/web/src/lib/workers/register.ts | 2 + apps/web/src/lib/workers/worker/thumbnails.ts | 67 ++++++++++++++++++ yarn.lock | 17 +++++ 9 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 apps/web/src/lib/instrumentation/getLiveThumb.ts create mode 100644 apps/web/src/lib/srizan2.jpg create mode 100644 apps/web/src/lib/workers/worker/thumbnails.ts diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index 5cc6bdb..e83b8c9 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -17,7 +17,7 @@ RUN turbo prune @hctv/web --docker # Add lockfile and package.json's of isolated subworkspace FROM base AS installer RUN apk update -RUN apk add --no-cache libc6-compat +RUN apk add --no-cache libc6-compat ffmpeg WORKDIR /app # First install the dependencies (as they change less often) diff --git a/apps/web/package.json b/apps/web/package.json index cba7519..ecd4998 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -47,6 +47,7 @@ "media-chrome": "^4.8.0", "next": "^15.2.3", "next-themes": "^0.4.4", + "node-cron": "^3.0.3", "pg": "^8.14.1", "pg-boss": "^10.1.6", "react": "19", @@ -63,6 +64,7 @@ }, "devDependencies": { "@types/node": "^20", + "@types/node-cron": "^3.0.11", "@types/react": "^18", "@types/react-dom": "^18", "@types/ws": "^8.18.0", diff --git a/apps/web/src/instrumentation.ts b/apps/web/src/instrumentation.ts index c9d2219..b2d18f1 100644 --- a/apps/web/src/instrumentation.ts +++ b/apps/web/src/instrumentation.ts @@ -2,11 +2,29 @@ export async function register() { if (process.env.NEXT_RUNTIME === 'nodejs') { await (await import('@/lib/instrumentation/streamInfo')).default(); } + if (process.env.NEXT_RUNTIME === 'nodejs') { const { registerWorkers } = await import('@/lib/workers/register'); - await registerWorkers(); - console.log('bullmq workers registered'); } + + if (process.env.NEXT_RUNTIME === 'nodejs') { + const cron = (await import('node-cron')).default; + + const getLiveThumb = (await import('@/lib/instrumentation/getLiveThumb')).default; + + if (process.env.NODE_ENV === 'production') { + console.log('running production cron job scheduling') + cron.schedule('*/3 * * * *', async () => { + await getLiveThumb(); + }); + } else { + console.log('running local cron job scheduling') + setInterval(async () => { + await getLiveThumb(); + }, 5000); + } + console.log('cron stuff registered'); + } } diff --git a/apps/web/src/lib/instrumentation/getLiveThumb.ts b/apps/web/src/lib/instrumentation/getLiveThumb.ts new file mode 100644 index 0000000..8677f02 --- /dev/null +++ b/apps/web/src/lib/instrumentation/getLiveThumb.ts @@ -0,0 +1,21 @@ +import { prisma } from "@hctv/db"; +import { getThumbnailQueue } from "../workers"; + +export default async function getLiveThumb() { + const liveChannels = await prisma.streamInfo.findMany({ + where: { + isLive: true, + }, + include: { + channel: true, + } + }); + const liveChannelNames = liveChannels.map((channel) => channel.channel.name); + + const thumbQueue = getThumbnailQueue(); + for (const channel of liveChannelNames) { + await thumbQueue.add("getLiveThumb", { + name: channel, + }); + } +} \ No newline at end of file diff --git a/apps/web/src/lib/srizan2.jpg b/apps/web/src/lib/srizan2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..67a527b3eeaf43ffe9ef500bdd8c75e4eaba5e8e GIT binary patch literal 24941 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 { await registerNotificationWorker(); + await registerThumbnailWorker(); console.log('All workers registered successfully'); } \ No newline at end of file diff --git a/apps/web/src/lib/workers/worker/thumbnails.ts b/apps/web/src/lib/workers/worker/thumbnails.ts new file mode 100644 index 0000000..b7c3988 --- /dev/null +++ b/apps/web/src/lib/workers/worker/thumbnails.ts @@ -0,0 +1,67 @@ +import { Worker } from 'bullmq'; +import { getRedisConnection } from '@/lib/services/redis'; +import { exec } from 'node:child_process'; +import { existsSync } from 'node:fs'; + +const globalForWorker = global as unknown as { + thumbnailWorker: Worker | null; +}; + +if (!globalForWorker.thumbnailWorker) { + globalForWorker.thumbnailWorker = null; +} + +export async function registerThumbnailWorker(): Promise { + if (globalForWorker.thumbnailWorker) { + console.log('Notification worker already registered'); + return; + } + + console.log('Registering notification worker...'); + + const worker = new Worker( + 'thumbnails', + async (job) => { + try { + // this is totally unnecessary, but i'll keep it for security purposes. + const name = job.data.name.replace(/[^a-zA-Z0-9]/g, '_'); + const m3u8location = `/dev/shm/hls/${name}.m3u8`; + if (!existsSync(m3u8location)) return; + + exec( + `/usr/bin/ffmpeg -i ${m3u8location} -vframes 1 -an -y -f image2 /home/srizan/Documents/Development/hclive/apps/web/src/lib/${name}.jpg`, + (error) => { + if (error) { + console.error(`Error: ${error.message}`); + return { success: false, error: error.message }; + } + } + ); + + return { success: true }; + } catch (e) { + console.error('Slack notification failed:', e); + // @ts-ignore e is unknown + return { success: false, error: e.message }; + } + }, + { + connection: getRedisConnection(), + concurrency: 3, + limiter: { + max: 50, + duration: 30000, + }, + } + ); + + globalForWorker.thumbnailWorker = worker; +} + +// Close the worker +export async function closeThumbnailWorker(): Promise { + if (globalForWorker.thumbnailWorker) { + await globalForWorker.thumbnailWorker.close(); + globalForWorker.thumbnailWorker = null; + } +} diff --git a/yarn.lock b/yarn.lock index 925cd01..2508deb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1661,6 +1661,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/node-cron@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@types/node-cron/-/node-cron-3.0.11.tgz#70b7131f65038ae63cfe841354c8aba363632344" + integrity sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg== + "@types/node@*": version "22.13.11" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.11.tgz#f0ed6b302dcf0f4229d44ea707e77484ad46d234" @@ -4278,6 +4283,13 @@ node-abort-controller@^3.1.1: resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== +node-cron@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/node-cron/-/node-cron-3.0.3.tgz#c4bc7173dd96d96c50bdb51122c64415458caff2" + integrity sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A== + dependencies: + uuid "8.3.2" + node-domexception@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" @@ -5928,6 +5940,11 @@ util-utils@^1.0.3: resolved "https://registry.yarnpkg.com/util-utils/-/util-utils-1.0.3.tgz#abde6c79d373eb7fae42f28933273eab6d895dd3" integrity sha512-KXQzb5Y1cmQOubnYn2TMSJDwX+cLrFNi3CRp0bm+yTUdR4tV4LDkk2RAzGSweucwvTdhuoDdZrY67ds7QDRYXQ== +uuid@8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + uuid@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"