Skip to content

Commit 13654df

Browse files
committed
Read song files
1 parent c022258 commit 13654df

File tree

14 files changed

+213
-86
lines changed

14 files changed

+213
-86
lines changed

App/Containers/RootContainer.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { Component } from 'react'
22
import { View, StatusBar } from 'react-native'
33
import NavigationRouter from '../Navigation/NavigationRouter'
44
import { connect } from 'react-redux'
5-
import StartupActions from '../Redux/StartupRedux'
5+
import SonglistActions from '../Redux/SonglistRedux'
66
import ReduxPersist from '../Config/ReduxPersist'
77

88
// Styles
@@ -11,9 +11,12 @@ import styles from './Styles/RootContainerStyles'
1111
class RootContainer extends Component {
1212

1313
componentDidMount () {
14-
// if redux persist is not active fire startup action
14+
// if redux persist is not active fire action to scan files
1515
if (!ReduxPersist.active) {
16-
this.props.startup()
16+
const { scanFiles } = this.props
17+
if (scanFiles) {
18+
scanFiles()
19+
}
1720
}
1821
}
1922

@@ -29,7 +32,7 @@ class RootContainer extends Component {
2932

3033
// wraps dispatch to create nicer functions to call within our component
3134
const mapDispatchToProps = (dispatch) => ({
32-
startup: () => dispatch(StartupActions.startup())
35+
scanFiles: () => dispatch(SonglistActions.scanFiles())
3336
})
3437

3538
export default connect(null, mapDispatchToProps)(RootContainer)

App/Redux/SonglistRedux.js

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { createReducer, createActions } from 'reduxsauce'
2+
import Immutable from 'seamless-immutable'
3+
4+
/* ------------- Types and Action Creators ------------- */
5+
6+
const { Types, Creators } = createActions({
7+
scanFiles: null,
8+
songlistAdd: ['songs']
9+
})
10+
11+
export const SonglistTypes = Types
12+
export default Creators
13+
14+
export const INITIAL_STATE = Immutable({
15+
songs: [],
16+
genres: []
17+
})
18+
19+
let uniqueItems = songs => songs.filter((elem, pos, arr) => arr.indexOf(elem) === pos)
20+
21+
export const scan = (state, { songs }) => state.merge({ scanning: true })
22+
23+
export const songlistAdd = (state, { songs }) => {
24+
const genres = uniqueItems(songs.map(song => song.metadata.genre))
25+
return state.merge({ songs, genres, scanning: false })
26+
}
27+
28+
export const reducer = createReducer(INITIAL_STATE, {
29+
[Types.SONGLIST_ADD]: songlistAdd,
30+
[Types.SCAN_FILES]: scan
31+
})

App/Redux/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export default () => {
77
const rootReducer = combineReducers({
88
github: require('./GithubRedux').reducer,
99
login: require('./LoginRedux').reducer,
10-
search: require('./SearchRedux').reducer
10+
search: require('./SearchRedux').reducer,
11+
songs: require('./SonglistRedux').reducer
1112
})
1213

1314
return configureStore(rootReducer, rootSaga)

App/Sagas/SonglistSagas.js

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { put, call } from 'redux-saga/effects'
2+
import SonglistActions from '../Redux/SonglistRedux'
3+
import RNFS from 'react-native-fs'
4+
import MediaMeta from 'react-native-media-meta'
5+
6+
// config
7+
const MEDIA_DIR = '/mnt/sdcard/Music'
8+
// functions
9+
const getFolders = (dir) => {
10+
return RNFS.readDir(dir).then(
11+
result => result
12+
.filter(item => item.isDirectory())
13+
.map(item => item.path)
14+
)
15+
}
16+
17+
const getFiles = (dir) => {
18+
return RNFS.readDir(dir).then(
19+
result => result
20+
.filter(item => !item.isDirectory())
21+
.map(item => item.path)
22+
)
23+
}
24+
25+
async function getFoldersRecursive (dir, directories) {
26+
const subfolders = await getFolders(dir)
27+
return Promise.all(subfolders.map(folder => {
28+
directories.push(folder)
29+
return getFoldersRecursive(folder, directories)
30+
}))
31+
}
32+
33+
async function getAllFiles (allDirectories, allFiles) {
34+
for (let i = 0; i < allDirectories.length; i++) {
35+
const files = await getFiles(allDirectories[i])
36+
files.map(file => allFiles.push(file))
37+
}
38+
}
39+
40+
async function addMetadata (allFiles, resultMetadata) {
41+
for (let i = 0; i < allFiles.length; i++) {
42+
const file = allFiles[i]
43+
const metadata = await MediaMeta.get(file)
44+
if (metadata) {
45+
resultMetadata.push({file, metadata})
46+
}
47+
}
48+
}
49+
50+
export function * scanFiles (action) {
51+
const all = []
52+
const allfiles = []
53+
const allMeta = []
54+
try {
55+
yield call(getFoldersRecursive, MEDIA_DIR, all)
56+
yield call(getAllFiles, all, allfiles)
57+
yield call(addMetadata, allfiles, allMeta)
58+
console.tron.display({
59+
name: 'GOT FILES',
60+
preview: 'folders:' + all.length + ' files: ' + allfiles.length,
61+
value: allMeta
62+
})
63+
if (allfiles) {
64+
yield put(SonglistActions.songlistAdd(allMeta))
65+
}
66+
} catch (error) {
67+
console.tron.display({
68+
name: 'Failed to read data',
69+
preview: `folder:${MEDIA_DIR}`,
70+
value: allMeta
71+
})
72+
}
73+
}

App/Sagas/StartupSagas.js

-40
This file was deleted.

App/Sagas/index.js

+3-23
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,18 @@
11
import { takeLatest } from 'redux-saga'
2-
import API from '../Services/Api'
3-
import FixtureAPI from '../Services/FixtureApi'
4-
import DebugConfig from '../Config/DebugConfig'
52

63
/* ------------- Types ------------- */
74

8-
import { StartupTypes } from '../Redux/StartupRedux'
9-
import { GithubTypes } from '../Redux/GithubRedux'
10-
import { LoginTypes } from '../Redux/LoginRedux'
11-
import { OpenScreenTypes } from '../Redux/OpenScreenRedux'
5+
import { SonglistTypes } from '../Redux/SonglistRedux'
126

137
/* ------------- Sagas ------------- */
148

15-
import { startup } from './StartupSagas'
16-
import { login } from './LoginSagas'
17-
import { getUserAvatar } from './GithubSagas'
18-
import { openScreen } from './OpenScreenSagas'
19-
20-
/* ------------- API ------------- */
21-
22-
// The API we use is only used from Sagas, so we create it here and pass along
23-
// to the sagas which need it.
24-
const api = DebugConfig.useFixtures ? FixtureAPI : API.create()
9+
import { scanFiles } from './SonglistSagas'
2510

2611
/* ------------- Connect Types To Sagas ------------- */
2712

2813
export default function * root () {
2914
yield [
3015
// some sagas only receive an action
31-
takeLatest(StartupTypes.STARTUP, startup),
32-
takeLatest(LoginTypes.LOGIN_REQUEST, login),
33-
takeLatest(OpenScreenTypes.OPEN_SCREEN, openScreen),
34-
35-
// some sagas receive extra parameters in addition to an action
36-
takeLatest(GithubTypes.USER_REQUEST, getUserAvatar, api)
16+
takeLatest(SonglistTypes.SCAN_FILES, scanFiles)
3717
]
3818
}

App/Services/RehydrationServices.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import ReduxPersist from '../Config/ReduxPersist'
22
import { AsyncStorage } from 'react-native'
33
import { persistStore } from 'redux-persist'
4-
import StartupActions from '../Redux/StartupRedux'
4+
import SonglistActions from '../Redux/SonglistRedux'
55

66
const updateReducers = (store: Object) => {
77
const reducerVersion = ReduxPersist.reducerVersion
88
const config = ReduxPersist.storeConfig
9-
const startup = () => store.dispatch(StartupActions.startup())
9+
const scanFiles = () => store.dispatch(SonglistActions.scanFiles())
1010

1111
// Check to ensure latest reducer version
1212
AsyncStorage.getItem('reducerVersion').then((localVersion) => {
@@ -21,13 +21,13 @@ const updateReducers = (store: Object) => {
2121
important: true
2222
})
2323
// Purge store
24-
persistStore(store, config, startup).purge()
24+
persistStore(store, config, scanFiles).purge()
2525
AsyncStorage.setItem('reducerVersion', reducerVersion)
2626
} else {
27-
persistStore(store, config, startup)
27+
persistStore(store, config, scanFiles)
2828
}
2929
}).catch(() => {
30-
persistStore(store, config, startup)
30+
persistStore(store, config, scanFiles)
3131
AsyncStorage.setItem('reducerVersion', reducerVersion)
3232
})
3333
}

Tests/Sagas/StartupSagaTest.js

-13
This file was deleted.

android/app/build.gradle

+3
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ android {
126126
}
127127

128128
dependencies {
129+
compile project(':react-native-fs')
130+
compile project(':react-native-sound')
131+
compile project(':react-native-media-meta')
129132
compile project(':react-native-i18n')
130133
compile project(':react-native-vector-icons')
131134
compile project(':react-native-device-info')

android/app/src/main/java/com/musicplayer/MainApplication.java

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import android.app.Application;
44

55
import com.facebook.react.ReactApplication;
6+
import com.rnfs.RNFSPackage;
7+
import com.zmxv.RNSound.RNSoundPackage;
8+
import com.mybigday.rnmediameta.RNMediaMetaPackage;
69
import com.i18n.reactnativei18n.ReactNativeI18n;
710
import com.oblador.vectoricons.VectorIconsPackage;
811
import com.learnium.RNDeviceInfo.RNDeviceInfo;
@@ -27,6 +30,9 @@ public boolean getUseDeveloperSupport() {
2730
protected List<ReactPackage> getPackages() {
2831
return Arrays.<ReactPackage>asList(
2932
new MainReactPackage(),
33+
new RNFSPackage(),
34+
new RNSoundPackage(),
35+
new RNMediaMetaPackage(),
3036
new ReactNativeI18n(),
3137
new VectorIconsPackage(),
3238
new RNDeviceInfo(),

android/settings.gradle

+6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
rootProject.name = 'musicplayer'
2+
include ':react-native-fs'
3+
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
4+
include ':react-native-sound'
5+
project(':react-native-sound').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sound/android')
6+
include ':react-native-media-meta'
7+
project(':react-native-media-meta').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-media-meta/android')
28
include ':react-native-i18n'
39
project(':react-native-i18n').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-i18n/android')
410
include ':react-native-vector-icons'

0 commit comments

Comments
 (0)