fix(proxy): make proxied nzb URLs static

This commit is contained in:
Viren070
2025-10-18 22:31:52 +01:00
parent 481338afa3
commit f8847bfd19
6 changed files with 79 additions and 25 deletions
+2 -1
View File
@@ -99,7 +99,8 @@ export class NewznabAddon extends BaseNabAddon<NewznabAddonConfig, NewznabApi> {
urlsToProxy.map((url) => ({
url,
filename: url.split('/').pop(),
}))
})),
false // don't encrypt NZB URLs to make sure the URLs stay the same.
);
if (!proxiedUrls) {
throw new Error('Failed to proxy NZBs');
+7 -3
View File
@@ -60,7 +60,8 @@ export abstract class BaseProxy {
protected abstract getPublicIpEndpoint(): string;
protected abstract getPublicIpFromResponse(data: any): string | null;
protected abstract generateStreamUrls(
streams: ProxyStream[]
streams: ProxyStream[],
encrypt?: boolean
): Promise<string[] | null>;
public async getPublicIp(): Promise<string | null> {
@@ -133,7 +134,10 @@ export abstract class BaseProxy {
protected abstract getHeaders(): Record<string, string>;
public async generateUrls(streams: ProxyStream[]): Promise<string[] | null> {
public async generateUrls(
streams: ProxyStream[],
encrypt?: boolean
): Promise<string[] | null> {
if (!streams.length) {
return [];
}
@@ -143,7 +147,7 @@ export abstract class BaseProxy {
}
try {
let urls = await this.generateStreamUrls(streams);
let urls = await this.generateStreamUrls(streams, encrypt);
const publicUrl = this.config.publicUrl;
if (publicUrl && urls) {
const publicUrlObj = new URL(publicUrl);
+33 -16
View File
@@ -7,6 +7,7 @@ import {
encryptString,
decryptString,
Cache,
toUrlSafeBase64,
} from '../utils/index.js';
import path from 'path';
import z from 'zod';
@@ -111,25 +112,41 @@ export class BuiltinProxy extends BaseProxy {
}
protected override async generateStreamUrls(
streams: ProxyStream[]
streams: ProxyStream[],
encrypt: boolean = true
): Promise<string[] | null> {
const auth = BuiltinProxy.validateAuth(this.config.credentials);
return streams.map((stream) => {
const encryptedAuth = encryptString(
JSON.stringify({
username: auth.username,
password: auth.password,
})
);
const encryptedData = encryptString(
JSON.stringify({
url: stream.url,
filename: stream.filename,
requestHeaders: stream.headers?.request,
responseHeaders: stream.headers?.response,
})
);
return `${Env.BASE_URL}/api/v1/proxy/${encryptedAuth.data}.${encryptedData.data}/${encodeURIComponent(stream.filename ?? '')}`;
let authData = JSON.stringify({
username: auth.username,
password: auth.password,
});
let streamData = JSON.stringify({
url: stream.url,
filename: stream.filename,
requestHeaders: stream.headers?.request,
responseHeaders: stream.headers?.response,
});
if (encrypt) {
const { success, data, error } = encryptString(authData);
if (!success) {
throw new Error(`Failed to encrypt auth data: ${error}`);
}
authData = data;
} else {
authData = toUrlSafeBase64(authData);
}
if (encrypt) {
const { success, data, error } = encryptString(streamData);
if (!success) {
throw new Error(`Failed to encrypt stream data: ${error}`);
}
streamData = data;
} else {
streamData = toUrlSafeBase64(streamData);
}
return `${Env.BASE_URL}/api/v1/proxy/${encrypt ? 'e' : 'u'}.${authData}.${streamData}/${encodeURIComponent(stream.filename ?? '')}`;
});
}
}
+2 -1
View File
@@ -29,7 +29,8 @@ export class MediaFlowProxy extends BaseProxy {
}
protected async generateStreamUrls(
streams: ProxyStream[]
streams: ProxyStream[],
encrypt?: boolean
): Promise<string[] | null> {
const proxyUrl = this.generateProxyUrl('/generate_urls');
+2 -1
View File
@@ -33,7 +33,8 @@ export class StremThruProxy extends BaseProxy {
}
protected async generateStreamUrls(
streams: ProxyStream[]
streams: ProxyStream[],
encrypt?: boolean
): Promise<string[] | null> {
const proxyUrl = this.generateProxyUrl('/v0/proxy');
+33 -3
View File
@@ -5,6 +5,7 @@ import {
createLogger,
decryptString,
Env,
fromUrlSafeBase64,
getProxyAgent,
getTimeTakenSincePoint,
shouldProxy,
@@ -197,11 +198,40 @@ router.all(
try {
// decrypt and authenticate the request
const { encryptedAuthAndData } = req.params;
const [encryptedAuth, encryptedData] = encryptedAuthAndData.split('.');
// const [encodeMode, encryptedAuth, encryptedData] =
// encryptedAuthAndData.split('.');
const parts = encryptedAuthAndData.split('.');
let encodedAuth: string | undefined;
let encodedData: string | undefined;
let encodeMode: 'e' | 'u' | undefined;
if (parts.length == 2) {
encodeMode = 'e';
encodedAuth = parts[0];
encodedData = parts[1];
} else if (parts.length == 3) {
encodeMode = parts[0] as 'e' | 'u';
encodedAuth = parts[1];
encodedData = parts[2];
} else {
throw new APIError(
constants.ErrorCode.BAD_REQUEST,
undefined,
'Invalid encrypted auth and data'
);
}
const filename = req.params.filename as string | undefined;
const { data: rawData } = decryptString(encryptedData);
const { data: rawAuth } = decryptString(encryptedAuth);
let rawData: string | undefined;
let rawAuth: string | undefined;
if (encodeMode === 'e') {
const { data: streamData } = decryptString(encodedData);
const { data: authData } = decryptString(encodedAuth);
rawData = streamData ?? undefined;
rawAuth = authData ?? undefined;
} else {
rawAuth = fromUrlSafeBase64(encodedAuth);
rawData = fromUrlSafeBase64(encodedData);
}
if (!rawData || !rawAuth) {
logger.error(`[${requestId}] Decryption failed`);