Fix some bugs and add some translations
Seto committed
Jan 24, 2022 at 13:28 UTC
ebb56804a589b01477843be4e6ef3fec8db1a62c
11 files changed
+120
-38
src/components/AudioPlayer.tsx
+63
-23
@@ -28,7 +28,15 @@ interface AudioFile {
28
interface YouTubeEventTarget {
29
pauseVideo: () => void,
30
playVideo: () => void,
31
- seekTo: (arg0: number) => void
31
+ seekTo: (arg0: number) => void,
32
+ getPlayerState: () => YouTubePlayerState,
33
+ getCurrentTime: () => number
34
+
35
+}
36
+
37
+// https://developers.google.com/youtube/iframe_api_reference#Playback_status
38
+enum YouTubePlayerState {
39
+ unstarted = -1, ended, playing, paused, buffering, videoCued
40
}
41
42
type AudioProps = {
@@ -61,6 +69,7 @@ class AudioPlayer extends Component<AudioProps, AudioState> {
69
pianoPlayer: Player | null = null;
70
71
youTubeEventTarget: YouTubeEventTarget | null = null
72
+ updateYouTubePlayerStatusIntervalId: any | null = null
73
74
constructor(props: AudioProps) {
75
super(props);
@@ -268,7 +277,11 @@ class AudioPlayer extends Component<AudioProps, AudioState> {
277
showinfo: 0
278
},
279
};
271
- let togglePlayButton = <p>Waiting for YouTube...</p>
280
+ let togglePlayButton = <p>
281
+ <FormattedMessage id="player.loadingYoutube"
282
+ defaultMessage="Waiting for YouTube..."
283
+ description="Loading message"/>
284
+ </p>
285
if (this.state.isYoutubePlayerReady) {
286
let button
287
if (this.state.isPlaying) {
@@ -298,14 +311,7 @@ class AudioPlayer extends Component<AudioProps, AudioState> {
311
312
return (
313
<IconContext.Provider value={{ size: "2em", color: "white", className: "global-class-name" }}>
301
- <Row className="mb-3 mt-3">
302
- <Col>
303
- <h1>SplitFire AI</h1>
304
- <a href="https://testflight.apple.com/join/4cb0rDIo" target={`__blank`}>
305
- <img src='https://splitfire.ai/packs/media/images/Pre-order_on_the_App_Store_Badge_US-UK_RGB_blk_121217-5fb138d3f649c68f7b3250e3887dbef5.svg' />
306
- </a>
307
- </Col>
308
- </Row>
314
+ <Header/>
315
<Row className="mb-3 mt-3">
316
{togglePlayButton}
317
</Row>
@@ -342,8 +348,7 @@ class AudioPlayer extends Component<AudioProps, AudioState> {
348
</Col>
349
</Row>
350
<Row>
345
- <Col style={{display: this.state.isYoutubePlayerReady ? 'block' : 'none'}}>
346
-
351
+ <Col style={{display: this.state.isYoutubePlayerReady ? 'block' : 'none', pointerEvents: 'none'}}>
352
<YouTube
353
videoId={this.state.audioFile?.youtube_video_id}
354
opts={opts}
@@ -357,16 +362,13 @@ class AudioPlayer extends Component<AudioProps, AudioState> {
362
} else {
363
return (
364
<Row>
360
- <Row className="mb-3 mt-3">
361
- <Col>
362
- <h1>SplitFire AI</h1>
363
- <a href="https://testflight.apple.com/join/4cb0rDIo" target={`__blank`}>
364
- <img src='https://splitfire.ai/packs/media/images/Pre-order_on_the_App_Store_Badge_US-UK_RGB_blk_121217-5fb138d3f649c68f7b3250e3887dbef5.svg' />
365
- </a>
366
- </Col>
367
- </Row>
365
+ <Header/>
366
<Col className="mb-3 mt-3">
369
- <p>🤖 AI is doing its business...</p>
367
+ <p>
368
+ <FormattedMessage id="player.loading"
369
+ defaultMessage="🤖 I'm doing my business..."
370
+ description="Loading message"/>
371
+ </p>
372
<Spinner animation='grow'></Spinner>
373
</Col>
374
</Row>
@@ -378,17 +380,55 @@ class AudioPlayer extends Component<AudioProps, AudioState> {
380
console.log('YouTube ready: ', event)
381
this.youTubeEventTarget = event.target
382
this.youTubeEventTarget?.playVideo()
381
- setTimeout(() => {this.youTubeEventTarget?.pauseVideo()}, 5000)
383
+ this.updateYouTubePlayerStatusIntervalId = setInterval(() => {
384
+ if (!this.state.isYoutubePlayerReady) {
385
+ this._updateYouTubePlayerStatusIfNeeded()
386
+ }
387
+ }, 2000)
388
}
389
390
_onStateChange(event: any) {
391
console.log('State changed: ', event)
392
console.log('getCurrentTime: ', event.target.getCurrentTime())
393
console.log('getPlayerState: ', event.target.getPlayerState())
388
- if (!this.state.isYoutubePlayerReady && event.target.getCurrentTime() > 1) {
394
+ if (!this.youTubeEventTarget) return
395
+
396
+ if (this.youTubeEventTarget.getPlayerState() === YouTubePlayerState.ended) {
397
+ this.setState({ isPlaying: false })
398
+ }
399
+ }
400
+
401
+ _updateYouTubePlayerStatusIfNeeded() {
402
+
403
+ if (this.state.isYoutubePlayerReady) return
404
+ if (!this.youTubeEventTarget) return
405
+
406
+ // Let's say 5 seconds buffer is enough.
407
+ if (this.youTubeEventTarget?.getCurrentTime() > 5) {
408
this.setState({ isYoutubePlayerReady: true })
409
+ this.youTubeEventTarget?.pauseVideo()
410
+
411
+ if (this.updateYouTubePlayerStatusIntervalId)
412
+ clearInterval(this.updateYouTubePlayerStatusIntervalId)
413
}
414
}
415
+
416
}
417
418
+const Header = () => (
419
+ <Row className="mb-3 mt-3">
420
+ <Col>
421
+ <h1>SplitFire AI</h1>
422
+ <p>
423
+ <FormattedMessage id="splitfire.whatIs"
424
+ defaultMessage="I'm an artificial intelligence software who will split your favorite music to its separate components."
425
+ description="Explanation message"/>
426
+ </p>
427
+ <a href="https://testflight.apple.com/join/4cb0rDIo" target={`__blank`}>
428
+ <img src='https://splitfire.ai/packs/media/images/Pre-order_on_the_App_Store_Badge_US-UK_RGB_blk_121217-5fb138d3f649c68f7b3250e3887dbef5.svg' />
429
+ </a>
430
+ </Col>
431
+ </Row>
432
+)
433
+
434
export default AudioPlayer;
\ No newline at end of file
src/components/FooterLinks.tsx
+1
-1
@@ -25,7 +25,7 @@ const FooterLinks = () => (
25
<div className="container">
26
<p className="float-end mb-1">
27
<a href="#" style={linkStyle}>
28
- <FormattedMessage id="footer.to_top"
28
+ <FormattedMessage id="footer.toTop"
29
defaultMessage="Back to top"
30
description="To top link" />
31
</a>
src/components/Home.tsx
+20
-6
@@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl';
4
import Firebase from './Firebase';
5
import app from 'firebase/app';
6
import axios from 'axios';
7
-import { Button, Carousel } from 'react-bootstrap';
7
+import { Button, Carousel, Spinner } from 'react-bootstrap';
8
import { Link } from 'react-router-dom';
9
10
const pStyle: CSSProperties = {
@@ -115,11 +115,25 @@ class Home extends Component<HomeProps, HomeState> {
115
render() {
116
const { files } = this.state
117
118
- let caraoselContent: any = 'Loading SplitFire AI...'
118
+ let caraoselContent: any = <div>
119
+ <p>
120
+ <FormattedMessage id="home.loading"
121
+ defaultMessage="Loading SplitFire AI..."
122
+ description="Loading message"/>
123
+ </p>
124
+ <Spinner animation='grow'></Spinner>
125
+ </div>
126
127
if (files.length > 0) {
121
- caraoselContent = <Carousel>
122
- {files.slice(0, 3).map(item => (
128
+ caraoselContent = <div>
129
+ <h2>SplitFire AI</h2>
130
+ <p>
131
+ <FormattedMessage id="splitfire.whatIs"
132
+ defaultMessage="I'm an artificial intelligence software that can split your favorite music to its separate components."
133
+ description="Explanation message"/>
134
+ </p>
135
+ <Carousel>
136
+ {files.slice(0, 5).map(item => (
137
<Carousel.Item key={item.id} style={{ padding: '20px' }}>
138
<img
139
className="d-block w-100"
@@ -133,7 +147,8 @@ class Home extends Component<HomeProps, HomeState> {
147
</Carousel.Caption>
148
</Carousel.Item>
149
))}
136
- </Carousel>
150
+ </Carousel>
151
+ </div>
152
}
153
154
return (
@@ -149,7 +164,6 @@ class Home extends Component<HomeProps, HomeState> {
164
defaultMessage="I'm "
165
description="My self description" />
166
<ReactRotatingText style={this.state.wordStyle} items={this.state.alterEgos} /></p>
152
- <h2>SplitFire AI</h2>
167
{caraoselContent}
168
</div>
169
);
src/components/db.ts
+1
-1
@@ -14,7 +14,7 @@ export class MySubClassedDexie extends Dexie {
14
audioFiles!: Table<AudioFiles>;
15
16
constructor() {
17
- super('myDatabase');
17
+ super('splitfireDB');
18
this.version(2).stores({
19
audioFiles: '++id, [audioFileId+type]' // Primary key and indexed props
20
});
src/translations/de.json
+5
-1
@@ -11,5 +11,9 @@
11
"cv.title": "CV",
12
"contact.title": "Kontakt",
13
"player.instruction": "Klicken Sie auf das Instrumentensymbol, um es aus dem Song zu entfernen.",
14
- "footer.to_top": "Zurück nach oben"
14
+ "home.loading": "SplitFire AI wird geladen...",
15
+ "player.loadingYoutube": "Warten auf YouTube...",
16
+ "footer.toTop": "Zurück nach oben",
17
+ "splitfire.whatIs": "Ich bin eine Software für künstliche Intelligenz, die Ihre Lieblingsmusik in ihre einzelnen Komponenten aufteilt.",
18
+ "player.loading": "🤖 Ich mache mein Geschäft..."
19
}
src/translations/en.json
+5
-1
@@ -11,5 +11,9 @@
11
"cv.title": "CV",
12
"contact.title": "Contact",
13
"player.instruction": "Click the instrument icon to remove it from the song.",
14
- "footer.to_top": "Back to top"
14
+ "home.loading": "Loading SplitFire AI...",
15
+ "player.loadingYoutube": "Waiting for YouTube...",
16
+ "footer.toTop": "Back to top",
17
+ "splitfire.whatIs": "I'm an artificial intelligence software who will split your favorite music to its separate components.",
18
+ "player.loading": "🤖 I'm doing my job..."
19
}
src/translations/es.json
+5
-1
@@ -11,5 +11,9 @@
11
"cv.title": "CV",
12
"contact.title": "Contacto",
13
"player.instruction": "Haz clic en el ícono del instrumento para eliminarlo de la canción.",
14
- "footer.to_top": "Volver arriba"
14
+ "home.loading": "Cargando SplitFire IA...",
15
+ "player.loadingYoutube": "Esperando YouTube...",
16
+ "footer.toTop": "Volver al principio",
17
+ "splitfire.whatIs": "Soy un software de inteligencia artificial que dividirá tu música favorita en sus componentes separados",
18
+ "player.loading": "🤖 Estoy haciendo mi trabajo..."
19
}
src/translations/fr.json
+5
-1
@@ -11,5 +11,9 @@
11
"cv.title": "CV",
12
"contact.title": "Contact",
13
"player.instruction": "Cliquez sur l'icône de l'instrument pour le supprimer du morceau.",
14
- "footer.to_top": "Retour en haut"
14
+ "home.loading": "Chargement de SplitFire AI...",
15
+ "player.loadingYoutube": "En attente de YouTube...",
16
+ "footer.toTop": "Retour en haut",
17
+ "splitfire.whatIs": "Je suis un logiciel d'intelligence artificielle qui divisera votre musique préférée en ses composants séparés.",
18
+ "player.loading": "🤖 Je fais mes affaires..."
19
}
src/translations/id.json
+5
-1
@@ -11,5 +11,9 @@
11
"cv.title": "CV",
12
"contact.title": "Kontak",
13
"player.instruction": "Klik ikon instrumen untuk menghapusnya dari lagu.",
14
- "footer.to_top": "Kembali ke atas"
14
+ "home.loading": "Memuat SplitFire AI...",
15
+ "player.loadingYoutube": "Menunggu YouTube...",
16
+ "footer.toTop": "Kembali ke atas",
17
+ "splitfire.whatIs": "Saya adalah perangkat lunak kecerdasan buatan yang akan membagi musik favorit Anda ke komponen terpisah.",
18
+ "player.loading": "🤖 Saya sedang melakukan pekerjaan saya..."
19
}
src/translations/sv.json
+5
-1
@@ -11,5 +11,9 @@
11
"cv.title": "CV",
12
"contact.title": "Kontakt",
13
"player.instruction": "Klicka på instrumentikonen för att ta bort den från låten.",
14
- "footer.to_top": "Tillbaka till toppen"
14
+ "home.loading": "Laddar SplitFire AI...",
15
+ "player.loadingYoutube": "Väntar på YouTube...",
16
+ "footer.toTop": "Tillbaka till toppen",
17
+ "splitfire.whatIs": "Jag är en programvara för artificiell intelligens som delar upp din favoritmusik till dess separata komponenter.",
18
+ "player.loading": "🤖 Jag gör min job..."
19
}
\ No newline at end of file
src/translations/zh.json
+5
-1
@@ -11,5 +11,9 @@
11
"cv.title": "CV",
12
"contact.title": "接触",
13
"player.instruction": "点击乐器图标将其从歌曲中移除。",
14
- "footer.to_top": "回到顶部"
14
+ "home.loading": "正在加载 SplitFire AI...",
15
+ "player.loadingYoutube": "正在等待 YouTube...",
16
+ "footer.toTop": "回到顶部",
17
+ "splitfire.whatIs": "我是一个人工智能软件,可以将你最喜欢的音乐拆分成不同的组件.",
18
+ "player.loading": "🤖 我在做我的事......"
19
}