Files
MeBox/web/src/api/playback.ts
T
truewhile b0fe40142a Rebrand MMTL to MeBox (name, logo, Docker image) (#17)
* Rebrand MMTL to MeBox across codebase and assets

Rename the project display name, Go module path, environment variable
prefix (MEBOX_*), Docker image references, and UI branding from MMTL/mmtl
to MeBox/mebox. Replace logo assets with the new MeBox icon and keep
legacy SQLite migration support for existing mmtl.db deployments.

Co-authored-by: truewhile <truewhile@users.noreply.github.com>

* Fix logo icons: use cube-only crop without truncated text

Previous icon generation cropped too much of the source image, including
partial MeBox wordmark text that was cut off in square icon containers.
Regenerate logo-64/192/512, favicon, and SVG from cube-only region.

Co-authored-by: truewhile <truewhile@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: truewhile <truewhile@users.noreply.github.com>
2026-09-02 16:26:28 +08:00

95 lines
2.6 KiB
TypeScript

import { api } from './client'
import type { Media, Playlist } from '../types'
// History rows arrive joined with their Media row; the backend returns null
// for orphaned rows whose media has been removed.
export interface HistoryItem {
id: string
user_id: string
media_id: string
position_ms: number
duration_ms: number
watched_at: string
completed: boolean
media?: Media | null
created_at: string
updated_at: string
}
export interface PlaylistDetail {
playlist: Playlist
items: Media[]
}
export interface ExternalPlayer {
name: string
scheme: string
url: string
}
function publicOriginHeader() {
if (typeof window === 'undefined' || !window.location?.origin) return undefined
return { 'X-MeBox-Public-Origin': window.location.origin }
}
export const playbackAPI = {
getResume: (mediaId: string) =>
api
.get<{ position_ms: number; duration_ms: number; completed: boolean }>(`/playback/${mediaId}/resume`)
.then((r) => r.data),
recordProgress: (mediaId: string, positionMs: number, durationMs: number) =>
api
.post('/history', {
media_id: mediaId,
position_ms: positionMs,
duration_ms: durationMs,
})
.then((r) => r.data),
recentHistory: () =>
api.get<{ items: HistoryItem[] }>('/history').then((r) => r.data.items),
toggleFavourite: (mediaId: string) =>
api
.post<{ favourite: boolean }>(`/favourites/${mediaId}`)
.then((r) => r.data.favourite),
listFavourites: () =>
api.get<{ items: Media[] }>('/favourites').then((r) => r.data.items),
listPlaylists: () =>
api.get<{ items: Playlist[] }>('/playlists').then((r) => r.data.items),
createPlaylist: (name: string, isPublic = false) =>
api
.post<Playlist>('/playlists', { name, is_public: isPublic })
.then((r) => r.data),
getPlaylist: (id: string) =>
api.get<PlaylistDetail>(`/playlists/${id}`).then((r) => r.data),
addToPlaylist: (id: string, mediaId: string) =>
api.post(`/playlists/${id}/items`, { media_id: mediaId }).then((r) => r.data),
removeFromPlaylist: (id: string, mediaId: string) =>
api.delete(`/playlists/${id}/items/${mediaId}`).then((r) => r.data),
deletePlaylist: (id: string) =>
api.delete(`/playlists/${id}`).then((r) => r.data),
externalPlayers: (mediaId: string) =>
api
.get<{ players: ExternalPlayer[]; url?: string }>(`/playback/${mediaId}/external-players`, {
headers: publicOriginHeader(),
})
.then((r) => r.data),
externalURL: (mediaId: string) =>
api
.get<{ url: string }>(`/playback/${mediaId}/external-url`, {
headers: publicOriginHeader(),
})
.then((r) => r.data),
}