Fixed lingq's cropping of subtitle timestamps in Android and IOS

in Android and IOS, it is indeed cropped . lingq supports only one digit in milliseconds, and will round down to .200 if it is .290
I make all my own subtitles, and the timestamps and audio are very accurate. So if I import with lingq, I have problems with the audio being cropped.
So I wrote a plugin for sublime text4 for python 3.3.6. It rounds up the milliseconds of the end time of the audio, which solves the problem.

This script doesn’t work directly through python, if you want a pure Python script, it may need some modification, feel free to post it after modification.

import sublime
import sublime_plugin
import re
import os

class SrtFixCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        # Get the entire text content of the file
        content = self.view.substr(sublime.Region(0, self.view.size()))

        # Define the regex pattern for the timestamps
        pattern = r'(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})'

        def fix_timestamps(match):
            # Split the start and end timestamps
            start, end = match.groups()

            # Keep the start timestamp unchanged
            # Fix the end timestamp
            end = self.fix_end_time(end)

            # Return the modified timestamp using 'format' method
            return '{} --> {}'.format(start, end)

        # Replace the timestamps in the content
        new_content = re.sub(pattern, fix_timestamps, content)

        # Get the current file name and directory
        current_file = self.view.file_name()
        current_dir = os.path.dirname(current_file)

        # Create the new file name
        base_name, ext = os.path.splitext(os.path.basename(current_file))
        new_file_name = "{}-lingq{}".format(base_name, ext)
        new_file_path = os.path.join(current_dir, new_file_name)

        # Create a new view and insert the modified content
        new_view = self.view.window().new_file()
        new_view.insert(edit, 0, new_content)

        # Save the new view with the new file name
        new_view.retarget(new_file_path)
        sublime.status_message("File saved as {}".format(new_file_name))

    def fix_end_time(self, timestamp):
        # Extract hours, minutes, seconds, and milliseconds
        hours, minutes, seconds, milliseconds = map(int, re.split('[:,]', timestamp))

        # Increment the milliseconds and handle carry-over
        milliseconds = (milliseconds // 100 + 1) * 100

        if milliseconds >= 1000:
            milliseconds -= 1000
            seconds += 1

        if seconds >= 60:
            seconds -= 60
            minutes += 1

        if minutes >= 60:
            minutes -= 60
            hours += 1

        # Format the timestamp back with leading zeros
        return '{:02}:{:02}:{:02},{}'.format(hours, minutes, seconds, str(milliseconds).ljust(3, '0'))
1 Like

Cool script. Its an interesting edge case of the timestamp ending at .995-.999 to have to think about. What benefit is the sublime plugin offering here?

It’s also interesting the way that LingQ crops the ms instead of rounding it. In the data itself the timestamp ms are saved as a float but are just displayed that way for simplicity. You can test that yourself by pressing the generate timestamps button and having a look at the data returned from the API

1 Like

image

1 Like

I use the sublime plugin simply out of habit, a lot of my text processing scripts go into sublime.

1 Like

your method works well and is a simple solution to the problem. If you wanted to take it a step further for whatever reason then see below. Written in JS but have included some methods to run in python.

Update LingQ timestamp data to 3 decimals

/* 
This example uses Cookies for API access

You can do this externally in Python via API or CSRF token
CSRF EXAMPLE
#AUTH
HEADERS = {
    "Content-Type": "application/json",
}

COOKIES = {
   'fs.session.id': '',
   'wwwlingqcomsa': '',
   "csrftoken": "", // get your csrftoken from website cookies
   # add more cookies if needed
}

API EXAMPLE (Bamboozled)

Get your Key Here -> https://www.lingq.com/accounts/apikey/

KEY = "12345"

base_url = "https://www.lingq.com"
headers = {
    "accept": "application/json",
    "content-type": "application/json",
    "Authorization": f"Token {KEY}",
}


*/

const getCookieValue = (name) => {
    const cookies = document.cookie.split(';');
    for (let cookie of cookies) {
        const [key, value] = cookie.trim().split('=');
        if (key === name) return value;
    }
    return null;
};

const getCookies = () => {
    return {
        'fs.session.id': getCookieValue('fs.session.id'),
        'wwwlingqcomsa': getCookieValue('wwwlingqcomsa')
    };
};

const cookies = getCookies();

const headers = {
    'Content-Type': 'application/json',
    'Cookie': `fs.session.id=${cookies['fs.session.id']}; wwwlingqcomsa=${cookies['wwwlingqcomsa']}`,
    'Accept': 'application/json'
};


async function updateTimestamp(language, lessonId, start, end, index) {
    const timestampData = {
        timestamp: [start, end], //as many decimals as you want
        lone: false,
        index: index,
        action: "update"
    };
    const response = await fetch(`https://www.lingq.com/api/v3/${language}/lessons/${lessonId}/sentences/`, {
    method: 'POST',
    headers: {
        ...headers,
    },
    body: JSON.stringify(timestampData)
    });

    if (!response.ok) {
        throw new Error('Network response was not ok');
    }

    const data = await response.json();
    console.log(data);
    console.log(`Timestamp ${index} updated successfully!`);
}



//Update 1 timestamp
updateTimestamp('fi', 23592537, 0.000, 2.114, 1);

returnedData

2 Likes

Update, I remembered. On the web side, although it shows one digit, it’s actually three digits. However, Iin Android and OS, it is indeed cropped. That’s the reason I did this before, I think. I just tested it and it does.

By the way, another issue is that when the srt and mp3 are opened immediately after just importing the course, the audio and timestamps are misaligned, and it seems to wait a minute or two for it to be right after there is an import success in the upper right corner. The page prompt for import success doesn’t seem to handle the timestamps.

1 Like