From a45b7dde0a8440eb88528ce3500d183a3f889531 Mon Sep 17 00:00:00 2001 From: Mohamed El Mahallawy Date: Fri, 7 Apr 2017 17:47:58 -0700 Subject: [PATCH] Separating audio files from verses api (#721) * Separating audio files from verses api * change reciter * remove dependency on audio from verses route --- src/components/Audioplayer/index.js | 55 +++++++++---- src/components/Verse/index.js | 35 ++++++++- src/containers/Surah/index.js | 5 +- src/helpers/buildAudio.js | 9 +-- src/redux/actions/audioplayer.js | 16 ++++ src/redux/actions/verses.js | 4 +- src/redux/constants/audioplayer.js | 4 +- src/redux/modules/audioplayer.js | 115 +++++++--------------------- 8 files changed, 124 insertions(+), 119 deletions(-) diff --git a/src/components/Audioplayer/index.js b/src/components/Audioplayer/index.js index ecdd98e6e..2d3aa554c 100644 --- a/src/components/Audioplayer/index.js +++ b/src/components/Audioplayer/index.js @@ -11,7 +11,7 @@ import LocaleFormattedMessage from 'components/LocaleFormattedMessage'; import debug from 'helpers/debug'; import scroller from 'utils/scroller'; -import { surahType, segmentType } from 'types'; +import { surahType, segmentType, verseType } from 'types'; // Redux import * as AudioActions from 'redux/actions/audioplayer'; @@ -58,7 +58,8 @@ export class Audioplayer extends Component { currentTime: PropTypes.number, duration: PropTypes.number, // NOTE: should be PropTypes.instanceOf(Audio) but not on server. - currentFile: PropTypes.any // eslint-disable-line + currentFile: PropTypes.any, // eslint-disable-line + startVerse: verseType // eslint-disable-line }; componentDidMount() { @@ -103,6 +104,20 @@ export class Audioplayer extends Component { return false; } + componentDidUpdate() { + const { currentFile, isPlaying } = this.props; + + if (!currentFile) return false; + + if (isPlaying) { + currentFile.play(); + } else { + currentFile.pause(); + } + + return false; + } + componentWillUnmount() { const { files, currentFile } = this.props; debug('component:Audioplayer', 'componentWillUnmount'); @@ -398,6 +413,7 @@ export class Audioplayer extends Component { segments, isLoading, currentVerse, + currentFile, currentTime, duration, chapter, @@ -408,7 +424,7 @@ export class Audioplayer extends Component { setRepeat // eslint-disable-line no-shadow } = this.props; - if (isLoading || !currentVerse) { + if (isLoading || !currentFile) { return (
  • @@ -474,19 +490,24 @@ export class Audioplayer extends Component { } } -const mapStateToProps = (state, ownProps) => ({ - files: state.audioplayer.files[ownProps.chapter.chapterNumber], - segments: state.audioplayer.segments[ownProps.chapter.chapterNumber], - currentFile: state.audioplayer.currentFile, - currentVerse: state.audioplayer.currentVerse, - chapterId: state.audioplayer.chapterId, - isPlaying: state.audioplayer.isPlaying, - isLoadedOnClient: state.audioplayer.isLoadedOnClient, - isLoading: state.audioplayer.isLoading, - repeat: state.audioplayer.repeat, - shouldScroll: state.audioplayer.shouldScroll, - duration: state.audioplayer.duration, - currentTime: state.audioplayer.currentTime, -}); +const mapStateToProps = (state, ownProps) => { + const currentVerse = state.audioplayer.currentVerse || ownProps.startVerse.verseKey; + const files = state.audioplayer.files[ownProps.chapter.id]; + + return { + files, + currentVerse, + segments: state.audioplayer.segments[ownProps.chapter.id], + currentFile: files[currentVerse], + chapterId: ownProps.chapter.id, + isPlaying: state.audioplayer.isPlaying, + isLoadedOnClient: state.audioplayer.isLoadedOnClient, + isLoading: state.audioplayer.isLoading, + repeat: state.audioplayer.repeat, + shouldScroll: state.audioplayer.shouldScroll, + duration: state.audioplayer.duration, + currentTime: state.audioplayer.currentTime, + }; +}; export default connect(mapStateToProps, AudioActions)(Audioplayer); diff --git a/src/components/Verse/index.js b/src/components/Verse/index.js index 90ef215b1..db93f90a8 100644 --- a/src/components/Verse/index.js +++ b/src/components/Verse/index.js @@ -5,6 +5,7 @@ import { connect } from 'react-redux'; import Loadable from 'react-loadable'; import { verseType, matchType, surahType } from 'types'; +import { load as loadAudio } from 'redux/actions/audioplayer'; import ComponentLoader from 'components/ComponentLoader'; import LocaleFormattedMessage from 'components/LocaleFormattedMessage'; import Word from 'components/Word'; @@ -52,7 +53,9 @@ class Verse extends Component { currentWord: PropTypes.number, // gets passed in an integer, null by default iscurrentVerse: PropTypes.bool, currentVerse: PropTypes.string, - userAgent: PropTypes.func + userAgent: PropTypes.func, + audio: PropTypes.number.isRequired, + loadAudio: PropTypes.func.isRequired }; @@ -61,6 +64,32 @@ class Verse extends Component { isSearched: false }; + // TODO: Should this belong here? + componentDidMount() { + const { verse, audio } = this.props; + + this.props.loadAudio({ + chapterId: verse.chapterId, + verseId: verse.id, + verseKey: verse.verseKey, + audio + }); + } + + // TODO: Should this belong here? + componentWillReceiveProps(nextProps) { + if (this.props.audio !== nextProps.audio) { + const { verse, audio } = nextProps; + + this.props.loadAudio({ + chapterId: verse.chapterId, + verseId: verse.id, + verseKey: verse.verseKey, + audio + }); + } + } + shouldComponentUpdate(nextProps) { const conditions = [ this.props.verse !== nextProps.verse, @@ -311,6 +340,4 @@ class Verse extends Component { } } -export default connect(state => ({ - userAgent: state.options.userAgent -}))(Verse); +export default connect(() => ({}), { loadAudio })(Verse); diff --git a/src/containers/Surah/index.js b/src/containers/Surah/index.js index 0067c965b..ba79e2b5d 100644 --- a/src/containers/Surah/index.js +++ b/src/containers/Surah/index.js @@ -328,6 +328,8 @@ class Surah extends Component { isPlaying={isPlaying} isAuthenticated={isAuthenticated} key={`${verse.chapterId}-${verse.id}-verse`} + userAgent={options.userAgent} + audio={options.audio} /> )); } @@ -349,7 +351,7 @@ class Surah extends Component { } render() { - const { chapter, options, actions } = this.props; // eslint-disable-line no-shadow + const { chapter, verses, options, actions } = this.props; // eslint-disable-line no-shadow debug('component:Surah', 'Render'); if (!this.hasAyahs()) return
    {this.renderNoAyah()}
    ; @@ -409,6 +411,7 @@ class Surah extends Component {
    diff --git a/src/helpers/buildAudio.js b/src/helpers/buildAudio.js index 1845c0023..774f172c1 100644 --- a/src/helpers/buildAudio.js +++ b/src/helpers/buildAudio.js @@ -4,15 +4,10 @@ export function buildAudioForAyah(audio) { let segments = null; scopedAudio.preload = 'none'; + if (audio.url) { scopedAudio.src = audio.url; - segments = audio.encryptedSegments; - return { audio: scopedAudio, segments }; - } - - if (audio.mp3) { - scopedAudio.src = audio.mp3.url; - segments = audio.mp3.encryptedSegments; + segments = audio.segments; return { audio: scopedAudio, segments }; } diff --git a/src/redux/actions/audioplayer.js b/src/redux/actions/audioplayer.js index 14dd3b3cf..c63372798 100644 --- a/src/redux/actions/audioplayer.js +++ b/src/redux/actions/audioplayer.js @@ -10,6 +10,9 @@ import { SET_REPEAT, TOGGLE_SCROLL, BUILD_ON_CLIENT, + LOAD, + LOAD_SUCCESS, + LOAD_FAIL, UPDATE } from 'redux/constants/audioplayer.js'; export function setCurrentFile(file) { @@ -92,3 +95,16 @@ export function update(payload) { payload }; } + +export function load({ chapterId, verseId, verseKey, audio }) { // eslint-disable-line + return { + types: [LOAD, LOAD_SUCCESS, LOAD_FAIL], + promise: client => client.get(`/api/v3/chapters/${chapterId}/verses/${verseId}/audio_files`, { + params: { + recitation: audio || 8 // NOTE: default, but should never be used + } + }), + verseKey, + chapterId + }; +} diff --git a/src/redux/actions/verses.js b/src/redux/actions/verses.js index 2e98f98b2..bbba19628 100644 --- a/src/redux/actions/verses.js +++ b/src/redux/actions/verses.js @@ -12,7 +12,6 @@ import { // NOTE: For safe measure const defaultOptions = { - audio: 8, translations: [20] }; @@ -20,7 +19,7 @@ const defaultOptions = { const perPage = 10; export function load(id, paging, options = defaultOptions) { - const { audio, translations } = options; + const { translations } = options; // TODO: move this to module/verses // cookie.save('lastVisit', JSON.stringify({ chapterId: id, verseId: from })); @@ -31,7 +30,6 @@ export function load(id, paging, options = defaultOptions) { promise: client => client.get(`/api/v3/chapters/${id}/verses`, { params: { ...paging, - recitation: audio, translations } }), diff --git a/src/redux/constants/audioplayer.js b/src/redux/constants/audioplayer.js index 366d0b6aa..54b082806 100644 --- a/src/redux/constants/audioplayer.js +++ b/src/redux/constants/audioplayer.js @@ -10,4 +10,6 @@ export const SET_REPEAT = '@@quran/audioplayer/SET_REPEAT'; export const TOGGLE_SCROLL = '@@quran/audioplayer/TOGGLE_SCROLL'; export const BUILD_ON_CLIENT = '@@quran/audioplayer/BUILD_ON_CLIENT'; export const UPDATE = '@@quran/audioplayer/UPDATE'; - +export const LOAD = '@@quran/audioplayer/LOAD'; +export const LOAD_SUCCESS = '@@quran/audioplayer/LOAD_SUCCESS'; +export const LOAD_FAIL = '@@quran/audioplayer/LOAD_FAIL'; diff --git a/src/redux/modules/audioplayer.js b/src/redux/modules/audioplayer.js index 542ed31b7..757a67311 100644 --- a/src/redux/modules/audioplayer.js +++ b/src/redux/modules/audioplayer.js @@ -1,7 +1,6 @@ /* eslint-disable no-case-declarations */ -import { buildAudioFromHash } from 'helpers/buildAudio'; -import { buildSegments, extractSegments } from 'helpers/buildSegments'; -import debug from 'helpers/debug'; +import { buildAudioForAyah } from 'helpers/buildAudio'; +import { buildSegments } from 'helpers/buildSegments'; import { SET_CURRENT_FILE, @@ -14,12 +13,13 @@ import { PREVIOUS, SET_REPEAT, TOGGLE_SCROLL, - BUILD_ON_CLIENT, - UPDATE + UPDATE, + // LOAD, + LOAD_SUCCESS, + // LOAD_FAIL } from 'redux/constants/audioplayer.js'; import { - LOAD_SUCCESS as VERSES_LOAD_SUCCESS, LOAD as VERSES_LOAD, CLEAR_CURRENT as VERSES_CLEAR_CURRENT, SET_CURRENT_VERSE @@ -47,30 +47,6 @@ const initialState = { export default function reducer(state = initialState, action = {}) { switch (action.type) { - case BUILD_ON_CLIENT: { - debug('reducer:audioplayer', 'BUILD_ON_CLIENT init'); - const audioFromHash = buildAudioFromHash(state.files[action.chapterId], state.userAgent); - - debug('reducer:audioplayer', 'BUILD_ON_CLIENT return'); - - const stateFiles = state.files; - const filesById = stateFiles[action.chapterId]; - const filesFromHash = audioFromHash.files; - - return { - ...state, - isLoadedOnClient: true, - files: { - ...stateFiles, - [action.chapterId]: { - ...filesById, - ...filesFromHash - } - }, - currentFile: Object.values(audioFromHash.files)[0], - currentVerse: Object.keys(audioFromHash.files)[0] - }; - } case VERSES_CLEAR_CURRENT: { const stateFilesCurrent = state.files; @@ -88,49 +64,27 @@ export default function reducer(state = initialState, action = {}) { isLoading: false }; } - case VERSES_LOAD_SUCCESS: { - debug('reducer:audioplayer', 'VERSES_LOAD_SUCCESS init'); + case LOAD_SUCCESS: { + const data = buildAudioForAyah(action.result.audioFile); - const verses = action.result.entities.verses; - const audioFromHash = __CLIENT__ ? buildAudioFromHash(verses, state.userAgent) : verses; - const files = Object.assign( - {}, - state.files[action.chapterId], - __CLIENT__ ? audioFromHash.files : audioFromHash - ); - - const currentVerse = state.currentVerse ? state.currentVerse : Object.keys(files)[0]; - - let currentFile; - if (state.currentFile && state.currentFile === Object.values(files)[0]) { - // If the same file is being used, for example in lazy loading, then keep same file - currentFile = state.currentFile; - } else if (state.currentVerse || currentVerse) { - // If the user changes the reciter, we want to maintain the file where - // the user last left off - currentFile = files[state.currentVerse || currentVerse]; - } else { - // Otherwise, just choose the first file - currentFile = Object.values(files)[0]; - } - - const stateFiles = state.files; - const stateSegments = state.segments; - - debug('reducer:audioplayer', 'VERSES_LOAD_SUCCESS return'); return { ...state, - currentVerse, - currentFile, - chapterId: action.chapterId, - isLoadedOnClient: __CLIENT__, + loaded: true, + loading: false, + errored: false, files: { - ...stateFiles, - [action.chapterId]: files + ...state.files, + [action.chapterId]: { + ...state.files[action.chapterId], + [action.verseKey]: data.audio + } }, segments: { - ...stateSegments, - [action.chapterId]: extractSegments(action.result.entities.verses) + ...state.segments, + [action.chapterId]: { + ...state.segments[action.chapterId], + [action.verseKey]: buildSegments(data.segments) + } } }; } @@ -142,27 +96,16 @@ export default function reducer(state = initialState, action = {}) { }; } case PLAY: { - if (state.currentFile) { - state.currentFile.play(); - return { - ...state, - isPlaying: true - }; - } - - return state; + return { + ...state, + isPlaying: true + }; } case PAUSE: { - if (state.currentFile) { - state.currentFile.pause(); - - return { - ...state, - isPlaying: false - }; - } - - return state; + return { + ...state, + isPlaying: false + }; } case NEXT: { const [chapterId, ayahNum] = action.currentVerse.split(':');