watch: started work on stream backend
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
parent
1ffb926631
commit
f24c17d35e
@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: Stream
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
get {
|
||||
url: https://invidious.drgns.space/api/v1/videos/CcHevgjAnV0
|
||||
body: none
|
||||
auth: none
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: Stream
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
get {
|
||||
url: https://pipedapi.kavin.rocks/streams/CcHevgjAnV0
|
||||
body: none
|
||||
auth: none
|
||||
}
|
@ -1,12 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import { useClient } from "@/hooks/useClient";
|
||||
import { Component } from "@/typings/component";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
export const Watch: Component = () => {
|
||||
const client = useClient();
|
||||
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const videoId = searchParams.get("v");
|
||||
|
||||
const { data, error } = useQuery({
|
||||
queryKey: ["watch", videoId],
|
||||
queryFn: () => {
|
||||
return client.getStream(videoId ?? "");
|
||||
}
|
||||
});
|
||||
|
||||
console.log(data, error);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
@ -0,0 +1,15 @@
|
||||
import z from "zod";
|
||||
|
||||
export const StoryboardModel = z.object({
|
||||
url: z.string(),
|
||||
templateUrl: z.string().url(),
|
||||
width: z.number(),
|
||||
height: z.number(),
|
||||
count: z.number(),
|
||||
interval: z.number(),
|
||||
storyboardWidth: z.number(),
|
||||
storyboardHeight: z.number(),
|
||||
storyboardCount: z.number()
|
||||
});
|
||||
|
||||
export type Storyboard = z.infer<typeof StoryboardModel>;
|
@ -0,0 +1,104 @@
|
||||
import z from "zod";
|
||||
import { AuthorThumbnailModel, ThumbnailModel } from "./thumbnail";
|
||||
import { StoryboardModel } from "./storyboard";
|
||||
import { VideoModel } from "./video";
|
||||
|
||||
export const AdaptiveFormatModel = z.object({
|
||||
index: z.string(),
|
||||
bitrate: z.string(),
|
||||
init: z.string(),
|
||||
url: z.string().url(),
|
||||
itag: z.string(),
|
||||
type: z.string(),
|
||||
clen: z.string(),
|
||||
lmt: z.string(),
|
||||
projectionType: z.number().or(z.string()),
|
||||
container: z.string().optional(),
|
||||
encoding: z.string().optional(),
|
||||
qualityLabel: z.string().optional(),
|
||||
resolution: z.string().optional(),
|
||||
audioQuality: z.string().optional(),
|
||||
audioSampleRate: z.number().optional(),
|
||||
audioChannels: z.number().optional()
|
||||
});
|
||||
|
||||
export const FormatStreamModel = z.object({
|
||||
url: z.string().url(),
|
||||
itag: z.string(),
|
||||
type: z.string(),
|
||||
quality: z.string(),
|
||||
fps: z.number(),
|
||||
container: z.string(),
|
||||
encoding: z.string(),
|
||||
resolution: z.string(),
|
||||
qualityLabel: z.string(),
|
||||
size: z.string()
|
||||
});
|
||||
|
||||
export const CaptionModel = z.object({
|
||||
label: z.string(),
|
||||
language_code: z.string(),
|
||||
url: z.string()
|
||||
});
|
||||
|
||||
export const RecommendedVideoModel = z.object({
|
||||
title: z.string(),
|
||||
videoId: z.string(),
|
||||
videoThumbnails: ThumbnailModel.array(),
|
||||
|
||||
lengthSeconds: z.number(),
|
||||
viewCount: z.number(),
|
||||
|
||||
author: z.string(),
|
||||
authorId: z.string(),
|
||||
authorUrl: z.string(),
|
||||
|
||||
liveNow: z.boolean().optional().default(false),
|
||||
paid: z.boolean().optional().default(false),
|
||||
premium: z.boolean().optional().default(false)
|
||||
});
|
||||
|
||||
export type RecommendedVideo = z.infer<typeof RecommendedVideoModel>;
|
||||
|
||||
export const StreamModel = z.object({
|
||||
type: z.string(),
|
||||
title: z.string(),
|
||||
videoId: z.string(),
|
||||
videoThumbnails: ThumbnailModel.array(),
|
||||
storyboards: StoryboardModel.array(),
|
||||
description: z.string(),
|
||||
descriptionHtml: z.string(),
|
||||
published: z.number(),
|
||||
publishedText: z.string(),
|
||||
keywords: z.string().array(),
|
||||
viewCount: z.number(),
|
||||
likeCount: z.number(),
|
||||
dislikeCount: z.number(),
|
||||
paid: z.boolean().optional().default(false),
|
||||
premium: z.boolean().optional().default(false),
|
||||
isFamilyFriendly: z.boolean(),
|
||||
allowedRegions: z.string().array(),
|
||||
genre: z.string(),
|
||||
genreUrl: z.string(),
|
||||
author: z.string(),
|
||||
authorId: z.string(),
|
||||
authorUrl: z.string(),
|
||||
authorVerified: z.boolean(),
|
||||
authorThumbnails: AuthorThumbnailModel.array(),
|
||||
subCountText: z.string(),
|
||||
lengthSeconds: z.number(),
|
||||
allowRatings: z.boolean(),
|
||||
rating: z.number(),
|
||||
isListed: z.boolean(),
|
||||
liveNow: z.boolean().optional().default(false),
|
||||
isUpcoming: z.boolean(),
|
||||
dashUrl: z.string().url(),
|
||||
adaptiveFormats: AdaptiveFormatModel.array(),
|
||||
formatStreams: FormatStreamModel.array(),
|
||||
captions: CaptionModel.array(),
|
||||
recommendedVideos: RecommendedVideoModel.array()
|
||||
});
|
||||
|
||||
type Stream = z.infer<typeof StreamModel>;
|
||||
|
||||
export default Stream;
|
@ -0,0 +1,41 @@
|
||||
import z from "zod";
|
||||
import { VideoModel } from "./video";
|
||||
|
||||
export const VideoItemModel = z
|
||||
.object({
|
||||
type: z.literal("stream")
|
||||
})
|
||||
.and(VideoModel);
|
||||
|
||||
export const ChannelItemModel = z.object({
|
||||
type: z.literal("channel"),
|
||||
url: z.string(),
|
||||
name: z.string(),
|
||||
thumbnail: z.string().url(),
|
||||
description: z.string().nullable(),
|
||||
subscribers: z.number(),
|
||||
videos: z.number(),
|
||||
verified: z.boolean()
|
||||
});
|
||||
|
||||
export const PlaylistItemModel = z.object({
|
||||
type: z.literal("playlist"),
|
||||
url: z.string(),
|
||||
name: z.string(),
|
||||
thumbnail: z.string().url(),
|
||||
uploaderName: z.string(),
|
||||
uploaderUrl: z.string(),
|
||||
uploaderVerified: z.boolean(),
|
||||
playlistType: z.string(),
|
||||
videos: z.number()
|
||||
});
|
||||
|
||||
export const ItemModel = z.union([
|
||||
VideoItemModel,
|
||||
ChannelItemModel,
|
||||
PlaylistItemModel
|
||||
]);
|
||||
|
||||
type Item = z.infer<typeof ItemModel>;
|
||||
|
||||
export default Item;
|
@ -0,0 +1,100 @@
|
||||
import z from "zod";
|
||||
import { ItemModel } from "./item";
|
||||
|
||||
export const AudioStreamModel = z.object({
|
||||
url: z.string().url(),
|
||||
format: z.string(),
|
||||
quality: z.string(),
|
||||
mimeType: z.string(),
|
||||
codec: z.string().nullable(),
|
||||
audioTrackId: z.null(),
|
||||
audioTrackName: z.null(),
|
||||
audioTrackType: z.null(),
|
||||
audioTrackLocale: z.null(),
|
||||
videoOnly: z.boolean(),
|
||||
itag: z.number(),
|
||||
bitrate: z.number(),
|
||||
initStart: z.number(),
|
||||
initEnd: z.number(),
|
||||
indexStart: z.number(),
|
||||
indexEnd: z.number(),
|
||||
width: z.number(),
|
||||
height: z.number(),
|
||||
fps: z.number(),
|
||||
contentLength: z.number()
|
||||
});
|
||||
|
||||
export const VideoStreamModel = z.object({
|
||||
url: z.string(),
|
||||
format: z.string(),
|
||||
quality: z.string(),
|
||||
mimeType: z.string(),
|
||||
codec: z.string().nullable(),
|
||||
audioTrackId: z.null(),
|
||||
audioTrackName: z.null(),
|
||||
audioTrackType: z.null(),
|
||||
audioTrackLocale: z.null(),
|
||||
videoOnly: z.boolean(),
|
||||
itag: z.number(),
|
||||
bitrate: z.number(),
|
||||
initStart: z.number(),
|
||||
initEnd: z.number(),
|
||||
indexStart: z.number(),
|
||||
indexEnd: z.number(),
|
||||
width: z.number(),
|
||||
height: z.number(),
|
||||
fps: z.number(),
|
||||
contentLength: z.number()
|
||||
});
|
||||
|
||||
export const ChapterModel = z.object({
|
||||
title: z.string(),
|
||||
image: z.string(),
|
||||
start: z.number()
|
||||
});
|
||||
|
||||
export const PreviewFrameModel = z.object({
|
||||
urls: z.array(z.string()),
|
||||
frameWidth: z.number(),
|
||||
frameHeight: z.number(),
|
||||
totalCount: z.number(),
|
||||
durationPerFrame: z.number(),
|
||||
framesPerPageX: z.number(),
|
||||
framesPerPageY: z.number()
|
||||
});
|
||||
|
||||
export const StreamModel = z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
uploadDate: z.coerce.date(),
|
||||
uploader: z.string(),
|
||||
uploaderUrl: z.string(),
|
||||
uploaderAvatar: z.string().url(),
|
||||
thumbnailUrl: z.string().url(),
|
||||
hls: z.string().url(),
|
||||
dash: z.null(),
|
||||
lbryId: z.null(),
|
||||
category: z.string(),
|
||||
license: z.string(),
|
||||
visibility: z.string(),
|
||||
tags: z.array(z.string()),
|
||||
metaInfo: z.array(z.unknown()),
|
||||
uploaderVerified: z.boolean(),
|
||||
duration: z.number(),
|
||||
views: z.number(),
|
||||
likes: z.number(),
|
||||
dislikes: z.number(),
|
||||
uploaderSubscriberCount: z.number(),
|
||||
audioStreams: AudioStreamModel.array(),
|
||||
videoStreams: VideoStreamModel.array(),
|
||||
relatedStreams: ItemModel.array(),
|
||||
subtitles: z.array(z.unknown()),
|
||||
livestream: z.boolean(),
|
||||
proxyUrl: z.string().url(),
|
||||
chapters: ChapterModel.array(),
|
||||
previewFrames: PreviewFrameModel.array()
|
||||
});
|
||||
|
||||
type Stream = z.infer<typeof StreamModel>;
|
||||
|
||||
export default Stream;
|
@ -0,0 +1,33 @@
|
||||
import { Video } from "./video";
|
||||
|
||||
export type VideoItem = Video & { type: "video" };
|
||||
|
||||
export interface ChannelItem {
|
||||
type: "channel";
|
||||
name: string;
|
||||
id: string;
|
||||
thumbnail: string;
|
||||
subscribers: number;
|
||||
videos: number;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface PlaylistItem {
|
||||
type: "playlist";
|
||||
title: string;
|
||||
id: string;
|
||||
author: {
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
numberOfVideos: number;
|
||||
thumbnail: string;
|
||||
videos?: {
|
||||
title: string;
|
||||
id: string;
|
||||
duration: number;
|
||||
thumbnail: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export type Item = VideoItem | ChannelItem | PlaylistItem;
|
@ -1,38 +1,6 @@
|
||||
import { Video } from "../video";
|
||||
|
||||
export type VideoResult = Video & { type: "video" };
|
||||
|
||||
export interface ChannelResult {
|
||||
type: "channel";
|
||||
name: string;
|
||||
id: string;
|
||||
thumbnail: string;
|
||||
subscribers: number;
|
||||
videos: number;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface PlaylistResult {
|
||||
type: "playlist";
|
||||
title: string;
|
||||
id: string;
|
||||
author: {
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
numberOfVideos: number;
|
||||
thumbnail: string;
|
||||
videos?: {
|
||||
title: string;
|
||||
id: string;
|
||||
duration: number;
|
||||
thumbnail: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export type SearchItems = (VideoResult | ChannelResult | PlaylistResult)[];
|
||||
import { Item } from "../item";
|
||||
|
||||
export interface SearchResults {
|
||||
items: SearchItems;
|
||||
items: Item[];
|
||||
nextCursor: string;
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
import { Item } from "./item";
|
||||
import { Video } from "./video";
|
||||
|
||||
export interface Stream {
|
||||
video: Video;
|
||||
keywords: string[];
|
||||
likes: number;
|
||||
dislikes: number;
|
||||
category: string;
|
||||
related: Item[];
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
export const videoUrl = (videoId: string): string => `/watch?v=${videoId}`;
|
||||
|
||||
export const channelUrl = (channelId: string): string =>
|
||||
`/channel/${channelId}`;
|
Loading…
Reference in new issue