sc-voice
Version:
SuttaCentral Voice
601 lines (558 loc) • 20.6 kB
JavaScript
(function(exports) {
const DN33_EN_SECONDS = 2*3600 + 0*60 + 27;
const DN33_EN_SECONDS_PER_SEGMENT = DN33_EN_SECONDS/(1158);
const DN33_PLI_SECONDS_PER_SEGMENT = 1.8 * DN33_EN_SECONDS_PER_SEGMENT;
const DN33_EN_SECONDS_PER_CHAR = DN33_EN_SECONDS/(83588);
const DN33_PLI_SECONDS_PER_CHAR = DN33_EN_SECONDS/(79412);
const IPS_CHOICES = [{
url: '',
i18n: 'bellNone',
value: 0,
},{
url: '/audio/rainforest-ambience-glory-sunz-public-domain.mp3',
i18n: 'bellRainforest',
volume: 0.1,
value: 1,
hide: true,
},{
url: '/audio/indian-bell-flemur-sampling-plus-1.0.mp3',
i18n: 'bellIndian',
volume: 0.1,
value: 2,
},{
url: '/audio/tibetan-singing-bowl-horst-cc0.mp3',
i18n: "bellTibetan",
volume: 0.3,
value: 3,
},{
url: '/audio/jetrye-bell-meditation-cleaned-CC0.mp3',
i18n: "bellMeditation",
volume: 0.1,
value: 4,
hide: true,
},{
url: '/audio/STE-004-Coemgenu.mp3',
i18n: "bellMidrange",
volume: 0.5,
value: 5,
},{
url: '/audio/simple-bell.mp3',
i18n: "bellSimple",
volume: 0.5,
value: 6,
}];
const COOKIE_SETTINGS = {
expires: "100Y",
SameSite: "Strict",
};
const WEB_LANGUAGES = [{
name: 'cs',
label: 'Čeština / CS',
}, {
name: 'da',
label: 'Dansk / DA',
}, {
name: 'de',
label: 'Deutsch / DE',
}, {
name: 'en',
label: 'English / EN',
}, {
name: 'fr',
label: 'Français / FR',
}, {
name: 'hi',
label: 'हिंदी / HI',
}, {
name: 'is',
label: 'Íslenska / IS',
}, {
name: 'ja',
label: '日本語 / JA',
}, {
name: 'nb',
label: 'Norsk / NB',
}, {
name: 'nl',
label: 'Nederlands / NL',
}, {
name: 'pl',
label: 'Polski / PL',
}, {
name: 'pt',
label: 'Português / PT',
}, {
name: 'ro',
label: 'Română / RO',
}, {
name: 'si',
label: 'සිංහල / SI',
}, {
name: 'vi',
label: 'Tiếng Việt / VI',
}];
class ScvSingleton {
constructor(g) {
this.showId = false;
// default voices
this.vnameRoot = 'Aditi';
this.vnameTrans = 'Amy';
// Default language may change default vnameTrans
var navLang = g.navigator && g.navigator.language;
var navLang = g.navigator && g.navigator.language;
this.locale = navLang && navLang.split('-')[0] || 'en';
this.lang = this.locale;
this.changed('lang');
this.checkVoiceLang(false);
console.log(`ScvSingleton.ctor`,
`navLang:${navLang}`,
`vnameTrans:${this.vnameTrans}`,
'');
this.scid = null;
this.showLang = this.SHOWLANG_BOTH;
this.fullLine = false;
this.search = null;
this.maxResults = 5;
this.audio = this.AUDIO_OPUS;
this.ips = 6;
if (g == null) {
throw new Error(`g is required`);
}
Object.defineProperty(this, "transLanguages", {
writable: true,
value: [],
});
Object.defineProperty(this, "authors", {
writable: true,
value: {},
});
Object.defineProperty(this, "_voices", {
writable: true,
value: [],
});
Object.defineProperty(this, "_user", {
writable: true,
value: {
username: null,
isAdmin: false,
isTranslator: false,
isEditor: false,
token: null,
},
});
Object.defineProperty(this, "g", {
value: g,
});
Object.defineProperty(this, "vueRoot", {
writable: true,
value: g.vueRoot,
});
Object.defineProperty(this, "title", {
writable: true,
value: "no title",
});
}
get SHOWLANG_BOTH() {
return 0;
}
get SHOWLANG_PALI() {
return 1;
}
get SHOWLANG_TRANS() {
return 2;
}
get AUDIO_MP3() {
return 'mp3';
}
get AUDIO_OGG() {
return 'ogg';
}
get AUDIO_OPUS() {
return 'opus';
}
get languages() {
return WEB_LANGUAGES;
}
get user() {
return this._user;
}
set user(value) {
var g = this.g;
g.Vue.set(this._user, "username", value.username);
g.Vue.set(this._user, "token", value.token);
g.Vue.set(this._user, "isAdmin", value.isAdmin);
g.Vue.set(this._user, "isTranslator", value.isTranslator);
g.Vue.set(this._user, "isEditor", value.isEditor);
}
get ipsChoices() {
return IPS_CHOICES;
}
get voices() {
return this._voices;
}
set voices(value) {
var g = this.g;
g.Vue.set(this, "_voices", value);
}
get useCookies() {
var g = this.g;
return this.vueRoot
? this.vueRoot.$cookie.get("useCookies") === 'true'
: false;
}
get showPali( ){
return this.showLang === this.SHOWLANG_BOTH
|| this.showLang === this.SHOWLANG_PALI;
}
get showTrans(){
return this.showLang === this.SHOWLANG_BOTH
|| this.showLang === this.SHOWLANG_TRANS;
}
set useCookies(value) {
if (value) {
this.vueRoot.$cookie.set("useCookies", value, COOKIE_SETTINGS);
} else {
this.vueRoot.$cookie.delete("useCookies");
}
}
deleteCookies() {
this.vueRoot.$cookie.delete('useCookies');
Object.keys(this).forEach(key => {
this.vueRoot.$cookie.delete(key);
});
console.log('cookies deleted');
}
loadCookie(prop) {
var g = this.g;
var v = this.vueRoot.$cookie.get(prop);
if (v === 'false') {
g.Vue.set(this, prop, false);
} else if (v === 'true') {
g.Vue.set(this, prop, true);
} else if (v != null) {
var vnum = Number(v);
g.Vue.set(this, prop, `${vnum}`===v ? vnum : v);
}
}
checkVoiceLang(save=true, tries = 1) {
if (!this.voices || !this.voices.length) {
if (tries < 3) {
console.log(`checkVoiceLang(${save},${tries})`,
`lang:${this.lang}`,
`...`);
var that = this;
setTimeout(
() => that.checkVoiceLang(save, tries+1),
1000);
} else {
console.warn(`checkVoiceLang(${save},${tries})`,
`lang:${this.lang}`,
`GIVING UP`);
}
return;
}
console.log(`checkVoiceLang(${save},${tries})`,
`lang:${this.lang}`);
var lang = this.lang;
var lv = this.langVoices().filter(v =>
v.name === this.vnameTrans);
if (lv.length === 0) { // no voice
var voice = this.langVoices()[0];
var vname = voice && voice.name;
if (vname) {
console.log(
`checkVoiceLang changing voice for lang:${lang}`,
`${this.vnameTrans} => ${vname}`,
'');
this.vnameTrans = vname;
save && this.changed('vnameTrans');
}
}
}
changed(prop, tries=1) { // save changes
if (this.vueRoot == null) {
if (tries < 3) {
var that = this;
console.log(`changed(${prop}, ${tries})...`);
setTimeout(() => that.changed(prop, tries+1));
return;
} else {
console.warn(`changed(${prop}, ${tries})`,
`GIVING UP`);
return;
}
return;
}
if (tries > 1) {
console.log(`...changed(${prop}, ${tries})`);
}
var cookie = this.vueRoot.$cookie;
var v = this[prop];
if (v != null && this.vueRoot) {
if (this.useCookies) {
cookie.set(prop, v, COOKIE_SETTINGS);
console.log(`setting cookie (${prop}):${v}`,
`${this.vueRoot.$cookie.get(prop)}`,
typeof this.vueRoot.$cookie.get(prop));
}
if (prop === 'useCookies') {
if (v) {
cookie.set( "showId", this.showId, COOKIE_SETTINGS);
cookie.set( "vnameTrans", this.vnameTrans, COOKIE_SETTINGS);
cookie.set( "vnameRoot", this.vnameRoot, COOKIE_SETTINGS);
cookie.set( "lang", this.lang, COOKIE_SETTINGS);
cookie.set( "maxResults", this.maxResults, COOKIE_SETTINGS);
cookie.set( "audio", this.audio, COOKIE_SETTINGS);
cookie.set( "showLang", this.showLang, COOKIE_SETTINGS);
cookie.set( "fullLine", this.fullLine, COOKIE_SETTINGS);
cookie.set( "locale", this.locale, COOKIE_SETTINGS);
cookie.set( "ips", this.ips, COOKIE_SETTINGS);
cookie.set( "useCookies", this.useCookies, COOKIE_SETTINGS);
console.log(`saved settings to cookies`, cookie);
} else {
Object.keys(this).forEach(key => {
cookie.delete(key);
});
}
}
}
}
hash(opts={}) {
var state = Object.assign({}, this, opts);
var hash = '';
if (this.useCookies) {
var search = state.search || '';
hash = `#/?search=${search}`;
} else {
hash = Object.keys(state).sort().reduce((acc,key) => {
var val = state[key];
if (val != null) {
if (acc == null) {
acc = `#/?`;
} else {
acc = acc + '&';
}
acc += `${key}=${state[key]}`;
}
return acc;
}, null);
}
return `${hash}`;
}
url(opts={}) {
var g = this.g;
var hash = this.hash(opts);
var loc = g.window.location;
var r = Math.random();
return `${loc.origin}${loc.pathname}?r=${r}${hash}`;
}
reload(opts={}) {
var g = this.g;
Object.assign(this, opts);
var url = this.url();
console.debug("main.scvState.reload()", url);
g.window.location.href = url;
return;
}
mounted(vueRoot) {
var g = this.g;
this.vueRoot = vueRoot;
var query = vueRoot.$route.query;
if (this.useCookies) {
let cookies = {};
let that = this;
Object.keys(this).forEach(key => {
this.loadCookie(key);
cookies[key] = this[key];
that.changed(key);
});
console.log(`ScvSingleton.mounted() cookies:`, cookies);
}
var isSCLink = query && query.lang && query.locale===undefined;
if (isSCLink) {
console.log(`ScvSingleton.mounted() SC query:`, query);
this.lang = query.lang;
if (!this.useCookies) {
this.locale = query.lang;
}
}
if (query) {
if (!this.useCookies) {
query.showId != null &&
(this.showId = query.showId==='true');
query.vnameTrans &&
(this.vnameTrans = query.vnameTrans);
query.vnameRoot &&
(this.vnameRoot = query.vnameRoot);
query.audio &&
(this.audio = query.audio);
query.maxResults &&
(this.maxResults = Number(query.maxResults));
query.fullLine &&
(this.fullLine = !!query.fullLine);
query.showLang &&
(this.showLang = Number(query.showLang||0));
query.ips != null &&
(this.ips = Number(query.ips));
if (query.lang && this.lang !== query.lang) {
this.lang = query.lang;
this.checkVoiceLang(false);
}
query.locale != null &&
(this.locale = query.locale);
}
var search = query.scid || query.search || '';
query.search && (this.search = search);
}
var localeVoices = WEB_LANGUAGES
.filter(l => l.name===this.locale);
if (localeVoices.length === 0) {
console.log(`ScvSingleton.mounted()`,
`unknown locale:${this.locale}=>en`);
this.locale = 'en';
this.changed('locale');
this.lang = 'en';
this.changed('lang');
this.checkVoiceLang();
}
vueRoot.$vuetify.lang.current = this.locale;
}
durationDisplay(totalSeconds) {
totalSeconds = Math.round(totalSeconds);
var seconds = totalSeconds;
var hours = Math.trunc(seconds / 3600);
seconds -= hours * 3600;
var minutes = Math.trunc(seconds / 60);
seconds -= minutes * 60;
var $vuetify = this.vueRoot.$vuetify;
if (hours) {
var tDisplay = $vuetify.lang.t('$vuetify.scv.HHMM');
var tAria = $vuetify.lang.t('$vuetify.scv.ariaHHMM');
} else if (minutes) {
var tDisplay = $vuetify.lang.t('$vuetify.scv.MMSS');
var tAria = $vuetify.lang.t('$vuetify.scv.ariaMMSS');
} else {
var tDisplay = $vuetify.lang.t('$vuetify.scv.seconds');
var tAria = $vuetify.lang.t('$vuetify.scv.ariaSeconds');
}
var display = tDisplay
.replace(/A_HOURS/, hours)
.replace(/A_MINUTES/, minutes)
.replace(/A_SECONDS/, seconds);
var aria = tAria
.replace(/A_HOURS/, hours)
.replace(/A_MINUTES/, minutes)
.replace(/A_SECONDS/, seconds);
return {
totalSeconds,
hours,
minutes,
seconds,
display,
aria,
}
}
duration(obj){
if (typeof obj === 'number') {
return this.durationSegments(obj);
}
var {
showTrans,
showPali,
} = this || {};
var chars = obj;
var seconds = 0;
Object.keys(obj).forEach(lang => {
var cl = chars[lang];
if (lang === 'pli') {
showPali && (seconds += cl*DN33_PLI_SECONDS_PER_CHAR);
} else {
showTrans && (seconds += cl*DN33_EN_SECONDS_PER_CHAR);
}
});
return this.durationDisplay(seconds);
}
durationSegments(nSegments){
var secondsPerSegment =
(this.showPali ? DN33_PLI_SECONDS_PER_SEGMENT : 0) +
(this.showTrans ? DN33_EN_SECONDS_PER_SEGMENT: 0);
var totalSeconds = Math.trunc(nSegments * secondsPerSegment);
return this.durationDisplay(totalSeconds);
}
timeRemaining(tracks, curTrack=0, curSeg=0, stats) {
var remChars = this.charsRemaining(tracks, curTrack, curSeg);
var result = {
chars,
};
if (stats) {
var {
showTrans,
showPali,
} = this;
var charsTot = 0;
var chars = 0;
var remCharsTot = this.charsRemaining(tracks, 0, 0);
Object.keys(remCharsTot).forEach(lang => {
if (lang === 'expanded') {
// skip
} else if (lang === 'pli') {
if (showPali) {
charsTot += remCharsTot[lang] || 0;
chars += remChars[lang] || 0;
}
} else if (showTrans && lang === this.lang) {
charsTot += remCharsTot[lang] || 0;
chars += remChars[lang] || 0;
}
});
let seconds = stats.seconds;
var secsRem = charsTot === 0 || isNaN(charsTot)
? seconds
: seconds * chars / charsTot ;
Object.assign(result, this.durationDisplay(secsRem));
} else {
console.log(`timeRemaining (DEPRECATED)`, remChars);
Object.assign(result, this.duration(remChars));
}
result.chars = remChars;
return result;
}
charsRemaining(tracks, curTrack=0, curSeg=0) {
const EN_CHARS_PER_SEGMENT =
DN33_EN_SECONDS_PER_SEGMENT/DN33_EN_SECONDS_PER_CHAR;
const PLI_CHARS_PER_SEGMENT =
DN33_PLI_SECONDS_PER_SEGMENT/DN33_PLI_SECONDS_PER_CHAR;
return tracks.reduce((acc, track, iTrack) => {
if (curTrack <= iTrack) {
var segments = track.segments;
if (segments) {
segments.forEach((seg, iSeg) => {
var remaining = curTrack < iTrack ||
iTrack === curTrack && curSeg <= iSeg;
remaining && Object.keys(seg).forEach(key => {
if (key !== 'scid') {
acc[key] = (acc[key] || 0) +
seg[key].length;
}
});
});
} else {
var lang = this.lang || 'en';
var segsRemaining = track.nSegments - curSeg;
acc[lang] = acc[lang] || 0;
acc[lang] += segsRemaining * EN_CHARS_PER_SEGMENT;
acc['pli'] = acc['pli'] || 0;
acc['pli'] += segsRemaining * PLI_CHARS_PER_SEGMENT;
}
}
return acc;
}, {});
}
langVoices(lang = this.lang) {
return this.voices
? this.voices.filter(v => v.langTrans===lang)
: [];
}
}
module.exports = exports.ScvSingleton = ScvSingleton;
})(typeof exports === "object" ? exports : (exports = {}));