Merge remote-tracking branch 'origin/feat_player'

This commit is contained in:
Kentai Radiquum 2024-07-29 21:39:37 +05:00
parent bb437fe7ca
commit 25e31a7799
Signed by: Radiquum
GPG key ID: 858E8EE696525EED
62 changed files with 1508 additions and 701 deletions

View file

@ -0,0 +1,38 @@
import { NextResponse, NextRequest } from "next/server";
import { fetchDataViaGet, fetchDataViaPost } from "../utils";
import { API_URL } from "../config";
export async function GET(
req: NextRequest,
{ params }: { params: { endpoint: Array<string> } }
) {
const { endpoint } = params;
let API_V2: boolean | string =
req.nextUrl.searchParams.get("API_V2") || false;
if (API_V2 === "true") {
req.nextUrl.searchParams.delete("API_V2");
}
const query = req.nextUrl.searchParams.toString();
const url = `${API_URL}/${endpoint.join("/")}${query ? `?${query}` : ""}`;
const response = await fetchDataViaGet(url, API_V2);
return NextResponse.json(response);
}
export async function POST(
req: NextRequest,
{ params }: { params: { endpoint: Array<string> } }
) {
const { endpoint } = params;
let API_V2: boolean | string =
req.nextUrl.searchParams.get("API_V2") || false;
if (API_V2 === "true") {
req.nextUrl.searchParams.delete("API_V2");
}
const query = req.nextUrl.searchParams.toString();
const url = `${API_URL}/${endpoint.join("/")}${query ? `?${query}` : ""}`;
const body = JSON.stringify( await req.json());
const response = await fetchDataViaPost(url, body, API_V2);
return NextResponse.json(response);
}

View file

@ -1,43 +0,0 @@
import { NextResponse } from "next/server";
import { fetchDataViaGet } from "../utils";
import { ENDPOINTS } from "../config";
import { sort } from "../common";
const list = {
watching: 1,
planned: 2,
watched: 3,
delayed: 4,
abandoned: 5,
};
export async function GET(request) {
const page = parseInt(request.nextUrl.searchParams.get(["page"])) || 0;
const listName = request.nextUrl.searchParams.get(["list"]) || null;
const token = request.nextUrl.searchParams.get(["token"]) || null;
const sortName =
request.nextUrl.searchParams.get(["sort"]) || "adding_descending";
if (!token || token == "null") {
return NextResponse.json({ message: "No token provided" }, { status: 403 });
}
if (!listName || listName == "null") {
return NextResponse.json({ message: "No list provided" }, { status: 400 });
}
if (!list[listName]) {
return NextResponse.json({ message: "Unknown list" }, { status: 400 });
}
if (!sort[sortName]) {
return NextResponse.json({ message: "Unknown sort" }, { status: 400 });
}
let url = new URL(`${ENDPOINTS.user.bookmark}/${list[listName]}/${page}`);
url.searchParams.set("token", token);
url.searchParams.set("sort", sort[sortName]);
const response = await fetchDataViaGet(url.toString());
return NextResponse.json(response);
}

View file

@ -1,8 +0,0 @@
export const sort = {
adding_descending: 1,
adding_ascending: 2,
year_descending: 3,
year_ascending: 4,
alphabet_descending: 5,
alphabet_ascending: 6,
};

View file

@ -1,32 +0,0 @@
export const API_URL = "https://api.anixart.tv";
export const USER_AGENT =
"AnixartApp/8.2.1-23121216 (Android 11; SDK 30; arm64-v8a;)";
export const ENDPOINTS = {
release: {
info: `${API_URL}/release`,
episode: `${API_URL}/episode`,
},
user: {
profile: `${API_URL}/profile`,
bookmark: `${API_URL}/profile/list/all`,
history: `${API_URL}/history`,
favorite: `${API_URL}/favorite/all`,
},
filter: `${API_URL}/filter`,
auth: `${API_URL}/auth/signIn`,
// user: {
// history: `${API_URL}/history`,
// watching: `${API_URL}/profile/list/all/1`,
// planned: `${API_URL}/profile/list/all/2`,
// watched: `${API_URL}/profile/list/all/3`,
// delayed: `${API_URL}/profile/list/all/4`,
// abandoned: `${API_URL}/profile/list/all/5`,
// favorite: `${API_URL}/favorite`,
// },
search: `${API_URL}/search/releases`,
statistic: {
addHistory: `${API_URL}/history/add`,
markWatched: `${API_URL}/episode/watch`,
},
};

32
app/api/config.ts Normal file
View file

@ -0,0 +1,32 @@
export const API_URL = "https://api.anixart.tv";
export const API_PREFIX = "/api";
export const USER_AGENT =
"AnixartApp/8.2.1-23121216 (Android 11; SDK 30; arm64-v8a;)";
export const ENDPOINTS = {
release: {
info: `${API_PREFIX}/release`,
episode: `${API_PREFIX}/episode`,
},
user: {
profile: `${API_PREFIX}/profile`,
bookmark: `${API_PREFIX}/profile/list`,
history: `${API_PREFIX}/history`,
favorite: `${API_PREFIX}/favorite`,
},
filter: `${API_PREFIX}/filter`,
// user: {
// history: `${API_PREFIX}/history`,
// watching: `${API_PREFIX}/profile/list/all/1`,
// planned: `${API_PREFIX}/profile/list/all/2`,
// watched: `${API_PREFIX}/profile/list/all/3`,
// delayed: `${API_PREFIX}/profile/list/all/4`,
// abandoned: `${API_PREFIX}/profile/list/all/5`,
// favorite: `${API_PREFIX}/favorite`,
// },
search: `${API_URL}/search`,
statistic: {
addHistory: `${API_PREFIX}/history/add`,
markWatched: `${API_PREFIX}/episode/watch`,
},
};

View file

@ -1,26 +0,0 @@
import { NextResponse } from "next/server";
import { fetchDataViaGet } from "../utils";
import { ENDPOINTS } from "../config";
import { sort } from "../common";
export async function GET(request) {
const page = parseInt(request.nextUrl.searchParams.get(["page"])) || 0;
const token = request.nextUrl.searchParams.get(["token"]) || null;
const sortName = request.nextUrl.searchParams.get(["sort"]) || "adding_descending";
if (!token || token == "null") {
return NextResponse.json({ message: "No token provided" }, { status: 403 });
}
if (!sort[sortName]) {
return NextResponse.json({ message: "Unknown sort" }, { status: 400 });
}
let url = new URL(`${ENDPOINTS.user.favorite}/${page}`);
url.searchParams.set("token", token);
url.searchParams.set("sort", sort[sortName]);
const response = await fetchDataViaGet(url.toString());
return NextResponse.json(response);
}

View file

@ -1,19 +0,0 @@
import { NextResponse } from "next/server";
import { fetchDataViaGet } from "../utils";
import { ENDPOINTS } from "../config";
export async function GET(request) {
const page = parseInt(request.nextUrl.searchParams.get(["page"])) || 0;
const token = request.nextUrl.searchParams.get(["token"]) || null;
const sortName = request.nextUrl.searchParams.get(["sort"]) || "adding_descending";
if (!token || token == "null") {
return NextResponse.json({ message: "No token provided" }, { status: 403 });
}
let url = new URL(`${ENDPOINTS.user.history}/${page}`);
url.searchParams.set("token", token);
const response = await fetchDataViaGet(url.toString());
return NextResponse.json(response);
}

View file

@ -1,50 +0,0 @@
import { NextResponse } from "next/server";
import { fetchDataViaPost } from "../utils";
import { ENDPOINTS } from "../config";
export async function GET(request) {
const page = parseInt(request.nextUrl.searchParams.get(["page"])) || 0;
const status = request.nextUrl.searchParams.get(["status"]) || null;
const token = request.nextUrl.searchParams.get(["token"]) || null;
let statusId;
if (status == "last" || !status) {
statusId = null;
} else if (status == "finished") {
statusId = 1;
} else if (status == "ongoing") {
statusId = 2;
} else if (status == "announce") {
statusId = 3;
} else {
return NextResponse.json({ message: "Bad status" }, { status: 400 });
}
let url = new URL(`${ENDPOINTS.filter}/${page}`);
if (token) {
url.searchParams.set("token", token);
}
const data = {
country: null,
season: null,
sort: 0,
studio: null,
age_ratings: [],
category_id: null,
end_year: null,
episode_duration_from: null,
episode_duration_to: null,
episodes_from: null,
episodes_to: null,
genres: [],
profile_list_exclusions: [],
start_year: null,
status_id: statusId,
types: [],
is_genres_exclude_mode_enabled: false,
};
const response = await fetchDataViaPost(url.toString(), data);
return NextResponse.json(response);
}

View file

@ -1,21 +0,0 @@
import { NextResponse } from "next/server";
import { fetchDataViaGet } from "@/app/api/utils";
import { ENDPOINTS } from "@/app/api/config";
export async function GET(request, params) {
const token = request.nextUrl.searchParams.get(["token"]) || null;
let url = new URL(`${ENDPOINTS.user.profile}/${params["params"]["id"]}`);
if (token) {
url.searchParams.set("token", token);
}
const response = await fetchDataViaGet(url.toString());
if (!response) {
return NextResponse.json({ message: "Server Error" }, { status: 500 });
}
if (!response.profile) {
return NextResponse.json({ message: "Profile not found" }, { status: 404 });
}
return NextResponse.json(response);
}

View file

@ -1,15 +0,0 @@
import { NextResponse } from "next/server";
import { authorize } from "@/app/api/utils";
import { ENDPOINTS } from "@/app/api/config";
export async function POST(request) {
const response = await authorize(ENDPOINTS.auth, await request.json());
if (!response) {
return NextResponse.json({ message: "Server Error" }, { status: 500 });
}
if (!response.profile) {
return NextResponse.json({ message: "Profile not found" }, { status: 404 });
}
return NextResponse.json(response);
}

View file

@ -0,0 +1,14 @@
import { NextResponse, NextRequest } from "next/server";
import { authorize } from "#/api/utils";
import { API_URL } from "#/api/config";
export async function POST(request: NextRequest) {
const response = await authorize(`${API_URL}/auth/signIn`, await request.json());
if (!response) {
return NextResponse.json({ message: "Server Error" }, { status: 500 });
}
if (!response.profile) {
return NextResponse.json({ message: "Profile not found" }, { status: 404 });
}
return NextResponse.json(response);
}

View file

@ -1,21 +0,0 @@
import { NextResponse } from "next/server";
import { fetchDataViaPost } from "../utils";
import { ENDPOINTS } from "../config";
export async function GET(request) {
const page = parseInt(request.nextUrl.searchParams.get(["page"])) || 0;
const query = request.nextUrl.searchParams.get(["q"]) || null;
const token = request.nextUrl.searchParams.get(["token"]) || null;
let url = new URL(`${ENDPOINTS.search}/${page}`);
if (token) {
url.searchParams.set("token", token);
}
const data = { query, searchBy: 0 };
const response = await fetchDataViaPost(url.toString(), data, true);
if (!response) {
return NextResponse.json({ message: "Bad request" }, { status: 400 });
}
return NextResponse.json(response);
}

39
app/api/search/route.ts Normal file
View file

@ -0,0 +1,39 @@
import { NextResponse } from "next/server";
import { NextRequest } from "next/server";
import { fetchDataViaPost } from "../utils";
import { ENDPOINTS } from "../config";
export async function GET(request: NextRequest) {
const page = parseInt(request.nextUrl.searchParams.get("page")) || 0;
const query = request.nextUrl.searchParams.get("q") || null;
const token = request.nextUrl.searchParams.get("token") || null;
const where = request.nextUrl.searchParams.get("where") || "releases"
const searchBy = parseInt(request.nextUrl.searchParams.get("searchBy")) || 0
const list = parseInt(request.nextUrl.searchParams.get("list")) || null
let url: URL;
if (where == "releases") {
url = new URL(`${ENDPOINTS.search}/releases/${page}`);
} else if (where == "list") {
if (!list) { return NextResponse.json({ message: "List ID required" }, { status: 400 })}
if (!token) { return NextResponse.json({ message: "token required" }, { status: 400 })}
url = new URL(`${ENDPOINTS.search}/profile/list/${list}/${page}`);
}
if (token) {
url.searchParams.set("token", token);
}
const data = { query, searchBy };
const response = await fetchDataViaPost(
url.toString(),
JSON.stringify(data),
true
);
if (!response) {
return NextResponse.json({ message: "Bad request" }, { status: 400 });
}
return NextResponse.json(response);
}

View file

@ -1,126 +0,0 @@
import { USER_AGENT } from "./config";
export const HEADERS = {
"User-Agent": USER_AGENT,
"Content-Type": "application/json; charset=UTF-8",
};
export const fetchDataViaGet = async (url, API_V2) => {
if (API_V2) {
HEADERS["API-Version"] = "v2";
}
try {
const response = await fetch(url, {
headers: HEADERS,
});
if (response.status !== 200) {
throw new Error("Error fetching data");
}
const data = await response.json();
return data;
} catch (error) {
console.log(error);
}
};
export const fetchDataViaPost = async (url, body, API_V2) => {
if (API_V2) {
HEADERS["API-Version"] = "v2";
}
try {
const response = await fetch(url, {
method: "POST",
headers: HEADERS,
body: JSON.stringify(body),
});
if (response.status !== 200) {
throw new Error("Error fetching data");
}
const data = await response.json();
return data;
} catch (error) {
console.log(error);
}
};
export const authorize = async (url, data) => {
try {
const response = await fetch(
`${url}?login=${data.login}&password=${data.password}`,
{
method: "POST",
headers: {
"User-Agent": USER_AGENT,
Sign: "9aa5c7af74e8cd70c86f7f9587bde23d",
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
if (response.status !== 200) {
throw new Error("Error authorizing user");
}
return await response.json();
} catch (error) {
return error;
}
};
export function setJWT(user_id, jwt) {
const data = { jwt: jwt, user_id: user_id };
localStorage.setItem("JWT", JSON.stringify(data));
}
export function getJWT() {
const data = localStorage.getItem("JWT");
return JSON.parse(data);
}
export function removeJWT() {
localStorage.removeItem("JWT");
}
export function numberDeclension(number, one, two, five) {
if (number > 10 && [11, 12, 13, 14].includes(number % 100)) return five;
let last_num = number % 10;
if (last_num == 1) return one;
if ([2, 3, 4].includes(last_num)) return two;
if ([5, 6, 7, 8, 9, 0].includes(last_num)) return five;
}
export function unixToDate(unix) {
const date = new Date(unix * 1000);
return date.toLocaleString("ru-RU");
}
export function sinceUnixDate(unixInSeconds) {
const unix = Math.floor(unixInSeconds * 1000);
const date = new Date(unix);
const currentDate = new Date().valueOf();
const dateDifferenceSeconds = new Date(currentDate - unix) / 1000;
const minutes = Math.floor(dateDifferenceSeconds / 60)
const hours = Math.floor(dateDifferenceSeconds / 3600);
const days = Math.floor(dateDifferenceSeconds / 86400);
const minutesName = numberDeclension(minutes, "минута", "минуты", "минут");
const hoursName = numberDeclension(hours, "час", "часа", "часов");
const daysName = numberDeclension(days, "день", "дня", "дней");
if (dateDifferenceSeconds < 60) return "менее минуты назад";
if (dateDifferenceSeconds < 3600)
return `${minutes} ${minutesName} назад`;
if (dateDifferenceSeconds < 86400)
return `${hours} ${hoursName} назад`;
if (dateDifferenceSeconds < 2592000)
return `${days} ${daysName} назад`;
return date.toLocaleString("ru-RU").split(",")[0];
}
export function minutesToTime(min) {
const d = Math.floor(min / 1440); // 60*24
const h = Math.floor((min - d * 1440) / 60);
const m = Math.round(min % 60);
var dDisplay = d > 0 ? `${d} ${numberDeclension(d, "день", "дня", "дней")}, ` : "";
var hDisplay = h > 0 ? `${h} ${numberDeclension(h, "час", "часа", "часов")}, ` : "";
var mDisplay = m > 0 ? `${m} ${numberDeclension(m, "минута", "минуты", "минут")}` : "";
return dDisplay + hDisplay + mDisplay;
}

249
app/api/utils.ts Normal file
View file

@ -0,0 +1,249 @@
import { USER_AGENT, ENDPOINTS } from "./config";
export const HEADERS = {
"User-Agent": USER_AGENT,
"Content-Type": "application/json; charset=UTF-8",
};
export const fetchDataViaGet = async (
url: string,
API_V2: string | boolean = false
) => {
if (API_V2) {
HEADERS["API-Version"] = "v2";
}
try {
const response = await fetch(url, {
headers: HEADERS,
});
if (response.status !== 200) {
throw new Error("Error fetching data");
}
const data = await response.json();
return data;
} catch (error) {
console.log(error);
}
};
export const fetchDataViaPost = async (
url: string,
body: string,
API_V2: string | boolean = false
) => {
if (API_V2) {
HEADERS["API-Version"] = "v2";
}
try {
const response = await fetch(url, {
method: "POST",
headers: HEADERS,
body: body,
});
if (response.status !== 200) {
throw new Error("Error fetching data");
}
const data = await response.json();
return data;
} catch (error) {
console.log(error);
}
};
export const authorize = async (
url: string,
data: { login: string; password: string }
) => {
try {
const response = await fetch(
`${url}?login=${data.login}&password=${data.password}`,
{
method: "POST",
headers: {
"User-Agent": USER_AGENT,
Sign: "9aa5c7af74e8cd70c86f7f9587bde23d",
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
if (response.status !== 200) {
throw new Error("Error authorizing user");
}
return await response.json();
} catch (error) {
return error;
}
};
export function setJWT(user_id: number | string, jwt: string) {
const data = { jwt: jwt, user_id: user_id };
localStorage.setItem("JWT", JSON.stringify(data));
}
export function getJWT() {
const data = localStorage.getItem("JWT");
return JSON.parse(data);
}
export function removeJWT() {
localStorage.removeItem("JWT");
}
export function numberDeclension(
number: number,
one: string,
two: string,
five: string
) {
if (number > 10 && [11, 12, 13, 14].includes(number % 100)) return five;
let last_num = number % 10;
if (last_num == 1) return one;
if ([2, 3, 4].includes(last_num)) return two;
if ([5, 6, 7, 8, 9, 0].includes(last_num)) return five;
}
const months = [
"янв.",
"фев.",
"мар.",
"апр.",
"мая",
"июня",
"июля",
"авг.",
"сен.",
"окт.",
"ноя.",
"дек.",
];
export function unixToDate(unix: number) {
const date = new Date(unix * 1000);
return date.getDate() + " " + months[date.getMonth()] + " " + date.getFullYear();
}
export const getSeasonFromUnix = (unix: number) => {
const date = new Date(unix * 1000);
const month = date.getMonth();
if (month >= 3 && month <= 5) return "весна";
if (month >= 6 && month <= 8) return "лето";
if (month >= 9 && month <= 11) return "осень";
return "зима";
};
export function sinceUnixDate(unixInSeconds: number) {
const unix = Math.floor(unixInSeconds * 1000);
const date = new Date(unix);
const currentDate = new Date().valueOf();
const dateDifferenceSeconds = new Date(currentDate - unix).getTime() / 1000;
const minutes = Math.floor(dateDifferenceSeconds / 60);
const hours = Math.floor(dateDifferenceSeconds / 3600);
const days = Math.floor(dateDifferenceSeconds / 86400);
const minutesName = numberDeclension(minutes, "минута", "минуты", "минут");
const hoursName = numberDeclension(hours, "час", "часа", "часов");
const daysName = numberDeclension(days, "день", "дня", "дней");
if (dateDifferenceSeconds < 60) return "менее минуты назад";
if (dateDifferenceSeconds < 3600) return `${minutes} ${minutesName} назад`;
if (dateDifferenceSeconds < 86400) return `${hours} ${hoursName} назад`;
if (dateDifferenceSeconds < 2592000) return `${days} ${daysName} назад`;
return date.getDate() + " " + months[date.getMonth()] + " " + date.getFullYear();
}
export function minutesToTime(min: number) {
const d = Math.floor(min / 1440); // 60*24
const h = Math.floor((min - d * 1440) / 60);
const m = Math.round(min % 60);
var dDisplay =
d > 0 ? `${d} ${numberDeclension(d, "день", "дня", "дней")}, ` : "";
var hDisplay =
h > 0 ? `${h} ${numberDeclension(h, "час", "часа", "часов")}, ` : "";
var mDisplay =
m > 0 ? `${m} ${numberDeclension(m, "минута", "минуты", "минут")}` : "";
return dDisplay + hDisplay + mDisplay;
}
const StatusList: Record<string, null | number> = {
last: null,
finished: 1,
ongoing: 2,
announce: 3,
};
export async function _FetchHomePageReleases(
status: string,
token: string | null,
page: string | number = 0
) {
let statusId: null | number = null;
let categoryId: null | number = null;
if (status == "films") {
categoryId = 2;
} else {
statusId = StatusList[status];
}
const body = {
country: null,
season: null,
sort: 0,
studio: null,
age_ratings: [],
category_id: categoryId,
end_year: null,
episode_duration_from: null,
episode_duration_to: null,
episodes_from: null,
episodes_to: null,
genres: [],
profile_list_exclusions: [],
start_year: null,
status_id: statusId,
types: [],
is_genres_exclude_mode_enabled: false,
};
let url: string;
url = `${ENDPOINTS.filter}/${page}`;
if (token) {
url += `?token=${token}`;
}
const data: Object = fetch(url, {
method: "POST",
body: JSON.stringify(body),
})
.then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error("Error fetching data");
}
})
.then((data: Object) => {
return data;
})
.catch((error) => {
console.log(error);
return null;
});
return data;
}
export const BookmarksList = {
watching: 1,
planned: 2,
watched: 3,
delayed: 4,
abandoned: 5,
};
export const SortList = {
adding_descending: 1,
adding_ascending: 2,
year_descending: 3,
year_ascending: 4,
alphabet_descending: 5,
alphabet_ascending: 6,
};