Skip to main content
CinePro Core is in active development. Providers may be added, updated, or removed at any time. Check the changelog for updates.

Overview

CinePro Core aggregates streaming sources from multiple independent providers. Each provider is a TypeScript class that extends BaseProvider from @omss/framework and declares what content types it supports — movies, TV shows, or both. The framework handles all the heavy lifting: routing, response formatting, TMDB validation, caching, and proxying. You only write the source-fetching logic.

Built-in Providers

CinePro Core ships with the following providers out of the box. All providers live under src/providers/ and are auto-discovered at startup.

Directory Structure

Each provider lives in its own folder inside src/providers/. A typical provider contains a single TypeScript file named after the provider:
src
providers
vidsrc
vidsrc.ts
vidzee
vidzee.ts
your-provider
your-provider.ts

Auto-Discovery

CinePro Core uses @omss/framework’s built-in auto-discovery feature to register providers automatically at startup. You do not need to manually import or register each provider. In src/server.ts, providers are loaded with a single call:
const registry = server.getRegistry();
await registry.discoverProviders(path.join(__dirname, './providers/'));
The discoverProviders method scans the given directory recursively, finds every class that extends BaseProvider, instantiates it, and registers it with the server — all without any manual wiring.
The path passed to discoverProviders is resolved relative to the compiled output directory (i.e. dist/providers/ in production). If you add a new provider folder, no changes to server.ts are needed.

Adding a New Provider

1

Create the provider folder and file

Create a new directory and TypeScript file under src/providers/:
mkdir src/providers/my-provider
touch src/providers/my-provider/my-provider.ts
2

Implement BaseProvider

Extend BaseProvider from @omss/framework and implement the required properties and methods:
import { BaseProvider } from '@omss/framework';
import type {
    ProviderCapabilities,
    ProviderMediaObject,
    ProviderResult
} from '@omss/framework';

export class MyProvider extends BaseProvider {
    // Unique machine-readable identifier
    readonly id = 'my-provider';

    // Human-readable display name
    readonly name = 'My Provider';

    // Set to false to disable without removing the file
    readonly enabled = true;

    readonly BASE_URL = 'https://provider.example.com';
    readonly HEADERS = {
        'User-Agent': 'Mozilla/5.0',
        Referer: 'https://provider.example.com'
    };

    // Declare what content types this provider handles
    readonly capabilities: ProviderCapabilities = {
        supportedContentTypes: ['movies', 'tv']
    };

    async getMovieSources(media: ProviderMediaObject): Promise<ProviderResult> {
        try {
            // Your source-fetching logic here
            return {
                sources: [
                    {
                        url: this.createProxyUrl(streamUrl, this.HEADERS),
                        type: 'hls',
                        quality: '1080p',
                        audioTracks: [{ language: 'en', label: 'English' }],
                        provider: { id: this.id, name: this.name }
                    }
                ],
                subtitles: [],
                diagnostics: []
            };
        } catch (error) {
            return {
                sources: [],
                subtitles: [],
                diagnostics: [
                    {
                        code: 'PROVIDER_ERROR',
                        message: error instanceof Error ? error.message : 'Unknown error',
                        field: '',
                        severity: 'error'
                    }
                ]
            };
        }
    }

    async getTVSources(media: ProviderMediaObject): Promise<ProviderResult> {
        // Implement TV logic or return an empty result
        return { sources: [], subtitles: [], diagnostics: [] };
    }

    // Optional but recommended
    async healthCheck(): Promise<boolean> {
        try {
            const response = await fetch(this.BASE_URL, { method: 'HEAD' });
            return response.ok;
        } catch {
            return false;
        }
    }
}
3

Start the server

That’s it. No registration or imports needed — discoverProviders picks up the new class automatically on the next server start.
npm run dev

Key Interface Properties

PropertyTypeRequiredDescription
idstringUnique machine-readable identifier (lowercase, no spaces)
namestringHuman-readable display name
enabledbooleanToggle the provider without removing it
BASE_URLstringProvider’s base URL, used as the default Referer
HEADERSobjectDefault HTTP headers for all requests
capabilitiesProviderCapabilitiesDeclares supported content types (movies, tv)
getMovieSourcesasync methodReturns sources for a given movie
getTVSourcesasync methodReturns sources for a given TV episode
healthCheckasync methodOptionalReturns true if the provider is reachable

ProviderMediaObject Fields

The media argument passed to getMovieSources and getTVSources contains:
FieldTypeDescription
tmdbIdnumberTMDB ID for the title
type"movie" | "tv"Content type
snumber | undefinedSeason number (TV only)
enumber | undefinedEpisode number (TV only)

Best Practices

Never return a raw stream URL directly. Use this.createProxyUrl(url, headers) so the framework can forward required headers (e.g. Referer, Origin) through its built-in proxy. This keeps stream URLs playable from the browser without CORS issues.
url: this.createProxyUrl(streamUrl, this.HEADERS)
Providers must never throw uncaught exceptions — the framework expects a ProviderResult regardless of success or failure. Wrap all logic in try/catch and return a diagnostics entry on error. This lets the API aggregate results across all providers even if one fails.
catch (error) {
    return {
        sources: [],
        subtitles: [],
        diagnostics: [{
            code: 'PROVIDER_ERROR',
            message: error instanceof Error ? error.message : 'Unknown error',
            field: '',
            severity: 'error'
        }]
    };
}
If you want to temporarily disable a provider, set readonly enabled = false rather than deleting the file. This keeps the code in place and makes it trivial to re-enable later.
Provider instances are reused across requests. Do not store per-request state in instance variables. Use local variables inside getMovieSources / getTVSources for anything request-specific.

Further Reading