Skip to content

Commit

Permalink
Add option to convert timestamp format
Browse files Browse the repository at this point in the history
  • Loading branch information
pepri committed Jul 14, 2021
1 parent 4172c75 commit f878ab6
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 50 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to the "subtitles-editor" extension are documented in this f

Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.

## [1.1.6] - 2021-07-13

- Added option to format timestamp format.

## [1.1.5] - 2021-05-19

- Fix translation: Skip sequence and empty lines when translating.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ With **Subtitles: Renumber**, you can renumber the frames using the sequence wit

The command **Subtitles: Linear Correction** prompts you for two timestamp mappings. The input is in form "original timestamp -> new timestamp". Pick one point from the beginning of the movie and second one from the end of the movie to get the best approximation.

The command **Subtitles: Convert Time Format** converts timestamps in the file into one of the supported formats you can choose from (SRT, VTT, and SBV). Converting changes separator between start and end timestamp, separator for milliseconds, and number of digits used for milliseconds. You can also use this command to normalize the format of the timestamps if the file contains mixed timestamp formats.

Use command **Subtitles: Translate** to translate subtitles to another language using Google translation service.

With **Subtitles: Reorder**, you can reorder the frames based on their sequence number. This can be useful if you want to work with translated and original subtitles at the same time. You can first translate the subtitles (which will replace the original ones) and append the original subtitles at the end. Then, you can reorder them so you will have translated and original frames near each other.
Expand Down
94 changes: 46 additions & 48 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"icon": "res/icon.png",
"displayName": "Subtitles Editor",
"description": "Edit subtitles in SRT, WebVTT, and SBV in VS Code.",
"version": "1.1.5",
"version": "1.1.6",
"publisher": "pepri",
"license": "MIT",
"repository": "https://github.com/pepri/subtitles-editor.git",
Expand All @@ -18,6 +18,7 @@
"onCommand:subtitles.renumber",
"onCommand:subtitles.reorder",
"onCommand:subtitles.linearCorrection",
"onCommand:subtitles.convertTimeFormat",
"onCommand:subtitles.translate"
],
"main": "./out/extension",
Expand Down Expand Up @@ -68,6 +69,10 @@
"command": "subtitles.linearCorrection",
"title": "Subtitles: Linear Correction"
},
{
"command": "subtitles.convertTimeFormat",
"title": "Subtitles: Convert Time Format"
},
{
"command": "subtitles.translate",
"title": "Subtitles: Translate"
Expand Down Expand Up @@ -98,6 +103,6 @@
"typescript": "^4.2.4",
"@types/vscode": "^1.25.0",
"vscode-test": "^1.5.2",
"vsce": "^1.88.0"
"vsce": "^1.95.1"
}
}
78 changes: 78 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,83 @@ async function linearCorrection() {
return true;
}

interface SubtitleType {
name: string;
extensions: string[];
timeSeparator: string;
millisSeparator: string;
shortMillis: boolean;
}

async function convertTimeFormat() {
const textEditor = vscode.window.activeTextEditor;

if (typeof textEditor === 'undefined') {
return false;
}

const subtitleTypes: { [name: string]: SubtitleType } = {
['SubRip Text']: {
name: 'SubRip Text',
extensions: ['srt'],
timeSeparator: ' --> ',
millisSeparator: ',',
shortMillis: false,
},
['Web Video Text Tracks']: {
name: 'Web Video Text Tracks',
extensions: ['vtt'],
timeSeparator: ' --> ',
millisSeparator: '.',
shortMillis: false,
},
['SubViewer']: {
name: 'SubViewer',
extensions: ['sbv', 'sub'],
timeSeparator: ',',
millisSeparator: '.',
shortMillis: true,
},
};
const items = Object.values(subtitleTypes)
.map(({ name, extensions }) => ({ label: name, detail: extensions.join(', ') }));

const quickPickOpts: vscode.QuickPickOptions = {
placeHolder: 'Type',
matchOnDetail: true
};

const value = await vscode.window.showQuickPick(items, quickPickOpts);

if (typeof value === 'undefined') {
return false;
}

const subtitleType = subtitleTypes[value.label];
const workspaceEdit = new vscode.WorkspaceEdit();
const documentUri = textEditor.document.uri;
const selections = !textEditor.selection.isEmpty
? textEditor.selections
: [new vscode.Selection(textEditor.document.positionAt(0), textEditor.document.lineAt(textEditor.document.lineCount - 1).range.end)];

for (const selection of selections) {
for (let lineIndex = selection.start.line; lineIndex <= selection.end.line; ++lineIndex) {
const line = textEditor.document.lineAt(lineIndex);
if (!line.isEmptyOrWhitespace) {
const timeLine = TimeLine.parse(line.text);
if (timeLine) {
timeLine.convert(subtitleType.timeSeparator, subtitleType.millisSeparator, subtitleType.shortMillis);
workspaceEdit.replace(documentUri, line.range, timeLine.format());
}
}
}
}

await vscode.workspace.applyEdit(workspaceEdit);

return true;
}

async function translate() {
const textEditor = vscode.window.activeTextEditor;

Expand Down Expand Up @@ -359,6 +436,7 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.commands.registerCommand('subtitles.renumber', renumber));
context.subscriptions.push(vscode.commands.registerCommand('subtitles.reorder', reorder));
context.subscriptions.push(vscode.commands.registerCommand('subtitles.linearCorrection', linearCorrection));
context.subscriptions.push(vscode.commands.registerCommand('subtitles.convertTimeFormat', convertTimeFormat));
context.subscriptions.push(vscode.commands.registerCommand('subtitles.translate', translate));
}

Expand Down
5 changes: 5 additions & 0 deletions src/model/Time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ export class Time {
this.value = (this.value - original.startTime.value) * factor + updated.startTime.value;
}

convert(separator: string, shortMillis: boolean) {
this.separator = separator;
this.shortMillis = shortMillis;
}

normalize(): void {
this.separator = ',';
this.shortMillis = false;
Expand Down
6 changes: 6 additions & 0 deletions src/model/TimeLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ export class TimeLine {
this.endTime.applyLinearCorrection(original, updated);
}

convert(timeSeparator: string, millisSeparator: string, shortMillis: boolean) {
this.startTime.convert(millisSeparator, shortMillis);
this.endTime.convert(millisSeparator, shortMillis);
this.separator = timeSeparator;
}

format() {
return `${this.startTime.format()}${this.separator}${this.endTime.format()}${this.extraData}`;
}
Expand Down
20 changes: 20 additions & 0 deletions src/test/model/Time.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ function applyLinearCorrection(time: string, originalTimeLine: string, updatedTi
return value.format();
}

function convert(timeLine: string, timeSeparator: string, millisSeparator: string, shortMillis: boolean): string {
const value = TimeLine.parse(timeLine);
if (!value) {
return timeLine;
}
value.convert(timeSeparator, millisSeparator, shortMillis);
return value.format();
}

suite('Time Tests', function() {
test('format', function() {
assert.strictEqual(format('12:34:56,7891'), '12:34:56,789');
Expand Down Expand Up @@ -70,4 +79,15 @@ suite('Time Tests', function() {
test('linear correction', function() {
assert.strictEqual(applyLinearCorrection('01:00:00,000', '00:00:00,000-->02:00:00,000', '01:00:00,000-->05:00:00,000'), '03:00:00,000');
});

test('convert', function() {
assert.strictEqual(convert('01:23:45,678-->23:45:12,345', ' --> ', '.', false), '01:23:45.678 --> 23:45:12.345');
assert.strictEqual(convert('01:23:45,678-->23:45:12,345', ',', '.', true), '01:23:45.68,23:45:12.35');

assert.strictEqual(convert('11:23:45.678-->23:45:12.345', ' --> ', ',', false), '11:23:45,678 --> 23:45:12,345');
assert.strictEqual(convert('11:23:45.678-->23:45:12.345', ',', '.', true), '11:23:45.68,23:45:12.35');

assert.strictEqual(convert('21:23:45.68,23:45:12.35', ' --> ', ',', false), '21:23:45,680 --> 23:45:12,350');
assert.strictEqual(convert('21:23:45.68,23:45:12.35', ' --> ', '.', false), '21:23:45.680 --> 23:45:12.350');
});
});

0 comments on commit f878ab6

Please sign in to comment.