Imported from vcaldo/continuo (
skills_scripts/playlist2sqlite/SKILL.md). Install upstream withnpx skills add vcaldo/continuo --skill playlist2sqlite. Copyright stays with the author.
Playlist2SQLite
Extract and parse M3U/M3U8 playlist files into portable SQL dump files for querying and analysis.
Overview
M3U playlists (commonly used for IPTV, streaming services, and media collections) contain channel/stream metadata in a semi-structured text format. This skill provides a bash script to parse M3U files (from local files or URLs) and export as SQL dump files that can be imported into any SQLite database.
Features:
- Download large playlists reliably with retry logic
- Handle files with 100k+ entries
- Proper SQL escaping for all edge cases
- Progress indicators for large files
- Automatic cleanup on errors
Quick Start
# Parse from URL
scripts/playlist2sqlite.sh --url "https://provider.com/iptv.m3u" --name my-iptv
# Parse from local file
scripts/playlist2sqlite.sh --input playlist.m3u --name channels
# Append to existing SQL file
scripts/playlist2sqlite.sh --input new-channels.m3u --name channels --append
# Verbose mode with progress
scripts/playlist2sqlite.sh --url "https://example.com/list.m3u" --name streams --verbose
# For very large playlists (increase timeout)
scripts/playlist2sqlite.sh --url "https://large-provider.com/huge.m3u" --name huge --timeout 3600 --progress
# Import into SQLite database
sqlite3 channels.db < channels.sql
Output Format
The script outputs a .sql file (SQL dump) containing:
- Transaction wrapping for performance
- Table creation statements
- Index creation statements
- INSERT statements for each channel
This portable format can be imported into any SQLite database.
Database Schema
The SQL creates a channels table with the following structure:
| Column | Type | Description |
|---|---|---|
id |
INTEGER PRIMARY KEY | Auto-increment ID |
tvg_id |
TEXT | TVG ID (EPG identifier) |
tvg_name |
TEXT | Channel name |
tvg_logo |
TEXT | Logo URL |
group_title |
TEXT | Channel group/category |
url |
TEXT | Stream URL |
raw_extinf |
TEXT | Full EXTINF line (for non-standard attributes) |
M3U Format Reference
M3U files follow this structure:
#EXTM3U
#EXTINF:-1 tvg-id="channel1" tvg-name="Channel 1" tvg-logo="http://logo.png" group-title="News",Channel 1
http://stream.url/channel1
#EXTINF:-1 tvg-id="channel2" tvg-name="Channel 2" tvg-logo="http://logo2.png" group-title="Sports",Channel 2
http://stream.url/channel2
The script parses:
#EXTM3Uheader (validates format)#EXTINFmetadata lines (extracts tvg-*, group-title, channel name)- Stream URLs (line immediately following EXTINF)
Script Usage
Located at: scripts/playlist2sqlite.sh
Options
--name NAME Output file name, creates NAME.sql (required)
--input FILE M3U/M3U8 file to parse (mutually exclusive with --url)
--url URL M3U/M3U8 URL to download and parse (mutually exclusive with --input)
--append Append to existing SQL file (default: recreate)
--verbose Show detailed progress
--progress Show download/parsing progress (auto-enabled for large files)
--timeout SECS Max download time in seconds (default: 1800 = 30 minutes)
--retries N Number of download retries (default: 3)
--help Display usage information
Examples
From URL (most common):
./scripts/playlist2sqlite.sh --url "https://provider.com/iptv.m3u" --name my-iptv
# Output: my-iptv.sql
From local file:
./scripts/playlist2sqlite.sh --input iptv.m3u --name channels
# Output: channels.sql
Append new channels:
./scripts/playlist2sqlite.sh --input additional.m3u --name channels --append
Large playlist with extended timeout:
./scripts/playlist2sqlite.sh --url "https://example.com/large.m3u" --name streams \
--timeout 3600 --verbose
Importing the SQL
After generating the SQL file, import it into a SQLite database:
# Create new database and import
sqlite3 channels.db < channels.sql
# Or import into existing database
sqlite3 existing.db < channels.sql
Querying the Database
After importing, query with standard SQLite:
# Count channels by group
sqlite3 channels.db "SELECT group_title, COUNT(*) FROM channels GROUP BY group_title;"
# Find all sports channels
sqlite3 channels.db "SELECT tvg_name, url FROM channels WHERE group_title LIKE '%Sport%';"
# Export specific group to CSV
sqlite3 -header -csv channels.db "SELECT * FROM channels WHERE group_title='Movies';" > movies.csv
# Find channels without logos
sqlite3 channels.db "SELECT tvg_name FROM channels WHERE tvg_logo IS NULL OR tvg_logo='';"
Large Playlist Handling
The script is optimized for very large playlists:
Automatic Optimizations
- Progress indicators: Automatically enabled for files >50MB
- Transaction wrapping: All inserts wrapped in transaction for faster imports
- Retry logic: Downloads automatically retry up to 3 times on failure
- Memory efficiency: Streaming parser (doesn't load entire file into memory)
Recommended Settings for Large Playlists
| Playlist Size | Recommended Settings |
|---|---|
| <10k entries | Default settings work fine |
| 10k-50k entries | Add --progress to see status |
| 50k-100k entries | Add --timeout 3600 --verbose |
| >100k entries | Add --timeout 7200 --verbose, consider running on fast storage |
Example: 100k+ Entry Playlist
./scripts/playlist2sqlite.sh \
--url "https://large-provider.com/mega.m3u" \
--name mega-list \
--timeout 7200 \
--retries 5 \
--verbose
Troubleshooting
Download Issues
"Download failed after X attempts"
- Check if URL is accessible:
curl -I "URL" - Increase timeout:
--timeout 3600 - Increase retries:
--retries 5 - Some providers rate-limit; wait and retry
"Downloaded file appears to be HTML"
- URL returned error page instead of M3U
- Check if URL requires authentication
- Verify URL is correct and not expired
- Try accessing URL in browser to see the actual error
Slow downloads
- Large playlists (>100MB) can take 10+ minutes on slow connections
- Use
--progressto see download status - Increase
--timeoutfor slow connections
Parsing Issues
"File contains no #EXTINF entries"
- File is not a valid M3U playlist
- Check file content:
head -20 file.m3u - May be wrong format (e.g., plain text URL list)
"Entries skipped" in output
- Normal for M3U files with malformed entries
- Use
--verboseto see which entries were skipped - Usually caused by missing URL after EXTINF line
Special characters causing issues
- Script handles quotes, newlines, and unicode automatically
- If import fails, check for unusual binary content in M3U
Import Issues
SQLite "database is locked"
- Close other applications using the database
- For large imports, use:
sqlite3 -cmd ".timeout 30000" db.db < file.sql
Import very slow
- The SQL file uses transactions, which should be fast
- Ensure database is on SSD, not spinning disk
- For very large files, import may take several minutes
Out of memory
- SQLite handles large imports well, but OS needs memory
- For 100k+ entries, ensure 1GB+ free RAM
- Consider splitting very large playlists
Common Use Cases
- IPTV Management: Parse provider playlists from URLs, filter/search channels, export subsets
- Playlist Analysis: Count channels per group, identify missing metadata, find duplicates
- Playlist Merging: Combine multiple M3U files into single database, deduplicate
- Channel Migration: Extract channels from old format, transform, export to new format
- EPG Integration: Use tvg-id to link channels with Electronic Program Guide data
- Backup: Store playlist data in portable SQL format
- Intelligent Search: Find channels broadcasting specific programs/events
Channel Search
Find channels broadcasting specific programs or events by searching the web for broadcasting information and matching against your local channel database.
Quick Start
# Find channels showing Barcelona match
./scripts/search-channel.sh --db channels.db --query "jogo do Barça"
# Search for Champions League in Brazil
./scripts/search-channel.sh --db channels.db --query "Champions League" --country BR
# JSON output for integration
./scripts/search-channel.sh --db channels.db --query "NBA Finals" --json
How It Works
- Web Search: Searches DuckDuckGo for "{query} onde assistir canais transmissão"
- Channel Extraction: Extracts channel names from search results (ESPN, TNT, Globo, etc.)
- Fuzzy Matching: Matches found channels against your SQLite database
- Ranking: Scores results by match quality, sports relevance, and channel popularity
- Output: Returns top matches with channel name, category, and stream URL
Script Usage
Located at: scripts/search-channel.sh
--db FILE SQLite database file (required)
--query "TEXT" Event/program to search for (required)
--country CODE Country filter: BR, US, ES, PT, etc.
--lang LANG Search language: pt, es, en (default: pt)
--max N Maximum results to show (default: 10)
--json Output results as JSON
--verbose Show detailed progress
--no-cache Skip cache for web search
--help Display help
Examples
Sports Event (Portuguese):
./scripts/search-channel.sh --db channels.db --query "jogo do Flamengo"
# Output:
# Found 5 possible channels for "jogo do Flamengo":
#
# 1. [⭐⭐⭐] SporTV HD
# Category: Sports BR
# URL: http://stream.url/sportv
#
# 2. [⭐⭐⭐] Premiere FC
# Category: Sports BR
# URL: http://stream.url/premiere
Soccer Match (Spanish):
./scripts/search-channel.sh --db channels.db --query "Real Madrid vs Barcelona" --lang es
# Searches for: "Real Madrid vs Barcelona donde ver canal transmisión en vivo"
Movies:
./scripts/search-channel.sh --db channels.db --query "filme Gladiador" --lang pt
JSON Output (for integration):
./scripts/search-channel.sh --db channels.db --query "UFC 300" --json
# Output:
# {
# "query": "UFC 300",
# "count": 3,
# "results": [
# {"name": "ESPN Brasil HD", "category": "Sports", "url": "...", "score": 180},
# {"name": "Combate", "category": "Sports", "url": "...", "score": 150}
# ]
# }
Scoring Logic
Channels are ranked by score based on:
| Factor | Points |
|---|---|
| Exact name match | +100 |
| Name contains search term | +80 |
| Partial word match | +30 per word |
| Group matches event type (sports→sports) | +40 |
| Known popular channel | +20-50 |
| HD/FHD/4K quality | +10 |
Event Type Detection:
- Sports keywords → prioritize sports channels
- Movie keywords → prioritize entertainment channels
- News keywords → prioritize news channels
Caching
Web search results are cached for 1 hour in /tmp/playlist2sqlite-cache/ to reduce API calls. Use --no-cache to force fresh search.
Supported Languages
| Language | Search Terms |
|---|---|
| Portuguese (pt) | "onde assistir canal transmissão ao vivo" |
| Spanish (es) | "donde ver canal transmisión en vivo" |
| English (en) | "where to watch channel broadcast live stream" |
Known Channel Patterns
The script recognizes popular broadcasting channels:
Sports: ESPN, Fox Sports, TNT Sports, SporTV, Premiere, BandSports, beIN Sports, Sky Sports, DAZN, Movistar, Star+
Entertainment: HBO, HBO Max, Netflix, Prime Video, Disney+, Paramount+
News: GloboNews, CNN, CNN Brasil, BBC
Notes
- Script requires
bash,sqlite3,curl(for URL mode), and standard Unix tools (sed,awk,grep) - Handles both M3U and M3U8 formats (M3U8 is UTF-8 encoded M3U)
- Non-standard EXTINF attributes are preserved in
raw_extinfcolumn - Empty/invalid URLs are skipped with warning in verbose mode
- URL downloads use curl with automatic retry and temp file cleanup
- Output is
.sqlfile (SQL dump), not.db(SQLite binary) - SQL is wrapped in transaction for faster imports