Progress so far

This commit is contained in:
Tom
2023-12-30 10:56:40 +00:00
parent d191f8fb6f
commit 8eb9b9096f
41 changed files with 1984 additions and 6018 deletions

100
lib/audio/audio.ts Normal file
View File

@ -0,0 +1,100 @@
import { createPubSub } from '../pubsub';
import { AudioState } from './types';
export const createAudio = () => {
const pubsub = createPubSub();
const element = document.createElement('video');
let currentTime = 0;
let state: AudioState = {
duration: 0,
playing: false,
volume: 0,
};
const setState = (value: Partial<AudioState>) => {
state = { ...state, ...value };
pubsub.publish('change', state);
};
const setup = () => {
element.addEventListener('durationchange', () =>
setState({ duration: element.duration }),
);
element.addEventListener('playing', () => setState({ playing: true }));
element.addEventListener('pause', () => setState({ playing: false }));
element.addEventListener('timeupdate', () => {
const newCurrentTime = Math.round(element.currentTime);
if (currentTime !== newCurrentTime) {
currentTime = newCurrentTime;
pubsub.publish('change-current-time', currentTime);
}
});
element.addEventListener('volumechange', () =>
setState({ volume: element.volume }),
);
setState({ volume: element.volume });
};
setup();
return {
seek(seconds: number) {
element.currentTime = seconds;
currentTime = seconds;
pubsub.publish('change-current-time', currentTime);
},
getElement() {
return element;
},
getState() {
return state;
},
getCurrentTime() {
return currentTime;
},
play() {
element.play();
},
pause() {
element.pause();
},
volume(value: number) {
element.volume = value;
},
setUrl(url: string) {
element.setAttribute('src', url);
setState({ playing: false });
},
subscribe(listener: (newState: AudioState) => void) {
return pubsub.subscribe('change', listener);
},
onChangeCurrentTime(listener: (newCurrentTime: number) => void) {
return pubsub.subscribe('change-current-time', listener);
},
onEnded(listener: () => void) {
element.addEventListener('ended', listener);
return () => element.removeEventListener('ended', listener);
},
};
};

27
lib/audio/hooks.ts Normal file
View File

@ -0,0 +1,27 @@
import { useState, useEffect } from 'react';
import player from './player';
export const usePlayerState = () => {
const [state, setState] = useState(player.getState());
useEffect(() => {
const unsubscribe = player.subscribe(setState);
return unsubscribe;
}, []);
return state;
};
export const useCurrentTime = () => {
const [currentTime, setCurrentTime] = useState(player.getCurrentTime());
useEffect(() => {
const unsubscribe = player.onChangeCurrentTime(setCurrentTime);
return unsubscribe;
}, []);
return currentTime;
};

106
lib/audio/player.ts Normal file
View File

@ -0,0 +1,106 @@
import { createPubSub } from '../pubsub';
import { createAudio } from './audio';
import { State, Track } from './types';
const createPlayer = () => {
const pubsub = createPubSub();
const audio = createAudio();
let state: State = {
...audio.getState(),
tracks: [],
currentTrackIndex: null,
currentTrack: null,
};
const setState = (value: Partial<State>) => {
state = { ...state, ...value };
pubsub.publish('change', state);
};
audio.subscribe(setState);
const changeTrack = () => {
const track = state.currentTrack;
if (track) {
audio.setUrl(track.url);
audio.play();
}
};
const next = () => {
if (state.currentTrackIndex === null) {
return;
}
const lastIndex = state.tracks.length - 1;
const newIndex = state.currentTrackIndex + 1;
if (newIndex <= lastIndex) {
setState({
currentTrackIndex: newIndex,
currentTrack: state.tracks[newIndex],
});
changeTrack();
}
};
audio.onEnded(next);
return {
play: audio.play,
pause: audio.pause,
seek: audio.seek,
volume: audio.volume,
getCurrentTime: audio.getCurrentTime,
getElement: audio.getElement,
onChangeCurrentTime: audio.onChangeCurrentTime,
getState() {
return state;
},
setQueue(tracks: Track[]) {
setState({ tracks });
},
playTrack(trackIndex: number) {
setState({
currentTrackIndex: trackIndex,
currentTrack: state.tracks[trackIndex],
});
changeTrack();
},
next,
prev() {
if (state.currentTrackIndex === null) {
return;
}
const newIndex = state.currentTrackIndex - 1;
if (newIndex >= 0) {
setState({
currentTrack: state.tracks[newIndex],
currentTrackIndex: newIndex,
});
changeTrack();
}
},
subscribe(listener: (newState: State) => void) {
return pubsub.subscribe('change', listener);
},
};
};
const player = createPlayer();
export default player;

17
lib/audio/types.ts Normal file
View File

@ -0,0 +1,17 @@
export type AudioState = {
duration: number;
playing: boolean;
volume: number;
};
export type Track = {
url: string;
title: string;
};
export type State = AudioState & {
tracks: Track[];
currentTrack: Track | null;
currentTrackIndex: number | null;
};

18
lib/current-user.ts Normal file
View File

@ -0,0 +1,18 @@
import { db } from "@/lib/db"
import { useSession } from "next-auth/react"
export const currentUser = async() => {
const { data: session, status } = useSession()
if (status !== "authenticated") {
return null;
}
const user = await db.user.findUnique({
where: {
id: session?.user?.name as string
}
});
return user;
}

9
lib/db.ts Normal file
View File

@ -0,0 +1,9 @@
import { PrismaClient } from "@prisma/client"
declare global {
var prisma: PrismaClient | undefined;
}
export const db = globalThis.prisma || new PrismaClient();
if (process.env.NODE_ENV !== "production") globalThis.prisma = db

50
lib/pubsub.ts Normal file
View File

@ -0,0 +1,50 @@
type Listener = (value: any) => void;
type Topics = {
[name: string]: Listener[];
};
export const createPubSub = () => {
let topics: Topics = {};
let destroyed = false;
const getTopic = (name: string) => {
if (!topics[name]) {
topics[name] = [];
}
return topics[name];
};
return {
subscribe(topic: string, fn: Listener) {
const listeners = getTopic(topic);
listeners.push(fn);
const unsubscribe = () => {
const index = listeners.indexOf(fn);
listeners.splice(index, 1);
};
return unsubscribe;
},
publish(topic: string, value: any) {
const listeners = getTopic(topic);
const currentListeners = listeners.slice();
currentListeners.forEach((listener) => {
if (!destroyed) {
listener(value);
}
});
},
destroy() {
topics = {};
destroyed = true;
},
};
};

44
lib/validate-api.ts Normal file
View File

@ -0,0 +1,44 @@
import { getServerSession } from "next-auth";
import { db } from "./db";
export default async function fetchUserUsingAPI(req: Request) {
const session = await getServerSession()
console.log("server session:", session)
if (session) {
const user = await db.user.findFirst({
where: {
username: session.user?.name?.toLowerCase() as string
}
})
return {
id: user?.id,
username: user?.username
}
}
const token = req.headers?.get('x-api-key')
console.log("x-api-key:", token)
if (token === null || token === undefined)
return null
const key = await db.apiKey.findFirst({
where: {
id: token as string
}
})
const user = await db.user.findFirst({
where: {
id: key?.userId
}
})
console.log("user:", user)
return {
id: user?.id,
username: user?.username
}
}