-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added asset owner validation before register ownership, added s…
…ubtitles handle to videos
- Loading branch information
Showing
12 changed files
with
460 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// REACT IMPORTS | ||
import { useState, useCallback } from 'react'; | ||
|
||
// VIEM IMPORTS | ||
import { Address } from 'viem'; | ||
import { publicClient } from '@src/clients/viem/publicClient'; | ||
|
||
// LOCAL IMPORTS | ||
import AssetOwnershipAbi from '@src/config/abi/AssetOwnership.json'; | ||
import { GLOBAL_CONSTANTS } from '@src/config-global.ts'; | ||
|
||
// ---------------------------------------------------------------------- | ||
|
||
/** | ||
* Interface for handling errors within the hook. | ||
*/ | ||
interface GetAssetOwnerError { | ||
message: string; | ||
code?: number; | ||
[key: string]: any; | ||
} | ||
|
||
/** | ||
* Interface defining the structure of the hook's return value. | ||
*/ | ||
interface UseGetAssetOwnerHook { | ||
ownerAddress?: Address; | ||
loading: boolean; | ||
error?: GetAssetOwnerError | null; | ||
fetchOwnerAddress: (assetIdHex: string) => Promise<Address | undefined>; | ||
} | ||
|
||
/** | ||
* Hook to retrieve the owner's address of a specific asset. | ||
* | ||
* @returns {UseGetAssetOwnerHook} An object containing the owner's address, loading state, error information, and a function to fetch the owner's address. | ||
*/ | ||
export const useGetAssetOwner = (): UseGetAssetOwnerHook => { | ||
// State variables | ||
const [ownerAddress, setOwnerAddress] = useState<Address | undefined>(undefined); | ||
const [loading, setLoading] = useState<boolean>(false); | ||
const [error, setError] = useState<GetAssetOwnerError | null>(null); | ||
|
||
/** | ||
* Converts a hexadecimal asset ID to decimal. | ||
* | ||
* @param hexId - The asset ID in hexadecimal format. | ||
* @returns The asset ID in decimal format as a string. | ||
*/ | ||
const convertHexToDecimal = (hexId: string): string => { | ||
// Remove '0x' prefix if present | ||
const cleanHex = hexId.startsWith('0x') ? hexId.slice(2) : hexId; | ||
return BigInt(`0x${cleanHex}`).toString(10); | ||
}; | ||
|
||
/** | ||
* Fetches the owner's address of the asset. | ||
* | ||
* @param assetIdHex - The asset ID in hexadecimal format. | ||
* @returns {Promise<Address | undefined>} The owner's address if successful, otherwise undefined. | ||
*/ | ||
const fetchOwnerAddress = useCallback(async (assetIdHex: string): Promise<Address | undefined> => { | ||
if (!assetIdHex) { | ||
setError({ message: 'Asset ID is missing.' }); | ||
return undefined; | ||
} | ||
|
||
setLoading(true); | ||
setError(null); | ||
|
||
try { | ||
// Convert assetId from hexadecimal to decimal | ||
const assetIdDecimal = convertHexToDecimal(assetIdHex); | ||
|
||
// Call the 'ownerOf' function on the AssetOwnership contract | ||
const owner: any = await publicClient.readContract({ | ||
address: GLOBAL_CONSTANTS.ASSET_OWNERSHIP_ADDRESS, | ||
abi: AssetOwnershipAbi.abi, | ||
functionName: 'ownerOf', | ||
args: [assetIdDecimal], | ||
}); | ||
|
||
setOwnerAddress(owner); | ||
setError(null); | ||
return owner; | ||
} catch (err: any) { | ||
console.error('USE GET ASSET OWNER ERROR:', err); | ||
setOwnerAddress(undefined); | ||
setError({ | ||
message: err?.message || 'An error occurred while retrieving the asset owner.', | ||
}); | ||
return undefined; | ||
} finally { | ||
setLoading(false); | ||
} | ||
}, []); | ||
|
||
return { | ||
ownerAddress, | ||
loading, | ||
error, | ||
fetchOwnerAddress, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { useCallback, useState } from 'react'; | ||
import useMetadata from '@src/hooks/use-metadata'; | ||
|
||
/** | ||
* Represents a subtitle track configuration for media players. | ||
*/ | ||
export interface SubtitleTrack { | ||
src: string; | ||
label: string; | ||
language: string; | ||
kind: 'subtitles'; | ||
default: boolean; | ||
} | ||
|
||
/** | ||
* Return type of the useGetSubtitles hook | ||
*/ | ||
export interface UseGetSubtitlesReturn { | ||
/** Array of formatted subtitle tracks */ | ||
tracks: SubtitleTrack[]; | ||
/** Loading state indicator */ | ||
loading: boolean; | ||
/** Function to fetch subtitles by CID */ | ||
getSubtitles: (cid: string) => Promise<SubtitleTrack[]>; | ||
} | ||
|
||
/** | ||
* Maps common language names to standard language codes | ||
*/ | ||
const getLanguageCode = (label: string): string => { | ||
const normalized = label.trim().toLowerCase(); | ||
switch (normalized) { | ||
case 'english': | ||
return 'en-US'; | ||
case 'spanish': | ||
return 'es-ES'; | ||
case 'french': | ||
return 'fr-FR'; | ||
case 'german': | ||
return 'de-DE'; | ||
case 'portuguese': | ||
return 'pt-BR'; | ||
default: | ||
return 'en-US'; // Fallback to English | ||
} | ||
}; | ||
|
||
/** | ||
* Custom hook to fetch and format subtitle tracks for media players | ||
*/ | ||
const useGetSubtitles = (): UseGetSubtitlesReturn => { | ||
const { getMetadata } = useMetadata(); | ||
const [tracks, setTracks] = useState<SubtitleTrack[]>([]); | ||
const [loading, setLoading] = useState(false); | ||
|
||
/** | ||
* Fetches and processes subtitles for a given CID | ||
* @param cid Content identifier for the media | ||
* @returns Formatted subtitle tracks | ||
*/ | ||
const getSubtitles = useCallback(async (cid: string): Promise<SubtitleTrack[]> => { | ||
setLoading(true); | ||
try { | ||
const metadata = await getMetadata(cid); | ||
const subtitleAttachments = metadata.Data.attachments.filter( | ||
(attachment) => attachment.type === 'text/vtt' | ||
); | ||
|
||
let hasDefault = false; | ||
const processedTracks = subtitleAttachments.map((attachment) => { | ||
const isEnglish = attachment.title.toLowerCase() === 'english'; | ||
const language = getLanguageCode(attachment.title); | ||
|
||
const track: SubtitleTrack = { | ||
src: `https://g.watchit.movie/content/${attachment.cid}/`, | ||
label: attachment.title, | ||
language, | ||
kind: 'subtitles', | ||
default: !hasDefault && isEnglish | ||
}; | ||
|
||
if (track.default) hasDefault = true; | ||
return track; | ||
}); | ||
|
||
// Fallback to first track if no English found | ||
if (!hasDefault && processedTracks.length > 0) { | ||
processedTracks[0].default = true; | ||
} | ||
|
||
setTracks(processedTracks); | ||
return processedTracks; | ||
} catch (error) { | ||
setTracks([]); | ||
return []; | ||
} finally { | ||
setLoading(false); | ||
} | ||
}, [getMetadata]); | ||
|
||
return { tracks, loading, getSubtitles }; | ||
}; | ||
|
||
export default useGetSubtitles; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { useState, useCallback } from 'react'; | ||
|
||
/** | ||
* Interface representing each attachment in the metadata. | ||
*/ | ||
export interface Attachment { | ||
cid: string; | ||
type: string; | ||
title: string; | ||
description: string; | ||
} | ||
|
||
/** | ||
* Interface representing the Data object within the metadata response. | ||
*/ | ||
export interface MetadataData { | ||
title: string; | ||
description: string; | ||
attachments: Attachment[]; | ||
custom_fields: any | null; | ||
} | ||
|
||
/** | ||
* Interface representing the overall metadata response structure. | ||
*/ | ||
export interface Metadata { | ||
Type: string; | ||
Data: MetadataData; | ||
} | ||
|
||
/** | ||
* Interface representing the return value of the useMetadata hook. | ||
*/ | ||
export interface UseMetadataReturn { | ||
metadata: Metadata | null; | ||
loading: boolean; | ||
getMetadata: (cid: string) => Promise<Metadata>; | ||
} | ||
|
||
/** | ||
* Custom React hook to fetch metadata from a specific URL using a CID. | ||
* | ||
* @returns {UseMetadataReturn} An object containing: | ||
* - metadata: The fetched metadata or null. | ||
* - loading: A boolean indicating the loading state. | ||
* - getMetadata: An asynchronous function to fetch the metadata. | ||
*/ | ||
const useMetadata = (): UseMetadataReturn => { | ||
const [metadata, setMetadata] = useState<Metadata | null>(null); | ||
const [loading, setLoading] = useState<boolean>(false); | ||
|
||
/** | ||
* Asynchronous function to fetch metadata using the provided CID. | ||
* | ||
* @param {string} cid - The CID used to construct the request URL. | ||
* @returns {Promise<Metadata>} The fetched metadata. | ||
* @throws Will throw an error if the fetch operation fails. | ||
*/ | ||
const getMetadata = useCallback(async (cid: string): Promise<Metadata> => { | ||
setLoading(true); | ||
try { | ||
const response = await fetch(`https://g.watchit.movie/metadata/${cid}/`); | ||
|
||
if (!response.ok) { | ||
throw new Error(`Error fetching metadata: ${response.statusText}`); | ||
} | ||
|
||
const data: Metadata = await response.json(); | ||
setMetadata(data); | ||
return data; | ||
} catch (error) { | ||
console.error(error); | ||
setMetadata(null); | ||
throw error; | ||
} finally { | ||
setLoading(false); | ||
} | ||
}, []); | ||
|
||
return { metadata, loading, getMetadata }; | ||
}; | ||
|
||
export default useMetadata; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.