mirror of
https://github.com/captbaritone/webamp.git
synced 2026-08-02 23:13:20 +00:00
Get playlist html working
This commit is contained in:
parent
ed3f6666b2
commit
067fc7d280
4 changed files with 190 additions and 8 deletions
|
|
@ -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");
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 => (
|
|||
</ContextMenu>
|
||||
);
|
||||
|
||||
const ConnectedSortcontextMenu = connect(null, {
|
||||
const ConnectedSortContextMenu = connect(null, {
|
||||
reverseList,
|
||||
randomizeList,
|
||||
sortListByTitle
|
||||
})(SortContextMenu);
|
||||
|
||||
const MiscOptionsContextMenu = props => (
|
||||
<ContextMenu style={{ width: "100%", height: "100%" }} top handle={<div />}>
|
||||
<Node onClick={props.downloadHtmlPlaylist} label="Generate HTML playlist" />
|
||||
</ContextMenu>
|
||||
);
|
||||
|
||||
const ConnectedMiscOptionsContextMenu = connect(null, { downloadHtmlPlaylist })(
|
||||
MiscOptionsContextMenu
|
||||
);
|
||||
|
||||
const MiscMenu = () => (
|
||||
<PlaylistMenu id="playlist-misc-menu">
|
||||
<div className="sort-list" onClick={e => e.stopPropagation()}>
|
||||
<ConnectedSortcontextMenu />
|
||||
<ConnectedSortContextMenu />
|
||||
</div>
|
||||
<div
|
||||
className="file-info"
|
||||
onClick={() => alert("Not supported in Winamp2-js")}
|
||||
/>
|
||||
<div
|
||||
className="misc-options"
|
||||
onClick={() => alert("Not supported in Winamp2-js")}
|
||||
/>
|
||||
|
||||
<div className="misc-options" onClick={e => e.stopPropagation()}>
|
||||
<ConnectedMiscOptionsContextMenu />
|
||||
</div>
|
||||
</PlaylistMenu>
|
||||
);
|
||||
|
||||
|
|
|
|||
133
js/playlistHtml.js
Normal file
133
js/playlistHtml.js
Normal file
|
|
@ -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 <html> tag out to the string creation step in order
|
||||
// to avoid the warning.
|
||||
const Playlist = props => (
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="null" />
|
||||
<style type="text/css">
|
||||
{`
|
||||
body { background: #000040; }
|
||||
.para1 { margin-top: -42px; margin-left: 145px; margin-right: 10px; font-family: "font2, Arial"; font-size: 30px; line-height: 35px; text-align: left; color: #E1E1E1; }
|
||||
.para2 { margin-top: 15px; margin-left: 15px; margin-right: 50px; font-family: "font1, Arial Black"; font-size: 50px; line-height: 40px; text-align: left; color: #004080; }
|
||||
`}
|
||||
</style>
|
||||
<title>Winamp Generated PlayList</title>
|
||||
</head>
|
||||
<body bgcolor="#000080" topmargin="0" leftmargin="0" text="#FFFFFF">
|
||||
<div align="center">
|
||||
<div className="para2" align="center">
|
||||
<p>WINAMP</p>
|
||||
</div>
|
||||
<div className="para1" align="center">
|
||||
<p>playlist</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr
|
||||
align="left"
|
||||
width="90%"
|
||||
size="1"
|
||||
color="#FFBF00"
|
||||
style={noshadeStyle}
|
||||
/>
|
||||
<div align="right">
|
||||
<table border="0" cellSpacing="0" cellPadding="0" width="98%">
|
||||
{/* Added <tbody> tag */}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<small>
|
||||
<small>
|
||||
<font face="Arial" color="#FFBF00">
|
||||
{props.numberOfTracks}
|
||||
</font>
|
||||
<font color="#409FFF" face="Arial">
|
||||
{" track in playlist, average track length: "}
|
||||
</font>
|
||||
<font face="Arial" color="#FFBF00">
|
||||
{props.averageTrackLength}
|
||||
</font>
|
||||
</small>
|
||||
</small>
|
||||
<br />
|
||||
<small>
|
||||
<small>
|
||||
<font color="#409FFF" face="Arial">
|
||||
{"Playlist length: "}
|
||||
</font>
|
||||
<font face="Arial" color="#FFBF00">
|
||||
{props.playlistLengthMinutes}
|
||||
</font>
|
||||
<font color="#409FFF" face="Arial">
|
||||
{" minutes "}
|
||||
</font>
|
||||
<font face="Arial" color="#FFBF00">
|
||||
{props.playlistLengthSeconds}
|
||||
</font>
|
||||
<font color="#409FFF" face="Arial">
|
||||
{" second "}
|
||||
</font>
|
||||
<br />
|
||||
<font color="#409FFF" face="Arial">
|
||||
Right-click <a href="./">here</a> to save this HTML file.
|
||||
</font>
|
||||
</small>
|
||||
</small>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<blockquote>
|
||||
<p>
|
||||
<font color="#FFBF00" face="Arial">
|
||||
<big>Playlist files:</big>
|
||||
</font>
|
||||
{/* Added closing tag here */}
|
||||
</p>
|
||||
<ul>
|
||||
<font face="Arial" color="#FFFFFF">
|
||||
<small>
|
||||
{props.tracks.map(track => (
|
||||
<span key={track}>
|
||||
{track}
|
||||
<br />
|
||||
</span>
|
||||
))}
|
||||
{/* Added closing tag here */}
|
||||
</small>
|
||||
</font>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<hr
|
||||
align="left"
|
||||
width="90%"
|
||||
size="1"
|
||||
color="#FFBF00"
|
||||
style={noshadeStyle}
|
||||
/>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
const createPlaylistHTML = props => {
|
||||
const node = document.createElement("div");
|
||||
render(<Playlist {...props} />, node);
|
||||
return node.innerHTML;
|
||||
};
|
||||
|
||||
export const createPlaylistURL = props =>
|
||||
getAsDataURI(createPlaylistHTML(props));
|
||||
|
|
@ -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
|
||||
)})`
|
||||
)
|
||||
})
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue