jbilcke-hf HF staff commited on
Commit
6967c22
1 Parent(s): 2ed75c8

faster download of public channels

Browse files
src/app/server/actions/ai-tube-hf/getChannel.ts CHANGED
@@ -7,8 +7,8 @@ import { getChannels } from "./getChannels"
7
 
8
  export async function getChannel(options: {
9
  channelId?: string | string[] | null
10
- apiKey?: string
11
- owner?: string
12
  renewCache?: boolean
13
  neverThrow?: boolean
14
  } = {}): Promise<ChannelInfo | undefined> {
@@ -20,8 +20,8 @@ export async function getChannel(options: {
20
 
21
  const channels = await getChannels({
22
  channelId: id,
23
- apiKey: options?.apiKey,
24
- owner: options?.owner,
25
  renewCache: options.renewCache,
26
  })
27
 
 
7
 
8
  export async function getChannel(options: {
9
  channelId?: string | string[] | null
10
+ // apiKey?: string // deprecated, we only work on public content
11
+ // owner?: string // deprecated, we only work on public content
12
  renewCache?: boolean
13
  neverThrow?: boolean
14
  } = {}): Promise<ChannelInfo | undefined> {
 
20
 
21
  const channels = await getChannels({
22
  channelId: id,
23
+ // apiKey: options?.apiKey, // deprecated, we only work on public content
24
+ // owner: options?.owner, // deprecated, we only work on public content
25
  renewCache: options.renewCache,
26
  })
27
 
src/app/server/actions/ai-tube-hf/getChannelVideos.ts CHANGED
@@ -4,7 +4,7 @@ import { ChannelInfo, VideoInfo, VideoStatus } from "@/types"
4
 
5
  import { getVideoRequestsFromChannel } from "./getVideoRequestsFromChannel"
6
  import { adminApiKey } from "../config"
7
- import { getIndex } from "./getIndex"
8
 
9
  // return
10
  export async function getChannelVideos({
@@ -25,8 +25,8 @@ export async function getChannelVideos({
25
 
26
  // TODO: use a database instead
27
  // normally
28
- const queued = await getIndex({ status: "queued" })
29
- const published = await getIndex({ status: "published" })
30
 
31
  return videos.map(v => {
32
  let video: VideoInfo = {
 
4
 
5
  import { getVideoRequestsFromChannel } from "./getVideoRequestsFromChannel"
6
  import { adminApiKey } from "../config"
7
+ import { getVideoIndex } from "./getVideoIndex"
8
 
9
  // return
10
  export async function getChannelVideos({
 
25
 
26
  // TODO: use a database instead
27
  // normally
28
+ const queued = await getVideoIndex({ status: "queued" })
29
+ const published = await getVideoIndex({ status: "published" })
30
 
31
  return videos.map(v => {
32
  let video: VideoInfo = {
src/app/server/actions/ai-tube-hf/getChannels.ts CHANGED
@@ -1,87 +1,47 @@
1
  "use server"
2
 
3
- import { Credentials, listDatasets, whoAmI } from "@/huggingface/hub/src"
4
  import { ChannelInfo } from "@/types"
5
 
6
- import { adminCredentials } from "../config"
7
- import { parseChannel } from "./parseChannel"
8
 
9
- export async function getChannels(options: {
 
 
 
 
10
  channelId?: string
11
- apiKey?: string
12
- owner?: string
13
  renewCache?: boolean
 
14
  } = {}): Promise<ChannelInfo[]> {
15
- // console.log("getChannels")
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`)
25
- }
26
- // everything is in order,
27
- owner = username
28
- } catch (err) {
29
- console.error(err)
30
- return []
31
- }
32
- }
33
-
34
- let channels: ChannelInfo[] = []
35
-
36
- const prefix = "ai-tube-"
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
-
44
- for await (const { id, name, likes, updatedAt } of listDatasets({
45
- search,
46
- credentials,
47
- requestInit: options?.renewCache
48
- ? { cache: "no-store" }
49
- : undefined
50
- })) {
51
- if (options.channelId && options.channelId !== id) {
52
- continue
53
- }
54
-
55
- // TODO: need to handle better cases where the username is missing
56
 
57
- const chunks = name.split("/")
58
- const [_datasetUser, datasetName] = chunks.length === 2
59
- ? chunks
60
- : [name, name]
61
 
62
- // console.log(`found a candidate dataset "${datasetName}" owned by @${datasetUser}`)
63
-
64
- // ignore channels which don't start with ai-tube
65
- if (!datasetName.startsWith(prefix)) {
66
- continue
 
67
  }
68
 
69
- // ignore the video index
70
- if (datasetName === "ai-tube-index") {
71
- continue
72
- }
73
 
74
- const channel = await parseChannel({
75
- ...options,
76
- id, name, likes, updatedAt
77
- })
78
 
79
- channels.push(channel)
80
 
81
- if (options.channelId && options.channelId === id) {
82
- break
 
 
 
83
  }
 
84
  }
85
-
86
- return channels
87
  }
 
1
  "use server"
2
 
 
3
  import { ChannelInfo } from "@/types"
4
 
5
+ import { adminUsername } from "../config"
 
6
 
7
+ export async function getChannels({
8
+ channelId = "",
9
+ renewCache = true,
10
+ neverThrow = true
11
+ }: {
12
  channelId?: string
 
 
13
  renewCache?: boolean
14
+ neverThrow?: boolean
15
  } = {}): Promise<ChannelInfo[]> {
16
+ try {
17
+ const response = await fetch(
18
+ `https://huggingface.co/datasets/${adminUsername}/ai-tube-index/raw/main/channels.json`
19
+ , {
20
+ cache: renewCache ? "no-store" : "default"
21
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ const jsonResponse = await response?.json()
 
 
 
24
 
25
+ if (
26
+ typeof jsonResponse === "undefined" ||
27
+ typeof jsonResponse !== "object" ||
28
+ Array.isArray(jsonResponse) ||
29
+ jsonResponse === null) {
30
+ throw new Error("index is not an object, admin repair needed")
31
  }
32
 
33
+ const channelsIndex = (jsonResponse as Record<string, ChannelInfo>) || {}
 
 
 
34
 
35
+ const channels = Object.values(channelsIndex)
 
 
 
36
 
37
+ if (!channelId) { return channels }
38
 
39
+ return channels.filter(channel => channel.id === channelId)
40
+ } catch (err) {
41
+ if (neverThrow) {
42
+ console.error(`failed to get the channel index:`, err)
43
+ return []
44
  }
45
+ throw err
46
  }
 
 
47
  }
src/app/server/actions/ai-tube-hf/getPrivateChannels.ts ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use server"
2
+
3
+ import { Credentials, listDatasets, whoAmI } from "@/huggingface/hub/src"
4
+ import { ChannelInfo } from "@/types"
5
+
6
+ import { adminCredentials } from "../config"
7
+ import { parseChannel } from "./parseChannel"
8
+
9
+ export async function getPrivateChannels(options: {
10
+ channelId?: string
11
+ apiKey?: string
12
+ owner?: string
13
+ renewCache?: boolean
14
+ } = {}): Promise<ChannelInfo[]> {
15
+ // console.log("getChannels")
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`)
25
+ }
26
+ // everything is in order,
27
+ owner = username
28
+ } catch (err) {
29
+ console.error(err)
30
+ return []
31
+ }
32
+ }
33
+
34
+ let channels: ChannelInfo[] = []
35
+
36
+ const prefix = "ai-tube-"
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
+
44
+ for await (const { id, name, likes, updatedAt } of listDatasets({
45
+ search,
46
+ credentials,
47
+ requestInit: options?.renewCache
48
+ ? { cache: "no-store" }
49
+ : undefined
50
+ })) {
51
+ if (options.channelId && options.channelId !== id) {
52
+ continue
53
+ }
54
+
55
+ // TODO: need to handle better cases where the username is missing
56
+
57
+ const chunks = name.split("/")
58
+ const [_datasetUser, datasetName] = chunks.length === 2
59
+ ? chunks
60
+ : [name, name]
61
+
62
+ // console.log(`found a candidate dataset "${datasetName}" owned by @${datasetUser}`)
63
+
64
+ // ignore channels which don't start with ai-tube
65
+ if (!datasetName.startsWith(prefix)) {
66
+ continue
67
+ }
68
+
69
+ // ignore the video index
70
+ if (datasetName === "ai-tube-index") {
71
+ continue
72
+ }
73
+
74
+ const channel = await parseChannel({
75
+ ...options,
76
+ id, name, likes, updatedAt
77
+ })
78
+
79
+ channels.push(channel)
80
+
81
+ if (options.channelId && options.channelId === id) {
82
+ break
83
+ }
84
+ }
85
+
86
+ return channels
87
+ }
src/app/server/actions/ai-tube-hf/getTags.ts CHANGED
@@ -1,6 +1,6 @@
1
  "use server"
2
 
3
- import { getIndex } from "./getIndex"
4
 
5
  export async function getTags({
6
  renewCache = true,
@@ -13,7 +13,7 @@ export async function getTags({
13
  neverThrow: true,
14
  }): Promise<string[]> {
15
  try {
16
- const published = Object.values(await getIndex({
17
  status: "published",
18
  renewCache,
19
  }))
 
1
  "use server"
2
 
3
+ import { getVideoIndex } from "./getVideoIndex"
4
 
5
  export async function getTags({
6
  renewCache = true,
 
13
  neverThrow: true,
14
  }): Promise<string[]> {
15
  try {
16
+ const published = Object.values(await getVideoIndex({
17
  status: "published",
18
  renewCache,
19
  }))
src/app/server/actions/ai-tube-hf/getVideo.ts CHANGED
@@ -2,7 +2,7 @@
2
 
3
  import { VideoInfo } from "@/types"
4
 
5
- import { getIndex } from "./getIndex"
6
 
7
  export async function getVideo({
8
  videoId,
@@ -17,7 +17,7 @@ export async function getVideo({
17
  if (!id) {
18
  throw new Error(`cannot get the video, invalid id: "${id}"`)
19
  }
20
- const published = await getIndex({ status: "published" })
21
 
22
  const video = published[id] || undefined
23
 
 
2
 
3
  import { VideoInfo } from "@/types"
4
 
5
+ import { getVideoIndex } from "./getVideoIndex"
6
 
7
  export async function getVideo({
8
  videoId,
 
17
  if (!id) {
18
  throw new Error(`cannot get the video, invalid id: "${id}"`)
19
  }
20
+ const published = await getVideoIndex({ status: "published" })
21
 
22
  const video = published[id] || undefined
23
 
src/app/server/actions/ai-tube-hf/{getIndex.ts → getVideoIndex.ts} RENAMED
@@ -2,7 +2,7 @@ import { VideoInfo, VideoStatus } from "@/types"
2
 
3
  import { adminUsername } from "../config"
4
 
5
- export async function getIndex({
6
  status,
7
  renewCache = true,
8
  neverThrow = true,
@@ -22,8 +22,8 @@ export async function getIndex({
22
  const jsonResponse = await response?.json()
23
 
24
  if (
25
- typeof jsonResponse === "undefined" &&
26
- typeof jsonResponse !== "object" &&
27
  Array.isArray(jsonResponse) ||
28
  jsonResponse === null) {
29
  throw new Error("index is not an object, admin repair needed")
 
2
 
3
  import { adminUsername } from "../config"
4
 
5
+ export async function getVideoIndex({
6
  status,
7
  renewCache = true,
8
  neverThrow = true,
 
22
  const jsonResponse = await response?.json()
23
 
24
  if (
25
+ typeof jsonResponse === "undefined" ||
26
+ typeof jsonResponse !== "object" ||
27
  Array.isArray(jsonResponse) ||
28
  jsonResponse === null) {
29
  throw new Error("index is not an object, admin repair needed")
src/app/server/actions/ai-tube-hf/getVideos.ts CHANGED
@@ -2,7 +2,7 @@
2
 
3
  import { VideoInfo } from "@/types"
4
 
5
- import { getIndex } from "./getIndex"
6
 
7
  const HARD_LIMIT = 100
8
 
@@ -31,7 +31,7 @@ export async function getVideos({
31
  }): Promise<VideoInfo[]> {
32
  // the index is gonna grow more and more,
33
  // but in the future we will use some DB eg. Prisma or sqlite
34
- const published = await getIndex({
35
  status: "published",
36
  renewCache: true
37
  })
 
2
 
3
  import { VideoInfo } from "@/types"
4
 
5
+ import { getVideoIndex } from "./getVideoIndex"
6
 
7
  const HARD_LIMIT = 100
8
 
 
31
  }): Promise<VideoInfo[]> {
32
  // the index is gonna grow more and more,
33
  // but in the future we will use some DB eg. Prisma or sqlite
34
+ const published = await getVideoIndex({
35
  status: "published",
36
  renewCache: true
37
  })
src/app/views/user-account-view/index.tsx CHANGED
@@ -5,12 +5,12 @@ 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/ai-tube-hf/getChannels"
9
  import { ChannelList } from "@/app/interface/channel-list"
10
  import { localStorageKeys } from "@/app/state/localStorageKeys"
11
  import { defaultSettings } from "@/app/state/defaultSettings"
12
  import { Input } from "@/components/ui/input"
13
- import { ChannelInfo } from "@/types"
 
14
 
15
  export function UserAccountView() {
16
  const [_isPending, startTransition] = useTransition()
@@ -29,7 +29,7 @@ export function UserAccountView() {
29
  if (!isLoaded) {
30
  startTransition(async () => {
31
  try {
32
- const channels = await getChannels({
33
  apiKey: huggingfaceApiKey,
34
  renewCache: true,
35
  })
 
5
 
6
  import { useStore } from "@/app/state/useStore"
7
  import { cn } from "@/lib/utils"
 
8
  import { ChannelList } from "@/app/interface/channel-list"
9
  import { localStorageKeys } from "@/app/state/localStorageKeys"
10
  import { defaultSettings } from "@/app/state/defaultSettings"
11
  import { Input } from "@/components/ui/input"
12
+
13
+ import { getPrivateChannels } from "@/app/server/actions/ai-tube-hf/getPrivateChannels"
14
 
15
  export function UserAccountView() {
16
  const [_isPending, startTransition] = useTransition()
 
29
  if (!isLoaded) {
30
  startTransition(async () => {
31
  try {
32
+ const channels = await getPrivateChannels({
33
  apiKey: huggingfaceApiKey,
34
  renewCache: true,
35
  })