From 41862ffde6fe84677b096e1aea78826f12ebd4cb Mon Sep 17 00:00:00 2001
From: Jordan Eldredge
Date: Wed, 28 Feb 2018 16:25:07 -0800
Subject: [PATCH 01/24] Improve handling of x-origin duration fetching
---
js/actionCreators.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/js/actionCreators.js b/js/actionCreators.js
index 7fbee50e..0ef1940d 100644
--- a/js/actionCreators.js
+++ b/js/actionCreators.js
@@ -232,6 +232,7 @@ export function fetchMediaDuration(url, id) {
// TODO: Does this actually stop downloading the file once it's
// got the duration?
const audio = document.createElement("audio");
+ audio.crossOrigin = "anonymous";
const durationChange = () => {
const { duration } = audio;
dispatch({ type: SET_MEDIA_DURATION, duration, id });
From f429fdcd0b6b6cd36c364f9f7a157afab501f193 Mon Sep 17 00:00:00 2001
From: Jordan Eldredge
Date: Wed, 28 Feb 2018 16:26:42 -0800
Subject: [PATCH 02/24] Fix ordering when loading multiple files
---
js/actionCreators.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/js/actionCreators.js b/js/actionCreators.js
index 0ef1940d..85259a09 100644
--- a/js/actionCreators.js
+++ b/js/actionCreators.js
@@ -262,7 +262,7 @@ export function loadMediaFiles(tracks, loadStyle = null, atIndex = 0) {
}
tracks.forEach((track, i) => {
const priority = i === 0 && loadStyle != null ? loadStyle : null;
- dispatch(loadMediaFile(track, priority, atIndex));
+ dispatch(loadMediaFile(track, priority, atIndex + i));
});
};
}
From 5836e9f542d1ac51decf833c8bb6c6d0ce56ce18 Mon Sep 17 00:00:00 2001
From: Jordan Eldredge
Date: Wed, 28 Feb 2018 16:31:19 -0800
Subject: [PATCH 03/24] Add infra for dropbox chooser and lazy id3 tags
---
js/actionCreators.js | 60 ++++++++++++++++++++++++++++++++++--
js/actionTypes.js | 2 ++
js/constants.js | 7 +++++
js/reducers/playlist.js | 31 +++++++++++++++++--
js/reducers/playlist.test.js | 3 ++
js/selectors.js | 6 ++++
6 files changed, 104 insertions(+), 5 deletions(-)
diff --git a/js/actionCreators.js b/js/actionCreators.js
index 85259a09..64a5576a 100644
--- a/js/actionCreators.js
+++ b/js/actionCreators.js
@@ -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 };
}
diff --git a/js/actionTypes.js b/js/actionTypes.js
index 92d52697..5add53b5 100644
--- a/js/actionTypes.js
+++ b/js/actionTypes.js
@@ -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";
diff --git a/js/constants.js b/js/constants.js
index 309fe8e1..802e17d2 100644
--- a/js/constants.js
+++ b/js/constants.js
@@ -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;
diff --git a/js/reducers/playlist.js b/js/reducers/playlist.js
index 918f7ca1..4f5313a9 100644
--- a/js/reducers/playlist.js
+++ b/js/reducers/playlist.js
@@ -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,
diff --git a/js/reducers/playlist.test.js b/js/reducers/playlist.test.js
index 6a8fbbaf..bba91498 100644
--- a/js/reducers/playlist.test.js
+++ b/js/reducers/playlist.test.js
@@ -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"
}
diff --git a/js/selectors.js b/js/selectors.js
index c9d036aa..78789bac 100644
--- a/js/selectors.js
+++ b/js/selectors.js
@@ -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 => {
From b34289990ea8aa1ba560c76d1bf241a2017a6179 Mon Sep 17 00:00:00 2001
From: Jordan Eldredge
Date: Sun, 4 Mar 2018 19:19:24 -0800
Subject: [PATCH 04/24] Add changelog and howtogeek to press page
---
press.md | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
diff --git a/press.md b/press.md
index ab7b6a4d..0fb45b38 100644
--- a/press.md
+++ b/press.md
@@ -15,7 +15,7 @@
## News Aggregators
-* [/r/InternetIsBeautiful](https://www.reddit.com/r/InternetIsBeautiful/comments/2lh3ob/winamp_2_preserved_in_html5/)
+* [/r/InternetIsBeautiful](https://www.reddit.com/r/InternetIsBeautiful/comments/2lh3ob/winamp_2_preserved_in_html5/)
* [Hacker News](https://news.ycombinator.com/item?id=16333550) (Feb. 2018)
* [Hacker News](https://news.ycombinator.com/item?id=15314629) (Sept. 2017)
* [Hacker News](https://news.ycombinator.com/item?id=8565665) (Nov. 2014)
@@ -45,6 +45,7 @@
## Articles (continued)
### Russian
+
* http://altervision.org/2018/02/winamp-zapustili-v-vashem-brauzere/
* http://droider.ru/post/emulyator-dlya-nostalgii-po-winamp-09-02-2018/
* http://fornote.net/2018/02/winamp2-js-legendarny-j-winamp-dostupny-j-pryamo-v-brauzere/
@@ -62,6 +63,7 @@
* https://tech.onliner.by/2018/02/09/winamp-5
### Spanish
+
* http://datainfox.com/2018/02/lanzan-version-web-winamp/
* http://html5facil.com/noticias/winamp-web/
* http://omicrono.elespanol.com/2014/11/winamp-2-en-html5-es-nostalgia-pura/
@@ -81,6 +83,7 @@
* https://www.rocambola.com/winamp2-js-la-nueva-version-del-famoso-reproductor-en-formato-web/
### Chinese
+
* http://baijiahao.baidu.com/s?id=1592074295875163282
* http://blog.csdn.net/souhugirl/article/details/43907469
* http://m.sohu.com/a/221750379_549015
@@ -95,6 +98,7 @@
* https://www.waerfa.com/winamp2-js
### English
+
* http://entertainment.ie/trending/news/Remember-Winamp-Now-theres-a-web-browser-version-of-it/402141.htm
* http://newsvire.com/get-some-media-player-nostalgia-with-this-web-version-of-winamp/
* http://osxdaily.com/2018/02/10/run-winamp-web-browser/
@@ -103,8 +107,11 @@
* https://thenextweb.com/shareables/2018/02/09/bring-nostalgia-browser-winamp-emulator/
* https://www.follownews.com/winamp2js-is-a-webbased-version-of-audio-player-winamp-43icg
* https://www.gizmodo.com.au/2018/02/someone-remade-winamp-so-it-runs-in-your-browser/
+* https://changelog.com/news/winamp-29-in-htmljs--PJr
+* https://www.howtogeek.com/343813/re-live-90s-computing-in-your-browser-right-now/amp/
### German
+
* http://de.engadget.com/2018/02/09/retro-winamp-emulator-fur-deinen-browser/
* http://www.chip.de/news/Winamp-ist-zurueck-Kult-Player-ueber-Browser-nutzen_124413881.html
* http://www.intro.de/life/mit-winamp-zuruck-in-die-vergangenheit-nostalgische-browser-version-des-media-players
@@ -113,6 +120,7 @@
* https://www.smartdroid.de/winamp2-js-laesst-den-klassiker-im-browser-wieder-aufleben/
### Polish
+
* https://www.antyradio.pl/Technologia/Internet/Winamp-powrocil-jako-strona-internetowa-17497
* http://innpoland.pl/137663,kultowy-winamp-powraca-w-nowej-formie-uderza-tam-gdzie-jest-juz-spotify
* http://www.instalki.pl/aktualnosci/software/30055-winamp-przegladarka.html
@@ -120,6 +128,7 @@
* https://www.dobreprogramy.pl/Kultowy-odtwarzacz-muzyki-Winamp-powrocil-w-przegladarce-internetowej,News,86017.html
### French
+
* http://www.phonandroid.com/winamps-lecteur-multimedia-retour.html
* http://www.lemonde.fr/pixels/breve/2014/11/07/winamp-en-html_4520190_4408996.html
* https://korben.info/winamp-en-html-javascript.html
@@ -128,6 +137,7 @@
* https://www.macg.co/logiciels/2018/02/revivez-les-glorieuses-annees-winamp-avec-votre-navigateur-101345
### Italian
+
* https://tech.everyeye.it/notizie/il-ritorno-winamp-storico-player-riappare-in-una-versione-web-320373.html
* https://www.hwupgrade.it/news/web/winamp-arriva-su-qualsiasi-browser-web-nostalgici-gioiscono_74100.html
* https://www.wired.it/internet/web/2018/02/09/winamp-rinasce-sul-web/
@@ -135,47 +145,59 @@
* https://www.windowsblogitalia.com/2018/02/ascoltare-la-musica-edge-firefox-chrome-winamp2-js/
### Thai
+
* http://www.flashfly.net/wp/208175
* https://www.aripfan.com/bring-winamp-emulator-in-web-browser/
* https://www.beartai.com/news/itnews/221530
### Hungarian
-* http://hvg.hu/tudomany/20180212_winamp_zenelejatszo_letoltes_bongeszoben
+
+* http://hvg.hu/tudomany/20180212_winamp_zenelejatszo_letoltes_bongeszoben
* http://www.karpatinfo.net/cikk/tudomany/2567147-feltamadt-winamp-90-es-evek-kedvenc-zenelejatszoja-hasznalhatja
* https://www.gamestar.hu/hir/winamp-html5-verzio-243839.html
### Bosnian
+
* https://www.b92.net/tehnopolis/vesti.php?yyyy=2018&mm=02&nav_id=1357852
* https://www.benchmark.rs/vesti/novi_winamp_emulator_budi_nostalgiju_na_stara_vremena-74941
### Turkish
+
* http://www.bursateknikpc.com/winamp-2nin-html5-de-yazilmis-versiyonu-cikti/
* https://www.log.com.tr/winamp-efsanesini-yeniden-kullanima-sunan-site/
### Japanese
+
* http://jp.techcrunch.com/2018/02/10/2018-02-09-whip-the-llamas-ass-with-this-javascript-winamp-emulator/
* https://gigazine.net/news/20180213-winamp2-js/
* https://www.gizmodo.jp/2014/11/90winamp_2html5.html
### Portuguese
+
* http://abertoatedemadrugada.com/2018/02/winamp-completamente-funcional-em.html
* https://canaltech.com.br/musica/desenvolvedor-ressuscita-winamp-em-emulador-com-javascript-108112/
### Swedish
+
* http://gaffa.se/nyhet/125537/klassisk-musikspelare-nu-pa-webben/
* https://www.idg.se/2.1085/1.697529/mp3-winamp-webblasare
### Armenian
+
* http://www.armblog.net/2018/02/github-winamp.htm
### Dutch
+
* https://tweakers.net/geek/135087/ontwikkelaar-bouwt-winamp-29-in-javascript.html
### Romanian
+
* https://zonait.tv/va-era-dor-de-winamp/
### Czech
+
* https://www.zive.cz/clanky/zpatky-do-minulosti-winamp-muzete-pouzivat-primo-v-prohlizeci/sc-3-a-191743/default.aspx
### Croatian
+
* http://idesh.net/tech-i-web/internet/winamp-se-vratio-nostalgija-na-najjace/
From 4423bf2418e4b6d87846fdf09cf0be13eb2653cd Mon Sep 17 00:00:00 2001
From: Jordan Eldredge
Date: Mon, 5 Mar 2018 16:41:17 -0800
Subject: [PATCH 05/24] Add todo
---
js/media/elementSource.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/js/media/elementSource.js b/js/media/elementSource.js
index 61df4398..7c0c6235 100644
--- a/js/media/elementSource.js
+++ b/js/media/elementSource.js
@@ -69,6 +69,7 @@ export default class ElementSource {
}
// Async for now, for compatibility with BufferAudioSource
+ // TODO: This does not need to be async
async loadUrl(url) {
this._audio.src = url;
}
From 736c54e1ecb1401e3825718542dd337892cf0d5d Mon Sep 17 00:00:00 2001
From: Jordan Eldredge
Date: Fri, 9 Mar 2018 17:17:09 -0800
Subject: [PATCH 06/24] Allow keywords in dot notation
---
.eslintrc | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/.eslintrc b/.eslintrc
index 088ba4a4..7fb6d974 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -33,12 +33,7 @@
"camelcase": "error",
"consistent-return": "warn",
"constructor-super": "error",
- "dot-notation": [
- "error",
- {
- "allowKeywords": false
- }
- ],
+ "dot-notation": "error",
"eqeqeq": ["error", "smart"],
"guard-for-in": "error",
"max-depth": ["warn", 4],
From 700b7392cad613ccac38a624ead0c79afec276a2 Mon Sep 17 00:00:00 2001
From: Jordan Eldredge
Date: Fri, 9 Mar 2018 17:17:28 -0800
Subject: [PATCH 07/24] Ignore the skin museum
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index c06694f5..e199ab42 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ node_modules
/built
/coverage
+/experiments/winamp-skin-museum
From 33e33b2dec0fe83c1eea84fb162c18040649eb3e Mon Sep 17 00:00:00 2001
From: Jordan Eldredge
Date: Fri, 9 Mar 2018 20:32:55 -0800
Subject: [PATCH 08/24] Add dropbox support
---
index.html | 1 +
js/actionCreators.js | 139 +++++++++---------
js/components/MainWindow/MainContextMenu.js | 9 +-
.../PlaylistWindow/PlaylistResizeTarget.js | 6 +-
js/components/PlaylistWindow/index.js | 1 +
js/fileUtils.js | 69 +++++++++
js/loadQueue.js | 47 ++++++
js/reducers/playlist.js | 1 +
js/reducers/playlist.test.js | 19 ++-
js/utils.js | 12 ++
loadQueue.test.js | 20 +++
package.json | 2 +
yarn.lock | 10 ++
13 files changed, 250 insertions(+), 86 deletions(-)
create mode 100644 js/loadQueue.js
create mode 100644 loadQueue.test.js
diff --git a/index.html b/index.html
index bb573f08..ec82b657 100755
--- a/index.html
+++ b/index.html
@@ -29,6 +29,7 @@
Feedback |
GitHub
+