File size: 3,473 Bytes
6f0655f
 
 
 
 
 
 
 
9b45fba
6f0655f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
abd54b8
6f0655f
46f3967
abd54b8
6f0655f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import React from "react";
import ExploreGrid from "./explore-grid";
import {
  DatasetMetadata,
  fetchJson,
  formatStringWithVars,
} from "@/utils/parquetUtils";
import { getDatasetVersion, buildVersionedUrl } from "@/utils/versionUtils";
import { buildProxyUrl } from "@/utils/apiHelpers";

export default async function ExplorePage({
  searchParams,
}: {
  searchParams: { p?: string };
}) {
  let datasets: any[] = [];
  let currentPage = 1;
  let totalPages = 1;
  try {
    const res = await fetch(
      "https://huggingface.co/api/datasets?sort=lastModified&filter=LeRobot",
      {
        cache: "no-store",
      },
    );
    if (!res.ok) throw new Error("Failed to fetch datasets");
    const data = await res.json();
    const allDatasets = data.datasets || data;
    // Use searchParams from props
    const page = parseInt(searchParams?.p || "1", 10);
    const perPage = 30;

    currentPage = page;
    totalPages = Math.ceil(allDatasets.length / perPage);

    const startIdx = (currentPage - 1) * perPage;
    const endIdx = startIdx + perPage;
    datasets = allDatasets.slice(startIdx, endIdx);
  } catch {
    return <div className="p-8 text-red-600">Failed to load datasets.</div>;
  }

  // Fetch episode 0 data for each dataset
  const datasetWithVideos = (
    await Promise.all(
      datasets.map(async (ds: any) => {
        try {
          const [org, dataset] = ds.id.split("/");
          const repoId = `${org}/${dataset}`;
          
          // Try to get compatible version, but don't fail the entire page if incompatible
          let version: string;
          try {
            version = await getDatasetVersion(repoId);
          } catch (err) {
            // Dataset is not compatible, skip it silently
            console.warn(`Skipping incompatible dataset ${repoId}: ${err instanceof Error ? err.message : err}`);
            return null;
          }
          
          const jsonUrl = buildVersionedUrl(repoId, version, "meta/info.json");
          const info = await fetchJson<DatasetMetadata>(jsonUrl);
          const videoEntry = Object.entries(info.features).find(
            ([, value]) => value.dtype === "video",
          );
          let videoUrl: string | null = null;
          if (videoEntry) {
            const [key] = videoEntry;
            const videoPath = formatStringWithVars(info.video_path, {
              video_key: key,
              episode_chunk: "0".padStart(3, "0"),
              episode_index: "0".padStart(6, "0"),
            });
            const url = buildVersionedUrl(repoId, version, videoPath);
            // Check if videoUrl exists (status 200) - use proxy for authenticated requests
            try {
              const proxyUrl = await buildProxyUrl(url);
              const headRes = await fetch(proxyUrl, { method: "HEAD" });
              if (headRes.ok) {
                videoUrl = url;
              }
            } catch {
              // If fetch fails, videoUrl remains null
            }
          }
          return videoUrl ? { id: repoId, videoUrl } : null;
        } catch (err) {
          console.error(
            `Failed to fetch or parse dataset info for ${ds.id}:`,
            err,
          );
          return null;
        }
      }),
    )
  ).filter(Boolean) as { id: string; videoUrl: string | null }[];

  return (
    <ExploreGrid
      datasets={datasetWithVideos}
      currentPage={currentPage}
      totalPages={totalPages}
    />
  );
}