yomitan-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +485 -0
- package/dist/anki-connect-BQyCGW3O.cjs +513 -0
- package/dist/anki-connect-BQyCGW3O.cjs.map +1 -0
- package/dist/anki-connect-CPPuhyiQ.js +6 -0
- package/dist/anki-connect-DbrQHphS.js +495 -0
- package/dist/anki-connect-DbrQHphS.js.map +1 -0
- package/dist/anki-connect-DcheJrp-.cjs +6 -0
- package/dist/anki.cjs +1758 -0
- package/dist/anki.cjs.map +1 -0
- package/dist/anki.d.cts +751 -0
- package/dist/anki.d.cts.map +1 -0
- package/dist/anki.d.ts +751 -0
- package/dist/anki.d.ts.map +1 -0
- package/dist/anki.js +1751 -0
- package/dist/anki.js.map +1 -0
- package/dist/audio-D9DvYyB7.d.cts +48 -0
- package/dist/audio-D9DvYyB7.d.cts.map +1 -0
- package/dist/audio-DQulUkDM.d.ts +48 -0
- package/dist/audio-DQulUkDM.d.ts.map +1 -0
- package/dist/audio-url-generator-BXvQaqUi.cjs +4 -0
- package/dist/audio-url-generator-Dy2hb2Mm.cjs +414 -0
- package/dist/audio-url-generator-Dy2hb2Mm.cjs.map +1 -0
- package/dist/audio-url-generator-Qi0rfzHz.js +4 -0
- package/dist/audio-url-generator-pFQAB5Nb.js +390 -0
- package/dist/audio-url-generator-pFQAB5Nb.js.map +1 -0
- package/dist/audio.cjs +7 -0
- package/dist/audio.d.cts +86 -0
- package/dist/audio.d.cts.map +1 -0
- package/dist/audio.d.ts +86 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +4 -0
- package/dist/batch-processor-BR-gB3H3.js +84 -0
- package/dist/batch-processor-BR-gB3H3.js.map +1 -0
- package/dist/batch-processor-CSF1acTw.cjs +3 -0
- package/dist/batch-processor-DFqM_L-_.cjs +91 -0
- package/dist/batch-processor-DFqM_L-_.cjs.map +1 -0
- package/dist/batch-processor-Quo9jUyf.js +3 -0
- package/dist/chunk-BCwAaXi7.cjs +31 -0
- package/dist/cjk-util-Dp0ZU0sh.cjs +167 -0
- package/dist/cjk-util-Dp0ZU0sh.cjs.map +1 -0
- package/dist/cjk-util-DubXBGDG.js +94 -0
- package/dist/cjk-util-DubXBGDG.js.map +1 -0
- package/dist/core-BUpclilG.d.cts +102 -0
- package/dist/core-BUpclilG.d.cts.map +1 -0
- package/dist/core-DFUj5GtA.d.ts +102 -0
- package/dist/core-DFUj5GtA.d.ts.map +1 -0
- package/dist/database.cjs +7 -0
- package/dist/database.d.cts +4 -0
- package/dist/database.d.ts +4 -0
- package/dist/database.js +5 -0
- package/dist/dictionary-D7l-qFt1.d.cts +316 -0
- package/dist/dictionary-D7l-qFt1.d.cts.map +1 -0
- package/dist/dictionary-_vzfBLWi.d.ts +316 -0
- package/dist/dictionary-_vzfBLWi.d.ts.map +1 -0
- package/dist/dictionary-data-util-CHnRdYZ9.cjs +378 -0
- package/dist/dictionary-data-util-CHnRdYZ9.cjs.map +1 -0
- package/dist/dictionary-data-util-CfOLfEDE.js +323 -0
- package/dist/dictionary-data-util-CfOLfEDE.js.map +1 -0
- package/dist/dictionary-database-BDC2f9zc.d.ts +58 -0
- package/dist/dictionary-database-BDC2f9zc.d.ts.map +1 -0
- package/dist/dictionary-database-CU4TsvCC.js +393 -0
- package/dist/dictionary-database-CU4TsvCC.js.map +1 -0
- package/dist/dictionary-database-DsOi04Sg.d.cts +58 -0
- package/dist/dictionary-database-DsOi04Sg.d.cts.map +1 -0
- package/dist/dictionary-database-lvFvftnO.cjs +412 -0
- package/dist/dictionary-database-lvFvftnO.cjs.map +1 -0
- package/dist/dictionary-importer-BkQQSBhm.d.ts +237 -0
- package/dist/dictionary-importer-BkQQSBhm.d.ts.map +1 -0
- package/dist/dictionary-importer-Cen1z6co.js +1821 -0
- package/dist/dictionary-importer-Cen1z6co.js.map +1 -0
- package/dist/dictionary-importer-DYmmWmcX.cjs +8 -0
- package/dist/dictionary-importer-Da3AuTZw.d.cts +237 -0
- package/dist/dictionary-importer-Da3AuTZw.d.cts.map +1 -0
- package/dist/dictionary-importer-Dhn75iZ4.cjs +1834 -0
- package/dist/dictionary-importer-Dhn75iZ4.cjs.map +1 -0
- package/dist/dictionary-importer-xWkel0h-.js +8 -0
- package/dist/dictionary-update-checker-BNE4pGTx.js +4 -0
- package/dist/dictionary-update-checker-Byjvifd2.cjs +75 -0
- package/dist/dictionary-update-checker-Byjvifd2.cjs.map +1 -0
- package/dist/dictionary-update-checker-YdpalZ41.cjs +4 -0
- package/dist/dictionary-update-checker-kKukiovj.js +69 -0
- package/dist/dictionary-update-checker-kKukiovj.js.map +1 -0
- package/dist/display-generator-BGVWiI0t.js +746 -0
- package/dist/display-generator-BGVWiI0t.js.map +1 -0
- package/dist/display-generator-BMQmG5Ov.cjs +9 -0
- package/dist/display-generator-BxZ7mBjP.js +9 -0
- package/dist/display-generator-DyP-HNzP.cjs +758 -0
- package/dist/display-generator-DyP-HNzP.cjs.map +1 -0
- package/dist/errors-BSezaJwm.cjs +35 -0
- package/dist/errors-BSezaJwm.cjs.map +1 -0
- package/dist/errors-DuuDSO5N.js +22 -0
- package/dist/errors-DuuDSO5N.js.map +1 -0
- package/dist/frequency-ranking-BXjfhhUQ.js +3 -0
- package/dist/frequency-ranking-Cx1kkIrw.cjs +3 -0
- package/dist/frequency-ranking-DEJMTMdg.js +159 -0
- package/dist/frequency-ranking-DEJMTMdg.js.map +1 -0
- package/dist/frequency-ranking-DVYxTXN-.cjs +166 -0
- package/dist/frequency-ranking-DVYxTXN-.cjs.map +1 -0
- package/dist/furigana-5HK97CY8.js +4 -0
- package/dist/furigana-9bBI9-qe.d.ts +47 -0
- package/dist/furigana-9bBI9-qe.d.ts.map +1 -0
- package/dist/furigana-B3-0y231.js +471 -0
- package/dist/furigana-B3-0y231.js.map +1 -0
- package/dist/furigana-CjOhzvZt.d.cts +47 -0
- package/dist/furigana-CjOhzvZt.d.cts.map +1 -0
- package/dist/furigana-DpZLcues.cjs +609 -0
- package/dist/furigana-DpZLcues.cjs.map +1 -0
- package/dist/furigana-h3v2ub4-.cjs +4 -0
- package/dist/import.cjs +12 -0
- package/dist/import.d.cts +107 -0
- package/dist/import.d.cts.map +1 -0
- package/dist/import.d.ts +107 -0
- package/dist/import.d.ts.map +1 -0
- package/dist/import.js +9 -0
- package/dist/index.cjs +275 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +211 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +211 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +238 -0
- package/dist/index.js.map +1 -0
- package/dist/json-DGd-cunA.js +17 -0
- package/dist/json-DGd-cunA.js.map +1 -0
- package/dist/json-DKWp-B7Y.cjs +30 -0
- package/dist/json-DKWp-B7Y.cjs.map +1 -0
- package/dist/language-KN_u-nTR.d.ts +104 -0
- package/dist/language-KN_u-nTR.d.ts.map +1 -0
- package/dist/language-xAbQxgXc.d.cts +104 -0
- package/dist/language-xAbQxgXc.d.cts.map +1 -0
- package/dist/language.cjs +15626 -0
- package/dist/language.cjs.map +1 -0
- package/dist/language.d.cts +959 -0
- package/dist/language.d.cts.map +1 -0
- package/dist/language.d.ts +959 -0
- package/dist/language.d.ts.map +1 -0
- package/dist/language.js +15522 -0
- package/dist/language.js.map +1 -0
- package/dist/log-D8KtR3aP.cjs +67 -0
- package/dist/log-D8KtR3aP.cjs.map +1 -0
- package/dist/log-hgSll-dS.js +60 -0
- package/dist/log-hgSll-dS.js.map +1 -0
- package/dist/lookup.cjs +13 -0
- package/dist/lookup.d.cts +161 -0
- package/dist/lookup.d.cts.map +1 -0
- package/dist/lookup.d.ts +161 -0
- package/dist/lookup.d.ts.map +1 -0
- package/dist/lookup.js +10 -0
- package/dist/media-loader-BABA_E4W.js +3 -0
- package/dist/media-loader-Ce9cuANS.cjs +21 -0
- package/dist/media-loader-Ce9cuANS.cjs.map +1 -0
- package/dist/media-loader-qRti-Q6h.js +14 -0
- package/dist/media-loader-qRti-Q6h.js.map +1 -0
- package/dist/media-loader-xlUGaJrx.cjs +3 -0
- package/dist/multi-language-transformer-AlxOM6b3.js +637 -0
- package/dist/multi-language-transformer-AlxOM6b3.js.map +1 -0
- package/dist/multi-language-transformer-MdbQBBOt.cjs +685 -0
- package/dist/multi-language-transformer-MdbQBBOt.cjs.map +1 -0
- package/dist/multi-language-transformer-SEhcJXEB.d.ts +63 -0
- package/dist/multi-language-transformer-SEhcJXEB.d.ts.map +1 -0
- package/dist/multi-language-transformer-Ul9mbRce.d.cts +63 -0
- package/dist/multi-language-transformer-Ul9mbRce.d.cts.map +1 -0
- package/dist/pronunciation-generator-BtBc4q_V.js +397 -0
- package/dist/pronunciation-generator-BtBc4q_V.js.map +1 -0
- package/dist/pronunciation-generator-CBYdXYou.js +4 -0
- package/dist/pronunciation-generator-CFbZlf5J.cjs +445 -0
- package/dist/pronunciation-generator-CFbZlf5J.cjs.map +1 -0
- package/dist/pronunciation-generator-DOz9hEuk.cjs +4 -0
- package/dist/render.cjs +2796 -0
- package/dist/render.cjs.map +1 -0
- package/dist/render.d.cts +424 -0
- package/dist/render.d.cts.map +1 -0
- package/dist/render.d.ts +424 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +2777 -0
- package/dist/render.js.map +1 -0
- package/dist/sentence-parser-BPAJNzqW.js +126 -0
- package/dist/sentence-parser-BPAJNzqW.js.map +1 -0
- package/dist/sentence-parser-BVIOI64h.cjs +132 -0
- package/dist/sentence-parser-BVIOI64h.cjs.map +1 -0
- package/dist/sentence-parser-BoHO3cHn.js +5 -0
- package/dist/sentence-parser-DQVLSW0z.cjs +5 -0
- package/dist/structured-content-generator-BtOApkTW.cjs +4 -0
- package/dist/structured-content-generator-Bx62RYa8.js +4 -0
- package/dist/structured-content-generator-CLnybumI.js +276 -0
- package/dist/structured-content-generator-CLnybumI.js.map +1 -0
- package/dist/structured-content-generator-DrwkB0-k.cjs +282 -0
- package/dist/structured-content-generator-DrwkB0-k.cjs.map +1 -0
- package/dist/text-utilities-B7PIythe.js +8 -0
- package/dist/text-utilities-B7PIythe.js.map +1 -0
- package/dist/text-utilities-Del2Ivkg.cjs +15 -0
- package/dist/text-utilities-Del2Ivkg.cjs.map +1 -0
- package/dist/translator-CRPlPzqi.cjs +1545 -0
- package/dist/translator-CRPlPzqi.cjs.map +1 -0
- package/dist/translator-CWgG5drA.js +1539 -0
- package/dist/translator-CWgG5drA.js.map +1 -0
- package/dist/translator-CaGtJvnQ.cjs +6 -0
- package/dist/translator-Cc6OGxrW.d.ts +180 -0
- package/dist/translator-Cc6OGxrW.d.ts.map +1 -0
- package/dist/translator-CcA-s-W4.d.cts +180 -0
- package/dist/translator-CcA-s-W4.d.cts.map +1 -0
- package/dist/translator-CuJOTK6l.js +6 -0
- package/dist/utilities-C-lbZaJE.cjs +52 -0
- package/dist/utilities-C-lbZaJE.cjs.map +1 -0
- package/dist/utilities-bi3EF-q5.js +33 -0
- package/dist/utilities-bi3EF-q5.js.map +1 -0
- package/package.json +102 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { readResponseJson } from "./json-DGd-cunA.js";
|
|
2
|
+
|
|
3
|
+
//#region src/audio/audio-url-generator.ts
|
|
4
|
+
/**
|
|
5
|
+
* A default DOM parser implementation that uses the standard DOMParser API
|
|
6
|
+
* (available in browsers and some Node.js DOM libraries like linkedom/jsdom).
|
|
7
|
+
*/
|
|
8
|
+
var NativeSimpleDOMParser = class {
|
|
9
|
+
_document;
|
|
10
|
+
constructor(content) {
|
|
11
|
+
const parser = new DOMParser();
|
|
12
|
+
this._document = parser.parseFromString(content, "text/html");
|
|
13
|
+
}
|
|
14
|
+
getElementById(id) {
|
|
15
|
+
return this._document.getElementById(id);
|
|
16
|
+
}
|
|
17
|
+
getElementsByClassName(className, root) {
|
|
18
|
+
const parent = root ?? this._document;
|
|
19
|
+
return [...parent.getElementsByClassName(className)];
|
|
20
|
+
}
|
|
21
|
+
getElementByTagName(tagName, root) {
|
|
22
|
+
const parent = root ?? this._document;
|
|
23
|
+
const elements = parent.getElementsByTagName(tagName);
|
|
24
|
+
return elements.length > 0 ? elements[0] : null;
|
|
25
|
+
}
|
|
26
|
+
getAttribute(element, attribute) {
|
|
27
|
+
return element.getAttribute(attribute);
|
|
28
|
+
}
|
|
29
|
+
getTextContent(element) {
|
|
30
|
+
return element.textContent ?? "";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const DEFAULT_REQUEST_INIT_PARAMS = {
|
|
34
|
+
method: "GET",
|
|
35
|
+
mode: "cors",
|
|
36
|
+
cache: "default",
|
|
37
|
+
credentials: "omit",
|
|
38
|
+
redirect: "follow",
|
|
39
|
+
referrerPolicy: "no-referrer"
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Generates audio URLs for dictionary terms from various audio sources.
|
|
43
|
+
* Ported from Yomitan's AudioDownloader, but only extracts URL generation logic (not download).
|
|
44
|
+
*/
|
|
45
|
+
var AudioUrlGenerator = class {
|
|
46
|
+
_getInfoHandlers;
|
|
47
|
+
_regionNames;
|
|
48
|
+
_domParserFactory;
|
|
49
|
+
_customAudioListSchema;
|
|
50
|
+
constructor(options) {
|
|
51
|
+
this._domParserFactory = options?.domParserFactory ?? null;
|
|
52
|
+
this._customAudioListSchema = null;
|
|
53
|
+
this._regionNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
54
|
+
this._getInfoHandlers = new Map([
|
|
55
|
+
["jpod101", this._getInfoJpod101.bind(this)],
|
|
56
|
+
["language-pod-101", this._getInfoLanguagePod101.bind(this)],
|
|
57
|
+
["jisho", this._getInfoJisho.bind(this)],
|
|
58
|
+
["lingua-libre", this._getInfoLinguaLibre.bind(this)],
|
|
59
|
+
["wiktionary", this._getInfoWiktionary.bind(this)],
|
|
60
|
+
["text-to-speech", this._getInfoTextToSpeech.bind(this)],
|
|
61
|
+
["text-to-speech-reading", this._getInfoTextToSpeechReading.bind(this)],
|
|
62
|
+
["custom", this._getInfoCustom.bind(this)],
|
|
63
|
+
["custom-json", this._getInfoCustomJson.bind(this)]
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Gets audio URL info for a single source.
|
|
68
|
+
*/
|
|
69
|
+
async getTermAudioInfoList(source, term, reading, languageSummary) {
|
|
70
|
+
const handler = this._getInfoHandlers.get(source.type);
|
|
71
|
+
if (typeof handler === "function") try {
|
|
72
|
+
return await handler(term, reading, source, languageSummary);
|
|
73
|
+
} catch (_e) {}
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Gets audio URLs for a term from multiple sources.
|
|
78
|
+
*/
|
|
79
|
+
async getUrls(term, reading, sources, languageSummary) {
|
|
80
|
+
const results = [];
|
|
81
|
+
for (const source of sources) {
|
|
82
|
+
const infoList = await this.getTermAudioInfoList(source, term, reading, languageSummary);
|
|
83
|
+
results.push(...infoList);
|
|
84
|
+
}
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
_normalizeUrl(url, base) {
|
|
88
|
+
return new URL(url, base).href;
|
|
89
|
+
}
|
|
90
|
+
_createSimpleDOMParser(content) {
|
|
91
|
+
if (this._domParserFactory !== null) return this._domParserFactory(content);
|
|
92
|
+
if (typeof DOMParser !== "undefined") return new NativeSimpleDOMParser(content);
|
|
93
|
+
throw new Error("DOM parsing not supported. Provide a domParserFactory in the constructor.");
|
|
94
|
+
}
|
|
95
|
+
async _getInfoJpod101(term, reading) {
|
|
96
|
+
if (reading === term && this._isStringEntirelyKana(term)) {
|
|
97
|
+
reading = term;
|
|
98
|
+
term = "";
|
|
99
|
+
}
|
|
100
|
+
const params = new URLSearchParams();
|
|
101
|
+
if (term.length > 0) params.set("kanji", term);
|
|
102
|
+
if (reading.length > 0) params.set("kana", reading);
|
|
103
|
+
const url = `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?${params.toString()}`;
|
|
104
|
+
return [{
|
|
105
|
+
type: "url",
|
|
106
|
+
url
|
|
107
|
+
}];
|
|
108
|
+
}
|
|
109
|
+
async _getInfoLanguagePod101(term, reading, _details, languageSummary) {
|
|
110
|
+
const { name: language } = languageSummary;
|
|
111
|
+
const fetchUrl = this._getLanguagePod101FetchUrl(language);
|
|
112
|
+
const data = new URLSearchParams({
|
|
113
|
+
post: "dictionary_reference",
|
|
114
|
+
match_type: "exact",
|
|
115
|
+
search_query: term,
|
|
116
|
+
vulgar: "true"
|
|
117
|
+
});
|
|
118
|
+
const response = await fetch(fetchUrl, {
|
|
119
|
+
...DEFAULT_REQUEST_INIT_PARAMS,
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
122
|
+
body: data
|
|
123
|
+
});
|
|
124
|
+
const responseText = await response.text();
|
|
125
|
+
const dom = this._createSimpleDOMParser(responseText);
|
|
126
|
+
const urls = new Set();
|
|
127
|
+
for (const row of dom.getElementsByClassName("dc-result-row")) try {
|
|
128
|
+
const audio = dom.getElementByTagName("audio", row);
|
|
129
|
+
if (audio === null) continue;
|
|
130
|
+
const source = dom.getElementByTagName("source", audio);
|
|
131
|
+
if (source === null) continue;
|
|
132
|
+
let url = dom.getAttribute(source, "src");
|
|
133
|
+
if (url === null) continue;
|
|
134
|
+
if (!this._validateLanguagePod101Row(language, dom, row, term, reading)) continue;
|
|
135
|
+
url = this._normalizeUrl(url, response.url);
|
|
136
|
+
urls.add(url);
|
|
137
|
+
} catch (_e) {}
|
|
138
|
+
return [...urls].map((url) => ({
|
|
139
|
+
type: "url",
|
|
140
|
+
url
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
_validateLanguagePod101Row(language, dom, row, term, reading) {
|
|
144
|
+
switch (language) {
|
|
145
|
+
case "Japanese":
|
|
146
|
+
{
|
|
147
|
+
const htmlReadings = dom.getElementsByClassName("dc-vocab_kana", row);
|
|
148
|
+
if (htmlReadings.length === 0) return false;
|
|
149
|
+
const htmlReading = dom.getTextContent(htmlReadings[0]);
|
|
150
|
+
if (!htmlReading) return false;
|
|
151
|
+
if (reading !== term && reading !== htmlReading) return false;
|
|
152
|
+
}
|
|
153
|
+
break;
|
|
154
|
+
default: {
|
|
155
|
+
const vocab = dom.getElementsByClassName("dc-vocab", row);
|
|
156
|
+
if (vocab.length === 0) return false;
|
|
157
|
+
if (term !== dom.getTextContent(vocab[0])) return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
_getLanguagePod101FetchUrl(language) {
|
|
163
|
+
const podOrClass = this._getLanguagePod101PodOrClass(language);
|
|
164
|
+
const lowerCaseLanguage = language.toLowerCase();
|
|
165
|
+
return `https://www.${lowerCaseLanguage}${podOrClass}101.com/learningcenter/reference/dictionary_post`;
|
|
166
|
+
}
|
|
167
|
+
_getLanguagePod101PodOrClass(language) {
|
|
168
|
+
switch (language) {
|
|
169
|
+
case "Afrikaans":
|
|
170
|
+
case "Arabic":
|
|
171
|
+
case "Bulgarian":
|
|
172
|
+
case "Dutch":
|
|
173
|
+
case "Filipino":
|
|
174
|
+
case "Finnish":
|
|
175
|
+
case "French":
|
|
176
|
+
case "German":
|
|
177
|
+
case "Greek":
|
|
178
|
+
case "Hebrew":
|
|
179
|
+
case "Hindi":
|
|
180
|
+
case "Hungarian":
|
|
181
|
+
case "Indonesian":
|
|
182
|
+
case "Italian":
|
|
183
|
+
case "Japanese":
|
|
184
|
+
case "Persian":
|
|
185
|
+
case "Polish":
|
|
186
|
+
case "Portuguese":
|
|
187
|
+
case "Romanian":
|
|
188
|
+
case "Russian":
|
|
189
|
+
case "Spanish":
|
|
190
|
+
case "Swahili":
|
|
191
|
+
case "Swedish":
|
|
192
|
+
case "Thai":
|
|
193
|
+
case "Urdu":
|
|
194
|
+
case "Vietnamese": return "pod";
|
|
195
|
+
case "Cantonese":
|
|
196
|
+
case "Chinese":
|
|
197
|
+
case "Czech":
|
|
198
|
+
case "Danish":
|
|
199
|
+
case "English":
|
|
200
|
+
case "Korean":
|
|
201
|
+
case "Norwegian":
|
|
202
|
+
case "Turkish": return "class";
|
|
203
|
+
default: throw new Error("Invalid language for LanguagePod101");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async _getInfoJisho(term, reading) {
|
|
207
|
+
const fetchUrl = `https://jisho.org/search/${term}`;
|
|
208
|
+
const response = await fetch(fetchUrl, DEFAULT_REQUEST_INIT_PARAMS);
|
|
209
|
+
const responseText = await response.text();
|
|
210
|
+
const dom = this._createSimpleDOMParser(responseText);
|
|
211
|
+
try {
|
|
212
|
+
const audio = dom.getElementById(`audio_${term}:${reading}`);
|
|
213
|
+
if (audio !== null) {
|
|
214
|
+
const source = dom.getElementByTagName("source", audio);
|
|
215
|
+
if (source !== null) {
|
|
216
|
+
let url = dom.getAttribute(source, "src");
|
|
217
|
+
if (url !== null) {
|
|
218
|
+
url = this._normalizeUrl(url, response.url);
|
|
219
|
+
return [{
|
|
220
|
+
type: "url",
|
|
221
|
+
url
|
|
222
|
+
}];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
} catch (_e) {}
|
|
227
|
+
throw new Error("Failed to find audio URL");
|
|
228
|
+
}
|
|
229
|
+
async _getInfoLinguaLibre(term, _reading, _details, languageSummary) {
|
|
230
|
+
if (typeof languageSummary !== "object" || languageSummary === null) throw new Error("Invalid arguments");
|
|
231
|
+
const { iso639_3 } = languageSummary;
|
|
232
|
+
const searchCategory = `incategory:"Lingua_Libre_pronunciation-${iso639_3}"`;
|
|
233
|
+
const searchString = `-${term}.wav`;
|
|
234
|
+
const fetchUrl = `https://commons.wikimedia.org/w/api.php?action=query&format=json&list=search&srsearch=intitle:/${searchString}/i+${searchCategory}&srnamespace=6&origin=*`;
|
|
235
|
+
const validateFilename = (filename, fileUser) => {
|
|
236
|
+
const validFilenameTest = new RegExp(`^File:LL-Q\\d+\\s+\\(${iso639_3}\\)-${fileUser}-${term}\\.wav$`, "i");
|
|
237
|
+
return validFilenameTest.test(filename);
|
|
238
|
+
};
|
|
239
|
+
return await this._getInfoWikimediaCommons(fetchUrl, validateFilename);
|
|
240
|
+
}
|
|
241
|
+
async _getInfoWiktionary(term, _reading, _details, languageSummary) {
|
|
242
|
+
if (typeof languageSummary !== "object" || languageSummary === null) throw new Error("Invalid arguments");
|
|
243
|
+
const { iso } = languageSummary;
|
|
244
|
+
const searchString = `${iso}(-[a-zA-Z]{2})?-${term}[0123456789]*.ogg`;
|
|
245
|
+
const fetchUrl = `https://commons.wikimedia.org/w/api.php?action=query&format=json&list=search&srsearch=intitle:/${searchString}/i&srnamespace=6&origin=*`;
|
|
246
|
+
const validateFilename = (filename) => {
|
|
247
|
+
const validFilenameTest = new RegExp(`^File:${iso}(-\\w\\w)?-${term}\\d*\\.ogg$`, "i");
|
|
248
|
+
return validFilenameTest.test(filename);
|
|
249
|
+
};
|
|
250
|
+
const displayName = (filename, fileUser) => {
|
|
251
|
+
const match = filename.match(new RegExp(`^File:${iso}(-\\w\\w)-${term}`, "i"));
|
|
252
|
+
if (match === null) return fileUser;
|
|
253
|
+
const region = match[1].substring(1).toUpperCase();
|
|
254
|
+
const regionName = this._regionNames.of(region);
|
|
255
|
+
return `(${regionName}) ${fileUser}`;
|
|
256
|
+
};
|
|
257
|
+
return await this._getInfoWikimediaCommons(fetchUrl, validateFilename, displayName);
|
|
258
|
+
}
|
|
259
|
+
async _getInfoWikimediaCommons(fetchUrl, validateFilename, displayName = (_filename, fileUser) => fileUser) {
|
|
260
|
+
const response = await fetch(fetchUrl, DEFAULT_REQUEST_INIT_PARAMS);
|
|
261
|
+
const lookupResponse = await readResponseJson(response);
|
|
262
|
+
const lookupResults = lookupResponse.query.search;
|
|
263
|
+
const fetchFileInfos = lookupResults.map(async ({ title }) => {
|
|
264
|
+
const fileInfoURL = `https://commons.wikimedia.org/w/api.php?action=query&format=json&titles=${title}&prop=imageinfo&iiprop=user|url&origin=*`;
|
|
265
|
+
const response2 = await fetch(fileInfoURL, DEFAULT_REQUEST_INIT_PARAMS);
|
|
266
|
+
const fileResponse = await readResponseJson(response2);
|
|
267
|
+
const fileResults = fileResponse.query.pages;
|
|
268
|
+
const results = [];
|
|
269
|
+
for (const page of Object.values(fileResults)) {
|
|
270
|
+
const fileUrl = page.imageinfo[0].url;
|
|
271
|
+
const fileUser = page.imageinfo[0].user;
|
|
272
|
+
if (validateFilename(title, fileUser)) results.push({
|
|
273
|
+
type: "url",
|
|
274
|
+
url: fileUrl,
|
|
275
|
+
name: displayName(title, fileUser)
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return results;
|
|
279
|
+
});
|
|
280
|
+
return (await Promise.all(fetchFileInfos)).flat();
|
|
281
|
+
}
|
|
282
|
+
async _getInfoTextToSpeech(term, _reading, details) {
|
|
283
|
+
if (typeof details !== "object" || details === null) throw new Error("Invalid arguments");
|
|
284
|
+
const { voice } = details;
|
|
285
|
+
if (typeof voice !== "string") throw new Error("Invalid voice");
|
|
286
|
+
return [{
|
|
287
|
+
type: "tts",
|
|
288
|
+
text: term,
|
|
289
|
+
voice
|
|
290
|
+
}];
|
|
291
|
+
}
|
|
292
|
+
async _getInfoTextToSpeechReading(_term, reading, details) {
|
|
293
|
+
if (typeof details !== "object" || details === null) throw new Error("Invalid arguments");
|
|
294
|
+
const { voice } = details;
|
|
295
|
+
if (typeof voice !== "string") throw new Error("Invalid voice");
|
|
296
|
+
return [{
|
|
297
|
+
type: "tts",
|
|
298
|
+
text: reading,
|
|
299
|
+
voice
|
|
300
|
+
}];
|
|
301
|
+
}
|
|
302
|
+
async _getInfoCustom(term, reading, details, languageSummary) {
|
|
303
|
+
if (typeof details !== "object" || details === null) throw new Error("Invalid arguments");
|
|
304
|
+
let { url } = details;
|
|
305
|
+
if (typeof url !== "string") throw new Error("Invalid url");
|
|
306
|
+
url = this._getCustomUrl(term, reading, url, languageSummary);
|
|
307
|
+
return [{
|
|
308
|
+
type: "url",
|
|
309
|
+
url
|
|
310
|
+
}];
|
|
311
|
+
}
|
|
312
|
+
async _getInfoCustomJson(term, reading, details, languageSummary) {
|
|
313
|
+
if (typeof details !== "object" || details === null) throw new Error("Invalid arguments");
|
|
314
|
+
let { url } = details;
|
|
315
|
+
if (typeof url !== "string") throw new Error("Invalid url");
|
|
316
|
+
url = this._getCustomUrl(term, reading, url, languageSummary);
|
|
317
|
+
const response = await fetch(url, DEFAULT_REQUEST_INIT_PARAMS);
|
|
318
|
+
if (!response.ok) throw new Error(`Invalid response: ${response.status}`);
|
|
319
|
+
const responseJson = await readResponseJson(response);
|
|
320
|
+
this._validateCustomAudioList(responseJson);
|
|
321
|
+
const results = [];
|
|
322
|
+
for (const { url: url2, name } of responseJson.audioSources) {
|
|
323
|
+
const info = {
|
|
324
|
+
type: "url",
|
|
325
|
+
url: url2
|
|
326
|
+
};
|
|
327
|
+
if (typeof name === "string") info.name = name;
|
|
328
|
+
results.push(info);
|
|
329
|
+
}
|
|
330
|
+
return results;
|
|
331
|
+
}
|
|
332
|
+
_getCustomUrl(term, reading, url, languageSummary) {
|
|
333
|
+
if (typeof url !== "string") throw new Error("No custom URL defined");
|
|
334
|
+
const data = {
|
|
335
|
+
term,
|
|
336
|
+
reading,
|
|
337
|
+
language: languageSummary.iso
|
|
338
|
+
};
|
|
339
|
+
const replacer = (m0, m1) => Object.prototype.hasOwnProperty.call(data, m1) ? `${data[m1]}` : m0;
|
|
340
|
+
return url.replace(/\{([^}]*)\}/g, replacer);
|
|
341
|
+
}
|
|
342
|
+
_validateCustomAudioList(data) {
|
|
343
|
+
if (typeof data !== "object" || data === null) throw new Error("Invalid custom audio list response");
|
|
344
|
+
const obj = data;
|
|
345
|
+
if (obj.type !== "audioSourceList") throw new Error("Invalid custom audio list type");
|
|
346
|
+
if (!Array.isArray(obj.audioSources)) throw new Error("Invalid custom audio list audioSources");
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Simple check for whether a string is entirely kana (hiragana/katakana).
|
|
350
|
+
*/
|
|
351
|
+
_isStringEntirelyKana(str) {
|
|
352
|
+
if (str.length === 0) return false;
|
|
353
|
+
for (const char of str) {
|
|
354
|
+
const code = char.codePointAt(0);
|
|
355
|
+
if (code === void 0) return false;
|
|
356
|
+
if (!(code >= 12352 && code <= 12447 || code >= 12448 && code <= 12543)) return false;
|
|
357
|
+
}
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* Returns the set of required audio source types for a given language ISO code.
|
|
363
|
+
*/
|
|
364
|
+
function getRequiredAudioSourceList(language) {
|
|
365
|
+
return language === "ja" ? new Set([
|
|
366
|
+
"jpod101",
|
|
367
|
+
"language-pod-101",
|
|
368
|
+
"jisho"
|
|
369
|
+
]) : new Set([
|
|
370
|
+
"lingua-libre",
|
|
371
|
+
"language-pod-101",
|
|
372
|
+
"wiktionary"
|
|
373
|
+
]);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Returns required audio sources that are not already present in the given sources list.
|
|
377
|
+
*/
|
|
378
|
+
function getRequiredAudioSources(language, sources) {
|
|
379
|
+
const requiredSources = getRequiredAudioSourceList(language);
|
|
380
|
+
for (const { type } of sources) requiredSources.delete(type);
|
|
381
|
+
return [...requiredSources].map((type) => ({
|
|
382
|
+
type,
|
|
383
|
+
url: "",
|
|
384
|
+
voice: ""
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
//#endregion
|
|
389
|
+
export { AudioUrlGenerator, NativeSimpleDOMParser, getRequiredAudioSourceList, getRequiredAudioSources };
|
|
390
|
+
//# sourceMappingURL=audio-url-generator-pFQAB5Nb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-url-generator-pFQAB5Nb.js","names":["content: string","id: string","className: string","root?: Element","tagName: string","element: Element","attribute: string","DEFAULT_REQUEST_INIT_PARAMS: RequestInit","options?: {\n domParserFactory?: (content: string) => SimpleDOMParser;\n }","source: AudioSourceInfo","term: string","reading: string","languageSummary: LanguageSummary","sources: AudioSourceInfo[]","results: AudioInfo[]","url: string","base: string","_details: AudioSourceInfo","language: string","dom: SimpleDOMParser","row: Element","_reading: string","filename: string","fileUser: string","fetchUrl: string","validateFilename: (filename: string, fileUser: string) => boolean","displayName: (filename: string, fileUser: string) => string","results: AudioUrlInfo[]","details: AudioSourceInfo","_term: string","info: AudioUrlInfo","data: Record<string, string>","m0: string","m1: string","data: unknown","str: string"],"sources":["../src/audio/audio-url-generator.ts"],"sourcesContent":["import type {\n AudioInfo,\n AudioSourceInfo,\n AudioSourceType,\n AudioUrlInfo,\n CustomAudioList,\n WikimediaCommonsFileResponse,\n WikimediaCommonsLookupResponse,\n} from '../types/audio';\nimport type { LanguageSummary } from '../types/language';\nimport { readResponseJson } from '../util/json';\n\n/**\n * Interface for parsing HTML content. Consumers can inject\n * a DOM parser implementation (e.g. linkedom, jsdom, or the browser's native DOMParser).\n */\nexport interface SimpleDOMParser {\n getElementById(id: string): Element | null;\n getElementsByClassName(className: string, root?: Element): Element[];\n getElementByTagName(tagName: string, root?: Element): Element | null;\n getAttribute(element: Element, attribute: string): string | null;\n getTextContent(element: Element): string;\n}\n\n/**\n * A default DOM parser implementation that uses the standard DOMParser API\n * (available in browsers and some Node.js DOM libraries like linkedom/jsdom).\n */\nexport class NativeSimpleDOMParser implements SimpleDOMParser {\n private _document: Document;\n\n constructor(content: string) {\n const parser = new DOMParser();\n this._document = parser.parseFromString(content, 'text/html');\n }\n\n getElementById(id: string): Element | null {\n return this._document.getElementById(id);\n }\n\n getElementsByClassName(className: string, root?: Element): Element[] {\n const parent = root ?? this._document;\n return [...parent.getElementsByClassName(className)];\n }\n\n getElementByTagName(tagName: string, root?: Element): Element | null {\n const parent = root ?? this._document;\n const elements = parent.getElementsByTagName(tagName);\n return elements.length > 0 ? elements[0] : null;\n }\n\n getAttribute(element: Element, attribute: string): string | null {\n return element.getAttribute(attribute);\n }\n\n getTextContent(element: Element): string {\n return element.textContent ?? '';\n }\n}\n\nconst DEFAULT_REQUEST_INIT_PARAMS: RequestInit = {\n method: 'GET',\n mode: 'cors',\n cache: 'default',\n credentials: 'omit',\n redirect: 'follow',\n referrerPolicy: 'no-referrer',\n};\n\ntype GetInfoHandler = (\n term: string,\n reading: string,\n source: AudioSourceInfo,\n languageSummary: LanguageSummary,\n) => Promise<AudioInfo[]>;\n\n/**\n * Generates audio URLs for dictionary terms from various audio sources.\n * Ported from Yomitan's AudioDownloader, but only extracts URL generation logic (not download).\n */\nexport class AudioUrlGenerator {\n private _getInfoHandlers: Map<AudioSourceType, GetInfoHandler>;\n private _regionNames: Intl.DisplayNames;\n private _domParserFactory: ((content: string) => SimpleDOMParser) | null;\n private _customAudioListSchema: object | null;\n\n constructor(options?: {\n domParserFactory?: (content: string) => SimpleDOMParser;\n }) {\n this._domParserFactory = options?.domParserFactory ?? null;\n this._customAudioListSchema = null;\n this._regionNames = new Intl.DisplayNames(['en'], { type: 'region' });\n\n this._getInfoHandlers = new Map<AudioSourceType, GetInfoHandler>([\n ['jpod101', this._getInfoJpod101.bind(this)],\n ['language-pod-101', this._getInfoLanguagePod101.bind(this)],\n ['jisho', this._getInfoJisho.bind(this)],\n ['lingua-libre', this._getInfoLinguaLibre.bind(this)],\n ['wiktionary', this._getInfoWiktionary.bind(this)],\n ['text-to-speech', this._getInfoTextToSpeech.bind(this)],\n ['text-to-speech-reading', this._getInfoTextToSpeechReading.bind(this)],\n ['custom', this._getInfoCustom.bind(this)],\n ['custom-json', this._getInfoCustomJson.bind(this)],\n ]);\n }\n\n /**\n * Gets audio URL info for a single source.\n */\n async getTermAudioInfoList(\n source: AudioSourceInfo,\n term: string,\n reading: string,\n languageSummary: LanguageSummary,\n ): Promise<AudioInfo[]> {\n const handler = this._getInfoHandlers.get(source.type);\n if (typeof handler === 'function') {\n try {\n return await handler(term, reading, source, languageSummary);\n } catch (_e) {\n // NOP\n }\n }\n return [];\n }\n\n /**\n * Gets audio URLs for a term from multiple sources.\n */\n async getUrls(\n term: string,\n reading: string,\n sources: AudioSourceInfo[],\n languageSummary: LanguageSummary,\n ): Promise<AudioInfo[]> {\n const results: AudioInfo[] = [];\n for (const source of sources) {\n const infoList = await this.getTermAudioInfoList(source, term, reading, languageSummary);\n results.push(...infoList);\n }\n return results;\n }\n\n // Private\n\n private _normalizeUrl(url: string, base: string): string {\n return new URL(url, base).href;\n }\n\n private _createSimpleDOMParser(content: string): SimpleDOMParser {\n if (this._domParserFactory !== null) {\n return this._domParserFactory(content);\n }\n // Try native DOMParser\n if (typeof DOMParser !== 'undefined') {\n return new NativeSimpleDOMParser(content);\n }\n throw new Error('DOM parsing not supported. Provide a domParserFactory in the constructor.');\n }\n\n private async _getInfoJpod101(term: string, reading: string): Promise<AudioInfo[]> {\n if (reading === term && this._isStringEntirelyKana(term)) {\n reading = term;\n term = '';\n }\n\n const params = new URLSearchParams();\n if (term.length > 0) {\n params.set('kanji', term);\n }\n if (reading.length > 0) {\n params.set('kana', reading);\n }\n\n const url = `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?${params.toString()}`;\n return [{ type: 'url', url }];\n }\n\n private async _getInfoLanguagePod101(\n term: string,\n reading: string,\n _details: AudioSourceInfo,\n languageSummary: LanguageSummary,\n ): Promise<AudioInfo[]> {\n const { name: language } = languageSummary;\n\n const fetchUrl = this._getLanguagePod101FetchUrl(language);\n const data = new URLSearchParams({\n post: 'dictionary_reference',\n match_type: 'exact',\n search_query: term,\n vulgar: 'true',\n });\n const response = await fetch(fetchUrl, {\n ...DEFAULT_REQUEST_INIT_PARAMS,\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: data,\n });\n const responseText = await response.text();\n\n const dom = this._createSimpleDOMParser(responseText);\n const urls = new Set<string>();\n for (const row of dom.getElementsByClassName('dc-result-row')) {\n try {\n const audio = dom.getElementByTagName('audio', row);\n if (audio === null) {\n continue;\n }\n\n const source = dom.getElementByTagName('source', audio);\n if (source === null) {\n continue;\n }\n\n let url = dom.getAttribute(source, 'src');\n if (url === null) {\n continue;\n }\n\n if (!this._validateLanguagePod101Row(language, dom, row, term, reading)) {\n continue;\n }\n url = this._normalizeUrl(url, response.url);\n urls.add(url);\n } catch (_e) {\n // NOP\n }\n }\n return [...urls].map((url): AudioUrlInfo => ({ type: 'url', url }));\n }\n\n private _validateLanguagePod101Row(\n language: string,\n dom: SimpleDOMParser,\n row: Element,\n term: string,\n reading: string,\n ): boolean {\n switch (language) {\n case 'Japanese':\n {\n const htmlReadings = dom.getElementsByClassName('dc-vocab_kana', row);\n if (htmlReadings.length === 0) {\n return false;\n }\n\n const htmlReading = dom.getTextContent(htmlReadings[0]);\n if (!htmlReading) {\n return false;\n }\n if (reading !== term && reading !== htmlReading) {\n return false;\n }\n }\n break;\n default: {\n const vocab = dom.getElementsByClassName('dc-vocab', row);\n if (vocab.length === 0) {\n return false;\n }\n\n if (term !== dom.getTextContent(vocab[0])) {\n return false;\n }\n }\n }\n return true;\n }\n\n private _getLanguagePod101FetchUrl(language: string): string {\n const podOrClass = this._getLanguagePod101PodOrClass(language);\n const lowerCaseLanguage = language.toLowerCase();\n return `https://www.${lowerCaseLanguage}${podOrClass}101.com/learningcenter/reference/dictionary_post`;\n }\n\n private _getLanguagePod101PodOrClass(language: string): 'pod' | 'class' {\n switch (language) {\n case 'Afrikaans':\n case 'Arabic':\n case 'Bulgarian':\n case 'Dutch':\n case 'Filipino':\n case 'Finnish':\n case 'French':\n case 'German':\n case 'Greek':\n case 'Hebrew':\n case 'Hindi':\n case 'Hungarian':\n case 'Indonesian':\n case 'Italian':\n case 'Japanese':\n case 'Persian':\n case 'Polish':\n case 'Portuguese':\n case 'Romanian':\n case 'Russian':\n case 'Spanish':\n case 'Swahili':\n case 'Swedish':\n case 'Thai':\n case 'Urdu':\n case 'Vietnamese':\n return 'pod';\n case 'Cantonese':\n case 'Chinese':\n case 'Czech':\n case 'Danish':\n case 'English':\n case 'Korean':\n case 'Norwegian':\n case 'Turkish':\n return 'class';\n default:\n throw new Error('Invalid language for LanguagePod101');\n }\n }\n\n private async _getInfoJisho(term: string, reading: string): Promise<AudioInfo[]> {\n const fetchUrl = `https://jisho.org/search/${term}`;\n const response = await fetch(fetchUrl, DEFAULT_REQUEST_INIT_PARAMS);\n const responseText = await response.text();\n\n const dom = this._createSimpleDOMParser(responseText);\n try {\n const audio = dom.getElementById(`audio_${term}:${reading}`);\n if (audio !== null) {\n const source = dom.getElementByTagName('source', audio);\n if (source !== null) {\n let url = dom.getAttribute(source, 'src');\n if (url !== null) {\n url = this._normalizeUrl(url, response.url);\n return [{ type: 'url', url }];\n }\n }\n }\n } catch (_e) {\n // NOP\n }\n\n throw new Error('Failed to find audio URL');\n }\n\n private async _getInfoLinguaLibre(\n term: string,\n _reading: string,\n _details: AudioSourceInfo,\n languageSummary: LanguageSummary,\n ): Promise<AudioInfo[]> {\n if (typeof languageSummary !== 'object' || languageSummary === null) {\n throw new Error('Invalid arguments');\n }\n const { iso639_3 } = languageSummary;\n const searchCategory = `incategory:\"Lingua_Libre_pronunciation-${iso639_3}\"`;\n const searchString = `-${term}.wav`;\n const fetchUrl = `https://commons.wikimedia.org/w/api.php?action=query&format=json&list=search&srsearch=intitle:/${searchString}/i+${searchCategory}&srnamespace=6&origin=*`;\n\n const validateFilename = (filename: string, fileUser: string): boolean => {\n const validFilenameTest = new RegExp(`^File:LL-Q\\\\d+\\\\s+\\\\(${iso639_3}\\\\)-${fileUser}-${term}\\\\.wav$`, 'i');\n return validFilenameTest.test(filename);\n };\n\n return await this._getInfoWikimediaCommons(fetchUrl, validateFilename);\n }\n\n private async _getInfoWiktionary(\n term: string,\n _reading: string,\n _details: AudioSourceInfo,\n languageSummary: LanguageSummary,\n ): Promise<AudioInfo[]> {\n if (typeof languageSummary !== 'object' || languageSummary === null) {\n throw new Error('Invalid arguments');\n }\n const { iso } = languageSummary;\n const searchString = `${iso}(-[a-zA-Z]{2})?-${term}[0123456789]*.ogg`;\n const fetchUrl = `https://commons.wikimedia.org/w/api.php?action=query&format=json&list=search&srsearch=intitle:/${searchString}/i&srnamespace=6&origin=*`;\n\n const validateFilename = (filename: string): boolean => {\n const validFilenameTest = new RegExp(`^File:${iso}(-\\\\w\\\\w)?-${term}\\\\d*\\\\.ogg$`, 'i');\n return validFilenameTest.test(filename);\n };\n\n const displayName = (filename: string, fileUser: string): string => {\n const match = filename.match(new RegExp(`^File:${iso}(-\\\\w\\\\w)-${term}`, 'i'));\n if (match === null) {\n return fileUser;\n }\n const region = match[1].substring(1).toUpperCase();\n const regionName = this._regionNames.of(region);\n return `(${regionName}) ${fileUser}`;\n };\n\n return await this._getInfoWikimediaCommons(fetchUrl, validateFilename, displayName);\n }\n\n private async _getInfoWikimediaCommons(\n fetchUrl: string,\n validateFilename: (filename: string, fileUser: string) => boolean,\n displayName: (filename: string, fileUser: string) => string = (_filename, fileUser) => fileUser,\n ): Promise<AudioUrlInfo[]> {\n const response = await fetch(fetchUrl, DEFAULT_REQUEST_INIT_PARAMS);\n\n const lookupResponse = await readResponseJson<WikimediaCommonsLookupResponse>(response);\n const lookupResults = lookupResponse.query.search;\n\n const fetchFileInfos = lookupResults.map(async ({ title }) => {\n const fileInfoURL = `https://commons.wikimedia.org/w/api.php?action=query&format=json&titles=${title}&prop=imageinfo&iiprop=user|url&origin=*`;\n const response2 = await fetch(fileInfoURL, DEFAULT_REQUEST_INIT_PARAMS);\n const fileResponse = await readResponseJson<WikimediaCommonsFileResponse>(response2);\n const fileResults = fileResponse.query.pages;\n const results: AudioUrlInfo[] = [];\n for (const page of Object.values(fileResults)) {\n const fileUrl = page.imageinfo[0].url;\n const fileUser = page.imageinfo[0].user;\n if (validateFilename(title, fileUser)) {\n results.push({ type: 'url', url: fileUrl, name: displayName(title, fileUser) });\n }\n }\n return results;\n });\n\n return (await Promise.all(fetchFileInfos)).flat();\n }\n\n private async _getInfoTextToSpeech(term: string, _reading: string, details: AudioSourceInfo): Promise<AudioInfo[]> {\n if (typeof details !== 'object' || details === null) {\n throw new Error('Invalid arguments');\n }\n const { voice } = details;\n if (typeof voice !== 'string') {\n throw new Error('Invalid voice');\n }\n return [{ type: 'tts', text: term, voice }];\n }\n\n private async _getInfoTextToSpeechReading(\n _term: string,\n reading: string,\n details: AudioSourceInfo,\n ): Promise<AudioInfo[]> {\n if (typeof details !== 'object' || details === null) {\n throw new Error('Invalid arguments');\n }\n const { voice } = details;\n if (typeof voice !== 'string') {\n throw new Error('Invalid voice');\n }\n return [{ type: 'tts', text: reading, voice }];\n }\n\n private async _getInfoCustom(\n term: string,\n reading: string,\n details: AudioSourceInfo,\n languageSummary: LanguageSummary,\n ): Promise<AudioInfo[]> {\n if (typeof details !== 'object' || details === null) {\n throw new Error('Invalid arguments');\n }\n let { url } = details;\n if (typeof url !== 'string') {\n throw new Error('Invalid url');\n }\n url = this._getCustomUrl(term, reading, url, languageSummary);\n return [{ type: 'url', url }];\n }\n\n private async _getInfoCustomJson(\n term: string,\n reading: string,\n details: AudioSourceInfo,\n languageSummary: LanguageSummary,\n ): Promise<AudioInfo[]> {\n if (typeof details !== 'object' || details === null) {\n throw new Error('Invalid arguments');\n }\n let { url } = details;\n if (typeof url !== 'string') {\n throw new Error('Invalid url');\n }\n url = this._getCustomUrl(term, reading, url, languageSummary);\n\n const response = await fetch(url, DEFAULT_REQUEST_INIT_PARAMS);\n\n if (!response.ok) {\n throw new Error(`Invalid response: ${response.status}`);\n }\n\n const responseJson = await readResponseJson<CustomAudioList>(response);\n\n this._validateCustomAudioList(responseJson);\n\n const results: AudioInfo[] = [];\n for (const { url: url2, name } of responseJson.audioSources) {\n const info: AudioUrlInfo = { type: 'url', url: url2 };\n if (typeof name === 'string') {\n info.name = name;\n }\n results.push(info);\n }\n return results;\n }\n\n private _getCustomUrl(term: string, reading: string, url: string, languageSummary: LanguageSummary): string {\n if (typeof url !== 'string') {\n throw new Error('No custom URL defined');\n }\n const data: Record<string, string> = {\n term,\n reading,\n language: languageSummary.iso,\n };\n const replacer = (m0: string, m1: string): string =>\n Object.prototype.hasOwnProperty.call(data, m1) ? `${data[m1]}` : m0;\n return url.replace(/\\{([^}]*)\\}/g, replacer);\n }\n\n private _validateCustomAudioList(data: unknown): void {\n if (typeof data !== 'object' || data === null) {\n throw new Error('Invalid custom audio list response');\n }\n const obj = data as Record<string, unknown>;\n if (obj.type !== 'audioSourceList') {\n throw new Error('Invalid custom audio list type');\n }\n if (!Array.isArray(obj.audioSources)) {\n throw new Error('Invalid custom audio list audioSources');\n }\n }\n\n /**\n * Simple check for whether a string is entirely kana (hiragana/katakana).\n */\n private _isStringEntirelyKana(str: string): boolean {\n if (str.length === 0) {\n return false;\n }\n for (const char of str) {\n const code = char.codePointAt(0);\n if (code === undefined) {\n return false;\n }\n // Hiragana: U+3040-U+309F, Katakana: U+30A0-U+30FF\n if (!((code >= 0x3040 && code <= 0x309f) || (code >= 0x30a0 && code <= 0x30ff))) {\n return false;\n }\n }\n return true;\n }\n}\n\n/**\n * Returns the set of required audio source types for a given language ISO code.\n */\nexport function getRequiredAudioSourceList(language: string): Set<AudioSourceType> {\n return language === 'ja'\n ? new Set<AudioSourceType>(['jpod101', 'language-pod-101', 'jisho'])\n : new Set<AudioSourceType>(['lingua-libre', 'language-pod-101', 'wiktionary']);\n}\n\n/**\n * Returns required audio sources that are not already present in the given sources list.\n */\nexport function getRequiredAudioSources(language: string, sources: AudioSourceInfo[]): AudioSourceInfo[] {\n const requiredSources = getRequiredAudioSourceList(language);\n\n for (const { type } of sources) {\n requiredSources.delete(type);\n }\n\n return [...requiredSources].map((type): AudioSourceInfo => ({ type, url: '', voice: '' }));\n}\n"],"mappings":"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"}
|
package/dist/audio.cjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
require('./json-DKWp-B7Y.cjs');
|
|
2
|
+
const require_audio_url_generator = require('./audio-url-generator-Dy2hb2Mm.cjs');
|
|
3
|
+
|
|
4
|
+
exports.AudioUrlGenerator = require_audio_url_generator.AudioUrlGenerator
|
|
5
|
+
exports.NativeSimpleDOMParser = require_audio_url_generator.NativeSimpleDOMParser
|
|
6
|
+
exports.getRequiredAudioSourceList = require_audio_url_generator.getRequiredAudioSourceList
|
|
7
|
+
exports.getRequiredAudioSources = require_audio_url_generator.getRequiredAudioSources
|
package/dist/audio.d.cts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { AudioInfo, AudioSourceInfo, AudioSourceType } from "./audio-D9DvYyB7.cjs";
|
|
2
|
+
import { LanguageSummary } from "./language-xAbQxgXc.cjs";
|
|
3
|
+
|
|
4
|
+
//#region src/audio/audio-url-generator.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Interface for parsing HTML content. Consumers can inject
|
|
7
|
+
* a DOM parser implementation (e.g. linkedom, jsdom, or the browser's native DOMParser).
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Interface for parsing HTML content. Consumers can inject
|
|
11
|
+
* a DOM parser implementation (e.g. linkedom, jsdom, or the browser's native DOMParser).
|
|
12
|
+
*/
|
|
13
|
+
interface SimpleDOMParser {
|
|
14
|
+
getElementById(id: string): Element | null;
|
|
15
|
+
getElementsByClassName(className: string, root?: Element): Element[];
|
|
16
|
+
getElementByTagName(tagName: string, root?: Element): Element | null;
|
|
17
|
+
getAttribute(element: Element, attribute: string): string | null;
|
|
18
|
+
getTextContent(element: Element): string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A default DOM parser implementation that uses the standard DOMParser API
|
|
22
|
+
* (available in browsers and some Node.js DOM libraries like linkedom/jsdom).
|
|
23
|
+
*/
|
|
24
|
+
declare class NativeSimpleDOMParser implements SimpleDOMParser {
|
|
25
|
+
private _document;
|
|
26
|
+
constructor(content: string);
|
|
27
|
+
getElementById(id: string): Element | null;
|
|
28
|
+
getElementsByClassName(className: string, root?: Element): Element[];
|
|
29
|
+
getElementByTagName(tagName: string, root?: Element): Element | null;
|
|
30
|
+
getAttribute(element: Element, attribute: string): string | null;
|
|
31
|
+
getTextContent(element: Element): string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generates audio URLs for dictionary terms from various audio sources.
|
|
35
|
+
* Ported from Yomitan's AudioDownloader, but only extracts URL generation logic (not download).
|
|
36
|
+
*/
|
|
37
|
+
declare class AudioUrlGenerator {
|
|
38
|
+
private _getInfoHandlers;
|
|
39
|
+
private _regionNames;
|
|
40
|
+
private _domParserFactory;
|
|
41
|
+
private _customAudioListSchema;
|
|
42
|
+
constructor(options?: {
|
|
43
|
+
domParserFactory?: (content: string) => SimpleDOMParser;
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* Gets audio URL info for a single source.
|
|
47
|
+
*/
|
|
48
|
+
getTermAudioInfoList(source: AudioSourceInfo, term: string, reading: string, languageSummary: LanguageSummary): Promise<AudioInfo[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Gets audio URLs for a term from multiple sources.
|
|
51
|
+
*/
|
|
52
|
+
getUrls(term: string, reading: string, sources: AudioSourceInfo[], languageSummary: LanguageSummary): Promise<AudioInfo[]>;
|
|
53
|
+
private _normalizeUrl;
|
|
54
|
+
private _createSimpleDOMParser;
|
|
55
|
+
private _getInfoJpod101;
|
|
56
|
+
private _getInfoLanguagePod101;
|
|
57
|
+
private _validateLanguagePod101Row;
|
|
58
|
+
private _getLanguagePod101FetchUrl;
|
|
59
|
+
private _getLanguagePod101PodOrClass;
|
|
60
|
+
private _getInfoJisho;
|
|
61
|
+
private _getInfoLinguaLibre;
|
|
62
|
+
private _getInfoWiktionary;
|
|
63
|
+
private _getInfoWikimediaCommons;
|
|
64
|
+
private _getInfoTextToSpeech;
|
|
65
|
+
private _getInfoTextToSpeechReading;
|
|
66
|
+
private _getInfoCustom;
|
|
67
|
+
private _getInfoCustomJson;
|
|
68
|
+
private _getCustomUrl;
|
|
69
|
+
private _validateCustomAudioList;
|
|
70
|
+
/**
|
|
71
|
+
* Simple check for whether a string is entirely kana (hiragana/katakana).
|
|
72
|
+
*/
|
|
73
|
+
private _isStringEntirelyKana;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Returns the set of required audio source types for a given language ISO code.
|
|
77
|
+
*/
|
|
78
|
+
declare function getRequiredAudioSourceList(language: string): Set<AudioSourceType>;
|
|
79
|
+
/**
|
|
80
|
+
* Returns required audio sources that are not already present in the given sources list.
|
|
81
|
+
*/
|
|
82
|
+
declare function getRequiredAudioSources(language: string, sources: AudioSourceInfo[]): AudioSourceInfo[]; //#endregion
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=audio-url-generator.d.ts.map
|
|
85
|
+
export { AudioUrlGenerator, NativeSimpleDOMParser, SimpleDOMParser, getRequiredAudioSourceList, getRequiredAudioSources };
|
|
86
|
+
//# sourceMappingURL=audio.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio.d.cts","names":[],"sources":["../src/audio/audio-url-generator.ts"],"sourcesContent":null,"mappings":";;;;;;;;;;;;UAgBiB,eAAA;8BACe;EADf,sBAAe,CAAA,SAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EAEqB,OAFrB,CAAA,EAE+B,OAF/B,EAAA;EAAA,mBAAA,CAAA,OAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EAGgB,OAHhB,CAAA,EAG0B,OAH1B,GAAA,IAAA;EAAA,YACA,CAAA,OAAA,EAGN,OAHM,EAAA,SAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EAAO,cACc,CAAA,OAAA,EAGzB,OAHyB,CAAA,EAAA,MAAA;;;;;;AAGlB,cAOtB,qBAAA,YAAiC,eAPX,CAAA;;;8BAeH;mDAIqB,UAAU;EAZlD,mBAAA,CAAA,OAAsB,EAAA,MAAA,EAAA,IAAA,CAAA,EAiBa,OAjBb,CAAA,EAiBuB,OAjBvB,GAAA,IAAA;EAAA,YAAA,CAAA,OAAA,EAuBT,OAvBS,EAAA,SAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EAAA,cAQH,CAAA,OAAA,EAmBJ,OAnBI,CAAA,EAAA,MAAA;;;;;;AAmBJ,cAyBf,iBAAA,CAzBe;EAAO,QA3BW,gBAAA;EAAe,QAAA,YAAA;;;qBAoDhD;4CAOmC;EAPnC,CAAA;EAAiB;;;EA8BC,oBAGN,CAAA,MAAA,EAHT,eAGS,EAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,EAAA,eAAA,EAAA,eAAA,CAAA,EAClB,OADkB,CACV,SADU,EAAA,CAAA;EAAe;;;EAmBR,OACP,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,EAAA,OAAA,EADR,eACQ,EAAA,EAAA,eAAA,EAAA,eAAA,CAAA,EAClB,OADkB,CACV,SADU,EAAA,CAAA;EAAe,QACzB,aAAA;EAAS,QAAjB,sBAAA;EAAO,QAAA,eAAA;;;;EAwaE,QAAA,4BAA0B;EAAA,QAAA,aAAA;EAAA,QAAwB,mBAAA;EAAe,QAAnB,kBAAA;EAAG,QAAA,wBAAA;;;;EASjD,QAAA,kBAAuB;EAAA,QAAA,aAAA;EAAA,QAA4B,wBAAA;EAAe;AAAoB;;;;;;;iBATtF,0BAAA,oBAA8C,IAAI;;;;iBASlD,uBAAA,4BAAmD,oBAAoB"}
|
package/dist/audio.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { AudioInfo, AudioSourceInfo, AudioSourceType } from "./audio-DQulUkDM.js";
|
|
2
|
+
import { LanguageSummary } from "./language-KN_u-nTR.js";
|
|
3
|
+
|
|
4
|
+
//#region src/audio/audio-url-generator.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Interface for parsing HTML content. Consumers can inject
|
|
7
|
+
* a DOM parser implementation (e.g. linkedom, jsdom, or the browser's native DOMParser).
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Interface for parsing HTML content. Consumers can inject
|
|
11
|
+
* a DOM parser implementation (e.g. linkedom, jsdom, or the browser's native DOMParser).
|
|
12
|
+
*/
|
|
13
|
+
interface SimpleDOMParser {
|
|
14
|
+
getElementById(id: string): Element | null;
|
|
15
|
+
getElementsByClassName(className: string, root?: Element): Element[];
|
|
16
|
+
getElementByTagName(tagName: string, root?: Element): Element | null;
|
|
17
|
+
getAttribute(element: Element, attribute: string): string | null;
|
|
18
|
+
getTextContent(element: Element): string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A default DOM parser implementation that uses the standard DOMParser API
|
|
22
|
+
* (available in browsers and some Node.js DOM libraries like linkedom/jsdom).
|
|
23
|
+
*/
|
|
24
|
+
declare class NativeSimpleDOMParser implements SimpleDOMParser {
|
|
25
|
+
private _document;
|
|
26
|
+
constructor(content: string);
|
|
27
|
+
getElementById(id: string): Element | null;
|
|
28
|
+
getElementsByClassName(className: string, root?: Element): Element[];
|
|
29
|
+
getElementByTagName(tagName: string, root?: Element): Element | null;
|
|
30
|
+
getAttribute(element: Element, attribute: string): string | null;
|
|
31
|
+
getTextContent(element: Element): string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generates audio URLs for dictionary terms from various audio sources.
|
|
35
|
+
* Ported from Yomitan's AudioDownloader, but only extracts URL generation logic (not download).
|
|
36
|
+
*/
|
|
37
|
+
declare class AudioUrlGenerator {
|
|
38
|
+
private _getInfoHandlers;
|
|
39
|
+
private _regionNames;
|
|
40
|
+
private _domParserFactory;
|
|
41
|
+
private _customAudioListSchema;
|
|
42
|
+
constructor(options?: {
|
|
43
|
+
domParserFactory?: (content: string) => SimpleDOMParser;
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* Gets audio URL info for a single source.
|
|
47
|
+
*/
|
|
48
|
+
getTermAudioInfoList(source: AudioSourceInfo, term: string, reading: string, languageSummary: LanguageSummary): Promise<AudioInfo[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Gets audio URLs for a term from multiple sources.
|
|
51
|
+
*/
|
|
52
|
+
getUrls(term: string, reading: string, sources: AudioSourceInfo[], languageSummary: LanguageSummary): Promise<AudioInfo[]>;
|
|
53
|
+
private _normalizeUrl;
|
|
54
|
+
private _createSimpleDOMParser;
|
|
55
|
+
private _getInfoJpod101;
|
|
56
|
+
private _getInfoLanguagePod101;
|
|
57
|
+
private _validateLanguagePod101Row;
|
|
58
|
+
private _getLanguagePod101FetchUrl;
|
|
59
|
+
private _getLanguagePod101PodOrClass;
|
|
60
|
+
private _getInfoJisho;
|
|
61
|
+
private _getInfoLinguaLibre;
|
|
62
|
+
private _getInfoWiktionary;
|
|
63
|
+
private _getInfoWikimediaCommons;
|
|
64
|
+
private _getInfoTextToSpeech;
|
|
65
|
+
private _getInfoTextToSpeechReading;
|
|
66
|
+
private _getInfoCustom;
|
|
67
|
+
private _getInfoCustomJson;
|
|
68
|
+
private _getCustomUrl;
|
|
69
|
+
private _validateCustomAudioList;
|
|
70
|
+
/**
|
|
71
|
+
* Simple check for whether a string is entirely kana (hiragana/katakana).
|
|
72
|
+
*/
|
|
73
|
+
private _isStringEntirelyKana;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Returns the set of required audio source types for a given language ISO code.
|
|
77
|
+
*/
|
|
78
|
+
declare function getRequiredAudioSourceList(language: string): Set<AudioSourceType>;
|
|
79
|
+
/**
|
|
80
|
+
* Returns required audio sources that are not already present in the given sources list.
|
|
81
|
+
*/
|
|
82
|
+
declare function getRequiredAudioSources(language: string, sources: AudioSourceInfo[]): AudioSourceInfo[]; //#endregion
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=audio-url-generator.d.ts.map
|
|
85
|
+
export { AudioUrlGenerator, NativeSimpleDOMParser, SimpleDOMParser, getRequiredAudioSourceList, getRequiredAudioSources };
|
|
86
|
+
//# sourceMappingURL=audio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio.d.ts","names":[],"sources":["../src/audio/audio-url-generator.ts"],"sourcesContent":null,"mappings":";;;;;;;;;;;;UAgBiB,eAAA;8BACe;EADf,sBAAe,CAAA,SAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EAEqB,OAFrB,CAAA,EAE+B,OAF/B,EAAA;EAAA,mBAAA,CAAA,OAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EAGgB,OAHhB,CAAA,EAG0B,OAH1B,GAAA,IAAA;EAAA,YACA,CAAA,OAAA,EAGN,OAHM,EAAA,SAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EAAO,cACc,CAAA,OAAA,EAGzB,OAHyB,CAAA,EAAA,MAAA;;;;;;AAGlB,cAOtB,qBAAA,YAAiC,eAPX,CAAA;;;8BAeH;mDAIqB,UAAU;EAZlD,mBAAA,CAAA,OAAsB,EAAA,MAAA,EAAA,IAAA,CAAA,EAiBa,OAjBb,CAAA,EAiBuB,OAjBvB,GAAA,IAAA;EAAA,YAAA,CAAA,OAAA,EAuBT,OAvBS,EAAA,SAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EAAA,cAQH,CAAA,OAAA,EAmBJ,OAnBI,CAAA,EAAA,MAAA;;;;;;AAmBJ,cAyBf,iBAAA,CAzBe;EAAO,QA3BW,gBAAA;EAAe,QAAA,YAAA;;;qBAoDhD;4CAOmC;EAPnC,CAAA;EAAiB;;;EA8BC,oBAGN,CAAA,MAAA,EAHT,eAGS,EAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,EAAA,eAAA,EAAA,eAAA,CAAA,EAClB,OADkB,CACV,SADU,EAAA,CAAA;EAAe;;;EAmBR,OACP,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,EAAA,OAAA,EADR,eACQ,EAAA,EAAA,eAAA,EAAA,eAAA,CAAA,EAClB,OADkB,CACV,SADU,EAAA,CAAA;EAAe,QACzB,aAAA;EAAS,QAAjB,sBAAA;EAAO,QAAA,eAAA;;;;EAwaE,QAAA,4BAA0B;EAAA,QAAA,aAAA;EAAA,QAAwB,mBAAA;EAAe,QAAnB,kBAAA;EAAG,QAAA,wBAAA;;;;EASjD,QAAA,kBAAuB;EAAA,QAAA,aAAA;EAAA,QAA4B,wBAAA;EAAe;AAAoB;;;;;;;iBATtF,0BAAA,oBAA8C,IAAI;;;;iBASlD,uBAAA,4BAAmD,oBAAoB"}
|
package/dist/audio.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import "./json-DGd-cunA.js";
|
|
2
|
+
import { AudioUrlGenerator, NativeSimpleDOMParser, getRequiredAudioSourceList, getRequiredAudioSources } from "./audio-url-generator-pFQAB5Nb.js";
|
|
3
|
+
|
|
4
|
+
export { AudioUrlGenerator, NativeSimpleDOMParser, getRequiredAudioSourceList, getRequiredAudioSources };
|