Skip to content

Quantity Switching

When your video is in the .m3u8 streaming format, you can use hls.js to switch the quality directly on the client side. However, for the .mp4 container format, you need to prepare multiple video sources of different qualities for users to switch between.

Tip

Typically, after a user uploads a high-definition video to the website's server, tools like FFmpeg can be used to re-encode and generate videos of different qualities and bitrates.

For example, here are three different quality versions of a video that I have prepared:

test-video_1080p.mp4
test-video_720p.mp4
test-video_480p.mp4

Then, you can implement this by creating a custom controller item:

js
import Player from 'qier-player'

const quantity = {
  el: document.createElement('div'),
  init(player) {
    const quantities = [
      {
        id: '1080p',
        label: '1080p HD',
        checked: true,
        dom: null
      },
      {
        id: '720p',
        label: '720p Clear',
        checked: false,
        dom: null
      },
      {
        id: '480p',
        label: '480p Smooth',
        checked: false,
        dom: null
      },
    ]

    this.btn = document.createElement('div')
    this.btn.textContent = quantities[0].label
    this.el.appendChild(this.btn)
    // Filling to prevent loss of popover when cursor leaves the control item
    this.stuffing = document.createElement('div')
    this.stuffing.classList.add('qier-player_controller_quantity_stuffing')
    this.el.appendChild(this.stuffing)

    this.popover = new Popover(this.el)
    this.el.addEventListener('mouseenter', () => {
      this.stuffing.style.display = 'block'
      this.popover.show()
      // This code notifies other popovers to hide immediately
      player.emit(EVENT.POPOVER_SHOW_CHANGE);
    })
    this.el.addEventListener('mouseleave', () => {
      this.stuffing.style.display = 'none'
      this.popover.hide()
    })
    this.el.classList.add('qier-player_controller_quantity')

    const quantityWrapper = document.createElement('div')

    quantities.forEach((item) => {
      const quantityItem = document.createElement('div')
      quantityItem.classList.add('qier-player_controller_quantity_item')
      quantityItem.setAttribute('data-id', item.id)
      quantityItem.innerText = item.label
      item.dom = quantityItem
      quantityWrapper.appendChild(quantityItem)

      quantityItem.addEventListener('click', (e) => {
        e.stopPropagation()
        if (e.target) {
          const targetDom = e.target as HTMLElement
          const id = targetDom.getAttribute('data-id')
          const checkedItem = quantities.find(item => item.id === id)
          if (!checkedItem.checked) {
            quantities.forEach(item => {
              item.checked = false
              item.dom.classList.remove('qier-player_controller_quantity_item--active')
            })
            checkedItem.checked = true
            item.dom.classList.add('qier-player_controller_quantity_item--active')
            this.btn.textContent = item.label
            // Note: Changing the src of the video tag this way is not ideal and has bugs. This will be fixed later with an update method.
            player.video.src = `/test-video_${item.id}.mp4`
          }
        }
      })
    })

    this.popover.panelEl.appendChild(quantityWrapper);
  }
}

const player = new Player({
  controller: {
    progress: ['progress'],
    eles: ['play', 'time', 'spacer', quantity, 'volume', 'settings', 'web-fullscreen', 'fullscreen'],
  },
})

player.mount(document.body)

Style code:

css
.qier-player_controller_quantity {
  position: relative;
  height: 100%;
  padding: 6px;
  color: rgba(255, 255, 255, 0.8);
  cursor: pointer;
  font-size: 14px;
}

.qier-player_controller_quantity_item {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 5px 8px;
  width: 128px;
  position: relative;
}

.qier-player_controller_quantity_item:hover {
  color: var(--theme-color);
  background: rgba(255, 255, 255, 0.2);
}

.qier-player_controller_quantity_item--active {
  color: var(--theme-color);
}

.qier-player_controller_quantity_item--active::before {
  margin-right: 16px;
  margin-bottom: 4px;
  margin-left: 10px;
  opacity: 1;
  content: '';
  display: inline-block;
  width: 5px;
  height: 12px;
  border-right: 1px solid var(--theme-color);
  border-bottom: 1px solid var(--theme-color);
  transform: rotate(45deg);
  position: absolute;
  left: 4px;
}

.qier-player_controller_quantity_stuffing {
  position: absolute;
  bottom: 100%;
  left: 0;
  display: none;
  width: 100%;
  padding: 20px 0;
}

Released under the MIT License.