mirror of
https://github.com/neosubhamoy/neodlp.git
synced 2026-03-22 12:35:49 +05:30
feat: added support for youtube po token generation
This commit is contained in:
83
src/App.tsx
83
src/App.tsx
@@ -25,6 +25,7 @@ import { Toaster as Sonner } from "@/components/ui/sonner";
|
||||
import { toast } from "sonner";
|
||||
import { useLogger } from "@/helpers/use-logger";
|
||||
import useDownloader from "@/helpers/use-downloader";
|
||||
import usePotServer from "@/helpers/use-pot-server";
|
||||
|
||||
export default function App({ children }: { children: React.ReactNode }) {
|
||||
const { data: downloadStates, isSuccess: isSuccessFetchingDownloadStates } = useFetchAllDownloadStates();
|
||||
@@ -39,6 +40,7 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
const setIsUsingDefaultSettings = useSettingsPageStatesStore((state) => state.setIsUsingDefaultSettings);
|
||||
const setSettingsKey = useSettingsPageStatesStore((state) => state.setSettingsKey);
|
||||
const appVersion = useSettingsPageStatesStore(state => state.appVersion);
|
||||
const isRunningPotServer = useSettingsPageStatesStore(state => state.isRunningPotServer);
|
||||
const ytDlpVersion = useSettingsPageStatesStore(state => state.ytDlpVersion);
|
||||
const setYtDlpVersion = useSettingsPageStatesStore((state) => state.setYtDlpVersion);
|
||||
const setIsFetchingYtDlpVersion = useSettingsPageStatesStore((state) => state.setIsFetchingYtDlpVersion);
|
||||
@@ -50,6 +52,7 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
download_dir: DOWNLOAD_DIR,
|
||||
theme: APP_THEME,
|
||||
color_scheme: APP_COLOR_SCHEME,
|
||||
use_potoken: USE_POTOKEN,
|
||||
} = useSettingsPageStatesStore(state => state.settings);
|
||||
|
||||
const erroredDownloadIds = useDownloaderPageStatesStore((state) => state.erroredDownloadIds);
|
||||
@@ -64,9 +67,11 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
const { updateYtDlp } = useYtDlpUpdater();
|
||||
const { registerToMac } = useMacOsRegisterer();
|
||||
const { checkForAppUpdate } = useAppUpdater();
|
||||
const { startPotServer, stopPotServer } = usePotServer();
|
||||
const setKvPairsKey = useKvPairsStatesStore((state) => state.setKvPairsKey);
|
||||
const ytDlpUpdateLastCheck = useKvPairsStatesStore(state => state.kvPairs.ytdlp_update_last_check);
|
||||
const macOsRegisteredVersion = useKvPairsStatesStore(state => state.kvPairs.macos_registered_version);
|
||||
const linuxRegisteredVersion = useKvPairsStatesStore(state => state.kvPairs.linux_registered_version);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const downloadStatusUpdater = useUpdateDownloadStatus();
|
||||
@@ -76,7 +81,9 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
|
||||
const hasRunYtDlpAutoUpdateRef = useRef(false);
|
||||
const hasRunAppUpdateCheckRef = useRef(false);
|
||||
const hasRunPotServerStatusCheckRef = useRef(false);
|
||||
const isRegisteredToMacOsRef = useRef(false);
|
||||
const isRegisteredToLinuxRef = useRef(false);
|
||||
const pendingErrorUpdatesRef = useRef<Set<string>>(new Set());
|
||||
|
||||
const { fetchVideoMetadata, startDownload, pauseDownload, resumeDownload, cancelDownload, processQueuedDownloads } = useDownloader();
|
||||
@@ -98,6 +105,19 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
appWindow.onCloseRequested(handleCloseRequested);
|
||||
}, []);
|
||||
|
||||
// Cleanup before page refresh/unload
|
||||
useEffect(() => {
|
||||
const handleBeforeUnload = (_event: BeforeUnloadEvent) => {
|
||||
if (isRunningPotServer) {
|
||||
stopPotServer();
|
||||
}
|
||||
};
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
};
|
||||
}, [stopPotServer]);
|
||||
|
||||
// Listen for websocket messages
|
||||
useEffect(() => {
|
||||
const unlisten = listen<WebSocketMessage>('websocket-message', (event) => {
|
||||
@@ -272,6 +292,32 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
}, [isSettingsStatePropagated, isKvPairsStatePropagated]);
|
||||
|
||||
// Check POT server status and auto-start if enabled
|
||||
useEffect(() => {
|
||||
// Only run once when both settings and KV pairs are loaded
|
||||
if (!isSettingsStatePropagated || !isKvPairsStatePropagated) {
|
||||
console.log("Skipping POT server status check, waiting for configs to load...");
|
||||
return;
|
||||
}
|
||||
// Skip if we've already run the POT server status check once
|
||||
if (hasRunPotServerStatusCheckRef.current) {
|
||||
console.log("POT server status check already performed in this session, skipping");
|
||||
return;
|
||||
}
|
||||
hasRunPotServerStatusCheckRef.current = true;
|
||||
console.log("Checking POT server status with loaded config values:", {
|
||||
usePotoken: USE_POTOKEN,
|
||||
});
|
||||
if (USE_POTOKEN) {
|
||||
console.log("Auto-starting POT server...");
|
||||
startPotServer().catch((error) => {
|
||||
console.error("Error starting POT server:", error);
|
||||
});
|
||||
} else {
|
||||
console.log("Skipping POT server auto-start, not enabled.");
|
||||
}
|
||||
}, [isSettingsStatePropagated, isKvPairsStatePropagated]);
|
||||
|
||||
// Check for MacOS auto-registration
|
||||
useEffect(() => {
|
||||
// Only run once when both settings and KV pairs are loaded
|
||||
@@ -307,6 +353,41 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
}, [isSettingsStatePropagated, isKvPairsStatePropagated]);
|
||||
|
||||
// Check for Linux auto-registration
|
||||
useEffect(() => {
|
||||
// Only run once when both settings and KV pairs are loaded
|
||||
if (!isSettingsStatePropagated || !isKvPairsStatePropagated) {
|
||||
console.log("Skipping Linux auto registration, waiting for configs to load...");
|
||||
return;
|
||||
}
|
||||
// Skip if we've already run the linux auto-registration once
|
||||
if (isRegisteredToLinuxRef.current) {
|
||||
console.log("Linux auto registration check already performed in this session, skipping");
|
||||
return;
|
||||
}
|
||||
isRegisteredToLinuxRef.current = true;
|
||||
console.log("Checking Linux auto registration with loaded config values:", {
|
||||
appVersion: appVersion,
|
||||
registeredVersion: linuxRegisteredVersion
|
||||
});
|
||||
if (currentPlatform === 'linux' && (!linuxRegisteredVersion || linuxRegisteredVersion !== appVersion)) {
|
||||
console.log("Running Linux auto registration...");
|
||||
LOG.info('NEODLP', 'Running Linux registration');
|
||||
registerToMac().then((result: { success: boolean, message: string }) => {
|
||||
if (result.success) {
|
||||
console.log("Linux registration successful:", result.message);
|
||||
LOG.info('NEODLP', 'Linux registration successful');
|
||||
} else {
|
||||
console.error("Linux registration failed:", result.message);
|
||||
LOG.error('NEODLP', `Linux registration failed: ${result.message}`);
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error("Error during Linux registration:", error);
|
||||
LOG.error('NEODLP', `Error during Linux registration: ${error}`);
|
||||
});
|
||||
}
|
||||
}, [isSettingsStatePropagated, isKvPairsStatePropagated]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccessFetchingDownloadStates && downloadStates) {
|
||||
// console.log("Download States fetched successfully:", downloadStates);
|
||||
@@ -341,7 +422,7 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
});
|
||||
});
|
||||
|
||||
const timeoutIds: NodeJS.Timeout[] = [];
|
||||
const timeoutIds: ReturnType<typeof setTimeout>[] = [];
|
||||
unexpectedErrors.forEach((downloadId) => {
|
||||
pendingErrorUpdatesRef.current.add(downloadId);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Switch } from "@/components/ui/switch";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { toast } from "sonner";
|
||||
import { BadgeCheck, BellRing, BrushCleaning, Bug, Cookie, ExternalLink, FilePen, FileVideo, Folder, FolderOpen, Github, Globe, Heart, Info, Loader2, LucideIcon, Mail, Monitor, Moon, Package, Scale, ShieldMinus, SquareTerminal, Sun, Terminal, Timer, Trash, TriangleAlert, WandSparkles, Wifi, Wrench } from "lucide-react";
|
||||
import { BadgeCheck, BellRing, BrushCleaning, Bug, Cookie, ExternalLink, FilePen, FileVideo, Folder, FolderOpen, Github, Globe, Heart, Info, KeyRound, Loader2, LucideIcon, Mail, Monitor, Moon, Package, Scale, ShieldMinus, SquareTerminal, Sun, Terminal, Timer, Trash, TriangleAlert, WandSparkles, Wifi, Wrench } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Slider } from "@/components/ui/slider";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -35,6 +35,7 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import neosubhamoyImage from "@/assets/images/neosubhamoy.jpg";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { NumberInput } from "@/components/custom/numberInput";
|
||||
import usePotServer from "@/helpers/use-pot-server";
|
||||
|
||||
const proxyUrlSchema = z.object({
|
||||
url: z.url({
|
||||
@@ -106,6 +107,19 @@ const requestSleepIntervalSchema = z.object({
|
||||
}),
|
||||
})
|
||||
|
||||
const potServerPortSchema = z.object({
|
||||
port: z.coerce.number<number>({
|
||||
error: (issue) => issue.input === undefined || issue.input === null || issue.input === ""
|
||||
? "POT Server Port is required"
|
||||
: "POT Server Port must be a valid number"
|
||||
}).int({
|
||||
message: "POT Server Port must be an integer"
|
||||
}).min(4000, {
|
||||
message: "POT Server Port must be at least 4000"
|
||||
}).max(5000, {
|
||||
message: "POT Server Port must be at most 5000"
|
||||
}),
|
||||
});
|
||||
|
||||
function AppGeneralSettings() {
|
||||
const { saveSettingsKey } = useSettings();
|
||||
@@ -1234,6 +1248,155 @@ function AppDelaySettings() {
|
||||
);
|
||||
}
|
||||
|
||||
function AppPoTokenSettings() {
|
||||
const formResetTrigger = useSettingsPageStatesStore(state => state.formResetTrigger);
|
||||
const acknowledgeFormReset = useSettingsPageStatesStore(state => state.acknowledgeFormReset);
|
||||
|
||||
const usePotoken = useSettingsPageStatesStore(state => state.settings.use_potoken);
|
||||
const disableInnertube = useSettingsPageStatesStore(state => state.settings.disable_innertube);
|
||||
const potServerPort = useSettingsPageStatesStore(state => state.settings.pot_server_port);
|
||||
const useCustomCommands = useSettingsPageStatesStore(state => state.settings.use_custom_commands);
|
||||
const isRunningPotServer = useSettingsPageStatesStore(state => state.isRunningPotServer);
|
||||
const isStartingPotServer = useSettingsPageStatesStore(state => state.isStartingPotServer);
|
||||
const isChangingPotServerPort = useSettingsPageStatesStore(state => state.isChangingPotServerPort);
|
||||
const setIsChangingPotServerPort = useSettingsPageStatesStore(state => state.setIsChangingPotServerPort);
|
||||
|
||||
const { saveSettingsKey } = useSettings();
|
||||
const { startPotServer, stopPotServer } = usePotServer();
|
||||
|
||||
const potServerPortForm = useForm<z.infer<typeof potServerPortSchema>>({
|
||||
resolver: zodResolver(potServerPortSchema),
|
||||
defaultValues: {
|
||||
port: potServerPort,
|
||||
},
|
||||
mode: "onChange",
|
||||
});
|
||||
const watchedPotServerPort = potServerPortForm.watch("port");
|
||||
const { errors: potServerPortFormErrors } = potServerPortForm.formState;
|
||||
|
||||
async function handlePotServerPortSubmit(values: z.infer<typeof potServerPortSchema>) {
|
||||
setIsChangingPotServerPort(true);
|
||||
try {
|
||||
saveSettingsKey('pot_server_port', values.port);
|
||||
if (isRunningPotServer) {
|
||||
await stopPotServer();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await startPotServer(values.port);
|
||||
}
|
||||
toast.success("POT Server Port updated", {
|
||||
description: `PO Token Server Port changed to ${values.port}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error changing PO Token Server Port:", error);
|
||||
toast.error("Failed to change POT Server Port", {
|
||||
description: "An error occurred while trying to change the PO Token Server Port. Please try again.",
|
||||
});
|
||||
} finally {
|
||||
setIsChangingPotServerPort(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (formResetTrigger > 0) {
|
||||
potServerPortForm.reset();
|
||||
acknowledgeFormReset();
|
||||
}
|
||||
}, [formResetTrigger]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="potoken">
|
||||
<h3 className="font-semibold">PO Token</h3>
|
||||
<p className="text-xs text-muted-foreground mb-3">Generate proof-of-origin token for youtube to make seem your traffic more legitimate (bypasses some bot-protection checks, sometimes requires cookies)</p>
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<Switch
|
||||
id="use-potoken"
|
||||
checked={usePotoken}
|
||||
onCheckedChange={async (checked) => {
|
||||
saveSettingsKey('use_potoken', checked);
|
||||
if (checked) {
|
||||
await startPotServer();
|
||||
} else {
|
||||
await stopPotServer();
|
||||
}
|
||||
}}
|
||||
disabled={useCustomCommands || isStartingPotServer || isChangingPotServerPort}
|
||||
/>
|
||||
<Label htmlFor="use-potoken">Use PO Token</Label>
|
||||
</div>
|
||||
<Label className="text-xs text-muted-foreground flex items-center">
|
||||
<span className="mr-1">NeoDLP POT Server is</span>
|
||||
{isStartingPotServer ? (
|
||||
<span className="text-amber-600 dark:text-amber-500 underline">Starting</span>
|
||||
) : isRunningPotServer ? (
|
||||
<span className="text-emerald-600 dark:text-emerald-500 underline">Running</span>
|
||||
) : (
|
||||
<span className="text-red-600 dark:text-red-500 underline">Not Running</span>
|
||||
)}
|
||||
{isRunningPotServer && potServerPort ? (
|
||||
<span className="ml-1">on Port {potServerPort}</span>
|
||||
) : null}
|
||||
</Label>
|
||||
</div>
|
||||
<div className="disable-innertube">
|
||||
<h3 className="font-semibold">Disable Innertube</h3>
|
||||
<p className="text-xs text-muted-foreground mb-3">Disable the usage of innertube api for potoken generation (falls back to legacy mode, use only if normal potoken is not working)</p>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
id="disable-innertube"
|
||||
checked={disableInnertube}
|
||||
onCheckedChange={(checked) => saveSettingsKey('disable_innertube', checked)}
|
||||
disabled={useCustomCommands || !usePotoken}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pot-server-port">
|
||||
<h3 className="font-semibold">POT Server Port</h3>
|
||||
<p className="text-xs text-muted-foreground mb-3">Change neodlp proof-of-origin token server port</p>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Form {...potServerPortForm}>
|
||||
<form onSubmit={potServerPortForm.handleSubmit(handlePotServerPortSubmit)} className="flex gap-4 w-full" autoComplete="off">
|
||||
<FormField
|
||||
control={potServerPortForm.control}
|
||||
name="port"
|
||||
disabled={!usePotoken || useCustomCommands || isChangingPotServerPort || isStartingPotServer}
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormControl>
|
||||
<NumberInput
|
||||
className="w-full"
|
||||
placeholder="Enter port number"
|
||||
min={0}
|
||||
readOnly={useCustomCommands}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<Label htmlFor="port" className="text-xs text-muted-foreground">(Current: {potServerPort}) (Default: 4416, Range: 4000-5000)</Label>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!watchedPotServerPort || Number(watchedPotServerPort) === potServerPort || Object.keys(potServerPortFormErrors).length > 0 || !usePotoken || useCustomCommands || isChangingPotServerPort || isStartingPotServer}
|
||||
>
|
||||
{isChangingPotServerPort ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
Changing
|
||||
</>
|
||||
) : (
|
||||
'Change'
|
||||
)}
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function AppNotificationSettings() {
|
||||
const { saveSettingsKey } = useSettings();
|
||||
|
||||
@@ -1296,12 +1459,14 @@ function AppNotificationSettings() {
|
||||
|
||||
function AppCommandSettings() {
|
||||
const { saveSettingsKey } = useSettings();
|
||||
const { startPotServer, stopPotServer } = usePotServer();
|
||||
|
||||
const formResetTrigger = useSettingsPageStatesStore(state => state.formResetTrigger);
|
||||
const acknowledgeFormReset = useSettingsPageStatesStore(state => state.acknowledgeFormReset);
|
||||
|
||||
const useCustomCommands = useSettingsPageStatesStore(state => state.settings.use_custom_commands);
|
||||
const customCommands = useSettingsPageStatesStore(state => state.settings.custom_commands);
|
||||
const usePotoken = useSettingsPageStatesStore(state => state.settings.use_potoken);
|
||||
|
||||
const setDownloadConfigurationKey = useDownloaderPageStatesStore((state) => state.setDownloadConfigurationKey);
|
||||
const resetDownloadConfiguration = useDownloaderPageStatesStore((state) => state.resetDownloadConfiguration);
|
||||
@@ -1379,9 +1544,14 @@ function AppCommandSettings() {
|
||||
<Switch
|
||||
id="use-custom-commands"
|
||||
checked={useCustomCommands}
|
||||
onCheckedChange={(checked) => {
|
||||
onCheckedChange={async(checked) => {
|
||||
saveSettingsKey('use_custom_commands', checked)
|
||||
resetDownloadConfiguration();
|
||||
if (checked && usePotoken) {
|
||||
await stopPotServer();
|
||||
} else if (!checked && usePotoken) {
|
||||
await startPotServer();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="use-custom-commands">Use Custom Commands</Label>
|
||||
@@ -1524,6 +1694,7 @@ function AppInfoSettings() {
|
||||
{ key: 'ffprobe', name: 'FFprobe', desc: 'Multimedia stream analyzer for retrieving media information', url: 'https://ffmpeg.org/ffprobe.html', license: 'LGPLv2.1+', licenseUrl: 'https://ffmpeg.org/legal.html' },
|
||||
{ key: 'deno', name: 'Deno', desc: 'The modern JavaScript/TypeScript runtime', url: 'https://deno.land/', license: 'MIT', licenseUrl: 'https://github.com/denoland/deno/blob/main/LICENSE.md' },
|
||||
{ key: 'aria2', name: 'Aria2', desc: 'Lightweight multi-protocol & multi-source download utility', url: 'https://aria2.github.io/', license: 'GPLv2+', licenseUrl: 'https://github.com/aria2/aria2/blob/master/COPYING' },
|
||||
{ Key: 'bgutil-pot-rs', name: 'BgUtils POT Provider (Rust)', desc: 'A high-performance YouTube POT (Proof-of-Origin Token) provider implemented in Rust', url: 'https://github.com/jim60105/bgutil-ytdlp-pot-provider-rs', license: 'GPLv3+', licenseUrl: 'https://github.com/jim60105/bgutil-ytdlp-pot-provider-rs/blob/master/LICENSE' },
|
||||
];
|
||||
const langDepsList = [
|
||||
{ key: 'tauri', name: 'Tauri', desc: 'Framework for building cross-platform, tiny and blazing fast binaries', url: 'https://tauri.app/', license: 'MIT, Apache-2.0', licenseUrl: 'https://github.com/tauri-apps/tauri/blob/dev/LICENSE_MIT' },
|
||||
@@ -1725,6 +1896,7 @@ export function ApplicationSettings() {
|
||||
{ key: 'cookies', label: 'Cookies', icon: Cookie, component: <AppCookiesSettings /> },
|
||||
{ key: 'sponsorblock', label: 'Sponsorblock', icon: ShieldMinus, component: <AppSponsorblockSettings /> },
|
||||
{ key: 'delay', label: 'Delay', icon: Timer, component: <AppDelaySettings /> },
|
||||
{ key: 'potoken', label: 'Potoken', icon: KeyRound, component: <AppPoTokenSettings /> },
|
||||
{ key: 'notifications', label: 'Notifications', icon: BellRing, component: <AppNotificationSettings /> },
|
||||
{ key: 'commands', label: 'Commands', icon: SquareTerminal, component: <AppCommandSettings /> },
|
||||
{ key: 'debug', label: 'Debug', icon: Bug, component: <AppDebugSettings /> },
|
||||
@@ -1810,7 +1982,7 @@ export function ApplicationSettings() {
|
||||
</TabsList>
|
||||
<div className="min-h-full flex flex-col w-full border-l border-border pl-4">
|
||||
{tabsList.map((tab) => (
|
||||
<TabsContent key={tab.key} value={tab.key} className={clsx("flex flex-col gap-4 min-h-120", tab.key === "info" ? "max-w-[80%]" : "max-w-[70%]")}>
|
||||
<TabsContent key={tab.key} value={tab.key} className={clsx("flex flex-col gap-4 min-h-130", tab.key === "info" ? "max-w-[80%]" : "max-w-[70%]")}>
|
||||
{tab.component}
|
||||
</TabsContent>
|
||||
))}
|
||||
|
||||
@@ -69,7 +69,11 @@ export default function useDownloader() {
|
||||
max_sleep_interval: MAX_SLEEP_INTERVAL,
|
||||
request_sleep_interval: REQUEST_SLEEP_INTERVAL,
|
||||
delay_playlist_only: DELAY_PLAYLIST_ONLY,
|
||||
use_potoken: USE_POTOKEN,
|
||||
disable_innertube: DISABLE_INNERTUBE,
|
||||
pot_server_port: POT_SERVER_PORT,
|
||||
} = useSettingsPageStatesStore(state => state.settings);
|
||||
const isRunningPotServer = useSettingsPageStatesStore(state => state.isRunningPotServer);
|
||||
|
||||
const expectedErrorDownloadIds = useDownloaderPageStatesStore((state) => state.expectedErrorDownloadIds);
|
||||
const addErroredDownload = useDownloaderPageStatesStore((state) => state.addErroredDownload);
|
||||
@@ -181,6 +185,16 @@ export default function useDownloader() {
|
||||
args.push('--sleep-requests', REQUEST_SLEEP_INTERVAL.toString(), '--sleep-interval', MIN_SLEEP_INTERVAL.toString(), '--max-sleep-interval', MAX_SLEEP_INTERVAL.toString());
|
||||
}
|
||||
}
|
||||
if ((!USE_CUSTOM_COMMANDS && !resumeState?.custom_command) && USE_POTOKEN) {
|
||||
if (!isRunningPotServer) {
|
||||
LOG.warning("NEODLP", "Looks like you want to use PO Token! But, NeoDLP POT Server is not running. PO Token generation will most likely fail!");
|
||||
}
|
||||
if (DISABLE_INNERTUBE) {
|
||||
args.push('--extractor-args', `youtubepot-bgutilhttp:base_url=http://localhost:${POT_SERVER_PORT};disable_innertube=1`);
|
||||
} else {
|
||||
args.push('--extractor-args', `youtubepot-bgutilhttp:base_url=http://localhost:${POT_SERVER_PORT}`);
|
||||
}
|
||||
}
|
||||
|
||||
const command = Command.sidecar('binaries/yt-dlp', args);
|
||||
|
||||
@@ -525,6 +539,17 @@ export default function useDownloader() {
|
||||
LOG.warning('NEODLP', `Looks like you are using aria2 for this yt-dlp download: ${downloadId}. Make sure aria2 is installed on your system if you are on macOS for this to work. Also, pause/resume might not work as expected especially on windows (using aria2 is not recommended for most downloads).`);
|
||||
}
|
||||
|
||||
if ((!USE_CUSTOM_COMMANDS && !resumeState?.custom_command) && USE_POTOKEN) {
|
||||
if (!isRunningPotServer) {
|
||||
LOG.warning("NEODLP", "Looks like you want to use PO Token! But, NeoDLP POT Server is not running. PO Token generation will most likely fail!");
|
||||
}
|
||||
if (DISABLE_INNERTUBE) {
|
||||
args.push('--extractor-args', `youtubepot-bgutilhttp:base_url=http://localhost:${POT_SERVER_PORT};disable_innertube=1`);
|
||||
} else {
|
||||
args.push('--extractor-args', `youtubepot-bgutilhttp:base_url=http://localhost:${POT_SERVER_PORT}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (resumeState || (!USE_CUSTOM_COMMANDS && USE_ARIA2)) {
|
||||
args.push('--continue');
|
||||
} else {
|
||||
|
||||
52
src/helpers/use-linux-registerer.ts
Normal file
52
src/helpers/use-linux-registerer.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { join, resourceDir, homeDir } from "@tauri-apps/api/path";
|
||||
import * as fs from "@tauri-apps/plugin-fs";
|
||||
import { useKvPairs } from "@/helpers/use-kvpairs";
|
||||
import { useSettingsPageStatesStore } from "@/services/store";
|
||||
|
||||
interface FileMap {
|
||||
source: string;
|
||||
destination: string;
|
||||
dir: string;
|
||||
}
|
||||
|
||||
export function useLinuxRegisterer() {
|
||||
const { saveKvPair } = useKvPairs();
|
||||
const appVersion = useSettingsPageStatesStore(state => state.appVersion);
|
||||
|
||||
const registerToLinux = async () => {
|
||||
try {
|
||||
const filesToCopy: FileMap[] = [
|
||||
{ source: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil.py', destination: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil.py', dir: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/' },
|
||||
{ source: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil_cli.py', destination: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil_cli.py', dir: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/' },
|
||||
{ source: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil_http.py', destination: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil_http.py', dir: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/' },
|
||||
];
|
||||
|
||||
const resourceDirPath = await resourceDir();
|
||||
const homeDirPath = await homeDir();
|
||||
|
||||
for (const file of filesToCopy) {
|
||||
const sourcePath = await join(resourceDirPath, file.source);
|
||||
const destinationDir = await join(homeDirPath, file.dir);
|
||||
const destinationPath = await join(homeDirPath, file.destination);
|
||||
|
||||
const dirExists = await fs.exists(destinationDir);
|
||||
if (dirExists) {
|
||||
await fs.copyFile(sourcePath, destinationPath);
|
||||
console.log(`File ${file.source} copied successfully to ${destinationPath}`);
|
||||
} else {
|
||||
await fs.mkdir(destinationDir, { recursive: true })
|
||||
console.log(`Created dir ${destinationDir}`);
|
||||
await fs.copyFile(sourcePath, destinationPath);
|
||||
console.log(`File ${file.source} copied successfully to ${destinationPath}`);
|
||||
}
|
||||
}
|
||||
saveKvPair('linux_registered_version', appVersion);
|
||||
return { success: true, message: 'Registered successfully' }
|
||||
} catch (error) {
|
||||
console.error('Error copying files:', error);
|
||||
return { success: false, message: 'Failed to register' }
|
||||
}
|
||||
}
|
||||
|
||||
return { registerToLinux };
|
||||
}
|
||||
@@ -3,27 +3,36 @@ import * as fs from "@tauri-apps/plugin-fs";
|
||||
import { useKvPairs } from "@/helpers/use-kvpairs";
|
||||
import { useSettingsPageStatesStore } from "@/services/store";
|
||||
|
||||
interface FileMap {
|
||||
source: string;
|
||||
destination: string;
|
||||
dir: string;
|
||||
}
|
||||
|
||||
export function useMacOsRegisterer() {
|
||||
const { saveKvPair } = useKvPairs();
|
||||
const appVersion = useSettingsPageStatesStore(state => state.appVersion);
|
||||
|
||||
|
||||
const registerToMac = async () => {
|
||||
try {
|
||||
const filesToCopy = [
|
||||
const filesToCopy: FileMap[] = [
|
||||
{ source: 'neodlp-autostart.plist', destination: 'Library/LaunchAgents/com.neosubhamoy.neodlp.plist', dir: 'Library/LaunchAgents/' },
|
||||
{ source: 'neodlp-msghost.json', destination: 'Library/Application Support/Google/Chrome/NativeMessagingHosts/com.neosubhamoy.neodlp.json', dir: 'Library/Application Support/Google/Chrome/NativeMessagingHosts/' },
|
||||
{ source: 'neodlp-msghost.json', destination: 'Library/Application Support/Chromium/NativeMessagingHosts/com.neosubhamoy.neodlp.json', dir: 'Library/Application Support/Chromium/NativeMessagingHosts/' },
|
||||
{ source: 'neodlp-msghost-moz.json', destination: 'Library/Application Support/Mozilla/NativeMessagingHosts/com.neosubhamoy.neodlp.json', dir: 'Library/Application Support/Mozilla/NativeMessagingHosts/' },
|
||||
{ source: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil.py', destination: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil.py', dir: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/' },
|
||||
{ source: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil_cli.py', destination: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil_cli.py', dir: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/' },
|
||||
{ source: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil_http.py', destination: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/getpot_bgutil_http.py', dir: 'yt-dlp-plugins/bgutil-ytdlp-pot-provider/yt_dlp_plugins/extractor/' },
|
||||
];
|
||||
|
||||
|
||||
const resourceDirPath = await resourceDir();
|
||||
const homeDirPath = await homeDir();
|
||||
|
||||
|
||||
for (const file of filesToCopy) {
|
||||
const sourcePath = await join(resourceDirPath, file.source);
|
||||
const destinationDir = await join(homeDirPath, file.dir);
|
||||
const destinationPath = await join(homeDirPath, file.destination);
|
||||
|
||||
|
||||
const dirExists = await fs.exists(destinationDir);
|
||||
if (dirExists) {
|
||||
await fs.copyFile(sourcePath, destinationPath);
|
||||
@@ -44,4 +53,4 @@ export function useMacOsRegisterer() {
|
||||
}
|
||||
|
||||
return { registerToMac };
|
||||
}
|
||||
}
|
||||
|
||||
86
src/helpers/use-pot-server.ts
Normal file
86
src/helpers/use-pot-server.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { useSettingsPageStatesStore } from "@/services/store";
|
||||
import { useLogger } from "@/helpers/use-logger";
|
||||
import { Command } from "@tauri-apps/plugin-shell";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export default function usePotServer() {
|
||||
const setIsRunningPotServer = useSettingsPageStatesStore(state => state.setIsRunningPotServer);
|
||||
const setIsStartingPotServer = useSettingsPageStatesStore(state => state.setIsStartingPotServer);
|
||||
const potServerPid = useSettingsPageStatesStore(state => state.potServerPid);
|
||||
const setPotServerPid = useSettingsPageStatesStore(state => state.setPotServerPid);
|
||||
const potServerPort = useSettingsPageStatesStore(state => state.settings.pot_server_port);
|
||||
const LOG = useLogger();
|
||||
|
||||
const stripAnsiAndLogPrefix = (line: string): string => {
|
||||
const stripped = line.replace(/\x1b\[\d+m/g, '');
|
||||
return stripped.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z\s+\w+\s+[\w:]+:\s*/, '');
|
||||
};
|
||||
|
||||
const startPotServer = async (port?: number) => {
|
||||
const runCommand = Command.sidecar('binaries/neodlp-pot', [
|
||||
'server',
|
||||
'--port',
|
||||
port ? port.toString() : potServerPort.toString(),
|
||||
]);
|
||||
|
||||
try {
|
||||
setIsStartingPotServer(true);
|
||||
LOG.info("NEODLP POT-SERVER", `Starting POT Server on port: ${port ?? potServerPort}`);
|
||||
|
||||
runCommand.on("close", (data) => {
|
||||
if (data.code === 0) {
|
||||
LOG.info("NEODLP POT-SERVER", `POT Server process exited with code: ${data.code}`);
|
||||
} else {
|
||||
LOG.error("NEODLP POT-SERVER", `POT Server process exited with code: ${data.code} (ignore if you manually stopped the server)`);
|
||||
}
|
||||
setIsRunningPotServer(false);
|
||||
setPotServerPid(null);
|
||||
});
|
||||
|
||||
runCommand.on("error", (error) => {
|
||||
LOG.error("NEODLP POT-SERVER", `Error running POT Server: ${error}`);
|
||||
setIsRunningPotServer(false);
|
||||
setPotServerPid(null);
|
||||
});
|
||||
|
||||
runCommand.stdout.on("data", (line) => {
|
||||
const cleanedLine = stripAnsiAndLogPrefix(line).trim();
|
||||
if (cleanedLine !== '') LOG.info("NEODLP POT-SERVER", cleanedLine);
|
||||
if (cleanedLine.startsWith("POT server")) {
|
||||
setIsRunningPotServer(true);
|
||||
}
|
||||
});
|
||||
|
||||
runCommand.stderr.on("data", (line) => {
|
||||
const cleanedLine = stripAnsiAndLogPrefix(line).trim();
|
||||
if (cleanedLine !== '') LOG.error("NEODLP POT-SERVER", cleanedLine);
|
||||
});
|
||||
|
||||
const child = await runCommand.spawn();
|
||||
setPotServerPid(child.pid);
|
||||
} catch (error) {
|
||||
LOG.error("NEODLP POT-SERVER", `Error starting POT Server: ${error}`);
|
||||
} finally {
|
||||
setIsStartingPotServer(false);
|
||||
}
|
||||
}
|
||||
|
||||
const stopPotServer = async () => {
|
||||
if (!potServerPid) {
|
||||
LOG.warning("NEODLP POT-SERVER", "No POT Server process found to stop.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
LOG.info("NEODLP POT-SERVER", `Stopping POT Server with PID: ${potServerPid}`);
|
||||
await invoke('kill_all_process', { pid: potServerPid });
|
||||
LOG.info("NEODLP POT-SERVER", "POT Server stopped successfully.");
|
||||
setIsRunningPotServer(false);
|
||||
setPotServerPid(null);
|
||||
} catch (error) {
|
||||
LOG.error("NEODLP POT-SERVER", `Error stopping POT Server: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return { startPotServer, stopPotServer};
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { useSettings } from "@/helpers/use-settings";
|
||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
|
||||
import { ExtensionSettings } from "@/components/pages/settings/extensionSettings";
|
||||
import { ApplicationSettings } from "@/components/pages/settings/applicationSettings";
|
||||
import usePotServer from "@/helpers/use-pot-server";
|
||||
|
||||
export default function SettingsPage() {
|
||||
const { setTheme } = useTheme();
|
||||
@@ -17,10 +18,12 @@ export default function SettingsPage() {
|
||||
const setActiveTab = useSettingsPageStatesStore(state => state.setActiveTab);
|
||||
|
||||
const isUsingDefaultSettings = useSettingsPageStatesStore(state => state.isUsingDefaultSettings);
|
||||
const isRunningPotServer = useSettingsPageStatesStore(state => state.isRunningPotServer);
|
||||
const appTheme = useSettingsPageStatesStore(state => state.settings.theme);
|
||||
const appColorScheme = useSettingsPageStatesStore(state => state.settings.color_scheme);
|
||||
|
||||
const { resetSettings } = useSettings();
|
||||
const { stopPotServer } = usePotServer();
|
||||
|
||||
useEffect(() => {
|
||||
const updateTheme = async () => {
|
||||
@@ -60,8 +63,11 @@ export default function SettingsPage() {
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={
|
||||
() => {
|
||||
resetSettings()
|
||||
async () => {
|
||||
resetSettings();
|
||||
if (isRunningPotServer) {
|
||||
await stopPotServer();
|
||||
}
|
||||
}
|
||||
}>Reset</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
|
||||
@@ -218,6 +218,9 @@ export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set)
|
||||
max_sleep_interval: 20,
|
||||
request_sleep_interval: 1,
|
||||
delay_playlist_only: true,
|
||||
use_potoken: false,
|
||||
disable_innertube: false,
|
||||
pot_server_port: 4416,
|
||||
// extension settings
|
||||
websocket_port: 53511
|
||||
},
|
||||
@@ -230,6 +233,10 @@ export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set)
|
||||
appUpdateDownloadProgress: 0,
|
||||
formResetTrigger: 0,
|
||||
resetAcknowledgements: 0,
|
||||
isRunningPotServer: false,
|
||||
isStartingPotServer: false,
|
||||
isChangingPotServerPort: false,
|
||||
potServerPid: null,
|
||||
setActiveTab: (tab) => set(() => ({ activeTab: tab })),
|
||||
setActiveSubAppTab: (tab) => set(() => ({ activeSubAppTab: tab })),
|
||||
setActiveSubExtTab: (tab) => set(() => ({ activeSubExtTab: tab })),
|
||||
@@ -296,6 +303,9 @@ export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set)
|
||||
max_sleep_interval: 20,
|
||||
request_sleep_interval: 1,
|
||||
delay_playlist_only: true,
|
||||
use_potoken: false,
|
||||
disable_innertube: false,
|
||||
pot_server_port: 4416,
|
||||
// extension settings
|
||||
websocket_port: 53511
|
||||
},
|
||||
@@ -315,12 +325,17 @@ export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set)
|
||||
acknowledgeFormReset: () => set((state) => ({
|
||||
resetAcknowledgements: state.resetAcknowledgements + 1
|
||||
})),
|
||||
setIsRunningPotServer: (isRunning) => set(() => ({ isRunningPotServer: isRunning })),
|
||||
setIsStartingPotServer: (isStarting) => set(() => ({ isStartingPotServer: isStarting })),
|
||||
setIsChangingPotServerPort: (isChanging) => set(() => ({ isChangingPotServerPort: isChanging })),
|
||||
setPotServerPid: (pid) => set(() => ({ potServerPid: pid }))
|
||||
}));
|
||||
|
||||
export const useKvPairsStatesStore = create<KvPairsStatesStore>((set) => ({
|
||||
kvPairs: {
|
||||
ytdlp_update_last_check: null,
|
||||
macos_registered_version: null
|
||||
macos_registered_version: null,
|
||||
linux_registered_version: null
|
||||
},
|
||||
setKvPairsKey: (key, value) => set((state) => ({
|
||||
kvPairs: {
|
||||
|
||||
@@ -6,4 +6,5 @@ export interface KvStoreTable {
|
||||
export interface KvStore {
|
||||
ytdlp_update_last_check: number | null;
|
||||
macos_registered_version: string | null;
|
||||
}
|
||||
linux_registered_version: string | null;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,9 @@ export interface Settings {
|
||||
max_sleep_interval: number;
|
||||
request_sleep_interval: number;
|
||||
delay_playlist_only: boolean;
|
||||
use_potoken: boolean;
|
||||
disable_innertube: boolean;
|
||||
pot_server_port: number;
|
||||
// extension settings
|
||||
websocket_port: number;
|
||||
}
|
||||
|
||||
@@ -110,6 +110,10 @@ export interface SettingsPageStatesStore {
|
||||
appUpdateDownloadProgress: number;
|
||||
formResetTrigger: number;
|
||||
resetAcknowledgements: number;
|
||||
isRunningPotServer: boolean;
|
||||
isStartingPotServer: boolean;
|
||||
isChangingPotServerPort: boolean;
|
||||
potServerPid: number | null;
|
||||
setActiveTab: (tab: string) => void;
|
||||
setActiveSubAppTab: (tab: string) => void;
|
||||
setActiveSubExtTab: (tab: string) => void;
|
||||
@@ -130,6 +134,10 @@ export interface SettingsPageStatesStore {
|
||||
setAppUpdateDownloadProgress: (progress: number) => void;
|
||||
triggerFormReset: () => void;
|
||||
acknowledgeFormReset: () => void;
|
||||
setIsRunningPotServer: (isRunning: boolean) => void;
|
||||
setIsStartingPotServer: (isStarting: boolean) => void;
|
||||
setIsChangingPotServerPort: (isChanging: boolean) => void;
|
||||
setPotServerPid: (pid: number | null) => void;
|
||||
}
|
||||
|
||||
export interface KvPairsStatesStore {
|
||||
|
||||
Reference in New Issue
Block a user