@mindedge/vuetify-player 0.3.1 → 0.4.1

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.
@@ -2,33 +2,40 @@
2
2
  <v-card>
3
3
  <v-card-title>{{ t(language, 'playlist.up_next') }}</v-card-title>
4
4
  <v-card-text>
5
- <v-list>
5
+ <v-list class="playlist-list">
6
6
  <v-list-item-group v-model="sourceIndex">
7
- <v-list-item
7
+ <v-tooltip
8
+ bottom
8
9
  v-for="(source, index) of playlist"
9
10
  :key="index + 'playlistSources'"
10
- @click="onPlaylistSelect(index)"
11
11
  >
12
- <v-list-item-icon>
13
- <v-avatar
14
- v-if="getPoster(source.poster, poster)"
15
- tile
12
+ <template #activator="{ on, attrs }">
13
+ <v-list-item
14
+ class="pl-1"
15
+ v-bind="attrs"
16
+ v-on="on"
17
+ @click="onPlaylistSelect(index)"
16
18
  >
17
- <img :src="getPoster(source.poster, poster)" />
18
- </v-avatar>
19
- <v-skeleton-loader
20
- v-if="!getPoster(source.poster, poster)"
21
- class="ma-3"
22
- type="avatar"
23
- tile
24
- ></v-skeleton-loader>
25
- </v-list-item-icon>
26
- <v-list-item-content>
27
- <v-tooltip bottom>
28
- <template v-slot:activator="{ on, attrs }">
19
+ <v-list-item-icon class="ma-2">
20
+ <v-avatar
21
+ v-if="getPoster(source.poster, poster)"
22
+ tile
23
+ >
24
+ <img
25
+ :src="
26
+ getPoster(source.poster, poster)
27
+ "
28
+ />
29
+ </v-avatar>
30
+ <v-skeleton-loader
31
+ v-if="!getPoster(source.poster, poster)"
32
+ class="ma-3"
33
+ type="avatar"
34
+ tile
35
+ ></v-skeleton-loader>
36
+ </v-list-item-icon>
37
+ <v-list-item-content>
29
38
  <div
30
- v-bind="attrs"
31
- v-on="on"
32
39
  class="text-lg-subtitle-1 text-truncate"
33
40
  >
34
41
  {{
@@ -36,16 +43,16 @@
36
43
  t(language, 'playlist.default_name')
37
44
  }}
38
45
  </div>
39
- </template>
40
- <span>
41
- {{
42
- source.name ||
43
- t(language, 'playlist.default_name')
44
- }}
45
- </span>
46
- </v-tooltip>
47
- </v-list-item-content>
48
- </v-list-item>
46
+ </v-list-item-content>
47
+ </v-list-item>
48
+ </template>
49
+ <span>
50
+ {{
51
+ source.name ||
52
+ t(language, 'playlist.default_name')
53
+ }}
54
+ </span>
55
+ </v-tooltip>
49
56
  </v-list-item-group>
50
57
  </v-list>
51
58
  </v-card-text>
@@ -122,17 +129,8 @@ export default {
122
129
  </script>
123
130
 
124
131
  <style scoped>
125
- .captions-list {
126
- max-height: 10em;
132
+ .playlist-list {
133
+ max-height: 200px;
127
134
  overflow-y: scroll;
128
- /* Fade the top/bottom 20% effect. The "red" mask is so the scrollbar doesn't get this effect*/
129
- mask: linear-gradient(90deg, rgba(255, 0, 0, 0) 98%, rgba(255, 0, 0, 1) 98%),
130
- linear-gradient(
131
- 0deg,
132
- rgba(0, 0, 0, 0) 0%,
133
- rgba(0, 0, 0, 1) 20%,
134
- rgba(0, 0, 0, 1) 80%,
135
- rgba(0, 0, 0, 0) 100%
136
- );
137
135
  }
138
136
  </style>
@@ -0,0 +1,98 @@
1
+ <template>
2
+ <!-- Settings -->
3
+ <v-menu :attach="attach" top left offset-y :close-on-content-click="false">
4
+ <template #activator="{ on, attrs }">
5
+ <v-btn small text v-bind="attrs" v-on="on">
6
+ <v-icon>mdi-cog</v-icon>
7
+ <span class="d-sr-only">{{
8
+ t(language, 'player.toggle_settings')
9
+ }}</span>
10
+ </v-btn>
11
+ </template>
12
+
13
+ <v-list>
14
+ <v-list-item v-if="attributes.captionsmenu">
15
+ <v-list-item-title>
16
+ <v-switch
17
+ :input-value="captionsVisible"
18
+ :label="t(language, 'captions.show_transcript')"
19
+ @change="$emit('update:captions-visible', $event)"
20
+ ></v-switch>
21
+ </v-list-item-title>
22
+ </v-list-item>
23
+ <v-list-item>
24
+ <v-list-item-title>
25
+ <v-icon>mdi-play-speed</v-icon>
26
+ {{ t(language, 'player.playback_speed') }}
27
+ </v-list-item-title>
28
+ </v-list-item>
29
+ <v-list-item>
30
+ <v-list-item-title class="text-center">
31
+ <v-btn
32
+ small
33
+ :disabled="state.playbackRateIndex === 0"
34
+ @click="onPlaybackSpeed(state.playbackRateIndex - 1)"
35
+ >
36
+ <v-icon> mdi-clock-minus-outline </v-icon>
37
+ <span class="d-sr-only">{{
38
+ t(language, 'player.playback_decrease')
39
+ }}</span>
40
+ </v-btn>
41
+ <span class="pl-2 pr-2"
42
+ >{{
43
+ attributes.playbackrates[state.playbackRateIndex]
44
+ }}x</span
45
+ >
46
+ <v-btn
47
+ small
48
+ :disabled="
49
+ state.playbackRateIndex >=
50
+ attributes.playbackrates.length - 1
51
+ "
52
+ @click="onPlaybackSpeed(state.playbackRateIndex + 1)"
53
+ >
54
+ <v-icon> mdi-clock-plus-outline </v-icon>
55
+ <span class="d-sr-only">{{
56
+ t(language, 'player.playback_increase')
57
+ }}</span>
58
+ </v-btn>
59
+ </v-list-item-title>
60
+ </v-list-item>
61
+ </v-list>
62
+ </v-menu>
63
+ </template>
64
+
65
+ <script>
66
+ import { t } from '../../i18n/i18n'
67
+
68
+ export default {
69
+ name: 'SettingsMenu',
70
+ components: {},
71
+ props: {
72
+ language: { type: String, required: false, default: 'en-US' },
73
+ attach: { type: null, required: false, default: false },
74
+ state: {
75
+ type: Object,
76
+ required: true,
77
+ },
78
+ attributes: {
79
+ type: Object,
80
+ required: true,
81
+ },
82
+ captionsVisible: { type: Boolean, required: false, default: undefined },
83
+ },
84
+ emits: ['update:captions-visible', 'change:playback-rate-index'],
85
+ watch: {},
86
+ computed: {},
87
+ data() {
88
+ return { t }
89
+ },
90
+ beforeMount() {},
91
+ mounted() {},
92
+ methods: {
93
+ onPlaybackSpeed(index) {
94
+ this.$emit('change:playback-rate-index', index)
95
+ },
96
+ },
97
+ }
98
+ </script>
@@ -34,9 +34,13 @@ export default {
34
34
  watch: {},
35
35
  computed: {
36
36
  playerClass() {
37
- let classList = 'player-' + this.type
37
+ let classList = 'player-' + this.resolvedType
38
38
  return classList
39
39
  },
40
+ resolvedType() {
41
+ // Youtube is always a video
42
+ return 'video'
43
+ },
40
44
  },
41
45
  data() {
42
46
  return {
@@ -1,7 +1,23 @@
1
1
  <template>
2
2
  <div>
3
- <v-row>
4
- <v-col :cols="playerCols">
3
+ <v-row tabindex="0">
4
+ <v-col cols="12">
5
+ <div v-if="parseSourceType(current.src.sources) === null">
6
+ <div class="player-skeleton--no-source">
7
+ <slot name="no-source">
8
+ <div>
9
+ <v-icon x-large>mdi-cloud-question</v-icon>
10
+ <p>
11
+ {{ t(language, 'player.not_configured') }}
12
+ </p>
13
+ </div>
14
+ </slot>
15
+ </div>
16
+ <v-skeleton-loader
17
+ type="image, image, list-item-avatar"
18
+ class="player-skeleton"
19
+ ></v-skeleton-loader>
20
+ </div>
5
21
  <YoutubePlayer
6
22
  ref="youtubePlayer"
7
23
  v-if="parseSourceType(current.src.sources) === 'youtube'"
@@ -9,6 +25,9 @@
9
25
  :type="current.type"
10
26
  :attributes="current.attributes"
11
27
  :src="current.src"
28
+ :elevation="flat ? 0 : elevation"
29
+ @focusin="onFocusin"
30
+ @focusout="onFocusout"
12
31
  @click:fullscreen="onFullscreen"
13
32
  ></YoutubePlayer>
14
33
  <Html5Player
@@ -18,6 +37,14 @@
18
37
  :type="current.type"
19
38
  :attributes="current.attributes"
20
39
  :src="current.src"
40
+ :captions-expanded.sync="captionsExpandedState"
41
+ :captions-hide-expand="captionsHideExpand"
42
+ :captions-paragraph-view="captionsParagraphView"
43
+ :captions-hide-paragraph-view="captionsHideParagraphView"
44
+ :captions-autoscroll="captionsAutoscroll"
45
+ :captions-hide-autoscroll="captionsHideAutoscroll"
46
+ :captions-visible.sync="captionsVisibleState"
47
+ :elevation="flat ? 0 : elevation"
21
48
  @load="$emit('load', $event)"
22
49
  @ended="onEnded"
23
50
  @loadeddata="onLoadeddata"
@@ -37,19 +64,29 @@
37
64
  @abort="$emit('abort', $event)"
38
65
  @mouseover="$emit('mouseover', $event)"
39
66
  @mouseout="$emit('mouseout', $event)"
67
+ @update:captions-expanded="
68
+ $emit('update:captions-expanded', $event)
69
+ "
70
+ @update:captions-paragraph-view="
71
+ $emit('update:captions-paragraph-view', $event)
72
+ "
73
+ @update:captions-autoscroll="
74
+ $emit('update:captions-autoscroll', $event)
75
+ "
40
76
  @click:fullscreen="onFullscreen"
41
77
  @click:pictureinpicture="onPictureInPicture"
42
78
  @click:remoteplayback="onRemoteplayback"
43
79
  @click:captions-expand="onClickCaptionsExpand"
44
- @click:captions-paragraph="onClickCaptionsParagraph"
80
+ @click:captions-paragraph-view="onClickCaptionsParagraph"
81
+ @click:captions-autoscroll="onClickCaptionsAutoscroll"
82
+ @click:captions-close="onClickCaptionsClose"
83
+ @focusin="onFocusin"
84
+ @focusout="onFocusout"
45
85
  ></Html5Player>
46
86
  </v-col>
47
87
 
48
88
  <!-- Playlist stuff -->
49
- <v-col
50
- v-if="playlistmenu && playlist.length > 1"
51
- :cols="playlistCols"
52
- >
89
+ <v-col v-if="showPlaylist" cols="12" class="mt-0 pt-0">
53
90
  <PlaylistMenu
54
91
  v-model="sourceIndex"
55
92
  :language="language"
@@ -63,6 +100,7 @@
63
100
  </template>
64
101
 
65
102
  <script>
103
+ import { t } from '../i18n/i18n'
66
104
  import YoutubePlayer from './Media/YoutubePlayer.vue'
67
105
  import Html5Player from './Media/Html5Player.vue'
68
106
  import PlaylistMenu from './Media/PlaylistMenu.vue'
@@ -90,7 +128,7 @@ export default {
90
128
  return []
91
129
  },
92
130
  },
93
- type: { type: String, required: false, default: 'video' }, // Allowed video|audio. In audio mode the player has a max-height of 40px
131
+ type: { type: String, required: false, default: 'auto' }, // Allowed auto|video|audio. In audio mode the player has a max-height of 40px
94
132
  autoplay: { type: Boolean, required: false, default: false }, // Autoplay on load. It's in the spec but DON'T USE THIS
95
133
  autopictureinpicture: {
96
134
  type: Boolean,
@@ -123,6 +161,42 @@ export default {
123
161
  poster: { type: String, required: false, default: '' }, // Overridden with the playlist.poster if one is set there
124
162
  preload: { type: String, required: false, default: '' },
125
163
  captionsmenu: { type: Boolean, required: false, default: true }, // Show the captions below the video
164
+ captionsExpanded: {
165
+ type: Boolean,
166
+ required: false,
167
+ default: undefined,
168
+ },
169
+ captionsHideExpand: { type: Boolean, required: false, default: true },
170
+ captionsParagraphView: {
171
+ type: Boolean,
172
+ required: false,
173
+ default: undefined,
174
+ },
175
+ captionsHideParagraphView: {
176
+ type: Boolean,
177
+ required: false,
178
+ default: false,
179
+ },
180
+ captionsAutoscroll: {
181
+ type: Boolean,
182
+ required: false,
183
+ default: undefined,
184
+ },
185
+ captionsHideAutoscroll: {
186
+ type: Boolean,
187
+ required: false,
188
+ default: false,
189
+ },
190
+ captionsHideClose: {
191
+ type: Boolean,
192
+ required: false,
193
+ default: false,
194
+ },
195
+ captionsVisible: {
196
+ type: Boolean,
197
+ required: false,
198
+ default: true,
199
+ },
126
200
  playlistmenu: { type: Boolean, required: false, default: true }, // Show the playlist menu if there's multiple videos
127
201
  playlistautoadvance: { type: Boolean, required: false, default: true }, // Play the next source group
128
202
  playbackrates: {
@@ -132,7 +206,40 @@ export default {
132
206
  return [0.5, 1, 1.5, 2]
133
207
  },
134
208
  }, // Default playback speeds
209
+ elevation: { type: [Number, String], required: false, default: 2 },
210
+ flat: { type: Boolean, required: false, default: false },
135
211
  },
212
+ emits: [
213
+ 'load',
214
+ 'loadeddata',
215
+ 'loadedmetadata',
216
+ 'play',
217
+ 'pause',
218
+ 'seeking',
219
+ 'timeupdate',
220
+ 'progress',
221
+ 'canplay',
222
+ 'waiting',
223
+ 'canplaythrough',
224
+ 'error',
225
+ 'emptied',
226
+ 'ratechange',
227
+ 'stalled',
228
+ 'abort',
229
+ 'mouseover',
230
+ 'mouseout',
231
+ 'ended',
232
+ 'click:pictureinpicture',
233
+ 'click:fullscreen',
234
+ 'click:captions-expand',
235
+ 'click:captions-paragraph-view',
236
+ 'click:captions-autoscroll',
237
+ 'click:captions-close',
238
+ 'update:captions-expanded',
239
+ 'update:captions-paragraph-view',
240
+ 'update:captions-autoscroll',
241
+ 'update:captions-visible',
242
+ ],
136
243
  watch: {},
137
244
  computed: {
138
245
  player() {
@@ -180,17 +287,9 @@ export default {
180
287
  },
181
288
  playlistCols() {
182
289
  // Captions collapsed, playlist will appear on the right
183
- if (
184
- !this.captionsExpanded &&
185
- this.playlistmenu &&
186
- this.playlist.length > 1
187
- ) {
290
+ if (!this.captionsExpandedState && this.showPlaylist) {
188
291
  return 4
189
- } else if (
190
- this.captionsExpanded &&
191
- this.playlistmenu &&
192
- this.playlist.length > 1
193
- ) {
292
+ } else if (this.captionsExpandedState && this.showPlaylist) {
194
293
  // Captions expanded, playlist will appear as a new row on the bottom of everything
195
294
  return 12
196
295
  } else {
@@ -198,21 +297,52 @@ export default {
198
297
  }
199
298
  },
200
299
  playerCols() {
201
- if (
202
- this.captionsExpanded ||
203
- !this.playlistmenu ||
204
- this.playlist.length <= 1
205
- ) {
300
+ if (this.captionsExpandedState || !this.showPlaylist) {
206
301
  return 12
207
302
  } else {
208
303
  return 8
209
304
  }
210
305
  },
306
+ captionsVisibleState: {
307
+ get() {
308
+ if (typeof this.captionsVisible !== 'undefined') {
309
+ return this.captionsVisible
310
+ } else {
311
+ return this.captions.visible
312
+ }
313
+ },
314
+ set(v) {
315
+ this.$emit('update:captions-visible', v)
316
+ this.captions.visible = v
317
+ },
318
+ },
319
+ captionsExpandedState: {
320
+ get() {
321
+ if (typeof this.captionsExpanded !== 'undefined') {
322
+ return this.captionsExpanded
323
+ } else {
324
+ return this.captions.expanded
325
+ }
326
+ },
327
+ set(v) {
328
+ this.$emit('update:captions-expanded', v)
329
+ this.captions.expanded = v
330
+ },
331
+ },
332
+ showPlaylist() {
333
+ return this.playlistmenu && this.playlist.length > 1
334
+ },
211
335
  },
212
336
  data() {
213
337
  return {
338
+ t,
214
339
  sourceIndex: 0,
215
- captionsExpanded: false,
340
+ captions: {
341
+ visible: true,
342
+ expanded: false,
343
+ },
344
+ mediaFocus: false,
345
+ keyListener: null,
216
346
  }
217
347
  },
218
348
  methods: {
@@ -273,16 +403,25 @@ export default {
273
403
  }
274
404
  },
275
405
  onClickCaptionsExpand(expanded) {
276
- this.captionsExpanded = expanded
277
406
  this.$emit('click:captions-expand', expanded)
278
407
  },
279
408
  onClickCaptionsParagraph(isParagraph) {
280
- this.$emit('click:captions-paragraph', isParagraph)
409
+ this.$emit('click:captions-paragraph-view', isParagraph)
410
+ },
411
+ onClickCaptionsAutoscroll(autoscroll) {
412
+ this.$emit('click:captions-autoscroll', autoscroll)
413
+ },
414
+ onClickCaptionsClose() {
415
+ this.$emit('click:captions-close')
281
416
  },
282
417
  onPlaylistSelect(index) {
283
418
  this.sourceIndex = parseInt(index)
284
- this.player.load()
285
- this.player.play()
419
+
420
+ // If we give a bad media type then the player won't resolve
421
+ if (this.player && typeof this.player.load !== 'undefined') {
422
+ this.player.load()
423
+ this.player.play()
424
+ }
286
425
  },
287
426
  parseSourceType(sources) {
288
427
  const ytRegex =
@@ -304,6 +443,44 @@ export default {
304
443
  return 'html5'
305
444
  }
306
445
  },
446
+ onFocusin() {
447
+ this.mediaFocus = true
448
+ if (this.player && this.player.$el) {
449
+ this.player.$el.addEventListener('keydown', this.onKeydown)
450
+ }
451
+ },
452
+ onFocusout() {
453
+ this.mediaFocus = false
454
+ if (this.player && this.player.$el) {
455
+ this.player.$el.removeEventListener('keydown', this.onKeydown)
456
+ }
457
+ },
458
+ onKeydown(e) {
459
+ // Only process keyboard events if the media is focused
460
+ // This is just in case the media lost focus but the event listener wasn't removed for some reason
461
+ if (this.mediaFocus) {
462
+ e.preventDefault()
463
+ const map = {
464
+ Space: { cb: this.player.playToggle, params: [e] },
465
+ KeyK: { cb: this.player.playToggle, params: [e] },
466
+ KeyM: { cb: this.player.muteToggle, params: [e] },
467
+ ArrowLeft: { cb: this.player.rewind, params: [5] },
468
+ ArrowRight: { cb: this.player.fastForward, params: [5] },
469
+ KeyJ: { cb: this.player.rewind, params: [10] },
470
+ KeyL: { cb: this.player.fastForward, params: [10] },
471
+ ArrowUp: { cb: this.player.volumeAdjust, params: [0.1] },
472
+ ArrowDown: { cb: this.player.volumeAdjust, params: [-0.1] },
473
+ KeyF: { cb: this.player.fullscreenToggle, params: [e] },
474
+ KeyC: { cb: this.player.CCToggle, params: [e] },
475
+ }
476
+ if (
477
+ typeof map[e.code] !== 'undefined' &&
478
+ typeof map[e.code].cb !== 'undefined'
479
+ ) {
480
+ map[e.code].cb(...map[e.code].params)
481
+ }
482
+ }
483
+ },
307
484
  },
308
485
  beforeCreate() {},
309
486
  beforeMount() {},
@@ -311,3 +488,16 @@ export default {
311
488
  beforeDestroy() {},
312
489
  }
313
490
  </script>
491
+
492
+ <style scoped>
493
+ .player-skeleton {
494
+ aspect-ratio: 16 / 9;
495
+ overflow: hidden;
496
+ }
497
+ .player-skeleton--no-source {
498
+ height: 0px;
499
+ position: relative;
500
+ top: 100px;
501
+ text-align: center;
502
+ }
503
+ </style>
package/src/i18n/en-US.js CHANGED
@@ -10,6 +10,7 @@ export default {
10
10
  next: 'Play next item in playlist',
11
11
  },
12
12
  player: {
13
+ not_configured: 'Media not configured',
13
14
  playback_speed: 'Playback Speed',
14
15
  playback_decrease: 'Decrease playback speed',
15
16
  playback_increase: 'Increase playback speed',
@@ -27,9 +28,17 @@ export default {
27
28
  no_support: "Sorry, your browser doesn't support embedded videos.",
28
29
  },
29
30
  captions: {
31
+ show_transcript: 'Show Transcript',
32
+ close: 'Close',
33
+ search: 'Search',
34
+ none_found: "No captions found for '{0}'",
30
35
  expand: 'Expand',
31
36
  collapse: 'Collapse',
32
37
  view_as_paragraph: 'View as paragraph',
33
38
  view_as_captions: 'View as captions',
39
+ enable_autoscroll: 'Enable auto-scroll for transcript as media plays',
40
+ disable_autoscroll: 'Disable auto-scroll for transcript as media plays',
41
+ autoscroll_enabled: 'Autoscroll transcript enabled',
42
+ autoscroll_disabled: 'Autoscroll transcript disabled',
34
43
  },
35
44
  }
package/src/i18n/es-ES.js CHANGED
@@ -10,6 +10,7 @@ export default {
10
10
  next: 'Reproducir el siguiente elemento en la lista de reproducción',
11
11
  },
12
12
  player: {
13
+ not_configured: 'Medios no configurados',
13
14
  playback_speed: 'Velocidad de reproducción',
14
15
  playback_decrease: 'Disminuir la velocidad de reproducción',
15
16
  playback_increase: 'Aumentar la velocidad de reproducción',
@@ -27,9 +28,20 @@ export default {
27
28
  no_support: 'Lo sentimos, su navegador no admite vídeos incrustados. ',
28
29
  },
29
30
  captions: {
31
+ show_transcript: 'Mostrar transcripción',
32
+ close: 'Cerca',
33
+ search: 'Buscar',
34
+ none_found: "No se encontraron subtítulos para '{0}'",
30
35
  expand: 'Expandir',
31
36
  collapse: 'Colapsar',
32
37
  view_as_paragraph: 'Ver como párrafo',
33
38
  view_as_captions: 'Ver como subtítulos',
39
+ enable_autoscroll:
40
+ 'Habilite el desplazamiento automático para la transcripción mientras se reproducen los medios',
41
+ disable_autoscroll:
42
+ 'Deshabilite el desplazamiento automático para la transcripción mientras se reproducen los medios',
43
+ autoscroll_enabled:
44
+ 'Transcripción con desplazamiento automático habilitada',
45
+ autoscroll_disabled: 'Transcripción automática deshabilitado',
34
46
  },
35
47
  }
package/src/i18n/i18n.js CHANGED
@@ -18,7 +18,7 @@ function get(obj, path) {
18
18
  return get(obj[parts[0]], parts.slice(1).join('.'))
19
19
  }
20
20
 
21
- const t = (lang, path) => {
21
+ const t = (lang, path, replacements = []) => {
22
22
  if (typeof i18n[lang] === 'undefined') {
23
23
  console.warn(
24
24
  '[VuetifyPlayer] No support for locale ' +
@@ -35,6 +35,11 @@ const t = (lang, path) => {
35
35
  console.warn(
36
36
  '[VuetifyPlayer] localization path ' + path + ' does not exist'
37
37
  )
38
+ } else {
39
+ // Apply replacements
40
+ for (const key in replacements) {
41
+ localized = localized.replaceAll('{' + key + '}', replacements[key])
42
+ }
38
43
  }
39
44
 
40
45
  return localized
package/src/i18n/sv-SE.js CHANGED
@@ -10,6 +10,7 @@ export default {
10
10
  next: 'Spela nästa',
11
11
  },
12
12
  player: {
13
+ not_configured: 'Media inte konfigurerad',
13
14
  playback_speed: 'Uppspelningshastighet',
14
15
  playback_decrease: 'Minska uppspelningshastigheten',
15
16
  playback_increase: 'Öka uppspelningshastigheten',
@@ -27,9 +28,19 @@ export default {
27
28
  no_support: 'Tyvärr, din webbläsare stöder inte inbäddade videor.',
28
29
  },
29
30
  captions: {
31
+ show_transcript: 'Visa avskrift',
32
+ close: 'Stäng',
33
+ search: 'Sök',
34
+ none_found: "Inga bildtexter hittades för '{0}'",
30
35
  expand: 'Bygga ut',
31
36
  collapse: 'Kollaps',
32
37
  view_as_paragraph: 'Visa som stycke',
33
38
  view_as_captions: 'Visa som bildtexter',
39
+ enable_autoscroll:
40
+ 'Aktivera automatisk rullning för transkription när media spelas upp',
41
+ disable_autoscroll:
42
+ 'Inaktivera automatisk rullning för transkribering när media spelas upp',
43
+ autoscroll_enabled: 'Autoscroll-transkription aktiverad',
44
+ autoscroll_disabled: 'Autoscroll-transkription inaktiverad',
34
45
  },
35
46
  }