Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions content-providers/src/providers/dropbox/DropboxConverters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,27 @@ export function folderEntryToContentFolder(entry: DropboxFolderEntry, isLeaf?: b
}

export function fileEntryToContentFile(entry: DropboxFileEntry, url: string, downloadUrl?: string | null): ContentFile {
const mediaType = detectMediaType(entry.name);

// Videos: real duration from media_info (milliseconds). Images: intentionally no
// duration — playback apps keep them up until the operator advances.
const durationMs = entry.media_info?.metadata?.duration;
const seconds = mediaType === "video" && durationMs ? Math.round(durationMs / 1000) : undefined;

// Images are their own thumbnail (the shared raw link renders in an <img> tag).
// Videos get none here — Dropbox video thumbnails need an authed byte fetch;
// consumers can paint a first frame from the url instead.
const thumbnail = mediaType === "image" ? url : undefined;

return {
type: "file",
id: entry.id,
title: entry.name,
mediaType: detectMediaType(entry.name),
mediaType,
url,
downloadUrl: downloadUrl || url
downloadUrl: downloadUrl || url,
seconds,
thumbnail
};
}

Expand Down
14 changes: 14 additions & 0 deletions content-providers/src/providers/dropbox/DropboxInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ export interface DropboxFolderEntry {
path_display: string;
}

export interface DropboxMediaMetadata {
".tag": "photo" | "video";
/** Video duration in milliseconds. Only present for videos, and only once Dropbox has processed the file. */
duration?: number;
dimensions?: { height: number; width: number };
}

export interface DropboxMediaInfo {
".tag": "metadata" | "pending";
metadata?: DropboxMediaMetadata;
}

export interface DropboxFileEntry {
".tag": "file";
id: string;
Expand All @@ -14,6 +26,8 @@ export interface DropboxFileEntry {
path_display: string;
size: number;
is_downloadable: boolean;
/** Present when list_folder is called with include_media_info: true. */
media_info?: DropboxMediaInfo;
}

export type DropboxEntry = DropboxFolderEntry | DropboxFileEntry;
Expand Down
3 changes: 2 additions & 1 deletion content-providers/src/providers/dropbox/DropboxProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ export class DropboxProvider extends BaseProvider {

const response = await this.dropboxPost<DropboxListFolderResponse>(
"/2/files/list_folder",
{ path: dropboxPath, recursive: false, include_media_info: false },
// include_media_info gives us video durations (media_info.metadata.duration)
{ path: dropboxPath, recursive: false, include_media_info: true },
auth
);
if (!response) return [];
Expand Down
Loading