Files
simvision/PLAYLIST_FORMAT.md
Michael Simard 872354b834 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>
2026-01-21 22:12:08 -06:00

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
  • 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

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

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

  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