tracker-script

This commit is contained in:
tonyrewin 2022-11-14 09:28:46 +03:00
parent 42489bb297
commit 10e6938b11
6 changed files with 182 additions and 10 deletions

View File

@ -0,0 +1,74 @@
import { createMemo, createSignal, onMount } from 'solid-js'
import { For } from 'solid-js/web'
import type { Shout } from '../../graphql/types.gen'
import { drawAudio } from '../../utils/soundwave'
type MediaItem = any
export default (props: { shout: Shout }) => {
const media = createMemo<any[]>(() => {
if (props.shout.media) {
console.debug(props.shout.media)
return [...JSON.parse(props.shout.media)]
}
return []
})
const [currentTrack, setCurrentTrack] = createSignal(media()[0])
const [paused, setPaused] = createSignal(true)
const togglePlayPause = () => setPaused(!paused())
const playMedia = (m: MediaItem) => {}
const [audioContext, setAudioContext] = createSignal<AudioContext>()
const currentTimer = createMemo(() => {
// TODO: return current audio player track position
return 1
})
onMount(() => {
const actx = new AudioContext()
setAudioContext(actx)
drawAudio(actx, currentTrack().src)
})
const SoundWave = () => <canvas></canvas>
return (
<div class="audio-container">
<div class="audio-img">
<img
class="ligthbox-img lazyload zoom-in"
width="320"
height="320"
alt={props.shout.title}
title={props.shout.title}
src={props.shout.cover}
/>
</div>
<div class="audio-player-list">
<div class="player ng-scope">
<div class="player-title ng-binding ng-scope">{currentTrack().title}</div>
<i class="fas fa-pause fa-3x fa-fw ng-scope" onClick={togglePlayPause} style=""></i>
<div class="player-progress">
<SoundWave />
<span class="position ng-binding">{currentTimer() / currentTrack().length}</span>
</div>
</div>
<ul
class="other-songs ng-scope is-playing"
ng-class="{ 'is-playing': post._id === $root.currentMusicPostId }"
style=""
>
<For each={media()}>
{(m: MediaItem) => (
<li ng-repeat="mediaItem in post.media" class="ng-scope">
<div class="player-status">
<i class="fas fa-play fa-fw ng-scope" onClick={() => playMedia(m)}></i>
</div>
<span class="track-title ng-binding">{m.title}</span>
</li>
)}
</For>
</ul>
</div>
</div>
)
}

View File

@ -65,8 +65,20 @@ export const FullArticle = (props: ArticleProps) => {
})
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = 'https://ackee.discours.io/tracker.js'
script.setAttribute('data-ackee-domain-id', 'dfaf6bc4-163c-4885-9d8b-1e72f7064d42')
script.setAttribute('data-ackee-server', 'https://ackee.discours.io')
script.async = true
document.head.appendChild(script)
})
//<script async
// src="https://ackee.discours.io/tracker.js"
// data-ackee-server="https://ackee.discours.io"
// data-ackee-domain-id="dfaf6bc4-163c-4885-9d8b-1e72f7064d42"
return (
<div class="shout wide-container">
<article class="col-md-6 shift-content">

View File

@ -1,9 +0,0 @@
export default (props: { src?: string; cover?: string; title?: string }) => {
// TODO: styling
return (
<div class="audio-track">
<audio src={props.src} controls={true} />
<span class="audio-title">{props.title || ''}</span>
</div>
)
}

View File

@ -33,6 +33,8 @@ export default gql`
createdAt
updatedAt
publishedAt
visibility
media
stat {
_id: viewed
viewed

View File

@ -35,10 +35,10 @@ export type Author = {
}
export type AuthorStat = {
commented?: Maybe<Scalars['Int']>
followers?: Maybe<Scalars['Int']>
followings?: Maybe<Scalars['Int']>
rating?: Maybe<Scalars['Int']>
commented?: Maybe<Scalars['Int']>
}
export type Chat = {
@ -730,6 +730,7 @@ export type Shout = {
lang?: Maybe<Scalars['String']>
layout?: Maybe<Scalars['String']>
mainTopic?: Maybe<Scalars['String']>
media?: Maybe<Scalars['String']>
publishedAt?: Maybe<Scalars['DateTime']>
publishedBy?: Maybe<User>
slug: Scalars['String']

92
src/utils/soundwave.ts Normal file
View File

@ -0,0 +1,92 @@
/**
* A utility function for drawing our line segments
* @param {AudioContext} ctx the audio context
* @param {number} x the x coordinate of the beginning of the line segment
* @param {number} height the desired height of the line segment
* @param {number} width the desired width of the line segment
* @param {boolean} isEven whether or not the segmented is even-numbered
*/
export const drawLineSegment = (ctx, x, height, width, isEven) => {
ctx.lineWidth = 1 // how thick the line is
ctx.strokeStyle = '#fff' // what color our line is
ctx.beginPath()
height = isEven ? height : -height
ctx.moveTo(x, 0)
ctx.lineTo(x, height)
ctx.arc(x + width / 2, height, width / 2, Math.PI, 0, isEven)
ctx.lineTo(x + width, 0)
ctx.stroke()
}
/**
* Draws the audio file into a canvas element.
* @param {Array} normalizedData The filtered array returned from filterData()
* @returns {Array} a normalized array of data
*/
export const draw = (normalizedData) => {
// set up the canvas
const canvas = document.querySelector('canvas')
const dpr = window.devicePixelRatio || 1
const padding = 20
canvas.width = canvas.offsetWidth * dpr
canvas.height = (canvas.offsetHeight + padding * 2) * dpr
const ctx = canvas.getContext('2d')
ctx.scale(dpr, dpr)
ctx.translate(0, canvas.offsetHeight / 2 + padding) // set Y = 0 to be in the middle of the canvas
// draw the line segments
const width = canvas.offsetWidth / normalizedData.length
for (let i = 0; i < normalizedData.length; i++) {
const x = width * i
let height = normalizedData[i] * canvas.offsetHeight - padding
if (height < 0) {
height = 0
} else if (height > canvas.offsetHeight / 2) {
height = height - canvas.offsetHeight / 2
}
drawLineSegment(ctx, x, height, width, (i + 1) % 2)
}
}
/**
* Filters the AudioBuffer retrieved from an external source
* @param {AudioBuffer} audioBuffer the AudioBuffer from drawAudio()
* @returns {Array} an array of floating point numbers
*/
export const filterData = (audioBuffer) => {
const rawData = audioBuffer.getChannelData(0) // We only need to work with one channel of data
const samples = 70 // Number of samples we want to have in our final data set
const blockSize = Math.floor(rawData.length / samples) // the number of samples in each subdivision
const filteredData = []
for (let i = 0; i < samples; i++) {
let blockStart = blockSize * i // the location of the first sample in the block
let sum = 0
for (let j = 0; j < blockSize; j++) {
sum = sum + Math.abs(rawData[blockStart + j]) // find the sum of all the samples in the block
}
filteredData.push(sum / blockSize) // divide the sum by the block size to get the average
}
return filteredData
}
/**
* Normalizes the audio data to make a cleaner illustration
* @param {Array} filteredData the data from filterData()
* @returns {Array} an normalized array of floating point numbers
*/
export const normalizeData = (filteredData) => {
const multiplier = Math.pow(Math.max(...filteredData), -1)
return filteredData.map((n) => n * multiplier)
}
/**
* Retrieves audio from an external source, the initializes the drawing function
* @param {AudioContext} audioContext the audio context
* @param {String} url the url of the audio we'd like to fetch
*/
export const drawAudio = (audioContext, url) => {
fetch(url)
.then((response) => response.arrayBuffer())
.then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer))
.then((audioBuffer) => draw(normalizeData(filterData(audioBuffer))))
}