Natural text-to-speech for Scripture and legally imported scholar texts. The API key remains server-side and is never sent to readers.
';
echo '';
echo '
Accessibility: controls include labels, keyboard focus, live status announcements, speed selection, stop/pause, and chapter continuation.
';
}
function ytbs_scholar_admin_page(){
if(!current_user_can('manage_options')) return;
$file=ytbs_scholar_catalog_file(); $data=file_exists($file)?json_decode(file_get_contents($file),true):array();
$collections=$data['collections']??array(); $works=0; foreach($collections as $c) $works+=count($c['works']??array());
echo '
Yah Tunes World Scholar Library
';
echo '
'.esc_html(count($collections)).' collections and '.esc_html($works).' works cataloged. Catalog entries do not pretend that full text or language mappings are installed.
';
echo '
Collection
Classification
Cataloged Works
';
foreach($collections as $c) echo '
'.esc_html($c['name']??'').'
'.esc_html($c['badge']??'').'
'.esc_html(count($c['works']??array())).'
';
echo '
Permanent truth rule: a catalog record is not displayed as readable text until verified, legally distributable content is imported.
';
}
function ytbs_voice_cache_dir(){
$u=wp_upload_dir(); $dir=trailingslashit($u['basedir']).'yah-tunes-bible-audio';
if(!file_exists($dir)) wp_mkdir_p($dir);
return array('dir'=>$dir,'url'=>trailingslashit($u['baseurl']).'yah-tunes-bible-audio');
}
add_action('rest_api_init',function(){
register_rest_route('ytbs/v1','/speech',array(
'methods'=>'POST','callback'=>'ytbs_rest_speech','permission_callback'=>'__return_true',
'args'=>array('text'=>array('required'=>true),'voice'=>array('required'=>false),'speed'=>array('required'=>false))
));
});
function ytbs_rest_speech(WP_REST_Request $request){
$s=ytbs_voice_settings();
if($s['enabled']!=='1') return new WP_Error('ytbs_voice_disabled','Voice reading is disabled.',array('status'=>403));
$nonce=(string)$request->get_header('X-WP-Nonce');
if(!$nonce || !wp_verify_nonce($nonce,'wp_rest')) return new WP_Error('ytbs_bad_nonce','Security check failed.',array('status'=>403));
$text=trim(wp_strip_all_tags((string)$request->get_param('text')));
if($text==='') return new WP_Error('ytbs_empty_text','No readable text was supplied.',array('status'=>400));
$max=absint($s['max_chars']);
if(function_exists('mb_substr')) $text=mb_substr($text,0,$max); else $text=substr($text,0,$max);
$voices=ytbs_openai_voice_choices(); $voice=sanitize_key((string)$request->get_param('voice'));
if(!isset($voices[$voice])) $voice=$s['default_voice'];
$speed=(float)$request->get_param('speed'); $speed=max(.5,min(2.0,$speed?:1));
if(empty($s['api_key'])) return new WP_Error('ytbs_no_api_key','OpenAI is not configured.',array('status'=>503,'browser_fallback'=>$s['browser_fallback']==='1'));
$cache=ytbs_voice_cache_dir(); $hash=hash('sha256',$s['model'].'|'.$voice.'|'.$speed.'|'.$s['instructions'].'|'.$text); $name=$hash.'.mp3'; $path=$cache['dir'].'/'.$name;
if(file_exists($path) && filemtime($path)>(time()-DAY_IN_SECONDS*absint($s['cache_days']))) return rest_ensure_response(array('url'=>$cache['url'].'/'.$name,'cached'=>true));
$response=wp_remote_post('https://api.openai.com/v1/audio/speech',array(
'timeout'=>90,'headers'=>array('Authorization'=>'Bearer '.$s['api_key'],'Content-Type'=>'application/json'),
'body'=>wp_json_encode(array('model'=>$s['model'],'voice'=>$voice,'input'=>$text,'instructions'=>$s['instructions'],'response_format'=>'mp3','speed'=>$speed))
));
if(is_wp_error($response)) return new WP_Error('ytbs_openai_connection',$response->get_error_message(),array('status'=>502));
$code=wp_remote_retrieve_response_code($response); $body=wp_remote_retrieve_body($response);
if($code<200 || $code>=300){
$decoded=json_decode($body,true); $message=$decoded['error']['message']??'OpenAI speech generation failed.';
return new WP_Error('ytbs_openai_error',sanitize_text_field($message),array('status'=>502,'browser_fallback'=>$s['browser_fallback']==='1'));
}
if(!wp_mkdir_p($cache['dir']) || file_put_contents($path,$body)===false) return new WP_Error('ytbs_cache_error','Audio was generated but could not be cached.',array('status'=>500));
return rest_ensure_response(array('url'=>$cache['url'].'/'.$name,'cached'=>false));
}
add_action('wp_enqueue_scripts',function(){
$s=ytbs_voice_settings(); if($s['enabled']!=='1') return;
wp_register_script('ytbs-accessible-reader','',array(),YTBS_VER,true); wp_enqueue_script('ytbs-accessible-reader');
wp_localize_script('ytbs-accessible-reader','YTBSVoice',array(
'endpoint'=>rest_url('ytbs/v1/speech'),'nonce'=>wp_create_nonce('wp_rest'),'voices'=>ytbs_openai_voice_choices(),
'defaultVoice'=>$s['default_voice'],'browserFallback'=>$s['browser_fallback']==='1','resumeKey'=>'ytbs_voice_resume_v1'
));
$js=<<<'JS'
(function(){
'use strict';
function ready(fn){document.readyState!=='loading'?fn():document.addEventListener('DOMContentLoaded',fn);}
ready(function(){
var card=document.querySelector('.ytbs-reader .ytbs-chapter-card'); if(!card||document.getElementById('ytbs-voice-reader')) return;
var verses=[].slice.call(card.querySelectorAll('.ytbs-verse')).map(function(v){var n=v.getAttribute('data-verse')||'';var t=v.querySelector('.ytbs-verse-text');return (n?n+'. ':'')+(t?t.textContent:'');}).filter(Boolean);
var text=verses.length?verses.join(' '):card.innerText.replace(/Copy|Study/g,' '); if(!text.trim()) return;
var voices=YTBSVoice.voices||{}, opts=''; Object.keys(voices).forEach(function(v){opts+='';});
var wrap=document.createElement('section'); wrap.id='ytbs-voice-reader'; wrap.className='ytbs-voice-reader'; wrap.setAttribute('aria-label','Accessible book reader');
wrap.innerHTML='
🎧 Read to MeOpenAI natural voice accessibility reader
Ready to read.
';
card.parentNode.insertBefore(wrap,card); var audio=wrap.querySelector('#ytbs-audio'), read=wrap.querySelector('#ytbs-read'), pause=wrap.querySelector('#ytbs-pause'), stop=wrap.querySelector('#ytbs-stop'), status=wrap.querySelector('#ytbs-voice-status'), voice=wrap.querySelector('#ytbs-voice-select'), speed=wrap.querySelector('#ytbs-speed'), auto=wrap.querySelector('#ytbs-auto-next'); voice.value=YTBSVoice.defaultVoice||'coral';
var utter=null, usingBrowser=false;
function setStatus(s){status.textContent=s;}
function browserSpeak(){ if(!('speechSynthesis' in window)){setStatus('No speech engine is available on this device.');return;} usingBrowser=true; utter=new SpeechSynthesisUtterance(text); utter.rate=parseFloat(speed.value)||1; var list=speechSynthesis.getVoices(); if(list.length) utter.voice=list[0]; utter.onend=ended; utter.onerror=function(){setStatus('Device speech could not continue.');reset();}; speechSynthesis.cancel(); speechSynthesis.speak(utter); setStatus('Reading with this device’s voice.'); active(); }
function active(){read.disabled=true;pause.disabled=false;stop.disabled=false;}
function reset(){read.disabled=false;pause.disabled=true;stop.disabled=true;pause.textContent='⏸ Pause';}
function ended(){setStatus('Chapter finished.');reset(); if(auto.checked){var links=[].slice.call(document.querySelectorAll('.ytbs-prevnext a'));var next=links.find(function(a){return /next/i.test(a.textContent);});if(next) location.href=next.href;}}
read.addEventListener('click',function(){setStatus('Preparing natural voice…');active(); fetch(YTBSVoice.endpoint,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-WP-Nonce':YTBSVoice.nonce},body:JSON.stringify({text:text,voice:voice.value,speed:parseFloat(speed.value)||1})}).then(function(r){return r.json().then(function(j){if(!r.ok) throw j;return j;});}).then(function(j){usingBrowser=false;audio.src=j.url;audio.play();setStatus('Reading with OpenAI voice '+voices[voice.value]+'.');}).catch(function(e){if(YTBSVoice.browserFallback){setStatus('OpenAI unavailable. Starting the free device voice.');browserSpeak();}else{setStatus((e&&e.message)||'Voice generation failed.');reset();}});});
pause.addEventListener('click',function(){if(usingBrowser){if(speechSynthesis.paused){speechSynthesis.resume();pause.textContent='⏸ Pause';setStatus('Reading resumed.');}else{speechSynthesis.pause();pause.textContent='▶ Resume';setStatus('Reading paused.');}}else{if(audio.paused){audio.play();pause.textContent='⏸ Pause';setStatus('Reading resumed.');}else{audio.pause();pause.textContent='▶ Resume';setStatus('Reading paused.');}}});
stop.addEventListener('click',function(){audio.pause();audio.currentTime=0;if('speechSynthesis'in window)speechSynthesis.cancel();setStatus('Reading stopped.');reset();}); audio.addEventListener('ended',ended); audio.addEventListener('error',function(){setStatus('The audio could not be played.');reset();});
});
})();
JS;
wp_add_inline_script('ytbs-accessible-reader',$js);
});
add_action('wp_head',function(){
$s=ytbs_voice_settings(); if($s['enabled']!=='1') return;
echo '';
},99);
YTU University AI Teaching System - Yah Tunes.com
Skip to content
YTU University AI Teaching System
YTU UNIVERSAL LEARNING SYSTEM
Technology & AI
Open the material you chose. Nothing unrelated is placed in your way.