Right you are: merged PR. @henshin5 try updating the script and see if it works for you now.
Unfortunately this script seems to no longer work.
Depends on how itâs trying to pull audio from Wanikani.
Once API V2 is stable and WK have released their audio publicly (technically this script is using WKâs resources without permission) weâll be adding it to Kaniwani anyway.
FYI this definitely wonât work at the moment on KW 2.0 since all the markup/html content has changed.
Aw man, I just added it to the API and Third Party Apps list yesterday
Sorry for not replying sooner, Iâve been travelling. Iâll have a look when I get a chance (and when Iâve finished my reviews :-O).
Hi, thanks for creating this script, I was really glad it was available.
I tried getting it to work again but I think Iâm stuck at a point where you would have to look into it. It generates a URL that seems to be valid, such as https://wanikaniaudio.herokuapp.com/url/ç and returns an AWS URL but I get an AccessDenied exception for that AWS content.
Changing the script wasnât much work but since you are busy too I thought maybe it helps to share my changes. Not sure if that was all that had to be changed since I didnât look any further after being unable to load the mp3 file.
They also switched to https (or maybe it has always been that way) which causes problems at least for the loading image.
// ==UserScript==
// @name KaniWani audio
// @namespace http://tampermonkey.net/
// @version 0.22 alpha
// @description Play audio in KaniWani
// @author CometZero
// @match https://kaniwani.com/reviews/session
// @match https://www.kaniwani.com/reviews/session
// @grant GM_xmlhttpRequest
// ==/UserScript==
//https://kaniwani.com/kw/review/
var buttonHtml =
`<a id="playAudio" class="button -addsynonym" href="#">Play Audio</a>`;
var colorDisabled = "hsl(0, 0%, 65%)";
var loadingImageHtml = `<img src="http://img.etimg.com/photo/45627788.cms"
alt="Loading ..." style="margin-left:5px;width:15px;height:15px;display:none;">`;
var playSoundButton;
var loadingImage;
var audio = null;
var isPendingPlay = false;
var isLoadingAudio = false;
var onAudioReady = function(){
isLoading = false;
loadingImage.style.display = "none"; // hide loading image
playSoundButton.style.color = ""; // set default text color
if(isPendingPlay){
audio.play();
isPendingPlay = false;
}
};
var onAudioLoading = function(){
loadingImage.style.display = "inline"; // show loading image
playSoundButton.style.color = colorDisabled; // dimm play button
isLoading = true;
};
(function() {
'use strict';
// TODO test if my service is still working (wanikaniaudio.herokuapp.com)
// and notify the user to motivate me to enable the service
initWhenReady();
})();
// wait until the first word has been loaded so that we can complete the setup
function initWhenReady(){
console.log("Trying to initialize KaniWani audio... ");
var wordDom = getWordDom();
if(wordDom){
init();
}else{
//wait a bit and then try again, assuming the initial loading will be complete soon
setTimeout(function(){ initWhenReady(); }, 500);
}
}
function init(){
initElements();
// loads audio for the first time;
loadAudio();
onNewWordObserver(function(mutations, observer) {
// loads audio everytime the word DOM changes
loadAudio();
});
playSoundButton.onclick = function(){
playAudio();
};
document.getElementById('submitAnswer').onclick = function(){
playAudio();
};
}
// plays the audio
// finds the word than loads the audo if needed and plays it
function playAudio(){
var word = getWord();
if(word === null) {
console.log("Cannot get word :(");
return;
}
// if audio is available just play it
if(audio){
audio.play();
return;
}
// audio is not available we need to load it
// make sure audio is not already loading
if(!isLoadingAudio){
loadAudio(word);
}
// set pendingPlay true so when it loads it will play the audio
isPendingPlay = true;
}
// accepts function that is triggered when new word is shown
function onNewWordObserver(f){
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
var observer = new MutationObserver(f);
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };
// select the target node
var target = getWordDom();
// pass in the target node, as well as the observer options
observer.observe(target, config);
}
// accepts function that is triggered when user has answered correctly
function onCorrectAnswerObserver(f){
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
var observer = new MutationObserver(f);
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };
// TODO find target and change the config
// select the target node
var target = null;
// pass in the target node, as well as the observer options
observer.observe(target, config);
}
// adds all the buttons and loading images to the webpage
function initElements(){
// create "play audio button"
playSoundButton = htmlToElement(buttonHtml);
loadingImage = htmlToElement(loadingImageHtml);
playSoundButton.appendChild(loadingImage);
//buttonWraper.innerHTML = buttonHtml;
// insert
var answerPanel = document.getElementById('answer');
answerPanel.parentNode.insertBefore(playSoundButton, answerPanel.nextSibling);
}
// get the dom that is containg word that it has to play
function getWordDom(){
//var detailKanjiDiv = document.getElementById("detailKanji");
//var kanjisDom = detailKanjiDiv.getElementsByClassName("text");
var kanjisDom = document.querySelectorAll('div[lang=ja] div');
if(kanjisDom && kanjisDom.length >= 1){
return kanjisDom[0];
}
return null;
}
// finds the word that it has to play
function getWord(){
var kanjisDom = getWordDom();
if(kanjisDom != null){
// get just the first kanji
var kanjis = kanjisDom.innerHTML;
var splitKanjis = kanjis.split("<br>");
return splitKanjis[0];;
} else {
return null;
}
}
// get audio url for a word and play it
function loadAudio(){
vocubKanji = getWord();
if(isEmpty(vocubKanji)) throw "vocubKanji cannot be empty!";
audio = null;
onAudioLoading();
GM_xmlhttpRequest ( {
method: 'GET',
url: 'https://wanikaniaudio.herokuapp.com/url/' + vocubKanji,
accept: 'text/xml',
onreadystatechange: function (response) {
console.log(response);
if (response.readyState != 4)
return;
// get responseTxt
var responseTxt = response.responseText;
// check if responseTxt is valid
if (!isEmpty(responseTxt) && !responseTxt.startsWith("Cannot")){
audio = new Audio(responseTxt);
onAudioReady(audio);
} else {
console.log("Invalid response " + responseTxt);
}
}
} );
}
// check if string is empty
function isEmpty(str) {
return (!str || 0 === str.length);
}
/**
* Creates dom element from string.
* @param {String} HTML representing a single element
* @return {Element}
*/
function htmlToElement(html) {
var template = document.createElement('template');
template.innerHTML = html;
return template.content.firstChild;
}
Following this thread for updates
Sorry, Iâve had several goes at trying to get the script to work after the changes to both KaniWani and WaniKani and I havenât been able to figure anything out. The script will probably stay broken indefinitely, unless thereâs yet more major changes.
On OSX/macOS, there is a hacky alternative I use (I have UK keyboard layout but you can modify to your own shortcuts below).
First enable Japanese Text-to-Speech:
- System Preferences > Accessibility > Speech > System Voice > Japanese Otoya (Male) or Kyoko (Female)
- (You may need to go to âCustomizeâŠâ first if you donât see the Japanese voices in the list)
- Then enable the option to âSpeak selected text when the key is pressedâ and my current key is set to:
Option+Esc
Once you have that setup, you can use Option+Esc on any highlighted text and the OS will read it out in a semi-decent robot voice. N.B. The reading/pitch accent isnât always correct so it shouldnât be considered native audio.
So in a KaniWani review session, when you enter your answer correctly and the kanji is displayed you can:
- Cmd+A (to select your answerâs kanji) then
- Option+Esc (to read out your selection)
You can automate the last part a little bit by using a macro recorder like a BetterTouchTool keyboard trigger:
- BetterTouchTool > Preferences > Advanced > Gestures > Keyboard
- Click â+ Add New Shortcut or Key Sequenceâ
- Click to record shortcut and hit âCmd+§â (for example)
- Select âTrigger Other Keyboard Shortcutâ and hit Cmd+A
- Click âAttach Additional Actionâ
- Select âTrigger Other Keyboard Shortcutâ and hit Option+§
If you do all of the above, after correctly entering a KaniWani answer you can just hit âCmd+§â and get audio.
Thatâs what I do anyway after trying this script and it not working⊠but I also use the system voice to read out kanji/kana in plenty of other places so itâs quite handy to have setup. Along with the Japanese/English Apple Dictionary for word lookups.
Edit:
I found I needed to add a short delay between the BetterTouchTool actions to make this more reliable. If you attach an additional action and use âTrigger Predefined Action > Auxiliary Actions > Delay Next Actionâ then drag it in to be ordered like in the image below it seems to work well:
I played around with the non-working script that @irrelephant posted, and I was able to get it working by scraping the WaniKani vocab page for the audio tag. You can try it out here: KaniWani audio
Note that because KaniWani is a single-page app, the current @match
tags wonât work unless you do a refresh once youâre on the study or review page (but itâs not too cumbersome for me). It might be possible to avoid this by changing the @match
to be on all of KaniWani, and adding special code that detects page navigations, but I havenât done that yet.
It turns out that remembering to do a page refresh before starting reviews is too hard for me⊠haha. So I decided to just fix the script. It should work pretty much out of the box now, I think. Updated version is available here: KaniWani audio
Hi, thanks for sharing this!
When I go to KaniWani, Tampermonkey says that âNo script is runningâ, even though I double checked to make sure the script is both installed and enabled. The same thing happened with the previous version you posted a while ago (even after going to review page and doing a page refresh).
Iâm using Tampermonkey v4.8.41, Chromium Version 67.0.3396.99 (Official Build) and Ubuntu 17.10 (64-bit).
Oh interesting - Iâm not completely sure why that would be the case. I use ViolentMonkey, but I tried it with TamperMonkey v4.8.41, and it seems to work for me.
By chance, do you use www.kaniwani.com
instead of kaniwani.com
? (I actually donât know the difference between them, but I use the latter; I also canât seem to log in to the former at all)
I modified the scriptâs @match to use a simpler form. Maybe try the newest script version and see if that works? KaniWani audio
It works now!
I do use www.kaniwani.com
! Actually, I just type k
on the address bar and the browser just takes me there.
Thanks once again!
Thank you so much for doing this. I was on the verge of giving up and deleting kaniwani from my study routine when I decided to check one last time to see if the script had been fixed.
I added a J-key shortcut with the following:
// @require http://www.openjs.com/scripts/events/keyboard_shortcuts/shortcut.js
âŠ
shortcut.add(âJâ,function() {
playAudio();
},{
âtypeâ:âkeydownâ,
âdisable_in_inputâ:true,
âtargetâ:document
});
You can add code blocks with triple backticks ```
Awesome, so happy this is working again! Was this written using API v1? Would it be possible to add a variable for changing the voice to male? Not sure if API v2 would need to be used. Thanks!
Got it, thanks.