Skip to content

Commit e479d85

Browse files
authored
Merge pull request #9 from ackzell/2-recording-component
#1 #2 Abstracting the recording component.
2 parents 6725344 + f60378f commit e479d85

File tree

3 files changed

+173
-65
lines changed

3 files changed

+173
-65
lines changed

components/RecordingItem.vue

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<template>
2+
<v-list-item :ripple="false">
3+
<div class="mr-5">
4+
<span class="overline mr-1">Recording {{ recording.number }}</span>
5+
</div>
6+
<v-list-item-avatar>
7+
<v-btn x-large icon @click="toggleAudio">
8+
<v-icon v-if="isPlaying">mdi-pause</v-icon>
9+
<v-icon v-else>mdi-play</v-icon>
10+
</v-btn>
11+
</v-list-item-avatar>
12+
<v-list-item-content class="px-5">
13+
<v-slider
14+
active
15+
color="primary lighten-4"
16+
rounded
17+
:value="progress"
18+
:hide-details="true"
19+
@change="seek"
20+
@start="sliderConnected = false"
21+
@end="sliderConnected = true"
22+
/>
23+
</v-list-item-content>
24+
<div class="caption mr-5 ml-3 ">
25+
<span> {{ elapsedTime }} / {{ totalDuration }} </span>
26+
</div>
27+
<v-list-item-action>
28+
<v-btn
29+
icon
30+
x-small
31+
color="primary darken-3"
32+
:href="href"
33+
:download="download"
34+
>
35+
<v-icon>mdi-download</v-icon>
36+
</v-btn>
37+
</v-list-item-action>
38+
<v-list-item-action>
39+
<v-btn
40+
icon
41+
x-small
42+
color="primary lighten-3"
43+
@click="$emit('remove-item', recording.id)"
44+
>
45+
<v-icon>mdi-close</v-icon>
46+
</v-btn>
47+
</v-list-item-action>
48+
</v-list-item>
49+
</template>
50+
<script>
51+
function getMMSS(timeInSeconds) {
52+
let minutes = Math.floor(timeInSeconds / 60)
53+
let seconds = parseInt(timeInSeconds, 10) - minutes * 60
54+
55+
if (minutes < 10) {
56+
minutes = '0' + minutes
57+
}
58+
if (seconds < 10) {
59+
seconds = '0' + seconds
60+
}
61+
62+
return `${minutes}:${seconds}`
63+
}
64+
65+
export default {
66+
props: {
67+
recording: {
68+
type: Object,
69+
default: () => ({
70+
audio: null,
71+
encoding: 'mp3'
72+
})
73+
}
74+
},
75+
data() {
76+
return {
77+
track: null,
78+
isPlaying: false,
79+
totalDuration: null,
80+
elapsedTime: `00:00`,
81+
progress: 0,
82+
sliderConnected: true,
83+
href: null,
84+
download: ''
85+
}
86+
},
87+
beforeMount() {
88+
this.track = new Audio(this.recording.audio)
89+
90+
this.href = this.recording.audio
91+
this.download = `Recording ${this.recording.number + 1}.${
92+
this.recording.encoding
93+
}`
94+
95+
this.track.addEventListener('loadeddata', () => {
96+
console.log('finished loading')
97+
console.log(this.track.duration)
98+
this.totalDuration = getMMSS(this.track.duration)
99+
})
100+
101+
this.track.addEventListener('ended', () => {
102+
console.log('ended!')
103+
this.isPlaying = false
104+
})
105+
106+
this.track.addEventListener('timeupdate', (event) => {
107+
const s = parseInt(this.track.currentTime, 10)
108+
this.elapsedTime = getMMSS(s)
109+
110+
if (this.sliderConnected) {
111+
this.progress = (this.track.currentTime * 100) / this.track.duration
112+
}
113+
})
114+
},
115+
methods: {
116+
toggleAudio() {
117+
this.isPlaying = !this.isPlaying
118+
if (this.isPlaying) {
119+
this.track.play()
120+
} else {
121+
this.track.pause()
122+
}
123+
},
124+
seek(payload) {
125+
if (typeof payload === 'number') {
126+
this.track.currentTime = (payload * this.track.duration) / 100
127+
}
128+
}
129+
}
130+
}
131+
</script>

components/RecordingsList.vue

+25-46
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,26 @@
55
mandatory
66
@change="$emit('input', selected)"
77
>
8-
<div v-for="(recording, index) in recordings" :key="index">
9-
<v-list-item :ripple="false">
10-
<v-list-item-avatar>
11-
<v-btn
12-
x-large
13-
icon
14-
@click="recording.isPlaying = !recording.isPlaying"
15-
>
16-
<v-icon v-if="recording.isPlaying">mdi-pause</v-icon>
17-
<v-icon v-else>mdi-play</v-icon>
18-
</v-btn>
19-
</v-list-item-avatar>
20-
<v-list-item-content>
21-
<v-list-item-title class="overline">
22-
Recording {{ index + 1 }}
23-
</v-list-item-title>
24-
<v-slider
25-
value="45"
26-
active
27-
color="primary lighten-4"
28-
rounded
29-
inverse-label
30-
:label="`00:00 / ${recording.duration}`"
31-
/>
32-
</v-list-item-content>
33-
<v-list-item-action>
34-
<v-btn icon color="primary darken-3" @click.stop>
35-
<v-icon>mdi-download</v-icon>
36-
</v-btn>
37-
</v-list-item-action>
38-
<v-list-item-action>
39-
<v-btn icon x-small>
40-
<v-icon>mdi-close</v-icon>
41-
</v-btn>
42-
</v-list-item-action>
43-
</v-list-item>
44-
<v-divider v-show="index != recordings.length - 1" inset></v-divider>
8+
<div v-for="(recording, index) in recordingsInternal" :key="recording.id">
9+
<recording-item
10+
:recording="recording"
11+
@remove-item="remove(recording.id)"
12+
></recording-item>
13+
<v-divider
14+
v-show="index != recordingsInternal.length - 1"
15+
inset
16+
></v-divider>
4517
</div>
4618
</v-list-item-group>
4719
</v-list>
4820
</template>
4921

5022
<script>
23+
import RecordingItem from '@/components/RecordingItem'
5124
export default {
25+
components: {
26+
RecordingItem
27+
},
5228
props: {
5329
value: {
5430
type: Number,
@@ -61,16 +37,19 @@ export default {
6137
},
6238
data() {
6339
return {
64-
selected: this.selectedItem
40+
selected: this.selectedItem,
41+
recordingsInternal: this.recordings
42+
}
43+
},
44+
methods: {
45+
remove(id) {
46+
console.log(id)
47+
const index = this.recordings.findIndex(
48+
(recording) => recording.id === id
49+
)
50+
51+
this.recordingsInternal.splice(index, 1)
6552
}
6653
}
6754
}
6855
</script>
69-
70-
<style lang="scss" scoped>
71-
.v-application--is-ltr .v-list-item__action:last-of-type:not(:only-child),
72-
.v-application--is-ltr .v-list-item__avatar:last-of-type:not(:only-child),
73-
.v-application--is-ltr .v-list-item__icon:last-of-type:not(:only-child) {
74-
margin-left: 0;
75-
}
76-
</style>

pages/index.vue

+17-19
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,11 @@
2828
<timer :timer-status="timerStatus"></timer>
2929

3030
<recordings-list
31+
v-if="recordings.length"
3132
v-model="selected"
32-
:recordings="audios"
33+
:recordings="recordings"
3334
></recordings-list>
3435

35-
<ul v-if="recordings.length">
36-
<li v-for="(audio, index) in recordings" :key="index">
37-
<audio :src="audio" controls></audio>
38-
</li>
39-
</ul>
40-
4136
selected: {{ selected }}
4237
</v-card-text>
4338
</v-card>
@@ -52,7 +47,9 @@
5247
</v-card-text>
5348
<v-card-actions>
5449
<v-spacer></v-spacer>
55-
<v-btn color="primary" :disabled="!audios.length">Send it!</v-btn>
50+
<v-btn color="primary" :disabled="!recordings.length && !selected"
51+
>Send it!</v-btn
52+
>
5653
</v-card-actions>
5754
</v-card>
5855
</v-col>
@@ -65,6 +62,7 @@ import Timer from '@/components/Timer'
6562
6663
const ENCODING_TYPE = 'mp3'
6764
const ENCODE_AFTER_RECORD = true
65+
const TIME_LIMIT = 180
6866
6967
export default {
7068
components: {
@@ -74,19 +72,14 @@ export default {
7472
data() {
7573
return {
7674
isRecording: false,
77-
audios: [
78-
{ isPlaying: false, duration: '01:45', volume: 50 },
79-
{ isPlaying: false, duration: '01:23', volume: 50 },
80-
{ isPlaying: false, duration: '00:21', volume: 50 },
81-
{ isPlaying: false, duration: '00:13', volume: 50 }
82-
],
8375
getUserMediaStream: null,
8476
recorder: null,
8577
input: null,
8678
audioContext: null,
8779
selected: null,
8880
timerStatus: 'stopped',
89-
recordings: []
81+
recordings: [],
82+
count: 0
9083
}
9184
},
9285
created() {
@@ -140,14 +133,19 @@ export default {
140133
onComplete: (recorder, blob) => {
141134
console.warn('Encoding complete')
142135
const url = URL.createObjectURL(blob)
143-
console.log(blob)
144-
this.recordings.push(url)
145-
// createDownloadLink(blob, recorder.encoding);
136+
// the url already gives us a unique id, so we might as well use that :D
137+
const id = url.split('3333/')[1]
138+
this.recordings.push({
139+
number: ++this.count,
140+
id,
141+
audio: url,
142+
encoding: ENCODING_TYPE
143+
})
146144
}
147145
})
148146
149147
this.recorder.setOptions({
150-
timeLimit: 60,
148+
timeLimit: TIME_LIMIT,
151149
encodeAfterRecord: ENCODE_AFTER_RECORD,
152150
mp3: {
153151
bitRate: 160

0 commit comments

Comments
 (0)