Extension: Rooster Lesson Editor -> Easy LingQ Lesson editing program

ung-gat-ta-tai-oh at the end of every sentence… Not sure why they need so many syllables, maybe I’ll find out one day!

Short Answer: Honorifics, verb tenses, etc.

But yes, it’s a great language to dive into if you are ever interested, regardless, great to have the .txt patch text feature! :smiley: Thanks again!

I wanted it. I downloaded and installed it. I have it. I imported a lesson and clicked it. I saw it. I tried to use it. I’m lost. Help! You probably posted instructions somewhere in this thread…?

Sorry, my head is muddled after 2.5 hours of editing an imported YouTube video’s messed up captions. (All the captions just said: woo woo woo, wee wee, music, wee wee, etc…)

Hi @WillowMeDown! @roosterburton has great videos for the Rooster extensions linked on the main extension page: Web Browser Extensions (Software) for LingQ

These are ones I found particularly helpful for the Editor:

The directions for getting it to pop up are quoted below for your convenience:

More videos and visuals for the free version of the editor:

For the new .txt file feature, that is only in the Premium Version, the Patch 1.18 Video shows how to use that feature:

Edit: Note: Patch Video 1.15 shows how to use the Premium Editor Version without the .txt edit feature, but it still works the same way with the most recent patch.

1 Like

Above and beyond, thanks!

1 Like

ROOSTER LESSON EDITOR PREMIUM PATCH 1.19 - 1.31

for 1.31
→ Bug fix for loading of menu on reader page since 1.30

for 1.30
→ Bug fix for loading of menu on Chinese Traditional pages

for 1.29
→ Added Find and Replace feature
→ Bug fix for multiple X buttons next to Audio.

for 1.28
→ Now reliably opens the editing menu on Reader page. (instead of sometimes opening lesson editor page in new tab)

for 1.26/7
→ Added Line highlighting for current mouse selection


for 1.25
→ Added a Delete button (Which removes the Audio and Youtube Video from lesson)
image

for 1.24
→ Added complete LingQ Text to Speech voice map
→ Added support for 3 character languages (Serbian/Croatian)

Voice Map
const voiceMap = {
    'af':[
        {appName: "msspeak", voice: "af-ZA:Female"},
        {appName: "msspeak", voice: "af-ZA:Male"}
    ],
    'ar': [{appName: 'polly', voice: 'Zeina'}],
    'be': [{appName: 'msspeak', voice: 'be-BE:Female'}],
    'bg':[
        {appName: "msspeak", voice: "bg-BG:Female"},
        {appName: "msspeak", voice: "bg-BG:Male"}
    ],
    'ca':[
        {appName: 'msspeak', voice: 'ca-ES:Female'},
        {appName: 'msspeak', voice: 'ca-ES:Male'}
    ],
    'cs':[
        {appName: "gCloudTTS", voice: "cs-CZ:female"},
        {appName: "msspeak", voice: "cs-CZ:Male"}
    ],
    'da':[
        {appName: "polly", voice: "Sofie"},
        {appName: "gCloudTTS", voice: "da-DK:female"},
        {appName: "polly", voice: "Mads"}
    ],
    'de':[
        {appName: 'polly', voice: 'Marlene'},
        {appName: 'polly', voice: 'Vicki'},
        {appName: 'msspeak', voice: 'de-DE:Female'},
        {appName: 'polly', voice: 'Hans'}
    ],
    'el':[
        {appName: 'msspeak', voice: 'el-GR:Female'},
        {appName: 'gCloudTTS', voice: 'el-GR:female'}
    ],
    'en':[
        {appName: 'msspeak', voice: 'en-US:Female'},
        {appName: 'polly', voice: 'Nicole'},
        {appName: 'polly', voice: 'Brian'},
        {appName: 'polly', voice: 'Matthew'}
    ],
    'eo':[{appName: 'eSpeak', voice: 'eo'}],
    'es':[
        {appName: "polly", voice: "Lucia"},
        {appName: "polly", voice: "Conchita"},
        {appName: "polly", voice: "Mia"},
        {appName: "polly", voice: "Lupe"},
        {appName: "polly", voice: "Enrique"},
        {appName: "polly", voice: "Miguel"},
        {appName: "polly", voice: "Sergio"},
        {appName: "polly", voice: "Andres"},
        {appName: "polly", voice: "Pedro"}
    ],
    'fa': [
        { appName: "msspeak", voice: "fa-IR:Female" },
        { appName: "msspeak", voice: "fa-IR:Male" },
    ],
    'fi': [
        { appName: "msspeak", voice: "fi-FI:Female:SelmaNeural" },
        { appName: "gCloudTTS", voice: "fi-FI:female" },
        { appName: "msspeak", voice: "fi-FI:Female" }
    ],
    'fr':[
        {appName: 'polly', voice: 'Celine'},
        {appName: 'polly', voice: 'Lea'},
        {appName: 'polly', voice: 'Mathieu'},
        {appName: 'gCloudTTS', voice: 'fr-CA:female'}
    ],
    'gu': [
        { appName: "msspeak", voice: "gu-IN:Female" },
        { appName: "msspeak", voice: "gu-IN:Male" },
    ],
    'he':[
        {appName: "msspeak", voice: "he-IL:Male"},
        {appName: "msspeak", voice: "he-IL:Female"},
    ],
    'hk': [
        {appName: 'msspeak', voice: 'zh-HK:Female'},
        {appName: "msspeak", voice: "zh-HK:Male"}
    ],
    'hrv': [
        {appName: "msspeak", voice: "hr-HR:Female"},
        {appName: "msspeak", voice: "hr-HR:Male"}
    ],
    'hu': [
        {appName: "msspeak", voice: "hu-HU:Female"},
        {appName: "msspeak", voice: "hu-HU:Male"}
    ],
    'hy': [
        {appName: "msspeak", voice: "hy-AM:Female"},
        {appName: "msspeak", voice: "hy-AM:Male"}
    ],
    'id': [
        {appName: "gCloudTTS", voice: "id-ID:female"},
        {appName: "msspeak", voice: "id-ID:Male"}
    ],
    'is':[
        {appName: "polly", voice: "Dora"},
        {appName: "polly", voice: "Karl"},
    ],
    'it': [
        {appName: 'polly', voice: 'Carla'},
        {appName: 'polly', voice: 'Bianca'},
        {appName: 'msspeak', voice: 'it-IT:Female'},
        {appName: 'polly', voice: 'Giorgio'}
    ],
    'ja': [
        {appName: 'polly', voice: 'Takumi'},
        {appName: 'polly', voice: 'Mizuki'},
        {appName: 'gCloudTTS', voice: 'ja-JP:male'},
        {appName: 'gCloudTTS', voice: 'ja-JP:female'}
    ],
    'ka': [
        {appName: "msspeak", voice: "ka-GE:Female"},
        {appName: "msspeak", voice: "ka-GE:Male"}
    ],
    'ko': [
        {appName: 'polly', voice: 'Seoyeon'},
        {appName: 'msspeak', voice: 'ko-KR:Female'},
        {appName: "msspeak", voice: "ko-KR:Male"}
    ],
    'la':[{appName: 'gtts', voice: 'la'}],
    'mk':[{appName: "msspeak", voice: "mk-MK:Female"}],
    'ms': [
        {appName: "msspeak", voice: "ms-MY:Female"},
        {appName: "msspeak", voice: "ms-MY:Male"}
    ],
    'nl': [{appName: 'polly', voice: 'Lotte'}],
    'no':[
        {appName: "msspeak", voice: "nb-NO:Female"},
        {appName: "polly", voice: "Liv"},
        {appName: "msspeak", voice: "nb-NO:Male"}
    ],
    'pl':[
        {appName: "polly", voice: "Ewa"},
        {appName: "msspeak", voice: "pl-PL:Female"},
        {appName: "polly", voice: "Jacek"},
        {appName: "msspeak", voice: "pl-PL:Male"}
    ],
    'pt':[
        {appName: 'polly', voice: 'Vitoria'},
        {appName: 'polly', voice: 'Ricardo'},
        {appName: 'polly', voice: 'Cristiano'},
        {appName: 'polly', voice: 'Camila'}
    ],
    'ro': [
        {appName: 'polly', voice: 'Carmen'},
        {appName: "msspeak", voice: "ro-RO:Male"}
    ],
    'ru': [
        {appName: 'polly', voice: 'Tatyana'},
        {appName: 'polly', voice: 'Maxim'}
    ],
    'sk': [
        {appName: "gCloudTTS", voice: "sk-SK:female"},
        {appName: "msspeak", voice: "sk-SK:Male"}
    ],
    'sl':[
        {appName: 'msspeak', voice: 'sl-SI:Female'},
        {appName: 'msspeak', voice: 'sl-SI:Male'}
    ],
    'srp': [{appName: 'gCloudTTS', voice: 'sr-RS:female'}],
    'sv': [
        {appName: 'polly', voice: 'Astrid'},
        {appName: 'msspeak', voice: 'sv-SE:Female'}
    ],
    'sw': [
        {appName: "msspeak", voice: "sw-KE:Female"},
        {appName: "msspeak", voice: "sw-KE:Male"}
    ],
    'tl': [
        {appName: "msspeak", voice: "fil-PH:Female"},
        {appName: "msspeak", voice: "fil-PH:Male"}
    ],
    'tr': [
        {appName: "polly", voice: "Filiz"},
        {appName: "msspeak", voice: "tr-TR:Female"},
        {appName: "msspeak", voice: "tr-TR:Male"},
    ],
    'uk': [
        {appName: "gCloudTTS", voice: "uk-UA:female"},
        {appName: "msspeak", voice: "uk-UA:Female"},
        {appName: "msspeak", voice: "uk-UA:Male"},
    ],
    'zh': [
        {appName: 'polly', voice: 'Zhiyu'},
        {appName: 'msspeak', voice: 'zh-HK:Female'},
    ],
    'zh-t':[
        {appName: 'msspeak', voice: 'zh-CN:Female'},
        {appName: 'polly', voice: 'Zhiyu'}
    ]
    // ... add other languages here
};

for 1.23
→ Added more translation languages

for 1.22
→ Added Description editing popup
→ Added ‘Lesson Files’ button

for 1.21
→ Added Patch and Delete Audio Buttons
→ Changed Patch Text into a single button

for 1.20
→ Added Lesson Editor Button to Listen and Reader Modes (Auto Opens without ID)
→ Ability to open lesson editor from Library page. (Note: This should allow you to fix any lesson stuck in a death spiral timestamp)


1 Like

Hi again! Is there a way to remove a line break? I’ve imported a video from youtube, and it created too many linebreaks. Because of that some words that I want to save end up in a weird context (for instance, first word of the sentence is at the end the previous one), which messes up non-editable “source text” field. Alternatively, I’ve tried to remove the extra part from one sentence and copy it to another, but I can’t enter spaces - it toggles audio. I’ve tried to rebind “Toggle audio” with the hotkey extension, but with no avail. Or rather the new combination works, but space still toggles audio and I can’t type it in.

Hmm. If Lesson audio is being toggled with the lesson editor open it may be best to either… refresh the page, close the audio or open the extension on the lesson editor page. You can also add spaces with shift spacebar i believe.

You can also just toggle that paragraph line to a sentence and it will join them together

Lines 1/2 are joined in the translation. You can use that as a reference

Refreshing doesn’t work. No matter what I do, space toggles audio, with or without the opened editor. But Shift+space worked, thanks.

This makes it looks nicer, but the source text field is still affected. I care about it, since it is then used during the review of LingQs in tests and cannot be editted, as far as I know. So if I save the word where it is out of context, learning it with review will be a pain.

Btw, if I change something in the Editor (for instance this Sentence/Paragraph toggling), then close it, I can no longer open it again. Instead the native LingQ editor is opened in a new tab. Refreshing page helps, though.

Upd: Oops, sorry, refreshing does not help. Only re opening the lesson does.

Thank’s for letting me know. No workarounds at this stage. I’ll fix these in the next update.

Understandable. I’m guessing you’re referring to the fragment of text that is saved with the LingQed word / phrase… which would be hard to review in the SRS without sentence content.

AutoLingQ uses a larger fragment (13 words I think) but complete sentence context are harder to gather.

I’ll see what I can do about making those fields editable as well.

1 Like

That would be great!
For now it works for me just to edit the sentences myself with the Shift+Space trick (since I don’t need to do it for the entire lesson anyway and don’t care that much for the timing to be precise).

And I’ve noticed that the “space toggling audio” is not just about space and the audio. When I work in the editor, all the keys I press are actually affecting the lesson screen. So after playing around with cut-and-pasting, I’ve managed to set the status of some of the words to “Ignore”. But I don’t know if the problem is with the editor or if it is a more general LingQ bug (Firefox browser, the current (v 122.0b3) developer edition).

This seems like a really bad bug. I’ll make a patch that stop the key-binds interfering when lesson editor is open on the reader page. Thanks again for the report.

Hi @roosterburton

Would it be possible to add a search and replace feature in the Rooster Lesson Editor?

It would be very useful to edit spelling errors in transcribed lessons.

Thanks
Alan

1 Like

@alanelder

I just tried to see if Ctrl+H works but nope…! Its the history shortcut on my browser. I’ll see if there is a native option, otherwise will come up with something. Thanks for the suggestion

And does it work on Android?

@Akpivara

No android support right now. More browsers/devices coming when the software is finished.

@alanelder


image

This is a still a big L though…!

Patch within the hour

1 Like

Hi Rooster,
I just tried the lesson editor and while I normally don’t do much editing, it’s very useful for removing ‘paragraphs’ from imported subtitles and whisper transcripts. (No idea why those are automatically added by LingQ)
But unfortunately the process of ‘sentencing everything’ takes a long time. A typical Whisper transcript has only a couple of words per line, but lots of paragraphs (1000+). Not sure if this makes sense, but would it be possible to only make one request to the server that updates all sentences in a batch?
I’m on version 1.21 from the Chrome web store, using MS Edge.

Also, does the translation toggle use the new ChatGPT translator or Google translate?

@roosterburton

Thanks once again Rooster. An invaluable new feature.

I have used it a few times already and it works perfectly.

Being able to edit lessons on the fly (you just need to refresh after you have saved and closed the editor) is a leap forward for LingQ.

1 Like

Hi Bamboozled

Good to know.

No plans to change that to a flood request on release addon.

If you wanted to mess with it you could adjust that 500ms delay

or

The code on how it works
paragraphs.forEach((paragraph) => {
                paragraph.sentences.forEach((sentence, idx) => {
                    const sentenceContainer = document.createElement('div');
                    sentenceContainer.style.display = 'flex';

                    const sentenceDiv = document.createElement('div');
                    sentenceDiv.textContent = sentence.cleanText;
                    sentenceDiv.dataset.index = sentence.index;
                    sentenceContainer.appendChild(sentenceDiv);

                    if (!isFirstSentence) {
                        const isBroken = idx === 0; // Determine if the sentence is broken
                        sentence.isBroken = isBroken; // Set the value on the sentence object

                        const brokenButton = document.createElement('button');
                        brokenButton.style.webkitUserSelect = 'none'; // for Chrome and Safari
                        brokenButton.style.MozUserSelect = 'none'; // for Firefox
                        brokenButton.style.msUserSelect = 'none'; // for Internet Explorer
                        brokenButton.style.userSelect = 'none'; // standard
                        brokenButton.textContent = isBroken ? 'Paragraph' : 'Sentence'; // Use the local variable
                        brokenButton.style.marginLeft = '20px'; // Adjust the value as desired

                        brokenButton.addEventListener('click', async () => {
                            sentence.isBroken = !sentence.isBroken;
                            brokenButton.textContent = sentence.isBroken ? 'Sentence' : 'Paragraph';
                            let index;
                            let action;
                            let url;

                            if (!sentence.isBroken) { // If currently not broken, merge it
                                index = paragraph.index - 1; // Use paragraph index for merging
                                url = `https://www.lingq.com/api/v3/${language}/lessons/${lessonId}/paragraphs/`;
                                action = "merge";
                                sentence.isBroken = true; // Update the state
                            } else { // If currently merged, break it
                                index = sentence.index - 1; // Adjust the index
                                url = `https://www.lingq.com/api/v3/${language}/lessons/${lessonId}/sentences/`;
                                action = "break";
                                sentence.isBroken = false; // Update the state
                            }

                            // Data to send in the POST request
                            const data = {
                                index,
                                action,
                            };

                            // Perform the POST request
                            const response = await fetch(url, {
                                method: 'POST',
                                headers: {
                                    'Content-Type': 'application/json',
                                },
                                body: JSON.stringify(data),
                            });

                            // Check for successful response
                            if (response.ok) {
                                console.log('Success:', action, 'for index', index);
                                brokenButton.textContent = sentence.isBroken ? 'Sentence' : 'Paragraph';
                                if (headerButton.dataset.active === 'true') {
                                    headerButton.dataset.active = 'false';
                                    headerButton.textContent = 'Header';
                                }
                                // Initial creation of GUI box when the page loads
                                document.getElementById('saveButton').click();
                            } else {
                                console.error('Error:', response);
                            }
                        });

                        sentenceContainer.appendChild(brokenButton);

On the Video Tools addons you can import and overlay with GPT translation of whatever language you want. I’ll add that eventually to MasterLingQ

1 Like

Great feedback @alanelder Am glad the tools are helping and getting some use.