Advertisement
anonymous_you

2chen scripts

Aug 24th, 2022
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         2chen Scripts
  3. // @namespace    http://2chen.moe/
  4. // @version      1.2
  5. // @description  Add TikTok sauce button next to filenames; Hide posts with HEVC files; Highlight (You)s
  6. // @author       Anonymous (You)
  7. // @match        https://2chen.moe/*
  8. // @grant        none
  9. // ==/UserScript==
  10.  
  11. // ----- CONFIG -----
  12.  
  13. // border left for posts
  14. const yourPost = "3px dashed rgba(145, 182, 214, .8)";
  15. const quotesYou = "3px solid rgba(145, 182, 214, .8)";
  16. const borderNone = "none";
  17.  
  18. // saucing config
  19. const sauceText = "[tiktok]";
  20. const sauceUrl = "https://www.tiktok.com/@share/video/";
  21. const sauceRegex = /(6|7)\d{18}/g;
  22.  
  23. // thumbnails config
  24. const thumbnailSize = 10
  25.  
  26. // ----- MAIN -----
  27.  
  28. // editing state consts
  29. const NOT_EDITING = 0;
  30. const EDITING = 1;
  31. const EDITING_YOU = 2;
  32.  
  33. (function() {
  34.     'use strict';
  35.  
  36.     // parse all pre-existing posts when page loads
  37.     for (let fileMeta of document.getElementsByTagName("figcaption")) {
  38.         parseFileMeta(fileMeta);
  39.         removeIfHevc(fileMeta);
  40.     }
  41.     //for (let article of document.getElementsByTagName("article")) {
  42.     //    filterPost(article);
  43.     //}
  44.  
  45.     // callback function to execute when mutations are observed
  46.     let callback = function(mutationsList) {
  47.         for(let mutation of mutationsList) {
  48.            for (let node of mutation.addedNodes) {
  49.                //console.log(node)
  50.                if (node.tagName == "FIGCAPTION") {
  51.                    // cases where post appears first and attachment appears afterward (triggered by attachment attaching to post which generates a figcaption)
  52.                    parseFileMeta(node);
  53.                    removeIfHevc(node);
  54.                } else if (node.tagName == "ARTICLE") {
  55.                    // cases where attachment appears simultaneously with post (attachment generates the post which will not create a singular "figcaption" mutation event, adding text to the post will then continue to proc the else branch)
  56.                    // getElementsByTagName doesn't work on node?
  57.                    for (let child of node.childNodes) {
  58.                        if (child.tagName == "FIGCAPTION") {
  59.                            parseFileMeta(child);
  60.                            removeIfHevc(child);
  61.                            break;
  62.                        }
  63.                    }
  64.                }
  65.                // (you)s get added after page loads, but also all of them trigger the mutation observer
  66.                let article = getParentArticle(node);
  67.                if (article != null) {
  68.                    highlightYous(article);
  69.                    //filterPost(article);
  70.                }
  71.            }
  72.         }
  73.     };
  74.  
  75.     // observe page for changes to sauce new posts (all articles/posts should be inside the a tag with the id "thread-container")
  76.     var observer = new MutationObserver(callback);
  77.     observer.observe(document.getElementById("thread-container"), { childList: true, subtree:true });
  78. })();
  79.  
  80. // ----- HELPERS -----
  81.  
  82. // recursively find the (first) enclosing parent article, or null if no such node exists for the given node (returns node immediately if node is article)
  83. function getParentArticle(node) {
  84.     if (node == null || node.tagName == null) return null;
  85.     else if (node.tagName == "ARTICLE") return node;
  86.     else return getParentArticle(node.parentNode);
  87. }
  88.  
  89. // checks if article is editing (checks for presence of specific classes)
  90. // returns a const integer depending on if the article is not in edit mode, if it is in edit mode or if it is in edit mode and you are the one editing
  91. function isArticleEditing(article) {
  92.     if (article.classList.contains("editing")) {
  93.         if (article.classList.contains("reply-form")) {
  94.             return EDITING_YOU;
  95.         } else {
  96.             return EDITING;
  97.         }
  98.     } else {
  99.         return NOT_EDITING;
  100.     }
  101. }
  102.  
  103. // checks if an article was made by you (checks for presence of (You) in article name field)
  104. function isArticleYou(article) {
  105.     let name = article.getElementsByClassName("name")[0];
  106.     return name.innerHTML.includes("(You)");
  107. }
  108.  
  109. // ----- SAUCE -----
  110.  
  111. // filename & metadata is stored in the <fileinfo> tag inside <article> (article is a post essentially)
  112. function parseFileMeta(fileMeta) {
  113.     let sauceLink = null;
  114.     // attempt to get link from filename (check all links instead of breaking after you find the id to avoid duplicating the link if it already exists)
  115.     let links = fileMeta.getElementsByTagName("a");
  116.     let resolveFromMetaFilename = true;
  117.     for (let link of links) {
  118.         // found a pre-existing <a> tag that should link to sauce, no further action necessary (without this links can start duplicating when e.g. inlining replies)
  119.         if (link.innerHTML == sauceText) {
  120.             resolveFromMetaFilename = false;
  121.             sauceLink = null;
  122.             break;
  123.         }
  124.  
  125.         // attempt to resolve from "download" attribute of link (contains original filename)
  126.         let filename = link.getAttribute("download");
  127.         let generatedLink = parseSauceFromString(filename);
  128.         if (generatedLink != null) {
  129.             // found the id, <a> was created
  130.             sauceLink = generatedLink;
  131.             resolveFromMetaFilename = false;
  132.         }
  133.     }
  134.     // attempt to get link from filename in metadata if resolving from filename failed
  135.     if (resolveFromMetaFilename) {
  136.         try {
  137.             // one of the spans in <span class="fileinfo"> should have the filename if it exists
  138.             let fileInfoSpans = fileMeta.querySelector('span[class="fileinfo"]').getElementsByTagName("span");
  139.             for (let span of fileInfoSpans) {
  140.                 sauceLink = parseSauceFromString(span.innerHTML);
  141.                 if (sauceLink != null) break;
  142.             }
  143.         } catch (e) {}
  144.     }
  145.     // if saucing was successful add the link at the end of figcaption
  146.     if (sauceLink != null) {
  147.         fileMeta.appendChild(sauceLink);
  148.     }
  149. }
  150.  
  151. // pass a string that may or may not contain a TT id, returns an <a> element that links to sauce if it found an id, null otherwise
  152. function parseSauceFromString(string) {
  153.     if (string == null) return null;
  154.  
  155.     const found = string.match(sauceRegex);
  156.     if (found != null) {
  157.         var a = document.createElement('a');
  158.         a.setAttribute('href', sauceUrl + found);
  159.         a.setAttribute('target', "_blank"); // new window or tab
  160.         a.innerHTML = sauceText;
  161.         return a;
  162.     }
  163.     return null;
  164. }
  165.  
  166. // ----- HEVC -----
  167.  
  168. function removeIfHevc(fileMeta) {
  169.    if (isHevcOrThumbnail(fileMeta)) {
  170.         // seems posts need more than just the "deleted" class on <article> to shadowbin it
  171.         // removing the node is not great - the chen js will start throwing errors that it's missing nodes
  172.         // simply hide the post
  173.         //fileMeta.parentNode.style.display = 'none';
  174.         let figures = fileMeta.parentNode.querySelector('div[class="post-container"]').getElementsByTagName("figure");
  175.         for (let figure of figures) {
  176.             figure.style.display = 'none';
  177.         }
  178.     }
  179. }
  180.  
  181. // metadata is stored in the <figcaption> tag inside <article> (article is a post essentially)
  182. function isHevcOrThumbnail(fileMeta) {
  183.     let isHevc = false;
  184.     let isThumbnail = false;
  185.     try {
  186.         // one of the spans in <span class="fileinfo"> should be "HEVC" if it exists
  187.         let fileInfoSpans = fileMeta.querySelector('span[class="fileinfo"]').getElementsByTagName("span");
  188.         if (fileInfoSpans.length > 1) {
  189.             for (let span of fileInfoSpans) {
  190.                 isHevc = span.innerHTML.match("HEVC");
  191.                 if (isHevc) break;
  192.             }
  193.             let thumbnailRegex = fileInfoSpans[1].innerHTML.match(/\d+x\d+/);
  194.             if (thumbnailRegex != null) {
  195.                 let dimens = thumbnailRegex[0].split("x");
  196.                 isThumbnail = parseInt(dimens[0]) < thumbnailSize && parseInt(dimens[1]) < thumbnailSize;
  197.             }
  198.         }
  199.     } catch (e) {
  200.         console.log(e);
  201.     }
  202.     return isHevc || isThumbnail;
  203. }
  204.  
  205. // ----- (YOU)s -----
  206.  
  207. // highlight your posts and highlight replies to your posts
  208. function highlightYous(article) {
  209.     if (isArticleYou(article)) {
  210.         article.style.borderLeft = yourPost;
  211.     } else {
  212.         // post-container intermediate step required, otherwise it'll also grab backlinks
  213.         let postLinks = article.getElementsByClassName("post-container")[0].getElementsByClassName("post-link");
  214.         let isReply = false;
  215.         for (let link of postLinks) {
  216.             if (link.innerHTML.includes("(You)")) {
  217.                 isReply = true;
  218.                 break;
  219.             }
  220.         }
  221.         // if someone quotes you and deletes that quote the border needs to clear
  222.         if (isReply) {
  223.             article.style.borderLeft = quotesYou;
  224.         } else {
  225.             article.style.borderLeft = borderNone;
  226.         }
  227.     }
  228. }
  229.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement