refactor: extract env access into helpers

This commit is contained in:
WebStreamr 2025-06-04 10:29:50 +00:00
parent 64823685cc
commit 77fc20cfff
No known key found for this signature in database
10 changed files with 57 additions and 12 deletions

View file

@ -2,7 +2,7 @@ import { Request, Response, Router } from 'express';
import winston from 'winston';
import { Handler } from '../handler';
import { Config, Context } from '../types';
import { StreamResolver } from '../utils';
import { envIsProd, StreamResolver } from '../utils';
export class StreamController {
public readonly router: Router;
@ -38,7 +38,7 @@ export class StreamController {
const { streams, ttl } = await this.streamResolver.resolve(ctx, handlers, type, id);
if (ttl && process.env['NODE_ENV'] === 'production') {
if (ttl && envIsProd()) {
res.setHeader('Cache-Control', `max-age=${ttl / 1000}, public`);
}

View file

@ -13,7 +13,7 @@ import {
} from './handler';
import { ExtractorRegistry } from './extractor';
import { ConfigureController, ManifestController, StreamController } from './controller';
import { Fetcher, StreamResolver } from './utils';
import { envGet, envIsProd, Fetcher, StreamResolver } from './utils';
const logger = winston.createLogger({
transports: [
@ -54,7 +54,7 @@ addon.use((_req: Request, res: Response, next: NextFunction) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', '*');
if (process.env['NODE_ENV'] === 'production') {
if (envIsProd()) {
res.setHeader('Cache-Control', 'max-age=10, public');
}
@ -69,7 +69,7 @@ addon.get('/', (_req: Request, res: Response) => {
res.redirect('/configure');
});
const port = parseInt(process.env['PORT'] || '51546');
const port = parseInt(envGet('PORT') || '51546');
addon.listen(port, () => {
logger.info(`Add-on Repository URL: http://127.0.0.1:${port}/manifest.json`);
});

View file

@ -1,5 +1,6 @@
// Adapted version of https://github.com/Stremio/stremio-addon-sdk/blob/v1.6.2/src/landingTemplate.js
import { ManifestWithConfig } from './types';
import { envGet } from './utils';
const STYLESHEET = `
* {
@ -274,7 +275,7 @@ export function landingTemplate(manifest: ManifestWithConfig) {
<div class="separator"></div>
${process.env['CONFIGURATION_DESCRIPTION'] || ''}
${envGet('CONFIGURATION_DESCRIPTION') || ''}
<div class="separator"></div>

View file

@ -6,6 +6,7 @@ import { Context, TIMEOUT, UrlResult } from '../types';
import { Handler } from '../handler';
import { BlockedError, NotFoundError, QueueIsFullError } from '../error';
import { languageFromCountryCode } from './languageFromCountryCode';
import { envGetAppName } from './env';
interface ResolveResponse {
streams: Stream[];
@ -54,7 +55,7 @@ export class StreamResolver {
handlerErrorOccurred = true;
streams.push({
name: process.env['MANIFEST_NAME'] || 'WebStreamr',
name: envGetAppName(),
title: [`🔗 ${handler.label}`, this.logErrorAndReturnNiceString(ctx, handler.id, error)].join('\n'),
ytId: 'E4WlUXrJgy4',
});
@ -130,7 +131,7 @@ export class StreamResolver {
};
private readonly buildName = (ctx: Context, urlResult: UrlResult): string => {
let name = process.env['MANIFEST_NAME'] || 'WebStreamr';
let name = envGetAppName();
name += urlResult.meta.height ? ` ${urlResult.meta.height}P` : ' N/A';

View file

@ -2,6 +2,7 @@ import fs from 'node:fs';
import slugify from 'slugify';
import winston from 'winston';
import { Context } from '../../types';
import { envGet } from '../env';
const { Fetcher } = jest.requireActual('../Fetcher');
class MockedFetcher {
@ -39,7 +40,7 @@ class MockedFetcher {
} else {
let response;
try {
if (process.env['TEST_UPDATE_FIXTURES']) {
if (envGet('TEST_UPDATE_FIXTURES')) {
response = await fetch(url, this.fetcher.getInit(ctx, url, init));
} else {
console.error(`No fixture found at "${path}".`);

32
src/utils/env.test.ts Normal file
View file

@ -0,0 +1,32 @@
import { envGet, envGetAppId, envGetAppName, envIsProd } from './env';
describe('env', () => {
test('envGet', () => {
expect(envGet('NODE_ENV')).toBe('test');
});
test('envGetAppId', () => {
expect(envGetAppId()).toBe('webstreamr');
process.env['MANIFEST_ID'] = 'webstreamr.dev';
expect(envGetAppId()).toBe('webstreamr.dev');
delete process.env['MANIFEST_ID'];
});
test('envGetAppName', () => {
expect(envGetAppName()).toBe('WebStreamr');
process.env['MANIFEST_NAME'] = 'WebStreamr | dev';
expect(envGetAppName()).toBe('WebStreamr | dev');
delete process.env['MANIFEST_NAME'];
});
test('envIsProd', () => {
expect(envIsProd()).toBeFalsy();
const previousNodeEnv = process.env['NODE_ENV'];
process.env['NODE_ENV'] = 'production';
expect(envIsProd()).toBeTruthy();
process.env['NODE_ENV'] = previousNodeEnv;
});
});

7
src/utils/env.ts Normal file
View file

@ -0,0 +1,7 @@
export const envGet = (name: string): string | undefined => process.env[name];
export const envGetAppId = (): string => process.env['MANIFEST_ID'] || 'webstreamr';
export const envGetAppName = (): string => process.env['MANIFEST_NAME'] || 'WebStreamr';
export const envIsProd = (): boolean => process.env['NODE_ENV'] === 'production';

View file

@ -1,3 +1,4 @@
export * from './env';
export * from './Fetcher';
export * from './StreamResolver';
export * from './embed';

View file

@ -1,15 +1,16 @@
import { flag } from 'country-emoji';
import { Handler } from '../handler';
import { Config, CountryCode, ManifestWithConfig } from '../types';
import { envGetAppId, envGetAppName } from './env';
import { languageFromCountryCode } from './languageFromCountryCode';
const typedEntries = <T extends object>(obj: T): [keyof T, T[keyof T]][] => (Object.entries(obj) as [keyof T, T[keyof T]][]);
export const buildManifest = (handlers: Handler[], config: Config): ManifestWithConfig => {
const manifest: ManifestWithConfig = {
id: process.env['MANIFEST_ID'] || 'webstreamr',
id: envGetAppId(),
version: '0.22.9', // x-release-please-version
name: process.env['MANIFEST_NAME'] || 'WebStreamr',
name: envGetAppName(),
description: 'Provides HTTP URLs from streaming websites.',
resources: [
'stream',

View file

@ -1,12 +1,13 @@
import { ImdbId } from './imdb';
import { Context } from '../types';
import { Fetcher } from './Fetcher';
import { envGet } from './env';
export interface TmdbId { id: number; series: number | undefined; episode: number | undefined }
export const getTmdbIdFromImdbId = async (ctx: Context, fetcher: Fetcher, imdbId: ImdbId): Promise<TmdbId> => {
const url = new URL(`https://api.themoviedb.org/3/find/${imdbId.id}?external_source=imdb_id`);
const config = { 'headers': { Authorization: 'Bearer ' + process.env['TMDB_ACCESS_TOKEN'] }, 'Content-Type': 'application/json' };
const config = { 'headers': { Authorization: 'Bearer ' + envGet('TMDB_ACCESS_TOKEN') }, 'Content-Type': 'application/json' };
const response = JSON.parse(await fetcher.text(ctx, url, config));
const id = (imdbId.series ? response.tv_results[0] : response.movie_results[0])?.id;