Greasy Fork is available in English.

WaniKani Lesson Examples Audio

Allows you to play audio for example vocab during kanji lessons.

  1. // ==UserScript==
  2. // @name WaniKani Lesson Examples Audio
  3. // @namespace https://www.wanikani.com
  4. // @description Allows you to play audio for example vocab during kanji lessons.
  5. // @author seanblue
  6. // @version 1.0.2
  7. // @include *://www.wanikani.com/lesson/session*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. const eventPrefix = 'seanblue.example_audio.';
  12.  
  13. (function($) {$.each(['show'], function(i, ev) { var el = $.fn[ev]; $.fn[ev] = function() { this.trigger(eventPrefix + ev); return el.apply(this, arguments); }; }); })(jQuery);
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. if (!wkof) {
  19. var response = confirm('WaniKani Lesson Examples Audio requires WaniKani Open Framework.\n Click "OK" to be forwarded to installation instructions.');
  20.  
  21. if (response) {
  22. window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
  23. }
  24.  
  25. return;
  26. }
  27.  
  28. wkof.include('ItemData');
  29. wkof.ready('ItemData').then(initialize);
  30.  
  31. var subjectLoadingInitiated = false;
  32. var subjectsLoadedPromise = promise();
  33. var subjectToAudioMap;
  34.  
  35. var config = {
  36. wk_items: {
  37. filters: {
  38. item_type: 'voc'
  39. }
  40. }
  41. };
  42.  
  43. function promise(){var a,b,c=new Promise(function(d,e){a=d;b=e;});c.resolve=a;c.reject=b;return c;}
  44.  
  45. function initialize() {
  46. $('#supplement-kan-related-vocabulary').on(eventPrefix + 'show', function(e) {
  47. loadSubjects();
  48.  
  49. subjectsLoadedPromise.then(function() {
  50. let listElements = $(e.currentTarget).find('li');
  51.  
  52. for (let i = 0; i < listElements.length; i++) {
  53. setUpAudio($(listElements[i]));
  54. }
  55. });
  56. });
  57. }
  58.  
  59. function loadSubjects() {
  60. if (!subjectLoadingInitiated) {
  61. subjectLoadingInitiated = true;
  62. wkof.ItemData.get_items(config).then(processSubjects);
  63. }
  64.  
  65. return subjectsLoadedPromise;
  66. }
  67.  
  68. function processSubjects(items) {
  69. subjectToAudioMap = items.reduce(function(map, obj) {
  70. map[obj.data.characters] = getAudioUrl(obj);
  71. return map;
  72. }, {});
  73.  
  74. subjectsLoadedPromise.resolve();
  75. }
  76.  
  77. function getAudioUrl(item) {
  78. var mp3Audios = item.data.pronunciation_audios.filter(a => a.content_type === 'audio/mpeg');
  79. var preferredAudios = mp3Audios.filter(a => a.metadata.voice_actor_id === WaniKani.default_voice_actor_id);
  80.  
  81. if (preferredAudios.length > 0) {
  82. return preferredAudios[0].url;
  83. }
  84.  
  85. if (mp3Audios.length > 0) {
  86. return mp3Audios[0].url;
  87. }
  88.  
  89. return '';
  90. }
  91.  
  92. function setUpAudio(el) {
  93. let existingAudioButton = el.find('button.audio-btn');
  94.  
  95. if (existingAudioButton.length > 0) {
  96. return;
  97. }
  98.  
  99. let characters = el.find('span.vocabulary').text();
  100. let audioUrl = subjectToAudioMap[characters];
  101.  
  102. if (audioUrl === '') {
  103. return;
  104. }
  105.  
  106. let audioButtonElem = getAudioButtonElement().appendTo(el);
  107. let audioElem = getAudioElement(audioUrl);
  108.  
  109. setUpAudioEvents(audioButtonElem, audioElem);
  110. }
  111.  
  112. function getAudioButtonElement() {
  113. return $('<button type="button" title="Play pronunciation audio" class="audio-btn audio-idle"></button>');
  114. }
  115.  
  116. function getAudioElement(audioUrl) {
  117. let audioElem = $('<audio></audio>');
  118.  
  119. $('<source></source>', {
  120. src: audioUrl,
  121. type: 'audio/mpeg'
  122. }).appendTo(audioElem);
  123.  
  124. return audioElem;
  125. }
  126.  
  127. function setUpAudioEvents(audioButtonElem, audioElem) {
  128. audioElem[0].addEventListener('play', function () {
  129. audioButtonElem.removeClass('audio-idle').addClass('audio-play');
  130. });
  131.  
  132. audioElem[0].addEventListener('ended', function () {
  133. audioButtonElem.removeClass('audio-play').addClass('audio-idle');
  134. });
  135.  
  136. audioButtonElem.on('click', function() {
  137. audioElem[0].play();
  138. });
  139.  
  140. }
  141. })();