Initial commit: SimVision tvOS streaming app
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>
This commit is contained in:
417
PLAYLIST_FORMAT.md
Normal file
417
PLAYLIST_FORMAT.md
Normal file
@@ -0,0 +1,417 @@
|
||||
# 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:
|
||||
|
||||
1. **Action Movies** (3 items)
|
||||
- The Matrix, Inception, The Dark Knight
|
||||
|
||||
2. **Sci-Fi** (3 items)
|
||||
- Interstellar, Blade Runner 2049, Arrival
|
||||
|
||||
3. **Drama** (3 items)
|
||||
- The Shawshank Redemption, The Godfather, Forrest Gump
|
||||
|
||||
4. **Comedy** (3 items)
|
||||
- Superbad, The Hangover, Anchorman
|
||||
|
||||
5. **Horror** (3 items)
|
||||
- The Conjuring, Get Out, A Quiet Place
|
||||
|
||||
6. **Family & Kids** (3 items)
|
||||
- Toy Story, Finding Nemo, The Lion King
|
||||
|
||||
7. **Thriller** (2 items)
|
||||
- Parasite, Gone Girl
|
||||
|
||||
**Total: 7 categories, 20 movies**
|
||||
|
||||
---
|
||||
|
||||
## How Data is Parsed
|
||||
|
||||
### M3UParser.swift Logic
|
||||
|
||||
```swift
|
||||
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**:
|
||||
```swift
|
||||
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
|
||||
|
||||
```swift
|
||||
// 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
|
||||
|
||||
```swift
|
||||
// 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`:
|
||||
```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
|
||||
```swift
|
||||
- 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
|
||||
```swift
|
||||
- LazyVGrid (4 columns)
|
||||
- VODCardView for each item
|
||||
- Poster image
|
||||
- Title
|
||||
- Category label
|
||||
- Focus effects
|
||||
```
|
||||
|
||||
### State Management
|
||||
```swift
|
||||
VODLibraryViewModel:
|
||||
- selectedCategory: String? // nil = "All"
|
||||
- searchText: String // For future search
|
||||
|
||||
- filteredVODItems() returns filtered array
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
1. **Search**: Add search bar, filter by name
|
||||
2. **Sort**: Alphabetical, by year, by rating
|
||||
3. **Favorites**: Star items, separate favorites view
|
||||
4. **Continue Watching**: Track playback position
|
||||
5. **Collections**: Featured, New Releases, Top Rated
|
||||
6. **Multiple Genres**: Some items may fit multiple categories
|
||||
7. **Metadata Display**: Show rating, year, duration on cards
|
||||
|
||||
---
|
||||
|
||||
## Testing Your Actual Playlist
|
||||
|
||||
Once you connect to your real XStream server:
|
||||
|
||||
1. **Authenticate** with the app
|
||||
2. **Check Xcode console** for parsed playlist data
|
||||
3. **Verify categories** extracted correctly
|
||||
4. **Check metadata** (logos, titles, etc.)
|
||||
5. **Test filtering** by selecting different categories
|
||||
6. **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
|
||||
Reference in New Issue
Block a user