/* ======================================================================== * Yah Tunes Scholar Library + OpenAI Accessible Voice Reader (v2.1.0) * Additive module: preserves all existing folder names, shortcodes and data. * ====================================================================== */ function ytbs_scholar_catalog_file(){ return plugin_dir_path(__FILE__).'manifests/scholar_world_library.json'; } function ytbs_openai_voice_choices(){ return array( 'alloy'=>'Alloy','ash'=>'Ash','ballad'=>'Ballad','coral'=>'Coral', 'echo'=>'Echo','fable'=>'Fable','onyx'=>'Onyx','nova'=>'Nova', 'sage'=>'Sage','shimmer'=>'Shimmer','verse'=>'Verse','marin'=>'Marin','cedar'=>'Cedar' ); } function ytbs_voice_settings(){ $saved=get_option('ytbs_voice_settings',array()); return wp_parse_args(is_array($saved)?$saved:array(),array( 'enabled'=>'1','api_key'=>'','model'=>'gpt-4o-mini-tts','default_voice'=>'coral', 'instructions'=>'Read clearly, warmly, reverently, and naturally. Preserve verse numbers and names. Do not add commentary.', 'max_chars'=>3800,'cache_days'=>30,'browser_fallback'=>'1' )); } add_action('admin_init',function(){ register_setting('ytbs_voice_group','ytbs_voice_settings',array( 'type'=>'array','sanitize_callback'=>function($input){ $old=ytbs_voice_settings(); $voices=ytbs_openai_voice_choices(); $out=array(); $out['enabled']=empty($input['enabled'])?'0':'1'; $key=trim((string)($input['api_key']??'')); $out['api_key']=($key==='••••••••••••••••')?$old['api_key']:sanitize_text_field($key); $out['model']=sanitize_text_field($input['model']??'gpt-4o-mini-tts'); $voice=sanitize_key($input['default_voice']??'coral'); $out['default_voice']=isset($voices[$voice])?$voice:'coral'; $out['instructions']=sanitize_textarea_field($input['instructions']??''); $out['max_chars']=min(4000,max(800,absint($input['max_chars']??3800))); $out['cache_days']=min(365,max(1,absint($input['cache_days']??30))); $out['browser_fallback']=empty($input['browser_fallback'])?'0':'1'; return $out; } )); }); add_action('admin_menu',function(){ add_submenu_page('edit.php?post_type='.YTBS_PT,'AI Voice Reader','AI Voice Reader','manage_options','ytbs-ai-voice-reader','ytbs_voice_admin_page'); add_submenu_page('edit.php?post_type='.YTBS_PT,'World Scholar Library','World Scholar Library','manage_options','ytbs-world-scholar-library','ytbs_scholar_admin_page'); },70); function ytbs_voice_admin_page(){ if(!current_user_can('manage_options')) return; $s=ytbs_voice_settings(); $voices=ytbs_openai_voice_choices(); echo '

Yah Tunes OpenAI Voice Reader

'; echo '

Natural text-to-speech for Scripture and legally imported scholar texts. The API key remains server-side and is never sent to readers.

'; echo '
'; settings_fields('ytbs_voice_group'); echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo '
Enable reader
OpenAI API key

Leave the masked value unchanged to keep the saved key.

Speech model
Default voice
Narration direction
Maximum characters/request
Audio cache days
Free browser fallback
'; submit_button('Save Voice Reader Settings'); 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 ''; foreach($collections as $c) echo ''; echo '
CollectionClassificationCataloged Works
'.esc_html($c['name']??'').''.esc_html($c['badge']??'').''.esc_html(count($c['works']??array())).'

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

YTU University AI Teaching System

YTU UNIVERSAL LEARNING SYSTEM

Technology & AI

Open the material you chose. Nothing unrelated is placed in your way.

Opening Technology & AI books…

Premium Feature

This feature needs an upgrade.

View Plans
Scroll to Top
Enable Notifications OK No thanks