Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Commit
•
a64395a
1
Parent(s):
1f122c3
made some changes to the hub.js client
Browse files- src/app/main.tsx +2 -1
- src/app/server/actions/api.ts +10 -21
- src/app/state/defaultSettings.ts +5 -0
- src/app/state/locaStorageKeys.ts +6 -0
- src/app/views/channel-admin-view/index.tsx +4 -3
- src/app/views/channel-public-view/index.tsx +4 -3
- src/app/views/channels-admin-view/index.tsx +48 -17
- src/app/views/channels-public-view/index.tsx +23 -38
- src/app/views/home-view/index.tsx +4 -3
- src/huggingface/hub/src/lib/list-datasets.ts +5 -2
- src/types.ts +5 -1
src/app/main.tsx
CHANGED
@@ -17,11 +17,12 @@ export function Main() {
|
|
17 |
return (
|
18 |
<div className={cn(
|
19 |
`flex flex-row h-screen w-screen inset-0 overflow-hidden`,
|
|
|
20 |
)}>
|
21 |
<LeftMenu />
|
22 |
<div className={cn(
|
23 |
`flex flex-col`,
|
24 |
-
`w-[calc(100vh-
|
25 |
)}>
|
26 |
<TopMenu />
|
27 |
{view === "home" && <HomeView />}
|
|
|
17 |
return (
|
18 |
<div className={cn(
|
19 |
`flex flex-row h-screen w-screen inset-0 overflow-hidden`,
|
20 |
+
`dark`
|
21 |
)}>
|
22 |
<LeftMenu />
|
23 |
<div className={cn(
|
24 |
`flex flex-col`,
|
25 |
+
`w-[calc(100vh-96px)]`
|
26 |
)}>
|
27 |
<TopMenu />
|
28 |
{view === "home" && <HomeView />}
|
src/app/server/actions/api.ts
CHANGED
@@ -8,28 +8,17 @@ const adminUsername = `${process.env.ADMIN_HUGGING_FACE_USERNAME || ""}`
|
|
8 |
|
9 |
const adminCredentials: Credentials = { accessToken: adminApiKey }
|
10 |
|
11 |
-
export async function getChannels({
|
12 |
-
apiKey
|
13 |
-
owner
|
14 |
-
}
|
15 |
-
apiKey: string
|
16 |
-
owner: undefined
|
17 |
-
} | { // the user wants to browse someone's else public channels
|
18 |
-
apiKey: undefined
|
19 |
-
owner: string
|
20 |
-
} | { // the user wants to browse all public channels
|
21 |
-
apiKey: undefined
|
22 |
-
owner: undefined
|
23 |
-
} = { // by default we perform a gloval search using admin credentials
|
24 |
-
apiKey: undefined,
|
25 |
-
owner: undefined
|
26 |
-
}): Promise<ChannelInfo[]> {
|
27 |
|
28 |
let credentials: Credentials = adminCredentials
|
|
|
29 |
|
30 |
-
if (apiKey) {
|
31 |
try {
|
32 |
-
credentials = { accessToken: apiKey }
|
33 |
const { name: username } = await whoAmI({ credentials })
|
34 |
if (!username) {
|
35 |
throw new Error(`couldn't get the username`)
|
@@ -48,7 +37,7 @@ export async function getChannels({
|
|
48 |
|
49 |
let search = owner
|
50 |
? { owner } // search channels of a specific user
|
51 |
-
:
|
52 |
|
53 |
console.log("search:", search)
|
54 |
|
@@ -62,7 +51,7 @@ export async function getChannels({
|
|
62 |
? chunks
|
63 |
: [name, name]
|
64 |
|
65 |
-
console.log(`found a candidate dataset "${datasetName}" owned by @${datasetUsername}`)
|
66 |
|
67 |
if (!datasetName.startsWith(prefix)) {
|
68 |
continue
|
@@ -70,7 +59,7 @@ export async function getChannels({
|
|
70 |
|
71 |
const slug = datasetName.replaceAll(prefix, "")
|
72 |
|
73 |
-
console.log(`
|
74 |
|
75 |
// TODO parse the README to get the proper label
|
76 |
const label = slug.replaceAll("-", " ")
|
|
|
8 |
|
9 |
const adminCredentials: Credentials = { accessToken: adminApiKey }
|
10 |
|
11 |
+
export async function getChannels(options: {
|
12 |
+
apiKey?: string
|
13 |
+
owner?: string
|
14 |
+
} = {}): Promise<ChannelInfo[]> {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
let credentials: Credentials = adminCredentials
|
17 |
+
let owner = options?.owner
|
18 |
|
19 |
+
if (options?.apiKey) {
|
20 |
try {
|
21 |
+
credentials = { accessToken: options.apiKey }
|
22 |
const { name: username } = await whoAmI({ credentials })
|
23 |
if (!username) {
|
24 |
throw new Error(`couldn't get the username`)
|
|
|
37 |
|
38 |
let search = owner
|
39 |
? { owner } // search channels of a specific user
|
40 |
+
: prefix // global search (note: might be costly?)
|
41 |
|
42 |
console.log("search:", search)
|
43 |
|
|
|
51 |
? chunks
|
52 |
: [name, name]
|
53 |
|
54 |
+
// console.log(`found a candidate dataset "${datasetName}" owned by @${datasetUsername}`)
|
55 |
|
56 |
if (!datasetName.startsWith(prefix)) {
|
57 |
continue
|
|
|
59 |
|
60 |
const slug = datasetName.replaceAll(prefix, "")
|
61 |
|
62 |
+
console.log(`found an AI Tube channel: "${slug}"`)
|
63 |
|
64 |
// TODO parse the README to get the proper label
|
65 |
const label = slug.replaceAll("-", " ")
|
src/app/state/defaultSettings.ts
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Settings } from "@/types"
|
2 |
+
|
3 |
+
export const defaultSettings: Settings = {
|
4 |
+
huggingfaceApiKey: "",
|
5 |
+
}
|
src/app/state/locaStorageKeys.ts
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Settings } from "@/types"
|
2 |
+
|
3 |
+
export const localStorageKeys: Record<keyof Settings, string> = {
|
4 |
+
// important: prefix with AI_TUBE to avoid collisions when running the app on localhost
|
5 |
+
huggingfaceApiKey: "AI_TUBE_CONF_AUTH_HF_API_TOKEN",
|
6 |
+
}
|
src/app/views/channel-admin-view/index.tsx
CHANGED
@@ -33,10 +33,11 @@ export function ChannelAdminView() {
|
|
33 |
channelId: "",
|
34 |
channel: {
|
35 |
id: "",
|
|
|
36 |
label: "Hugging Face",
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
}
|
41 |
}
|
42 |
]
|
|
|
33 |
channelId: "",
|
34 |
channel: {
|
35 |
id: "",
|
36 |
+
slug: "",
|
37 |
label: "Hugging Face",
|
38 |
+
thumbnail: "",
|
39 |
+
prompt: "",
|
40 |
+
likes: 0,
|
41 |
}
|
42 |
}
|
43 |
]
|
src/app/views/channel-public-view/index.tsx
CHANGED
@@ -33,10 +33,11 @@ export function ChannelPublicView() {
|
|
33 |
channelId: "",
|
34 |
channel: {
|
35 |
id: "",
|
|
|
36 |
label: "Hugging Face",
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
}
|
41 |
}
|
42 |
]
|
|
|
33 |
channelId: "",
|
34 |
channel: {
|
35 |
id: "",
|
36 |
+
slug: "",
|
37 |
label: "Hugging Face",
|
38 |
+
thumbnail: "",
|
39 |
+
prompt: "",
|
40 |
+
likes: 0,
|
41 |
}
|
42 |
}
|
43 |
]
|
src/app/views/channels-admin-view/index.tsx
CHANGED
@@ -1,37 +1,68 @@
|
|
1 |
-
|
|
|
|
|
|
|
2 |
|
3 |
import { useStore } from "@/app/state/useStore"
|
4 |
import { cn } from "@/lib/utils"
|
5 |
import { getChannels } from "@/app/server/actions/api"
|
6 |
import { ChannelList } from "@/app/interface/channel-list"
|
|
|
|
|
|
|
7 |
|
8 |
export function ChannelsAdminView() {
|
9 |
const [_isPending, startTransition] = useTransition()
|
|
|
|
|
|
|
|
|
10 |
|
11 |
const currentChannels = useStore(s => s.currentChannels)
|
12 |
const setCurrentChannels = useStore(s => s.setCurrentChannels)
|
13 |
-
const
|
14 |
|
15 |
useEffect(() => {
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
27 |
|
28 |
return (
|
29 |
<div className={cn(
|
30 |
-
`flex flex-col`
|
31 |
)}>
|
32 |
-
<
|
33 |
-
|
34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
</div>
|
36 |
)
|
37 |
}
|
|
|
1 |
+
"use client"
|
2 |
+
|
3 |
+
import { useEffect, useState, useTransition } from "react"
|
4 |
+
import { useLocalStorage } from "usehooks-ts"
|
5 |
|
6 |
import { useStore } from "@/app/state/useStore"
|
7 |
import { cn } from "@/lib/utils"
|
8 |
import { getChannels } from "@/app/server/actions/api"
|
9 |
import { ChannelList } from "@/app/interface/channel-list"
|
10 |
+
import { Input } from "@/components/ui/input"
|
11 |
+
import { localStorageKeys } from "@/app/state/locaStorageKeys"
|
12 |
+
import { defaultSettings } from "@/app/state/defaultSettings"
|
13 |
|
14 |
export function ChannelsAdminView() {
|
15 |
const [_isPending, startTransition] = useTransition()
|
16 |
+
const [huggingfaceApiKey, setHuggingfaceApiKey] = useLocalStorage<string>(
|
17 |
+
localStorageKeys.huggingfaceApiKey,
|
18 |
+
defaultSettings.huggingfaceApiKey
|
19 |
+
)
|
20 |
|
21 |
const currentChannels = useStore(s => s.currentChannels)
|
22 |
const setCurrentChannels = useStore(s => s.setCurrentChannels)
|
23 |
+
const [isLoaded, setLoaded] = useState(false)
|
24 |
|
25 |
useEffect(() => {
|
26 |
+
if (!isLoaded) {
|
27 |
+
startTransition(async () => {
|
28 |
+
try {
|
29 |
+
const channels = await getChannels({ apiKey: huggingfaceApiKey })
|
30 |
+
setCurrentChannels(channels)
|
31 |
+
} catch (err) {
|
32 |
+
console.error("failed to load the channel for the current user:", err)
|
33 |
+
setCurrentChannels([])
|
34 |
+
} finally {
|
35 |
+
setLoaded(true)
|
36 |
+
}
|
37 |
+
})
|
38 |
+
}
|
39 |
+
}, [isLoaded])
|
40 |
|
41 |
return (
|
42 |
<div className={cn(
|
43 |
+
`flex flex-col space-y-4`
|
44 |
)}>
|
45 |
+
<div className="flex flex-col space-y-2">
|
46 |
+
<div className="flex flex-row space-x-2 items-center">
|
47 |
+
<label className="flex w-64">Hugging Face token:</label>
|
48 |
+
<Input
|
49 |
+
placeholder="Hugging Face token (with WRITE access)"
|
50 |
+
type="password"
|
51 |
+
className="font-mono"
|
52 |
+
onChange={(x) => {
|
53 |
+
setHuggingfaceApiKey(x.target.value)
|
54 |
+
}}
|
55 |
+
value={huggingfaceApiKey}
|
56 |
+
/>
|
57 |
+
</div>
|
58 |
+
<p className="text-neutral-100/70">
|
59 |
+
Note: your Hugging Face token must be a <span className="font-bold font-mono text-yellow-300">WRITE</span> access token.
|
60 |
+
</p>
|
61 |
+
</div>
|
62 |
+
{huggingfaceApiKey ?
|
63 |
+
<ChannelList
|
64 |
+
channels={currentChannels}
|
65 |
+
/> : null}
|
66 |
</div>
|
67 |
)
|
68 |
}
|
src/app/views/channels-public-view/index.tsx
CHANGED
@@ -1,54 +1,39 @@
|
|
1 |
-
import { useEffect } from "react"
|
2 |
|
3 |
import { useStore } from "@/app/state/useStore"
|
4 |
import { cn } from "@/lib/utils"
|
5 |
-
import {
|
6 |
-
import {
|
7 |
|
8 |
export function ChannelsPublicView() {
|
9 |
-
const
|
10 |
-
const setDisplayMode = useStore(s => s.setDisplayMode)
|
11 |
-
const currentChannel = useStore(s => s.currentChannel)
|
12 |
-
const setCurrentChannel = useStore(s => s.setCurrentChannel)
|
13 |
-
const currentCategory = useStore(s => s.currentCategory)
|
14 |
-
const setCurrentCategory = useStore(s => s.setCurrentCategory)
|
15 |
-
const currentVideos = useStore(s => s.currentVideos)
|
16 |
-
const setCurrentVideos = useStore(s => s.setCurrentVideos)
|
17 |
-
const currentVideo = useStore(s => s.currentVideo)
|
18 |
-
const setCurrentVideo = useStore(s => s.setCurrentVideo)
|
19 |
|
20 |
-
|
|
|
|
|
21 |
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
channelId: "",
|
34 |
-
channel: {
|
35 |
-
id: "",
|
36 |
-
label: "Hugging Face",
|
37 |
-
thumbnailUrl: "",
|
38 |
-
systemPrompt: "",
|
39 |
-
hfDatasetId: ""
|
40 |
}
|
41 |
-
}
|
42 |
-
|
43 |
-
|
44 |
-
}, [currentCategory])
|
45 |
|
46 |
return (
|
47 |
<div className={cn(
|
48 |
`flex flex-col`
|
49 |
)}>
|
50 |
-
<
|
51 |
-
|
52 |
/>
|
53 |
</div>
|
54 |
)
|
|
|
1 |
+
import { useEffect, useState, useTransition } from "react"
|
2 |
|
3 |
import { useStore } from "@/app/state/useStore"
|
4 |
import { cn } from "@/lib/utils"
|
5 |
+
import { getChannels } from "@/app/server/actions/api"
|
6 |
+
import { ChannelList } from "@/app/interface/channel-list"
|
7 |
|
8 |
export function ChannelsPublicView() {
|
9 |
+
const [_isPending, startTransition] = useTransition()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
+
const currentChannels = useStore(s => s.currentChannels)
|
12 |
+
const setCurrentChannels = useStore(s => s.setCurrentChannels)
|
13 |
+
const [isLoaded, setLoaded] = useState(false)
|
14 |
|
15 |
+
useEffect(() => {
|
16 |
+
if (!isLoaded) {
|
17 |
+
startTransition(async () => {
|
18 |
+
try {
|
19 |
+
const channels = await getChannels()
|
20 |
+
setCurrentChannels(channels)
|
21 |
+
} catch (err) {
|
22 |
+
console.error("failed to load the public channels", err)
|
23 |
+
setCurrentChannels([])
|
24 |
+
} finally {
|
25 |
+
setLoaded(true)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
}
|
27 |
+
})
|
28 |
+
}
|
29 |
+
}, [isLoaded])
|
|
|
30 |
|
31 |
return (
|
32 |
<div className={cn(
|
33 |
`flex flex-col`
|
34 |
)}>
|
35 |
+
<ChannelList
|
36 |
+
channels={currentChannels}
|
37 |
/>
|
38 |
</div>
|
39 |
)
|
src/app/views/home-view/index.tsx
CHANGED
@@ -32,10 +32,11 @@ export function HomeView() {
|
|
32 |
channelId: "",
|
33 |
channel: {
|
34 |
id: "",
|
|
|
35 |
label: "Hugging Face",
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
}
|
40 |
}
|
41 |
]
|
|
|
32 |
channelId: "",
|
33 |
channel: {
|
34 |
id: "",
|
35 |
+
slug: "",
|
36 |
label: "Hugging Face",
|
37 |
+
thumbnail: "",
|
38 |
+
prompt: "",
|
39 |
+
likes: 0,
|
40 |
}
|
41 |
}
|
42 |
]
|
src/huggingface/hub/src/lib/list-datasets.ts
CHANGED
@@ -20,7 +20,7 @@ export interface DatasetEntry {
|
|
20 |
export async function* listDatasets(params?: {
|
21 |
search?: {
|
22 |
owner?: string;
|
23 |
-
};
|
24 |
credentials?: Credentials;
|
25 |
hubUrl?: string;
|
26 |
/**
|
@@ -32,7 +32,10 @@ export async function* listDatasets(params?: {
|
|
32 |
const search = new URLSearchParams([
|
33 |
...Object.entries({
|
34 |
limit: "500",
|
35 |
-
...(
|
|
|
|
|
|
|
36 |
}),
|
37 |
...EXPAND_KEYS.map((val) => ["expand", val] satisfies [string, string]),
|
38 |
]).toString();
|
|
|
20 |
export async function* listDatasets(params?: {
|
21 |
search?: {
|
22 |
owner?: string;
|
23 |
+
} | string;
|
24 |
credentials?: Credentials;
|
25 |
hubUrl?: string;
|
26 |
/**
|
|
|
32 |
const search = new URLSearchParams([
|
33 |
...Object.entries({
|
34 |
limit: "500",
|
35 |
+
...(
|
36 |
+
typeof params?.search === "string" ? { search: params?.search }
|
37 |
+
: params?.search?.owner ? { author: params.search.owner }
|
38 |
+
: undefined),
|
39 |
}),
|
40 |
...EXPAND_KEYS.map((val) => ["expand", val] satisfies [string, string]),
|
41 |
]).toString();
|
src/types.ts
CHANGED
@@ -219,4 +219,8 @@ export type InterfaceView =
|
|
219 |
| "channels_public"
|
220 |
| "channel_admin" // for a user to admin their channels
|
221 |
| "channel_public" // public view of a channel
|
222 |
-
| "video_public" // public view of a video
|
|
|
|
|
|
|
|
|
|
219 |
| "channels_public"
|
220 |
| "channel_admin" // for a user to admin their channels
|
221 |
| "channel_public" // public view of a channel
|
222 |
+
| "video_public" // public view of a video
|
223 |
+
|
224 |
+
export type Settings = {
|
225 |
+
huggingfaceApiKey: string
|
226 |
+
}
|