term; } public function setTerm(Term $term): static { $this->fields['VocabKanji'] = $term->getKanji(); $this->fields['VocabFurigana'] = $term->getReading(); $this->fields['VocabDef'] = $term->toAnkiVocabDef(); $this->fields['SentFurigana'] = ''; // We don't want to keep this $this->fields['SentKanji'] = $this->stringHighlight( $this->fields['SentKanji'], $term->getKanji(), ); $this->term = $term; return $this; } // ------------------------------------------------------- Anki-related --- public static function fromAnki(array $noteInfo): static { $note = parent::fromAnki($noteInfo); if ($note->getModel() !== self::MODEL_NAME) { throw new \Exception('Trying to parse wrong model'); } $note->mediaInfo = self::parseMediaInfo($note->fields['Notes']); // Set VocabKanji field $note->term = Term::fromNoteFields($note->fields)[0] ?? null; if ($note->term === null) { throw new \Exception("Couldn't get term for Listening card"); } return $note; } public static function fromNote(Note $origNote, Term $term): static { $slNote = new static(); foreach (get_object_vars($origNote) as $prop => $value) { $slNote->$prop = $value; } // Related fields are updated using the setter $slNote->setTerm($term); // Reset relations and basic data $slNote->id = null; $slNote->model = self::MODEL_NAME; $slNote->cardIds = []; return $slNote; } public function toAnki(): array { return $this->fields; } // ---------------------------------------------------- Derived methods --- public function isSentKanjiHighlighted(): bool { return str_contains( $this->fields['SentKanji'], self::HIGHLIGHT_ATTR_KANJI, ); } private static function parseMediaInfo(string $notes): ?array { $matches = null; // Parse the notes fields. It can be in the form of // series-name_S01 EP07 (11h22m33s44ms) // or // movie-name EP (11h22m33s44ms) if (1 !== preg_match( '/(?[0-9A-Za-z\-_]+)(_S)?(?\d*) EP(?\d*) \((?