mirror of
https://github.com/kevin-DL/vue-audio-recorder.git
synced 2026-01-12 03:05:16 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e89d8a33a | ||
|
|
420cf2e194 | ||
|
|
52b7dfe958 | ||
|
|
5d8139d674 |
15
README.md
15
README.md
@@ -15,7 +15,7 @@
|
||||
- A lot of callbacks
|
||||
- Individual an audio player
|
||||
|
||||
### Tested in
|
||||
### Tested in (desktop)
|
||||
|
||||
- Chrome
|
||||
- Firefox
|
||||
@@ -33,6 +33,7 @@ npm i vue-audio-recorder --save
|
||||
| --------------------- | -------- | --------------------------------------------------------------- |
|
||||
| attempts | Number | Number of recording attempts |
|
||||
| compact | Boolean | Hide the download and upload buttons |
|
||||
| headers | Object | HTTP headers |
|
||||
| time | Number | Time limit for the record (minutes) |
|
||||
| upload-url | String | URL for uploading |
|
||||
| start-record | Function | Fires after click the record button |
|
||||
@@ -52,17 +53,6 @@ npm i vue-audio-recorder --save
|
||||
|
||||
## Usage
|
||||
|
||||
The most common use case is to register the component globally
|
||||
|
||||
```js
|
||||
import {AudioRecorder, AudioPlayer} from 'vue-audio-recorder'
|
||||
|
||||
Vue.component(AudioPlayer)
|
||||
Vue.component(AudioRecorder)
|
||||
```
|
||||
|
||||
Alternatively you can do this to register the components
|
||||
|
||||
```js
|
||||
import AudioRecorder from 'vue-audio-recorder'
|
||||
|
||||
@@ -73,6 +63,7 @@ Alternatively you can do this to register the components
|
||||
<audio-recorder
|
||||
upload-url="YOUR_API_URL"
|
||||
:attempts="3"
|
||||
:headers="headers"
|
||||
:time="2"
|
||||
:start-record="callback"
|
||||
:stop-record="callback"
|
||||
|
||||
13
demo/app.vue
13
demo/app.vue
@@ -13,6 +13,7 @@
|
||||
upload-url="some url"
|
||||
:attempts="3"
|
||||
:time="2"
|
||||
:headers="headers"
|
||||
:start-record="callback"
|
||||
:stop-record="callback"
|
||||
:start-upload="callback"
|
||||
@@ -24,19 +25,15 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AudioPlayer from '../src/components/player'
|
||||
import AudioRecorder from '../src/components/recorder'
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {
|
||||
AudioPlayer,
|
||||
AudioRecorder
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
mp3: '/demo/example.mp3',
|
||||
showRecorder: true
|
||||
showRecorder: true,
|
||||
headers: {
|
||||
'X-Custom-Header': 'some data'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -2,8 +2,12 @@ import Vue from 'vue'
|
||||
import axios from 'axios'
|
||||
import app from './app'
|
||||
|
||||
import AudioRecorder from '@/index'
|
||||
|
||||
Vue.prototype.$http = axios
|
||||
|
||||
Vue.use(AudioRecorder)
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
render: h => h(app)
|
||||
|
||||
3
dist/vue-audio-recorder.min.js
vendored
3
dist/vue-audio-recorder.min.js
vendored
File diff suppressed because one or more lines are too long
14
package.json
14
package.json
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "vue-audio-recorder",
|
||||
"description": "Audio recorder for Vue.js. It allows to create, play, download and store records on a server",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.0",
|
||||
"author": "Gennady Grishkovtsov <grishkovelli@gmail.com>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot --https",
|
||||
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
|
||||
"dev": "webpack-dev-server --env.NODE_ENV=development --mode development --open --hot --https",
|
||||
"build": "webpack --env.NODE_ENV=production --mode production --progress --hide-modules"
|
||||
},
|
||||
"dependencies": {},
|
||||
"browserslist": [
|
||||
@@ -26,11 +26,13 @@
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"node-sass": "^4.5.3",
|
||||
"sass-loader": "^6.0.6",
|
||||
"uglifyjs-webpack-plugin": "^2.0.1",
|
||||
"vue": "^2.5.16",
|
||||
"vue-loader": "^13.0.5",
|
||||
"vue-loader": "^14.2.2",
|
||||
"vue-template-compiler": "^2.4.4",
|
||||
"webpack": "^3.6.0",
|
||||
"webpack-dev-server": "^2.9.1",
|
||||
"webpack": "^4.17.1",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-dev-server": "^3.1.9",
|
||||
"webpack-merge": "^4.1.3"
|
||||
},
|
||||
"main": "dist/vue-audio-recorder.min.js",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { calculateLineHeadPosition } from '@/lib/utils.js'
|
||||
import { calculateLineHeadPosition } from '@/lib/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
||||
@@ -97,11 +97,11 @@
|
||||
:class="{'ar-player__play--active': isPlaying}"
|
||||
@click.native="decorator(playback)"/>
|
||||
|
||||
<icon-button
|
||||
<uploader
|
||||
id="upload"
|
||||
class="ar-icon ar-icon__sm"
|
||||
name="save"
|
||||
@click.native="decorator(upload)"/>
|
||||
:record="record"
|
||||
:options="uploaderOptions"/>
|
||||
</div>
|
||||
|
||||
<div class="ar-player-bar">
|
||||
@@ -120,32 +120,31 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import IconButton from './icon-button'
|
||||
import LineControl from './line-control'
|
||||
import IconButton from './icon-button'
|
||||
import LineControl from './line-control'
|
||||
import Uploader from './uploader'
|
||||
import VolumeControl from './volume-control'
|
||||
import { convertTimeMMSS } from '@/lib/utils.js'
|
||||
import { convertTimeMMSS } from '@/lib/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
src : { type: String },
|
||||
uploadUrl : { type: String },
|
||||
record : { type: Object },
|
||||
compact : { type: Boolean, default: true },
|
||||
startUpload : { type: Function },
|
||||
successfulUpload : { type: Function },
|
||||
failedUpload : { type: Function }
|
||||
src : { type: String },
|
||||
record : { type: Object },
|
||||
compact : { type: Boolean, default: true },
|
||||
uploaderOptions : { type: Object, default: () => new Object }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isPlaying: false,
|
||||
duration: convertTimeMMSS(0),
|
||||
playedTime: convertTimeMMSS(0),
|
||||
progress: 0
|
||||
isPlaying : false,
|
||||
duration : convertTimeMMSS(0),
|
||||
playedTime : convertTimeMMSS(0),
|
||||
progress : 0
|
||||
}
|
||||
},
|
||||
components: {
|
||||
IconButton,
|
||||
LineControl,
|
||||
Uploader,
|
||||
VolumeControl
|
||||
},
|
||||
mounted: function() {
|
||||
@@ -161,6 +160,10 @@
|
||||
})
|
||||
|
||||
this.player.addEventListener('timeupdate', this._onTimeUpdate)
|
||||
|
||||
this.$eventBus.$on('remove-record', () => {
|
||||
this._resetProgress()
|
||||
})
|
||||
},
|
||||
computed: {
|
||||
audioSource () {
|
||||
@@ -188,30 +191,6 @@
|
||||
|
||||
this.isPlaying = !this.isPlaying
|
||||
},
|
||||
upload () {
|
||||
if (this.startUpload) {
|
||||
this.startUpload()
|
||||
}
|
||||
|
||||
this.$emit('on-start-upload')
|
||||
|
||||
let data = new FormData()
|
||||
data.append('audio', this.record.blob, 'my-record')
|
||||
|
||||
this.$http.post(this.uploadUrl, data, {
|
||||
headers: {'Content-Type': `multipart/form-data; boundary=${data._boundary}`}
|
||||
}).then(resp => {
|
||||
this.$emit('on-end-upload', 'success')
|
||||
if (this.successfulUpload) {
|
||||
this.successfulUpload(resp)
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$emit('on-end-upload', 'fail')
|
||||
if (this.failedUpload) {
|
||||
this.failedUpload(error)
|
||||
}
|
||||
})
|
||||
},
|
||||
download () {
|
||||
let link = document.createElement('a')
|
||||
link.href = this.record.url
|
||||
@@ -228,12 +207,11 @@
|
||||
if (this.isPlaying) {
|
||||
this.player.pause()
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.duration = convertTimeMMSS(0)
|
||||
this.playedTime = convertTimeMMSS(0)
|
||||
this.progress = 0
|
||||
this.isPlaying = false
|
||||
}, 0)
|
||||
|
||||
this.duration = convertTimeMMSS(0)
|
||||
this.playedTime = convertTimeMMSS(0)
|
||||
this.progress = 0
|
||||
this.isPlaying = false
|
||||
},
|
||||
_onTimeUpdate () {
|
||||
this.playedTime = convertTimeMMSS(this.player.currentTime)
|
||||
|
||||
@@ -211,15 +211,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<audio-player
|
||||
:compact="compact"
|
||||
:record="selected"
|
||||
:upload-url="uploadUrl"
|
||||
:start-upload="startUpload"
|
||||
:successful-upload="successfulUpload"
|
||||
:failed-upload="failedUpload"
|
||||
@start-upload="onStartUpload"
|
||||
@end-upload="onEndUpload"/>
|
||||
<audio-player :compact="compact" :record="selected" :uploader-options="uploaderOptions"/>
|
||||
|
||||
<div :class="uploadStatusClasses" v-if="uploadStatus">{{message}}</div>
|
||||
</div>
|
||||
@@ -227,52 +219,76 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AudioPlayer from './player.vue'
|
||||
import IconButton from './icon-button.vue'
|
||||
import Recorder from '@/lib/recorder.js'
|
||||
import { convertTimeMMSS } from '@/lib/utils.js'
|
||||
import AudioPlayer from './player'
|
||||
import IconButton from './icon-button'
|
||||
import Recorder from '@/lib/recorder'
|
||||
import { convertTimeMMSS } from '@/lib/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
attempts : { type: Number },
|
||||
compact : { type: Boolean, default: false },
|
||||
time : { type: Number },
|
||||
uploadUrl : { type: String },
|
||||
attempts : { type: Number },
|
||||
compact : { type: Boolean, default: false },
|
||||
time : { type: Number },
|
||||
|
||||
attemptsLimit : { type: Function },
|
||||
micFailed : { type: Function },
|
||||
startRecord : { type: Function },
|
||||
stopRecord : { type: Function },
|
||||
|
||||
attemptsLimit : { type: Function },
|
||||
failedUpload : { type: Function },
|
||||
micFailed : { type: Function },
|
||||
startRecord : { type: Function },
|
||||
headers : { type: Object },
|
||||
startUpload : { type: Function },
|
||||
stopRecord : { type: Function },
|
||||
successfulUpload : { type: Function },
|
||||
uploadUrl : { type: String },
|
||||
|
||||
successfulUploadMsg : { type: String, default: 'Upload successful' },
|
||||
failedUploadMsg : { type: String, default: 'Upload fail' }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isUploading: false,
|
||||
recorder: new Recorder({
|
||||
afterStop: () => {
|
||||
this.recordList = this.recorder.recordList()
|
||||
|
||||
if (this.stopRecord) {
|
||||
this.stopRecord('stop record')
|
||||
}
|
||||
},
|
||||
attempts: this.attempts,
|
||||
time: this.time
|
||||
}),
|
||||
recordList: [],
|
||||
selected: {},
|
||||
uploadStatus: null
|
||||
isUploading : false,
|
||||
recorder : new Recorder({
|
||||
afterStop: () => {
|
||||
this.recordList = this.recorder.recordList()
|
||||
if (this.stopRecord) {
|
||||
this.stopRecord('stop record')
|
||||
}
|
||||
},
|
||||
attempts: this.attempts,
|
||||
time: this.time
|
||||
}),
|
||||
recordList : [],
|
||||
selected : {},
|
||||
uploadStatus : null,
|
||||
uploaderOptions : {}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
AudioPlayer,
|
||||
IconButton
|
||||
},
|
||||
created () {
|
||||
this.uploaderOptions = {
|
||||
failedUpload : this.failedUpload,
|
||||
headers : this.headers,
|
||||
startUpload : this.startUpload,
|
||||
successfulUpload : this.successfulUpload,
|
||||
uploadUrl : this.uploadUrl
|
||||
}
|
||||
|
||||
this.$eventBus.$on('start-upload', () => {
|
||||
this.isUploading = true
|
||||
})
|
||||
|
||||
this.$eventBus.$on('end-upload', (resp) => {
|
||||
this.isUploading = false
|
||||
this.uploadStatus = status
|
||||
setTimeout(() => {this.uploadStatus = null}, 1500)
|
||||
})
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.stopRecorder()
|
||||
},
|
||||
methods: {
|
||||
toggleRecorder () {
|
||||
if (this.attempts && this.recorder.records.length >= this.attempts) {
|
||||
@@ -301,14 +317,7 @@
|
||||
removeRecord (idx) {
|
||||
this.recordList.splice(idx, 1)
|
||||
this.$set(this.selected, 'url', null)
|
||||
},
|
||||
onStartUpload () {
|
||||
this.isUploading = true
|
||||
},
|
||||
onEndUpload (status) {
|
||||
this.isUploading = false
|
||||
this.uploadStatus = status
|
||||
setTimeout(() => {this.uploadStatus = null}, 1500)
|
||||
this.$eventBus.$emit('remove-record')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
54
src/components/uploader.vue
Normal file
54
src/components/uploader.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<style lang="scss">
|
||||
@import '../scss/icons';
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<icon-button name="save" @click.native="upload"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import IconButton from './icon-button'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
options : { type: Object },
|
||||
record : { type: Object }
|
||||
},
|
||||
components: {
|
||||
IconButton
|
||||
},
|
||||
methods: {
|
||||
upload () {
|
||||
if (!this.record.url) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$eventBus.$emit('start-upload')
|
||||
|
||||
if (this.options.startUpload) {
|
||||
this.options.startUpload()
|
||||
}
|
||||
|
||||
let data = new FormData()
|
||||
data.append('audio', this.record.blob, 'my-record')
|
||||
|
||||
let headers = Object.assign(this.options.headers, {})
|
||||
headers['Content-Type'] = `multipart/form-data; boundary=${data._boundary}`
|
||||
|
||||
this.$http.post(this.options.uploadUrl, data, { headers: headers }).then(resp => {
|
||||
this.$eventBus.$emit('end-upload', 'success')
|
||||
|
||||
if (this.options.successfulUpload) {
|
||||
this.options.successfulUpload(resp)
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$eventBus.$emit('end-upload', 'fail')
|
||||
|
||||
if (this.options.failedUpload) {
|
||||
this.options.failedUpload(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -37,8 +37,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import IconButton from './icon-button.vue'
|
||||
import LineControl from './line-control.vue'
|
||||
import IconButton from './icon-button'
|
||||
import LineControl from './line-control'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AudioPlayer from './components/player.vue'
|
||||
import AudioRecorder from './components/recorder.vue'
|
||||
import AudioPlayer from '@/components/player.vue'
|
||||
import AudioRecorder from '@/components/recorder.vue'
|
||||
|
||||
const components = {
|
||||
AudioPlayer,
|
||||
@@ -12,6 +12,8 @@ const components = {
|
||||
|
||||
this.installed = true
|
||||
|
||||
Vue.prototype.$eventBus = Vue.prototype.$eventBus || new Vue
|
||||
|
||||
Vue.component('audio-player', AudioPlayer)
|
||||
Vue.component('audio-recorder', AudioRecorder)
|
||||
}
|
||||
|
||||
@@ -1,67 +1,70 @@
|
||||
const webpack = require('webpack')
|
||||
const merge = require('webpack-merge')
|
||||
const env = `./webpack.${process.env.NODE_ENV === 'production' ? 'prod' : 'dev'}.js`
|
||||
const path = require('path')
|
||||
|
||||
module.exports = merge(require(env), {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
'css-loader',
|
||||
'sass-loader'
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
options: {
|
||||
loaders: {
|
||||
'scss': [
|
||||
'vue-style-loader',
|
||||
'css-loader',
|
||||
'sass-loader'
|
||||
]
|
||||
module.exports = (env, args) => {
|
||||
let conf = `./webpack.${env.NODE_ENV === 'production' ? 'prod' : 'dev'}.js`
|
||||
|
||||
return merge(require(conf), {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
'css-loader',
|
||||
'sass-loader'
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
options: {
|
||||
loaders: {
|
||||
'scss': [
|
||||
'vue-style-loader',
|
||||
'css-loader',
|
||||
'sass-loader'
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|gif|svg)$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[name].[ext]?[hash]'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|gif|svg)$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[name].[ext]?[hash]'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'vue$': 'vue/dist/vue.esm.js',
|
||||
'@': path.resolve(__dirname, 'src')
|
||||
]
|
||||
},
|
||||
extensions: ['*', '.js', '.vue', '.json']
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: `"${process.env.NODE_ENV}"`
|
||||
resolve: {
|
||||
alias: {
|
||||
'vue$': 'vue/dist/vue.esm.js',
|
||||
'@': path.resolve(__dirname, 'src')
|
||||
},
|
||||
VERSION: JSON.stringify(require("./package.json").version)
|
||||
}),
|
||||
],
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
noInfo: true,
|
||||
overlay: true
|
||||
},
|
||||
performance: {
|
||||
hints: false
|
||||
}
|
||||
})
|
||||
extensions: ['*', '.js', '.vue', '.json']
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: `"${process.env.NODE_ENV}"`
|
||||
},
|
||||
VERSION: JSON.stringify(require("./package.json").version)
|
||||
}),
|
||||
],
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
noInfo: true,
|
||||
overlay: true
|
||||
},
|
||||
performance: {
|
||||
hints: false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ const path = require('path')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
||||
module.exports = {
|
||||
devtool: '#eval-source-map',
|
||||
entry: './demo/index.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, './demo')
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
|
||||
|
||||
module.exports = {
|
||||
devtool: '#source-map',
|
||||
entry: './src/index.js',
|
||||
entry: {
|
||||
main: './src/index.js'
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'vue-audio-recorder.min.js',
|
||||
@@ -12,18 +14,21 @@ module.exports = {
|
||||
libraryExport: 'default',
|
||||
umdNamedDefine: true
|
||||
},
|
||||
optimization: {
|
||||
minimizer: [
|
||||
new UglifyJsPlugin({
|
||||
cache: true,
|
||||
parallel: true,
|
||||
sourceMap: true
|
||||
})
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
sourceMap: true,
|
||||
compress: {
|
||||
warnings: true
|
||||
}
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: false
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user