-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: improve ipfs media download when file its in folder #7752
base: develop
Are you sure you want to change the base?
Changes from all commits
8665596
885abae
10d6735
2edca0e
14db8a8
0e5b108
085399e
a073190
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export function getIPFSHash(url?: string): string | undefined { | ||
const ipfsPrefix = 'https://ipfs.io' | ||
|
||
if (url?.includes(ipfsPrefix)) { | ||
return url.slice(ipfsPrefix.length) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method now returns url with first 4 characters sliced ex. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be better to use the
and we want to get the const ipfsUrl = new URL("https://cloudflare-ipfs.com/ipfs/bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/wiki/Vincent_van_Gogh.html")
const path = ipfsUrl.pathname
return path.replace("/ipfs/", "").split("/")[0] |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
interface IIpfsLink { | ||
Hash: string | ||
Name: string | ||
Size: number | ||
Target: string | ||
Type: number | ||
Mode?: string | ||
Mtime?: number | ||
MtimeNsecs?: number | ||
} | ||
|
||
interface IIPfsEntry { | ||
readonly type: 'dir' | 'file' | ||
readonly cid: string | ||
readonly name: string | ||
readonly path: string | ||
mode?: number | ||
mtime?: { | ||
secs: number | ||
nsecs?: number | ||
} | ||
size: number | ||
} | ||
|
||
interface IIpfsObject { | ||
Hash: string | ||
Links: IIpfsLink[] | ||
} | ||
|
||
enum typeOfLink { | ||
Dir = 'dir', | ||
File = 'file', | ||
} | ||
|
||
const IPFS_ENDPOINT = 'https://ipfs.io' | ||
const IPFS_PATH = '/api/v0/ls' | ||
const IPFS_PREFIX = '/ipfs/' | ||
|
||
export async function getIpfsUri(link: { path?: string; hash: string }): Promise<string | undefined> { | ||
let ipfsLink = `${link.hash}${link.path ?? ''}` | ||
try { | ||
const ipfsEntry = await ls(ipfsLink) | ||
|
||
if (ipfsEntry) { | ||
if (ipfsEntry.type === 'dir') { | ||
const path = `${link.path ?? ''}/${ipfsEntry.name}` | ||
return await getIpfsUri({ hash: link.hash, path }) | ||
} | ||
ipfsLink = `${ipfsLink}/${encodeURIComponent(ipfsEntry.name)}` | ||
} | ||
} catch (error) { | ||
console.error('error', error) | ||
} | ||
|
||
return `${IPFS_ENDPOINT}${ipfsLink}` | ||
} | ||
|
||
async function ls(path: string): Promise<IIPfsEntry | undefined> { | ||
let ipfsEntry: IIPfsEntry | undefined | ||
|
||
try { | ||
const baseUrl = IPFS_ENDPOINT | ||
const method = 'get' | ||
const payload = undefined | ||
let headers = {} | ||
const timeout = undefined | ||
|
||
headers ??= {} | ||
|
||
let controller: AbortController | undefined | ||
let timerId: NodeJS.Timeout | undefined | ||
|
||
if (timeout !== undefined) { | ||
controller = new AbortController() | ||
timerId = setTimeout(() => { | ||
if (controller) { | ||
controller.abort() | ||
} | ||
}, timeout) | ||
} | ||
|
||
try { | ||
if (path.includes('ipfs')) { | ||
const response = await fetch(`${baseUrl}${IPFS_PATH}?arg=/${path}`, { | ||
method, | ||
headers, | ||
body: payload ? JSON.stringify(payload) : undefined, | ||
signal: controller ? controller.signal : undefined, | ||
}) | ||
const lsResponse = (await response.json()) as { Objects: IIpfsObject[] } | ||
const result = lsResponse.Objects[0] | ||
if (result) { | ||
const links = result.Links | ||
if (links.length > 0) { | ||
ipfsEntry = mapLinkToIpfsEntry(links[0], path) | ||
} | ||
} | ||
} | ||
} catch (error) { | ||
console.error('error', error) | ||
} finally { | ||
if (timerId) { | ||
clearTimeout(timerId) | ||
} | ||
} | ||
} catch (error) { | ||
console.error('error', error) | ||
} | ||
|
||
return ipfsEntry | ||
} | ||
|
||
function mapLinkToIpfsEntry(link: IIpfsLink, path: string): IIPfsEntry { | ||
const hash = link.Hash.startsWith(IPFS_PREFIX) ? link.Hash.slice(IPFS_PREFIX.length) : link.Hash | ||
const entry: IIPfsEntry = { | ||
name: link.Name, | ||
path: path + (link.Name ? `/${link.Name}` : ''), | ||
size: link.Size, | ||
cid: hash, | ||
type: typeOf(link), | ||
} | ||
if (link.Mode) { | ||
entry.mode = Number.parseInt(link.Mode, 8) | ||
} | ||
|
||
if (link.Mtime !== undefined && link.Mtime !== null) { | ||
entry.mtime = { | ||
secs: link.Mtime, | ||
} | ||
|
||
if (link.MtimeNsecs !== undefined && link.MtimeNsecs !== null) { | ||
entry.mtime.nsecs = link.MtimeNsecs | ||
} | ||
} | ||
|
||
return entry | ||
} | ||
|
||
function typeOf(link: IIpfsLink): typeOfLink { | ||
switch (link.Type) { | ||
case 1: | ||
case 5: { | ||
return typeOfLink.Dir | ||
} | ||
case 2: { | ||
return typeOfLink.File | ||
} | ||
default: { | ||
return typeOfLink.File | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are other gateways apart from ipfs.io, this isn't very robust imo