# Enhanced Playlist System
EasyStream now includes advanced playlist features with auto-play, shuffle, collaborative editing, and more.
## Features
- 📼 **Auto-play** - Automatically play next video when current ends
- 🔀 **Shuffle mode** - Random playback order
- 🔁 **Repeat modes** - None, repeat all, repeat one
- 👥 **Collaborative playlists** - Multiple users can add/manage videos
- 👁️ **Privacy controls** - Public, unlisted, or private playlists
- 📊 **Analytics** - Track playlist views and playback
- ❤️ **Follow playlists** - Users can follow and get updates
- 🎨 **Modern player UI** - Clean controls with visual feedback
## Setup
### 1. Database Migration
Run the migration to create/enhance playlist tables:
```bash
docker-compose exec db mysql -u easystream -peasystream easystream < __install/migrations/003_enhance_playlists.sql
```
This creates:
- `db_playlists` - Playlist metadata
- `db_playlist_items` - Videos in playlists
- `db_playlist_collaborators` - Collaborative access control
- `db_playlist_follows` - Playlist followers
- `db_playlist_plays` - Playback analytics
### 2. Add Frontend Assets
Include CSS and JavaScript in your templates:
```html
```
### 3. Video Player Integration
On your video watch page (`watch.php`), check for playlist parameter:
```php
$playlist_id = $_GET['list'] ?? null;
$video_id = $_GET['v'];
// If playlist exists, the JavaScript will auto-initialize the player
```
The playlist player will automatically initialize if the URL contains `?v=VIDEO_ID&list=PLAYLIST_ID`.
## Usage
### Creating Playlists
```php
require_once 'f_core/f_classes/class.playlists.php';
$playlists = new VPlaylists();
// Create a new playlist
$playlist_id = $playlists->create(
$usr_id,
'My Favorite Videos',
'A collection of my top picks',
'public', // 'public', 'unlisted', or 'private'
false // is_collaborative
);
// Create a collaborative playlist
$playlist_id = $playlists->create(
$usr_id,
'Team Project Videos',
'Videos for our project',
'unlisted',
true // Enable collaboration
);
```
### Managing Videos
```php
// Add video to playlist
$playlists->addVideo($playlist_id, $video_id, $usr_id);
// Remove video
$playlists->removeVideo($playlist_id, $video_id, $usr_id);
// Reorder videos (auto-fix gaps)
$playlists->reorderItems($playlist_id);
// Manual reorder
$positions = [
101 => 1, // item_id 101 → position 1
102 => 2, // item_id 102 → position 2
103 => 3
];
$playlists->reorderItems($playlist_id, $positions);
```
### Collaboration
```php
// Add collaborator with permissions
$playlists->addCollaborator($playlist_id, $owner_id, $collaborator_id, [
'can_add' => true,
'can_remove' => false,
'can_reorder' => false
]);
// Remove collaborator
$playlists->removeCollaborator($playlist_id, $owner_id, $collaborator_id);
```
### Following Playlists
```php
// Follow a playlist
$playlists->follow($playlist_id, $usr_id);
// Unfollow
$playlists->unfollow($playlist_id, $usr_id);
```
## API Endpoints
### Get Playlist
```http
GET /api/playlists.php?id=123&shuffle=true
```
**Response:**
```json
{
"success": true,
"data": {
"playlist_id": 123,
"title": "My Playlist",
"description": "Great videos",
"privacy": "public",
"is_collaborative": true,
"auto_play": true,
"allow_shuffle": true,
"video_count": 15,
"view_count": 342,
"is_following": true,
"is_collaborator": false,
"is_owner": false,
"items": [...]
}
}
```
### Create Playlist
```http
POST /api/playlists.php
Content-Type: application/json
{
"title": "New Playlist",
"description": "My videos",
"privacy": "public",
"is_collaborative": false
}
```
### Update Playlist
```http
PUT /api/playlists.php?id=123
Content-Type: application/json
{
"title": "Updated Title",
"auto_play": true,
"allow_shuffle": true
}
```
### Add Video
```http
POST /api/playlists.php?id=123&action=add_video
Content-Type: application/json
{
"video_id": 456
}
```
### Remove Video
```http
POST /api/playlists.php?id=123&action=remove_video
Content-Type: application/json
{
"video_id": 456
}
```
### Follow/Unfollow
```http
POST /api/playlists.php?id=123&action=follow
POST /api/playlists.php?id=123&action=unfollow
```
### Add Collaborator
```http
POST /api/playlists.php?id=123&action=add_collaborator
Content-Type: application/json
{
"collaborator_id": 789,
"permissions": {
"can_add": true,
"can_remove": false,
"can_reorder": false
}
}
```
## Frontend Player
### Initialization
The player auto-initializes from URL parameters:
```
/watch?v=456&list=123
```
Or initialize manually:
```javascript
const player = new PlaylistPlayer(playlistId, {
videoId: currentVideoId,
autoPlay: true,
shuffle: false,
repeat: 'all', // 'none', 'one', 'all'
playerElement: document.getElementById('video-player')
});
```
### Player Controls
- **Shuffle** - Randomize playback order
- **Previous** - Go to previous video
- **Next** - Go to next video
- **Autoplay** - Toggle auto-play when video ends
- **Repeat** - Cycle through: Off → All → One
### Player API
```javascript
// Access the global player instance
const player = window.playlistPlayer;
// Playback control
player.playNext();
player.playPrevious();
player.playByIndex(5);
player.playVideo(videoId);
// Settings
player.toggleShuffle();
player.toggleAutoplay();
player.cycleRepeat();
// Playlist management
player.removeVideo(videoId);
player.loadPlaylist(); // Reload from server
```
## Collaborative Playlists
Collaborative playlists allow multiple users to contribute.
**Owner permissions:**
- Create/delete playlist
- Add/remove/reorder videos
- Manage collaborators
- Change privacy settings
**Collaborator permissions (configurable):**
- `can_add` - Add videos to playlist
- `can_remove` - Remove videos from playlist
- `can_reorder` - Change video order
**Example workflow:**
1. Owner creates collaborative playlist
2. Owner adds collaborators with specific permissions
3. Collaborators can add videos (if permitted)
4. All changes tracked with `added_by` field
## Privacy Modes
| Mode | Visibility | Shareable | Use Case |
|------|-----------|-----------|----------|
| **public** | Everyone can see and search | Yes | Public collections, curated lists |
| **unlisted** | Only with direct link | Yes | Share with specific people |
| **private** | Only owner + collaborators | No | Personal collections |
## Database Schema
### db_playlists
| Column | Type | Description |
|--------|------|-------------|
| playlist_id | INT | Primary key |
| usr_id | INT | Owner user ID |
| title | VARCHAR(255) | Playlist title |
| description | TEXT | Optional description |
| privacy | ENUM | public/unlisted/private |
| thumbnail | VARCHAR(512) | Custom thumbnail URL |
| is_collaborative | TINYINT | Collaboration enabled |
| allow_shuffle | TINYINT | Shuffle allowed |
| auto_play | TINYINT | Auto-play enabled |
| video_count | INT | Cached video count |
| view_count | INT | Total playlist views |
### db_playlist_items
| Column | Type | Description |
|--------|------|-------------|
| item_id | INT | Primary key |
| playlist_id | INT | Playlist ID |
| video_id | INT | Video ID |
| position | INT | Order in playlist |
| added_by | INT | User who added video |
| added_at | DATETIME | When added |
### db_playlist_collaborators
| Column | Type | Description |
|--------|------|-------------|
| collaborator_id | INT | Primary key |
| playlist_id | INT | Playlist ID |
| usr_id | INT | Collaborator user ID |
| can_add | TINYINT | Can add videos |
| can_remove | TINYINT | Can remove videos |
| can_reorder | TINYINT | Can reorder videos |
## Analytics
Track playlist playback for insights:
```php
// Track a play
$playlists->trackPlay($playlist_id, $video_id, $usr_id);
// Get play history
$sql = "SELECT * FROM db_playlist_plays
WHERE playlist_id = ?
ORDER BY played_at DESC
LIMIT 100";
```
**Available metrics:**
- Total playlist views
- Video completion rates
- Popular videos within playlist
- User engagement patterns
## Integration Points
### Add "Save to Playlist" Button
On video pages, add a quick-save button:
```html
```
### Playlist Notifications
Notify followers when a collaborative playlist is updated:
```php
// After adding video to collaborative playlist
if ($playlist['is_collaborative']) {
$triggers = new VNotificationTriggers();
// Get followers
$followers = $playlists->getFollowers($playlist_id);
foreach ($followers as $follower) {
$triggers->notifySystem(
$follower['usr_id'],
"New video added to {$playlist['title']}",
$video_title,
"/playlist?id=$playlist_id"
);
}
}
```
## Performance
- **Pagination:** Load videos in batches for large playlists
- **Caching:** Cache playlist metadata with Redis
- **Lazy loading:** Load thumbnails on scroll
- **Position indexing:** Fast reordering with indexed position column
## Future Enhancements
- [ ] Playlist recommendations based on viewing history
- [ ] Batch add videos (from search/channel)
- [ ] Playlist templates (create from another playlist)
- [ ] Smart playlists (auto-add based on criteria)
- [ ] Playlist comments/discussions
- [ ] Export/import playlists (JSON/M3U)
- [ ] Playlist thumbnail generation (grid of video thumbs)
- [ ] Drag-and-drop reordering in UI
- [ ] Playlist merging
- [ ] View mode (grid vs list)
## Troubleshooting
### Videos not playing in sequence
1. Check `auto_play` is enabled in playlist settings
2. Verify video player has `ended` event listener
3. Check browser console for JavaScript errors
### Collaborator can't add videos
1. Verify playlist has `is_collaborative = 1`
2. Check collaborator has `can_add = 1` permission
3. Ensure user is authenticated
### Shuffle not working
1. Check playlist has `allow_shuffle = 1`
2. Verify JavaScript is loaded
3. Check for shuffle button active state
## Learn More
- [YouTube Playlists Documentation](https://support.google.com/youtube/answer/57792)
- [HTML5 Video Events](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#events)
- [Fisher-Yates Shuffle Algorithm](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle)