Gatsby: Bagaimana cara mengisi File Konteks React dengan data MDX dan file aset dari kueri GraphQL?

Dibuat pada 9 Okt 2019  ·  81Komentar  ·  Sumber: gatsbyjs/gatsby

Jadi, saya memiliki pemutar musik yang gigih dalam proyek yang sedang saya kerjakan, terima kasih untuk artikel ini:

https://upmostly.com/tutorials/how-to-use-the-usecontext-hook-in-react?unapproved=15591&moderation-hash=88f22760754aa9ff30643d45bc4c41eb#comment -15591

Dan itu luar biasa. Tetapi informasi untuk musik terdapat di File Konteks, seperti:

const MusicPlayerContext = React.createContext([{}, () => {}]);

const MusicPlayerProvider = (props) => {

  const [state, setState] = useState({
    audioPlayer: new Audio(),
    tracks: [
      {
        name: 'Baktun',
        artist: 'RYKR',
        file: Baktun,
        artwork: BaktunArtwork
      },
      {
        name: 'Bash',
        artist: 'RYKR',
        file: Bash,
        artwork: BashArtwork
      },
      {
        name: 'Frost',
        artist: 'RYKR',
        file: Frost,
        artwork: FrostArtwork
      },
      {
        name: 'Greyskull',
        artist: 'RYKR',
        file: Greyskull,
        artwork: GreyskullArtwork
      },
      {
        name: 'Sprial Up',
        artist: 'RYKR',
        file: SpiralUp,
        artwork: SpiralUpArtwork
      }  
    ],
    currentTrackIndex: null,
    isPlaying: false,
  })

  return (
    <MusicPlayerContext.Provider value={[state, setState]}>
      {props.children}
    </MusicPlayerContext.Provider>
  )

}

export { MusicPlayerContext, MusicPlayerProvider }

... tetapi daripada memasukkan semua informasi ini secara manual ke dalam file konteks melalui objek JS, saya lebih suka menyimpan musik di folder dengan file MDX per lagu dan file karya seni dan audio di sana juga, lalu memasukkan informasi itu ke dalam file Konteks, saya asumsikan melalui GraphQL.

Saya tidak benar-benar tahu bagaimana cara melakukan itu, bagaimanapun, jadi panduan atau contoh apa pun akan bagus, terima kasih :-)

Saat ini saya mengimpor semua file audio dan karya seni secara manual ke dalam file Konteks dan juga memasukkan informasi trek secara manual, yang tidak optimal dan dapat diskalakan.

question or discussion

Komentar yang paling membantu

Bisakah kamu mencobanya

const [state, setState] = useState({
  audioPlayer: {},
  currentTrackIndex: null,
  isPlaying: false,
})

const [currentTime, setCurrentTime] = useState(state.audioPlayer.currentTime)

useEffect(() => {
  setState({
    audioPlayer: new Audio(),
    currentTrackIndex: null,
    isPlaying: false,
  })
}, [])

const formattedTime = getTime(currentTime) || `0:00`
const formattedDuration = getTime(state.audioPlayer.duration) || `0:00`

......

let currentTrackName
let currentTrackArtist
let currentTrackArtwork
let currentTrackAudio

if (state.currentTrackIndex !== null) {
  const { currentTrackIndex } = state
  const currentTrack = trackList[currentTrackIndex]

  const base = currentTrack.audio.base // frost.mp3
  const baseName = basename(base) // frost

  currentTrackName = currentTrack.name
  currentTrackArtist = currentTrack.artist
  currentTrackArtwork = assetObj[`${baseName}/${currentTrack.artwork.base}`]
  currentTrackAudio = assetObj[`${baseName}/${currentTrack.audio.base}`]
}

.......

Semua 81 komentar

Pertama, Anda perlu membuat node mdx dengan gatsby-plugin-mdx .

plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `tracks`,
        path: `${__dirname}/src/tracks/`,
      },
    },
    {
      resolve: `gatsby-plugin-mdx`,
      options: {
        ...
      },
    },
  ]

Kemudian Anda dapat menggunakan useStaticQuery untuk membuat kueri trek di Provider .

const MusicPlayerProvider = (props) => {
  const tracks = useStaticQuery(graphql`
    query Tracks {
      allMdx {
        edges {
          node {
            ...
          }
        }
      }
    }
  `)

  const [state, setState] = useState({
    audioPlayer: new Audio(),
    currentTrackIndex: null,
    isPlaying: false,
  })

  return (
    <MusicPlayerContext.Provider value={[{ tracks, ...state }, setState]}>
      {props.children}
    </MusicPlayerContext.Provider>
  )

}

Hai @universitas ,

Jadi saya telah menambahkan kueri ini, yang merupakan kueri yang sama yang saya gunakan dari contoh Anda yang lain, ke kueri dalam file konteks:

const MusicPlayerContext = React.createContext([{}, () => {}])

const MusicPlayerProvider = (props) => {

  const tracks = useStaticQuery(graphql`
    query Tracks { 
      allMdx(filter: {fileAbsolutePath: {regex: "/content/music/"}}) {
        totalCount
        edges {
          node {
            fields {
              slug
            }
            frontmatter {
              name
              artist
              genre
              bpm
              artwork {
                childImageSharp {
                    fluid(maxWidth: 1000) {
                      ...GatsbyImageSharpFluid
                    }
                  }
              }
              alt
              description
              release(formatString: "MMMM Do, YYYY")
              audio {
                absolutePath
              }
            }
          }
        }
      }
    }
  `)

  const [state, setState] = useState({
    audioPlayer: new Audio(),
    currentTrackIndex: null,
    isPlaying: false,
  })

  return (
    <MusicPlayerContext.Provider value={[{ tracks, ...state }, setState]}>
      {props.children}
    </MusicPlayerContext.Provider>
  )

}

export { MusicPlayerContext, MusicPlayerProvider }

... tetapi tanpa melakukan sesuatu yang berbeda dengan file lain yang mengakses dan menggunakan konteks ini, saya mendapatkan kesalahan ini di browser:

Screen Shot 2019-10-13 at 9 49 40 AM

... jadi saya tidak yakin apa yang harus dilakukan untuk memperbaikinya, tetapi saya juga memiliki pengait khusus bernama useMusicPlayer yang menggunakan konteks ini, seperti:

const useMusicPlayer = () => {

  const [state, setState] = useContext(MusicPlayerContext)

  // Play a specific track
  function playTrack(index) {
    if (index === state.currentTrackIndex) {
      togglePlay()
    } else {
      state.audioPlayer.pause()
      state.audioPlayer = new Audio(state.tracks[index].file)
      state.audioPlayer.play()
      setState(state => ({ ...state, currentTrackIndex: index, isPlaying: true }))
    }
  }

  // Toggle play or pause
  function togglePlay() {
    if (state.isPlaying) {
      state.audioPlayer.pause()
    } else {
      state.audioPlayer.play()
    }
    setState(state => ({ ...state, isPlaying: !state.isPlaying }))
  }

  // Play the previous track in the tracks array
  function playPreviousTrack() {
    const newIndex = ((state.currentTrackIndex + -1) % state.tracks.length + state.tracks.length) % state.tracks.length
    playTrack(newIndex)
  }

  // Play the next track in the tracks array
  function playNextTrack() {
    const newIndex = (state.currentTrackIndex + 1) % state.tracks.length
    playTrack(newIndex)
  }

  // Get the current time of the currently playing track
  function currentTime() {
    if (state.isPlaying) {
      state.audioPlayer.currentTime()
    } 
  }

  return {
    playTrack,
    togglePlay,
    currentTrackName:
      state.currentTrackIndex !== null && state.tracks[state.currentTrackIndex].name,
    currentTrackArtist:
      state.currentTrackIndex !== null && state.tracks[state.currentTrackIndex].artist,
    currentTrackArtwork:
      state.currentTrackIndex !== null && state.tracks[state.currentTrackIndex].artwork,
    currentTime,
    trackList: state.tracks,
    isPlaying: state.isPlaying,
    playPreviousTrack,
    playNextTrack,
  }

}

export default useMusicPlayer

... yang akhirnya saya coba gunakan dalam file TrackList , seperti:

const TrackList = () => {

  const {
    trackList,
    currentTrackName,
    currentTrackArtist,
    currentTrackArtwork,
    playTrack,
    isPlaying
  } = useMusicPlayer()

  return (
    <>
      {trackList.map((track, index) => (
        <Card>
          {/* <Artwork src={track.frontmatter.artwork} alt="Album Artowrk."/> */}
          <Artwork src={currentTrackArtwork} alt="Album Artwork."/> 
          <Button
            whileHover={{ scale: 1.1 }}
            whileTap={{ scale: 0.9 }}
            onClick={() => playTrack(index)}
          >
            {
              currentTrackName === track.frontmatter.name && isPlaying ?
              <img src={PauseButton} />
              :
              <img src={PlayButton} />
            }
          </Button>
          <Text>
            <h1>{currentTrackName}</h1>
            <h3>{currentTrackArtist}</h3>
          </Text>  
        </Card>
      ))}
    </>
  )

}

.... dan semua file ini bekerja dengan baik ketika saya menggunakan info statis yang dikodekan dengan keras ke dalam file konteks, tetapi saya mendapatkan pesan kesalahan itu dari sebelumnya.

Jadi, saya berpikir bahwa mungkin saya perlu menambahkan track atau tracks di suatu tempat, tetapi tidak yakin di mana saya akan melakukannya. Atau mungkin masalahnya ada di tempat lain, tetapi saya tidak yakin apa masalahnya ...

Untuk komponen TrackList Anda, Anda perlu memetakan trackList seperti itu

...
trackList.edges.map((track, index) => {
  const { frontmatter } = track.node
})
...

Pada dasarnya karena Anda membuat kueri menggunakan graphql, struktur data trackList telah berubah dan Anda belum memperbarui <TrackList /> untuk mencerminkannya.

Sama untuk useMusicPlayer hook. Misalnya tracks.edges.length bukan tracks.length , tracks.edges[index].node bukan tracks[index] dll.

Cara lain adalah mengubah struktur data trackList yang baru agar sesuai dengan yang sekarang di MusicPlayerProvider . Dengan begitu Anda tidak perlu mengubah kode file lain.

Tidak yakin saya melewatkan hal lain, tetapi Anda dapat mencobanya terlebih dahulu.

hmmmmmm .... mengalami sedikit masalah dalam mengikuti bagaimana cara mengubah segala sesuatu di banyak file. Saya juga berpikir bahwa akan menyenangkan untuk dapat mengubah trackList menjadi apa yang dibutuhkan untuk bekerja dengan apa yang telah ditetapkan. SO di kepala saya, saya berpikir bahwa salah satu cara untuk melakukannya adalah dengan membuat array dan kemudian mengulang melalui semua data graphql yang telah dikembalikan dari kueri statis ke kunci suatu objek. Tidak benar-benar tahu bagaimana melakukan itu, tho. Jika Anda memiliki ide lain tentang bagaimana saya bisa melakukan ini, saya mendengarkan. Mencoba mencari tahu di pihakku juga.

Berpikir seperti ini, tetapi tidak tahu bagaimana melakukannya:

tracks: [
  // iterate over all of the tracks created via the staticQuery
  {
    name: data.allMdx.edges.node.frontmatter.name,
    artist: data.allMdx.edges.node.frontmatter.artist
  },
  {},
  {}
],

Jadi saya kira saya tidak tahu bagaimana memanipulasi data yang saya dapatkan dari kueri dengan sangat baik, atau bahkan sama sekali. Saya akan berpikir saya perlu menyuntikkan info dari kueri ke dalam array objek yang disebut tracks tetapi saya tidak yakin bagaimana untuk mulai melakukan itu.

Anda dapat melakukannya di MusicPlayerProvider

  const tracks = useStaticQuery(graphql`
    ...
  `)

  // need useMemo to avoid re-computation when state change
  const trackList = useMemo(
    () =>
      tracks.allMdx.edges.map(track => {
        const { frontmatter } = track.node
        const { name, artist } = frontmatter

        return { name, artist }
      }),
    [tracks]
  )

nah, bagaimanapun juga bantuan dari Anda sebagian besar berfungsi! Terima kasih! XD

.... kecuali untuk beberapa hal yang tersisa ...

misalnya, album artwork muncul di TrackList seperti yang diharapkan jika saya menggunakan gatsby-image , yang masuk akal, tetapi saya tidak bisa menampilkannya sebagai yang terbaru lacak karya seni di MusicPlayer .

Beginilah cara saya membuat currentAlbumArtwork di hook useMusicPlayer:

currentTrackArtwork:
  state.currentTrackIndex !== null && state.tracks[state.currentTrackIndex].artwork,

... tetapi saya berpikir karena ini adalah gambar yang menggunakan gatsby-image childImageSharp Saya perlu melakukan sesuatu yang berbeda di sini, saya hanya tidak tahu apa yang perlu diubah. Karena ini di luar JSX, saya tidak yakin bagaimana mengubahnya agar berfungsi dengan baik.

Tapi rintangan terbesar dan terakhir yang saya hadapi adalah membuat file audio benar-benar diputar. Saya tidak tahu apakah saya menanyakannya dengan benar atau bagaimana membuat mereka bekerja dengan cara yang sama seperti mereka bekerja di luar Graphql. Saya menanyakan audio di frontmatter saya, yang telah saya beri label pada bidang di file mdx, seperti:

---
name: Bash
artist: RYKR
genre: Electro
bpm: 124 bpm
artwork: bash.jpg
alt: Bash artwork.
audio: bash.mp3
description: What streamers and confetti sound like if they have souls.
release: 2019-02-01
---

... dan file audio ada di folder yang sama dengan file mdx, dan karya seni juga bekerja menggunakan pendekatan ini, jadi saya tidak yakin apa perbedaannya.

Saya menanyakan absolutePath dari audio , yang merupakan salah satu opsi di explorer, seperti ini:

audio {
  absolutePath
}

... tetapi saya tidak tahu apakah ini benar-benar mendapatkan file audio, daripada lokasi file audio. Mencoba memahami cara bekerja dengan file audio dan video di graphql dan gatsby dan mdx, tetapi masih belum jelas tentang ini.

Ini adalah bagian terakhir untuk membuat ini berfungsi, jadi mudah-mudahan Anda mungkin memiliki wawasan tentang bagaimana melakukan ini, atau ke mana harus mencari tahu caranya.

Untuk itu Anda perlu query path ke audio + image source di folder publik.

// useMusicPlayer.js

const [state, setState] = useContext(MusicPlayerContext)

// query all mp3 and png files from /content/music/
const assets = useStaticQuery(graphql`
  query Assets {
    allFile(filter: {extension: {in: ["mp3", "jpg"]}, absolutePath: {regex: "/content/music/"}}) {
      edges {
        node {
          publicURL
          relativePath
        }
      }
    }
  }
`)

// convert to obj for fast lookup
const assetObj= useMemo(
  () =>
    assets.allFile.edges.reduce((obj, file) => {
      const { publicURL, relativePath } = file.node

      obj[relativePath] = publicURL

      return obj
    }, {}),
  [assets]
)

const artwork = state.tracks[state.currentTrackIndex].artwork  // bash.jpg
const currentTrackArtworkSrc = assetObj[artwork] // /static/bash-[some-hash].jpg

const audio = state.tracks[state.currentTrackIndex].audio // bash.mp3
const currentAudioSrc = assetObj[audio] // /static/bash-[some-hash].mp3

Dan Anda tidak perlu menanyakan absolutePath pada audio , cukup audio saja.

Hai @universse , maaf karena tidak membalas lebih awal, karena pekerjaan sangat berat selama seminggu terakhir.

Jadi, saya melemparkan ini ke dalam aplikasi, dan bagian yang menaikkan aplikasi adalah bagian ini:

const artwork = state.tracks[state.currentTrackIndex].artwork  // bash.jpg
const currentTrackArtworkSrc = assetObj[artwork] // /static/bash-[some-hash].jpg

const audio = state.tracks[state.currentTrackIndex].audio // bash.mp3
const currentAudioSrc = assetObj[audio] // /static/bash-[some-hash].mp3

... yang memberi saya kesalahan berikut:

Screen Shot 2019-10-21 at 11 19 12 PM

... jadi, saya rasa saya tidak mengerti di mana harus menggunakan consts di aplikasi ... Saya lelah ini:

    currentTrackArtwork:
      state.currentTrackIndex !== null && currentTrackArtworkSrc,

... tapi sepertinya tidak ada bedanya. Bagaimana membuat sesuatu seperti currentTrackArtworkSrc digunakan di file-file lainnya? Apa yang harus diganti, jika ada?

Jadi di useMusicPlayer Anda mengalami ini

return {
  playTrack,
  togglePlay,
  currentTrackName:
    state.currentTrackIndex !== null && state.tracks[state.currentTrackIndex].name,
  currentTrackArtist:
    state.currentTrackIndex !== null && state.tracks[state.currentTrackIndex].artist,
  currentTrackArtwork:
    state.currentTrackIndex !== null && state.tracks[state.currentTrackIndex].artwork,
  currentTime,
  trackList: state.tracks,
  isPlaying: state.isPlaying,
  playPreviousTrack,
  playNextTrack,
}

Sekarang menjadi

const assets = useStaticQuery(graphql`
  query Assets {
    allFile(filter: {extension: {in: ["mp3", "jpg"]}, absolutePath: {regex: "/content/music/"}}) {
      edges {
        node {
          publicURL
          relativePath
        }
      }
    }
  }
`)

// convert to obj for fast lookup
const assetObj= useMemo(
  () =>
    assets.allFile.edges.reduce((obj, file) => {
      const { publicURL, relativePath } = file.node

      obj[relativePath] = publicURL

      return obj
    }, {}),
  [assets]
)

return {
  playTrack,
  togglePlay,
  currentTrackName:
    state.currentTrackIndex && state.tracks[state.currentTrackIndex].name,
  currentTrackArtist:
    state.currentTrackIndex && state.tracks[state.currentTrackIndex].artist,
  currentTrackArtwork:
    state.currentTrackIndex && assetObj[state.tracks[state.currentTrackIndex].artwork],
  currentTrackAudio:
    state.currentTrackIndex && assetObj[state.tracks[state.currentTrackIndex].audio],
  currentTime,
  trackList: state.tracks,
  isPlaying: state.isPlaying,
  playPreviousTrack,
  playNextTrack,
}

hmmmm ... masih mengalami masalah yang sama:

Screen Shot 2019-10-22 at 8 11 35 AM

... dan kami masih belum menggunakan konstanta yang kami buat di sini:

  const artwork = state.tracks[state.currentTrackIndex].artwork  // bash.jpg
  const currentTrackArtworkSrc = assetObj[artwork] // /static/bash-[some-hash].jpg

  const audio = state.tracks[state.currentTrackIndex].audio // bash.mp3
  const currentAudioSrc = assetObj[audio] // /static/bash-[some-hash].mp3

... jadi untuk apa currentTrackArtworkSrc dan currentAudioSrc ? Haruskah kita tidak menggunakannya di suatu tempat? Saya mencoba ini tetapi tidak berhasil:

currentTrackArtwork:
  state.currentTrackIndex
  &&
  currentTrackArtworkSrc[state.tracks[state.currentTrackIndex].artwork],
currentTrackAudio:
  state.currentTrackIndex
  &&
  currentAudioSrc[state.tracks[state.currentTrackIndex].audio],

Maukah Anda membagikan repo Anda agar saya bisa melihatnya nanti?

Tapi, tentu saja, terima kasih XD

https://github.com/rchrdnsh/RYKR

Pertama, di folder content/music , tebak Anda dapat dengan aman menghapus folder artwork dan audio .

Berikut kode terakhir dengan beberapa komentar. Saya juga membersihkan kode sedikit.

// MusicPlayerContext
import React, { useState, useMemo } from 'react'
import { useStaticQuery, graphql } from 'gatsby'

const MusicPlayerContext = React.createContext([{}, () => {}])

const MusicPlayerProvider = props => {
  const tracks = useStaticQuery(graphql`
    query Tracks {
      allMdx(filter: { fileAbsolutePath: { regex: "/content/music/" } }) {
        edges {
          node {
            fields {
              slug
            }
            frontmatter {
              name
              artist
              genre
              bpm
              # ADD BASE HERE
              artwork {
                base
                childImageSharp {
                  fluid(maxWidth: 1000) {
                    ...GatsbyImageSharpFluid
                  }
                }
              }
              alt
              description
              release(formatString: "MMMM Do, YYYY")
              audio {
                absolutePath
                base
              }
            }
          }
        }
      }
    }
  `)

  // need useMemo to avoid re-computation when state change
  const trackList = useMemo(
    () =>
      tracks.allMdx.edges.map(track => {
        const { frontmatter } = track.node
        const {
          name,
          artist,
          genre,
          bpm,
          artwork,
          alt,
          description,
          audio,
        } = frontmatter

        return { name, artist, genre, bpm, artwork, alt, description, audio }
      }),
    [tracks]
  )

  const [state, setState] = useState({
    audioPlayer: new Audio(),
    tracks: trackList,
    currentTrackIndex: null,
    isPlaying: false,
  })

  return (
    <MusicPlayerContext.Provider value={[state, setState]}>
      {props.children}
    </MusicPlayerContext.Provider>
  )
}

export { MusicPlayerContext, MusicPlayerProvider }
// useMusicPlayer.js
import { useContext, useMemo } from 'react'
import { useStaticQuery, graphql } from 'gatsby'
import { MusicPlayerContext } from './MusicPlayerContext'

// frost.mp3 -> frost
function basename(name) {
  return name.slice(0, name.lastIndexOf('.'))
}

const useMusicPlayer = () => {
  const [state, setState] = useContext(MusicPlayerContext)

  // query all mp3 and jpg files from /content/music/
  const assets = useStaticQuery(graphql`
    query Assets {
      allFile(
        filter: {
          extension: { in: ["mp3", "jpg"] }
          absolutePath: { regex: "/content/music/" }
        }
      ) {
        edges {
          node {
            publicURL
            relativePath
          }
        }
      }
    }
  `)
  // convert to obj for fast lookup
  const assetObj = useMemo(
    () =>
      assets.allFile.edges.reduce((obj, file) => {
        const { publicURL, relativePath } = file.node

        obj[relativePath] = publicURL

        return obj
      }, {}),
    [assets]
  )

  // Play a specific track
  function playTrack(index) {
    if (index === state.currentTrackIndex) {
      togglePlay()
    } else {
      state.audioPlayer.pause()

      const base = state.tracks[index].audio.base // frost.mp3
      const baseName = basename(base) // frost

      // new Audio() does not support relative path
      // hence the need for window.location.origin
      const audioPlayer = new Audio(
        `${window.location.origin}${assetObj[`${baseName}/${base}`]}`
      ) // new Audio('http://www.domain.com/static/frost-[hash].mp3')

      audioPlayer.play()
      setState(state => ({
        ...state,
        currentTrackIndex: index,
        isPlaying: true,
        audioPlayer,
      }))
    }
  }

  // Toggle play or pause
  function togglePlay() {
    if (state.isPlaying) {
      state.audioPlayer.pause()
    } else {
      state.audioPlayer.play()
    }
    setState(state => ({ ...state, isPlaying: !state.isPlaying }))
  }

  // Play the previous track in the tracks array
  function playPreviousTrack() {
    const newIndex =
      (((state.currentTrackIndex + -1) % state.tracks.length) +
        state.tracks.length) %
      state.tracks.length
    playTrack(newIndex)
  }

  // Play the next track in the tracks array
  function playNextTrack() {
    const newIndex = (state.currentTrackIndex + 1) % state.tracks.length
    playTrack(newIndex)
  }

  // Get the current time of the currently playing track
  function currentTime() {
    if (state.isPlaying) {
      state.audioPlayer.currentTime()
    }
  }

  let currentTrackArtwork, currentTrackAudio

  if (state.currentTrackIndex !== null) {
    const base = state.tracks[state.currentTrackIndex].audio.base // frost.mp3
    const baseName = basename(base) // frost

    currentTrackArtwork =
      assetObj[
        `${baseName}/${state.tracks[state.currentTrackIndex].artwork.base}`
      ] // assetObj['frost/frost.jpg']
    currentTrackAudio =
      assetObj[
        `${baseName}/${state.tracks[state.currentTrackIndex].audio.base}`
      ] // assetObj['frost/frost.mp3']
  }

  return {
    playTrack,
    togglePlay,
    currentTrackName:
      state.currentTrackIndex !== null &&
      state.tracks[state.currentTrackIndex].name,
    currentTrackArtist:
      state.currentTrackIndex !== null &&
      state.tracks[state.currentTrackIndex].artist,
    currentTrackArtwork,
    currentTrackAudio,
    currentTime,
    trackList: state.tracks,
    isPlaying: state.isPlaying,
    playPreviousTrack,
    playNextTrack,
  }
}

export default useMusicPlayer

WOW!!! Kamu luar biasa! Ini semua bekerja dengan sangat baik sekarang, dan saya mulai memahami hal-hal dengan lebih baik saat saya mempelajari kode. Hal-hal seperti regex dan useMemo dan banyak hal lainnya adalah hal baru bagi saya, jadi terima kasih telah menunjukkan hal-hal ini kepada saya dan bagaimana mereka dapat digunakan.

Ini benar-benar XD yang luar biasa !!!!

Apakah Anda punya cara untuk dibayar atau diberi tip atau apa pun? Ini sangat membantu saya :-)

Haha terima kasih tapi itu tidak perlu. Kira Anda bisa membayarnya lain kali :)

Hai @universse!

Saya mencoba untuk menyelesaikan pemutar musik ini, tetapi mengalami masalah ... tidak tahu ke mana harus pergi, jadi saya pikir saya akan bertanya kepada Anda, jika Anda tidak keberatan.

Mencoba membuat bilah kemajuan yang menunjukkan waktu saat ini dalam menit dan detik di sebelah kiri, bilah yang memvisualisasikan durasi trek yang dapat diklik dan / atau diseret oleh pendengar untuk bergerak di trek di tengah, dan total durasi di sisi lain. Ini sangat mirip dengan setiap pemutar musik yang ada, saya bayangkan. Juga ingin penggeser volume, tetapi saya akan menyimpannya untuk nanti.

Ngomong-ngomong, sudah googling dan mencoba banyak hal dan saya sampai di sini:

function getTime(time) {
    if(!isNaN(time)) {
      return Math.floor(time / 60) + ':' + ('0' + Math.floor(time % 60)).slice(-2)
    }
  }

useEffect(() => {
    const currentTime = setInterval(() => {getTime(state.audioPlayer.currentTime)}, 1000)
    return () => clearInterval(currentTime)
  }, []);

... di mana fungsi pertama memformat waktu menjadi menit dan detik dan hook useEffect kedua mencoba memperbarui currentTime setiap detik.

Masalah saya adalah saya mendapatkan currentTime untuk diperbarui, tetapi hanya ketika saya menekan tombol jeda, daripada memperbarui setiap detik. Beberapa dengan durasi, yang dimulai sebagai NaN tetapi pertama kali saya menekan tombol jeda, itu muncul.

Saya merasa saya mungkin sudah dekat, tetapi belum sampai di sana, dan saya tidak dapat melihat apa yang saya lewatkan atau lakukan salah.

Mungkin Anda bisa mencoba ini. Anda perlu memasukkan currentTime ke dalam status komponen dan memperbaruinya.

const [currentTime, setCurrentTime] = useState(state.audioPlayer.currentTime)

useEffect(() => {
  const timeoutId= setInterval(() => {
    setCurrentTime(getTime(state.audioPlayer.currentTime))
  }, 1000)
  return () => clearInterval(timeoutId)
}, [state.audioPlayer]);

Selain itu, saya sarankan Anda menambahkan https://github.com/facebook/react/tree/master/packages/eslint-plugin-react-hooks ke pengaturan Anda untuk menghindari potensi bug.

ya! yang pada dasarnya berhasil! Hanya ada penundaan kedua jika saya mengklik putar di trek baru selama beberapa detik untuk memperbarui ke trek baru, yang menurut saya masuk akal. Saya berpikir mungkin pernyataan bersyarat yang mencari indeks trek untuk berubah, dan jika demikian menghapus status currentTimes secara instan, atau sesuatu di sepanjang garis itu ...

Jadi saya perlu memiliki useState , yang menurut saya dapat saya gunakan untuk membuat bilah kemajuan juga. Saya tidak begitu paham tentang ide useState, tetapi saya berasumsi bahwa saya dapat memiliki useState sebanyak yang diperlukan?

Saya juga akan mengambil linter pengait itu, sekali lagi terima kasih :-)

Anda juga bisa mencobanya

const [currentTime, setCurrentTime] = useState(state.audioPlayer.currentTime)

// both formattedTime and progress are states derived from audioPlayer.currentTime
// so no need another useState
const formattedTime = getTime(currentTime)
const progress = currentTime / state.audioPlayer.duration

useEffect(() => {
  const timeoutId= setInterval(() => {
    setCurrentTime(state.audioPlayer.currentTime)
  }, 1000)
  return () => {
    // clean up function run when state.audioPlayer changes
    // reset currentTime to 0
    setCurrentTime(0)
    clearInterval(timeoutId)
  }
}, [state.audioPlayer]);

ya, itu berfungsi sedikit lebih baik sekarang, kecuali masih ada sedikit jeda antara mengklik tombol putar lagu dan mengatur ulang nomor ke '0:00' ... mungkin saya memerlukan beberapa nilai placeholder default atau sesuatu, tetapi tidak kesepakatan besar...

berikutnya adalah penggeser, satu untuk menampilkan dan mengubah kemajuan lagu, dan satu lagi untuk mengubah volume lagu ... melakukan penelitian sekarang, tetapi jika Anda memiliki pemikiran, atau saran, saya mendengarkan: - )

Untuk slider kemajuan, Anda dapat menggunakan <input type='range' /> , sesuatu di sepanjang baris ini.

<input
  max={state.audioPlayer.duration}
  min='0'
  step='1'
  type='range'
  value={currentTime}
  onChange={e => {
    setCurrentTime(e.target.value)
    state.AudioPlayer.currentTime = e.target.value
  }}
/>

Sedangkan untuk volume, Anda juga dapat menggunakan <input type='range' /> . Anw, alangkah baiknya jika Anda dapat membagikan repo Anda, karena saya tidak yakin bagaimana cara melakukannya.

Inilah reponya:

https://github.com/rchrdnsh/RYKR

mencoba membungkus kepalaku menggunakan status dari useMusicPlayer dalam contoh ini, tetapi masih agak membingungkan bagi saya: - /

hmmmm ... jadi dengan contoh Anda apakah saya akan menambahkan kode ini ke file hook useMusicPlayer ? Sepertinya saya tidak memiliki akses ke state.AudioPlayer.currentTime luarnya, bukan?

Apakah saya akan membuat sebuah fungsi dan menamainya seperti ini ?:

function progressSlider() {
  return (
    <input
      max={state.audioPlayer.duration}
      min='0'
      step='1'
      type='range'
      value={currentTime}
      onChange={e => {
        setCurrentTime(e.target.value)
        state.AudioPlayer.currentTime = e.target.value
      }}
    />
  )
}

... lalu ekspor dari file useMusicPlayer.js dan impor ke file PlayerControls.js ?

Saat ini saya melakukan itu sekarang dan slider muncul tetapi tidak bergerak dengan currentTime, lalu ketika saya mencoba mengklik slider untuk mengubah posisi musik saya mendapatkan kesalahan berikut:

Screen Shot 2019-11-14 at 10 36 31 AM

... yang menurut saya itu berarti saya perlu menyiapkan beberapa manajemen status di file PlayerControls.js ... tidak yakin apakah itu benar, atau bagaimana melakukannya ...

ProgressSlider adalah komponen baru. Itu dapat mengakses state.audioPlayer.currentTime melalui useContext .

function ProgressSlider() {
  const [{ audioPlayer }] =  useContext(MusicPlayerContext)

  return (
    <input
      max={audioPlayer.duration}
      min='0'
      step='1'
      type='range'
      value={currentTime}
      onChange={e => {
        setCurrentTime(e.target.value)
        // not AudioPlayer
        state.audioPlayer.currentTime = e.target.value
      }}
    />
  )
}

hmmmm ... jadi agak berhasil ... beberapa hal, meskipun ...

  1. Tidak dapat mengklik dan menyeret pada kontrol untuk meluncur di sekitar slider.
  2. Ketika saya mengklik pada bagian lain dari slider, currentTime diperbarui ke nomor aneh sebelum memformat ke menit dan detik.
  3. Tidak dapat menemukan cara mengisi waktu yang telah berlalu dengan warna lain (meskipun tampaknya ini hampir tidak mungkin di webkit / safari, jadi tidak terlalu penting)

Berikut adalah gif yang menunjukkan hal nomor, dan juga 'jempol' yang tidak dapat diseret, seperti yang terlihat:

2019-11-18 22 40 35

Saya harap itu masuk akal ... Saya mencoba mencari tahu sendiri, tetapi seperti yang Anda tahu, saya tidak terlalu pandai dalam hal semacam ini ... 🤷‍♂️

Ah menurutku angka yang aneh adalah waktu dalam hitungan detik. Jadi saya rasa Anda perlu memformatnya

<input
  max={audioPlayer.duration}
  min='0'
  step='1'
  type='range'
  value={currentTime} // this should have alr been formatted to minutes and seconds
  onChange={e => {
    setCurrentTime(getTime(e.target.value)) // getTime will format time to minutes and seconds, you probably already have it somewhere
    state.audioPlayer.currentTime = e.target.value
  }}
/>

Coba ini dan lihat apakah Anda dapat menyeret slider.

Saya mencoba melakukan hal itu sebelumnya, tetapi tidak, dan sepertinya masih tidak berhasil, baik untuk pemformatan maupun untuk kontrol slider.

Berikut ini juga kode getTime serta currentTime dan duration , antara lain ... nomor tersebut akhirnya terformat, tetapi ada beberapa penundaan milidetik atau lebih, yang cukup untuk terlihat oleh pengguna (me! XD) ...

// Transform the currentTime info into minutes and seconds
  function getTime(time) {
    if(!isNaN(time)) {
      return Math.floor(time / 60) + ':' + ('0' + Math.floor(time % 60)).slice(-2)
    }
  }

  // both formattedTime and progress are states derived from audioPlayer.currentTime
  // so no need another useState
  const formattedTime = getTime(currentTime)
  const progress = currentTime / state.audioPlayer.duration

  useEffect(() => {
    const timeoutId= setInterval(() => {
      setCurrentTime(getTime(state.audioPlayer.currentTime))
      // setCurrentTime(formattedTime)
    }, 1000)
    return () => {
      // clean up function run when state.audioPlayer changes
      // reset currentTime to 0
      setCurrentTime(0)
      clearInterval(timeoutId)
    }
  }, [state.audioPlayer]);

  // get and display the duration of the track, in minutes and seconds
  const duration = getTime(state.audioPlayer.duration)

tidak tahu apakah Anda dapat melihat sesuatu di sana ... masih berusaha menambahkan kontrol volume juga :-)

Saya mengunduh kode Anda. Saya akan refactor sedikit. Akan butuh waktu.

Saya rasa apa yang tampaknya tidak jelas bagi Anda adalah perbedaan antara berbagi status vs berbagi logika stateful.

Yang Anda inginkan adalah berbagi status tentang musik yang diputar di semua komponen. Apa yang dilakukan hook useMusicPlayer adalah membagikan logika stateful, yang tidak diperlukan untuk aplikasi Anda.

// MusicPlayerContext
import React, { useState, useMemo, useContext, useEffect } from 'react'
import { useStaticQuery, graphql } from 'gatsby'

const MusicPlayerContext = React.createContext([{}, () => {}])

const MusicPlayerProvider = props => {
  // COMMENT_ADDED
  // query both tracks and assets since only one staticQuery per file
  const { tracks, assets } = useStaticQuery(graphql`
    query Tracks {
      tracks: allMdx(
        filter: { fileAbsolutePath: { regex: "/content/music/" } }
      ) {
        edges {
          node {
            fields {
              slug
            }
            frontmatter {
              name
              artist
              genre
              bpm
              # ADD BASE HERE
              artwork {
                base
                childImageSharp {
                  fluid(maxWidth: 1000) {
                    ...GatsbyImageSharpFluid
                  }
                }
              }
              alt
              description
              release(formatString: "MMMM Do, YYYY")
              audio {
                absolutePath
                base
              }
            }
          }
        }
      }

      # query all mp3 and jpg files from /content/music/
      assets: allFile(
        filter: {
          extension: { in: ["mp3", "jpg"] }
          absolutePath: { regex: "/content/music/" }
        }
      ) {
        edges {
          node {
            publicURL
            relativePath
          }
        }
      }
    }
  `)

  // need useMemo to avoid re-computation when state change
  const trackList = useMemo(
    () =>
      tracks.edges.map(track => {
        const { frontmatter } = track.node
        const {
          name,
          artist,
          genre,
          bpm,
          artwork,
          alt,
          description,
          audio,
        } = frontmatter

        return { name, artist, genre, bpm, artwork, alt, description, audio }
      }),
    [tracks]
  )

  const [state, setState] = useState({
    audioPlayer: new Audio(),
    // COMMENT_ADDED
    // don't really need trackList in state
    // tracks: trackList,
    currentTrackIndex: null,
    isPlaying: false,
  })

  const [currentTime, setCurrentTime] = useState(state.audioPlayer.currentTime)

  // both formattedTime and progress are states derived from audioPlayer.currentTime
  // so no need another useState
  const formattedTime = getTime(currentTime)
  const progress = currentTime / state.audioPlayer.duration

  // get and display the duration of the track, in minutes and seconds
  const formattedDuration = getTime(state.audioPlayer.duration)

  useEffect(() => {
    // COMMENT_ADDED
    // reset currentTime to 0 when state.audioPlayer changes
    setCurrentTime(0)
  }, [state.audioPlayer])

  useEffect(() => {
    // COMMENT_ADDED
    // if isPlaying, start the timer
    if (state.isPlaying) {
      const timeoutId = setInterval(() => {
        setCurrentTime(currentTime => currentTime + 1)
      }, 1000)

      return () => {
        // COMMENT_ADDED
        // clear interval run when paused i.e. state.isPlaying is false
        clearInterval(timeoutId)
      }
    }
  }, [state.isPlaying])

  // convert to obj for fast lookup
  const assetObj = useMemo(
    () =>
      assets.edges.reduce((obj, file) => {
        const { publicURL, relativePath } = file.node
        obj[relativePath] = publicURL
        return obj
      }, {}),
    [assets]
  )

  function playTrack(index) {
    if (index === state.currentTrackIndex) {
      togglePlay()
    } else {
      state.audioPlayer.pause()

      const base = trackList[index].audio.base // frost.mp3
      const baseName = basename(base) // frost

      // new Audio() does not support relative path
      // hence the need for window.location.origin
      const audioPlayer = new Audio(
        `${window.location.origin}${assetObj[`${baseName}/${base}`]}`
      ) // new Audio('http://www.domain.com/static/frost-[hash].mp3')

      audioPlayer.play()
      setState(state => ({
        ...state,
        currentTrackIndex: index,
        isPlaying: true,
        audioPlayer,
      }))
    }
  }

  // Toggle play or pause
  function togglePlay() {
    if (state.isPlaying) {
      state.audioPlayer.pause()
    } else {
      state.audioPlayer.play()
    }
    setState(state => ({ ...state, isPlaying: !state.isPlaying }))
  }

  // Play the previous track in the tracks array
  function playPreviousTrack() {
    const newIndex =
      (((state.currentTrackIndex + -1) % trackList.length) + trackList.length) %
      trackList.length
    playTrack(newIndex)
  }

  // Play the next track in the tracks array
  function playNextTrack() {
    const newIndex = (state.currentTrackIndex + 1) % trackList.length
    playTrack(newIndex)
  }

  let currentTrackName,
    currentTrackArtist,
    currentTrackArtwork,
    currentTrackAudio

  // COMMENT_ADDED
  // simplify things a bit
  if (state.currentTrackIndex !== null) {
    const { currentTrackIndex } = state
    const currentTrack = trackList[currentTrackIndex]

    const base = currentTrack.audio.base // frost.mp3
    const baseName = basename(base) // frost

    currentTrackName = currentTrack.name
    currentTrackArtist = currentTrack.artist
    currentTrackArtwork = assetObj[`${baseName}/${currentTrack.artwork.base}`] // assetObj['frost/frost.jpg']
    currentTrackAudio = assetObj[`${baseName}/${currentTrack.audio.base}`] // assetObj['frost/frost.mp3']
  }

  return (
    <MusicPlayerContext.Provider
      value={{
        playTrack,
        togglePlay,
        currentTrackName,
        currentTrackArtist,
        currentTrackArtwork,
        currentTrackAudio,
        currentTime,
        // COMMENT_ADDED
        // setCurrentTime to be used by ProgressSlider
        setCurrentTime,
        formattedDuration,
        formattedTime,
        // volume,
        audioPlayer: state.audioPlayer,
        trackList,
        isPlaying: state.isPlaying,
        playPreviousTrack,
        playNextTrack,
      }}
    >
      {props.children}
    </MusicPlayerContext.Provider>
  )
}

// COMMENT_ADDED
// access global state from MusicPlayerContext
function useMusicPlayerState() {
  return useContext(MusicPlayerContext)
}

export { useMusicPlayerState, MusicPlayerProvider }

// frost.mp3 -> frost
function basename(name) {
  return name.slice(0, name.lastIndexOf('.'))
}

// Transform the currentTime info into minutes and seconds
function getTime(time) {
  if (!isNaN(time)) {
    return Math.floor(time / 60) + ':' + ('0' + Math.floor(time % 60)).slice(-2)
  }
}
// TrackList.js
import React from 'react'
import styled from 'styled-components'
import { motion } from 'framer-motion'
import Img from 'gatsby-image'

import { H1, H2, H3 } from '../components/Typography'

import PlayButton from '../images/controls/play-button.svg'
import PauseButton from '../images/controls/pause-button.svg'

import { useMusicPlayerState } from './MusicPlayerContext'

// const TrackGrid = styled.div`
//   margin: 1rem;
//   display: grid;
//   grid-template-rows: auto;
//   grid-template-columns: 1fr 1fr 1fr;
//   grid-gap: 1rem;
//   border: 1px solid red;
// `

const Card = styled.div`
  margin: 0;
  padding: 0;
  text-decoration: none;
  line-height: 1;
  background: black;
  cursor: pointer;
  box-sizing: border-box;
  width: 100%;
  height: auto;
  display: block;
  position: relative;
  /* display: grid;
  grid-template-columns: repeat(8, 1fr);
  grid-template-rows: repeat(16, 1fr); */
  background: #000;
  transition: All 200ms ease;
  z-index: 1;
  /* border: 1px solid white; */
`

const Artwork = styled(Img)`
  position: relative;
  margin: 0;
  padding: 0;
  max-width: 25rem;
  z-index: 1;
  border-radius: 16px;
`

const Text = styled.div`
  position: relative;
  z-index: 1;
  background: #222;
  border-radius: 16px;
  margin: -3rem 1rem 1rem 1rem;
  padding: 1rem;
  box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.75);

  > p {
    font-size: 24px;
  }
`

const Button = styled(motion.button)`
  margin: -6rem 0 0 15rem;
  padding: 1.5rem;
  width: 6rem;
  height: 6rem;
  border: none;
  border-radius: 3rem;
  background: #333;
  position: relative;
  box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.5);
  z-index: 5;

  :focus {
    outline: none;
  }

  :active {
    outline: none;
    box-shadow: 0px 0px 16px white;
    /* background: black; */
  }
`

const TrackList = () => {
  const {
    trackList,
    currentTrackName,
    playTrack,
    isPlaying,
  } = useMusicPlayerState()

  return (
    <>
      {trackList.map((track, index) => (
        // COMMENT_ADDED: add key here
        <Card key={index}>
          <Artwork
            fluid={track.artwork.childImageSharp.fluid}
            alt={track.alt}
          />
          <Button
            whileHover={{ scale: 1.1 }}
            whileTap={{ scale: 0.9 }}
            onClick={() => playTrack(index)}
          >
            {currentTrackName === track.name && isPlaying ? (
              <img src={PauseButton} alt="Pause Button" />
            ) : (
              <img src={PlayButton} alt="PlayButton" />
            )}
          </Button>
          <Text>
            <H1>{track.name}</H1>
            {/* <h3>{track.artist}</h3> */}
            {/* <p>{track.genre}</p> */}
            {/* <p>{track.bpm}</p> */}
          </Text>
        </Card>
      ))}
    </>
  )
}

export default TrackList
// PlayerControls.js
import React, { useState } from 'react'
import styled from 'styled-components'
import { H1, H2, H3 } from '../components/Typography'

import { useMusicPlayerState } from './MusicPlayerContext'

import previousButton from '../images/controls/previous-button.svg'
import playButton from '../images/controls/play-button.svg'
import pauseButton from '../images/controls/pause-button.svg'
import nextButton from '../images/controls/next-button.svg'

const Button = styled.button`
  margin: 1rem;
  padding: 0.8rem;
  width: 3rem;
  height: 3rem;
  border: none;
  background: #333;
  border-radius: 32px;

  :focus {
    outline: none;
    /* box-shadow: 0px 0px 16px white; */
  }

  :active {
    outline: none;
    box-shadow: 0px 0px 16px white;
    background: black;
  }
`

const Image = styled.img`
  margin: 0;
  padding: 0;
  height: 1.5rem;
  width: 1.5rem;
`

const FlexStart = styled.div`
  display: flex;
  align-items: center;
  justify-content: start;
`

const FlexCenter = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
`

const TrackName = styled(H1)`
  margin: 0 1rem;
  padding: 0;
`

const TrackDuration = styled.h3`
  margin: 0;
  padding: 0.5rem;
`

const TrackArtwork = styled.img`
  margin: 0 0.5rem;
  padding: 0;
  width: 4rem;
  height: 4rem;
  border: none;
`
// const ProgressBar = styled.progress`
//   margin: 0 1rem;
//   background-color: #333;
// `

// COMMENT_ADDED
// ProgressSlider is a standalone component
// it accesses global music state with the useMusicPlayerState hook
// jump to anywhere on the track using a progress bar style interface
function ProgressSlider() {
  const {
    audioPlayer,
    currentTime,
    formattedTime,
    setCurrentTime,
  } = useMusicPlayerState()

  return (
    <input
      max={audioPlayer.duration}
      min="0"
      step="1"
      type="range"
      value={currentTime}
      onChange={event => {
        // COMMENT_ADDED
        // convert event.target.value, a string, to number
        const newTiming = parseInt(event.target.value, 10)

        setCurrentTime(newTiming)
        // not AudioPlayer
        audioPlayer.currentTime = newTiming
      }}
      // COMMENT_ADDED
      // onInput is not needed
    />
  )
}

const Controls = () => {
  // const [state, setState] = useContext(MusicPlayerContext)
  // const [currentTime, setCurrentTime] = useState(state.audioPlayer.currentTime)

  const {
    isPlaying,
    currentTrackName,
    currentTrackArtwork,
    currentTime,
    formattedDuration,
    formattedTime,
    volume,
    togglePlay,
    playPreviousTrack,
    playNextTrack,
  } = useMusicPlayerState()

  // const [time, setTime] = useState(currentTime)

  // const handleChange = event => {
  //   setTime(event.target.value)
  // }

  // e => {
  //   setCurrentTime(e.target.value)
  //   state.AudioPlayer.currentTime = e.target.value
  // }

  return (
    <>
      <FlexStart>
        <TrackArtwork src={currentTrackArtwork} alt="Album Artwork." />
        <TrackName>{currentTrackName}</TrackName>
      </FlexStart>
      <FlexCenter>
        <Button onClick={playPreviousTrack} disabled={!currentTrackName}>
          <Image src={previousButton} alt="Previous Button" />
        </Button>
        <Button onClick={togglePlay} disabled={!currentTrackName}>
          {isPlaying ? (
            <Image src={pauseButton} alt="Pause Button" />
          ) : (
            <Image src={playButton} alt="Play Button" />
          )}
        </Button>
        <Button onClick={playNextTrack} disabled={!currentTrackName}>
          <Image src={nextButton} alt="Next Button" />
        </Button>
      </FlexCenter>
      <FlexCenter>
        <TrackDuration>{formattedTime}</TrackDuration>
        <ProgressSlider />
        <TrackDuration>{formattedDuration}</TrackDuration>
      </FlexCenter>
    </>
  )
}

export default Controls

Saya menambahkan beberapa komentar juga. Cukup telusuri COMMENT_ADDED . Jika ada bagian yang membuat Anda bingung, silakan klarifikasi.

hmmmmm .... Saya belum pernah mendengar atau memikirkan tentang perbedaan antara logika state dan stateful, jadi terima kasih banyak untuk itu :-)

Jadi, untuk memperjelas, status akan menjadi trek yang sedang diputar dan semua informasinya, dan logika stateful adalah pemutar musik yang memanipulasi trek yang sedang diputar, seperti bilah kemajuan, yang memungkinkan pendengar menavigasi waktu dalam waktu yang sedang diputar. jalur?

Saya pikir saya mulai memahami ini sedikit lebih baik. Perlu waktu untuk membahas apa yang telah Anda lakukan, tetapi itu tampak seperti keajaiban mutlak bagi mata saya yang tidak berpengalaman. Kemajuan yang telah Anda bantu saya capai benar-benar luar biasa dan telah sangat memotivasi saya, dan XD sangat dihargai

jadi saya melakukan ini untuk membuat teks placeholder untuk formattedDuration :

const formattedDuration = state.isPlaying ? getTime(state.audioPlayer.duration) : '0:00'

... dan secara umum berfungsi, kecuali saya mendapatkan penundaan yang sama di antara pembaruan ketika lagu berubah ... seperti:

2019-11-19 17 33 22

Saya pikir saya perlu useState tapi saya tidak yakin ... mungkin itu bisa dilakukan di <ProgressSlider> itu sendiri

Logika stateful melibatkan bagaimana Anda memanipulasi keadaan itu, apa yang terjadi ketika keadaan itu berubah, hal-hal semacam itu. Jadi katakanlah Anda ingin membuat pemutar musik lain, Anda dapat membagikan logika tersebut dengan pemutar baru alih-alih menulis ulang dari awal. Di sisi lain, jika Anda berbagi status di antara kedua pemutar musik tersebut, mereka akan memainkan lagu yang sama, dll ...

Saya pikir state.audioPlayer.duration mungkin undefined selama penundaan itu. Jadi mungkin Anda bisa melakukannya

const formattedDuration = getTime(state.audioPlayer.duration) || '0:00'

ya, itu benar-benar berfungsi :-) .... juga sesuatu yang belum saya lihat, yang merupakan operator OR logis, ya? (hanya mencarinya di Google)

Berbicara secara logis berarti:

thing 1                          ||                                thing 2

lakukan hal 1 ................................... tetapi jika tidak ada ...... ............................. lalu lakukan hal 2

Iya?

Senang rasanya mengetahui :-)

Ya. Jika thing1 adalah nilai yang salah, seperti undefined , null , 0 , atau '' .

Hai @universse , ada satu hal lagi yang ingin saya coba dan lakukan, tetapi saya tidak yakin bagaimana cara melakukannya. Saya memiliki banyak file mdx yang merupakan artikel tentang musik dan saya meletakkan contoh musik di dalamnya. Akan ada sejumlah contoh yang bervariasi di setiap artikel, tetapi yang saya ingin mereka lakukan adalah dimasukkan dalam konteks audio sehingga mereka bermain melalui pemutar yang persisten, menghentikan apa pun yang mungkin sedang diputar. Mencoba mencari cara untuk melakukan ini, tetapi saya agak bingung. Jika Anda memiliki pemikiran atau saran tentang di mana harus memulai, saya mendengarkan.

Contohnya adalah:

---
title: Music Stuff
category: Theory
---

# Heading

Some words and stuff.

<Audio src="example-audio.mp3"/>

More words and stuff.

Di mana contoh audio sebaris akan diputar melalui pemutar musik yang terus-menerus dan dikontrol oleh kontrol pemutar juga. Saya harap itu masuk akal.

Dapatkah Anda membagikan kode Anda untuk <Audio /> ?

Saya belum memiliki apa pun untuk tag <Audio/> .

Saya mencoba menggunakan tag <audio> dalam file mdx, dan itu tidak berhasil, lalu saya mencoba menggunakan gatsby-remark-audio dan itu juga tidak berhasil, jadi saya sedang mempelajari Web API Audio untuk melihat apakah ada sesuatu di sana yang dapat membantu saya dalam hal ini.

Saya juga mengalami bug di mdx itu sendiri, seperti ini:

https://github.com/gatsbyjs/gatsby/issues/19785

https://github.com/gatsbyjs/gatsby/issues/19825

... yang secara kasar terkait dengan ini, menurut saya:

https://github.com/gatsbyjs/gatsby/issues/16242

... dan saya yakin @ChristopherBiscardi sedang menangani masalah ini, tetapi tidak yakin kapan mdx dapat digunakan lagi.

Saya berpikir mungkin membungkus file template mdx dalam konteks audio dan kemudian memilih semua tag audio dan menambahkannya ke konteks entah bagaimana? Tidak yakin bagaimana melakukannya, atau apakah itu ide yang bagus 🤔

Saya rasa pertama-tama Anda memerlukan fungsi playArticleTrack di MusicPlayerProvider.js , mirip dengan fungsi playTrack .

  function playArticleTrack(src) {
    state.audioPlayer.pause()

    const audioPlayer = new Audio(src)

    audioPlayer.play()
    setState(state => ({
      ...state,
      currentTrackIndex: -1, // I assume article tracks are not parts of the original track list
      isPlaying: true,
      audioPlayer,
    }))
  }

Kemudian di komponen Audio

function Audio ({ src }) {
  const { playArticleTrack } = useMusicPlayerState()

  return ... 
}

Dan Anda perlu membungkus artikel mdx Anda dengan <MusicPlayerProvider /> , menggunakan https://www.gatsbyjs.org/docs/browser-apis/#wrapPageElement

ya, trek artikel bukan bagian dari daftar trek asli ... jadi, saya mencoba menerapkan saran Anda, dan sejauh ini saya telah melakukan ini pada komponen Audio.js :

import React from 'react'
import { useMusicPlayerState } from '../player/MusicPlayerContext'

function Audio ({ src }) {
  const { playArticleTrack } = useMusicPlayerState()

  return (
    <audio controls onPlay={playArticleTrack} src={src}></audio>
  )
}

export default Audio

... dan saya mencoba mengimplementasikan komponen itu di halaman indeks terlebih dahulu untuk mencoba membuatnya berfungsi, seperti:

// ...other imports before these...

import Audio from '../components/Audio'
import Groove from './groove.mp3'

const IndexPage = () => (
  <Container>
    <SEO title="Home" />
    <Audio src={Groove}></Audio>
    <TrackList />
  </Container>
)

dan saya mendapatkan pesan kesalahan berikut ketika saya menekan putar pada kontrol

Screen Shot 2019-12-06 at 12 00 21 AM

... jadi saya mencoba untuk memahami apa artinya dan apa yang perlu saya lakukan, tetapi jika Anda memiliki pemikiran ...

Bisakah Anda mencoba ini dan melihat bagaimana kelanjutannya?

  function playArticleTrack(src) {
    state.audioPlayer.pause()

    const audioPlayer = new Audio(src)

    audioPlayer.play()
    setState(state => ({
      ...state,
      currentTrackIndex: 0, // I change to 0 instead
      isPlaying: true,
      audioPlayer,
    }))
  }

Jika currentTrackIndex adalah -1 , currentTrack adalah undefined .

jadi saya mencobanya dan agak berhasil, kecuali sekarang trek 0 apa pun yang ada di indeks digantikan oleh file audio lain ini, dan kemudian aplikasi rusak ketika saya mencoba memutar trek lain itu lagi. Ia bahkan menampilkan karya seni trek pada posisi indeks 0 ketika saya mengeklik putar pada artikel Trek audio yang dimaksud ...

Saya juga berpikir bahwa saya tidak benar-benar membutuhkan tracklist sama sekali. Atau, dengan kata lain, yang benar-benar saya inginkan adalah file audio di banyak halaman berbeda dan halaman template (di dalam file mdx, dll ...) untuk semua hanya bermain melalui pemutar musik global dan persisten, dan semuanya memiliki metadata dan karya seni dan semuanya ditambahkan ke semacam riwayat mendengarkan.

Jadi mungkin membuat tracklist bukanlah pendekatan yang benar, dan alih-alih memiliki komponen audio yang dapat saya gunakan di mana saja di halaman mana pun yang menyentuh konteks musik global dan kemudian menambahkan dirinya ke riwayat daftar putar lebih sesuai dengan apa yang saya butuhkan untuk proyek ini ...

hmmmm, jadi saya menetapkan currentTrackIndex ke null saat trek artikel diputar, dan itu berfungsi, meskipun terus diputar tanpa suara setelah file selesai diputar. Kode terlihat seperti ini:

function playArticleTrack(src) {
    state.audioPlayer.pause()

    const audioPlayer = new Audio(src)

    audioPlayer.play()
    setState(state => ({
      ...state,
      // currentTrackIndex: 0, // I change to 0 instead
      currentTrackIndex: null, // Trying null and it seems to work, although the player keeps going after it's done.
      isPlaying: true,
      audioPlayer,
    }))
  }

... apakah ini boleh dilakukan? Apakah ini akan menyebabkan masalah lain di masa mendatang yang tidak saya sadari?

juga, apakah Anda di www.codementor.io atau apa pun, atau punya waktu atau minat untuk membantu saya menyelesaikan proyek ini di luar bantuan sumber terbuka? Saya menyadari bahwa saya terlalu bingung tetapi akan sangat senang jika ini dilakukan lebih cepat dari nanti dan memiliki produk yang berfungsi untuk membantu membangun bisnis musik saya. Jika tidak, jangan khawatir, saya pikir saya akan bertanya terlebih dahulu sebelum memperluas pencarian saya :-)

Apakah Anda terbuka untuk dihubungi melalui Whatsapp / Telegram?

Saya belum pernah menggunakan itu sebelumnya, tapi tentu :-)

sebelum kita membahasnya. Saat ini saya mencoba menautkan setiap kartu trek di trackList untuk membuka file mdx tentang trek itu, tetapi saya tidak terlalu beruntung.

Saya mencoba menambahkan konstanta slug dan kemudian mengaksesnya di trackList untuk menautkan ke file MDX individual tentang setiap trek, seperti:

  // need useMemo to avoid re-computation when state change
  const trackList = useMemo(
    () =>
      tracks.edges.map(track => {
        // add slug to the data...
        const slug = track.node.fields.slug
        const { frontmatter } = track.node
        const {
          name,
          artist,
          genre,
          bpm,
          artwork,
          alt,
          description,
          audio,
        } = frontmatter

        // then add slug to the return...
        return slug, { name, artist, genre, bpm, artwork, alt, description, audio }
      }),
    [tracks]
  )

... lalu gunakan itu untuk membuat tautan ke file mdx yang dibuat secara terprogram dalam komponen tracklist , seperti:

const TrackList = () => {
  const {
    trackList,
    currentTrackName,
    playTrack,
    isPlaying,
  } = useMusicPlayerState()

  return (
    <>
      {trackList.map((track, index) => (
        // use the track slug to link to the mdx file with the same slug...
        <Card key={index} to={track.slug}>
          <Artwork
            fluid={track.artwork.childImageSharp.fluid}
            alt={track.alt}
          />
          <Button
            whileHover={{ scale: 1.1 }}
            whileTap={{ scale: 0.9 }}
            onClick={() => playTrack(index)}
          >
            {currentTrackName === track.name && isPlaying ? (
              <img src={PauseButton} alt="Pause Button" />
            ) : (
              <img src={PlayButton} alt="Play Button" />
            )
            }
          </Button>
          <Text>
            <H1>{track.name}</H1>
            <H3>{track.artist}</H3>
            {/* <p>{track.genre}</p> */}
            {/* <p>{track.bpm}</p> */}
          </Text>
          <LinkButton>Learn More</LinkButton>
        </Card>
      ))}
    </>
  )
}

export default TrackList

... tetapi ini saat ini tidak berfungsi sama sekali, dan saya mendapatkan kesalahan bahwa nilai track.slug tidak ditentukan di konsol:

Screen Shot 2019-12-16 at 2 53 08 PM

Jadi, saya berpikir bahwa saya tidak terlalu jauh di sini, tetapi saya tidak yakin apa yang saya lewatkan untuk membuatnya berfungsi dengan benar, karena saya tidak sepenuhnya memahami apa yang saya lakukan :-(

Apakah itu salah ketik?

return { slug, name, artist, genre, bpm, artwork, alt, description, audio }

dari pada

return slug, { name, artist, genre, bpm, artwork, alt, description, audio }

jadi, saya sedikit bingung ... ini terlihat seperti objek bagi saya, karena kurung kurawal:

return { slug, name, artist, genre, bpm, artwork, alt, description, audio }

... tapi mengembalikan apa yang tampak seperti larik data? Tidak ada pasangan property: value , hanya satu kata, jadi saya pikir mungkin sintaksnya mirip dengan impor dan ekspor modul ES di mana Anda harus memberi nama impor dan ekspor di dalam kurung kurawal jika bukan default ekspor, yang saya juga tidak sepenuhnya mengerti, sejujurnya. Itulah mengapa saya meletakkan siput di luar kurung kurawal, karena saya tidak mengerti mengapa itu berada di dalam atau di luar kurung kurawal.

Saya akan mencoba memasukkannya ke dalam dan melihat apakah berhasil :-)

Ini disebut singkatan objek. Anda dapat membaca lebih lanjut di sini https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer

Pada dasarnya, ini adalah jalan pintas. Jadi, bukan o

const obj = { a: a, b: b, c: c }

Kamu bisa menulis

const obj = { a, b, c }

ooooooooh ... itu menjelaskan beberapa hal di kepalaku :-)

ok, jadi sekarang saya mencoba mencari cara agar tombol putar di setiap halaman trek berfungsi dengan benar ...

Jadi, saat ini, ada kumpulan musik:

Screen Shot 2019-12-18 at 11 39 46 AM

... dan saat pengguna mengklik tombol putar, bidak itu akan diputar. Jika pengguna mengklik tombol pelajari lebih lanjut, mereka akan diarahkan ke halaman mdx yang dibuat secara terprogram dengan artikel yang menjelaskan bagian tersebut dan cara pembuatannya:

Screen Shot 2019-12-18 at 11 41 01 AM

... di mana ada juga tombol putar di halaman itu juga di kartu judul, yang saya ingin ditautkan ke tombol putar dari tampilan kisi semua bagian, sehingga jika pengguna mengklik tombol putar di kisi, lalu mengklik tombol pelajari lebih lanjut dan menavigasi ke artikel yang menjelaskan bagian tersebut, lalu tombol putar di sana akan mencerminkan status pemutaran musik saat ini, artinya akan ada ikon jeda. Tetapi jika pengguna tidak mengklik tombol putar, melainkan hanya menavigasi ke halaman artikel individual, tombol putar akan menjadi ikon putar, dan saat diklik, trek itu akan diputar.

Jadi, saya mulai melakukan ini dengan menyalin dan menempelkan tombol putar dari komponen daftar lagu ke template artikel:

const Track = ({ data }) => {

  const {
    currentTrackName,
    playTrack,
    isPlaying,
  } = useMusicPlayerState()

  const { frontmatter, timeToRead, body } = data.mdx

  return (
    <MDXProvider components={{...components }}>
      <ArticleBlock>
      <TitleBlock>
        <TitleText>
          <Title>{frontmatter.name}</Title>
          <Subtitle>{frontmatter.artist}</Subtitle>
          <Subtitle>{frontmatter.genre}</Subtitle>
          <Subtitle>{frontmatter.bpm}</Subtitle>
          {/* <Subtitle>{frontmatter.description}</Subtitle>    */}
        </TitleText>
        <PlayButton
          whileHover={{ scale: 1.1 }}
          whileTap={{ scale: 0.9 }}
          onClick={() => playTrack()}
        >
          {currentTrackName === frontmatter.name && isPlaying ? (
            <img src={PauseButtonIcon} alt="Pause Button" />
          ) : (
            <img src={PlayButtonIcon} alt="Play Button" />
          )
          }
        </PlayButton>
        <Artwork fluid={frontmatter.artwork.childImageSharp.fluid} alt="cool stuff." />
      </TitleBlock>
      <TitleScrim />
      <TitleImage fluid={frontmatter.artwork.childImageSharp.fluid} alt="cool stuff." />
      <Content>
        <MDXRenderer>{body}</MDXRenderer>
      </Content>
    </ArticleBlock>
  </MDXProvider>
  )
}

... tetapi ketika saya mengklik tombol putar, saya mendapatkan pesan kesalahan berikut:

di Chrome:

Screen Shot 2019-12-18 at 11 45 45 AM

... dan di Safari:

Screen Shot 2019-12-18 at 11 46 23 AM

... jadi saya pikir saya perlu mengatakannya untuk memilih trek tertentu untuk dihubungkan ke tombol putar, tetapi tidak yakin bagaimana cara melakukannya.

Masalahnya adalah Anda tidak meneruskan index ke playTrack . Untuk melakukan itu, Anda perlu mengaitkan setiap halaman trek individu dengan indeks yang benar, dan meneruskan indeks itu ke playtrack click handler tombol putar.

Mungkin Anda bisa melakukan sesuatu seperti

// HERE get slug from pageContext
const Track = ({ data, pageContext: { slug } }) => {

  const {
    currentTrackName,
    playTrack,
    isPlaying,
    trackList
  } = useMusicPlayerState()

  const { frontmatter, timeToRead, body } = data.mdx
  const index = trackList.findIndex(track => track.slug === slug) // HERE - find the correct track index

  return (
    <MDXProvider components={{...components }}>
      <ArticleBlock>
      <TitleBlock>
        <TitleText>
          <Title>{frontmatter.name}</Title>
          <Subtitle>{frontmatter.artist}</Subtitle>
          <Subtitle>{frontmatter.genre}</Subtitle>
          <Subtitle>{frontmatter.bpm}</Subtitle>
          {/* <Subtitle>{frontmatter.description}</Subtitle>    */}
        </TitleText>
        <PlayButton
          whileHover={{ scale: 1.1 }}
          whileTap={{ scale: 0.9 }}
          onClick={() => playTrack(index)} // HERE
        >
          {currentTrackName === frontmatter.name && isPlaying ? (
            <img src={PauseButtonIcon} alt="Pause Button" />
          ) : (
            <img src={PlayButtonIcon} alt="Play Button" />
          )
          }
        </PlayButton>
        <Artwork fluid={frontmatter.artwork.childImageSharp.fluid} alt="cool stuff." />
      </TitleBlock>
      <TitleScrim />
      <TitleImage fluid={frontmatter.artwork.childImageSharp.fluid} alt="cool stuff." />
      <Content>
        <MDXRenderer>{body}</MDXRenderer>
      </Content>
    </ArticleBlock>
  </MDXProvider>
  )
}

wow, jadi ... baru ... berhasil! Sangat luar biasa!

Sekarang saya sedang mengerjakan komponen sampel audio inline lagi dan saya mengalami beberapa masalah. Sejauh ini saya memiliki ini:

import React from 'react'
import styled from 'styled-components'
import { motion } from 'framer-motion'

import PlayButton from '../images/controls/play-button.svg'
import PauseButton from '../images/controls/pause-button.svg'

import { useMusicPlayerState } from '../player/MusicPlayerContext'

const Button = styled(motion.button)`
  // styles n' stuff...
`

const AudioBlock = styled.div`
  // styles n' stuff...
`

const AudioText = styled.p`
    // styles n' stuff...
`

function Audio ({ src, children }) {
  const {
    currentTrackName,
    playArticleTrack,
    isPlaying,
  } = useMusicPlayerState()

  const articleTrackName = src;

  return (
    <AudioBlock>
      <Button
        whileHover={{ scale: 1.13 }}
        whileTap={{ scale: 0.9 }}
        onClick={() => playArticleTrack(src) }
        src={src}
      >
        { currentTrackName === articleTrackName && isPlaying ? (
          <img src={PauseButton} alt="Pause Button" />
        ) : (
          <img src={PlayButton} alt="Play Button" />
        )}
      </Button>
      <AudioText>{children}</AudioText>
    </AudioBlock>
  )
}

export default Audio

... di mana saya mencoba untuk memasukkan src sebagai file audio, dan memiliki fungsi tombol putar seperti biasanya.

Masalah 1:

  • Pemutar menjeda audio saat ini dan memutar klip, tetapi tombol tersebut tidak beralih ke fungsi jeda, itu hanya tetap sebagai tombol putar, dan ketika saya mengkliknya, itu hanya mulai diputar lagi dari awal.

Masalah 2:

  • Tidak tahu cara menampilkan nama klip di kontrol pemutar pusat seperti yang dilakukan oleh judul lagu.

Masalah 3:

  • Trek yang dijeda tidak memulai lagi di tempat yang ditinggalkan saat dijeda oleh klip audio, melainkan mulai diputar lagi.

Masalah 4 (sebenarnya, masalah yang lebih global, mungkin):

  • Tampaknya ini menjadi masalah di semua bagian audio, tetapi tidak sepenuhnya yakin, karena saya tidak menyadarinya sebelumnya. Ketika trek atau klip selesai diputar, fungsi currentTime di kontrol pemutar pusat terus menghitung, dan tidak berhenti, yang tentunya tidak boleh dilakukan.

jadi, saya mencoba untuk mengubah fungsi playArticleTrack() seperti ini:

  function playArticleTrack(src) {
    if (src  === state.currentTrackIndex)
    {
      togglePlay()
    } else {

      state.audioPlayer.pause()

      const audioPlayer = new Audio(src)

      audioPlayer.play()

      setState(state => ({
        ...state,
        currentTrackIndex: src,
        isPlaying: true,
        audioPlayer,
      }))
    }
  }

... jadi elemen Audio.js bisa terlihat seperti ini, kira-kira:

const Audio = ({ src, children }) => {
  const {
    currentTrackName,
    playArticleTrack,
    isPlaying,
  } = useMusicPlayerState()

  return (
    <Box>
      <AudioBlock>
        <Button
          whileHover={{ scale: 1.13 }}
          whileTap={{ scale: 0.9 }}
          onClick={() => playArticleTrack(src) }
          // onClick={togglePlay}
          src={src}
        >
          { currentTrackName === articleTrackName && isPlaying ? (
            <img src={PauseButton} alt="Pause Button" />
          ) : (
            <img src={PlayButton} alt="Play Button" />
          )}
        </Button>
        <AudioText>{children}</AudioText>
      </AudioBlock>
    </Box>
  )
}

... jadi bisa jadi ini di file mdx:

<Audio src={Groove}>Example Groove That is Super Duper Awesome!</Audio>

... dan ini di aplikasi itu sendiri:

Screen Shot 2019-12-29 at 8 03 03 PM

... tetapi belum berfungsi dengan baik ... ketika saya memilikinya sedikit lebih sederhana, saya tidak bisa menampilkan tombol jeda saat diputar, dan sekarang saya mencoba untuk lebih dekat mencerminkan playTrack() function, tetapi tidak yakin bagaimana menambahkan src ke trackIndex, saya rasa ... juga, ini sedang terjadi dengan versi saat ini dari fungsi playArticleTrack() :

Screen Shot 2019-12-29 at 10 27 53 PM

... tapi saya tidak yakin mengapa fungsi itu tertarik pada baris kode itu.

Masalahnya adalah src adalah string ("someurl.com/name.mp3") sedangkan currentIndex adalah angka. currentTrack yaitu trackList[currentTrackIndex] akan menjadi undefined bila Anda menggunakan src sebagai currentTrackIndex . Itulah mengapa baris di atas tidak dapat dievaluasi karena Anda mendapatkan properti audio dari undefined .

Dari apa yang saya lihat, Anda perlu merefaktor sebagian besar fungsi pemutar musik Anda untuk menangani kasus penggunaan baru ini.

ya, itulah yang saya pikirkan juga ... saya pikir untuk saat ini saya hanya dapat menggunakan elemen <audio/> , karena pada dasarnya berfungsi, dan kemudian kembali lagi nanti.

Namun, saya memiliki masalah lain yang lebih mendesak. Saya tidak bisa membuat proyek berhasil dibangun menggunakan gatsby build , karena benda new Audio() tidak didukung selama pembuatan SSR, jadi saya perlu mencari cara untuk menyiasatinya sehingga aplikasi benar-benar bisa merata bekerja di luar mode gatsby develop .

Inilah masalah terkait:

https://github.com/gatsbyjs/gatsby/issues/19522

Saya bertanya kepada @DSchau di twitter beberapa hari yang lalu dan dia mengatakan ini:

Screen Shot 2020-01-04 at 11 26 43 AM

... jadi saya saat ini mencoba untuk memahami apa yang dia maksud, tetapi saya memiliki banyak lubang dalam pengetahuan saya serta pengetahuan umum saya.

Apa yang saya tidak mengerti adalah bagaimana mengatur keadaan di dalam hook lain ... seperti ... apakah saya menggunakan useState di dalam useEffect?

Berdasarkan apa yang dia katakan, hanya ini yang bisa saya pikirkan:

 useEffect(() =>{
    [],
    const [client, isClient] = useState();
  })

... jelas ini salah dan tidak berfungsi dan penuh kesalahan, tetapi saya tidak yakin apa yang dia maksud tentang cara menyiapkannya, dan cara kerja sintaks. Jika Anda memiliki pemikiran, mereka, tentu saja, akan sangat dihargai :-)

Anda bisa mencobanya

const [state, setState] = useState({
    audioPlayer: {}, // instead of new Audio()
    currentTrackIndex: null,
    isPlaying: false,
})

Hai @univers XD

Jadi, @DSchau dan @jlengstorf merekomendasikan penggunaan pendekatan bersyarat isClient untuk membuat aplikasi ini benar-benar dibangun, dan saat ini saya mencoba membuat pendekatan ini berhasil. Mereka merekomendasikan melakukan sesuatu di sepanjang baris ini, di mana saya membuat pengait status untuk mengatur apakah kode berjalan pada klien atau tidak:

  // ---------------------------------------------------------
  // Establishing if the code is running on the client
  // ---------------------------------------------------------

  // state hook for setting a starting state for wether or not this code is running on the client
  const [isClient, setIsClient] = useState(false)

  // useEffect hook to set the client boolenan to true
  useEffect(() => {
    setIsClient(true)
  }, [])

... lalu setel status untuk new Audio() HTMLAudioElement menggunakan fungsi setState, seperti ini:

  // state hook for setting a starting state for the Audio() constructor which creates a new HTMLAudioElement
  const [state, setState] = useState({}) 

  // conditional statement to set up a few different things if the the current code is being run on the client. 
  if (isClient) {
    setState({
      // a key value pair creating a new Audio() HTMLAudioELement using the constructor function
      audioPlayer: new Audio(),
      currentTrackIndex: null,
      isPlaying: false,
    })
  }

... tetapi ini hanya akhirnya membuat hal-hal lain rusak, jadi saya sekarang mencoba menggabungkan sebanyak mungkin hal yang diperlukan menjadi isClient kondisional, dan saya terus beralih ke kesalahan baru setiap kali saya membungkus sesuatu yang lain, seperti menyetel currentTime misalnya:

  // ---------------------------------------------------------
  // Setting Current Time 
  // ---------------------------------------------------------

  // A useState hook that will read and write the current time of the HTMLAudioElement
  const [currentTime, setCurrentTime] = useState()
  // if the code is running on the client then set the current time
  if (isClient) {
    setCurrentTime(state.audioPlayer.currentTime)
  }

  // This const declaration uses the getTime() function to properly format the current time of the currently playing audio file. If no audio is playing then the current time will display 0:00
  const formattedTime = getTime(currentTime) || `0:00`

... tetapi meskipun saya melakukannya, saya masih mendapatkan pesan kesalahan seperti ini:

Screen Shot 2020-01-08 at 6 33 43 PM

... yang aneh karena itulah yang baru saja saya bungkus dengan isClient kondisional.

Bagaimanapun, berikut ini adalah tempat file AudioContext sekarang ... jika Anda dapat melihat apa pun yang perlu saya perbaiki menggunakan pendekatan ini, itu akan sangat dihargai. @DSchau dan @jlengstorf juga jika Anda punya waktu atau energi untuk melihat XD

// ---------------------------------------------------------
// Audio Context
// ---------------------------------------------------------

import React, { useState, useMemo, useContext, useEffect } from 'react'
import { useStaticQuery, graphql } from 'gatsby'

const AudioContext = React.createContext([{}, () => {}])

const AudioProvider = props => {

  // ---------------------------------------------------------
  // Data Querying
  // ---------------------------------------------------------

  // This is a static query to get all of the information and audio and artwork for all the pieces of music that are being stored as folders containing mdx files with frontmatter and any associated files, like audio and image files, among other. This queries both track information and the asociated assets since only one staticQuery per file is sufficient to do this properly.
  const { tracks, assets } = useStaticQuery(graphql`
    query Tracks {
      tracks: allMdx(filter: { fileAbsolutePath: { regex: "/content/music/" } }
      ) {
        edges {
          node {
            fields {
              slug
            }
            frontmatter {
              name
              artist
              genre
              bpm
              # ADD BASE HERE
              artwork {
                base
                childImageSharp {
                  fluid(maxWidth: 1000) {
                    ...GatsbyImageSharpFluid
                  }
                }
              }
              alt
              description
              release(formatString: "MMMM Do, YYYY")
              audio {
                absolutePath
                base
              }
            }
          }
        }
      }

      # query all mp3 and jpg files from /content/music/
      assets: allFile(
        filter: {
          extension: { in: ["mp3", "jpg"] }
          absolutePath: { regex: "/content/music/" }
        }
      ) {
        edges {
          node {
            publicURL
            relativePath
          }
        }
      }
    }
  `)

  // This uses the map function to create an array of objects, where each object is a track and all it's information and assets. We need to use `useMemo` to avoid re-computation when the state changes, as this information is all static.
  const trackList = useMemo(
    () =>
      tracks.edges.map(track => {
        const slug = track.node.fields.slug
        const { frontmatter } = track.node
        const {
          name,
          artist,
          genre,
          bpm,
          artwork,
          alt,
          description,
          audio,
        } = frontmatter

        return { slug, name, artist, genre, bpm, artwork, alt, description, audio }
      }),
    [tracks]
  )

  // ---------------------------------------------------------
  // Utility Functions
  // ---------------------------------------------------------

  // This a function to turn a file name, which is a string, into a new string without the file type at the end. For example: frost.mp3 -> frost
  function basename(name) {
    return name.slice(0, name.lastIndexOf('.'))
  }

  // A function that transforms the audio.currentTime value into human readable minutes and seconds
  function getTime(time) {
    if (!isNaN(time)) {
      return Math.floor(time / 60) + ':' + ('0' + Math.floor(time % 60)).slice(-2)
    }
  }


  // ---------------------------------------------------------
  // Establishing if the code is running on the client
  // ---------------------------------------------------------

  // state hook for setting a starting state for wether or not this code is running on the client
  const [isClient, setIsClient] = useState(false)

  // useEffect hook to set the client boolenan to true
  useEffect(() => {
    setIsClient(true)
  }, [])

  // ---------------------------------------------------------
  // Creating the Audio Context
  // ---------------------------------------------------------

  // -------- ORIGINAL VERSION -------- //

  // using a useState hook to set the state of the audio player, including the audioPlyer itself, the currentTrackIndex and the isPlaying boolean
  // const [state, setState] = useState({
  //   audioPlayer: new Audio(),
  //   // audioPlayer: {}, // instead of new Audio()
  //   // don't really need trackList in state
  //   // tracks: trackList,
  //   currentTrackIndex: null,
  //   isPlaying: false,
  // })

  // -------- END ORIGINAL VERSION -------- //

  // state hook for setting a starting state for the Audio() constructor which creates a new HTMLAudioElement
  const [state, setState] = useState({}) 

  // conditional statement to set up a few different things if the the current code is being run on the client. 
  if (isClient) {
    setState({
      // a key value pair creating a new Audio() HTMLAudioELement using the constructor function
      audioPlayer: new Audio(),
      currentTrackIndex: null,
      isPlaying: false,
    })
  }

  // ---------------------------------------------------------
  // Setting Current Time 
  // ---------------------------------------------------------

  // -------- ORIGINAL VERSION -------- //

  // A useState hook that will read and write the current time of the HTMLAudioElement
  // const [currentTime, setCurrentTime] = useState(state.audioPlayer.currentTime)

  // This const declaration uses the getTime() function to properly format the current time of the currently playing audio file. If no audio is playing then the current time will display 0:00
  // const formattedTime = getTime(currentTime) || `0:00`

  // -------- END ORIGINAL VERSION -------- //

  // A useState hook that will read and write the current time of the HTMLAudioElement
  const [currentTime, setCurrentTime] = useState()
  // if the code is running on the client then set the current time
  if (isClient) {
    setCurrentTime(state.audioPlayer.currentTime)
  }

  // This const declaration uses the getTime() function to properly format the current time of the currently playing audio file. If no audio is playing then the current time will display 0:00
  const formattedTime = getTime(currentTime) || `0:00`

  // ---------------------------------------------------------
  // Setting Duration 
  // ---------------------------------------------------------

  // -------- ORIGINAL VERSION -------- //

  // This const declaration uses the getTime() function to properly format the duration of the currently playing audio file. If no audio is playing then the duration will display 0:00
  // const formattedDuration = getTime(state.audioPlayer.duration) || `0:00`

  // This const declaration uses the getTime() function to properly format the duration of the currently playing audio file. If no audio is playing then the duration will display 0:00
  // const formattedDuration = getTime(currentDuration) || `0:00`

  // -------- END ORIGINAL VERSION -------- //

  // A useState hook that will read and write the duration of the HTMLAudioElement
  const [currentDuration, setCurrentDuration] = useState()
  // if the code is running on the client then set the current time
  if (isClient) {
    setCurrentDuration(state.audioPlayer.duration)
  }

  // This const declaration uses the getTime() function to properly format the duration of the currently playing audio file. If no audio is playing then the duration will display 0:00
  const formattedDuration = getTime(currentDuration) || `0:00`

  // ---------------------------------------------------------
  // Setting Time
  // ---------------------------------------------------------

  // useEffect hook that will reset currentTime to 0 when state.audioPlayer changes
  useEffect(() => {
    setCurrentTime(0)
  }, [state.audioPlayer])

  useEffect(() => {
    // if isPlaying is true, then this useEffect hook will start the timer of the currentTime and set it again every second, written in milliseconds
    if (state.isPlaying) {
      const timeoutId = setInterval(() => {
        setCurrentTime(currentTime => currentTime + 1)
      }, 1000)

      return () => {
        // clear interval run when paused i.e. state.isPlaying is false
        clearInterval(timeoutId)
      }
    }
  }, [state.isPlaying])

  // convert to obj for fast lookup
  const assetObj = useMemo(
    () =>
      assets.edges.reduce((obj, file) => {
        const { publicURL, relativePath } = file.node
        obj[relativePath] = publicURL
        return obj
      }, {}),
    [assets]
  )

  // This function will Toggle between play or pause
  function togglePlay() {
    if (state.isPlaying) {
      state.audioPlayer.pause()
    } else {
      state.audioPlayer.play()
    }
    // calls the setState function and passes in an arrow function that is then passed an object with the current state being passed in the spread operator, then the isPlaying property being updated with the opposite boolean state of what it currently is.
    setState(state => ({ ...state, isPlaying: !state.isPlaying }))
  }

  // This function plays the current track
  function playTrack(index) {
    if (index === state.currentTrackIndex) {
      togglePlay()
    } else {
      state.audioPlayer.pause()

      const base = trackList[index].audio.base // frost.mp3
      const baseName = basename(base) // frost

      // new Audio() does not support relative path
      // hence the need for window.location.origin
      const audioPlayer = new Audio(
        `${window.location.origin}${assetObj[`${baseName}/${base}`]}`
      ) // new Audio('http://www.domain.com/static/frost-[hash].mp3')

      audioPlayer.play()
        setState(state => ({
          ...state,
          currentTrackIndex: index,
          isPlaying: true,
          audioPlayer,
        }))
      }
  }

  // Play the previous track in the tracks array
  function playPreviousTrack() {
    const newIndex =
      (((state.currentTrackIndex + -1) % trackList.length) + trackList.length) %
      trackList.length
    playTrack(newIndex)
  }

  // Play the next track in the tracks array
  function playNextTrack() {
    const newIndex = (state.currentTrackIndex + 1) % trackList.length
    playTrack(newIndex)
  }

  // This function is for pausing the main music that is being played and play the audio clip that has been selected somewhere in the application
  function playArticleTrack(src) {
    state.audioPlayer.pause()
    const audioPlayer = new Audio(src)
    audioPlayer.play()
    setState(state => ({
      ...state,
      currentTrackIndex: null, // I assume article tracks are not parts of the original track list
      isPlaying: true,
      audioPlayer,
    }))
  }

  // function playArticleTrack(src) {
  //   if (src  === state.currentTrackIndex)
  //   {
  //     togglePlay()
  //   } else {

  //     state.audioPlayer.pause()

  //     const audioPlayer = new Audio(src)

  //     audioPlayer.play()

  //     setState(state => ({
  //       ...state,
  //       currentTrackIndex: src,
  //       isPlaying: true,
  //       audioPlayer,
  //     }))
  //   }
  // }

  // function playArticleTrack(src) {

  //   state.audioPlayer.pause()

  //   const audioPlayer = new Audio(src)

  //   if (src === state.currentTrackIndex) {
  //     togglePlay()
  //   } else {
  //     state.audioPlayer.pause()
  //   }

  //   setState(state => ({
  //     ...state,
  //     // currentTrackIndex: 0, // I change to 0 instead
  //     // currentTrackIndex: null, // Trying null and it seems to work, although the player keeps going after it's done.
  //     currentTrackIndex: src,
  //     isPlaying: true,

  //     audioPlayer,
  //   }))
  // }

  let currentTrackName,
  currentTrackArtist,
  currentTrackArtwork,
  currentTrackAudio

  // simplify things a bit
  if (isClient && state.currentTrackIndex !== null) {
  // if (state.currentTrackIndex !== null) {
    const { currentTrackIndex } = state
    const currentTrack = trackList[currentTrackIndex]

    const base = currentTrack.audio.base // frost.mp3
    const baseName = basename(base) // frost

    currentTrackName = currentTrack.name
    currentTrackArtist = currentTrack.artist
    currentTrackArtwork = assetObj[`${baseName}/${currentTrack.artwork.base}`] // assetObj['frost/frost.jpg']
    currentTrackAudio = assetObj[`${baseName}/${currentTrack.audio.base}`] // assetObj['frost/frost.mp3']
  }

  return (
    <AudioContext.Provider
      value={{
        playTrack,
        playArticleTrack,
        togglePlay,
        currentTrackName,
        currentTrackArtist,
        currentTrackArtwork,
        currentTrackAudio,
        currentTime,
        // setCurrentTime to be used by ProgressSlider
        setCurrentTime,
        formattedTime,
        currentDuration,
        setCurrentDuration,
        formattedDuration,
        // volume,
        audioPlayer: state.audioPlayer,
        trackList,
        isPlaying: state.isPlaying,
        playPreviousTrack,
        playNextTrack,
      }}
    >
      {props.children}
    </AudioContext.Provider>
  )
}

// access global state from MusicPlayerContext
function useAudioState() {
  return useContext(AudioContext)
}

export { useAudioState, AudioProvider }

Pendekatan isClient baik-baik saja tetapi dalam kasus Anda ini adalah hal-hal yang terlalu rumit. Anda tidak perlu new Audio() sebagai status awal. {} kosong seperti yang disarankan di atas sudah cukup. Anda dapat mencoba dan melihat apakah itu dibangun.

hmmm ... mencobanya sebelumnya tetapi tidak berhasil ... coba lagi dan tombol putar tidak berfungsi dan kemudian saya mendapatkan pesan kesalahan ini:

Screen Shot 2020-01-08 at 8 24 16 PM

juga, saya tidak begitu mengerti bagaimana seseorang dapat memiliki audio tanpa Audio() ...

Jadi, untuk memperjelas, tampaknya membangun, tetapi kemudian tidak berhasil.

Anda bisa sedikit menyederhanakan:

  const [state, setState] = useState({}) 

  useEffect(() => {
    setState({
      // a key value pair creating a new Audio() HTMLAudioELement using the constructor function
      audioPlayer: new Audio(),
      currentTrackIndex: null,
      isPlaying: false,
    })
  }, [])

kesalahan yang Anda dapatkan sepertinya tidak dimulai setelah dimuat di browser

Anda tidak dapat memutar audio tanpa browser, dan ketika Gatsby sedang membangun, tidak ada klien - ini rendering sisi server - yang berarti Audio dan API browser lainnya tidak ada

jadi yang Anda lakukan adalah mengatakan, "buat situs _tanpa_ pemutar Audio, lalu inisialisasi pemutar Audio setelah situs dimuat di browser"

Apakah itu masuk akal?

Hai @jlengstorf , terima kasih telah meluangkan waktu untuk membantu saya :-)

Jadi, saya mengerti apa yang Anda katakan secara konseptual dengan sangat jelas, ini hanya bagaimana cara membuatnya berfungsi di aplikasi, yang sangat menantang bagi saya.

Saya menyalin dan menempelkan cuplikan kode Anda dari komentar sebelumnya secara verbatim dan mendapatkan pesan kesalahan ini:

Screen Shot 2020-01-08 at 11 29 23 PM

... jadi saya berpikir bahwa saya jelas perlu melakukan satu atau (banyak) hal lagi untuk memfaktorkan ulang kode agar berfungsi di dalam Gatsby dan mendapatkan kode tersebut melewati langkah pembuatan SSR.

Jadi sekarang saya pindah ke bawah halaman dan menambahkan useEffect hook ke deklarasi currentTime , seperti ini:

  // -------- `useEffect` VERSION -------- //

  // A useState hook that will read and write the current time of the HTMLAudioElement
  const [currentTime, setCurrentTime] = useState()
  // useEffect hook to set the currentTime when the code is running in the client
  useEffect(() => {
    setCurrentTime(state.audioPlayer.currentTime)
  }, [])

  // This const declaration uses the getTime() function to properly format the current time of the currently playing audio file. If no audio is playing then the current time will display 0:00
  const formattedTime = getTime(currentTime) || `0:00`

  // -------- End `useEffect` VERSION -------- //

... dan sekarang saya mendapatkan pesan kesalahan ini:

Screen Shot 2020-01-08 at 11 35 08 PM

... jadi saya pindah ke bawah halaman dan menambahkan useEffect hook ke deklarasi duration , seperti ini:

  // -------- `useEffect` VERSION -------- //

  // A useState hook that will read and write the duration of the HTMLAudioElement
  const [currentDuration, setCurrentDuration] = useState()
  // useEffect hook to set the currentDuration when the code is running in the client
  useEffect(() => {
    setCurrentDuration(state.audioPlayer.duration)
  }, [])

  // This const declaration uses the getTime() function to properly format the duration of the currently playing audio file. If no audio is playing then the duration will display 0:00
  const formattedDuration = getTime(currentDuration) || `0:00`

  // -------- End `useEffect` VERSION -------- //

... dan dapatkan pesan kesalahan ini:

Screen Shot 2020-01-08 at 11 58 51 PM

... jadi sekarang saya pindah ke bawah halaman dan mencoba menambahkan hook `useEffect ke pernyataan if bahwa baris kode ini terkandung di dalamnya, seperti:

// -------- `useEffect` VERSION -------- //

  useEffect(() => {
    if (state.currentTrackIndex !== null) {
      const { currentTrackIndex } = state
      const currentTrack = trackList[currentTrackIndex]

      const base = currentTrack.audio.base // frost.mp3
      const baseName = basename(base) // frost

      currentTrackName = currentTrack.name
      currentTrackArtist = currentTrack.artist
      currentTrackArtwork = assetObj[`${baseName}/${currentTrack.artwork.base}`] // assetObj['frost/frost.jpg']
      currentTrackAudio = assetObj[`${baseName}/${currentTrack.audio.base}`] // assetObj['frost/frost.mp3']
    }
  },[])

  // -------- END `useEffect` VERSION -------- //

... dan sekarang saya mendapatkan pesan kesalahan yang sama dengan yang saya mulai!

Screen Shot 2020-01-08 at 11 59 25 PM

... baru kali ini saya sudah memiliki useEffect dililitkan di sekitar deklarasi currentTime , jadi ............... maksud saya ..... ........................saya......................... ...hanya.............................................. ..

huh

tenor

cosmos

uhhhhh

... mungkin saya perlu membungkus hook useEffect di dalam useEffect , mungkin membuat regresi tak terbatas useEffect s ...? (Saya pikir itu humor, meskipun sulit bagi saya untuk mengatakannya pada saat ini ;-P

... jadi saya tidak begitu yakin apa yang harus dilakukan selanjutnya untuk mencapai garis akhir, atau pesan kesalahan apa yang menunggu saya di sana, meskipun mendapatkan pesan kesalahan yang sama setelah memecahkan masalah itu membuat saya bingung pada saat ini.

Bisakah kamu mencobanya

const [state, setState] = useState({
  audioPlayer: {},
  currentTrackIndex: null,
  isPlaying: false,
})

const [currentTime, setCurrentTime] = useState(state.audioPlayer.currentTime)

useEffect(() => {
  setState({
    audioPlayer: new Audio(),
    currentTrackIndex: null,
    isPlaying: false,
  })
}, [])

const formattedTime = getTime(currentTime) || `0:00`
const formattedDuration = getTime(state.audioPlayer.duration) || `0:00`

......

let currentTrackName
let currentTrackArtist
let currentTrackArtwork
let currentTrackAudio

if (state.currentTrackIndex !== null) {
  const { currentTrackIndex } = state
  const currentTrack = trackList[currentTrackIndex]

  const base = currentTrack.audio.base // frost.mp3
  const baseName = basename(base) // frost

  currentTrackName = currentTrack.name
  currentTrackArtist = currentTrack.artist
  currentTrackArtwork = assetObj[`${baseName}/${currentTrack.artwork.base}`]
  currentTrackAudio = assetObj[`${baseName}/${currentTrack.audio.base}`]
}

.......

Sial !!!!!

https://rykr.netlify.com

Terima kasih @universse !!!!!

... dan terima kasih @jlengstorf dan @DSchau juga telah meluangkan waktu untuk membantu saya. Belajar banyak akhir-akhir ini dengan memberi orang seperti Anda :-)

@rchrdnsh Saya hampir tidak membantu itu semua @jlengstorf. Terima kasih atas kata-katanya yang baik dan terima kasih telah menggunakan Gatsby 💜

Hai @DSK XD,

Dengan segala hormat kepada @jlengstorf (dan dia tentu saja layak mendapatkan rasa hormat maaaad) sebenarnya solusi @universse yang akhirnya berhasil untuk saya ... dia juga umumnya seorang badass juga dan sangat murah hati dan memberikan waktu dan keahliannya ... tidak tahu apakah dia membutuhkan pekerjaan dan / atau jika gatsby sedang merekrut, tetapi dia adalah legenda, setidaknya bagi saya :-)

Namun pada akhirnya, semua orang bersama-sama sebagai sarang sumber terbuka kolektif yang benar-benar membantu mendorong sumber terbuka ke depan, yang sangat luar biasa, dan saya tidak dapat melakukan apa yang telah saya lakukan sejauh ini tanpa semua orang yang telah meluangkan waktu untuk membantu saya, termasuk Anda sendiri dan @jlengstorf dan @universse dan semua orang yang memberikan diri mereka sendiri untuk mendorong teknologi web maju :-)

Sebenarnya, ditemukan masalah baru @universse dan @DSchau dan @jlengstorf , tetapi hanya dengan versi produksi langsung dari aplikasi yang berjalan langsung di web melalui netlify ...

Saat saya menjalankan aplikasi dalam mode gatsby develop secara lokal, trek langsung diputar (tentu saja)

Ketika saya gatsby build aplikasi, lalu gatsby serve , trek diputar secara instan (masuk akal)

Ketika saya mengunggah situs ke netlify dan menjalankannya dalam produksi di https://rykr.netlify.com , trek membutuhkan waktu sekitar 8-10 detik sebelum diputar, bahkan meskipun bilah kemajuan mulai bergerak segera. Awalnya saya pikir mereka hanya membutuhkan waktu untuk memuat, tetapi itu juga terjadi ketika saya memutar ulang trek yang telah diputar sebelumnya, jadi saya tidak yakin di mana masalahnya ... apakah ini masalah gatsby? masalah netlify? masalah audio html? semua yang di atas?

semua pemikiran akan sangat dihargai. Googling masalah belum banyak membantu ...

kedengarannya seperti masalah pemuatan bagi saya - saya akan mencoba mengunggah HTML / vanilla JS biasa ke Netlify untuk melihat apakah penundaan yang sama terjadi. jika itu memuat dengan cepat, maka Anda dapat menambahkan Gatsby kembali dan melihat apakah itu diperkenalkan kembali.

Saya berani bertaruh bahwa link rel="prefetch" dan caching akan menjadi teman Anda di sini

hmmmm ... mereka juga merupakan file yang cukup besar ... dalam kisaran 10 hingga 15 MB, bahkan sebagai mp3 ... mencoba mencari cara untuk mengompresnya lebih jauh tanpa kehilangan audio terlalu banyak, tapi ya ... mungkin saya perlu untuk melihat perpustakaan untuk streaming audio dan video di web ...

@rchrdnsh Cloudinary cukup bagus dalam hal ini - mereka membuatnya terdengar seperti mereka hanya mendukung video, tetapi saya menggunakannya untuk misalnya Honkify dan itu sangat mudah + secara otomatis mengambil sampel untuk ukuran file yang lebih kecil

hmmmm ... jadi, saya bisa membuat file audio lebih kecil sendiri, dan netlify juga cdn ... jadi apakah ada fitur cloudinary lain yang membuatnya lebih baik daripada menggulung sendiri? Saya tidak melihat fitur streaming audio atau video apa pun yang terdaftar, seperti memecah audio menjadi beberapa bagian dan memuat potongan-potongan tersebut ke dalam buffer sehingga dapat diputar secara instan, tetapi tentu saja saya bisa melewatkannya ...

Saya suka itu tidak mengharuskan saya untuk menggulung sendiri, tetapi pengorbanannya mungkin berbeda untuk Anda

detail lengkap tentang apa yang dapat mereka lakukan dengan audio: https://cloudinary.com/documentation/audio_transformations

Luar biasa, belum menemukan halaman itu, terima kasih :-)

Sooooooo ... kembali ke keseluruhan ide pramuat dan cache ......... ummmmmm ............ bagaimana?

Telah googling untuk solusi Gatsby tetapi sedikit tersesat ...

Saya bertanya karena sebenarnya tidak ada tautan, katakanlah, karena file audio dikumpulkan melalui kueri statis grafis, jadi saya tidak memiliki pengetahuan tentang cara menerapkan preloading dan caching untuk membantu dari kueri graphql, atau jika itu harus dilakukan melalui sesuatu di template, atau melalui plugin paket web atau sesuatu ...

... ya ... 🤷🏼‍♂️

Saya akan membiarkan @DSchau mengambil alih pertanyaan caching - Saya tidak yakin apa sikap resmi Gatsby saat ini tentang itu

untuk pramuat: https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content

Hai @universse!

Situs ini sekarang bekerja tidak terlalu buruk dengan gatsby-plugin-offline diaktifkan, dan itu bagus.

Namun, mengalami masalah aneh dalam safari. audioPlayer.duration mengembalikan infinity:aN , bukan durasinya, tetapi berfungsi dengan baik di chrome dan firefox ...

Screen Shot 2020-01-16 at 8 28 23 PM

mencari di Google sedikit dan menemukan beberapa hal, tetapi saya tidak memahaminya sama sekali ...

https://stackoverflow.com/questions/29019672/javascript-returns-nan-for-audio-duration-depending-on-request-sequence

https://stackoverflow.com/questions/22213577/audio-track-duration-showing-infinitynannan-in-safari-only

jika Anda punya kesempatan, maukah Anda melihat masalah tersebut dan melihat apakah itu masuk akal bagi Anda? Aku akan terus berusaha membungkus kepalaku juga.

NB - Saya juga harus menyebutkan bahwa ini berfungsi di gatsby develop tetapi tidak setelah gatsby build saat menjalankan gatsby serve , atau hidup di internet melalui netlify:

https://rykr.co

Hai @universitas ,

Saya pikir saya telah menemukan apa yang menyebabkan masalah infinity:aN ... gatsby-plugin-offline

ketika saya menonaktifkan plugin itu maka durasinya berfungsi dengan baik di safari, tetapi ketika saya mengaktifkan kembali plugin itu dan mendorongnya kembali ke server, maka infinity:aN kembali ... belum tahu cara memperbaikinya, tapi setidaknya saya mulai mempersempitnya ... juga menemukan artikel ini, tapi belum memahaminya sama sekali ...

https://philna.sh/blog/2018/10/23/service-workers-beware-safaris-range-request/

Bisakah Anda console.log(state.audioPlayer.duration) ? Saya ingin tahu apa nilai yang tidak diformat.

Jadi, dalam mode pengembangan atau dengan gatsby-plugin-offline dinonaktifkan, saya mendapatkan nilai durasi mentah, seperti:

Screen Shot 2020-01-18 at 9 47 54 AM

... di mana durasinya NaN tanpa audio yang dipilih atau diputar, tapi kemudian berubah menjadi nilai integer saat bermain.

Dengan gatsby-plugin-mode diaktifkan, ini terjadi:

Screen Shot 2020-01-18 at 10 33 21 AM

... di mana nilainya adalah NaN sebelum pemutaran audio dan infinity setelah tombol putar ditekan, dan setiap kali setTimeout berjalan, nilai tak terhingga dihitung ulang? Kupikir.

Sebagai catatan tambahan, saya sedang menjajaki kemungkinan menggunakan perpustakaan seperti howlerjs daripada terus menggulung milik saya sendiri, karena mereka telah memperhitungkan banyak kasus tepi dan memiliki rangkaian fitur bawaan yang bagus, seperti streaming dan chunking dan sprite -ing dan memuat negara, spasialisasi, dll ...

Bahkan ada perpustakaan kecil yang bagus bernama:

https://github.com/E-Kuerschner/useAudioPlayer

... yang merupakan pembungkus kait reaksi di sekitar howler, dan pengembangnya juga sangat dingin.

Telah belajar begitu banyak melakukan hal ini, jadi mungkin bahkan menggali kode howler dan melihat apakah saya dapat mentransfer salah satu teknik mungkin merupakan ide yang bagus juga.

Sepertinya ada masalah dengan service worker yang menyajikan audio. Saya tidak sepenuhnya yakin bagaimana cara memperbaikinya. Saya sarankan Anda membuka edisi baru untuk ini.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

totsteps picture totsteps  ·  3Komentar

Oppenheimer1 picture Oppenheimer1  ·  3Komentar

rossPatton picture rossPatton  ·  3Komentar

KyleAMathews picture KyleAMathews  ·  3Komentar

3CordGuy picture 3CordGuy  ·  3Komentar