@saooti/octopus-sdk 33.0.1 → 33.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -688,3 +688,5 @@ See [Configuration Reference](https://cli.vuejs.org/config/).
688
688
  * 32.0.35 Enlever Dummy param
689
689
  * 32.0.36 Select -> font family
690
690
  * 32.0.40 Problème majeur !!!
691
+
692
+ * 33.0.3 SpeechToText player octopus
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saooti/octopus-sdk",
3
- "version": "33.0.1",
3
+ "version": "33.0.3",
4
4
  "private": false,
5
5
  "description": "Javascript SDK for using octopus",
6
6
  "author": "Saooti",
Binary file
@@ -198,7 +198,7 @@ body{
198
198
  display: contents;
199
199
  }
200
200
 
201
- .img-box {
201
+ .img-box{
202
202
  height: 13rem;
203
203
  width: 13rem;
204
204
  background-size: cover;
@@ -32,11 +32,6 @@
32
32
  border: 0;
33
33
  }
34
34
  }
35
- .icon-caution {
36
- margin-right: 5px;
37
- margin-bottom: 2px;
38
- height: 25px;
39
- }
40
35
 
41
36
  .vue-swatches__trigger{
42
37
  border: solid 1px black;
@@ -18,16 +18,16 @@
18
18
  :alt="$t('Emission name image', {name:emission.name})"
19
19
  >
20
20
  <div class="emission-item-text">
21
- <div
22
- class="emission-name"
23
- >
24
- <img
21
+ <div class="d-flex align-items-center">
22
+ <span
25
23
  v-if="!activeEmission && !isPodcastmaker && editRight"
26
- class="icon-caution"
27
- src="/img/caution.png"
28
24
  :title="$t('Emission have not podcasts')"
29
- :alt="$t('Emission have not podcasts')"
30
- >{{ emission.name }}
25
+ class="saooti-warning text-danger me-1"/>
26
+ <div
27
+ class="emission-name"
28
+ >
29
+ {{ emission.name }}
30
+ </div>
31
31
  </div>
32
32
  <div
33
33
  ref="descriptionEmissionContainer"
@@ -18,14 +18,14 @@
18
18
  :alt="$t('Animator image')"
19
19
  class="img-box-circle"
20
20
  >
21
- <div class="participant-name">
22
- <img
21
+ <div class="d-flex align-items-center">
22
+ <span
23
23
  v-if="!activeParticipant && !isPodcastmaker && editRight"
24
- src="/img/caution.png"
25
- class="icon-caution"
26
24
  :title="$t('Participant have not podcasts')"
27
- :alt="$t('Participant have not podcasts')"
28
- >{{ name }}
25
+ class="saooti-warning text-danger me-1"/>
26
+ <div class="participant-name">
27
+ {{ name }}
28
+ </div>
29
29
  </div>
30
30
  <div
31
31
  ref="descriptionParticipantContainer"
@@ -19,14 +19,14 @@
19
19
  class="img-box"
20
20
  >
21
21
  <div class="emission-item-text">
22
- <div class="emission-name">
23
- <img
22
+ <div class="d-flex align-items-center">
23
+ <span
24
24
  v-if="!activePlaylist && !isPodcastmaker"
25
- class="icon-caution"
26
- src="/img/caution.png"
27
25
  :title="$t('Playlist have not podcasts')"
28
- :alt="$t('Playlist have not podcasts')"
29
- >{{ name }}
26
+ class="saooti-warning text-danger me-1"/>
27
+ <div class="emission-name">
28
+ {{ name }}
29
+ </div>
30
30
  </div>
31
31
  <div
32
32
  ref="descriptionPlaylistContainer"
@@ -6,11 +6,9 @@
6
6
  <div
7
7
  class="d-flex align-items-center bg-error-message p-2 rounded my-1"
8
8
  >
9
- <img
10
- src="/img/caution.png"
11
- :alt="$t('Warning')"
12
- class="icon-caution"
13
- >
9
+ <span
10
+ :title="$t('Warning')"
11
+ class="saooti-warning text-danger me-1"/>
14
12
  <div class="alert-text">
15
13
  {{ message }}
16
14
  </div>
@@ -14,7 +14,7 @@
14
14
  @click="onDisplayMenu(true)"
15
15
  >
16
16
  <img
17
- :src="!filterOrga || '' === imgUrl ? logoUrl : proxyImageUrl(imgUrl, '160')"
17
+ :src="!filterOrga || '' === imgUrl ? logoUrl : proxyImageUrl(imgUrl, '', '50')"
18
18
  :alt="!filterOrga || '' === imgUrl ? $t('Logo of main page') : $t('Visual', {name: $store.state.filter?.name})"
19
19
  :class="isEducation ? 'educationLogo' : ''"
20
20
  >
@@ -18,6 +18,7 @@
18
18
  @playing="onPlay"
19
19
  @durationChange="onTimeUpdate"
20
20
  @error="onError"
21
+ @seeked="onSeeked"
21
22
  />
22
23
  <PlayerCompact
23
24
  v-if="!largeVersion"
@@ -12,7 +12,7 @@
12
12
  :to="podcastShareUrl"
13
13
  >
14
14
  <img
15
- v-lazy="proxyImageUrl(podcastImage,'260')"
15
+ v-lazy="proxyImageUrl(podcastImage,'200')"
16
16
  :alt="$t('Podcast image')"
17
17
  class="img-box"
18
18
  >
@@ -46,7 +46,10 @@
46
46
  <div>{{ totalTime }}</div>
47
47
  </div>
48
48
  </div>
49
- <div class="d-flex align-items-center">
49
+ <div class="flex-grow-1 d-flex align-items-center w-100" v-if="''!=transcriptText">
50
+ <div class="flex-grow-1 p-1 text-center mx-3 transcript-bg rounded">{{transcriptText}}</div>
51
+ </div>
52
+ <div class="d-flex align-items-center flex-grow-1">
50
53
  <button
51
54
  class="btn fs-1 bg-transparent text-light saooti-backward"
52
55
  @click="seekClick(-15)"
@@ -131,10 +134,8 @@ export default defineComponent({
131
134
  <style lang="scss">
132
135
  .octopus-app{
133
136
  .player-container .img-box{
134
- @media (max-width: 960px) {
135
- width: 10rem;
136
- height: 10rem;
137
- }
137
+ width: 10rem;
138
+ height: 10rem;
138
139
  }
139
140
  .player-reduce-button{
140
141
  position: absolute;
@@ -163,5 +164,8 @@ export default defineComponent({
163
164
  flex-shrink: 0;
164
165
  cursor: pointer;
165
166
  }
167
+ .transcript-bg{
168
+ background: #3e3e3e;
169
+ }
166
170
  }
167
171
  </style>
@@ -9,12 +9,13 @@ export const selenium ={
9
9
  };
10
10
  export const imageProxy ={
11
11
  methods: {
12
- proxyImageUrl(url:string, width:string): string{
12
+ proxyImageUrl(url:string, width:string, height?:string): string{
13
13
  if(!url){
14
14
  return "";
15
15
  }
16
16
  if(state.octopusApi.imageUrl && url.includes('http')){
17
- return state.octopusApi.imageUrl+"image/"+btoa(url)+"?width="+width+"&useWebp=true";
17
+ const size = height ? "height="+height:"width="+width;
18
+ return state.octopusApi.imageUrl+"image/"+btoa(url)+"?"+size+"&useWebp=true";
18
19
  }
19
20
  return url;
20
21
  },
@@ -68,6 +68,9 @@ export const playerDisplay = defineComponent({
68
68
  if (this.$store.state.player.podcast) return this.$store.state.player.podcast.emission.name;
69
69
  return '';
70
70
  },
71
+ transcriptText():string{
72
+ return this.$store.state.player.transcript?.actualText ?? "";
73
+ }
71
74
  },
72
75
  created(){
73
76
  window.addEventListener('keydown', this.addKeyboardControl);
@@ -59,6 +59,7 @@ export const playerLogic = defineComponent({
59
59
  deep: true,
60
60
  handler(){
61
61
  this.reInitPlayer();
62
+ this.getTranscription();
62
63
  }
63
64
  },
64
65
  live: {
@@ -95,6 +96,40 @@ export const playerLogic = defineComponent({
95
96
  },
96
97
 
97
98
  methods: {
99
+ async getTranscription(): Promise<void>{
100
+ if(!this.podcast){
101
+ this.$store.commit('playerTranscript',undefined);
102
+ return;
103
+ }
104
+ const result = await octopusApi.fetchDataPublic<string>(11 , `response/${this.podcast.podcastId}`);
105
+ const arrayTranscript = this.parseSrt(result);
106
+ const actualText = arrayTranscript?.[0]?.startTime === 0 ? arrayTranscript[0].text : "";
107
+ this.$store.commit('playerTranscript',{actual: 0,actualText:actualText, value : arrayTranscript});
108
+ },
109
+ parseSrt(transcript: string){
110
+ var pattern = /(\d+)\n([\d:,]+)\s+-{2}\>\s+([\d:,]+)\n([\s\S]*?(?=\n{2}|$))/gm;
111
+ var result = [];
112
+ if (typeof(transcript) != 'string'){
113
+ return;
114
+ }
115
+ if (transcript == null){
116
+ return;
117
+ }
118
+ transcript = transcript.replace(/\r\n|\r|\n|\t/g, '\n');
119
+ let matches;
120
+ while ((matches = pattern.exec(transcript)) != null) {
121
+ result.push({
122
+ startTime: this.srtTimeToSeconds(matches[2]),
123
+ endTime: this.srtTimeToSeconds(matches[3]),
124
+ text: matches[4]
125
+ });
126
+ }
127
+ return result;
128
+ },
129
+ srtTimeToSeconds(time:string): number{
130
+ var a = time.split(':');
131
+ return (+a[0]) * 60 * 60 + (+a[1]) * 60 + (+parseFloat(a[2]));
132
+ },
98
133
  getDomain(): string{
99
134
  let domain = "";
100
135
  const domainArray: RegExpExecArray | null = /\.(.+)/.exec(window.location.host);
@@ -184,11 +219,18 @@ export const playerLogic = defineComponent({
184
219
  }
185
220
  return streamDuration;
186
221
  },
222
+ onTimeUpdateTranscript(currentTime:number){
223
+ if((this.$store.state.player.transcript?.value[this.$store.state.player.transcript?.actual]?.endTime ?? Infinity) < currentTime){
224
+ this.$store.state.player.transcript.actual +=1;
225
+ this.$store.state.player.transcript.actualText = this.$store.state.player.transcript?.value[this.$store.state.player.transcript?.actual].text ?? "";
226
+ }
227
+ },
187
228
  onTimeUpdatePodcast(streamDuration:number, currentTime:number){
188
229
  this.displayAlertBar = false;
189
230
  this.percentLiveProgress = 100;
190
231
  this.$store.commit('playerTotalTime', streamDuration);
191
232
  this.$store.commit('playerElapsed', currentTime / streamDuration);
233
+ this.onTimeUpdateTranscript(currentTime);
192
234
  },
193
235
  onTimeUpdateLive(streamDuration: number, currentTime:number){
194
236
  if(!this.live){return;}
@@ -236,6 +278,17 @@ export const playerLogic = defineComponent({
236
278
  }
237
279
  this.onTimeUpdateLive(streamDuration,mediaTarget.currentTime);
238
280
  },
281
+ onSeeked(event: Event):void {
282
+ const mediaTarget = (event.currentTarget as HTMLMediaElement);
283
+ const currentTime = mediaTarget.currentTime;
284
+ if(this.$store.state.player.transcript){
285
+ let newActual = 0;
286
+ while (currentTime > (this.$store.state.player.transcript.value[newActual]?.endTime ?? Infinity)){
287
+ newActual +=1;
288
+ }
289
+ this.$store.state.player.transcript.actual = newActual;
290
+ }
291
+ },
239
292
  onFinished(): void {
240
293
  this.setDownloadId(null);
241
294
  if (this.live) {
package/src/main.ts CHANGED
@@ -10,7 +10,7 @@ import paramStore from '@/store/paramStore';
10
10
  const nameEQ = 'octopus-language=';
11
11
  const ca = document.cookie.split(';');
12
12
  let language = "";
13
- for (let valueCookie in ca) {
13
+ for (let valueCookie of ca) {
14
14
  let c = valueCookie;
15
15
  while (c.charAt(0) == ' ') c = c.substring(1, c.length);
16
16
  if (0 === c.indexOf(nameEQ)){
@@ -2,7 +2,7 @@
2
2
  $body-bg: #f8fafc;
3
3
 
4
4
  // Typography
5
- $font-family-sans-serif: 'Roboto', sans-serif;
5
+ $font-family-sans-serif: sans-serif;
6
6
  $font-size-base: 0.9rem;
7
7
  $line-height-base: 1.6;
8
8
  $primary: #32815C;
@@ -80,6 +80,9 @@ export default createStore({
80
80
  playerSeekTime(state, seekTime) {
81
81
  state.player.seekTime = seekTime;
82
82
  },
83
+ playerTranscript(state, transcript) {
84
+ state.player.transcript = transcript;
85
+ },
83
86
 
84
87
  filterOrga(state, filter) {
85
88
  state.filter.organisationId = filter.orgaId;
@@ -11,4 +11,5 @@ export interface Player{
11
11
  live: Podcast|undefined;
12
12
  stop?: boolean;
13
13
  seekTime?: number;
14
+ transcript?:{actual: number, actualText:string, value : Array<{endTime: number, startTime:number, text: string}>};
14
15
  }
Binary file
Binary file