使用 react-native-track-player 从 Napster API 流式传输歌曲

Posted

技术标签:

【中文标题】使用 react-native-track-player 从 Napster API 流式传输歌曲【英文标题】:Using react-native-track-player To A Streaming Songs From Napster API 【发布时间】:2021-03-31 03:59:05 【问题描述】:

我按照教程使用 react-native-track-player 构建了一个简单的音乐播放器,这是我在 react native 学习曲线的一部分。现在,我不只是从我的歌曲数组中提供的 url 曲目播放/流式播放歌曲,我想从 API 流式传输歌曲(尽管它可以是任何 API - 但我已经注册了 Napster API),我觉得不受限制只有几首歌。但是,我无法将如何实现或调用 Napster API 来获取歌曲放在一起。 请任何帮助/指导,我将不胜感激。

下面是我的代码:

我有 data.js,我的歌曲数组:

const songs = [
  
    title: "death bed",
    artist: "Powfu",
    artwork: require("../assets/album-arts/death-bed.jpg"),
    url: "https://github.com/ShivamJoker/sample-songs/raw/master/death%20bed.mp3",
    id: "1",
  ,
  
    title: "bad liar",
    artist: "Imagine Dragons",
    artwork: require("../assets/album-arts/bad-liar.jpg"),
    url: "https://github.com/ShivamJoker/sample-songs/raw/master/Bad%20Liar.mp3",
    id: "2",
  ,
  
    title: "faded",
    artist: "Alan Walker",
    artwork: require("../assets/album-arts/faded.jpg"),
    url: "https://github.com/ShivamJoker/sample-songs/raw/master/Faded.mp3",
    id: "3",
  ,
];

export default songs;

这是我的 playerScreen.js :

import React, useRef, useEffect, useState from 'react';
import 
  View,
  SafeAreaView,
  Text,
  Image,
  FlatList,
  Dimensions,
  Animated,
  StyleSheet,
 from 'react-native';

import TrackPlayer, 
  Capability,
  useTrackPlayerEvents,
  usePlaybackState,
  TrackPlayerEvents,
  STATE_PLAYING,
  Event,
 from 'react-native-track-player';

import songs from './data';
import Controller from './Controller';
import SliderComp from './SliderComp';

const width, height = Dimensions.get('window');

// const events = [
//   TrackPlayerEvents.PLAYBACK_STATE,
//   TrackPlayerEvents.PLAYBACK_ERROR
// ];

export default function PlayerScreen() 
  const scrollX = useRef(new Animated.Value(0)).current;

  const slider = useRef(null);
  const isPlayerReady = useRef(false);
  const index = useRef(0);

  const [songIndex, setSongIndex] = useState(0);

  const isItFromUser = useRef(true);

  // for tranlating the album art
  const position = useRef(Animated.divide(scrollX, width)).current;
  const playbackState = usePlaybackState();

  useEffect(() => 
    // position.addListener(( value ) => 
    //   console.log(value);
    // );

    scrollX.addListener((value) => 
      const val = Math.round(value / width);

      setSongIndex(val);
    );

    TrackPlayer.setupPlayer().then(async () => 
      // The player is ready to be used
      console.log('Player ready');
      // add the array of songs in the playlist
      await TrackPlayer.reset();
      await TrackPlayer.add(songs);
      TrackPlayer.play();
      isPlayerReady.current = true;

      await TrackPlayer.updateOptions(
        stopWithApp: false,
        alwaysPauseOnInterruption: true,
        capabilities: [
          Capability.Play,
          Capability.Pause,
          Capability.SkipToNext,
          Capability.SkipToPrevious,
        ],
      );
      //add listener on track change
      TrackPlayer.addEventListener(Event.PlaybackTrackChanged, async (e) => 
        console.log('song ended', e);

        const trackId = (await TrackPlayer.getCurrentTrack()) - 1; //get the current id

        console.log('track id', trackId, 'index', index.current);

        if (trackId !== index.current) 
          setSongIndex(trackId);
          isItFromUser.current = false;

          if (trackId > index.current) 
            goNext();
           else 
            goPrv();
          
          setTimeout(() => 
            isItFromUser.current = true;
          , 200);
        

        // isPlayerReady.current = true;
      );

      //monitor intterupt when other apps start playing music
      TrackPlayer.addEventListener(Event.RemoteDuck, (e) => 
        // console.log(e);
        if (e.paused) 
          // if pause true we need to pause the music
          TrackPlayer.pause();
         else 
          TrackPlayer.play();
        
      );
    );

    return () => 
      scrollX.removeAllListeners();
      TrackPlayer.destroy();

      // exitPlayer();
    ;
  , []);

  // change the song when index changes
  useEffect(() => 
    if (isPlayerReady.current && isItFromUser.current) 
      TrackPlayer.skip(songs[songIndex].id)
        .then((_) => 
          console.log('changed track');
        )
        .catch((e) => console.log('error in changing track ', e));
    
    index.current = songIndex;
  , [songIndex]);

  const exitPlayer = async () => 
    try 
      await TrackPlayer.stop();
     catch (error) 
      console.error('exitPlayer', error);
    
  ;

  const goNext = async () => 
    slider.current.scrollToOffset(
      offset: (index.current + 1) * width,
    );

    await TrackPlayer.play();
  ;
  const goPrv = async () => 
    slider.current.scrollToOffset(
      offset: (index.current - 1) * width,
    );

    await TrackPlayer.play();
  ;

  const renderItem = (index, item) => 
    return (
      <Animated.View
        style=
          alignItems: 'center',
          width: width,
          transform: [
            
              translateX: Animated.multiply(
                Animated.add(position, -index),
                -100,
              ),
            ,
          ],
        >
        <Animated.Image
          source=item.artwork
          style=width: 320, height: 320, borderRadius: 5
        />
      </Animated.View>
    );
  ;

  return (
    <SafeAreaView style=styles.container>
      <SafeAreaView style=height: 320>
        <Animated.FlatList
          ref=slider
          horizontal
          pagingEnabled
          showsHorizontalScrollIndicator=false
          scrollEventThrottle=16
          data=songs
          renderItem=renderItem
          keyExtractor=(item) => item.id
          onScroll=Animated.event(
            [nativeEvent: contentOffset: x: scrollX],
            useNativeDriver: true,
          )
        />
      </SafeAreaView>
      <View>
        <Text style=styles.title>songs[songIndex].title</Text>
        <Text style=styles.artist>songs[songIndex].artist</Text>
      </View>

      <SliderComp />

      <Controller onNext=goNext onPrv=goPrv />
    </SafeAreaView>
  );

【问题讨论】:

【参考方案1】:

我会通过道具将songs 传递给您当前的PlayerScreen。您可以使用单独的组件从 Napster API 加载曲目,然后使用这些道具渲染 PlayerScreen

我唯一不确定的部分是作为url 传递给玩家的路径。 Napster 数据包含一个属性previewURL,它是一个mp3,但它不是整首歌。我相信 href 是可流式传输的 URL。不过,它需要身份验证才能加载完整曲目。

我在这里使用的 API 路径适用于最流行的曲目。

export default function TopTracks() 
  const [songs, setSongs] = useState([]);

  useEffect(() => 
    const loadData = async () => 
      try 
        const url = `http://api.napster.com/v2.2/tracks/top?apikey=$API_KEY&limit=5`;
        const res = await axios.get(url);
        setSongs(
          res.data.tracks.map((track) => (
            duration: track.playbackSeconds,
            title: track.name,
            artist: track.artistName,
            album: track.albumName,
            id: track.id,
            url: track.href // or track.previewURL?
          ))
        );
       catch (error) 
        console.error(error);
      
    ;

    loadData();
  , []);

  return <PlayerScreen songs=songs />;

【讨论】:

非常感谢您的回复。我似乎不明白您所说的“我会通过道具将歌曲传递到您当前的 PlayerScreen ”是什么意思。因为包含歌曲数组的文件 data.js 仍然存在。

以上是关于使用 react-native-track-player 从 Napster API 流式传输歌曲的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)