Skip to content

Commit

Permalink
Merge pull request #140 from mykhailodanilenko/feature/homepage-api
Browse files Browse the repository at this point in the history
Add homepage API calls
  • Loading branch information
mykhailodanilenko authored Aug 30, 2024
2 parents 7956e87 + 94594e1 commit 3aa2870
Show file tree
Hide file tree
Showing 17 changed files with 394 additions and 287 deletions.
26 changes: 26 additions & 0 deletions OwnTube.tv/api/axiosInstance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import axios, { AxiosInstance } from "axios";

export abstract class AxiosInstanceBasedApi {
protected constructor(debugLogging: boolean = false) {
this.createAxiosInstance();
this.debugLogging = debugLogging;
}

debugLogging: boolean = false;
// Our Axios instance, https://axios-http.com/docs/instance
instance!: AxiosInstance;

/**
* Create the Axios instance with app identifier and request/response interceptors
*/
private createAxiosInstance(): void {
this.instance = axios.create({
withCredentials: false,
headers: {
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
"User-Agent": "OwnTube.tv/1.0.0 (https://app.owntube.tv)",
},
});
}
}
33 changes: 33 additions & 0 deletions OwnTube.tv/api/categoriesApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import i18n from "../i18n";
import { AxiosInstanceBasedApi } from "./axiosInstance";

/**
* Get available categories from the PeerTube backend `/api/v1/videos/categories` API
*
* @description https://docs.joinpeertube.org/api-rest-reference.html#tag/Video/operation/getCategories
*/
export class CategoriesApi extends AxiosInstanceBasedApi {
constructor() {
super();
}

/**
* Get a list of available categories from the PeerTube instance
*
* @param [baseURL] - Selected instance url
* @returns List of available categories
*/
async getCategories(baseURL: string): Promise<Array<{ name: string; id: number }>> {
try {
const response = await this.instance.get<Record<number, string>>("videos/categories", {
baseURL: `https://${baseURL}/api/v1`,
});

return Object.entries(response.data).map(([id, name]) => ({ id: Number(id), name }));
} catch (error: unknown) {
throw new Error(i18n.t("errors.failedToFetchAvailableCategories", { error: (error as Error).message }));
}
}
}

export const CategoriesApiImpl = new CategoriesApi();
79 changes: 79 additions & 0 deletions OwnTube.tv/api/channelsApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { VideoChannel } from "@peertube/peertube-types";
import { Video } from "@peertube/peertube-types/peertube-models/videos/video.model";
import { GetVideosVideo } from "./models";
import i18n from "../i18n";
import { AxiosInstanceBasedApi } from "./axiosInstance";
import { commonQueryParams } from "./constants";

/**
* Get channels from the PeerTube backend `/api/v1/video-channels` API
*
* @description https://docs.joinpeertube.org/api-rest-reference.html#tag/Video-Channels/operation/getVideoChannels
*/
export class ChannelsApi extends AxiosInstanceBasedApi {
constructor() {
super();
}

/**
* Get a list of channels from the PeerTube instance
*
* @param [baseURL] - Selected instance url
* @returns List of channels
*/
async getChannels(baseURL: string): Promise<{ data: VideoChannel[]; total: number }> {
try {
const response = await this.instance.get<{ data: VideoChannel[]; total: number }>("video-channels", {
params: { sort: "-createdAt", count: 30 },
baseURL: `https://${baseURL}/api/v1`,
});

return response.data;
} catch (error: unknown) {
throw new Error(i18n.t("errors.failedToFetchTotalVids", { error: (error as Error).message }));
}
}

/**
* Get a list of videos on an instance channel
*
* @param [baseURL] - Selected instance url
* @param [channelHandle] - Channel handle
* @param [count] - Count of videos to fetch
* @returns List of channel videos
*/
async getChannelVideos(
baseURL: string,
channelHandle: string,
count: number,
): Promise<{ data: GetVideosVideo[]; total: number }> {
try {
const response = await this.instance.get(`video-channels/${channelHandle}/videos`, {
params: { ...commonQueryParams, sort: "-originallyPublishedAt", count },
baseURL: `https://${baseURL}/api/v1`,
});

return {
data: response.data.data.map((video: Video) => {
return {
uuid: video.uuid,
name: video.name,
category: video.category,
description: video.description,
thumbnailPath: video.thumbnailPath,
duration: video.duration,
channel: video.channel,
publishedAt: video.publishedAt,
originallyPublishedAt: video.originallyPublishedAt,
views: video.views,
};
}),
total: response.data.total,
};
} catch (error: unknown) {
throw new Error(i18n.t("errors.failedToFetchTotalVids", { error: (error as Error).message }));
}
}
}

export const ChannelsApiImpl = new ChannelsApi();
12 changes: 12 additions & 0 deletions OwnTube.tv/api/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Common query parameters for fetching videos that are classified as "local", "non-live", and "Safe-For-Work"
import { VideosCommonQuery } from "@peertube/peertube-types";

export const commonQueryParams: VideosCommonQuery = {
start: 0,
count: 15,
sort: "createdAt",
nsfw: "false",
isLocal: true,
isLive: false,
skipCount: false,
};
91 changes: 36 additions & 55 deletions OwnTube.tv/api/peertubeVideosApi.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import axios, { AxiosInstance } from "axios";
import { VideosCommonQuery } from "@peertube/peertube-types";
import { Video } from "@peertube/peertube-types/peertube-models/videos/video.model";
import { GetVideosVideo } from "./models";
import i18n from "../i18n";
import { commonQueryParams } from "./constants";
import { AxiosInstanceBasedApi } from "./axiosInstance";

/**
* Get videos from the PeerTube backend `/api/v1/videos` API
*
* @description https://docs.joinpeertube.org/api-rest-reference.html#tag/Video/operation/getVideos
*/
export class PeertubeVideosApi {
export class PeertubeVideosApi extends AxiosInstanceBasedApi {
constructor(maxChunkSize: number = 100, debugLogging: boolean = false) {
this.createAxiosInstance();
super();
this.maxChunkSize = maxChunkSize;
this.debugLogging = debugLogging;
}
Expand All @@ -29,34 +30,6 @@ export class PeertubeVideosApi {
return this._maxChunkSize;
}

// Common query parameters for fetching videos that are classified as "local", "non-live", and "Safe-For-Work"
private readonly commonQueryParams: VideosCommonQuery = {
start: 0,
count: 15,
sort: "createdAt",
nsfw: "false",
isLocal: true,
isLive: false,
skipCount: false,
};

// Our Axios instance, https://axios-http.com/docs/instance
private instance!: AxiosInstance;

/**
* Create the Axios instance with app identifier and request/response interceptors
*/
private createAxiosInstance(): void {
this.instance = axios.create({
withCredentials: false,
headers: {
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
"User-Agent": "OwnTube.tv/1.0.0 (https://app.owntube.tv)",
},
});
}

/**
* Get total number of "local", "non-live", and "Safe-For-Work" videos from the PeerTube instance
*
Expand All @@ -66,7 +39,7 @@ export class PeertubeVideosApi {
async getTotalVideos(baseURL: string): Promise<number> {
try {
const response = await this.instance.get("videos", {
params: { ...this.commonQueryParams, count: undefined },
params: { ...commonQueryParams, count: undefined },
baseURL: `https://${baseURL}/api/v1`,
});
return response.data.total as number;
Expand All @@ -79,19 +52,24 @@ export class PeertubeVideosApi {
* Get "local", "non-live", and "Safe-For-Work" videos from the PeerTube instance
*
* @param [baseURL] - Selected instance url
* @param [limit=15] - The maximum number of videos to fetch
* @param [queryParams] - Any custom query params
* @returns A list of videos, with a lot of additional details from the API removed
*/
async getVideos(baseURL: string, limit: number = 15): Promise<GetVideosVideo[]> {
async getVideos(
baseURL: string,
queryParams?: VideosCommonQuery,
): Promise<{ data: GetVideosVideo[]; total: number }> {
let rawVideos: Required<GetVideosVideo>[] = [];
let total: number = 0;
let limit = queryParams?.count || 15;
if (limit <= this.maxChunkSize) {
try {
rawVideos = (
await this.instance.get("videos", {
params: { ...this.commonQueryParams, count: limit },
baseURL: `https://${baseURL}/api/v1`,
})
).data.data as Required<GetVideosVideo>[];
const response = await this.instance.get("videos", {
params: { ...commonQueryParams, ...(queryParams || {}), count: limit },
baseURL: `https://${baseURL}/api/v1`,
});
total = response.data.total;
rawVideos = response.data.data as Required<GetVideosVideo[]>;
} catch (error: unknown) {
throw new Error(i18n.t("errors.failedToFetchVids", { error: (error as Error).message }));
}
Expand All @@ -118,7 +96,7 @@ export class PeertubeVideosApi {
}
try {
const response = await this.instance.get("videos", {
params: { ...this.commonQueryParams, count: fetchCount, start: offset },
params: { ...commonQueryParams, count: fetchCount, start: offset },
baseURL: `https://${baseURL}/api/v1`,
});
rawTotal = response.data.total as number;
Expand All @@ -133,20 +111,23 @@ export class PeertubeVideosApi {
}
}

return rawVideos.map((video) => {
return {
uuid: video.uuid,
name: video.name,
category: video.category,
description: video.description,
thumbnailPath: video.thumbnailPath,
duration: video.duration,
channel: video.channel,
publishedAt: video.publishedAt,
originallyPublishedAt: video.originallyPublishedAt,
views: video.views,
};
});
return {
data: rawVideos.map((video) => {
return {
uuid: video.uuid,
name: video.name,
category: video.category,
description: video.description,
thumbnailPath: video.thumbnailPath,
duration: video.duration,
channel: video.channel,
publishedAt: video.publishedAt,
originallyPublishedAt: video.originallyPublishedAt,
views: video.views,
};
}),
total,
};
}

/**
Expand Down
Loading

0 comments on commit 3aa2870

Please sign in to comment.