diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..1320b9a --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env"] +} diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 03e137e..24c8715 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -2,6 +2,7 @@ module.exports = { env: { es2021: true, node: true, + jest: true, }, extends: ["eslint:recommended", "prettier"], plugins: ["prettier"], diff --git a/__tests__/utils/utils.test.js b/__tests__/utils/utils.test.js new file mode 100644 index 0000000..cc4eab5 --- /dev/null +++ b/__tests__/utils/utils.test.js @@ -0,0 +1,20 @@ +import { jsonToSrt } from "../../src/utils/utils.js"; + +describe("utils", () => { + it("conver YandexSubtitles json to srt", () => { + const jsonYS = [ + { text: "Привет", startMs: 2222.0, durationMs: 3610.0 }, + { text: "мир", startMs: 26050.0, durationMs: 970.0 }, + ]; + + const expectedSRT = `1 +00:00:02,222 --> 00:00:05,832 +Привет + +2 +00:00:26,050 --> 00:00:27,020 +мир`; + + expect(jsonToSrt(jsonYS)).toBe(expectedSRT); + }); +}); diff --git a/package.json b/package.json index 453253c..880e629 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "lint": "npx eslint .", "lint-fix": "npx eslint . --fix", "format": "prettier --write --ignore-unknown \"src/**/*.{js,ts,json}\"", - "prepare": "husky" + "prepare": "husky", + "test": "jest" }, "repository": { "type": "git", @@ -44,10 +45,14 @@ "uuid": "^10.0.0" }, "devDependencies": { + "@babel/core": "^7.24.9", + "@babel/preset-env": "^7.24.8", + "babel-jest": "^29.7.0", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "husky": "^9.0.11", + "jest": "^29.7.0", "prettier": "^3.3.2" } } diff --git a/src/download.js b/src/download.js index cede1a2..ec6e190 100644 --- a/src/download.js +++ b/src/download.js @@ -1,5 +1,7 @@ import fs from "fs"; +import { Writable } from "stream"; import axios from "axios"; +import { jsonToSrt } from "./utils/utils.js"; function calcPercents(current, max) { return ((current / max) * 100).toFixed(1); @@ -9,7 +11,7 @@ export default async function downloadFile(url, outputPath, subtask, videoId) { if (!url) { throw new Error("Invalid download link"); } - + const IS_NEED_CONVERT = outputPath.endsWith(".srt"); const writer = fs.createWriteStream(outputPath); const { data, headers } = await axios({ method: "get", @@ -31,7 +33,23 @@ export default async function downloadFile(url, outputPath, subtask, videoId) { // console.log(calcPercents(downloadedLength, totalLength)) }); - data.pipe(writer); + if (IS_NEED_CONVERT) { + let dataBuffer = ""; + const writableStream = new Writable({ + write(chunk, encoding, callback) { + dataBuffer += chunk.toString(); + callback(); + }, + }); + data.pipe(writableStream); + data.on("end", () => { + const jsonData = JSON.parse(dataBuffer); + writer.write(jsonToSrt(jsonData["subtitles"])); + writer.end(); + }); + } else { + data.pipe(writer); + } return new Promise((resolve, reject) => { writer.on("finish", resolve); diff --git a/src/index.js b/src/index.js index 3cabcaa..e963619 100644 --- a/src/index.js +++ b/src/index.js @@ -49,7 +49,9 @@ const argv = parseArgs(process.argv.slice(2)); const ARG_LINKS = argv._; const OUTPUT_DIR = argv.output; const OUTPUT_FILE = argv["output-file"]; -const IS_SUBS_REQ = argv.subs || argv.subtitles; +const IS_SUBS_FORMAT_SRT = argv["subs-srt"] || argv["subtitles-srt"]; +const RESPONSE_SUBTITLES_FORMAT = IS_SUBS_FORMAT_SRT ? "srt" : "json"; +const IS_SUBS_REQ = argv.subs || argv.subtitles || IS_SUBS_FORMAT_SRT; const ARG_HELP = argv.help || argv.h; const ARG_VERSION = argv.version || argv.v; const PROXY_STRING = argv.proxy; @@ -398,12 +400,12 @@ async function main() { const taskSubTitle = `(ID: ${videoId})`; const filename = OUTPUT_FILE - ? OUTPUT_FILE.endsWith(".json") + ? OUTPUT_FILE.endsWith(`.${RESPONSE_SUBTITLES_FORMAT}`) ? OUTPUT_FILE - : `${OUTPUT_FILE}.json` + : `${OUTPUT_FILE}.${RESPONSE_SUBTITLES_FORMAT}` : `${subOnReqLang.language}---${clearFileName( videoId, - )}---${uuidv4()}.json`; + )}---${uuidv4()}.${RESPONSE_SUBTITLES_FORMAT}`; await downloadFile( subOnReqLang.url, `${OUTPUT_DIR}/${filename}`, diff --git a/src/utils/utils.js b/src/utils/utils.js index b18e25a..5ef71da 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -2,4 +2,25 @@ function clearFileName(name) { return name.replace(/[^\w.-]/g, ""); } -export { clearFileName }; +function convertToSrtTimeFormat(ms) { + const date = new Date(ms); + const hours = String(date.getUTCHours()).padStart(2, "0"); + const minutes = String(date.getUTCMinutes()).padStart(2, "0"); + const seconds = String(date.getUTCSeconds()).padStart(2, "0"); + const milliseconds = String(date.getUTCMilliseconds()).padStart(3, "0"); + return `${hours}:${minutes}:${seconds},${milliseconds}`; +} + +function jsonToSrt(subtitles) { + return subtitles + .map((s, index) => { + const { text, startMs, durationMs } = s; + const startTime = convertToSrtTimeFormat(startMs); + const endTime = convertToSrtTimeFormat(startMs + durationMs); + return `${index + 1}\n${startTime} --> ${endTime}\n${text}\n`; + }) + .join("\n") + .trim(); +} + +export { clearFileName, jsonToSrt };