import React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import InputRange from 'react-input-range'; import AlbumGrid from '../../components/AlbumGrid'; import TrackList from '../../components/TrackList'; import Thumbnail from '../../components/Thumbnail'; import Parallax from '../../components/Parallax'; import DropdownField from '../../components/Fields/DropdownField'; import AddSeedField from '../../components/Fields/AddSeedField'; import URILink from '../../components/URILink'; import ContextMenuTrigger from '../../components/ContextMenuTrigger'; import RelatedArtists from '../../components/RelatedArtists'; import Icon from '../../components/Icon'; import * as uiActions from '../../services/ui/actions'; import * as mopidyActions from '../../services/mopidy/actions'; import * as spotifyActions from '../../services/spotify/actions'; import { isLoading, getFromUri, uriType, titleCase, } from '../../util/helpers'; import { arrayOf } from '../../util/arrays'; class Discover extends React.Component { constructor(props) { super(props); this._autocomplete_timer = false; this.state = { add_seed: '', adding_seed: false, seeds: [], tunabilities: { acousticness: { enabled: false, convert_to_decimal: true, min: 0, max: 100, value: { min: 25, max: 75, }, }, danceability: { enabled: false, convert_to_decimal: true, min: 0, max: 100, value: { min: 25, max: 75, }, }, energy: { enabled: false, convert_to_decimal: true, min: 0, max: 100, value: { min: 25, max: 75, }, }, instrumentalness: { enabled: false, convert_to_decimal: true, min: 0, max: 100, value: { min: 25, max: 75, }, }, key: { enabled: false, min: 0, max: 11, value: { min: 3, max: 8, }, }, liveness: { enabled: false, convert_to_decimal: true, min: 0, max: 100, value: { min: 25, max: 75, }, }, loudness: { enabled: false, convert_to_decimal: true, min: 0, max: 100, value: { min: 25, max: 75, }, }, popularity: { enabled: false, min: 0, max: 100, value: { min: 0, max: 100, }, }, speechiness: { enabled: false, convert_to_decimal: true, description: 'The presence of spoken words in a track', min: 0, max: 100, value: { min: 25, max: 75, }, }, tempo: { enabled: false, convert_to_decimal: true, min: 0, max: 100, value: { min: 25, max: 75, }, }, valence: { enabled: false, convert_to_decimal: true, description: 'The musical positiveness conveyed by a track', min: 0, max: 100, value: { min: 25, max: 75, }, }, }, }; } componentDidMount() { this.props.uiActions.setWindowTitle('Discover'); // We have seeds provided in the URL if (this.props.match.params.seeds) { this.handleURLSeeds(this.props.match.params.seeds); } } componentWillReceiveProps(newProps, newState) { // New seeds via URL if (newProps.match.params.seeds != this.props.match.params.seeds) { this.handleURLSeeds(newProps.match.params.seeds); } } handleContextMenu(e) { var uri = 'iris:discover'; if (this.state.seeds) { uri += ':'; for (var i = 0; i < this.state.seeds.length; i++) { if (i > 0) { uri += ','; } uri += this.state.seeds[i].split(':').join('_'); } } const tracks = []; if (this.props.recommendations.tracks_uris && this.props.tracks) { for (var i = 0; i < this.props.recommendations.tracks_uris.length; i++) { var uri = this.props.recommendations.tracks_uris[i]; if (this.props.tracks.hasOwnProperty(uri)) { tracks.push(this.props.tracks[uri]); } } } const data = { e, context: 'track', items: tracks, uris: arrayOf('uri', tracks), }; this.props.uiActions.showContextMenu(data); } handleURLSeeds(seeds_string = this.props.match.params.seeds) { // Rejoin if we've had to uri-encode these as strings // We'd need to do this if our URL has been encoded so the whole URL can become // it's own URI (eg iris:discover:spotify_artist_1234) where we can't use ":" const seeds = seeds_string.split('_').join(':').split(','); for (let i = 0; i < seeds.length; i++) { switch (uriType(seeds[i])) { case 'artist': this.props.spotifyActions.getArtist(seeds[i]); break; case 'track': this.props.spotifyActions.getTrack(seeds[i]); break; } } this.setState({ seeds }); this.getRecommendations(seeds); } getRecommendations(seeds = this.state.seeds, tunabilities = this.state.tunabilities) { if (seeds.length > 0) { const digested_tunabilities = {}; for (const key in tunabilities) { if (tunabilities.hasOwnProperty(key) && tunabilities[key].enabled) { const tunability = tunabilities[key]; let { max } = tunability.value; let { min } = tunability.value; if (tunability.convert_to_decimal) { max /= 100; min /= 100; } digested_tunabilities[`${key}_max`] = max.toString(); digested_tunabilities[`${key}_min`] = min.toString(); } } this.props.spotifyActions.getRecommendations(seeds, 50, digested_tunabilities); } } playTracks() { this.props.mopidyActions.playURIs(this.props.recommendations.tracks_uris); } removeSeed(index) { const { seeds } = this.state; seeds.splice(index, 1); this.setState({ seeds }); } handleSelect(e, uri) { const { seeds } = this.state; seeds.push(uri); this.setState({ seeds }); } renderSeeds() { const seeds_objects = []; if (this.state.seeds.length > 0) { for (let i = 0; i < this.state.seeds.length; i++) { const uri = this.state.seeds[i]; switch (uriType(uri)) { case 'track': if (typeof (this.props.tracks[uri]) !== 'undefined') { seeds_objects.push(this.props.tracks[uri]); } else { seeds_objects.push({ name: 'Loading...', uri, }); } break; case 'artist': if (typeof (this.props.artists[uri]) !== 'undefined') { seeds_objects.push(this.props.artists[uri]); } else { seeds_objects.push({ name: 'Loading...', uri, }); } break; case 'genre': var name = getFromUri('genreid', uri); seeds_objects.push({ name: (name.charAt(0).toUpperCase() + name.slice(1)).replace('-', ' '), uri, }); break; } } } return (
Too many seeds! You can use up to a total of 5 seed tracks, artists and genres.
: null}