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 }