Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement audio translation system #147

Merged
merged 19 commits into from
Jul 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions webapp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"emoji-regex": "^9.0.0",
"hls.js": "^1.0.8",
"i18next": "^21.0.1",
"iso-639-1": "^2.1.15",
"janus-gateway": "git+https://[email protected]/meetecho/janus-gateway.git#7b010cd265991132158541ea80afcaa14bf4eb42",
"jwt-decode": "^3.1.2",
"lodash": "^4.17.20",
Expand Down
58 changes: 58 additions & 0 deletions webapp/src/components/AudioTranslationDropdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template lang="pug">
div.c-audio-translation
h4 Audio Translation
bunt-select(
name="audio-translation",
v-model="selectedLanguage",
:options="languageOptions",
label="Audio Translation",
@input="sendLanguageChange"
)
</template>
<script>
export default {
name: 'AudioTranslationDropdown',

Check failure on line 14 in webapp/src/components/AudioTranslationDropdown.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 1 tab but found 2 spaces
props: {
languages: {
type: Array,
required: true
}
},
data() {

Check failure on line 21 in webapp/src/components/AudioTranslationDropdown.vue

View workflow job for this annotation

GitHub Actions / build

Missing space before function parentheses
return {
selectedLanguage: null, // Selected language for audio translation

Check failure on line 23 in webapp/src/components/AudioTranslationDropdown.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 3 tabs but found 6 spaces
languageOptions: [] // Options for the dropdown

Check failure on line 24 in webapp/src/components/AudioTranslationDropdown.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 3 tabs but found 6 spaces
};

Check failure on line 25 in webapp/src/components/AudioTranslationDropdown.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 2 tabs but found 4 spaces

Check failure on line 25 in webapp/src/components/AudioTranslationDropdown.vue

View workflow job for this annotation

GitHub Actions / build

Extra semicolon
},

Check failure on line 26 in webapp/src/components/AudioTranslationDropdown.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 1 tab but found 2 spaces
watch: {

Check failure on line 27 in webapp/src/components/AudioTranslationDropdown.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 1 tab but found 2 spaces
languages: {

Check failure on line 28 in webapp/src/components/AudioTranslationDropdown.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 2 tabs but found 4 spaces
immediate: true,

Check failure on line 29 in webapp/src/components/AudioTranslationDropdown.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 3 tabs but found 6 spaces
handler(newLanguages) {
this.languageOptions = newLanguages.map(entry => entry.language); // Directly assigning the list of languages
}
}
},
methods: {
sendLanguageChange() {
const selected = this.languages.find(item => item.language == this.selectedLanguage)
const regex = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/;
const matchId = selected.url.match(regex);
this.$emit('languageChanged', matchId ? matchId[1] : null);
}
}
};
</script>

<style scoped>
.c-audio-translation {
margin-bottom: 3em;
}

.c-audio-translation h4 {
margin-bottom: 0.5em;
}

.bunt-select {
width: 100%;
}
</style>
10 changes: 9 additions & 1 deletion webapp/src/components/MediaSource.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default {
}
},
computed: {
...mapState(['streamingRoom']),
...mapState(['streamingRoom', 'youtubeTransUrl']),
...mapGetters(['autoplay']),
module () {
if (!this.room) {
Expand All @@ -65,6 +65,14 @@ export default {
this.destroyIframe()
this.initializeIframe()
}
},
youtubeTransUrl(youtubeTransUrl) {
if (!this.room) {
return
}
this.destroyIframe()
this.module.config.ytid = youtubeTransUrl
this.initializeIframe()
}
},
async mounted () {
Expand Down
6 changes: 5 additions & 1 deletion webapp/src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ export default new Vuex.Store({
autoplayUserSetting: !localStorage.disableAutoplay ? null : localStorage.disableAutoplay !== 'true',
stageStreamCollapsed: false,
now: moment(),
unblockedIframeDomains: new Set(JSON.parse(localStorage.unblockedIframeDomains || '[]'))
unblockedIframeDomains: new Set(JSON.parse(localStorage.unblockedIframeDomains || '[]')),
youtubeTransUrl: null
},
getters: {
hasPermission (state) {
Expand Down Expand Up @@ -83,6 +84,9 @@ export default new Vuex.Store({
},
updateNow (state) {
state.now = moment()
},
updateYoutubeTransAudio(state, youtubeTransUrl){
state.youtubeTransUrl = youtubeTransUrl
}
},
actions: {
Expand Down
34 changes: 34 additions & 0 deletions webapp/src/views/admin/rooms/types-edit/stage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
bunt-icon-button(@click="deleteAlternativeStream(i)") delete-outline
bunt-button(@click="$set(modules['livestream.native'].config, 'alternatives', modules['livestream.native'].config.alternatives || []); modules['livestream.native'].config.alternatives.push({label: '', hls_url: ''})") Add alternative stream
bunt-input(v-else-if="modules['livestream.youtube']", name="ytid", v-model="modules['livestream.youtube'].config.ytid", label="YouTube Video ID", :validation="$v.modules['livestream.youtube'].config.ytid")
// Language and URL input for YouTube stream
.language-urls(v-if="modules['livestream.youtube']")
h4 Languages and YouTube URLs
.language-url-entry(v-for="(entry, index) in modules['livestream.youtube'].config.languageUrls" :key="index")
bunt-select(name="language", v-model="entry.language", :options="ISO_LANGUAGE_OPTIONS", label="Language")
bunt-input(name="url" v-model="entry.url" label="YouTube URL")
bunt-icon-button(@click="deleteLanguageUrl(index)") delete-outline
bunt-button(@click="addLanguageUrl") + Add Language and URL
bunt-input(v-else-if="modules['livestream.iframe']", name="iframe-player", v-model="modules['livestream.iframe'].config.url", label="Iframe player url", hint="iframe player should be autoplaying and support resizing to small sizes for background playing")
sidebar-addons(v-bind="$props")
</template>
Expand All @@ -23,6 +31,7 @@ import UploadUrlInput from 'components/UploadUrlInput'
import mixin from './mixin'
import SidebarAddons from './SidebarAddons'
import {youtubeid} from 'lib/validators'
import ISO6391 from 'iso-639-1';

const STREAM_SOURCE_OPTIONS = [
{ id: 'hls', label: 'HLS', module: 'livestream.native' },
Expand All @@ -39,7 +48,10 @@ export default {
data () {
return {
STREAM_SOURCE_OPTIONS,
ISO_LANGUAGE_OPTIONS: this.getLanguageOptions(),
b_streamSource: null,
// Initial empty array for languages and URLs
b_languageUrls: []
}
},
validations: {
Expand Down Expand Up @@ -70,16 +82,38 @@ export default {
this.b_streamSource = 'hls'
} else if (this.modules['livestream.youtube']) {
this.b_streamSource = 'youtube'
// languageUrls is set in the created lifecycle hook
if (!this.modules['livestream.youtube'].config.languageUrls) {
this.$set(this.modules['livestream.youtube'].config, 'languageUrls', [])
}
} else if (this.modules['livestream.iframe']) {
this.b_streamSource = 'iframe'
}
},
methods: {
// Added methods addLanguageUrl and deleteLanguageUrl to manage dynamic fields for language and URL input
addLanguageUrl () {
if (!this.modules['livestream.youtube'].config.languageUrls) {
this.$set(this.modules['livestream.youtube'].config, 'languageUrls', [])
}
this.modules['livestream.youtube'].config.languageUrls.push({ language: '', url: '' })
},
deleteLanguageUrl (index) {
if (this.modules['livestream.youtube'].config.languageUrls) {
this.modules['livestream.youtube'].config.languageUrls.splice(index, 1)
}
},
deleteAlternativeStream (index) {
this.modules['livestream.native'].config.alternatives.splice(index, 1)
if (this.modules['livestream.native'].config.alternatives.length === 0) {
this.modules['livestream.native'].config.alternatives = undefined
}
},
getLanguageOptions() {
return ISO6391.getAllCodes().map(code => ({
id: ISO6391.getName(code),
label: ISO6391.getName(code),
}));
}
}
}
Expand Down
20 changes: 17 additions & 3 deletions webapp/src/views/rooms/item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
.stage-tools(v-if="modules['livestream.native'] || modules['livestream.youtube'] || modules['livestream.iframe'] || modules['call.janus']")
reactions-bar(:expanded="true", @expand="activeStageTool = 'reaction'")
//- reactions-bar(:expanded="activeStageTool === 'reaction'", @expand="activeStageTool = 'reaction'")
// Added dropdown menu for audio translations near the reactions bar
AudioTranslationDropdown(:languages="languages", @languageChanged="handleLanguageChange")
media-source-placeholder(v-else-if="modules['call.bigbluebutton'] || modules['call.zoom']")
roulette(v-else-if="modules['networking.roulette'] && $features.enabled('roulette')", :module="modules['networking.roulette']", :room="room")
landing-page(v-else-if="modules['page.landing']", :module="modules['page.landing']")
Expand Down Expand Up @@ -44,10 +46,12 @@ import Polls from 'components/Polls'
import PosterHall from 'components/PosterHall'
import Questions from 'components/Questions'
import MediaSourcePlaceholder from 'components/MediaSourcePlaceholder'
import AudioTranslationDropdown from 'components/AudioTranslationDropdown';

export default {
name: 'Room',
components: { Chat, Exhibition, LandingPage, MarkdownPage, StaticPage, IframePage, ReactionsBar, ReactionsOverlay, UserListPage, Roulette, Polls, PosterHall, Questions, MediaSourcePlaceholder },
components: { Chat, Exhibition, LandingPage, MarkdownPage, StaticPage, IframePage, ReactionsBar, ReactionsOverlay, UserListPage, Roulette, Polls, PosterHall,
Questions, MediaSourcePlaceholder, AudioTranslationDropdown },
props: {
room: Object,
modules: Object
Expand All @@ -60,7 +64,8 @@ export default {
questions: false,
polls: false
},
activeStageTool: null // reaction, qa
activeStageTool: null, // reaction, qa
languages: [] // Languages for the dropdown menu
}
},
computed: {
Expand All @@ -73,19 +78,28 @@ export default {
this.unreadTabs[tab] = false
}
},
mounted () {
created () {
if (this.modules['chat.native']) {
this.activeSidebarTab = 'chat'
} else if (this.modules.question) {
this.activeSidebarTab = 'questions'
} else if (this.modules.poll) {
this.activeSidebarTab = 'polls'
}
// Populate languages from the languages added by the admin
if (this.modules['livestream.youtube'] && this.modules['livestream.youtube'].config.languageUrls) {
this.languages = this.modules['livestream.youtube'].config.languageUrls;
this.languages.unshift({language: 'Default', url: `https://www.youtube.com/watch?v=${this.modules['livestream.youtube'].config.ytid}`})

}
},
methods: {
changedTabContent (tab) {
if (tab === this.activeSidebarTab) return
this.unreadTabs[tab] = true
},
handleLanguageChange(selectedLanguage) {
this.$store.commit('updateYoutubeTransAudio', selectedLanguage)
}
}
}
Expand Down
Loading