Skip to content

Commit

Permalink
ffmpeg player
Browse files Browse the repository at this point in the history
Signed-off-by: mashaal <omar@teacups.io>
  • Loading branch information
mashaal committed Jan 7, 2017
1 parent 5971cd6 commit 3146f45
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 87 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -4,3 +4,5 @@ node_modules
Destroyer-darwin-x64
Destroyer-win32-x64
Destroyer-linux-x64
ffmpeg.zip
ffmpeg
89 changes: 42 additions & 47 deletions components/player/index.js
@@ -1,59 +1,58 @@
import { store } from '../../client.js'
const fs = require('fs')
import { remote } from 'electron'
const addSeconds = require('date-fns/add_seconds')
const differenceInSeconds = require('date-fns/difference_in_seconds')

export default class Player {
constructor () {
this.ch1 = false
}
playAlbum (album) {
store.dispatch({type: 'PLAY_ALBUM', album: album})
this.local(store.getState().player.track)
}
playTrack (track) {
store.dispatch({type: 'PLAY_TRACK', track: track})
this.local(store.getState().player.track)
this.local(track)
}
next () {
if (store.getState().player.next) this.playTrack(store.getState().player.next)
}
stop () {
if (this.ch1.playing) this.ch1.stop()
if (this.ch1.asset) this.ch1.asset.stop()
remote.app.stop()
store.dispatch({type: 'STOP'})
}
toggle () {
if (this.ch1.playing) {
this.ch1.pause()
store.dispatch({type: 'PAUSE'})
} else {
this.ch1.play()
store.dispatch({type: 'PLAY'})
}
remote.app.toggle()
}
pause () {
clearInterval(this.interval)
store.dispatch({type: 'PAUSE'})
}
local (file) {
fs.readFile(file.path, (error, data) => {
if (error) throw error
this.stop()
store.dispatch({type: 'PLAY_TRACK', track: file})
this.ch1 = AV.Player.fromBuffer(data)
this.ch1.play()
this.ch1.on('duration', (duration) => {
this.ch1.on('progress', (progress) => {
this.setCounter(Math.floor((duration - progress) / 1000))
this.setPlaybar(duration, progress)
})
})
this.ch1.on('buffer', (buffer) => {
this.setBuffer(buffer)
})
this.ch1.on('metadata', (metadata) => {
store.dispatch({type: 'METADATA', artist: metadata.artist, title: metadata.title, album: metadata.album})
})
this.ch1.on('error', (error) => {
store.dispatch({type: 'ERROR', error: error})
})
this.ch1.on('end', () => {
if (store.getState().player.next) this.local(store.getState().player.next)
else store.dispatch({type: 'STOP'})
})
})
resume () {
this.setDuration(this.remaining, true)
store.dispatch({type: 'PLAY'})
}
setDuration (duration = 0, previous) {
if (!previous) this.totalDuration = duration
let end = addSeconds(new Date(), duration)
this.seconds = differenceInSeconds(end, new Date())
clearInterval(this.interval)
this.setCounter(this.seconds)
this.interval = setInterval(() => {
this.remaining = differenceInSeconds(end, new Date())
if (this.remaining < 1) {
this.setCounter(0)
clearInterval(this.interval)
} else {
this.setCounter(this.remaining)
this.setPlaybar(this.totalDuration, this.remaining)
}
}, 666)
}
clearInterval () {
clearInterval(this.interval)
}
local (track) {
remote.app.playTrack(track)
store.dispatch({type: 'METADATA', artist: track.artist, title: track.title, album: track.album})
}
setCounter (seconds) {
if (!this.counter) this.counter = document.querySelector('figure h4')
Expand All @@ -65,11 +64,7 @@ export default class Player {
}
setPlaybar (duration, progress) {
if (!this.range) this.range = document.querySelector('[data-range]')
let elapsed = (progress / duration) * 100
this.range.style.transform = 'translateX(' + (elapsed - 100) + '%)'
}
setBuffer (buffer) {
if (!this.buffer) this.buffer = document.querySelector('[data-buffer]')
this.buffer.style.transform = 'translateX(' + (buffer - 100) + '%)'
let elapsed = ((progress / duration) * 100)
this.range.style.width = `${100 - elapsed}%`
}
}
4 changes: 3 additions & 1 deletion components/player/reducer.js
@@ -1,5 +1,4 @@
import { store } from '../../client.js'
import Player from './index.js'

export const playerReducer = (state = {}, action) => {
switch (action.type) {
Expand All @@ -26,6 +25,9 @@ export const playerReducer = (state = {}, action) => {
state = {...state, track: action.track, next: next, previous: previous, albumIndex: albumIndex}
break
}
case 'STOP': {
state = {}
}
}
return state
}
Expand Down
23 changes: 23 additions & 0 deletions ffbinaries.js
@@ -0,0 +1,23 @@
var ffbinaries = require('ffbinaries')
var platform = ffbinaries.detectPlatform()
var fs = require('fs')
var archiver = require('archiver')

ffbinaries.downloadFiles(platform, {components: ['ffmpeg', 'ffplay', 'ffprobe'], destination: './ffmpeg'}, () => {
var output = fs.createWriteStream('ffmpeg.zip')
var archive = archiver('zip')

output.on('close', function () {
console.log('ffmpeg complete')
})

archive.on('error', function (err) {
throw err
})

archive.pipe(output)
archive.bulk([
{expand: true, cwd: './ffmpeg', src: ['**'], dest: '/'}
])
archive.finalize()
})
98 changes: 60 additions & 38 deletions index.js
Expand Up @@ -2,11 +2,13 @@ const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const windowStateKeeper = require('electron-window-state')
const ffbinaries = require('ffbinaries')
const platform = ffbinaries.detectPlatform()
const fs = require('fs')
const dest = app.getPath('userData')
let mainWindow
let player
const spawn = require('child_process').spawn
const ffmpeg = require('fluent-ffmpeg')
const fs = require('fs')
const userData = app.getPath('userData')
var extract = require('extract-zip')

const createWindow = () => {
let mainWindowState = windowStateKeeper({
Expand All @@ -28,6 +30,7 @@ const createWindow = () => {
mainWindowState.manage(mainWindow)
mainWindow.loadURL('file://' + __dirname + '/components/app/index.html')
mainWindow.on('closed', () => {
app.stop()
mainWindow = null
})
}
Expand All @@ -36,6 +39,7 @@ app.on('ready', createWindow)

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.stop()
app.quit()
}
})
Expand All @@ -46,39 +50,57 @@ app.on('activate', () => {
}
})

if (!fs.existsSync(dest + '/hail-satan.txt')) {
ffbinaries.downloadFiles(platform, {components: ['ffmpeg'], quiet: true, destination: dest}, () => {
fs.writeFile(dest + '/hail-satan.txt', `
Invoking the majestic throne of Satan...
...
s, . .s
ss, . .. .ss
'SsSs, .. . .sSsS'
sSs'sSs, . . .sSs'sSs
sSs 'sSs, ... .sSs' sSs
sS, 'sSs, .sSs' .Ss
'Ss 'sSs, .sSs' sS'
... sSs ' .sSs' sSs ...
. sSs .sSs' .., sSs .
. .. sS, .sSs' . 'sSs, .Ss . ..
.. . 'Ss .Ss' . 'sSs. '' .. .
. . sSs ' . 'sSs, . .
... .sS.'sSs . .. 'sSs, ...
.sSs' sS, ..... .Ss 'sSs,
.sSs' 'Ss . sS' 'sSs,
.sSs' sSs . sSs 'sSs,
.sSs'____________________________ sSs ______________'sSs,
.sSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS'.Ss SSSSSSSSSSSSSSSSSSSSSs,
... sS'
sSs sSs
sSs sSs
sS, .Ss
'Ss sS'
sSs sSs
sSsSs
sSs
s
complete
`)
app.playTrack = file => {
app.stop()
player = spawn(`${userData}/ffmpeg/ffplay`, [file.path, '-nodisp', '-autoexit'], {stdio: 'ignore'})
ffmpeg.setFfprobePath(`${userData}/ffmpeg/ffprobe`)
ffmpeg.ffprobe(file.path, (error, metadata) => {
let duration
if (error) throw error
else if (metadata.format) {
duration = metadata.format.duration
} else duration = false
mainWindow.webContents.executeJavaScript(`window.player.setDuration(${duration})`)
})
player.on('exit', sss)
this.playing = this.cnt = true
this.track = file
}

app.currentTrack = () => this.track

const sss = () => {
if (this.cnt) next()
else app.stop()
}

let next = () => mainWindow.webContents.executeJavaScript('window.player.next()')

app.stop = () => {
if (player) player.removeListener('exit', sss)
if (this.playing) player.kill()
player = this.playing = this.cnt = this.track = null
}

app.pause = () => {
this.playing = false
mainWindow.webContents.executeJavaScript('window.player.pause()')
player.kill('SIGSTOP')
}

app.resume = () => {
this.playing = true
mainWindow.webContents.executeJavaScript('window.player.resume()')
player.kill('SIGCONT')
}

app.toggle = () => {
if (this.playing) app.pause()
else app.resume()
}

if (!fs.existsSync(`${userData}/ff.txt`)) {
extract(__dirname + '/ffmpeg.zip', {dir: userData}, function (err) {
fs.writeFile(`${userData}/ff.txt`, '🍑🍆')
})
}
6 changes: 5 additions & 1 deletion package.json
Expand Up @@ -10,13 +10,15 @@
"build": "NODE_ENV=development webpack --progress",
"build:watch": "npm run build -- --watch",
"start": "electron index.js",
"ffmpeg": "node ./ffbinaries.js",
"pack:osx": "rm -rf Destroyer-darwin-x64 && NODE_ENV=production webpack --progress -p && electron-packager ./ Destroyer --platform=darwin --arch=x64 --icon=icons.icns --prune"
},
"author": "omar mashaal",
"license": "ISC",
"dependencies": {
"date-fns": "^1.23.0",
"electron-window-state": "^4.0.1",
"ffbinaries": "^0.1.2",
"extract-zip": "^1.6.0",
"fluent-ffmpeg": "2.0.1",
"fuzzy-search": "^1.4.0",
"in-view": "^0.6.1",
Expand All @@ -32,6 +34,7 @@
"walk": "^2.3.9"
},
"devDependencies": {
"archiver": "^1.3.0",
"babel-core": "^6.21.0",
"babel-eslint": "^7.1.1",
"babel-loader": "^6.2.10",
Expand All @@ -50,6 +53,7 @@
"eslint-plugin-react": "^6.8.0",
"eslint-plugin-standard": "^2.0.1",
"exports-loader": "^0.6.3",
"ffbinaries": "^0.1.2",
"file-loader": "^0.9.0",
"imports-loader": "^0.7.0",
"url-loader": "^0.5.7",
Expand Down

0 comments on commit 3146f45

Please sign in to comment.