Add infra for dropbox chooser and lazy id3 tags

This commit is contained in:
Jordan Eldredge 2018-02-28 16:31:19 -08:00
parent f429fdcd0b
commit 5836e9f542
6 changed files with 104 additions and 5 deletions

View file

@ -6,14 +6,20 @@ import {
promptForFileReferences
} from "./fileUtils";
import skinParser from "./skinParser";
import { BANDS, TRACK_HEIGHT, LOAD_STYLE } from "./constants";
import {
BANDS,
TRACK_HEIGHT,
LOAD_STYLE,
MEDIA_TAG_REQUEST_STATUS
} from "./constants";
import {
getEqfData,
nextTrack,
getScrollOffset,
getOverflowTrackCount,
getPlaylistURL,
getSelectedTrackObjects
getSelectedTrackObjects,
getVisibleTracks
} from "./selectors";
import {
@ -53,7 +59,9 @@ import {
SET_MEDIA_TAGS,
SET_MEDIA_DURATION,
TOGGLE_SHADE_MODE,
TOGGLE_PLAYLIST_SHADE_MODE
TOGGLE_PLAYLIST_SHADE_MODE,
MEDIA_TAG_REQUEST_INITIALIZED,
MEDIA_TAG_REQUEST_FAILED
} from "./actionTypes";
function playRandomTrack() {
@ -311,12 +319,27 @@ export function loadMediaFile(track, priority = null, atIndex = 0) {
};
}
export function fetchMediaTagsForVisibleTracks() {
return (dispatch, getState) => {
getVisibleTracks(getState())
.filter(
track =>
track.mediaTagsRequestStatus ===
MEDIA_TAG_REQUEST_STATUS.NOT_REQUESTED
)
.forEach(track => {
dispatch(fetchMediaTags(track.url, track.id));
});
};
}
export function fetchMediaTags(file, id) {
// Workaround https://github.com/aadsm/jsmediatags/issues/83
if (typeof file === "string" && !/^[a-z]+:\/\//i.test(file)) {
file = `${location.protocol}//${location.host}${location.pathname}${file}`;
}
return dispatch => {
dispatch({ type: MEDIA_TAG_REQUEST_INITIALIZED, id });
try {
jsmediatags.read(file, {
onSuccess: data => {
@ -326,6 +349,7 @@ export function fetchMediaTags(file, id) {
dispatch({ type: SET_MEDIA_TAGS, artist, title, id });
},
onError: () => {
dispatch({ type: MEDIA_TAG_REQUEST_FAILED, id });
// Nothing to do. The filename will have to suffice.
}
});
@ -333,6 +357,7 @@ export function fetchMediaTags(file, id) {
// Possibly jsmediatags could not find a parser for this file?
// Nothing to do.
// Consider removing this after https://github.com/aadsm/jsmediatags/issues/83 is resolved.
dispatch({ type: MEDIA_TAG_REQUEST_FAILED, id });
}
};
}
@ -390,6 +415,35 @@ export function openSkinFileDialog() {
return _openFileDialog(".zip, .wsz");
}
// Requires Dropbox's Chooser to be loaded on the page
function genAudioFileUrlsFromDropbox() {
return new Promise((resolve, reject) => {
if (window.Dropbox == null) {
reject();
}
window.Dropbox.choose({
success: resolve,
error: reject,
linkType: "direct",
folderselect: false,
multiselect: true,
extensions: ["video", "audio"]
});
});
}
export function openDropboxFileDialog() {
return async dispatch => {
const files = await genAudioFileUrlsFromDropbox();
dispatch(
loadMediaFiles(
files.map(file => ({ url: file.link, defaultName: file.name })),
LOAD_STYLE.PLAY
)
);
};
}
export function setEqBand(band, value) {
return { type: SET_BAND_VALUE, band, value };
}

View file

@ -60,3 +60,5 @@ export const SET_MEDIA_TAGS = "SET_MEDIA_TAGS";
export const SET_MEDIA_DURATION = "SET_MEDIA_DURATION";
export const OPEN_GEN_WINDOW = "OPEN_GEN_WINDOW";
export const CLOSE_GEN_WINDOW = "CLOSE_GEN_WINDOW";
export const MEDIA_TAG_REQUEST_INITIALIZED = "MEDIA_TAG_REQUEST_INITIALIZED";
export const MEDIA_TAG_REQUEST_FAILED = "MEDIA_TAG_REQUEST_FAILED";

View file

@ -11,6 +11,13 @@ export const LOAD_STYLE = {
PLAY: "PLAY"
};
export const MEDIA_TAG_REQUEST_STATUS = {
INITIALIZED: "INITIALIZED",
FAILED: "FAILED",
COMPLETE: "COMPLETE",
NOT_REQUESTED: "NOT_REQUESTED"
};
export const UTF8_ELLIPSIS = "\u2026";
export const CHARACTER_WIDTH = 5;
export const PLAYLIST_RESIZE_SEGMENT_WIDTH = 25;

View file

@ -16,8 +16,11 @@ import {
BUFFER_TRACK,
DRAG_SELECTED,
SET_MEDIA_TAGS,
SET_MEDIA_DURATION
SET_MEDIA_DURATION,
MEDIA_TAG_REQUEST_INITIALIZED,
MEDIA_TAG_REQUEST_FAILED
} from "../actionTypes";
import { MEDIA_TAG_REQUEST_STATUS } from "../constants";
import { filenameFromUrl } from "../fileUtils";
import { shuffle, moveSelected, mapObject, filterObject } from "../utils";
@ -148,7 +151,8 @@ const playlist = (state = defaultPlaylistState, action) => {
selected: false,
defaultName: action.defaultName,
duration: null,
url: action.url
url: action.url,
mediaTagsRequestStatus: MEDIA_TAG_REQUEST_STATUS.NOT_REQUESTED
}
},
// TODO: This could probably be made to work, but we clear it just to be safe.
@ -172,11 +176,34 @@ const playlist = (state = defaultPlaylistState, action) => {
...state.tracks,
[action.id]: {
...state.tracks[action.id],
mediaTagsRequestStatus: MEDIA_TAG_REQUEST_STATUS.COMPLETE,
title: action.title,
artist: action.artist
}
}
};
case MEDIA_TAG_REQUEST_INITIALIZED:
return {
...state,
tracks: {
...state.tracks,
[action.id]: {
...state.tracks[action.id],
mediaTagsRequestStatus: MEDIA_TAG_REQUEST_STATUS.INITIALIZED
}
}
};
case MEDIA_TAG_REQUEST_FAILED:
return {
...state,
tracks: {
...state.tracks,
[action.id]: {
...state.tracks[action.id],
mediaTagsRequestStatus: MEDIA_TAG_REQUEST_STATUS.FAILED
}
}
};
case SET_MEDIA_DURATION:
return {
...state,

View file

@ -25,6 +25,7 @@ describe("playlist reducer", () => {
selected: false,
duration: null,
defaultName: "My Track Name",
mediaTagsRequestStatus: "NOT_REQUESTED",
url: "url://some-url"
}
},
@ -54,6 +55,7 @@ describe("playlist reducer", () => {
100: {
selected: false,
duration: null,
mediaTagsRequestStatus: "NOT_REQUESTED",
defaultName: "My Track Name",
url: "url://some-url"
}
@ -85,6 +87,7 @@ describe("playlist reducer", () => {
100: {
selected: false,
duration: null,
mediaTagsRequestStatus: "NOT_REQUESTED",
defaultName: "My Track Name",
url: "url://some-url"
}

View file

@ -156,6 +156,12 @@ export const getVisibleTrackIds = createSelector(
trackOrder.slice(offset, offset + numberOfVisibleTracks)
);
export const getVisibleTracks = createSelector(
getVisibleTrackIds,
getTracks,
(visibleTrackIds, tracks) => visibleTrackIds.map(id => tracks[id])
);
export const getPlaylist = state => state.playlist;
export const getDuration = state => {