Skip to content

Releases: kiko-g/finishershub

v3.0

26 Mar 02:58
Compare
Choose a tag to compare

This release is a checkpoint before metadata is added to the website. We implemented a stable version with multiple sources of videos for both MW games:

  • Filter by game
  • Share video URL
  • Individual video page view
  • Youtube and Twitter promo pages
  • UI revamp

2.1.0

10 Jan 16:36
e694975
Compare
Choose a tag to compare

Release 2 optimizations

  • About page
  • 404 page
  • Registry backend host migration
  • UI optimizations
  • Gatsby 5 upgrade

2.0.0

24 Jul 23:58
Compare
Choose a tag to compare

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

16 Apr 02:37
Compare
Choose a tag to compare

Shuffle video order when local storage is available. This is a minor code fix but still a breaking change to the UX.

if (isStorageValid(48)) {
  setVideos(shuffle(JSON.parse(localStorage.getItem('finishershub.videos'))))
}

v.1.2.0

16 Apr 01:59
Compare
Choose a tag to compare

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

15 Apr 17:26
010743f
Compare
Choose a tag to compare

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

15 Apr 16:15
a08d867
Compare
Choose a tag to compare

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)

14 Apr 16:38
Compare
Choose a tag to compare

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.