[READ-ONLY] Mirror of https://github.com/flo-bit/youtube-party-dj. democratic youtube player, everyone can vote and add songs, uni project, made with uix
0

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'main' into user_name

author
Florian
committer
GitHub
date (Jul 10, 2024, 12:43 PM +0200) commit c02f9f4b parent 565eaf65
+123 -19
+68 -13
backend/data.tsx
··· 1 1 import { Innertube } from "https://deno.land/x/youtubei@v10.0.0-deno/deno.ts"; 2 + import uniqBy from 'https://cdn.skypack.dev/lodash/uniqBy'; 3 + import shuffle from 'https://cdn.skypack.dev/lodash/shuffle'; 4 + import take from 'https://cdn.skypack.dev/lodash/take'; 2 5 3 6 const youtube = await Innertube.create(); 4 7 ··· 11 14 // @ts-ignore - no type for duration 12 15 const videos = result.videos.filter((item) => item.duration?.seconds).slice(0, 10); 13 16 14 - return videos.map((item) => { 15 - return { 16 - title: item.title.text ?? 'Untitled', 17 - // @ts-ignore - no type for thumbnails 18 - thumbnail: item.thumbnails?.[0].url, 19 - // @ts-ignore - no type for id 20 - id: item.id, 21 - likes: new Set<string>(), 22 - added: Date.now(), 23 - // @ts-ignore - no type for duration 24 - duration: item.duration?.text ?? '-', 25 - }; 26 - }); 17 + return videos.map(item => normalizeVideo(item)); 27 18 } catch (error) { 28 19 console.error(error); 29 20 return []; 30 21 } 31 22 } 23 + 24 + const normalizeVideo = (video: any) => { 25 + console.log(video); 26 + return { 27 + title: video.title.text ?? 'Untitled', 28 + // @ts-ignore - no type for thumbnails 29 + thumbnail: video.thumbnails?.[0].url, 30 + // @ts-ignore - no type for id 31 + id: video.id, 32 + likes: new Set<string>(), 33 + added: Date.now(), 34 + // @ts-ignore - no type for duration 35 + duration: video.duration?.text ?? '-', 36 + } 37 + } 38 + 39 + async function getRelatedVideos(videoId: string) { 40 + try { 41 + const videoDetails = await youtube.getInfo(videoId); 42 + const related = videoDetails.watch_next_feed.map(related => normalizeVideo(related)); 43 + return related; 44 + } catch (error) { 45 + console.error("Error fetching related videos:", error); 46 + throw error; 47 + } 48 + } 49 + 50 + export async function getRecommendations(queue: { id: string }[], maxRecommendations = 5) { 51 + let allRecommendations: string[] = []; 52 + 53 + for (const video of queue) { 54 + try { 55 + const relatedVideos = await getRelatedVideos(video.id); 56 + allRecommendations.push(...relatedVideos.slice(0, maxRecommendations)); 57 + } catch (error) { 58 + console.error(`Error processing video ID ${video.id}:`, error); 59 + } 60 + } 61 + 62 + // filter out duplicates that are in queue 63 + const newRecommendations = allRecommendations.filter((video) => !queue.some((item) => item.id === video.id)); 64 + 65 + const uniqueRecommendations = uniqBy(newRecommendations, 'id'); 66 + 67 + const shuffledRecommendations = shuffle(uniqueRecommendations); 68 + 69 + const finalRecommendations = take(shuffledRecommendations, maxRecommendations); 70 + 71 + console.log("FINDAL RECOMMENDATIONS", finalRecommendations); 72 + return finalRecommendations; 73 + } 74 + 75 + export async function updateRecommendations(session: any, code: string) { 76 + const recommendations = await getRecommendations(session.queue); 77 + 78 + recommendations.forEach(() => { 79 + session?.recommendedQueue.pop() 80 + }) 81 + recommendations.forEach((video) => { 82 + session?.recommendedQueue.push(video) 83 + }) 84 + 85 + return session; 86 + }
+13
backend/sessions.ts
··· 23 23 clientIds: Set<string>; 24 24 clients: Record<string, Client>; 25 25 queue: Item[]; 26 + recommendedQueue: Item[]; 26 27 currentlyPlaying: Item | null; 27 28 }; 28 29 ··· 146 147 clientIds: new Set() as Set<string>, 147 148 clients: {}, 148 149 queue: [] as Item[], 150 + recommendedQueue: [] as Item[], 149 151 currentlyPlaying: null as Item | null, 150 152 }; 151 153 sessions[code] = session; ··· 179 181 if (a.added < b.added) return -1; 180 182 return 0; 181 183 }); 184 + }); 185 + } 186 + 187 + export const getRecommendedQueue = (code: string) => { 188 + const session = sessions[code]; 189 + if (!session) { 190 + console.log("no session!") 191 + return $$([]) 192 + } 193 + return always(() => { 194 + return session.recommendedQueue 182 195 }); 183 196 }
+7 -1
common/components/QueueItem.tsx
··· 1 1 import { ObjectRef } from "datex-core-legacy/runtime/pointers.ts"; 2 2 import { QueueType } from "./Queue.tsx"; 3 3 import { getSessionWithCode, getUserId, Item, toggleLike } from "backend/sessions.ts"; 4 + import { getRecommendations, updateRecommendations } from "backend/data.tsx" 5 + 6 + const userId = (await getUserId()).userId; 4 7 5 8 const userId = (await getUserId()).userId; 6 9 ··· 150 153 onclick={async () => { 151 154 await handleButtonClick(); 152 155 await sleep(250); 153 - const session = await getSessionWithCode(code); 156 + const clickCode = code.length ? code : code.val 157 + const session = await getSessionWithCode(clickCode); 154 158 if (!session) return; 155 159 156 160 if ((session.queue.some((v) => v.id == item.id) || session.currentlyPlaying?.id == item.id)) { ··· 158 162 } 159 163 session?.queue.push(item); 160 164 toggleLike(code, item.id); 165 + 166 + await updateRecommendations(session, code); 161 167 }} 162 168 id={`button-${item.id}`} 163 169 class="queueframe bg-white dark:bg-white/5 border border-black dark:border-white/10 rounded-full w-10 h-10 flex items-center justify-center overflow-hidden "
+25 -2
common/page.tsx
··· 5 5 6 6 import QRCodeOverlay from "./components/QRCodeOverlay.tsx"; 7 7 import UserDisplay from "./components/UserDisplay.tsx"; 8 - import { getSessionUserHosts, getSortedQueue } from "backend/sessions.ts"; 8 + import { getSessionUserHosts, getSortedQueue, getRecommendedQueue } from "backend/sessions.ts"; 9 + 9 10 import { NowPlaying } from "./components/NowPlaying.tsx"; 10 11 import addDurations from "./helper.tsx"; 11 12 ··· 41 42 }); 42 43 43 44 const sorted = await getSortedQueue(code); 45 + const recommended = await getRecommendedQueue(code); 44 46 45 47 const timeLeft = always(() => { 46 48 let timeCounter = "0:00"; ··· 52 54 53 55 loadInitialTheme(); 54 56 55 - 57 + 58 + const Recommendations = () => { 59 + 60 + // console.log('session', session) 61 + if (recommended.$.length === 0) { 62 + return null 63 + } 64 + 65 + return ( 66 + <div class="flex flex-col gap-3 mt-5"> 67 + <div class="text-white">RECOMMENDED:</div> 68 + <div class="space-y-4">{ 69 + recommended.$.map(item => { 70 + return <QueueItem item={item} type={'search'} code={code}></QueueItem> 71 + })} 72 + </div> 73 + </div> 74 + ) 75 + 76 + } 77 + 56 78 return ( 57 79 <main class="w-screen h-screen relative bg-gray-50 dark:bg-gray-950"> 58 80 <div class="mx-auto grid md:grid-cols-2 h-screen"> ··· 103 125 return <QueueItem item={item} type={'player'} code={code}></QueueItem> 104 126 })} 105 127 </div> 128 + <Recommendations /> 106 129 </div> 107 130 </div> 108 131 </div>
+6
frontend/entrypoint.css
··· 873 873 874 874 .mt-6 { 875 875 margin-top: 1.5rem; 876 + .mt-5 { 877 + margin-top: 1.25rem; 876 878 } 877 879 878 880 .mt-8 { ··· 1063 1065 1064 1066 .gap-4 { 1065 1067 gap: 1rem; 1068 + } 1069 + 1070 + .gap-3 { 1071 + gap: 0.75rem; 1066 1072 } 1067 1073 1068 1074 .gap-6 {
+1
package-lock.json
··· 495 495 "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", 496 496 "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", 497 497 "dev": true, 498 + "license": "MIT", 498 499 "dependencies": { 499 500 "chalk": "^4.1.2", 500 501 "date-fns": "^2.30.0",
+3 -3
package.json
··· 1 1 { 2 2 "scripts": { 3 - "dev": "concurrently \"uix -l\" \"npx tailwindcss -i ./frontend/main.css -o ./frontend/entrypoint.css --watch\"" 3 + "dev": "concurrently \"uix --reload -l\" \"npx tailwindcss -i ./frontend/main.css -o ./frontend/entrypoint.css --watch\"" 4 4 }, 5 5 "devDependencies": { 6 6 "@tailwindcss/forms": "^0.5.7", ··· 10 10 "tailwindcss": "^3.4.3" 11 11 }, 12 12 "dependencies": { 13 - "tailwindcss": "^3.4.3", 14 13 "node": "^20.14.0", 15 14 "tailwind-scrollbar": "^3.1.0", 15 + "tailwindcss": "^3.4.3", 16 16 "typescript": "^5.4.5" 17 17 } 18 - } 18 + }