Features: - VOD library with movie grouping and version detection - TV show library with season/episode organization - TMDB integration for trending shows and recently aired episodes - Recent releases section with TMDB release date sorting - Watch history tracking with continue watching - Playlist caching (12-hour TTL) for offline support - M3U playlist parsing with XStream API support - Authentication with credential storage Technical: - SwiftUI for tvOS - Actor-based services for thread safety - Persistent caching for playlists, TMDB data, and watch history - KSPlayer integration for video playback Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
9.1 KiB
M3U Playlist Format Documentation
File Location
sample_playlist.m3u - Example playlist showing XStream VOD format
M3U Format Structure
Header
#EXTM3U
- Required first line identifying the file as an M3U playlist
Entry Format
Each VOD item consists of two lines:
Line 1: Metadata (EXTINF)
#EXTINF:-1 tvg-id="ID" tvg-name="NAME" tvg-logo="URL" group-title="CATEGORY",Display Title
Line 2: Stream URL
https://server.com/movie/username/password/12345.mp4
Metadata Attributes
tvg-id
- Purpose: Unique identifier for the video
- Example:
"movie001" - Maps to:
VODItem.id - Used for: Identifying specific content, tracking playback
tvg-name
- Purpose: Clean video title (without year/metadata)
- Example:
"The Matrix" - Maps to:
VODItem.name(if no comma-separated name exists) - Used for: Display, search
tvg-logo
- Purpose: Poster/thumbnail image URL
- Example:
"https://image.tmdb.org/t/p/w500/matrix.jpg" - Maps to:
VODItem.iconURL - Used for: Grid display, detail view
group-title
- Purpose: Category/genre classification
- Example:
"Action Movies","Sci-Fi","Drama" - Maps to:
VODItem.categoryID - Used for: Category filtering, menu organization
Display Title (after comma)
- Purpose: Full display name with year/additional info
- Example:
"The Matrix (1999)" - Maps to:
VODItem.name(preferred over tvg-name) - Used for: Primary display text
Current Categories in Sample
Based on the sample playlist, we have these categories:
-
Action Movies (3 items)
- The Matrix, Inception, The Dark Knight
-
Sci-Fi (3 items)
- Interstellar, Blade Runner 2049, Arrival
-
Drama (3 items)
- The Shawshank Redemption, The Godfather, Forrest Gump
-
Comedy (3 items)
- Superbad, The Hangover, Anchorman
-
Horror (3 items)
- The Conjuring, Get Out, A Quiet Place
-
Family & Kids (3 items)
- Toy Story, Finding Nemo, The Lion King
-
Thriller (2 items)
- Parasite, Gone Girl
Total: 7 categories, 20 movies
How Data is Parsed
M3UParser.swift Logic
1. Read line: #EXTINF:-1 tvg-id="movie001" tvg-name="The Matrix" tvg-logo="..." group-title="Action Movies",The Matrix (1999)
2. Extract attributes:
- tvg-id → "movie001"
- tvg-name → "The Matrix"
- tvg-logo → "https://..."
- group-title → "Action Movies"
3. Extract display name (after comma):
- "The Matrix (1999)"
4. Read next line (stream URL):
- "https://widen.velvetfort.com/movie/..."
5. Create VODItem:
VODItem(
id: "movie001",
name: "The Matrix (1999)", // Preferred over tvg-name
streamURL: "https://...",
iconURL: "https://...",
categoryID: "Action Movies"
)
Menu Organization Options
Option 1: Category-Based Navigation (Current)
Main View: Grid of all VOD items Filter Bar: Horizontal scrolling category buttons
- All
- Action Movies
- Sci-Fi
- Drama
- Comedy
- Horror
- Family & Kids
- Thriller
Pros:
- Simple, flat navigation
- Easy to browse all content
- Quick category filtering
Cons:
- Large libraries can be overwhelming
- No hierarchy
Option 2: Category Tabs
TabView with categories as tabs:
- Tab 1: All
- Tab 2: Action Movies
- Tab 3: Sci-Fi
- Tab 4: Drama
- etc.
Pros:
- Clear separation
- Native tvOS navigation
Cons:
- Limited to ~5-7 tabs before UI becomes crowded
- Not scalable for many categories
Option 3: Hierarchical Menu
Main Menu:
- Browse All
- Categories ▸
- Action Movies ▸
- Sci-Fi ▸
- Drama ▸
- etc.
- Recently Added
- Continue Watching
Pros:
- Scalable to many categories
- Can add other browse methods
- Professional feel
Cons:
- More navigation depth
- Requires more views
Option 4: Hybrid Approach (Recommended)
Main View: VODLibraryView (current implementation)
- Header with category filter (horizontal scroll)
- Grid of filtered content
- "All" shows everything
Additional Features:
- Search button
- Sort options (A-Z, Recently Added, etc.)
- Quick access to "Continue Watching" at top
Implementation:
VODLibraryView:
- All content by default
- Category pills at top (ScrollView horizontal)
- When category selected, filter grid
- Smooth animations between states
This is what is currently implemented.
Data Structures
Current Implementation
// Individual item
struct VODItem {
let id: String // tvg-id
let name: String // Display title (after comma)
let streamURL: String // Stream URL
let iconURL: String? // tvg-logo
let categoryID: String? // group-title
// Additional fields available for future use:
let description: String?
let duration: String?
let rating: String?
let added: String?
}
// Category
struct Category {
let id: String // Same as name for now
let name: String // group-title value
}
// Container
struct Playlist {
let vodItems: [VODItem]
let categories: [Category]
let lastUpdated: Date
}
Accessing Data
// Get all items in a category
playlist.vodItems(forCategory: "Action Movies")
// Get categorized dictionary
let categorized = playlist.categorizedVODItems
// Returns: ["Action Movies": [VODItem], "Sci-Fi": [VODItem], ...]
// Current filtering (in VODLibraryViewModel)
func filteredVODItems(from playlist: Playlist?) -> [VODItem] {
var items = playlist.vodItems
if let selectedCategory = selectedCategory {
items = items.filter { $0.categoryID == selectedCategory }
}
if !searchText.isEmpty {
items = items.filter { $0.name.contains(searchText) }
}
return items
}
Extending the Format
Your XStream provider may include additional attributes:
Common Extended Attributes
#EXTINF:-1 tvg-id="ID"
tvg-name="NAME"
tvg-logo="URL"
group-title="CATEGORY"
tvg-duration="7200" // Duration in seconds
tvg-rating="8.7" // IMDB rating
tvg-year="1999" // Release year
tvg-country="USA" // Country
tvg-language="English" // Language
tvg-genre="Action|Sci-Fi" // Multiple genres
,Display Title
To Support Additional Fields
Update VODItem.swift:
struct VODItem {
// ... existing fields ...
let duration: String? // Parse from tvg-duration
let rating: String? // Parse from tvg-rating
let year: String? // Parse from tvg-year
let country: String? // Parse from tvg-country
let language: String? // Parse from tvg-language
let genres: [String]? // Parse from tvg-genre (split by |)
}
Update M3UParser.swift to extract these attributes.
Recommendations for Menu Organization
Based on typical VOD libraries:
For Small Libraries (<100 items)
- Use current implementation (filter bar at top)
- Simple and effective
For Medium Libraries (100-500 items)
- Add search functionality
- Add sort options (A-Z, Year, Rating)
- Keep category filter
For Large Libraries (500+ items)
- Hierarchical navigation
- Featured/Recommended section
- Recently Added section
- Advanced filters (Year, Genre, Rating)
- Robust search with suggestions
Current UI Implementation
Location: VODLibraryView.swift
Header Section
- Title: "VOD Library"
- Refresh button
- Logout button
- Item count: "X videos"
- Category filter (horizontal scroll)
- All (default)
- Category buttons (generated from playlist.categories)
Grid Section
- LazyVGrid (4 columns)
- VODCardView for each item
- Poster image
- Title
- Category label
- Focus effects
State Management
VODLibraryViewModel:
- selectedCategory: String? // nil = "All"
- searchText: String // For future search
- filteredVODItems() returns filtered array
Future Enhancements
- Search: Add search bar, filter by name
- Sort: Alphabetical, by year, by rating
- Favorites: Star items, separate favorites view
- Continue Watching: Track playback position
- Collections: Featured, New Releases, Top Rated
- Multiple Genres: Some items may fit multiple categories
- Metadata Display: Show rating, year, duration on cards
Testing Your Actual Playlist
Once you connect to your real XStream server:
- Authenticate with the app
- Check Xcode console for parsed playlist data
- Verify categories extracted correctly
- Check metadata (logos, titles, etc.)
- Test filtering by selecting different categories
- Verify stream URLs are correct format
If your actual playlist format differs from this sample, the M3UParser may need adjustments.
Summary
Current State:
- Functional category-based filtering
- Grid display with focus effects
- Horizontal category selection
Format:
- Standard M3U with XStream attributes
- Extracts: id, name, logo, category, stream URL
Next Steps:
- Test with your actual XStream playlist
- Adjust parser if format differs
- Consider additional features based on content volume