Core data structure representing all media types (movies, TV shows, episodes).
export interface MediaItem {
id: number;
title: string;
year?: number;
overview?: string;
cast_names?: string;
poster_path?: string;
file_path?: string;
media_type: 'movie' | 'tvshow' | 'tvepisode';
duration_seconds?: number;
resume_position_seconds?: number;
last_watched?: string;
season_number?: number;
episode_number?: number;
progress_percent?: number;
parent_id?: number;
tmdb_id?: string;
episode_title?: string;
still_path?: string;
// Cloud storage fields
is_cloud?: boolean;
cloud_file_id?: string;
}
Unique database identifier
Media title (show name for TV shows, movie title for movies)
media_type
'movie' | 'tvshow' | 'tvepisode'
required
Type of media item:
movie - Standalone movie file
tvshow - TV series container (no file, just metadata)
tvepisode - Individual episode linked to parent show
Plot description from TMDB
Cached poster image filename (use getCachedImageUrl() to convert to URL)
Local file system path or cloud file name
Last playback position (0 if not started or completed)
Watch progress percentage (calculated field)
Season number (TV episodes only)
Episode number (TV episodes only)
ID of parent TV show (episodes only)
TMDB identifier for metadata lookups
Episode-specific title (TV episodes only)
Episode thumbnail image (TV episodes only)
Whether this is a cloud-stored file
Google Drive file ID (cloud files only)
Get Library
Retrieve movies or TV shows with optional search.
export const getLibrary = async (
type: 'movie' | 'tv',
search: string = ''
): Promise<MediaItem[]>
Media type to retrieve:
'movie' - Returns all movies
'tv' - Returns all TV shows (not individual episodes)
Case-insensitive title search filter
Array of media items sorted by title
Example Usage
import { getLibrary } from '@/services/api';
// Get all movies
const movies = await getLibrary('movie');
// Search for specific movie
const inception = await getLibrary('movie', 'inception');
// Get all TV shows
const shows = await getLibrary('tv');
Backend Command
#[tauri::command]
async fn get_library(
state: State<'_, AppState>,
media_type: String,
search: Option<String>,
) -> Result<Vec<database::MediaItem>, String>
Get Library Filtered
Retrieve library with cloud storage filtering.
export const getLibraryFiltered = async (
type: 'movie' | 'tv',
search: string = '',
isCloud?: boolean
): Promise<MediaItem[]>
Filter by storage location:
true - Only cloud files
false - Only local files
undefined - All files
Example Usage
// Get only cloud movies
const cloudMovies = await getLibraryFiltered('movie', '', true);
// Get only local TV shows
const localShows = await getLibraryFiltered('tv', '', false);
// Search cloud library
const results = await getLibraryFiltered('movie', 'avatar', true);
Get Episodes
Retrieve all episodes for a TV show.
export const getEpisodes = async (seriesId: number): Promise<MediaItem[]>
Array of episodes sorted by season and episode number
Example Usage
import { getLibrary, getEpisodes } from '@/services/api';
// Get a TV show
const shows = await getLibrary('tv', 'breaking bad');
const show = shows[0];
// Get all episodes
const episodes = await getEpisodes(show.id);
// Group by season
const seasons = episodes.reduce((acc, ep) => {
const season = ep.season_number || 1;
if (!acc[season]) acc[season] = [];
acc[season].push(ep);
return acc;
}, {} as Record<number, MediaItem[]>);
Get Watch History
Retrieve recently watched content.
export const getWatchHistory = async (): Promise<MediaItem[]>
Up to 50 most recently watched items, sorted by last_watched descending
Example Usage
const history = await getWatchHistory();
history.forEach(item => {
console.log(`Watched: ${item.title}`);
console.log(`Progress: ${item.progress_percent}%`);
console.log(`Last watched: ${item.last_watched}`);
});
Add/Remove from Watch History
// Remove single item
export const removeFromWatchHistory = async (id: number): Promise<void>
// Clear all history
export const clearAllWatchHistory = async (): Promise<void>
Media item ID to remove from history
Example Usage
import { removeFromWatchHistory, clearAllWatchHistory } from '@/services/api';
// Remove specific item
await removeFromWatchHistory(123);
// Clear all watch history
await clearAllWatchHistory();
Mark as Complete
Mark content as fully watched (100%).
export const markAsComplete = async (mediaId: number): Promise<{ message: string }>
ID of media to mark as complete
Example Usage
const response = await markAsComplete(movieId);
console.log(response.message); // "Marked 'Movie Title' as complete"
Search & Filter
Search TMDB for metadata matching.
export interface TmdbSearchResult {
id: number;
title?: string;
name?: string;
media_type: 'movie' | 'tv';
poster_path?: string;
backdrop_path?: string;
overview?: string;
release_date?: string;
first_air_date?: string;
vote_average?: number;
}
export interface TmdbSearchResponse {
results: TmdbSearchResult[];
total_results: number;
}
export const searchTmdb = async (query: string): Promise<TmdbSearchResponse>
Example Usage
import { searchTmdb } from '@/services/api';
const results = await searchTmdb('inception');
results.results.forEach(item => {
console.log(`${item.title || item.name} (${item.media_type})`);
});
Fix Match
Update media metadata from TMDB.
export const fixMatch = async (
id: number,
tmdbId: string,
type: 'movie' | 'tv'
): Promise<void>
TMDB ID for correct metadata
Example Usage
import { searchTmdb, fixMatch } from '@/services/api';
// User selects correct match
const results = await searchTmdb('inception');
const correctMatch = results.results[0];
// Update media with correct metadata
await fixMatch(mediaId, correctMatch.id.toString(), 'movie');
Delete media files from disk.
export interface DeleteResponse {
success: boolean;
deleted_count: number;
failed_count: number;
message: string;
}
export const deleteMediaFiles = async (mediaIds: number[]): Promise<DeleteResponse>
Array of media IDs to delete
Example Usage
import { deleteMediaFiles } from '@/services/api';
const result = await deleteMediaFiles([123, 456, 789]);
if (result.success) {
console.log(`Deleted ${result.deleted_count} files`);
if (result.failed_count > 0) {
console.warn(`Failed to delete ${result.failed_count} files`);
}
}
Image Handling
Convert cached image names to usable URLs.
export const getCachedImageUrl = (imageName: string): Promise<string | null>
Image filename from poster_path or still_path
Asset protocol URL or null if image doesn’t exist
Example Usage
import { getLibrary, getCachedImageUrl } from '@/services/api';
const movies = await getLibrary('movie');
for (const movie of movies) {
if (movie.poster_path) {
const posterUrl = await getCachedImageUrl(movie.poster_path);
if (posterUrl) {
// Use in <img src={posterUrl} />
}
}
}
Rich episode information from TMDB.
export interface TmdbEpisodeInfo {
episode_number: number;
name: string;
overview?: string;
still_path?: string;
is_cloud?: boolean;
cloud_file_id?: string;
air_date?: string;
runtime?: number;
vote_average?: number;
}
export interface TmdbSeasonDetails {
season_number: number;
name: string;
episodes: TmdbEpisodeInfo[];
}
export const getTvSeasonEpisodes = async (
tvId: number,
seasonNumber: number
): Promise<TmdbSeasonDetails | null>
Example Usage
import { getTvSeasonEpisodes } from '@/services/api';
const season = await getTvSeasonEpisodes(1399, 1); // Breaking Bad S01
if (season) {
console.log(`${season.name} - ${season.episodes.length} episodes`);
season.episodes.forEach(ep => {
console.log(`E${ep.episode_number}: ${ep.name}`);
});
}