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, removeCORS_ALLOWEDor addIP_LOCKED.PROXY_BLOCKED: (Cosmetic) Indicates streams shouldn't be proxied. For actual enforcement, removeCORS_ALLOWEDor addIP_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_ALLOWEDor addingIP_LOCKEDfor actual enforcement
With PROXY_BLOCKED (cosmetic only):
- 🏷️ Informational label indicating proxy incompatibility
- ⚠️ Still requires removing
CORS_ALLOWEDor addingIP_LOCKEDfor 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 whenconsistentIpForRequestsis 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,
});