Support loading multiple files at once

This commit is contained in:
Jordan Eldredge 2017-12-15 14:20:35 -08:00
parent 2c0c54102f
commit 302ea6728b
8 changed files with 92 additions and 38 deletions

View file

@ -20,7 +20,7 @@ import {
} from "./utils";
import {
CLOSE_WINAMP,
LOAD_AUDIO_URL,
ADD_TRACK_FROM_URL,
OPEN_FILE_DIALOG,
SEEK_TO_PERCENT_COMPLETE,
SET_BALANCE,
@ -44,9 +44,11 @@ import {
SET_TRACK_ORDER,
TOGGLE_VISUALIZER_STYLE,
PLAY_TRACK,
BUFFER_TRACK,
SET_PLAYLIST_SCROLL_POSITION,
DRAG_SELECTED,
SET_MEDIA_TAGS
SET_MEDIA_TAGS,
SET_MEDIA_DURATION
} from "./actionTypes";
function playRandomTrack() {
@ -168,9 +170,18 @@ function setEqFromFile(file) {
});
};
}
export function loadFilesFromReferences(fileReferences) {
return dispatch => {
Array.from(fileReferences).forEach((file, i) => {
dispatch(loadFileFromReference(file, i === 0 ? "PLAY" : "NONE"));
});
};
}
const SKIN_FILENAME_MATCHER = new RegExp("(wsz|zip)$", "i");
const EQF_FILENAME_MATCHER = new RegExp("eqf$", "i");
export function loadFileFromReference(fileReference) {
export function loadFileFromReference(fileReference, priority) {
return dispatch => {
const file = new MyFile();
file.setFileReference(fileReference);
@ -181,25 +192,54 @@ export function loadFileFromReference(fileReference) {
} else {
const id = uniqueId();
const url = URL.createObjectURL(fileReference);
dispatch(_loadMediaFromUrl(url, fileReference.name, true, id));
dispatch(_addTrackFromUrl(url, fileReference.name, id, priority));
dispatch(fetchMediaTags(fileReference, id));
}
};
}
export function fetchMediaDuration(url, id) {
return dispatch => {
// TODO: Does this actually stop downloading the file once it's
// got the duration?
const audio = document.createElement("audio");
const durationChange = () => {
const { duration } = audio;
dispatch({ type: SET_MEDIA_DURATION, duration, id });
audio.removeEventListener("durationchange", durationChange);
};
audio.addEventListener("durationchange", durationChange);
audio.src = url;
};
}
let counter = 0;
function uniqueId() {
return counter++;
}
export function _loadMediaFromUrl(url, name, autoPlay, id) {
return { type: LOAD_AUDIO_URL, url, name, autoPlay, id };
export function _addTrackFromUrl(url, name, id, priority) {
return dispatch => {
dispatch({ type: ADD_TRACK_FROM_URL, url, name, id });
switch (priority) {
case "BUFFER":
dispatch({ type: BUFFER_TRACK, name, id });
break;
case "PLAY":
dispatch({ type: PLAY_TRACK, name, id });
break;
default:
// If we're not going to load this right away,
// we should fetch duration on our own
dispatch(fetchMediaDuration(url, id));
}
};
}
export function loadMediaFromUrl(url, name, autoPlay) {
export function loadMediaFromUrl(url, name, priority) {
return dispatch => {
const id = uniqueId();
dispatch(_loadMediaFromUrl(url, name, autoPlay, id));
dispatch(_addTrackFromUrl(url, name, id, priority));
dispatch(fetchMediaTags(url, id));
};
}

View file

@ -1,4 +1,4 @@
export const LOAD_AUDIO_URL = "LOAD_AUDIO_URL";
export const ADD_TRACK_FROM_URL = "ADD_TRACK_FROM_URL";
export const CLOSE_WINAMP = "CLOSE_WINAMP";
export const CLOSE_EQUALIZER_WINDOW = "CLOSE_EQUALIZER_WINDOW";
export const IS_PLAYING = "IS_PLAYING";
@ -56,5 +56,7 @@ export const REVERSE_LIST = "REVERSE_LIST";
export const RANDOMIZE_LIST = "RANDOMIZE_LIST";
export const SET_TRACK_ORDER = "SET_TRACK_ORDER";
export const PLAY_TRACK = "PLAY_TRACK";
export const BUFFER_TRACK = "BUFFER_TRACK";
export const DRAG_SELECTED = "DRAG_SELECTED";
export const SET_MEDIA_TAGS = "SET_MEDIA_TAGS";
export const SET_MEDIA_DURATION = "SET_MEDIA_DURATION";

View file

@ -1,7 +1,7 @@
import React from "react";
import { connect } from "react-redux";
import { loadFileFromReference } from "../actionCreators";
import { loadFilesFromReferences } from "../actionCreators";
export class DropTarget extends React.Component {
constructor(props) {
@ -17,14 +17,12 @@ export class DropTarget extends React.Component {
handleDrop(e) {
this.supress(e);
const { files } = e.dataTransfer;
if (files[0]) {
this.props.loadFileFromReference(files[0]);
}
this.props.loadFilesFromReferences(files);
}
render() {
// eslint-disable-next-line no-shadow, no-unused-vars
const { loadFileFromReference, ...passThroughProps } = this.props;
const { loadFilesFromReferences, ...passThroughProps } = this.props;
return (
<div
{...passThroughProps}
@ -36,4 +34,4 @@ export class DropTarget extends React.Component {
}
}
export default connect(null, { loadFileFromReference })(DropTarget);
export default connect(null, { loadFilesFromReferences })(DropTarget);

View file

@ -21,7 +21,7 @@ describe("PlaylistShade", () => {
});
it("renders to snapshot", () => {
store.dispatch(loadMediaFromUrl("http://example.com", "Some Name", false));
store.dispatch(loadMediaFromUrl("http://example.com", "Some Name", "NONE"));
const tree = renderer
.create(
<Provider store={store}>

View file

@ -1,7 +1,6 @@
import {
IS_PLAYING,
IS_STOPPED,
LOAD_AUDIO_URL,
PAUSE,
PLAY,
SEEK_TO_PERCENT_COMPLETE,
@ -15,7 +14,8 @@ import {
UPDATE_TIME_ELAPSED,
SET_EQ_OFF,
SET_EQ_ON,
PLAY_TRACK
PLAY_TRACK,
BUFFER_TRACK
} from "./actionTypes";
import { next as nextTrack } from "./actionCreators";
import { getCurrentTrackId } from "./selectors";
@ -84,12 +84,19 @@ export default media => store => {
case SEEK_TO_PERCENT_COMPLETE:
media.seekToPercentComplete(action.percent);
break;
case LOAD_AUDIO_URL:
media.loadFromUrl(action.url, action.name, action.autoPlay);
break;
case PLAY_TRACK:
const track = store.getState().playlist.tracks[action.id];
media.loadFromUrl(track.url, track.title, true);
media.loadFromUrl(
store.getState().playlist.tracks[action.id].url,
action.name,
true
);
break;
case BUFFER_TRACK:
media.loadFromUrl(
store.getState().playlist.tracks[action.id].url,
action.name,
false
);
break;
case SET_BAND_VALUE:
if (action.band === "preamp") {

View file

@ -38,7 +38,7 @@ import {
PLAYLIST_SIZE_CHANGED,
SET_AVALIABLE_SKINS,
LOAD_AUDIO_FILE,
LOAD_AUDIO_URL
ADD_TRACK_FROM_URL
} from "../actionTypes";
import playlist from "./playlist";
@ -232,7 +232,7 @@ const media = (state, action) => {
case UPDATE_TIME_ELAPSED:
return { ...state, timeElapsed: action.elapsed };
case LOAD_AUDIO_FILE:
case LOAD_AUDIO_URL:
case ADD_TRACK_FROM_URL:
return {
...state,
timeElapsed: 0,

View file

@ -8,13 +8,15 @@ import {
INVERT_SELECTION,
REMOVE_ALL_TRACKS,
REMOVE_TRACKS,
LOAD_AUDIO_URL,
ADD_TRACK_FROM_URL,
REVERSE_LIST,
RANDOMIZE_LIST,
SET_TRACK_ORDER,
PLAY_TRACK,
BUFFER_TRACK,
DRAG_SELECTED,
SET_MEDIA_TAGS
SET_MEDIA_TAGS,
SET_MEDIA_DURATION
} from "../actionTypes";
import { shuffle, moveSelected, mapObject, filterObject } from "../utils";
@ -128,11 +130,10 @@ const playlist = (state = defaultPlaylistState, action) => {
case SET_TRACK_ORDER:
const { trackOrder } = action;
return { ...state, trackOrder };
case LOAD_AUDIO_URL:
case ADD_TRACK_FROM_URL:
return {
...state,
trackOrder: [...state.trackOrder, Number(action.id)],
currentTrack: action.id,
tracks: {
...state.tracks,
[action.id]: {
@ -168,7 +169,19 @@ const playlist = (state = defaultPlaylistState, action) => {
}
}
};
case SET_MEDIA_DURATION:
return {
...state,
tracks: {
...state.tracks,
[action.id]: {
...state.tracks[action.id],
duration: action.duration
}
}
};
case PLAY_TRACK:
case BUFFER_TRACK:
return {
...state,
currentTrack: action.id

View file

@ -9,7 +9,7 @@ import Media from "./media";
import {
setSkinFromUrl,
loadMediaFromUrl,
loadFileFromReference
loadFilesFromReferences
} from "./actionCreators";
import { SET_AVALIABLE_SKINS } from "./actionTypes";
@ -34,9 +34,7 @@ class Winamp {
);
this.fileInput.addEventListener("change", e => {
if (e.target.files[0]) {
this.store.dispatch(loadFileFromReference(e.target.files[0], true));
}
this.store.dispatch(loadFilesFromReferences(e.target.files));
});
if (this.options.initialTrack && this.options.initialTrack.url) {
@ -44,7 +42,7 @@ class Winamp {
loadMediaFromUrl(
this.options.initialTrack.url,
this.options.initialTrack.name,
false
"BUFFER"
)
);
}
@ -58,10 +56,6 @@ class Winamp {
new Hotkeys(this.fileInput, this.store);
}
loadTrackUrl(url, name) {
this.store.dispatch(loadMediaFromUrl(url, name));
}
}
export default Winamp;