Card Repetion Field / Javascript variable

Hi,
I’m learning a language. I need a possibility to play a certain audio files the first time a card is shown (much longer file with example sentences + a notification sound so that I know it’s a new word), the second time it is shown, would be played another audio file (shorter one) and the other times will be played third audio file (just definition of the word).
How can I do this? I’ve searched and couldn’t find anything, I was thinking about creating a field which will count card reviews, but even CoPilot said it isn’t possible in Anki to modify fields during reviews through Javascript.

Anki certainly counts how many times a card was reviewed, why can’t we use this variable in javascript?
Thank you.

Hello @j001 :slight_smile:

I think you will need to use the AnkiDroid JavaScript API for this: AnkiDroid Javascript API · ankidroid/Anki-Android Wiki · GitHub

For Compatibility with Anki Desktop, you will need one of those addons:
https://ankiweb.net/shared/info/1490471827

AFAIK, this method will not work on Ankimobile or Ankiweb.net.

As mentioned in the first link, the Ankidroid JavaScript API is a pretty advanced feature and you might want to consider that the API may break in future Ankidroid releases. The same will be true for the Desktop Addon.

This will be quite complicated because you want to play multiple audio files.

If you only had one audio file to play in specific situations then code like below works fine. Here, you’d place your first time audio in the note field First_time_audio and the normal audio in Normal_audio.

Only one audio file per case

<div id="first-time-audio">
	{{First_time_audio}}
</div>
<div id="normal-audio">
  {{Normal_audio}}
</div>

<script>
  var handlePromise = (val, func) => {
    if (typeof val === "object" && val !== null && !!val.then) {
      val.then((res) => func(res.value));
    } else {
      func(val);
    }
  };

  var setReps = (repsVal) =>
    handlePromise(repsVal, (reps = 0) => {
	let id = "normal-audio";
	if (reps === 0) id = "first-time-audio";

      // If there's multiple audio, this only selects the first one
      var audioBtn = document.querySelector(`#${id} .replay-button`);

      // Use R to replay audio
      document.addEventListener("keydown", (event) => {
        // Android keydown event always has keyCode = 229
        if (event.isComposing || event.keyCode === 229) {
          return;
        }
        // q = 81
        if (event.key === "r" && audioBtn) {
          audioBtn.click();
        }
      });

	// Set replay to AnkiDroid userAction
	window.userJs1 = () => {
		audioBtn.click();
	};

      if (audioBtn) audioBtn.click();
    });
  try {
    var jsApiContract = {
      version: "0.0.3",
      developer: "your@email.here"
    };

    var api = new AnkiDroidJS(jsApiContract);

    consoleLog("AnkiDroid API", api);

    setReps(AnkiDroidJS.ankiGetCardReps());
  } catch {
    pycmd("AnkiJS.ankiGetCardReps()", setReps);
  }
</script>

The problem with multiple audio files is that when you do audioBtn.click(); in a loop, only the first audio actually plays. You’d need to somehow wait for the audio playing to finish before calling the next audioBtn.click();

There is a solution to this too… but then it gets a lot more difficult as you will need to play your audio files using <audio> tags instead, copy your audio file references a second time in your note fields and then add more javascript to manage playing the files in a row.

Variation on the script from here: [AnkiMobile] Auto-advance vs. html audio element? - #7 by jhhr

Multiple audio files one or the other case

<audio id="player" controls></audio>

<script>
	function parseAudioArray(audioArrayStr) {
		try {
			return JSON.parse(audioArrayStr);
		} catch (e) {
			console.log("Error parsing audio array, check your audio fields", e);
			return [];
		}
  function playAudiosForRepCount(repsNum) {
    let audios = parseAudioArray(`{{Normal_audio_array}}`);
    if (repsNum === 0) {
			audios = parseAudioArray(`{{First_time_audio_array}}`);
			// Add notification sound as first audio
			audios.unshift("_notification.mp3");
		};
    const player = document.getElementById("player");
    let index = 0;

    player.addEventListener("ended", () => {
      if (++index < audios.length) {
        player.src = audios[index];
        player.play();
      }
    });
		
    // play the first audio file
    player.src = audios[index];
    player.play();
  }

	// Default to normal audio
	var playAudios = () => {
		playAudiosForRepCount(1);
	}
	
		
	// Use R to replay audio
	document.addEventListener("keydown", (event) => {
		// Android keydown event always has keyCode = 229
		if (event.isComposing || event.keyCode === 229) {
			return;
		}
		// q = 81
		if (event.key === "r" && audioBtn) {
			playAudios()
		}
	});

	// Set replay to AnkiDroid userAction
	window.userJs1 = playAudios;

  var handlePromise = (val, func) => {
    if (typeof val === "object" && val !== null && !!val.then) {
      val.then((res) => func(res.value));
    } else {
      func(val);
    }
  };

  var setReps = (repsVal) => {
    handlePromise(repsVal, (repsNum = 0) => {
      playAudios = () => {
				playAudiosForRepCount(repsNum);
			};
    });
  };
  try {
    const jsApiContract = { version: "0.0.3", developer: "your@email.here" };

    const api = new AnkiDroidJS(jsApiContract);

    console.log("AnkiDroid API", api);

    setReps(api.ankiGetCardReps());
  } catch (e) {
    if (globalThis.ankiPlatform !== "desktop") {
      console.log("AnkiDroid API error", e);
    } else {
      pycmd("AnkiJS.ankiGetCardReps()", setReps);
    }
  }
</script>

And then you’ll need to copy your audio files into additional fields in your notes (like I suggested here 我让卡片中的视频自动轮播,我需要知道某个视频何时结束? - #4 by jhhr). In your case you’ll have to store your audios in at least three fields

  1. All_audios = [sound:first_time_audio_1.mp3][sound:first_time_audio_2.mp3][sound:short_audio.mp3] This field is needed so that checking media doesn’t mark your audio files as unused. You won’t be using this in your template.
  2. First_time_audio_array = [“first_time_audio_1.mp3”, “first_time_audio_2.mp4”]
  3. Normal_audio_array = ["short_audio.mp3"]
  • Ensure that First_time_audio_array and Normal_audio_array contains a valid javascript array

Your notification sound is going to be the same every time so it doesn’t need to be in the note fields. You can prefix the file name with _ and put that in your collection.media, in my example above the file name is _notification.mp3

Thank you friends. I will try these solutions and report back.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.