Pages

Se afișează postările cu eticheta Google Apps Script. Afișați toate postările
Se afișează postările cu eticheta Google Apps Script. Afișați toate postările

sâmbătă, 29 martie 2025

News : Google Apps Script - add movies from website.

This source code with GAScript add few movies from cinemagia website:
This is the source code:
function scrapeProcessAndCleanUp() {
  const url = "https://www.cinemagia.ro/program-tv/filme-la-tv/filme-pro-tv,antena-1,tvr-1,prima-tv,diva,pro-cinema,film-cafe/azi/";
 
  let html;
  try {
    const response = UrlFetchApp.fetch(url);
    html = response.getContentText();
    Logger.log("Fetched HTML content length: ", html.length);
  } catch (error) {
    Logger.log("Error fetching HTML content: ", error.message);
    return;
  }

  let doc;
  try {
    doc = DocumentApp.create("Temporary HTML Content");
    doc.appendParagraph(html);
    doc.saveAndClose();
    Logger.log("Document created successfully with ID: ", doc.getId());
  } catch (error) {
    Logger.log("Error creating/saving document: ", error.message);
    return;
  }

  let text;
  try {
    const document = DocumentApp.openById(doc.getId());
    text = document.getBody().getText();
    Logger.log("Document text content length: ", text.length);
  } catch (error) {
    Logger.log("Error opening document or extracting text: ", error.message);
    return;
  }

  const titles = [...text.matchAll(/<li class="first">(.*?)<\/li>/g)];
  const images = [...text.matchAll(/<img src="(https:\/\/static\.cinemagia\.ro\/img\/resize\/db\/movie.*?)"/g)];
  const channels = [...text.matchAll(/<span class="r1">(.*?)<\/span>/g)];
  const times = [...text.matchAll(/<span class="r2">(.*?)<\/span>/g)];
  Logger.log("Titles found: ", titles.length);
  Logger.log("Images found: ", images.length);
  Logger.log("Channels found: ", channels.length);
  Logger.log("Times found: ", times.length);

  const extractedData = titles.map((title, index) => {
    const image = images[index] ? images[index][1] : "N/A";
    const channel = channels[index] ? channels[index][1].trim() : "N/A";
    const time = times[index] ? times[index][1].trim() : "N/A";
    return {
      title: title[1].trim(),
      image: image,
      channel: channel,
      time: time
    };
  });
  try {
    const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
    const currentDate = new Date();
    extractedData.forEach((movie, rowIndex) => {
      if (movie.title !== "N/A" && movie.image !== "N/A") {
        const imageFormula = `=IMAGE("${movie.image}")`;
        const rowIndexAdjusted = sheet.getLastRow() + 1;
        sheet.appendRow([currentDate, movie.title, imageFormula, movie.channel, movie.time]);
        sheet.setRowHeight(rowIndexAdjusted, 76); // Set row height to 330px
      }
    });
    Logger.log("Processed movies count: ", extractedData.length);
  } catch (error) {
    Logger.log("Error adding data to spreadsheet: ", error.message);
  }
}
This is the result:

marți, 25 martie 2025

News : Google Apps Script - find duplicate files in google drive.

For today, a simple GAScript source code to add into spreadsheet the duplicate files from Google drive.
This Google Apps Script finds duplicate files in Google Drive by comparing file sizes and optionally file types. It then displays the results in a spreadsheet with detailed information about each duplicate file. The script collects information about all files in Drive. Files are grouped by their size and optionally file type. Any group with more than one file is considered a set of duplicates These duplicate sets are displayed in the spreadsheet
Functions
  • checkDuplicatesInDrive(): Main function that searches your entire Google Drive for duplicates
  • checkDuplicatesInFolder(): Alternative function that searches a specific folder and its subfolders
  • findDuplicateFiles(): Core function that identifies duplicate files based on size and type
  • addDuplicatesToSheet(): Adds the found duplicates to a spreadsheet
I used artificial inteligence and this help me much ...
/**
 * Main function to check files in the root folder and add duplicates to the active spreadsheet
 */
function checkDuplicatesInDrive() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  
  // Clear the sheet and set up headers
  sheet.clear();
  sheet.appendRow(["Group", "File Name", "Size", "Type", "File Path", "Date Created", "Last Updated", "URL"]);
  sheet.getRange(1, 1, 1, 8).setFontWeight("bold").setBackground("#f3f3f3");
  
  // Find all duplicates
  const duplicates = findDuplicateFiles(true);
  
  // Check if any duplicates were found
  if (Object.keys(duplicates).length === 0) {
    sheet.appendRow(["No duplicate files found"]);
    sheet.autoResizeColumns(1, 8);
    return;
  }
  
  // Add duplicates to the sheet
  addDuplicatesToSheet(duplicates, sheet);
  
  // Auto-resize columns
  sheet.autoResizeColumns(1, 8);
}

/**
 * Adds duplicate files to the specified sheet
 */
function addDuplicatesToSheet(duplicates, sheet) {
  let groupNumber = 1;
  let rowIndex = 2;
  let totalDuplicateFiles = 0;
  
  for (const key in duplicates) {
    const files = duplicates[key];
    totalDuplicateFiles += files.length;
    
    files.forEach((file, index) => {
      // Get file path
      const filePath = getFilePath(file.id);
      
      sheet.appendRow([
        groupNumber,
        file.name,
        formatFileSize(file.size),
        file.mimeType,
        filePath,
        file.dateCreated.toLocaleString(),
        file.lastUpdated.toLocaleString(),
        file.url
      ]);
      
      // Add hyperlink to the file URL
      sheet.getRange(rowIndex, 8).setFormula(`=HYPERLINK("${file.url}","Open File")`);
      
      rowIndex++;
    });
    
    groupNumber++;
  }
  
  // Add summary at the bottom - only if we have duplicates
  if (totalDuplicateFiles > 0) {
    sheet.appendRow(["SUMMARY"]);
    sheet.appendRow([`Found ${totalDuplicateFiles} duplicate files in ${groupNumber - 1} groups.`]);
  }
  
  return totalDuplicateFiles;
}

/**
 * Gets the file path for a given file ID
 */
function getFilePath(fileId) {
  try {
    const file = DriveApp.getFileById(fileId);
    const parents = file.getParents();
    
    if (parents.hasNext()) {
      const parent = parents.next();
      return getFolderPath(parent) + "/" + file.getName();
    } else {
      return "/" + file.getName();
    }
  } catch (e) {
    return "Path not available";
  }
}

/**
 * Gets the folder path for a given folder
 */
function getFolderPath(folder) {
  try {
    const parents = folder.getParents();
    
    if (!parents.hasNext()) {
      return "/" + folder.getName();
    }
    
    const parent = parents.next();
    return getFolderPath(parent) + "/" + folder.getName();
  } catch (e) {
    return "/Unknown";
  }
}

/**
 * Finds duplicate files in Google Drive based on file size and optionally file type.
 * @param {boolean} considerFileType Whether to consider file type when finding duplicates (default: true)
 * @param {string} folderId Optional folder ID to search in. If not provided, searches in the entire Drive.
 * @return {Object} An object containing groups of duplicate files
 */
function findDuplicateFiles(considerFileType = true, folderId = null) {
  // Create a map to store files by their size (and optionally type)
  const fileMap = {};
  
  // Get files to check
  let files;
  if (folderId) {
    const folder = DriveApp.getFolderById(folderId);
    files = folder.getFiles();
  } else {
    files = DriveApp.getFiles();
  }
  
  // Process each file
  while (files.hasNext()) {
    const file = files.next();
    // Skip Google Docs, Sheets, etc. as they don't have a fixed size
    if (file.getSize() === 0) continue;
    
    const fileSize = file.getSize();
    const mimeType = file.getMimeType();
    
    // Create a key based on file size and optionally type
    let key = fileSize.toString();
    if (considerFileType) {
      key += '_' + mimeType;
    }
    
    // Add file to the map
    if (!fileMap[key]) {
      fileMap[key] = [];
    }
    
    fileMap[key].push({
      id: file.getId(),
      name: file.getName(),
      size: fileSize,
      mimeType: mimeType,
      url: file.getUrl(),
      dateCreated: file.getDateCreated(),
      lastUpdated: file.getLastUpdated()
    });
  }
  
  // Filter out unique files (groups with only one file)
  const duplicates = {};
  for (const key in fileMap) {
    if (fileMap[key].length > 1) {
      duplicates[key] = fileMap[key];
    }
  }
  
  return duplicates;
}

/**
 * Alternative function to check files in a specific folder and its subfolders
 */
function checkDuplicatesInFolder() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  
  // Clear the sheet and set up headers
  sheet.clear();
  sheet.appendRow(["Group", "File Name", "Size", "Type", "File Path", "Date Created", "Last Updated", "URL"]);
  sheet.getRange(1, 1, 1, 8).setFontWeight("bold").setBackground("#f3f3f3");
  
  // Collect all files from the folder and subfolders
  var fileMap = {};
  var rootFolder = DriveApp.getRootFolder(); // Change this to your specific folder if needed
  collectFilesFromFolder(rootFolder, fileMap);
  
  // Filter out unique files
  const duplicates = {};
  for (const key in fileMap) {
    if (fileMap[key].length > 1) {
      duplicates[key] = fileMap[key];
    }
  }
  
  // Check if any duplicates were found
  if (Object.keys(duplicates).length === 0) {
    sheet.appendRow(["No duplicate files found"]);
    sheet.autoResizeColumns(1, 8);
    return;
  }
  
  // Add duplicates to the sheet
  addDuplicatesToSheet(duplicates, sheet);
  
  // Auto-resize columns
  sheet.autoResizeColumns(1, 8);
}

/**
 * Recursively collects files from a folder and its subfolders
 */
function collectFilesFromFolder(folder, fileMap, considerFileType = true) {
  // Process files in this folder
  var files = folder.getFiles();
  while (files.hasNext()) {
    const file = files.next();
    // Skip Google Docs, Sheets, etc. as they don't have a fixed size
    if (file.getSize() === 0) continue;
    
    const fileSize = file.getSize();
    const mimeType = file.getMimeType();
    
    // Create a key based on file size and optionally type
    let key = fileSize.toString();
    if (considerFileType) {
      key += '_' + mimeType;
    }
    
    // Add file to the map
    if (!fileMap[key]) {
      fileMap[key] = [];
    }
    
    fileMap[key].push({
      id: file.getId(),
      name: file.getName(),
      size: fileSize,
      mimeType: mimeType,
      url: file.getUrl(),
      dateCreated: file.getDateCreated(),
      lastUpdated: file.getLastUpdated()
    });
  }
  
  // Process subfolders
  var subfolders = folder.getFolders();
  while (subfolders.hasNext()) {
    var subfolder = subfolders.next();
    collectFilesFromFolder(subfolder, fileMap, considerFileType);
  }
}

/**
 * Helper function to format file size in a human-readable format
 */
function formatFileSize(bytes) {
  if (bytes < 1024) return bytes + " bytes";
  else if (bytes < 1048576) return (bytes / 1024).toFixed(2) + " KB";
  else if (bytes < 1073741824) return (bytes / 1048576).toFixed(2) + " MB";
  else return (bytes / 1073741824).toFixed(2) + " GB";
}

/**
 * Creates a new Google Spreadsheet with the duplicate files report
 */
function createDuplicateFilesSpreadsheet() {
  const duplicates = findDuplicateFiles(true);
  
  // Create a new spreadsheet
  const ss = SpreadsheetApp.create("Duplicate Files Report - " + new Date().toLocaleString());
  const sheet = ss.getActiveSheet();
  
  // Set up headers
  sheet.appendRow(["Group", "File Name", "Size", "Type", "File Path", "Date Created", "Last Updated", "URL"]);
  
  // Format header row
  sheet.getRange(1, 1, 1, 8).setFontWeight("bold").setBackground("#f3f3f3");
  
  // Check if any duplicates were found
  if (Object.keys(duplicates).length === 0) {
    sheet.appendRow(["No duplicate files found"]);
    sheet.autoResizeColumns(1, 8);
    return ss.getUrl();
  }
  
  // Add duplicates to the sheet
  addDuplicatesToSheet(duplicates, sheet);
  
  // Auto-resize columns
  sheet.autoResizeColumns(1, 8);
  
  Logger.log(`Spreadsheet created: ${ss.getUrl()}`);
  return ss.getUrl();
}
See the result into the spreadsheet:

sâmbătă, 2 decembrie 2023

News : Chart with sizes values from Google Drive.

Visual data representation in graphical format can solve many of today's issues.
Here is a tutorial with a Google Apps Script script that allows you to view the size of the files in Google Drive in Chart Pie format and modify it.
You can find more tutorials about Google Apps Script on my Google site.
Let's see the source code:
function generateDriveUsageReportPie() {
  var drive = DriveApp.getRootFolder();
  var files = drive.getFiles();

  var data = [['Nume Fișier', 'Dimensiune (KB)']];
  
  while (files.hasNext()) {
    var file = files.next();
    var fileSizeBytes = file.getSize();
    var fileSizeKB = fileSizeBytes / 1024; // Convertim dimensiunea în KB
    data.push([file.getName(), fileSizeKB]);
  }

  // open Google Sheet
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName('Drive Usage Report');
  if (!sheet) {
    sheet = spreadsheet.insertSheet('Drive Usage Report');
  }

  // clean sheet
  sheet.clear();

  // write values
  sheet.getRange(1, 1, data.length, data[0].length).setValues(data);

  // clean charts from Google Sheets
  var charts = sheet.getCharts();
  for (var i = 0; i < charts.length; i++) {
    sheet.removeChart(charts[i]);
  }

  // create and add the Pie Chart with all values
  var chart = sheet.newChart()
    .asPieChart()
    .setTitle('Utilizare Google Drive')
    .addRange(sheet.getRange(2, 1, data.length - 1, 2))  // Exclude header row
    .setPosition(5, 1, 0, 0)
    .setOption('title', 'Utilizare Google Drive')
    .setOption('legend', {position: 'top'})
    .build();

  sheet.insertChart(chart);

  Logger.log('Raport generat cu succes.');
}
Here is the result obtained:

sâmbătă, 12 august 2023

GAS - show appreciated videos with like it by me.

As I said, my old tutorial website is on hiatus and I am filling in blogs.
Today, an incomplete but functional script for searching in the playlist of appreciated videos with like it by me.
Create an access key in the Google Console and also enable the YouTube Data service in both the Google Console and the Editor.
Here is a script in GAS - Google Apps Script that allows you to access the playlist of videos you have liked.
function searchLikedVideos(query) {
    if (!query) {
    Logger.log('Vă rugăm să furnizați un cuvânt cheie de căutare.');
    return;
  }
  var apiKey = 'YOUR_API_KEY';

  var url = 'https://www.googleapis.com/youtube/v3/videos';
  url += '?part=snippet';
  url += '&myRating=like'; // "Like" filter
  url += '&q=' + encodeURIComponent(query); // Cuvântul cheie de căutare
  url += '&maxResults=50'; // 

  var options = {
    'method': 'get',
    'headers': {
    'Authorization': 'Bearer ' + ScriptApp.getOAuthToken(),
    'X-JavaScript-User-Agent': 'Google Apps Script',
    'X-GData-Key': 'key=' + 'YOUR_API_KEY',
    'X-YouTube-Client-Name': 'youtubeLikeI', // Numele aplicației dvs.
    'X-YouTube-Client-Version': '1.0', // Versiunea aplicației dvs.
    'X-YouTube-Developer-Key': 'YOUR_API_KEY',
    'X-YouTube-Api-Version': '3',
    'X-YouTube-Page-Cl': 'cl=3009377', // Cod unic asociat aplicației dvs.
    'X-YouTube-Page-Token': 'token=' + ScriptApp.getOAuthToken(),
    'X-YouTube-Utc-Offset': '120', // Offset-ul UTC
    'X-YouTube-Time-Zone': 'Europe/Bucharest', // Fusul orar
    'X-YouTube-Max-Upload-Rate': '20000', // Rata maximă de încărcare (în KB/s)
    'X-YouTube-Max-Download-Rate': '5000000', // Rata maximă de descărcare (în KB/s)
    'X-YouTube-Locale': 'ro_RO', // Localizarea dvs.
    'X-YouTube-Ad-Format': 'html5_1_adsense2_0'
    }
  };

  var response = UrlFetchApp.fetch(url, options);
  var data = JSON.parse(response.getContentText());

  if (data.items && data.items.length > 0) {
    for (var i = 0; i < data.items.length; i++) {
      var channel_ID = data.items[i].snippet.channelId;
      Logger.log('Channel Id: ' + channel_ID);
      var videoTitle = data.items[i].snippet.title;
      Logger.log('Video Title: ' + videoTitle);
    }
  } else {
    Logger.log('Nu s-au găsit videoclipuri "Like" pentru căutarea dată.');
  }
}

function search() {
  searchLikedVideos('theescapist');
}

function searchByKeyword() {
  var results = YouTube.Search.list('id,snippet', {q: 'like', maxResults: 25});
  for(var i in results.items) {
    var item = results.items[i];
    Logger.log('[%s] Title: %s', item.id.videoId, item.snippet.title);
  }
}
This script show the original title of the video and not that is show on YouTube browser where is translate, see : EVİNİZDE 3 PATATES VE 1 YUMURTANIZ VARSA‼️ BU KOLAY ...
The result in console log in the Editor script is this :
10:20:42 PM	Notice	Execution started
10:20:45 PM	Info	Channel Id: UCqg5FCR7NrpvlBWMXdt-5Vg
10:20:45 PM	Info	Video Title: An Unexpected Dump | Adventure is Nigh! - The Liar, The Witch and The Wartorn | S3 EP 2
10:20:45 PM	Info	Channel Id: UCXuqSBlHAE6Xw-yeJA0Tunw
10:20:45 PM	Info	Video Title: Facebook Sold me this Antivirus USB
10:20:45 PM	Info	Channel Id: UCelXvXZDvx8_TdOOffevzGg
10:20:45 PM	Info	Video Title: Now You Know with Zac and Jesse: Tesla, EVs, SpaceX, and Twitter
10:20:45 PM	Info	Channel Id: UCH91ivVTdIPZkhWi6oqeQPQ
10:20:45 PM	Info	Video Title: There's something INSANE about this..
10:20:45 PM	Info	Channel Id: UCX0JHmYdFAOr8k8xCvSc0FQ
10:20:45 PM	Info	Video Title: AI Texture Generator free online, Polycam, seamless texture maker with Blender test render
10:20:45 PM	Info	Channel Id: UC7Ln337Vpli1JHEFDks1t7g
10:20:45 PM	Info	Video Title: Signals in Godot are Amazing!
10:20:45 PM	Info	Channel Id: UCC0_trsvWYyqjZ3njunfU4Q
10:20:45 PM	Info	Video Title: Memory Management - Unity Tutorial
10:20:45 PM	Info	Channel Id: UCAvdfiv_vwTH0fw_LHvX21g
10:20:45 PM	Info	Video Title: ADEVĂRUL despre margarină! Ce spune Sorin Gadola despre ALIMENTELE INTERZISE
10:20:45 PM	Info	Channel Id: UC6nPUTO22UXVxD8uOei8BjA
10:20:45 PM	Info	Video Title: Seagate | "Just" a Hard Drive
10:20:45 PM	Info	Channel Id: UCSojAWUnEUTUcdA9iJ6bryQ
10:20:45 PM	Info	Video Title: Let's Create Our Character Controller! - Creating a Horror Game in Godot 4 Part 1 C#
10:20:45 PM	Info	Channel Id: UC1sELGmy5jp5fQUugmuYlXQ
10:20:45 PM	Info	Video Title: Building a Massive Carousel in Minecraft!
10:20:45 PM	Info	Channel Id: UCelXvXZDvx8_TdOOffevzGg
10:20:45 PM	Info	Video Title: Tesla, SpaceX, and Robots, Oh My!
10:20:45 PM	Info	Channel Id: UCAvdfiv_vwTH0fw_LHvX21g
10:20:45 PM	Info	Video Title: 3 trucuri pentru înfrânarea poftelor
10:20:45 PM	Info	Channel Id: UCjCpZyil4D8TBb5nVTMMaUw
10:20:45 PM	Info	Video Title: Unity Shader Graph - Changing Parameters in Script
10:20:45 PM	Info	Channel Id: UCggKidH56IZIGQ8SppxYn-Q
10:20:45 PM	Info	Video Title: iPad apps you NEED😍 digital reading journal | iPad pro & apple pencil
10:20:45 PM	Info	Channel Id: UCUL9n86w1_MYQmdg-aRhJDg
10:20:45 PM	Info	Video Title: KeenTools GeoTracker for Blender (Beta)
10:20:45 PM	Info	Channel Id: UCk-UHW1Q5EBJIHB4jHkVTbA
10:20:45 PM	Info	Video Title: Samsung NotePaper Screen for Tab S9 tablets (review for writing and drawing)
10:20:45 PM	Info	Channel Id: UCdWNZCe8NLVuYluV_FdGogA
10:20:45 PM	Info	Video Title: Gravelbike test TREK ALR 4
10:20:45 PM	Info	Channel Id: UCUBVVi3dFyBX0B4zUxbrw-Q
10:20:45 PM	Info	Video Title: Restoration of a rusty 30-year-old MERCEDES T1 bus / Part 1
10:20:45 PM	Info	Channel Id: UCFkyds8iRRwi64iPrVh_0AA
10:20:45 PM	Info	Video Title: #121 Protejarea tavanelor din beton chiar daca ai tavan fals
10:20:45 PM	Info	Channel Id: UCAE8fRq0xXh6gZ6d2EHYipQ
10:20:45 PM	Info	Video Title: Plajele de la Cercul Polar se bat cu cele din Seychelles! Caniculă în plină vară arctică
10:20:45 PM	Info	Channel Id: UCelXvXZDvx8_TdOOffevzGg
10:20:45 PM	Info	Video Title: Why Elon Musk chose Austin, Texas as America's boomtown
10:20:45 PM	Info	Channel Id: UCjgVg6F0nUF1707kAyzCXVQ
10:20:45 PM	Info	Video Title: Three USB C Foldable Keyboards With Trackpads
10:20:45 PM	Info	Channel Id: UC_0CVCfC_3iuHqmyClu59Uw
10:20:45 PM	Info	Video Title: The YY3568 Is An All New ARM Based SBC : DEV Board That Run Linux Or Android
10:20:45 PM	Info	Channel Id: UCjgVg6F0nUF1707kAyzCXVQ
10:20:45 PM	Info	Video Title: Three Folding Keyboards With Usb C
10:20:45 PM	Info	Channel Id: UCCQWuh2eCjq6K9aY4BrAETA
10:20:45 PM	Info	Video Title: Lost in a Lava Cave for Days Looking for a Secret Waterfall
10:20:45 PM	Info	Channel Id: UCKPLvnWhN1Qo51IDDZsmq1g
10:20:45 PM	Info	Video Title: SparkFun 20th Anniversary: Jennifer Mullins
10:20:45 PM	Info	Channel Id: UCelXvXZDvx8_TdOOffevzGg
10:20:45 PM	Info	Video Title: Astronaut Shortage Uncovered: The Surprising Truth
10:20:45 PM	Info	Channel Id: UCelXvXZDvx8_TdOOffevzGg
10:20:45 PM	Info	Video Title: How This Gamer Turned Virtual Money into 40K for Wildfire Relief
10:20:45 PM	Info	Channel Id: UCelXvXZDvx8_TdOOffevzGg
10:20:45 PM	Info	Video Title: TOP 3 ways a rocket launch can FAIL #starship #rocket #rocketlaunch
10:20:45 PM	Info	Channel Id: UCelXvXZDvx8_TdOOffevzGg
10:20:45 PM	Info	Video Title: Elon Musk's superpower
10:20:45 PM	Info	Channel Id: UCelXvXZDvx8_TdOOffevzGg
10:20:45 PM	Info	Video Title: I drove the original #tesla #roadster
10:20:45 PM	Info	Channel Id: UCOALNiNsBtmONJGCw-SCfgA
10:20:45 PM	Info	Video Title: Godot 4.0: Metadata Demonstration
10:20:45 PM	Info	Channel Id: UChdrIsYOHZXgEyCLaOHc2Ew
10:20:45 PM	Info	Video Title: Feţele libertăţii cu Andrei Şerban (@TVR1)
10:20:45 PM	Info	Channel Id: UCSojAWUnEUTUcdA9iJ6bryQ
10:20:45 PM	Info	Video Title: Creating a Flexable Level Loading System in Godot 4 C#
10:20:45 PM	Info	Channel Id: UCA4YUiMFaa3Wn4Rkd1UKFAw
10:20:45 PM	Info	Video Title: How to create a MENU in Godot 4.1!
10:20:45 PM	Info	Channel Id: UCTyq5-4JUA_-694Y2pNdYng
10:20:45 PM	Info	Video Title: Multiplayer in Godot 4 in 3 minutes
10:20:45 PM	Info	Channel Id: UCTyq5-4JUA_-694Y2pNdYng
10:20:45 PM	Info	Video Title: API calls in Godot 4 under 4 minutes
10:20:45 PM	Info	Channel Id: UCr-5TdGkKszdbboXXsFZJTQ
10:20:45 PM	Info	Video Title: Terrain3D - The New Terrain Engine for Godot
10:20:45 PM	Info	Channel Id: UCr-5TdGkKszdbboXXsFZJTQ
10:20:45 PM	Info	Video Title: Godot 4.1 Is Here!
10:20:45 PM	Info	Channel Id: UClARBYN-cvOBb4DhKKyjo2w
10:20:45 PM	Info	Video Title: 24) C language. Roguelike: simple AI of monsters. Movement and attack
10:20:45 PM	Info	Channel Id: UClARBYN-cvOBb4DhKKyjo2w
10:20:45 PM	Info	Video Title: Tiled GUIDE for developer (level/map editor)
10:20:45 PM	Info	Channel Id: UClARBYN-cvOBb4DhKKyjo2w
10:20:45 PM	Info	Video Title: [Solution] Issue with Unity license
10:20:45 PM	Info	Channel Id: UCX0JHmYdFAOr8k8xCvSc0FQ
10:20:45 PM	Info	Video Title: Blender Free Addon for Tree Generator - Generate Tree 3D Model
10:20:45 PM	Info	Channel Id: UC-2Y8dQb0S6DtpxNgAKoJKA
10:20:45 PM	Info	Video Title: Date Z - Announce Trailer | PS5 & PS4 Games
10:20:45 PM	Info	Channel Id: UCelXvXZDvx8_TdOOffevzGg
10:20:45 PM	Info	Video Title: Space library tour with astronomer Jonathan McDowell!
10:20:45 PM	Info	Channel Id: UCUOilIEi4sJqM8Z1W53RXZw
10:20:45 PM	Info	Video Title: Dread the Dragons! | The Vallorian Legend | Chapter 21 | Elvenar
10:20:45 PM	Info	Channel Id: UCiHVGt9LmI1SBaswRg3Ufnw
10:20:45 PM	Info	Video Title: EVİNİZDE 3 PATATES VE 1 YUMURTANIZ VARSA‼️ BU KOLAY VE LEZZETLİ PATATES TARİFİNİ HAZIRLAYIN🤌
10:20:45 PM	Info	Channel Id: UCRSx63y-VPidCrycgDS-o_Q
10:20:45 PM	Info	Video Title: [SOLVED] Bone Heat Weighting failed (Automatic Weights doesn't work in Blender)
10:20:43 PM	Notice	Execution completed