Overview
The main module is the entry point for the Cricfy Kodi plugin. It handles URL routing, UI navigation, provider/channel listing, and video playback setup.
Functions
build_url()
Constructs a Kodi plugin URL with query parameters.
Dictionary of query parameters to encode in the URL
Returns:
Kodi plugin URL in format plugin://plugin.video.cricfy/?key1=value1&key2=value2
Example:
url = build_url({
'mode': 'list_channels',
'url': 'https://example.com/playlist.m3u',
'title': 'Sports'
})
print(url)
# plugin://plugin.video.cricfy/?mode=list_channels&url=https%3A%2F%2F...
list_providers()
Lists all available providers in the Kodi UI.
Behavior:
- Fetches providers using
get_providers()
- Creates a folder item for each provider with:
- Title from
title field
- Thumbnail/icon from
image field (with fallback to default unknown icon)
- URL linking to channel list for that provider
- Skips providers with invalid or missing
catLink URLs
- Calls
xbmcplugin.endOfDirectory() to finalize the listing
Provider Data Structure:
Each provider from get_providers() contains:
{
"title": "Sports Network",
"image": "https://example.com/logo.png",
"catLink": "https://example.com/channels.m3u"
}
UI Flow:
Cricfy Plugin Root
├── Provider 1 [Folder]
├── Provider 2 [Folder]
└── Provider 3 [Folder]
Example:
When the user opens the Cricfy plugin, this function displays the provider list. Each provider is clickable and navigates to its channel list.
list_channels()
Fetches and displays channels for a specific provider.
def list_channels(provider_url)
M3U playlist URL for the provider (from catLink field)
Behavior:
- Validates the provider URL (must start with
http)
- Fetches channels using
get_channels(provider_url)
- Creates a playable list item for each channel with:
- Title and metadata (title, genre/group)
- Thumbnail from
tvg_logo
IsPlayable property set to true
- Shows error notification if URL is invalid or fetch fails
- Finalizes directory listing
Channel List Item Structure:
For each channel:
li = xbmcgui.ListItem(label=ch.title)
li.setArt({'thumb': ch.tvg_logo, 'icon': ch.tvg_logo})
li.setInfo('video', {'title': ch.title, 'genre': ch.group_title})
li.setProperty('IsPlayable', 'true')
URL Parameters:
Channels link to playback mode with:
mode='play'
provider_url: Original provider URL (for re-fetching channels)
channel_title: Channel title for identification
UI Flow:
Provider Name
├── Channel 1 [Playable]
├── Channel 2 [Playable]
└── Channel 3 [Playable]
Error Handling:
- Invalid URL: Shows notification “Invalid provider URL”
- Empty channels: Shows notification “No channels found”
- Fetch error: Shows notification “Failed to fetch playlist content”
All errors call xbmcplugin.endOfDirectory() to prevent UI hanging
Example:
When user clicks a provider folder, this function fetches and displays all channels from that provider’s M3U playlist.
play_video()
Resolves channel URL and configures Kodi for video playback.
def play_video(provider_url, channel_title)
Provider M3U URL (used to fetch channels)
Title of the channel to play (used to find the channel in the list)
Behavior:
- Fetches channels from
provider_url
- Finds the channel matching
channel_title
- Extracts streaming configuration (URL, headers, DRM)
- Configures Inputstream Adaptive for HLS/DASH/DRM streams
- Sets up HTTP headers (User-Agent, Referer, Cookie)
- Configures DRM license if present
- Calls
xbmcplugin.setResolvedUrl() to start playback
Stream Configuration:
The function extracts from the PlaylistItem:
url - Stream URL
user_agent - Custom User-Agent header
cookie - Authentication cookie
referer - Referer header
license_string - DRM license key/URL
headers - Additional HTTP headers
Inputstream Adaptive Setup:
if '.mpd' in url or '.m3u8' in url or '.m3u' in url or license_string:
li.setProperty('inputstream', 'inputstream.adaptive')
# Set headers for manifest and stream
li.setProperty('inputstream.adaptive.manifest_headers', encoded_headers)
li.setProperty('inputstream.adaptive.stream_headers', encoded_headers)
DRM Configuration:
Two DRM license formats are supported:
-
Clearkey Hex Pair (format:
key:kid):
# Example: "a1b2c3d4e5f6:1a2b3c4d5e6f"
drm_config = f"org.w3.clearkey|{license_string}"
li.setProperty('inputstream.adaptive.drm_legacy', drm_config)
-
License Server URL:
# Example: "https://license.example.com/proxy"
drm_config = f"org.w3.clearkey|{license_string}|{headers}"
li.setProperty('inputstream.adaptive.drm_legacy', drm_config)
Header Encoding:
Headers are encoded as key=value pairs joined with &:
stream_headers = [
'User-Agent=Mozilla/5.0',
'Referer=https://example.com',
'Cookie=session=abc123'
]
encoded = '&'.join(stream_headers)
# "User-Agent=Mozilla/5.0&Referer=https://example.com&Cookie=session=abc123"
Headers are also appended to URL with pipe separator:
url += '|' + encoded_headers
# "https://stream.m3u8|User-Agent=...&Referer=...&Cookie=..."
Error Handling:
Shows error notification if:
- Channel is not found in the provider’s channel list
- Provider URL fetch fails
- Any exception occurs during URL resolution
Error: “Failed to resolve channel URL”
Example:
When user clicks a channel, this function:
- Re-fetches the provider’s channels (from cache if available)
- Finds the selected channel by title
- Configures Kodi to play the stream with proper headers and DRM
router()
Routes requests to the appropriate handler based on URL parameters.
Query string from Kodi (e.g., "mode=list_channels&url=https://...")
Routing Logic:
| Mode | Function Called | Parameters |
|---|
None (root) | list_providers() | - |
list_channels | list_channels() | url |
play | play_video() | provider_url, channel_title |
| Other | Error notification | - |
Example Routes:
# Root - show providers
router("") # mode=None
→ list_providers()
# Show channels for a provider
router("mode=list_channels&url=https://example.com/playlist.m3u")
→ list_channels("https://example.com/playlist.m3u")
# Play a channel
router("mode=play&provider_url=https://...&channel_title=ESPN")
→ play_video("https://...", "ESPN")
# Invalid mode
router("mode=invalid")
→ Shows "Not implemented" error notification
URL Parameter Parsing:
params = dict(parse_qsl(param_string))
# "mode=play&title=ESPN" → {'mode': 'play', 'title': 'ESPN'}
Example:
This is the main entry point called by Kodi:
if __name__ == '__main__':
router(sys.argv[2][1:]) # Remove leading '?' from query string
Module Variables
| Variable | Value | Description |
|---|
BASE_URL | sys.argv[0] | Kodi plugin base URL (plugin://plugin.video.cricfy/) |
ADDON_HANDLE | int(sys.argv[1]) | Kodi handle for this plugin instance |
Kodi Arguments:
Kodi passes these arguments to the plugin:
sys.argv[0] - Plugin URL
sys.argv[1] - Handle ID (integer)
sys.argv[2] - Query string (with leading ?)
Navigation Flow
┌─────────────────────┐
│ Plugin Root │
│ list_providers() │
└──────────┬──────────┘
│ Click provider
▼
┌─────────────────────┐
│ Channel List │
│ list_channels() │
└──────────┬──────────┘
│ Click channel
▼
┌─────────────────────┐
│ Video Playback │
│ play_video() │
└─────────────────────┘
Dependencies
urllib.parse - URL encoding/decoding
sys - Kodi arguments
re - DRM license pattern matching
xbmcgui - Kodi UI components
xbmcplugin - Kodi plugin API
lib.providers - Provider and channel fetching
lib.req - HTTP utilities
lib.logger - Error logging