Skip to content

Commit

Permalink
feat: figure out stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
devsargam committed Nov 21, 2024
1 parent b510fec commit ca57ad8
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 36 deletions.
36 changes: 18 additions & 18 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ model NotionMetadata {
model VideoMetadata {
id Int @id @default(autoincrement())
contentId Int
appxVideoId String?
video_1080p_mp4_1 String? // Link to 1080p mp4 quality video variant 1
video_1080p_mp4_2 String? // Link to 1080p mp4 quality video variant 2
video_1080p_mp4_3 String? // Link to 1080p mp4 quality video variant 3
Expand Down Expand Up @@ -138,29 +139,30 @@ model Session {
}

model User {
id String @id @default(cuid())
id String @id @default(cuid())
name String?
email String? @unique
email String? @unique
token String?
sessions Session[]
purchases UserPurchases[]
videoProgress VideoProgress[]
comments Comment[]
votes Vote[]
discordConnect DiscordConnect?
disableDrm Boolean @default(false)
bunnyProxyEnabled Boolean @default(false)
disableDrm Boolean @default(false)
bunnyProxyEnabled Boolean @default(false)
bookmarks Bookmark[]
password String?
appxUserId String?
appxUsername String?
appxAuthToken String?
questions Question[]
answers Answer[]
certificate Certificate[]
upiIds UpiId[] @relation("UserUpiIds")
solanaAddresses SolanaAddress[] @relation("UserSolanaAddresses")
githubUser GitHubLink? @relation("UserGithub")
bounties BountySubmission[]
upiIds UpiId[] @relation("UserUpiIds")
solanaAddresses SolanaAddress[] @relation("UserSolanaAddresses")
githubUser GitHubLink? @relation("UserGithub")
bounties BountySubmission[]
}

model GitHubLink {
Expand Down Expand Up @@ -324,20 +326,19 @@ model Event {
}

model BountySubmission {
id String @id @default(uuid())
prLink String
id String @id @default(uuid())
prLink String
paymentMethod String
status String @default("pending")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
amount Float @default(0)
userId String
user User @relation(fields: [userId], references: [id])
status String @default("pending")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
amount Float @default(0)
userId String
user User @relation(fields: [userId], references: [id])
@@unique([userId, prLink])
}


enum VoteType {
UPVOTE
DOWNVOTE
Expand All @@ -359,4 +360,3 @@ enum MigrationStatus {
MIGRATED
MIGRATION_ERROR
}

51 changes: 51 additions & 0 deletions src/actions/user/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
'use server';
import db from '@/db';
import { authOptions } from '@/lib/auth';
import axios from 'axios';
import { getServerSession } from 'next-auth';

export const logoutUser = async (email: string, adminPassword: string) => {
if (adminPassword !== process.env.ADMIN_SECRET) {
Expand All @@ -25,3 +28,51 @@ export const logoutUser = async (email: string, adminPassword: string) => {

return { message: 'User logged out' };
};

type GetAppxAuthTokenResponse = {
name: string | null;
email: string | null;
appxAuthToken: string | null;
appxUserId: string | null;
}

export const GetAppxAuthToken = async (): Promise<GetAppxAuthTokenResponse> => {
const session = await getServerSession(authOptions)
if (!session || !session.user) throw new Error("User is not logged in");

const user = await db.user.findFirst({
where: {
email: session.user.email,
},
select: {
name: true,
email: true,
appxAuthToken: true,
appxUserId: true
}
});

if (!user) throw new Error("User not found");
return user
}

export const GetAppxVideoPlayerUrl = async (courseId: string, videoId: string): Promise<string> => {
const { name, email, appxAuthToken, appxUserId } = await GetAppxAuthToken();
const url = `${process.env.APPX_BASE_API}/get/fetchVideoDetailsById?course_id=${courseId}&video_id=${videoId}&ytflag=${1}&folder_wise_course=${1}`;
const config = {
url,
method: "get",
maxBodyLength: Infinity,
headers: {
Authorization: appxAuthToken,
"Auth-Key": process.env.APPX_AUTH_KEY,
"User-Id": appxUserId,
},
};

const res = await axios.request(config);
const { video_player_token, video_player_url } = res.data.data;
const full_video_url = `${video_player_url}${video_player_token}&watermark=${name}%0A${email}`;
return full_video_url;
}

13 changes: 10 additions & 3 deletions src/app/api/admin/content/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const POST = async (req: NextRequest) => {
rest,
discordChecked,
}: {
type: 'video' | 'folder' | 'notion';
type: 'video' | 'folder' | 'notion' | 'appx';
thumbnail: string;
title: string;
courseId: number;
Expand Down Expand Up @@ -110,6 +110,13 @@ export const POST = async (req: NextRequest) => {
},
});
}
} else if (type === 'appx') {
await db.videoMetadata.create({
data: {
appxVideoId: metadata.appxVideoId,
contentId: content.id,
},
});
} else if (type === 'video') {
await db.videoMetadata.create({
data: {
Expand Down Expand Up @@ -156,7 +163,7 @@ export const POST = async (req: NextRequest) => {
});
}
}
if (discordChecked && (type === 'notion' || type === 'video')) {
if (discordChecked && (type === 'notion' || type === 'video' || type === 'appx')) {
if (!process.env.NEXT_PUBLIC_DISCORD_WEBHOOK_URL) {
return NextResponse.json(
{ message: 'Environment variable for discord webhook is not set' },
Expand All @@ -181,7 +188,7 @@ export const POST = async (req: NextRequest) => {
return NextResponse.json(
{
message:
discordChecked && (type === 'notion' || type === 'video')
discordChecked && (type === 'notion' || type === 'video' || type === 'appx')
? 'Content Added and Discord notification has been sent'
: 'Content has been added',
},
Expand Down
36 changes: 36 additions & 0 deletions src/components/AppxVideoPlayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client";
import { GetAppxVideoPlayerUrl } from "@/actions/user";
import { useEffect, useState } from "react";

export const AppxVideoPlayer = ({
courseId,
videoId,
}: {
courseId: string;
videoId: string;
}) => {
const [url, setUrl] = useState("");

useEffect(() => {
(async () => {
try {
const videoUrl = await GetAppxVideoPlayerUrl(courseId, videoId)
setUrl(videoUrl)
} catch {
if (window === undefined) return;
location.href = '/api/auth/signin';
}
})();
}, [])

if (!url.length) {
return <p>Loading...</p>;
}

return (
<iframe
src={url}
className="w-[80vw] h-[80vh] rounded-lg"
></iframe>
);
}
22 changes: 17 additions & 5 deletions src/components/VideoPlayer2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { YoutubeRenderer } from './YoutubeRenderer';
import { toast } from 'sonner';
import { createRoot } from 'react-dom/client';
import { PictureInPicture2 } from 'lucide-react';
import { AppxVideoPlayer } from './AppxVideoPlayer';

// todo correct types
interface VideoPlayerProps {
Expand Down Expand Up @@ -311,7 +312,7 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
player.playbackRate(1);
}
};
document.addEventListener('keydown', handleKeyPress, {capture: true});
document.addEventListener('keydown', handleKeyPress, { capture: true });
document.addEventListener('keyup', handleKeyUp);
// Cleanup function
return () => {
Expand Down Expand Up @@ -471,12 +472,23 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
return regex.test(url);
};

if (isYoutubeUrl(vidUrl)) {
return <YoutubeRenderer url={vidUrl} />;
}
const isAppxEncryptedVideo = (url: string) => {
return url.startsWith('https://player.akamai.net.in/secure-player');
};

if (isYoutubeUrl(vidUrl)) return <YoutubeRenderer url={vidUrl} />;

//TODO: Figure out how to get the courseId
if (isAppxEncryptedVideo(vidUrl))
return (
<AppxVideoPlayer courseId={'courseId'} videoId={contentId.toString()} />
);

return (
<div data-vjs-player style={{ maxWidth: '850px', margin: '0 auto', width: '100%' }}>
<div
data-vjs-player
style={{ maxWidth: '850px', margin: '0 auto', width: '100%' }}
>
<div ref={videoRef} style={{ width: '100%', height: 'auto' }} />
</div>
);
Expand Down
29 changes: 26 additions & 3 deletions src/components/admin/AddContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const AddContent = ({
const [loading, setLoading] = useState<boolean>(false);

const getLabelClassName = (value: string) => {
return `flex gap-6 p-6 rounded-lg items-center space-x-2 ${
return `flex gap-1 p-4 rounded-lg items-center space-x-2 ${
type === value ? 'border-[3px] border-blue-500' : 'border-[3px]'
}`;
};
Expand All @@ -61,6 +61,7 @@ export const AddContent = ({
title,
courseId,
parentContentId,
//* Metadata will be list of resolutions for normal videos and appxVideoId for appx videos
metadata,
adminPassword,
courseTitle,
Expand Down Expand Up @@ -88,17 +89,21 @@ export const AddContent = ({

return (
<div className="grid grid-cols-1 gap-4 rounded-xl border-2 p-6 lg:grid-cols-7">
<aside className="col-span-1 flex flex-col gap-8 lg:col-span-3">
<aside className="col-span-1 flex w-full flex-col gap-8 lg:col-span-3">
<div>Select the Content Mode</div>

<RadioGroup
className="flex-warp no-scrollbar flex max-w-full items-start gap-4 overflow-auto"
className="flex max-w-full flex-wrap items-start gap-2"
value={type}
onValueChange={(value) => {
setType(value);
setMetadata({});
}}
>
<Label htmlFor="appx" className={getLabelClassName('appx')}>
<RadioGroupItem value="appx" id="appx" />
<span>Appx</span>
</Label>
<Label htmlFor="video" className={getLabelClassName('video')}>
<RadioGroupItem value="video" id="video" />
<span>Video</span>
Expand Down Expand Up @@ -187,6 +192,7 @@ export const AddContent = ({
className="h-14"
/>
{type === 'video' && <AddVideosMetadata onChange={setMetadata} />}
{type === 'appx' && <AddAppxVideoMetadata onChange={setMetadata} />}
{type === 'notion' && <AddNotionMetadata onChange={setMetadata} />}
<Button
onClick={handleContentSubmit}
Expand All @@ -200,6 +206,23 @@ export const AddContent = ({
);
};

function AddAppxVideoMetadata({
onChange,
}: {
onChange: (metadata: any) => void;
}) {
return (
<div>
<Input
type="text"
placeholder="Appx Video Id"
onChange={(e) => onChange({ appxVideoId: e.target.value })}
className="h-14"
/>
</div>
);
}

const VARIANTS = 1;
function AddVideosMetadata({
onChange,
Expand Down
19 changes: 12 additions & 7 deletions src/lib/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface AppxSigninResponse {
userid: string;
name: string;
username?: string;
token: string;
} | null;
}

Expand Down Expand Up @@ -56,12 +57,12 @@ async function validateUser(
): Promise<
| { data: null }
| {
data: {
name: string;
userid: string;
token: string;
};
}
data: {
name: string;
userid: string;
token: string;
};
}
> {
if (process.env.LOCAL_CMS_PROVIDER) {
if (password === '123456') {
Expand Down Expand Up @@ -136,12 +137,14 @@ export const authOptions = {
password: true,
id: true,
name: true,
appxAuthToken: true,
},
});
if (
userDb &&
userDb.password &&
(await bcrypt.compare(credentials.password, userDb.password))
(await bcrypt.compare(credentials.password, userDb.password)) &&
userDb?.appxAuthToken
) {
const jwt = await generateJWT({
id: userDb.id,
Expand Down Expand Up @@ -184,13 +187,15 @@ export const authOptions = {
email: credentials.username,
token: jwt,
password: hashedPassword,
appxAuthToken: user.data.token,
},
update: {
id: user.data.userid,
name: user.data.name,
email: credentials.username,
token: jwt,
password: hashedPassword,
appxAuthToken: user.data.token,
},
});
} catch (e) {
Expand Down

2 comments on commit ca57ad8

@avparadox
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the name of this PR, "feat: figure out stuff." It's a great name for a PR that has some great learning and complex stuff. paaji tusi kamal ho. I have a lot to learn from you.

@devsargam
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you bhai. It's nothing great. You will also get there soon enough if you put in enough time and focus on understanding things 🙏

Please sign in to comment.