# How LunaTV Handles Playback History and Favorites Synchronization

> Discover how LunaTV syncs playback history and favorites across devices with its stateless API, pluggable storage, and secure authentication for real-time data consistency.

- Repository: [MoonTechLab/LunaTV](https://github.com/MoonTechLab/LunaTV)
- Tags: how-to-guide
- Published: 2026-09-09

---

**LunaTV synchronizes playback history and favorites across devices using a stateless HTTP API with pluggable storage backends (Redis, Upstash, Kvrocks, or local storage), composite key identification, and signed-cookie authentication to ensure real-time data consistency.**

LunaTV, an open-source streaming platform by MoonTechLab, implements a storage-agnostic architecture for persisting user-specific data. The system manages **playback history** (play records) and **favorite items** through a unified abstraction layer that supports both local and distributed storage engines. This design enables seamless cross-device synchronization while maintaining strict data integrity through composite key generation and automated migration handling.

## Architecture Overview

### Pluggable Storage Backends

According to the LunaTV source code, the platform selects its storage implementation at runtime via the `NEXT_PUBLIC_STORAGE_TYPE` environment variable. Supported backends include **local storage**, **Redis**, **Upstash**, and **Kvrocks**. The `DbManager` class in [`src/lib/db.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/db.ts) abstracts all storage operations, ensuring consistent behavior regardless of the underlying engine.

All database access flows through the `ensureMigrated` method, which guarantees that any pending data migrations complete before executing read or write operations. This prevents synchronization errors during schema updates or backend switches.

### Authentication and User Validation

Every request to the synchronization endpoints first extracts user identity via `getAuthInfoFromCookie` from [`src/lib/auth.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/auth.ts). For non-owner users, the system validates against `UserConfig.Users` in [`src/lib/config.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/config.ts), checking both existence and ban status before permitting data access. This guard logic is shared identically between the playback history and favorites APIs.

## Playback History Synchronization

The playback history system tracks user viewing progress through the `/api/playrecords` endpoint defined in [`src/app/api/playrecords/route.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/app/api/playrecords/route.ts).

### API Operations and Storage Methods

The endpoint supports five distinct operations mapped to `DbManager` methods:

- **Read all records**: `GET /api/playrecords` calls `db.getAllPlayRecords(username)`, which delegates to `IStorage.getAllPlayRecords` and returns an object mapping composite keys to `PlayRecord` objects.
- **Read specific record**: Internally uses `db.getPlayRecord(username, source, id)` → `IStorage.getPlayRecord`.
- **Save or update**: `POST /api/playrecords` accepts a body with `{key, record}`, parses the composite key into `source` and `id`, populates missing `save_time` fields, and invokes `db.savePlayRecord(username, source, id, finalRecord)` → `IStorage.setPlayRecord`.
- **Delete single**: `DELETE /api/playrecords?key=source+id` triggers `db.deletePlayRecord(username, source, id)` → `IStorage.deletePlayRecord`.
- **Delete all**: `DELETE /api/playrecords` (without key parameter) executes `db.deleteAllPlayRecords(username)` → `IStorage.deleteAllPlayRecords`.

### Data Persistence Flow

When saving a play record, the server normalizes the input by ensuring the `save_time` timestamp is present. The composite key format `<source>+<id>` (e.g., `douyin+12345`) uniquely identifies media items across different platforms, preventing collisions between content from separate sources.

## Favorites Synchronization

The favorites system mirrors the playback history architecture through the `/api/favorites` endpoint in [`src/app/api/favorites/route.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/app/api/favorites/route.ts).

### CRUD Operations

Favorites implement identical CRUD patterns using the `DbManager` abstraction:

1. **List favorites**: `GET /api/favorites` calls `db.getAllFavorites(username)` → `IStorage.getAllFavorites`, returning `{[key]: Favorite}`.
2. **Get specific**: `GET /api/favorites?key=source+id` uses `db.getFavorite(username, source, id)` → `IStorage.getFavorite`.
3. **Create/update**: `POST /api/favorites` validates required fields, constructs `finalFavorite` with `save_time`, and calls `db.saveFavorite(username, source, id, finalFavorite)` → `IStorage.setFavorite`.
4. **Remove single**: `DELETE /api/favorites?key=source+id` invokes `db.deleteFavorite(username, source, id)` → `IStorage.deleteFavorite`.
5. **Clear all**: `DELETE /api/favorites` executes `db.deleteAllFavorites(username)` → `IStorage.deleteAllFavorites`.

### Key Generation Strategy

Both systems rely on the `generateStorageKey` helper function in [`src/lib/db.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/db.ts) to construct composite keys. This ensures that a YouTube video with ID `abcde` generates the key `youtube+abcde`, creating a consistent namespace across all user data operations.

## Cross-Device Synchronization Mechanics

LunaTV achieves real-time synchronization through three core mechanisms:

- **Stateless HTTP API**: Clients (web or mobile) issue authenticated GET/POST/DELETE requests for every interaction. No local state assumptions exist between the client and server.
- **External Storage as Source of Truth**: By persisting data in Redis, Upstash, or Kvrocks rather than browser-local storage, the server maintains a central repository accessible from any authenticated device.
- **Immediate Consistency**: Each write operation updates the backend storage directly. Subsequent reads from any device instantly reflect the latest playback positions or favorite additions, eliminating synchronization lag.

## Implementation Examples

### Fetching Playback History

```typescript
// Retrieve all play records for authenticated user
const response = await fetch('/api/playrecords', {
  credentials: 'include',
});
const playRecords = await response.json(); 
// Returns: { "douyin+12345": { title: "...", index: 1 }, ... }

```

### Recording Playback Progress

```typescript
// Save or update a play record
await fetch('/api/playrecords', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    key: 'douyin+12345',
    record: { 
      title: 'Example Video', 
      source_name: 'Douyin', 
      index: 1 
    }
  })
});

```

### Managing Favorites

```typescript
// Add a favorite
await fetch('/api/favorites', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    key: 'youtube+abcde',
    favorite: { 
      title: 'Cool Clip', 
      source_name: 'YouTube' 
    }
  })
});

// Remove a specific favorite
await fetch('/api/favorites?key=youtube+abcde', {
  method: 'DELETE',
  credentials: 'include'
});

```

## Summary

- **Pluggable Architecture**: LunaTV supports Redis, Upstash, Kvrocks, and local storage via runtime configuration in [`src/lib/db.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/db.ts).
- **Composite Keys**: Media items are uniquely identified using the `<source>+<id>` format generated by `generateStorageKey`.
- **Unified API Pattern**: Both playback history and favorites use identical CRUD patterns through `DbManager` methods with `ensureMigrated` safety checks.
- **Real-Time Sync**: Stateless HTTP endpoints with external storage backends ensure immediate cross-device synchronization for all user data.
- **Secure Access**: All endpoints validate users via signed cookies and check against `UserConfig.Users` before data access.

## Frequently Asked Questions

### What storage backends does LunaTV support for synchronization?

LunaTV supports four storage backends selectable via the `NEXT_PUBLIC_STORAGE_TYPE` environment variable: local storage for single-instance deployments, Redis for high-performance caching, Upstash for serverless Redis-compatible storage, and Kvrocks for disk-persistent key-value storage. The `DbManager` class abstracts all implementations behind the `IStorage` interface.

### How does LunaTV authenticate requests for playback history and favorites?

Every synchronization request extracts user identity from signed HTTP cookies using `getAuthInfoFromCookie` in [`src/lib/auth.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/auth.ts). For non-administrative users, the system validates the username against the `UserConfig.Users` registry and checks ban status before permitting access to personal data endpoints.

### Can LunaTV migrate data between different storage backends?

Yes. The `DbManager` class implements an `ensureMigrated` method that runs before any storage operation. This ensures pending data migrations complete before accessing the backend, allowing administrators to switch from local storage to Redis (or vice versa) without data loss or corruption.

### How does LunaTV handle simultaneous updates from multiple devices?

Since the storage backend (Redis, Upstash, etc.) serves as the single source of truth, the last-write-wins model applies. Each device sends stateless HTTP requests that immediately persist to the shared storage, ensuring that the most recent operation from any device becomes the current state visible to all subsequent reads.