From 067fc7d28061f80a2ac071b0da3daf9faa9430ef Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 10 Dec 2017 13:31:36 -0800 Subject: [PATCH] Get playlist html working --- js/actionCreators.js | 10 +- js/components/PlaylistWindow/MiscMenu.js | 26 +++-- js/playlistHtml.js | 133 +++++++++++++++++++++++ js/selectors.js | 29 +++++ 4 files changed, 190 insertions(+), 8 deletions(-) create mode 100644 js/playlistHtml.js diff --git a/js/actionCreators.js b/js/actionCreators.js index e061eea5..5bbf9f4b 100644 --- a/js/actionCreators.js +++ b/js/actionCreators.js @@ -7,7 +7,8 @@ import { getEqfData, nextTrack, getScrollOffset, - getOverflowTrackCount + getOverflowTrackCount, + getPlaylistURL } from "./selectors"; import { @@ -423,3 +424,10 @@ export function dragSelected(offset) { } }; } + +export function downloadHtmlPlaylist() { + return (dispatch, getState) => { + const uri = getPlaylistURL(getState()); + downloadURI(uri, "Winamp Playlist.html"); + }; +} diff --git a/js/components/PlaylistWindow/MiscMenu.js b/js/components/PlaylistWindow/MiscMenu.js index 3f11ccd4..944ed2a7 100644 --- a/js/components/PlaylistWindow/MiscMenu.js +++ b/js/components/PlaylistWindow/MiscMenu.js @@ -3,8 +3,10 @@ import { connect } from "react-redux"; import { reverseList, randomizeList, - sortListByTitle + sortListByTitle, + downloadHtmlPlaylist } from "../../actionCreators"; + import PlaylistMenu from "./PlaylistMenu"; import { ContextMenu, Hr, Node } from "../ContextMenu"; @@ -19,25 +21,35 @@ const SortContextMenu = props => ( ); -const ConnectedSortcontextMenu = connect(null, { +const ConnectedSortContextMenu = connect(null, { reverseList, randomizeList, sortListByTitle })(SortContextMenu); +const MiscOptionsContextMenu = props => ( + }> + + +); + +const ConnectedMiscOptionsContextMenu = connect(null, { downloadHtmlPlaylist })( + MiscOptionsContextMenu +); + const MiscMenu = () => (
e.stopPropagation()}> - +
alert("Not supported in Winamp2-js")} /> -
alert("Not supported in Winamp2-js")} - /> + +
e.stopPropagation()}> + +
); diff --git a/js/playlistHtml.js b/js/playlistHtml.js new file mode 100644 index 00000000..2dda8e2a --- /dev/null +++ b/js/playlistHtml.js @@ -0,0 +1,133 @@ +import React from "react"; +import { render } from "react-dom"; + +export const getAsDataURI = text => + `data:text/html;base64,${window.btoa(text)}`; + +// Replaces deprecated "noshade" attribute +const noshadeStyle = { + height: "2px", + borderWidth: 0, + color: "gray", + backgroundColor: "gray" +}; + +// TODO: Move tag out to the string creation step in order +// to avoid the warning. +const Playlist = props => ( + + + + + Winamp Generated PlayList + + +
+
+

WINAMP

+
+
+

playlist

+
+
+
+
+ + {/* Added tag */} + + + + + +
+ + + + {props.numberOfTracks} + + + {" track in playlist, average track length: "} + + + {props.averageTrackLength} + + + +
+ + + + {"Playlist length: "} + + + {props.playlistLengthMinutes} + + + {" minutes "} + + + {props.playlistLengthSeconds} + + + {" second "} + +
+ + Right-click here to save this HTML file. + +
+
+
+
+
+

+ + Playlist files: + + {/* Added closing tag here */} +

+
    + + + {props.tracks.map(track => ( + + {track} +
    +
    + ))} + {/* Added closing tag here */} +
    +
    +
+
+
+ + +); + +const createPlaylistHTML = props => { + const node = document.createElement("div"); + render(, node); + return node.innerHTML; +}; + +export const createPlaylistURL = props => + getAsDataURI(createPlaylistHTML(props)); diff --git a/js/selectors.js b/js/selectors.js index dc9a7857..c56505c6 100644 --- a/js/selectors.js +++ b/js/selectors.js @@ -4,6 +4,7 @@ import { PLAYLIST_RESIZE_SEGMENT_HEIGHT, TRACK_HEIGHT } from "./constants"; +import { createPlaylistURL } from "./playlistHtml"; import { createSelector } from "reselect"; import * as fromPlaylist from "./reducers/playlist"; @@ -156,3 +157,31 @@ export const getMediaText = createSelector( (trackNumber, name, duration) => `${trackNumber}. ${name} (${getTimeStr(duration)}) *** ` ); + +const getNumberOfTracks = state => getTrackOrder(state).length; +const getPlaylistDuration = createSelector(getTracks, tracks => + Object.values(tracks).reduce((total, track) => total + track.duration, 0) +); + +// TODO: Move to action creator +export const getPlaylistURL = createSelector( + state => state, + getNumberOfTracks, + getPlaylistDuration, + getTrackOrder, + getTracks, + (state, numberOfTracks, playlistDuration, trackOrder, tracks) => + createPlaylistURL({ + numberOfTracks, + averageTrackLength: getTimeStr(playlistDuration / numberOfTracks), + // TODO: Handle hours + playlistLengthMinutes: Math.floor(playlistDuration / 60), + playlistLengthSeconds: Math.floor(playlistDuration % 60), + tracks: trackOrder.map( + (id, i) => + `${i + 1}. ${getTrackDisplayName(state, id)} (${getTimeStr( + tracks[id].duration + )})` + ) + }) +);