Со времени моего последнего сообщения в блоге я исправил несколько сохраняющихся ошибок и продолжил работу с ffmpeg. Приложение будет принимать пользовательский ввод для url вопросов stackoverflow и url видео на youtube и создавать рассказывающий тикток, подобный этому проекту. Сначала я кратко объясню некоторые из этих ошибок и как я их исправил, а затем объясню первый вызов функции, с которой все начинается.
Внесенные изменения и исправленные ошибки
Ошибки, которые я в итоге исправил, на самом деле были частью функции makeApiCall()
, о которой я расскажу позже в этом посте. У меня были проблемы с асинхронностью при получении данных из API stackoverflow. Я впервые делал запросы на выборку в Node, и забыл добавить await
перед выборкой данных. Это не всегда было проблемой, только иногда функции, которые полагались на ответ, выдавали ошибку. Поскольку обычно это происходило так редко, я подумал, что это проблема с API и количеством запросов. Я решил посмотреть на функцию поближе и заметил, что в обоих вызовах API отсутствует await
, и с тех пор ошибка не возникала.
Я отошел от этого проекта, чтобы немного поработать над своим портфолио. Я не достиг большого прогресса с момента моего последнего сообщения, но все же кое-что сделал. Функция, которая добавляет mp3-звук из API Googles text-to-speech к скриншотам, иногда работает, но у меня все еще есть несколько постоянных ошибок, которые я устраняю. Даже просто при написании этого сообщения и просмотре ошибок у меня появилось еще несколько идей по устранению проблем, и я надеюсь, что к следующему сообщению все будет работать немного лучше.
Файл api-call.js
api-call.js
— это первая функция, которая вызывается внутри startVideoEdit()
после того, как пользователь передал свои данные. Функция принимает url вопроса и несколько глобальных переменных, которые она использует для хранения частей ответа.
Файл импортирует три модуля
import fetch from "node-fetch";
import chalk from "chalk";
import { parseText } from "./parse-text.js";
Node Fetch довольно прост, и я уже объяснял, что такое Chalk и как он используется в предыдущих постах. Parse text get вызывается из этого файла для преобразования HTML-строк, возвращаемых из вызовов API, в обычный текст.
Первым шагом будет извлечение ID вопроса из url, переданного в функцию, так как это единственная часть url, которая нам действительно нужна.
const url = questionURL;
const questionId = url
.match(//(d+)+[/]?/g)
.map((id) => id.replace(///g, ""));
Затем мы можем интерполировать questionId
в два разных url, один для получения данных о вопросах, а другой для ответов.
const qURL = `https://api.stackexchange.com/2.3/questions/${questionId}?order=desc&sort=activity&site=stackoverflow&filter=withbody`;
const aURL = `https://api.stackexchange.com/2.3/questions/${questionId}/answers?order=desc&sort=activity&site=stackoverflow&filter=withbody`;
Затем мы можем использовать qURL
для запроса API.
const getQuestionData = async () => {
await fetch(qURL)
.then((r) => r.json())
.then((data) => handleQuestionResponse(data));
};
Вот здесь и проявляется та ошибка, о которой я говорил ранее. Теперь я знаю, что в Node всегда нужно await
fetches. Затем я вызываю вспомогательную функцию handleQuestionResponse()
и передаю ей JSON-данные ответа.
Во-первых, если на вопрос нет ответа, ничего из этого не сработает. Если на вопрос был дан ответ, я сохраняю количество ответов, html-строку тела вопроса, а также html-строку заголовка вопроса.
async function handleQuestionResponse(data) {
if (data.items[0].is_answered !== true) {
console.log(
"Sorry, this question isn't answered yet. Try again with another question."
);
questionDataObj.isAnswered = data.items[0].is_answered;
} else {
questionDataObj.answerCount = data.items[0].answer_count;
questionDataObj.htmlString = [data.items[0].body];
questionDataObj.title = [data.items[0].title];
}
}
Затем aURL
используется для запроса к API
const getAnswerData = async () => {
await fetch(aURL)
.then((r) => r.json())
.then((data) => handleAnswerResponse(data));
};
Опять же, именно здесь ошибка await
вызывала у меня проблемы. Опять же, я использую вспомогательную функцию, которая принимает JSON-данные ответов.
Сначала я отображаю каждый объект в JSON, сохраняя его ID в массив. Затем я снова работаю с каждым объектом, сохраняя HTML-строки в массив. То же самое я делаю для интерполяции ID в строку, чтобы дать каждому ответу название, которое впоследствии можно будет использовать в файлах. Затем я трижды вызываю функцию parseText()
. Первый вызов используется для получения обычного текста для ответов, второй — для тела вопроса и последний — для заголовка. Глядя на эту функцию сейчас, я не уверен, почему parseText()
находится внутри handleAnswerResponse()
, а не вызывается снаружи в главной функции, экспортируемой из файла. Возможно, я еще повожусь с этим и перенесу вызовы этих функций за пределы этой вспомогательной функции.
async function handleAnswerResponse(data) {
let answersIds = data.items.map((el) => el.answer_id);
questionDataObj.answerIds = answersIds;
let htmlAns = data.items.map((el) => el.body);
let titles = data.items.map((el) => `answer${el.answer_id}`);
titles.forEach((el) => fileNames.push(el));
// parse answers
await parseText(htmlAns, plainTextStrings, questionDataObj);
// parse question body
await parseText(
questionDataObj.htmlString,
plainTextStrings,
questionDataObj,
false
);
// parse question title
await parseText(
questionDataObj.title,
plainTextStrings,
questionDataObj,
true
);
}
Это все, что есть в этом файле, кроме вызова getAnswerData()
и getQuestionData()
. Конечно, здесь есть куда совершенствоваться, и я планирую кое-что из этого изменить к следующему разу, когда буду писать об этом проекте. Если у вас есть какие-либо вопросы, я постараюсь ответить на них в комментариях. Спасибо за чтение и не стесняйтесь проверять некоторые из моих других постов здесь на dev.to