LocalClient
LocalClient extends MediaClient to provide integration with local file systems using the browser’s File System Access API. It creates in-memory video records from File objects and uses Blob URLs for playback.
Overview
LocalClient is designed for browser-based local folder access without requiring a server. It:
- Works with browser File System Access API or file input
- Creates Blob URLs for video playback
- Stores favorites in localStorage
- Filters videos by supported extensions
- Manages memory by revoking old Blob URLs
Constructor
constructor(config: ServerConfig, files: File[] = [])
Server configuration with serverType: 'local'
Array of File objects from file input or File System Access API
Implementation
From LocalClient.ts:45-51:
constructor(config: ServerConfig, files: File[] = []) {
super(config);
const { records, urls } = this.buildRecords(files);
this.records = records;
this.videoUrlMap = new Map(records.map((record) => [record.item.Id, record.objectUrl]));
this.replaceObjectUrls(urls);
}
Methods
authenticate
async authenticate(username: string): Promise<ServerConfig>
Returns a local server configuration. No actual authentication is performed.
Display name for local user (defaults to “Local User”)
Returns: Promise resolving to ServerConfig with serverType: 'local'
Example:
const config = await localClient.authenticate('My Videos');
// {
// url: 'local://folder',
// username: 'My Videos',
// token: '',
// userId: 'local-user',
// serverType: 'local'
// }
getLibraries
async getLibraries(): Promise<EmbyLibrary[]>
Returns a single library representing local videos.
Returns: Array with one library: { Id: 'local-library', Name: '本地视频' }
Example:
const libraries = await localClient.getLibraries();
// [{ Id: 'local-library', Name: '本地视频' }]
getVideos
async getVideos(
parentId: string | undefined,
libraryName: string,
feedType: FeedType,
skip: number,
limit: number,
orientationMode: OrientationMode
): Promise<VideoResponse>
Retrieves videos from local file records with filtering and pagination.
latest - Videos sorted by file modification time (newest first)
random - Shuffled selection of videos
favorites - Only favorited videos from localStorage
Filter by video dimensions (vertical/horizontal/both)
Number of items to skip (for pagination)
Maximum number of items to return
Returns: VideoResponse with filtered and paginated items
Implementation details:
From LocalClient.ts:67-103:
- Filters by favorites if
feedType === 'favorites'
- Filters by orientation mode
- Shuffles for random mode
- Sorts by lastModified for latest mode
getVideoUrl
getVideoUrl(item: EmbyItem): string
Returns the Blob URL for a video item.
Video item to get URL for
Returns: Blob URL string (e.g., blob:http://localhost:5173/abc-123)
Example:
const url = localClient.getVideoUrl(videoItem);
// "blob:http://localhost:5173/550e8400-e29b-41d4-a716-446655440000"
getImageUrl
getImageUrl(itemId: string, tag?: string, type?: 'Primary' | 'Backdrop'): string
Returns empty string as local mode doesn’t support thumbnails.
Returns: Empty string
getFavorites
async getFavorites(libraryName: string): Promise<Set<string>>
Reads favorites from localStorage.
Returns: Set of video item IDs marked as favorite
Storage key: embytokLocalFavorites (from LocalClient.ts:22)
toggleFavorite
async toggleFavorite(
itemId: string,
isFavorite: boolean,
libraryName: string
): Promise<void>
Adds or removes a video from localStorage favorites.
ID of the video to toggle
Current favorite state (true = remove, false = add)
From LocalClient.ts:24-36:
const VIDEO_EXTENSIONS = new Set([
'mp4', 'mkv', 'avi', 'mov', 'webm',
'm4v', 'flv', 'wmv', 'ts', 'm2ts', '3gp'
]);
Special handling:
.ts files must be ≥5MB to avoid false positives with TypeScript files
Memory Management
LocalClient manages Blob URLs to prevent memory leaks:
From LocalClient.ts:167-172:
private replaceObjectUrls(nextUrls: string[]): void {
LocalClient.activeObjectUrls.forEach((url) => {
URL.revokeObjectURL(url);
});
LocalClient.activeObjectUrls = new Set(nextUrls);
}
When switching folders or reloading, old Blob URLs are revoked and replaced with new ones.
Item ID Generation
Each local video gets a unique ID based on:
From LocalClient.ts:179-181:
private buildItemId(relativePath: string, file: File): string {
return `local:${encodeURIComponent(relativePath)}:${file.size}:${file.lastModified}`;
}
This ensures stable IDs across sessions if the same files are loaded.
Usage Example
import { ClientFactory } from './services/clientFactory';
// Get files from File System Access API
const dirHandle = await window.showDirectoryPicker();
const files = await loadVideoFilesFromDirectory(dirHandle);
// Create local client
const config = {
url: 'local://folder',
username: 'Local User',
token: '',
userId: 'local-user',
serverType: 'local'
};
const client = ClientFactory.create(config, { localFiles: files });
// Browse videos
const { items, totalCount } = await client.getVideos(
undefined,
'本地视频',
'latest',
0,
20,
'vertical'
);
// Get playback URL
const videoUrl = client.getVideoUrl(items[0]);
// Use videoUrl in <video> element