skip to Main Content

I am working on a Chrome extension, which can sort the videos of a website. For example, I search for a word `stackoverflow’ and many videos appear there, but these videos do not have the number of comments displayed in the search result, you have to open this video and get the number of comments. And this is my problem, I actually managed to do something similar, I can get the number of comments but it’s not working properly

const property = message.property;
const videos = Array.from(document.querySelectorAll("." + sortikContainer));
const parent = videos[0].parentNode;

originalOrder = Array.from(parent.children); // Store the original order

const savedURL = window.location.href; // Save the current URL

if (videos.length > 0) {
  const visitedVideos = {}; // Object to store visited video URLs
  const videoObjects = []; // Array to store video objects
  const commentCounts = new Map(); // Map to store comment counts for each video URL

  const openAndPrintVideoContent = async () => {
    for (const video of videos) {
      const videoUrl = video.getAttribute("src");

      if (!visitedVideos[videoUrl]) {
        visitedVideos[videoUrl] = true; // Mark video as visited

        await new Promise((resolve) => {
          const videoTriggerElement = video.querySelector(".tiktok-18e9va3-DivContainer");
          videoTriggerElement.click(); // Open video

          waitForElm('[data-e2e="browse-comment-count"]').then((commentCountElement) => {
            if (commentCountElement && commentCountElement.textContent.trim() !== "") {
              const commentCount = parseFloat(commentCountElement.textContent);
              commentCounts.set(videoUrl, commentCount); // Store the comment count for the video URL
            }

            const closeButton = video.querySelector('[data-e2e="browse-close"]');
            if (closeButton) {
              closeButton.click();
              resolve();
            }
          });
        });

        // Return to the list of videos by restoring the saved URL
        history.pushState(null, null, savedURL);
      }
    }

    sortVideosByComments(); // Sort videos by comments
    console.log("Video Objects:", videoObjects); // Print video objects
  };

  const sortVideosByComments = () => {
    const sortedVideos = videos.sort((a, b) => {
      const commentCountA = commentCounts.get(a.getAttribute("src")) || 0;
      const commentCountB = commentCounts.get(b.getAttribute("src")) || 0;

      console.log("A=>" + commentCountA);
      console.log("B=>" + commentCountB);
      return commentCountB - commentCountA; // Sort by comment count in descending order
    });

    sortedVideos.forEach((video) => {
      const parent = video.parentNode;
      parent.prepend(video);
    });

    console.log("Videos sorted by comments");

    // Build video object and add it to the array
    sortedVideos.forEach((video) => {
      const videoObject = {
        url: video.getAttribute("href"),
        commentCount: commentCounts.get(video.getAttribute("src")) || 0,
        html: video.outerHTML,
      };
      videoObjects.push(videoObject);
    });
  };

  openAndPrintVideoContent().then(() => {
    console.log("Printing completed");
  });
} else {
  console.log("No video elements found");
  sendResponse({ success: false, videoCounts: [] });
}

function waitForElm(selector) {
  return new Promise((resolve) => {
    if (document.querySelector(selector)) {
      return resolve(document.querySelector(selector));
    }

    const observer = new MutationObserver((mutations) => {
      if (document.querySelector(selector)) {
        resolve(document.querySelector(selector));
        observer.disconnect();
      }
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
    });
  });
}

This code returns the number of comments, but it also changes the URL in Chrome, and then takes me away from the page where I’m sorting, is there any other way I can do what I’m looking for?

2

Answers


  1. use mutator to wait until element exists this way ou can be sure that the comments are loaded after

    videoTriggerElement.click(); // Open Video
    

    when the element is present you can get the comments count safely

    example of mutator to wait until element exists :

    function waitForElm(selector) {
        return new Promise(resolve => {
            if (document.querySelector(selector)) {
                return resolve(document.querySelector(selector));
            }
    
            const observer = new MutationObserver(mutations => {
                if (document.querySelector(selector)) {
                    resolve(document.querySelector(selector));
                    observer.disconnect();
                }
            });
    
            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        });
    }
    

    To use it:

    waitForElm('.some-class').then((elm) => {
        console.log('Element is ready');
        console.log(elm.textContent);
    });
    

    you can also find other functions that use timeout if the comments page are not loaded proprely

    async function waitForElement(selector, timeout = null, location = document.body) {
        return new Promise((resolve) => {
            let element = document.querySelector(selector);
            if (element) {
                return resolve(element);
            }
    
            const observer = new MutationObserver(async () => {
                let element = document.querySelector(selector);
                if (element) {
                    resolve(element);
                    observer.disconnect();
                } else {
                    if (timeout) {
                        async function timeOver() {
                            return new Promise((resolve) => {
                                setTimeout(() => {
                                    observer.disconnect();
                                    resolve(false);
                                }, timeout);
                            });
                        }
                        resolve(await timeOver());
                    }
                }
            });
    
            observer.observe(location, {
                childList: true,
                subtree: true,
            });
        });
    }
    

    usage

    await waitForElement(".nav-alt", 500, ".main-body")
    
    Login or Signup to reply.
  2. Try this solution it look working for me but you need to rework this code to be most performant because if it is for commercial use you need to add a reject to your promise when waiting for element and if it is not found (caused by network failure…) you can set comment count to 0 or reschedule the url

    Tittok comment count

    function waitForElm(selector) {
      return new Promise((resolve) => {
        if (document.querySelector(selector)) {
          return resolve(document.querySelector(selector));
        }
    
        const observer = new MutationObserver((mutations) => {
          if (document.querySelector(selector)) {
            resolve(document.querySelector(selector));
            observer.disconnect();
          }
        });
    
        observer.observe(document.body, {
          childList: true,
          subtree: true,
        });
      });
    }
    
    function simulateMouseClick(element) {
      // Send mouseover, mousedown, mouseup, click, mouseout
      const eventNames = [
        'mouseover',
        'mouseenter',
        'mousedown',
        'mouseup',
        'click',
        'mouseout',
      ];
      eventNames.forEach((eventName) => {
        const detail = eventName === 'mouseover' ? 0 : 1;
        const event = new MouseEvent(eventName, {
          detail: detail,
          view: window,
          bubbles: true,
          cancelable: true,
        });
        element.dispatchEvent(event);
      });
    
    }
    
    
    const openAndPrintVideoContent = async (selector) => {
    
        try {
        
            const videoTriggerElement = document.querySelector(selector);
                
            simulateMouseClick(videoTriggerElement)
    
            const commentCountElement = await waitForElm('[data-e2e="browse-comment-count"]');
                
            alert(commentCountElement.textContent);
    
            const browserCloseElement = await waitForElm('[data-e2e="browse-close"]');
                
            simulateMouseClick(browserCloseElement)
        }
        catch (error) {
            console.log(error)
        }
    }
    
    openAndPrintVideoContent(".tiktok-9oopl7-DivVideoCardContainer")
    

    wait for element with reject

    const waitForElement = (selector, opts) => {
      let timeout = undefined;
      let root = undefined;
    
      if (opts) {
        ({ root, timeout } = opts);
      }
    
      if (root === undefined) root = document.body;
      
      const nodeFound = root.querySelector(selector);
      if (nodeFound) return new Promise(resolve => resolve(nodeFound));
    
      return new Promise((resolve, reject) => {
        let callback = () => {
          observer.disconnect();
        };
    
        const _resolve = (node) => {
          callback();
          resolve(node);
        };
    
        const _reject = (err) => {
          callback();
          reject(err);
        };
    
        if (timeout && timeout > 0) {
          const handle = setTimeout(() => {
            _reject(new Error("Element not found: timeout exceeded."));
          }, timeout);
          callback = () => {
            observer.disconnect();
            clearTimeout(handle);
          };
        }
    
        const observer = new MutationObserver(mutations => {
          for (const mutation of mutations) {
            for (const addedNode of mutation.addedNodes) {
              if (addedNode.matches(selector)) {
                _resolve(addedNode);
                return;
              }
            }
          }
        });
    
        observer.observe(root, {
          childList: true,
          subtree: true,
        });
      });
    }
    

    Example call:

    await waitForElement("div.bla-bla-bla", {
      timeout: 10000,
      root: document.querySelector("div.some-container") 
    });
    

    finally note that i trigger click for this element :

    .tiktok-XXXXXX-DivVideoCardContainer
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search