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

Adding dynamic language and URL input fields #133

Merged
merged 24 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b29b559
add new logo
untari Jun 26, 2024
667990e
add eventyay video logo
untari Jun 26, 2024
97b8add
Merge branch 'development' into adapt-branding
untari Jun 26, 2024
07841f9
update logo across multiple components
untari Jun 26, 2024
1a54435
rename to eventyay
untari Jun 27, 2024
4d28efa
changing logos into blue color
untari Jun 27, 2024
2476f07
add dynamic language and URL input fields with a button to add more e…
untari Jul 1, 2024
51066cb
Created AudioTranslationDropdown to display audio translation options.
untari Jul 4, 2024
9418f25
Integrated audio dropdown component into item.vue
untari Jul 4, 2024
81b93e8
fixed the label position
untari Jul 4, 2024
c1de7a7
Implement a method to handle the selection of different audio transla…
odkhang Jul 5, 2024
4012320
import iso-639-1 package
odkhang Jul 8, 2024
cad57a4
update package-lock.json
odkhang Jul 8, 2024
05bde03
mute the audio from the default video when language track is selecte…
untari Jul 19, 2024
5bbaa67
Mute video and play selected language audio on language change
untari Jul 22, 2024
af87115
fixed duplicate default language entries
untari Jul 22, 2024
3267273
Modify the audio translation to handle YouTube video IDs instead of URLs
untari Jul 22, 2024
5ed1a75
Merge branch 'development' into translation-system
untari Jul 22, 2024
2f3d25a
Merge branch 'development' into translation-system
untari Jul 22, 2024
d5d6d51
fix indentations
untari Jul 23, 2024
05f4ccd
remove extra columns
untari Jul 23, 2024
c73c04e
remove semicolumn
untari Jul 23, 2024
01acc5f
fix lint errors
untari Jul 23, 2024
69118dc
Merge branch 'development' into translation-system
marcoag Jul 23, 2024
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
64 changes: 31 additions & 33 deletions webapp/src/components/AudioTranslationDropdown.vue
Original file line number Diff line number Diff line change
@@ -1,58 +1,56 @@
<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"
h4 Audio Translation
bunt-select(
name="audio-translation",
v-model="selectedLanguage",
:options="languageOptions",
label="Audio Translation",
@input="sendLanguageChange"
)
</template>
<script>
export default {
name: 'AudioTranslationDropdown',
name: 'AudioTranslationDropdown',
props: {
languages: {
type: Array,
required: true
}
},
data() {
data () {
return {
selectedLanguage: null, // Selected language for audio translation
languageOptions: [] // Options for the dropdown
};
},
watch: {
languages: {
immediate: true,
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);
}
}
};
selectedLanguage: null, // Selected language for audio translation
languageOptions: [] // Options for the dropdown
}
},
watch: {
languages: {
immediate: true,
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)
this.$emit('languageChanged', selected.youtube_id || null)
}
}
}
</script>

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

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

.bunt-select {
width: 100%;
width: 100%;
}
</style>
4 changes: 2 additions & 2 deletions webapp/src/components/Chat.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
import { mapState, mapGetters } from 'vuex'
import { createPopper } from '@popperjs/core'

import ChatContent from 'components/ChatContent'
// import ChatContent from 'components/ChatContent'
import Avatar from 'components/Avatar'
import ChatInput from 'components/ChatInput'
import ChatUserCard from 'components/ChatUserCard'
Expand Down Expand Up @@ -258,4 +258,4 @@ export default {
.user-count
font-weight: 600
color: $clr-secondary-text-light
</style>
</style>
50 changes: 25 additions & 25 deletions webapp/src/components/ChatContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,55 @@
import store from 'store'
import { markdownEmoji } from 'lib/emoji'
import { getUserName } from 'lib/profile'
import VModal from 'vue-js-modal'
// import VModal from 'vue-js-modal'

const markdownIt = MarkdownIt('zero', {
linkify: true
linkify: true
})
markdownIt.enable('linkify')
markdownIt.renderer.rules.link_open = (tokens, idx, options, env, self) => {
tokens[idx].attrPush(['target', '_blank'])
tokens[idx].attrPush(['rel', 'noopener noreferrer'])
return self.renderToken(tokens, idx, options)
tokens[idx].attrPush(['target', '_blank'])
tokens[idx].attrPush(['rel', 'noopener noreferrer'])
return self.renderToken(tokens, idx, options)
}
markdownIt.use(markdownEmoji)

const mentionRegex = /(@[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12})/g

export async function contentToPlainText(content) {
const parts = content.split(mentionRegex)
let plaintext = ''
for (const string of parts) {
if (string.match(mentionRegex)) {
const userId = string.slice(1)
if (!store.state.chat.usersLookup[userId]) {
await store.dispatch('chat/fetchUsers', [userId])
}
const user = store.state.chat.usersLookup[userId]
if (user) {
plaintext += `@${getUserName(user)}`
}
} else {
plaintext += string
}
}
return plaintext
export async function contentToPlainText (content) {
const parts = content.split(mentionRegex)
let plaintext = ''

for (const string of parts) {
if (string.match(mentionRegex)) {
const userId = string.slice(1)
if (!store.state.chat.usersLookup[userId]) {
await store.dispatch('chat/fetchUsers', [userId])
}
const user = store.state.chat.usersLookup[userId]
if (user) {
plaintext += `@${getUserName(user)}`
}
} else {
plaintext += string
}
}
return plaintext
}

const generateHTML = (input) => {
if (!input) return

Check failure on line 43 in webapp/src/components/ChatContent.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 1 tab but found 2 spaces
return markdownIt.renderInline(input)

Check failure on line 44 in webapp/src/components/ChatContent.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 1 tab but found 2 spaces
}

export default {
functional: true,

Check failure on line 48 in webapp/src/components/ChatContent.vue

View workflow job for this annotation

GitHub Actions / build

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

Check failure on line 49 in webapp/src/components/ChatContent.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 1 tab but found 2 spaces
content: String

Check failure on line 50 in webapp/src/components/ChatContent.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 2 tabs but found 4 spaces
},

Check failure on line 51 in webapp/src/components/ChatContent.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 1 tab but found 2 spaces
data() {

Check failure on line 52 in webapp/src/components/ChatContent.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 1 tab but found 2 spaces

Check failure on line 52 in webapp/src/components/ChatContent.vue

View workflow job for this annotation

GitHub Actions / build

Missing space before function parentheses
return {

Check failure on line 53 in webapp/src/components/ChatContent.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 2 tabs but found 4 spaces
selectedUser: null

Check failure on line 54 in webapp/src/components/ChatContent.vue

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 3 tabs but found 6 spaces
}
},
methods: {
Expand Down Expand Up @@ -86,7 +86,7 @@
return createElement('span', { domProps: { innerHTML: part.html } })
})
},
components: {

Check warning on line 89 in webapp/src/components/ChatContent.vue

View workflow job for this annotation

GitHub Actions / build

The "components" property should be above the "props" property on line 49
'user-modal': {
template: `
<modal name="user-modal" height="auto" @before-close="closeUserModal">
Expand All @@ -108,4 +108,4 @@
}
}
}
</script>
</script>
41 changes: 35 additions & 6 deletions webapp/src/components/MediaSource.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
janus-call(v-else-if="room && module.type === 'call.janus'", ref="janus", :room="room", :module="module", :background="background", :size="background ? 'tiny' : 'normal'", :key="`janus-${room.id}`")
janus-channel-call(v-else-if="call", ref="janus", :call="call", :background="background", :size="background ? 'tiny' : 'normal'", :key="`call-${call.id}`", @close="$emit('close')")
.iframe-error(v-if="iframeError") {{ $t('MediaSource:iframe-error:text') }}
iframe#video-player-translation(v-if="languageIframeUrl", :src="languageIframeUrl", style="position: absolute; width: 50%; height: 100%; z-index: -1", frameborder="0", gesture="media", allow="autoplay; encrypted-media", allowfullscreen="true")
</template>
<script>
// TODO functional component?
Expand All @@ -34,7 +35,10 @@ export default {
},
data () {
return {
iframeError: null
iframeError: null,
iframe: null, // Track the iframe element
languageAudioUrl: null, // URL for the selected language audio
languageIframeUrl: null // URL for the language iframe // Added languageIframeUrl to data
}
},
computed: {
Expand Down Expand Up @@ -63,7 +67,7 @@ export default {
handler (value, oldValue) {
if (isEqual(value, oldValue)) return
this.destroyIframe()
this.initializeIframe()
this.initializeIframe(false)
}
},
youtubeTransUrl(youtubeTransUrl) {
Expand All @@ -72,23 +76,25 @@ export default {
}
this.destroyIframe()
this.module.config.ytid = youtubeTransUrl
this.initializeIframe()
this.initializeIframe(false)
}
},
async mounted () {
if (!this.room) {
return
}
this.initializeIframe()
this.initializeIframe(false)
this.$root.$on('languageChanged', this.handleLanguageChange);
},
beforeDestroy () {
this.iframe?.remove()
if (api.socketState !== 'open') return
// TODO move to store?
if (this.room) api.call('room.leave', {room: this.room.id})
this.$root.$off('languageChanged', this.handleLanguageChange);
},
methods: {
async initializeIframe () {
async initializeIframe (mute) {
try {
let iframeUrl
let hideIfBackground = false
Expand All @@ -108,7 +114,7 @@ export default {
break
}
case 'livestream.youtube': {
iframeUrl = `https://www.youtube-nocookie.com/embed/${this.module.config.ytid}?${this.autoplay ? 'autoplay=1&' : ''}rel=0&showinfo=0`
iframeUrl = this.getYoutubeUrl(this.module.config.ytid, this.autoplay, mute)
break
}
}
Expand All @@ -134,6 +140,7 @@ export default {
},
destroyIframe () {
this.iframe?.remove()
this.iframe = null
},
isPlaying () {
if (this.call) {
Expand All @@ -152,6 +159,28 @@ export default {
return !!this.iframe
}
return true
},
handleLanguageChange(languageUrl) {
this.languageAudioUrl = languageUrl; // Set the audio source to the selected language URL
const mute = !!languageUrl; // Mute if language URL is present, otherwise unmute
this.destroyIframe();
this.initializeIframe(mute); // Initialize iframe with the appropriate mute state
// Set the language iframe URL when language changes
this.languageIframeUrl = this.getLanguageIframeUrl(languageUrl);
},
getYoutubeUrl(ytid, autoplay, mute) {
// Construct the autoplay parameter based on the input
const autoplayParam = autoplay ? 'autoplay=1&' : '';
// Construct the mute parameter based on the input
const muteParam = mute ? 'mute=1' : 'mute=0';
// Return the complete YouTube URL with the provided video ID, autoplay, and mute parameters
return `https://www.youtube-nocookie.com/embed/${ytid}?${autoplayParam}rel=0&showinfo=0&${muteParam}`;
},
// Added method to get the language iframe URL
getLanguageIframeUrl(languageUrl) {
// Checks if the languageUrl is not provided the retun null
if (!languageUrl) return null;
return `https://www.youtube.com/embed/${languageUrl}?enablejsapi=1&autoplay=1&modestbranding=1&loop=1&controls=0&disablekb=1&languageUrl=${languageUrl}`;
}
}
}
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
8 changes: 4 additions & 4 deletions webapp/src/views/admin/rooms/types-edit/stage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
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
h4 Languages and YouTube ID
.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-input(name="youtube_id" v-model="entry.youtube_id" label="YouTube Video ID")
bunt-icon-button(@click="deleteLanguageUrl(index)") delete-outline
bunt-button(@click="addLanguageUrl") + Add Language and URL
bunt-button(@click="addLanguageUrl") + Add Language and Youtube ID
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 Down Expand Up @@ -96,7 +96,7 @@ export default {
if (!this.modules['livestream.youtube'].config.languageUrls) {
this.$set(this.modules['livestream.youtube'].config, 'languageUrls', [])
}
this.modules['livestream.youtube'].config.languageUrls.push({ language: '', url: '' })
this.modules['livestream.youtube'].config.languageUrls.push({ language: '', youtube_id: '' })
},
deleteLanguageUrl (index) {
if (this.modules['livestream.youtube'].config.languageUrls) {
Expand Down
23 changes: 14 additions & 9 deletions webapp/src/views/rooms/item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ export default {
watch: {
activeSidebarTab (tab) {
this.unreadTabs[tab] = false
}
},
room: 'initializeLanguages'
},
created () {
if (this.modules['chat.native']) {
Expand All @@ -86,20 +87,24 @@ export default {
} 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}`})

}
this.initializeLanguages();
},
methods: {
changedTabContent (tab) {
if (tab === this.activeSidebarTab) return
this.unreadTabs[tab] = true
},
handleLanguageChange(selectedLanguage) {
this.$store.commit('updateYoutubeTransAudio', selectedLanguage)
handleLanguageChange(languageUrl) {
this.$root.$emit('languageChanged', languageUrl);
},
initializeLanguages(){
this.languages = [];
if (this.modules['livestream.youtube'] && this.modules['livestream.youtube'].config.languageUrls) {
this.languages = this.modules['livestream.youtube'].config.languageUrls;
}
if (!this.languages.find(lang => lang.language === 'Default')) {
this.languages.unshift({language: 'Default', url: ``});
}
}
}
}
Expand Down
Loading