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:
2026-01-21 22:12:08 -06:00
commit 872354b834
283 changed files with 338296 additions and 0 deletions

417
PLAYLIST_FORMAT.md Normal file
View 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