Open openGeeksLab opened 8 years ago
Does anybody have this working?
@JoJordens this is not implemented and I have no time at the moment to work on this module, but that should not be hard
For iOS https://github.com/tlenclos/react-native-audio-streaming/blob/master/ios/ReactNativeAudioStreaming.m#L438 For Android (add an image view and setters to update it) https://github.com/tlenclos/react-native-audio-streaming/blob/master/android/src/main/res/layout/streaming_notification_player.xml#L7
This is possible by using the iTunes API and a little modification of the Player Component in the provided example.
This basically ads a getArtwork function which fetches the artwork from iTunes. The song state is a bit modified and now contains 'artist, title & artwork'
Artwork images are usually available at the following sizes:
In the now added Image append the URL with the size and .jpg like:
${artwork}80x80.jpg
import React, { Component } from 'react';
import {
NativeModules,
StyleSheet,
Text,
View,
Image,
TouchableOpacity,
DeviceEventEmitter,
ActivityIndicator,
Platform
} from 'react-native';
const { ReactNativeAudioStreaming } = NativeModules;
// Possibles states
const PLAYING = 'PLAYING';
const STREAMING = 'STREAMING';
const PAUSED = 'PAUSED';
const STOPPED = 'STOPPED';
const ERROR = 'ERROR';
const METADATA_UPDATED = 'METADATA_UPDATED';
const BUFFERING = 'BUFFERING';
const START_PREPARING = 'START_PREPARING'; // Android only
const BUFFERING_START = 'BUFFERING_START'; // Android only
// UI
const iconSize = 60;
class Player extends Component {
constructor(props) {
super(props);
this._onPress = this._onPress.bind(this);
this._getArtwork = this._getArtwork.bind(this);
this.state = {
status: STOPPED,
song: ''
};
}
componentDidMount() {
this.subscription = DeviceEventEmitter.addListener(
'AudioBridgeEvent', (evt) => {
// We just want meta update for song name
if (evt.status === METADATA_UPDATED && evt.key === 'StreamTitle') {
this._getArtwork(evt.value)
.then(song => this.setState({song: song}))
} else if (evt.status != METADATA_UPDATED) {
this.setState(evt);
}
}
);
ReactNativeAudioStreaming.getStatus((error, status) => {
(error) ? console.log(error) : this.setState(status)
});
}
_getArtwork(metaData){
// This asumes the metadata is passed to function as 'artist - title'
// Modify if needed
const [ artist, title ] = metaData.split(' - ');
let song = { artist: artist, title: title, artwork:''}
return fetch(`https://itunes.apple.com/search?term=${escape(artist)} ${escape(title)}&limit=1&entity=song`)
.then(blob => blob.json())
.then(response => {
// Some streams start with a jingle,
// at that moment there is no song info,
// but itunes returns a result when the api is called with an empty sting
if(artist === ''){
song['artwork'] = undefined
return song;
} else if(!response.results[0] || !response.results[0].artworkUrl100) {
song['artwork'] = undefined
return song;
} else {
song['artwork'] = response.results[0].artworkUrl100.slice(0, -13)
return song
}
})
.catch(error => {
song['artwork'] = undefined
return song;
});
}
_onPress() {
switch (this.state.status) {
case PLAYING:
case STREAMING:
ReactNativeAudioStreaming.pause();
break;
case PAUSED:
ReactNativeAudioStreaming.resume();
break;
case STOPPED:
case ERROR:
ReactNativeAudioStreaming.play(this.props.url, {showIniOSMediaCenter: true, showInAndroidNotifications: true});
break;
case BUFFERING:
ReactNativeAudioStreaming.stop();
break;
}
}
render() {
let icon = null;
switch (this.state.status) {
case PLAYING:
case STREAMING:
icon = <Text style={styles.icon}>॥</Text>;
break;
case PAUSED:
case STOPPED:
case ERROR:
icon = <Text style={styles.icon}>▸</Text>;
break;
case BUFFERING:
case BUFFERING_START:
case START_PREPARING:
icon = <ActivityIndicator
animating={true}
style={{height: 80}}
size="large"
/>;
break;
}
const { title, artist, artwork } = this.state.song
return (
<TouchableOpacity style={styles.container} onPress={this._onPress}>
{icon}
<View style={styles.textContainer}>
<Text style={styles.songName}>{title}</Text>
<Text style={styles.songName}>{artist}</Text>
</View>
{ artwork !== undefined && <Image resizeMode='cover' style={styles.artwork} source={{uri:`${artwork}80x80.jpg`}} />}
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
alignItems: 'center',
flexDirection: 'row',
height: 80,
paddingLeft: 10,
paddingRight: 10,
borderColor: '#000033',
borderTopWidth: 1,
},
icon: {
color: '#000',
fontSize: 26,
borderColor: '#000033',
borderWidth: 1,
borderRadius: iconSize / 2,
width: iconSize,
height: Platform.OS == 'ios' ? iconSize : 40,
justifyContent: 'center',
alignItems: 'center',
textAlign: 'center',
paddingTop: Platform.OS == 'ios' ? 10 : 0
},
textContainer: {
flexDirection: 'column',
margin: 10,
flex:1,
},
textLive: {
color: '#000',
marginBottom: 5
},
songName: {
fontSize: 20,
textAlign: 'center',
color: '#000'
},
artwork: {
height:80,
width:80,
}
});
Player.propTypes = {
url: React.PropTypes.string.isRequired
};
export { Player, ReactNativeAudioStreaming }
+1
in contrast to @gwitteveen's code, I didn't use .slice(0, -13)
, but hackily replaced parts of the URL to receive the image over HTTPS, since it's required by iOS's "App Transport Security Settings".
const artworkUrl = response.results[0].artworkUrl600
.replace(/http:\/\//, 'https://')
.replace(/\.mzstatic/, '-ssl.mzstatic')
<Image source={{ uri: artworkUrl }} />
i will pursue a different solution in the near future though, as this is not maintainable.
Could you add artwork of artist? Thank you.