Merge pull request #1240 from Borewit/music-metadata
Fix playing back audio files & reading music metadata
@@ -32,7 +32,7 @@
|
|||||||
"location-history": "^1.0.0",
|
"location-history": "^1.0.0",
|
||||||
"material-ui": "^0.17.0",
|
"material-ui": "^0.17.0",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"musicmetadata": "^2.0.2",
|
"music-metadata": "^0.9.8",
|
||||||
"network-address": "^1.1.0",
|
"network-address": "^1.1.0",
|
||||||
"parse-torrent": "^5.7.3",
|
"parse-torrent": "^5.7.3",
|
||||||
"prettier-bytes": "^1.0.1",
|
"prettier-bytes": "^1.0.1",
|
||||||
|
|||||||
@@ -33,12 +33,18 @@ function isVideo (file) {
|
|||||||
function isAudio (file) {
|
function isAudio (file) {
|
||||||
return [
|
return [
|
||||||
'.aac',
|
'.aac',
|
||||||
|
'.aiff',
|
||||||
|
'.ape',
|
||||||
'.ac3',
|
'.ac3',
|
||||||
'.mp3',
|
|
||||||
'.ogg',
|
|
||||||
'.wav',
|
|
||||||
'.flac',
|
'.flac',
|
||||||
'.m4a'
|
'.m4a',
|
||||||
|
'.mp2',
|
||||||
|
'.mp3',
|
||||||
|
'.oga',
|
||||||
|
'.ogg',
|
||||||
|
'.opus',
|
||||||
|
'.wav',
|
||||||
|
'.wma'
|
||||||
].includes(getFileExtension(file))
|
].includes(getFileExtension(file))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -207,25 +207,18 @@ function renderOverlay (state) {
|
|||||||
function renderAudioMetadata (state) {
|
function renderAudioMetadata (state) {
|
||||||
const fileSummary = state.getPlayingFileSummary()
|
const fileSummary = state.getPlayingFileSummary()
|
||||||
if (!fileSummary.audioInfo) return
|
if (!fileSummary.audioInfo) return
|
||||||
const info = fileSummary.audioInfo
|
const common = fileSummary.audioInfo.common
|
||||||
|
|
||||||
// Get audio track info
|
// Get audio track info
|
||||||
let title = info.title
|
const title = common.title ? common.title : fileSummary.name
|
||||||
if (!title) {
|
|
||||||
title = fileSummary.name
|
|
||||||
}
|
|
||||||
let artist = info.artist && info.artist[0]
|
|
||||||
let album = info.album
|
|
||||||
if (album && info.year && !album.includes(info.year)) {
|
|
||||||
album += ' (' + info.year + ')'
|
|
||||||
}
|
|
||||||
let 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 with title/album/etc
|
// Show a small info box in the middle of the screen with title/album/etc
|
||||||
const elems = []
|
const elems = []
|
||||||
|
|
||||||
|
// Audio metadata: artist(s)
|
||||||
|
const artist = common.albumartist || common.artist ||
|
||||||
|
(common.artists && common.artists.filter(function (a) { return a }).join(', ')) ||
|
||||||
|
'(Unknown Artist)'
|
||||||
if (artist) {
|
if (artist) {
|
||||||
elems.push((
|
elems.push((
|
||||||
<div key='artist' className='audio-artist'>
|
<div key='artist' className='audio-artist'>
|
||||||
@@ -233,14 +226,44 @@ function renderAudioMetadata (state) {
|
|||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
if (album) {
|
|
||||||
|
// Audio metadata: album
|
||||||
|
if (common.album) {
|
||||||
elems.push((
|
elems.push((
|
||||||
<div key='album' className='audio-album'>
|
<div key='album' className='audio-album'>
|
||||||
<label>Album</label>{album}
|
<label>Album</label>{common.album}
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
if (track) {
|
|
||||||
|
// Audio metadata: year
|
||||||
|
if (common.year) {
|
||||||
|
elems.push((
|
||||||
|
<div key='year' className='audio-year'>
|
||||||
|
<label>Year</label>{common.year}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio metadata: release information (label & catalog-number)
|
||||||
|
if (common.label || common.catalognumber) {
|
||||||
|
const releaseInfo = []
|
||||||
|
if (common.label) {
|
||||||
|
releaseInfo.push(common.label)
|
||||||
|
}
|
||||||
|
if (common.catalognumber) {
|
||||||
|
releaseInfo.push(common.catalognumber)
|
||||||
|
}
|
||||||
|
elems.push((
|
||||||
|
<div key='release' className='audio-release'>
|
||||||
|
<label>Release</label>{ releaseInfo.join(' / ') }
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio metadata: track-number
|
||||||
|
if (common.track && common.track.no && common.track.of) {
|
||||||
|
const track = common.track.no + ' of ' + common.track.of
|
||||||
elems.push((
|
elems.push((
|
||||||
<div key='track' className='audio-track'>
|
<div key='track' className='audio-track'>
|
||||||
<label>Track</label>{track}
|
<label>Track</label>{track}
|
||||||
@@ -248,6 +271,37 @@ function renderAudioMetadata (state) {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Audio metadata: format
|
||||||
|
const format = []
|
||||||
|
if (fileSummary.audioInfo.format.dataformat) {
|
||||||
|
format.push(fileSummary.audioInfo.format.dataformat)
|
||||||
|
}
|
||||||
|
if (fileSummary.audioInfo.format.bitrate) {
|
||||||
|
format.push(fileSummary.audioInfo.format.bitrate / 1000 + ' kbps')
|
||||||
|
}
|
||||||
|
if (fileSummary.audioInfo.format.sampleRate) {
|
||||||
|
format.push(fileSummary.audioInfo.format.sampleRate / 1000 + ' kHz')
|
||||||
|
}
|
||||||
|
if (fileSummary.audioInfo.format.bitsPerSample) {
|
||||||
|
format.push(fileSummary.audioInfo.format.bitsPerSample + ' bit')
|
||||||
|
}
|
||||||
|
if (format.length > 0) {
|
||||||
|
elems.push((
|
||||||
|
<div key='format' className='audio-format'>
|
||||||
|
<label>Format</label>{ format.join(', ') }
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio metadata: comments
|
||||||
|
if (common.comment) {
|
||||||
|
elems.push((
|
||||||
|
<div key='comments' className='audio-comments'>
|
||||||
|
<label>Comments</label>{common.comment.join(' / ')}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
// Align the title with the other info, if available. Otherwise, center title
|
// Align the title with the other info, if available. Otherwise, center title
|
||||||
const emptyLabel = (<label />)
|
const emptyLabel = (<label />)
|
||||||
elems.unshift((
|
elems.unshift((
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const defaultAnnounceList = require('create-torrent').announceList
|
|||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const mkdirp = require('mkdirp')
|
const mkdirp = require('mkdirp')
|
||||||
const musicmetadata = require('musicmetadata')
|
const mm = require('music-metadata')
|
||||||
const networkAddress = require('network-address')
|
const networkAddress = require('network-address')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const WebTorrent = require('webtorrent')
|
const WebTorrent = require('webtorrent')
|
||||||
@@ -334,16 +334,26 @@ function stopServer () {
|
|||||||
server = null
|
server = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('Initializing...')
|
||||||
|
|
||||||
function getAudioMetadata (infoHash, index) {
|
function getAudioMetadata (infoHash, index) {
|
||||||
const torrent = client.get(infoHash)
|
const torrent = client.get(infoHash)
|
||||||
const file = torrent.files[index]
|
const file = torrent.files[index]
|
||||||
musicmetadata(file.createReadStream(), function (err, info) {
|
|
||||||
if (err) return console.log('error getting audio metadata for ' + infoHash + ':' + index, err)
|
const options = {native: false, skipCovers: true, fileSize: file.length}
|
||||||
const { artist, album, albumartist, title, year, track, disk, genre } = info
|
const onMetaData = file.done
|
||||||
const importantInfo = { artist, album, albumartist, title, year, track, disk, genre }
|
// If completed; use direct file access
|
||||||
console.log('got audio metadata for %s: %o', file.name, importantInfo)
|
? mm.parseFile(path.join(torrent.path, file.path), options)
|
||||||
ipc.send('wt-audio-metadata', infoHash, index, importantInfo)
|
// otherwise stream
|
||||||
})
|
: mm.parseStream(file.createReadStream(), file.name, options)
|
||||||
|
|
||||||
|
onMetaData
|
||||||
|
.then(function (metadata) {
|
||||||
|
console.log('got audio metadata for %s (length=%s): %o', file.name, file.length, metadata)
|
||||||
|
ipc.send('wt-audio-metadata', infoHash, index, metadata)
|
||||||
|
}).catch(function (err) {
|
||||||
|
return console.log('error getting audio metadata for ' + infoHash + ':' + index, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectFiles (torrentOrInfoHash, selections) {
|
function selectFiles (torrentOrInfoHash, selections) {
|
||||||
|
|||||||
@@ -820,12 +820,17 @@ video::-webkit-media-text-track-container {
|
|||||||
|
|
||||||
.audio-metadata label {
|
.audio-metadata label {
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
width: 100px;
|
width: 120px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin-right: 25px;
|
margin-right: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.audio-metadata .audio-format,
|
||||||
|
.audio-metadata .audio-comments {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ERRORS
|
* ERRORS
|
||||||
*/
|
*/
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 237 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 238 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 237 KiB |
|
Before Width: | Height: | Size: 406 KiB After Width: | Height: | Size: 725 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 239 KiB |