Releases: kiko-g/finishershub
v3.0
2.1.0
2.0.0
Registry
This release brings a new feature to FinishersHub: the finishers club registry, which allows Finishers Club members to manage their stats in a centralized, practical and relatively safe way.
v1.2.1
v.1.2.0
Local storage is now used so that the user doesn't have to request the videos every time the page is loaded.
Key changes in the pages/index.tsx
file:
const requestLoadAll = () => {
if (isStorageValid(48)) {
setVideos(JSON.parse(localStorage.getItem('finishershub.videos')))
} else {
api.getAllClips((allEmbedUrls: string[]) => {
const shuffledVideos = shuffle(allEmbedUrls)
setVideos(shuffledVideos)
writeVideosStorage(shuffledVideos)
})
}
}
useEffect(() => requestLoadAll(), [])
isStorageValid(hours elapsed: number)
is an auxiliary function that determines if the local storage values are valid. In this version, the clips are re-requested if the storage is empty or if the values were written more than 48 hours ago. This is done inside utils/storage.ts
const isStorageValid = (hoursElapsed: number) => {
const storedVideos = JSON.parse(localStorage.getItem('finishershub.videos'))
const storedSavedTime = new Date(JSON.parse(localStorage.getItem('finishershub.videos-fetch-date'))).getTime()
const expiredStorage = Math.abs(new Date().getTime() - storedSavedTime) / 36e5 > hoursElapsed
if (storedVideos === null || storedSavedTime === null || expiredStorage) return false
else return true
}
const writeVideosStorage = (videos: string[]) => {
localStorage.setItem('finishershub.videos', JSON.stringify(videos))
localStorage.setItem('finishershub.videos-fetch-date', JSON.stringify(new Date()))
}
v1.1.0
All clips fetched at once and then shuffled. This causes some overhead but it is ultimately worth it for the UX.
Fetching all URLs in the api/twitch.ts
file:
const getAllClips = (callback: Function) => {
let videos: string[] = []
const url = `clips?broadcaster_id=${process.env.GATSBY_TWITCH_BROADCASTER_ID}&first=${TWITCH_API_MAX_CLIPS}`
twitchApiRequest(url, (response: ClipsResponse) => {
let embed_urls = response.data.map(({ embed_url }) => embed_url)
let cursor = response.pagination.cursor
videos = videos.concat(embed_urls)
paginationCycle(videos, url, cursor, callback)
})
}
const paginationCycle = (videos: string[], url: string, cursor: string, callback: Function) => {
if (cursor) {
twitchApiRequest(`${url}&after=${cursor}`, (res: ClipsResponse) => {
let embed_urls = res.data.map(({ embed_url }) => embed_url)
let newCursor = res.pagination.cursor
videos = videos.concat(embed_urls)
paginationCycle(videos, url, newCursor, callback)
})
} else callback(videos)
}
v1.0.1
API responds with curated parameters instead of just a ClipsResponse
. This removes API logic from the frontend (pages).
Important change to api/twitch.ts
:
twitchApiRequest(url, (response: ClipsResponse) =>
callback(
response.pagination.cursor,
response.data.map(({ embed_url }) => embed_url)
)
)
Important change to pages/index.tsx
:
const requestLoad = () => {
api.getClips((cursor: string, embedUrls: string[]) => {
setCursor(cursor)
setVideos(embedUrls)
}, paginationQuantity)
}
const requestLoadMore = () => {
api.getMoreClips(
(cursor: string, embedUrls: string[]) => {
setCursor(cursor)
setVideos([...videos.concat(embedUrls)])
},
paginationQuantity,
cursor
)
}
v1.0.0 (Twitch API)
Finishers Hub's first release is done using the Twitch API for clips. As this service does not grant clip sorting the usability for the Finishers Hub is somewhat limited, due to the amount of processing needed to work around the insufficiencies of the clips API service.
Using the service described above we are forced to limit only the broadcaster_id
, leaving the game id filtering to the frontend. There is no sorting ability and the clips are always fetched in descending order by total views. The only way I found to play around with this restriction was to define started_at
and ended_at
values, which allow you to define the period of time that the clips were created in RFC3339 date-time format. Using an offset to the upper bound (started_at
) generated with randomness to make the request results not completely static. Below is an example of how the clips are requested. To see more about take a look at the src/api/twitch.ts
file.
const getClips = (callback: Function, paginationQuantity: any) => {
const today = new Date()
const debut = new Date('2021, 5, 1')
const minimum = new Date('2021, 7, 20')
const start = debut
const end = today
const offset = randomBetween(0, Math.round(daysDifference(debut, minimum)))
start.setDate(start.getDate() + offset)
const url =
'clips' +
('?broadcaster_id=' + process.env.GATSBY_TWITCH_BROADCASTER_ID) +
('&first=' + (paginationQuantity + 3)) +
('&started_at=' + start.toISOString()) +
('&ended_at=' + end.toISOString())
twitchApiRequest(url, callback)
}
There may be some intermediate releases where the date interval is better, but for release 1.x.x Twitch API will remain as the mean of retrieving clips. Further major releases include plans of creating an S3 bucket containing videos, managing them in an Admin page, and querying them with a graphql layer. This would be much better, since not only would Twitch not be the content provider, but we would also have more control over the clips.