Skip to content

Commit ba04e45

Browse files
authored
히라가나가 노래방 모드에서 제대로 한자를 인식하지 못하던 문제 수정
1 parent 468e7b7 commit ba04e45

File tree

6 files changed

+149
-68
lines changed

6 files changed

+149
-68
lines changed

FuriganaConverter.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,12 @@ const FuriganaConverter = (() => {
111111

112112
for (const token of tokens) {
113113
const surface = token.surface_form; // 表層形 (actual text)
114-
const reading = token.reading; // 読み (reading in katakana)
114+
const reading = token.reading || token.pronunciation; // 読み (reading in katakana), fallback to pronunciation
115+
116+
// Debug logging for tokens without reading
117+
if (containsKanji(surface) && !reading) {
118+
console.log('[FuriganaConverter] ⚠️ No reading for kanji:', surface, 'token:', token);
119+
}
115120

116121
// If token has kanji and reading is available
117122
if (reading && containsKanji(surface)) {

Pages.js

Lines changed: 108 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ const KaraokeLine = react.memo(({ line, position, isActive, globalCharOffset = 0
265265
const processedText = Utils.applyFuriganaIfEnabled(rawLineText);
266266
const hasFurigana = processedText !== rawLineText && processedText.includes('<ruby>');
267267

268+
console.log('[Karaoke Line Debug] rawLineText:', rawLineText);
269+
console.log('[Karaoke Line Debug] processedText:', processedText);
270+
console.log('[Karaoke Line Debug] hasFurigana:', hasFurigana);
271+
268272
// 전체 글자 정보를 먼저 수집
269273
const allChars = [];
270274
line.syllables.forEach((syllable, syllableIndex) => {
@@ -364,6 +368,9 @@ const KaraokeLine = react.memo(({ line, position, isActive, globalCharOffset = 0
364368
// Build text from charRenderData to ensure exact matching
365369
const actualText = charRenderData.map(c => c.char).join('');
366370

371+
console.log('[Karaoke Line Debug] actualText from charRenderData:', actualText);
372+
console.log('[Karaoke Line Debug] charRenderData length:', charRenderData.length);
373+
367374
// Reset regex state
368375
whitespacePattern.lastIndex = 0;
369376
while ((match = whitespacePattern.exec(actualText)) !== null) {
@@ -383,9 +390,69 @@ const KaraokeLine = react.memo(({ line, position, isActive, globalCharOffset = 0
383390
const totalTokenChars = tokens.reduce((sum, token) => sum + Array.from(token.value).length, 0);
384391
const actualCharCount = charRenderData.length;
385392

393+
// Parse furigana HTML to extract readings for each kanji with position tracking
394+
// Do this BEFORE word grouping so it's available for both paths
395+
const furiganaMap = new Map(); // position -> reading
396+
if (hasFurigana) {
397+
const rubyRegex = /<ruby>([^<]+)<rt>([^<]+)<\/rt><\/ruby>/g;
398+
399+
// Build clean text from processedText (removing all HTML tags)
400+
const cleanText = processedText.replace(/<ruby>([^<]+)<rt>[^<]+<\/rt><\/ruby>/g, '$1');
401+
402+
// Now parse the HTML and map positions
403+
let currentPos = 0;
404+
let lastMatchEnd = 0;
405+
let match;
406+
407+
rubyRegex.lastIndex = 0;
408+
409+
while ((match = rubyRegex.exec(processedText)) !== null) {
410+
const kanjiSequence = match[1];
411+
const reading = match[2];
412+
413+
// Calculate position by counting plain text before this match
414+
const beforeMatch = processedText.substring(lastMatchEnd, match.index);
415+
const plainTextBefore = beforeMatch.replace(/<[^>]+>/g, '');
416+
currentPos += plainTextBefore.length;
417+
418+
// Map each kanji to its reading
419+
if (kanjiSequence.length === 1) {
420+
furiganaMap.set(currentPos, reading);
421+
} else {
422+
// Multiple kanji - split the reading
423+
const kanjiChars = Array.from(kanjiSequence);
424+
const readingChars = Array.from(reading);
425+
const charsPerKanji = Math.floor(readingChars.length / kanjiChars.length);
426+
427+
kanjiChars.forEach((kanji, idx) => {
428+
let kanjiReading;
429+
if (idx === kanjiChars.length - 1) {
430+
// Last kanji gets all remaining reading
431+
kanjiReading = readingChars.slice(idx * charsPerKanji).join('');
432+
} else {
433+
kanjiReading = readingChars.slice(idx * charsPerKanji, (idx + 1) * charsPerKanji).join('');
434+
}
435+
furiganaMap.set(currentPos + idx, kanjiReading);
436+
});
437+
}
438+
439+
// Move position forward by the number of kanji
440+
currentPos += kanjiSequence.length;
441+
lastMatchEnd = match.index + match[0].length;
442+
}
443+
444+
console.log('[Karaoke Furigana Debug] furiganaMap:', Array.from(furiganaMap.entries()));
445+
console.log('[Karaoke Furigana Debug] actualText:', actualText);
446+
console.log('[Karaoke Furigana Debug] cleanText:', cleanText);
447+
}
448+
386449
// Word grouping works if we have spaces and total chars match
387450
let useWordGrouping = hasWhitespaceToken && totalTokenChars === actualCharCount;
388451

452+
console.log('[Karaoke Word Grouping] hasWhitespaceToken:', hasWhitespaceToken);
453+
console.log('[Karaoke Word Grouping] totalTokenChars:', totalTokenChars, 'actualCharCount:', actualCharCount);
454+
console.log('[Karaoke Word Grouping] useWordGrouping:', useWordGrouping);
455+
389456
if (useWordGrouping) {
390457
let charCursor = 0;
391458
let mappingFailed = false;
@@ -404,17 +471,41 @@ const KaraokeLine = react.memo(({ line, position, isActive, globalCharOffset = 0
404471
return;
405472
}
406473

407-
const wordChildren = wordCharData.map(charData =>
408-
react.createElement(
409-
"span",
410-
{
411-
key: charData.key,
412-
className: charData.className,
413-
style: charData.style
414-
},
415-
charData.char
416-
)
417-
);
474+
// Apply furigana to each character in the word
475+
const wordChildren = wordCharData.map((charData, localIdx) => {
476+
const globalIdx = charCursor + localIdx;
477+
const char = charData.char;
478+
const reading = furiganaMap.get(globalIdx);
479+
480+
if (reading) {
481+
// Has furigana
482+
return react.createElement(
483+
"span",
484+
{
485+
key: charData.key,
486+
className: charData.className,
487+
style: charData.style
488+
},
489+
react.createElement(
490+
"ruby",
491+
null,
492+
char,
493+
react.createElement("rt", null, reading)
494+
)
495+
);
496+
} else {
497+
// No furigana
498+
return react.createElement(
499+
"span",
500+
{
501+
key: charData.key,
502+
className: charData.className,
503+
style: charData.style
504+
},
505+
char
506+
);
507+
}
508+
});
418509

419510
elements.push(
420511
react.createElement(
@@ -472,42 +563,14 @@ const KaraokeLine = react.memo(({ line, position, isActive, globalCharOffset = 0
472563
}
473564

474565
if (!useWordGrouping) {
475-
// Parse furigana HTML to extract readings for each kanji
476-
const furiganaMap = new Map();
477-
if (hasFurigana) {
478-
// Extract kanji and their readings from processedText
479-
const rubyRegex = /<ruby>([^<]+)<rt>([^<]+)<\/rt><\/ruby>/g;
480-
let match;
481-
while ((match = rubyRegex.exec(processedText)) !== null) {
482-
const kanjiSequence = match[1];
483-
const reading = match[2];
484-
485-
// If it's a single character, just map it directly
486-
if (kanjiSequence.length === 1) {
487-
furiganaMap.set(kanjiSequence, reading);
488-
} else {
489-
// Multiple kanji - split the reading evenly
490-
const kanjiChars = Array.from(kanjiSequence);
491-
const readingChars = Array.from(reading);
492-
const charsPerKanji = Math.floor(readingChars.length / kanjiChars.length);
493-
494-
kanjiChars.forEach((kanji, idx) => {
495-
if (idx === kanjiChars.length - 1) {
496-
// Last kanji gets all remaining reading
497-
const kanjiReading = readingChars.slice(idx * charsPerKanji).join('');
498-
furiganaMap.set(kanji, kanjiReading);
499-
} else {
500-
const kanjiReading = readingChars.slice(idx * charsPerKanji, (idx + 1) * charsPerKanji).join('');
501-
furiganaMap.set(kanji, kanjiReading);
502-
}
503-
});
504-
}
505-
}
506-
}
507-
566+
// Furigana map is already built above, just use it
508567
charRenderData.forEach((charData, index) => {
509568
const char = charData.char;
510-
const reading = furiganaMap.get(char);
569+
const reading = furiganaMap.get(index); // Use position index instead of character
570+
571+
if (index < 10) { // Log first 10 chars for debugging
572+
console.log(`[Karaoke Furigana] Char[${index}]: '${char}', reading: '${reading}'`);
573+
}
511574

512575
// If this character has a furigana reading, wrap in ruby tag
513576
if (reading) {

Utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ const Utils = {
581581
/**
582582
* Current version of the lyrics-plus app
583583
*/
584-
currentVersion: "1.1.9",
584+
currentVersion: "1.2.0",
585585

586586
/**
587587
* Check for updates from remote repository

index.js

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ const FuriganaConverter = (() => {
88
let isInitializing = false;
99
let initPromise = null;
1010
const conversionCache = new Map();
11+
12+
// Debug mode - set to false to reduce console logs
13+
const DEBUG_MODE = false;
1114

1215
// Patch XMLHttpRequest to fix URL issues
1316
const originalXHROpen = XMLHttpRequest.prototype.open;
@@ -17,28 +20,28 @@ const FuriganaConverter = (() => {
1720
// If URL doesn't start with http/https, fix it
1821
if (!url.startsWith('http://') && !url.startsWith('https://')) {
1922
url = 'https://unpkg.com/[email protected]/dict/' + url.split('/dict/').pop();
20-
console.log('[FuriganaConverter] Fixed URL to:', url);
23+
if (DEBUG_MODE) console.log('[FuriganaConverter] Fixed URL to:', url);
2124
}
2225
// If URL has wrong host, fix it
2326
else if (url.includes('xpui.app.spotify.com')) {
2427
const filename = url.split('/dict/').pop();
2528
url = 'https://unpkg.com/[email protected]/dict/' + filename;
26-
console.log('[FuriganaConverter] Fixed wrong host URL to:', url);
29+
if (DEBUG_MODE) console.log('[FuriganaConverter] Fixed wrong host URL to:', url);
2730
}
2831
}
2932
return originalXHROpen.call(this, method, url, ...args);
3033
};
3134

3235
const init = async () => {
33-
console.log('[FuriganaConverter] init() called');
36+
if (DEBUG_MODE) console.log('[FuriganaConverter] init() called');
3437

3538
if (kuromojiInstance) {
36-
console.log('[FuriganaConverter] ✓ Already initialized');
39+
if (DEBUG_MODE) console.log('[FuriganaConverter] ✓ Already initialized');
3740
return Promise.resolve();
3841
}
3942

4043
if (isInitializing) {
41-
console.log('[FuriganaConverter] ⏳ Already initializing, waiting...');
44+
if (DEBUG_MODE) console.log('[FuriganaConverter] ⏳ Already initializing, waiting...');
4245
return initPromise;
4346
}
4447

@@ -51,11 +54,11 @@ const FuriganaConverter = (() => {
5154
return;
5255
}
5356

54-
console.log('[FuriganaConverter] ✓ Kuromoji library found, building...');
57+
if (DEBUG_MODE) console.log('[FuriganaConverter] ✓ Kuromoji library found, building...');
5558

5659
// Use any path - our XHR patch will fix it
5760
const dicPath = '/dict';
58-
console.log('[FuriganaConverter] Using dictionary path:', dicPath);
61+
if (DEBUG_MODE) console.log('[FuriganaConverter] Using dictionary path:', dicPath);
5962

6063
window.kuromoji.builder({
6164
dicPath: dicPath
@@ -101,41 +104,46 @@ const FuriganaConverter = (() => {
101104
};
102105

103106
const convertToFurigana = (text) => {
104-
console.log('[FuriganaConverter] convertToFurigana called');
105-
console.log('[FuriganaConverter] text sample:', text?.substring(0, 50));
107+
if (DEBUG_MODE) console.log('[FuriganaConverter] convertToFurigana called');
108+
if (DEBUG_MODE) console.log('[FuriganaConverter] text sample:', text?.substring(0, 50));
106109

107110
if (!text || typeof text !== 'string') {
108-
console.log('[FuriganaConverter] ❌ Invalid text type');
111+
if (DEBUG_MODE) console.log('[FuriganaConverter] ❌ Invalid text type');
109112
return text;
110113
}
111114

112115
if (!containsKanji(text)) {
113-
console.log('[FuriganaConverter] ℹ️ No kanji in text');
116+
if (DEBUG_MODE) console.log('[FuriganaConverter] ℹ️ No kanji in text');
114117
return text;
115118
}
116119

117-
console.log('[FuriganaConverter] ✓ Text contains kanji');
120+
if (DEBUG_MODE) console.log('[FuriganaConverter] ✓ Text contains kanji');
118121

119122
if (conversionCache.has(text)) {
120-
console.log('[FuriganaConverter] ✓ Found in cache');
123+
if (DEBUG_MODE) console.log('[FuriganaConverter] ✓ Found in cache');
121124
return conversionCache.get(text);
122125
}
123126

124127
if (!kuromojiInstance) {
125-
console.log('[FuriganaConverter] ❌ Kuromoji not initialized yet');
128+
if (DEBUG_MODE) console.log('[FuriganaConverter] ❌ Kuromoji not initialized yet');
126129
return text;
127130
}
128131

129-
console.log('[FuriganaConverter] ✓ Kuromoji is ready, tokenizing...');
132+
if (DEBUG_MODE) console.log('[FuriganaConverter] ✓ Kuromoji is ready, tokenizing...');
130133

131134
try {
132135
const tokens = kuromojiInstance.tokenize(text);
133-
console.log('[FuriganaConverter] ✓ Tokenized into', tokens.length, 'tokens');
136+
if (DEBUG_MODE) console.log('[FuriganaConverter] ✓ Tokenized into', tokens.length, 'tokens');
134137
let result = '';
135138

136139
for (const token of tokens) {
137140
const surface = token.surface_form;
138-
const reading = token.reading;
141+
const reading = token.reading || token.pronunciation; // Fallback to pronunciation
142+
143+
// Debug logging for tokens without reading - only warn once per unique kanji
144+
if (containsKanji(surface) && !reading) {
145+
console.warn('[FuriganaConverter] ⚠️ No reading for kanji:', surface);
146+
}
139147

140148
// Only add ruby if token has kanji AND reading
141149
if (reading && containsKanji(surface)) {

style.css

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -996,13 +996,18 @@ ruby rt {
996996
white-space: nowrap;
997997
}
998998

999-
/* Karaoke active/sung states for ruby elements */
999+
/* Ruby 내부의 개별 문자 스타일 */
1000+
.lyrics-karaoke-line ruby span {
1001+
display: inline;
1002+
}
1003+
1004+
/* Karaoke active/sung states for ruby elements and spans inside ruby */
10001005
.lyrics-karaoke-char.active,
1001-
ruby.active {
1006+
ruby .lyrics-karaoke-char.active {
10021007
color: var(--karaoke-active-color, currentColor);
10031008
}
10041009

10051010
.lyrics-karaoke-char.sung,
1006-
ruby.sung {
1011+
ruby .lyrics-karaoke-char.sung {
10071012
color: var(--karaoke-sung-color, currentColor);
10081013
}

version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.1.9
1+
1.2.0

0 commit comments

Comments
 (0)