From f56af6402c4e6f140567839c5b304e7859b5a06e Mon Sep 17 00:00:00 2001 From: DC Date: Tue, 22 Mar 2016 03:37:27 -0700 Subject: [PATCH] Audio metadata --- package.json | 5 ++++- renderer/index.css | 28 +++++++++++++++++++++++++++ renderer/index.js | 11 +++++++++++ renderer/views/player.js | 42 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index c2aa8333..88cc6f08 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "hyperx": "^2.0.2", "main-loop": "^3.2.0", "mkdirp": "^0.5.1", + "musicmetadata": "^2.0.2", "network-address": "^1.1.0", "prettier-bytes": "^1.0.1", "upload-element": "^1.0.1", @@ -30,7 +31,6 @@ "winreg": "^1.0.1" }, "devDependencies": { - "appdmg": "^0.3.6", "electron-osx-sign": "^0.3.0", "electron-packager": "^5.0.0", "electron-prebuilt": "0.37.2", @@ -40,6 +40,9 @@ "rimraf": "^2.5.2", "standard": "^6.0.5" }, + "optionalDependencies": { + "appdmg": "^0.3.6" + }, "homepage": "https://webtorrent.io", "keywords": [ "electron", diff --git a/renderer/index.css b/renderer/index.css index b6528428..d9e6400b 100644 --- a/renderer/index.css +++ b/renderer/index.css @@ -730,6 +730,34 @@ body.drag .torrent-placeholder span { font-weight: bold; } +/* + * AUDIO DETAILS + */ + +.audio-metadata { + width: 500px; + max-width: 100%; + white-space: nowrap; + text-overflow: ellipsis; + align-self: center; + margin: 0 auto; + font-weight: bold; + font-size: 24px; + line-height: 2; +} + +.audio-metadata .audio-title { + font-size: 32px; +} + +.audio-metadata label { + display:inline-block; + width: 100px; + text-align: right; + font-weight: normal; + margin-right: 25px; +} + /* * ERRORS */ diff --git a/renderer/index.js b/renderer/index.js index 9d532192..2763be5f 100644 --- a/renderer/index.js +++ b/renderer/index.js @@ -8,6 +8,7 @@ var EventEmitter = require('events') var fs = require('fs') var mainLoop = require('main-loop') var mkdirp = require('mkdirp') +var musicmetadata = require('musicmetadata') var networkAddress = require('network-address') var path = require('path') var remote = require('remote') @@ -609,7 +610,17 @@ function startServerFromReadyTorrent (torrent, index, cb) { state.playing.infoHash = torrent.infoHash state.playing.fileIndex = index state.playing.type = TorrentPlayer.isVideo(file) ? 'video' : 'audio' + state.playing.audioInfo = null + // if it's audio, parse out the metadata (artist, title, etc) + musicmetadata(file.createReadStream(), function (err, info) { + if (err) return + console.log('Got audio metadata for %s: %v', file.name, info) + state.playing.audioInfo = info + update() + }) + + // either way, start a streaming torrent-to-http server var server = torrent.createServer() server.listen(0, function () { var port = server.address().port diff --git a/renderer/views/player.js b/renderer/views/player.js index 4dc7c69f..c769ceef 100644 --- a/renderer/views/player.js +++ b/renderer/views/player.js @@ -21,6 +21,8 @@ function Player (state, dispatch) { } function renderMedia (state, dispatch) { + if (!state.server) return + // Unfortunately, play/pause can't be done just by modifying HTML. // Instead, grab the DOM node and play/pause it if necessary var mediaType = state.playing.type /* 'audio' or 'video' */ @@ -57,8 +59,9 @@ function renderMedia (state, dispatch) { // Show the media. // Video fills the window, centered with black bars if necessary // Audio gets a static poster image and a summary of the file metadata. + var isAudio = mediaType === 'audio' var style = { - backgroundImage: mediaType === 'audio' ? cssBackgroundImagePoster(state) : '' + backgroundImage: isAudio ? cssBackgroundImagePoster(state) : '' } return hx`
dispatch('mediaMouseMoved')}> ${mediaTag} + ${renderAudioMetadata(state)}
` @@ -86,6 +90,34 @@ function renderMedia (state, dispatch) { } } +function renderAudioMetadata (state) { + if (!state.playing.audioInfo) return + var info = state.playing.audioInfo + + // Get audio track info + var title = info.title + if (!title) { + var torrentSummary = getPlayingTorrentSummary(state) + title = torrentSummary.files[state.playing.fileIndex].name + } + var artist = info.artist && info.artist[0] + var album = info.album + if (album && info.year && !album.includes(info.year)) { + album += ' (' + info.year + ')' + } + var track + if (info.track && info.track.no && info.track.of) { + track = info.track.no + ' of ' + info.track.of + } + + // Show a small info box in the middle of the screen + var elems = [hx`
${title}
`] + if (artist) elems.push(hx`
${artist}
`) + if (album) elems.push(hx`
${album}
`) + if (track) elems.push(hx`
${track}
`) + return hx`
${elems}
` +} + function renderCastScreen (state, dispatch) { var isChromecast = state.playing.location.startsWith('chromecast') var isAirplay = state.playing.location.startsWith('airplay') @@ -112,8 +144,7 @@ function renderCastScreen (state, dispatch) { // Returns the CSS background-image string for a poster image + dark vignette function cssBackgroundImagePoster (state) { - var infoHash = state.playing.infoHash - var torrentSummary = state.saved.torrents.find((x) => x.infoHash === infoHash) + var torrentSummary = getPlayingTorrentSummary(state) if (!torrentSummary || !torrentSummary.posterURL) return '' var cleanURL = torrentSummary.posterURL.replace(/\\/g, '/') return 'radial-gradient(circle at center, ' + @@ -121,6 +152,11 @@ function cssBackgroundImagePoster (state) { `, url(${cleanURL})` } +function getPlayingTorrentSummary (state) { + var infoHash = state.playing.infoHash + return state.saved.torrents.find((x) => x.infoHash === infoHash) +} + function renderPlayerControls (state, dispatch) { var positionPercent = 100 * state.playing.currentTime / state.playing.duration var playbackCursorStyle = { left: 'calc(' + positionPercent + '% - 8px)' }