Progress so far
This commit is contained in:
100
lib/audio/audio.ts
Normal file
100
lib/audio/audio.ts
Normal 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
27
lib/audio/hooks.ts
Normal 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
106
lib/audio/player.ts
Normal 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
17
lib/audio/types.ts
Normal 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
18
lib/current-user.ts
Normal 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
9
lib/db.ts
Normal 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
50
lib/pubsub.ts
Normal 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
44
lib/validate-api.ts
Normal 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
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user