Flags

Flags are the primary way the library separates entities between different environments and indicates special properties of streams.

For example, some sources only give back content that has the CORS headers set to allow anyone, so that source gets the flag CORS_ALLOWED. Now if you set your target to BROWSER, sources without that flag won't even get listed.

Sometimes a source will block netlify or cloudflare. Making self hosted proxies on P-Stream impossible. In cases where it would break some user's experiences, we should require the extension.

Available Flags

  • CORS_ALLOWED: Headers from the output streams are set to allow any origin.
  • IP_LOCKED: The streams are locked by IP: requester and watcher must be the same.
  • CF_BLOCKED: (Cosmetic) Indicates the source/embed blocks Cloudflare IPs. For actual enforcement, remove CORS_ALLOWED or add IP_LOCKED.
  • PROXY_BLOCKED: (Cosmetic) Indicates streams shouldn't be proxied. For actual enforcement, remove CORS_ALLOWED or add IP_LOCKED.

How Flags Affect Target Compatibility

Stream-Level Flags Impact

With CORS_ALLOWED:

  • ✅ Browser targets (can fetch and play streams)
  • ✅ Extension targets (bypass needed restrictions)
  • ✅ Native targets (direct stream access)

Without CORS_ALLOWED:

  • ❌ Browser targets (CORS restrictions block access)
  • ✅ Extension targets (can bypass CORS)
  • ✅ Native targets (no CORS restrictions)

With IP_LOCKED:

  • ❌ Proxy setups (different IP between request and playback)
  • ✅ Direct connections (same IP for request and playback)
  • ✅ Extension targets (when user has consistent IP)

With CF_BLOCKED (cosmetic only):

  • 🏷️ Informational label indicating Cloudflare issues
  • ⚠️ Still requires removing CORS_ALLOWED or adding IP_LOCKED for actual enforcement

With PROXY_BLOCKED (cosmetic only):

  • 🏷️ Informational label indicating proxy incompatibility
  • ⚠️ Still requires removing CORS_ALLOWED or adding IP_LOCKED for actual enforcement

Provider-Level Flags Impact

With CORS_ALLOWED:

  • Source appears for all target types
  • Individual streams still need appropriate flags

Without CORS_ALLOWED:

  • Source only appears for extension/native targets
  • Hidden entirely from browser-only users

Important: Cosmetic vs Enforcement Flags

Cosmetic flags (CF_BLOCKED, PROXY_BLOCKED) are informational labels only. They don't enforce any behavior.

Enforcement flags (CORS_ALLOWED, IP_LOCKED) actually control stream compatibility:

  • Remove all flags: Most common way to make streams extension/native-only (no browser support)
  • Add IP_LOCKED: Prevents proxy usage when consistentIpForRequests is false (rarely needed - most extension-only streams just use no flags)

The Golden Rule

Extension-only providers: Remove all flags (most common case) when streams only work with extensions (e.g., need special headers or IP restrictions that only extensions can bypass).

Universal providers: Include CORS_ALLOWED flags when using M3U8 proxies or streams that can work across all targets.

Comprehensive Flags Guide

For detailed information about using flags in your scrapers, including:

  • When and how to use each flag
  • Provider-level vs stream-level flags
  • Best practices and examples
  • How flags affect stream playback

See the Flags System section in Advanced Concepts.

Quick Reference

import { flags } from '@/entrypoint/utils/targets';
import { createM3U8ProxyUrl } from '@/utils/proxy';

// Extension-only streams (MOST COMMON - just remove all flags)
return {
  stream: [{
    id: 'primary',
    type: 'hls',
    playlist: createM3U8ProxyUrl(originalUrl, ctx.features, headers),
    headers,
    flags: [], // No flags = extension/native only, but this case doesn't make sense because the stream is getting proxied.
    captions: []
  }]
};

// Universal streams with CORS support
return {
  stream: [{
    id: 'primary',
    type: 'hls', 
    playlist: createM3U8ProxyUrl(originalUrl, ctx.features, headers),
    headers, // again listing headers twice so the extension can use them.
    flags: [flags.CORS_ALLOWED], // Works across all targets
    captions: []
  }]
};

// Direct streams (no proxy needed)
return {
  stream: [{
    id: 'primary',
    type: 'hls',
    playlist: 'https://example.com/playlist.m3u8',
    flags: [flags.CORS_ALLOWED], // Stream can be played directly in browsers
    captions: []
  }]
};

// Extension-only streams (usual approach - just remove all flags)
return {
  stream: [{
    id: 'primary',
    type: 'hls',
    playlist: 'https://example.com/playlist.m3u8',
    flags: [], // No flags = extension/native only (most common)
    captions: []
  }]
};

// Cloudflare-blocked streams with cosmetic label (if needed)
return {
  stream: [{
    id: 'primary',
    type: 'hls',
    playlist: 'https://example.com/playlist.m3u8',
    flags: [flags.CF_BLOCKED], // Cosmetic only - still extension/native only due to no CORS_ALLOWED
    captions: []
  }]
};

// IP-locked streams (when you specifically need consistent IP)
return {
  stream: [{
    id: 'primary',
    type: 'hls',
    playlist: 'https://example.com/playlist.m3u8',
    flags: [flags.IP_LOCKED], // Prevents proxy usage when IP consistency required
    captions: []
  }]
};

// IP-locked streams (when you specifically need consistent IP)
return {
  stream: [{
    id: 'primary',
    type: 'hls',
    playlist: 'https://example.com/playlist.m3u8',
    flags: [flags.IP_LOCKED], // Prevents proxy usage when IP consistency required
    captions: []
  }]
};

// Provider-level flags affect source visibility
export const myScraper = makeSourcerer({
  id: 'my-scraper',
  name: 'My Scraper', 
  rank: 150,
  flags: [flags.CORS_ALLOWED], // Source shows for all targets
  scrapeMovie: comboScraper,
  scrapeShow: comboScraper,
});