136 lines
4.9 KiB
TypeScript
136 lines
4.9 KiB
TypeScript
import { config, DEFAULT_CONFIG } from "../config";
|
|
import {
|
|
isAvailable as isYtdlpAvailable,
|
|
checkUrl,
|
|
addToFastQueue,
|
|
addToSlowQueue,
|
|
getQueues,
|
|
cancelSlowQueueItem,
|
|
} from "../ytdlp";
|
|
import { getOrCreateUser } from "./helpers";
|
|
import { createPlaylist, generateUniquePlaylistName } from "../db";
|
|
|
|
const ytdlpConfig = config.ytdlp || DEFAULT_CONFIG.ytdlp!;
|
|
|
|
// POST /api/fetch - fetch from URL (yt-dlp)
|
|
export async function handleFetch(req: Request, server: any): Promise<Response> {
|
|
const { user, headers } = getOrCreateUser(req, server);
|
|
if (!user) {
|
|
return Response.json({ error: "Authentication required" }, { status: 401 });
|
|
}
|
|
if (user.is_guest) {
|
|
return Response.json({ error: "Guests cannot fetch from URLs" }, { status: 403 });
|
|
}
|
|
|
|
if (!ytdlpConfig.enabled) {
|
|
return Response.json({ error: "Feature disabled" }, { status: 403 });
|
|
}
|
|
if (!isYtdlpAvailable()) {
|
|
return Response.json({ error: "yt-dlp not available" }, { status: 503 });
|
|
}
|
|
|
|
try {
|
|
const { url } = await req.json();
|
|
if (!url || typeof url !== "string") {
|
|
return Response.json({ error: "URL is required" }, { status: 400 });
|
|
}
|
|
|
|
console.log(`[Fetch] ${user.username} checking URL: ${url}`);
|
|
|
|
const info = await checkUrl(url);
|
|
|
|
if (info.type === "playlist") {
|
|
if (!ytdlpConfig.allowPlaylists) {
|
|
return Response.json({ error: "Playlist downloads are disabled" }, { status: 403 });
|
|
}
|
|
return Response.json(info, { headers });
|
|
} else {
|
|
const item = addToFastQueue(info.url, info.title, user.id);
|
|
console.log(`[Fetch] ${user.username} queued: ${info.title} (id=${item.id})`);
|
|
return Response.json({
|
|
type: "single",
|
|
id: item.id,
|
|
title: item.title,
|
|
queueType: "fast"
|
|
}, { headers });
|
|
}
|
|
} catch (e: any) {
|
|
console.error("[Fetch] Error:", e);
|
|
return Response.json({ error: e.message || "Invalid request" }, { status: 400 });
|
|
}
|
|
}
|
|
|
|
// POST /api/fetch/confirm - confirm playlist download
|
|
export async function handleFetchConfirm(req: Request, server: any): Promise<Response> {
|
|
const { user, headers } = getOrCreateUser(req, server);
|
|
if (!user) {
|
|
return Response.json({ error: "Authentication required" }, { status: 401 });
|
|
}
|
|
if (user.is_guest) {
|
|
return Response.json({ error: "Guests cannot fetch from URLs" }, { status: 403 });
|
|
}
|
|
if (!ytdlpConfig.enabled || !isYtdlpAvailable()) {
|
|
return Response.json({ error: "Feature not available" }, { status: 503 });
|
|
}
|
|
|
|
try {
|
|
const { items, playlistTitle } = await req.json();
|
|
if (!Array.isArray(items) || items.length === 0) {
|
|
return Response.json({ error: "Items required" }, { status: 400 });
|
|
}
|
|
|
|
// Auto-create playlist with unique name
|
|
const baseName = playlistTitle || "Imported Playlist";
|
|
const uniqueName = generateUniquePlaylistName(baseName, user.id);
|
|
const playlist = createPlaylist(uniqueName, user.id, `Imported from URL (${items.length} tracks)`);
|
|
|
|
console.log(`[Fetch] ${user.username} created playlist: ${uniqueName} (id=${playlist.id})`);
|
|
|
|
const queueItems = addToSlowQueue(items, user.id, { id: playlist.id, name: uniqueName });
|
|
const estimatedMinutes = Math.ceil(queueItems.length * ytdlpConfig.slowQueueInterval / 60);
|
|
const hours = Math.floor(estimatedMinutes / 60);
|
|
const mins = estimatedMinutes % 60;
|
|
const estimatedTime = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
|
|
|
|
console.log(`[Fetch] ${user.username} confirmed playlist: ${queueItems.length} items`);
|
|
|
|
return Response.json({
|
|
message: `Added ${queueItems.length} items to queue`,
|
|
queueType: "slow",
|
|
estimatedTime,
|
|
playlistId: playlist.id,
|
|
playlistName: uniqueName,
|
|
items: queueItems.map(i => ({ id: i.id, title: i.title }))
|
|
}, { headers });
|
|
} catch (e) {
|
|
console.error("[Fetch] Confirm error:", e);
|
|
return Response.json({ error: "Invalid request" }, { status: 400 });
|
|
}
|
|
}
|
|
|
|
// GET /api/fetch - get fetch queue status (all users see all tasks)
|
|
export function handleGetFetchQueue(req: Request, server: any): Response {
|
|
const { user, headers } = getOrCreateUser(req, server);
|
|
if (!user) {
|
|
return Response.json({ error: "Authentication required" }, { status: 401 });
|
|
}
|
|
|
|
const queues = getQueues(); // Return all queues, not filtered by user
|
|
return Response.json(queues, { headers });
|
|
}
|
|
|
|
// DELETE /api/fetch/:id - cancel a slow queue item
|
|
export function handleCancelFetchItem(req: Request, server: any, itemId: string): Response {
|
|
const { user, headers } = getOrCreateUser(req, server);
|
|
if (!user) {
|
|
return Response.json({ error: "Authentication required" }, { status: 401 });
|
|
}
|
|
|
|
const success = cancelSlowQueueItem(itemId, user.id);
|
|
if (success) {
|
|
return Response.json({ message: "Item cancelled" }, { headers });
|
|
} else {
|
|
return Response.json({ error: "Cannot cancel item (not found, not owned, or already downloading)" }, { status: 400, headers });
|
|
}
|
|
}
|