The current implementation of the YouTube search results apps script functions effectively. However, a challenge arises when attempting to retrieve results for more than 100 keywords, resulting in a timeout error. Assistance is sought in identifying a viable solution or workaround to address this limitation.
const CONFIG = { sheetName: "Youtube Data", spreadsheetId: "1GolMiGKQQtXoEy1L92q7ZDWlhbftKKKwSOW-RGEeQOQ", maxResults: 5, videoDuration: "", publishedAfter: "2023-05-29T00:00:00Z", rangeToClear: 'A2:M', startRow: 5, imageWidth: 200, imageHeight: 150,};function youTubeSearchResults() { const sheet = getSheet(); const keywordValues = getKeywordValues(); const results = searchVideos(keywordValues); appendResults(sheet, results); }function getSheet() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.sheetName); if (!sheet) { throw new Error(`Sheet not found: ${CONFIG.sheetName}`); } return sheet;}function getKeywordValues() { const source = SpreadsheetApp.openById(CONFIG.spreadsheetId); if (!source) { throw new Error(`Spreadsheet not found: ${CONFIG.spreadsheetId}`); } const keywordRange = source.getRange("A2:A" + source.getLastRow()); const keywordValues = keywordRange.getDisplayValues().filter(([a]) => a); return keywordValues;}function searchVideos(keywordValues) { const sheet = getSheet(); sheet.getRange(CONFIG.rangeToClear).clearContent(); const results = []; keywordValues.forEach(([keyword]) => { const apiUrl = `https://yt.lemnoslife.com/noKey/search?part=snippet&q=${keyword}&maxResults=${CONFIG.maxResults}&type=video&order=viewCount&publishedAfter=${CONFIG.publishedAfter}`; const response = UrlFetchApp.fetch(apiUrl); if (response.getResponseCode() === 200) { const searchResults = JSON.parse(response.getContentText());Logger.log(searchResults) const videoResults = searchResults.items.filter(sr => sr.id.kind === "youtube#video"); const rowResults = videoResults.map(sr => [keyword, sr.id.videoId, `https://www.youtube.com/watch?v=${sr.id.videoId}`, sr.snippet.title, sr.snippet.publishedAt, sr.snippet.channelTitle, sr.snippet.channelId, `https://www.youtube.com/channel/${sr.snippet.channelId}`, sr.snippet.thumbnails.high.url]); if (rowResults.length > 0) { results.push(...rowResults); } } else { throw new Error(`YouTube API error: ${response.getResponseCode()}`); } }); return results;}function appendResults(sheet, results) { const batchSize = 1000; const rows = results.length; const iterations = Math.ceil(rows / batchSize); for (let i = 0; i < iterations; i++) { const startRow = i * batchSize; const endRow = (i + 1) * batchSize; const valuesRange = results.slice(startRow, endRow); if (valuesRange.length > 0) { sheet.getRange(sheet.getLastRow() + 1, 1, valuesRange.length, valuesRange[0].length).setValues(valuesRange); } }}function secondPhase() { removeDuplicateRows(); viewAndSubCount(); sortSheet(); formatData(); compareFormula(); imageFormula();}function removeDuplicateRows() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.sheetName); if (sheet) { // Get the range of data in the sheet, excluding the header row. const dataRange = sheet.getDataRange().offset(1, 0).getValues(); // Create an object to keep track of unique video IDs. const uniqueIds = {}; // Loop through each row of data in reverse order and keep track of unique video IDs. for (let i = dataRange.length - 1; i >= 0; i--) { const videoId = dataRange[i][1]; if (!uniqueIds[videoId]) { uniqueIds[videoId] = true; } else { // If the video ID already exists in the object, remove the row. sheet.deleteRow(i + 2); // Add 2 to account for the header row and 0-based indexing. } } } else { console.error("Sheet not found."); }}function viewAndSubCount() { // 1. Retrieve the video URLs from Spreadsheet. const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.sheetName); const range = sheet.getRange("B2:B" + sheet.getLastRow()); // 2. Retrieve the video IDs. const values = range.getValues().map(([a]) => a.split("=").pop().trim());Logger.log(values) const check = values.slice(); const limit = 50; const obj = [...Array(Math.ceil(values.length / limit))].reduce((obj, _) => { // 3. Retrieve the channel IDs and view counts from the video IDs. const videoIds = values.splice(0, limit).join(','); const res1 = YouTube.Videos.list(["id", "snippet", "statistics"], { id: videoIds, maxResults: CONFIG.maxResults }).items.reduce((o, { id, snippet: { channelId }, statistics: { viewCount, subscriberCount } }) => { o[id] = { viewCount: Number(viewCount), subscriberCount: Number(subscriberCount), channelId }; return o; }, {}); // 4. Retrieve the values of subscriberCount from the channel IDs. const res2 = YouTube.Channels.list(["statistics"], { id: Object.values(res1).map(e => e.channelId), maxResults: CONFIG.maxResults }).items.reduce((o, { id, statistics: { subscriberCount } }) => { o[id] = Number(subscriberCount); return o; }, {}); // 5. Create an object containing view count and subscriber count for each video. Object.entries(res1).forEach(([k, v]) => { obj[k] = { viewCount: v.viewCount, subscriberCount: res2[v.channelId] ? res2[v.channelId] : "", channelId: v.channelId }; }); return obj; }, {}); // 6. Put the values of view count and subscriber count to Spreadsheet. range.offset(0, 9).setValues(check.map(a => [obj[a] ? obj[a].viewCount : ""])); range.offset(0, 10).setValues(check.map(a => [obj[a] ? obj[a].subscriberCount : ""]));}function formatBothColumns() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.sheetName); const data = sheet.getDataRange().getValues(); const newData = []; for (let i = 0; i < data.length; i++) { const row = data[i]; // Skip the first row (header row) if (i === 0) { newData.push(row); continue; } // Check column J for values less than 1000 and column K for values greater than 5000 if (row[9] >= 1000 && row[10] <= 5000) { newData.push(row); } } // Clear existing data and write new data sheet.getDataRange().clearContent(); sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);}function getImages() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.sheetName); let lastRow = sheet.getLastRow(); for (let i = CONFIG.startRow - 1; i < lastRow - 1; i++) { let url = sheet.getRange(i + 1, 9).getValue(); let image = SpreadsheetApp.newCellImage().setSourceUrl(url).build(); try { sheet.getRange(i + 1, 9).setValue(image); sheet.setRowHeight(i + 1, CONFIG.imageHeight); sheet.setColumnWidth(9, CONFIG.imageWidth); } catch (e) { console.log(e.message) } }}function sortSheet() { var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.sheetName); var range = sheet.getDataRange(); var numRows = range.getNumRows(); var numCols = range.getNumColumns(); var headerRows = 1; // number of header rows var dataRange = sheet.getRange(headerRows + 1, 1, numRows - headerRows, numCols); // range excluding header rows dataRange.sort({column: 11, ascending: false});}function formatData() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.sheetName); const data = sheet.getDataRange().getValues(); const newData = []; for (let i = 0; i < data.length; i++) { const row = data[i]; // Skip the first row (header row) if (i === 0) { newData.push(row); continue; } // Check column K for values less than 5000 or empty cells if (row[10] === '' || row[10] > 5000) { newData.push(row); } } // Clear existing data and write new data sheet.getDataRange().clearContent(); sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);}function compareFormula() { var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.sheetName); var formula = '=IF(ROW()>ROW(INDEX(D:D,MAX(D:D<>""))),ArrayFormula(ISNUMBER(MATCH(F2:F,Mix!G2:G,0))))'; var cell = sheet.getRange('M2'); cell.setFormula(formula);}function imageFormula() { var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.sheetName); var range = sheet.getDataRange(); // Apply formula to column J starting from row 2 var formulaJ = "=ArrayFormula(IMAGE(I2))"; var columnJ = sheet.getRange("J2:J" + range.getLastRow()); columnJ.setFormula(formulaJ);}