{
  "version": "1.0.0",
  "exported_at": "2026-05-31T12:15:00.000Z",
  "project": {
    "name": "TikTok Video Details  Comments Scraper",
    "description": "Best-effort TikTok video details and comments scraper for a TikTok video URL. Extracts video metadata and one row per verified visible comment when TikTok exposes comments in the browser DOM. TikTok comments are normally infinite-scroll and may be hidden behind login, CAPTCHA, region, or anti-bot controls; in blocked sessions this template exports one fallback metadata row with comment_text explaining that no visible comments loaded. Uses fileMode=create for a clean CSV on each run.",
    "color": "bg-[#ff0050]",
    "template_id": "ai-generated"
  },
  "blocks": [
    {
      "block_id": "set-window-size-1",
      "block_type": "process",
      "title": "Set Window Size",
      "description": "Set browser window dimensions",
      "position_x": 120,
      "position_y": 220,
      "config": {
        "width": 1920,
        "height": 1080,
        "color": "bg-[#4589ff]"
      }
    },
    {
      "block_id": "navigate-1",
      "block_type": "process",
      "title": "Navigate",
      "description": "Go to a URL",
      "position_x": 480,
      "position_y": 220,
      "config": {
        "url": "https://www.tiktok.com/@mindfullykenzie/video/7447350140836400430",
        "color": "bg-[#4589ff]"
      }
    },
    {
      "block_id": "wait-for-page-load-1",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait for page to finish loading",
      "position_x": 840,
      "position_y": 220,
      "config": {
        "timeout": 45,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "inject-javascript-1",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Run custom JavaScript",
      "position_x": 1200,
      "position_y": 220,
      "config": {
        "jsCode": "(()=>{const words=['Accept','Got it','Close','Allow all','OK'];document.querySelectorAll('button,[role=button]').forEach(b=>{const t=(b.innerText||b.textContent||b.getAttribute('aria-label')||'').trim();if(words.some(w=>t.toLowerCase()===w.toLowerCase())){try{b.click();}catch(e){}}});return true;})()",
        "waitForCompletion": true,
        "timeout": 10,
        "color": "bg-[#a56eff]"
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 1560,
      "position_y": 220,
      "config": {
        "duration": 2,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 1920,
      "position_y": 220,
      "config": {
        "selector": "article, main, body",
        "timeout": 45,
        "visible": true,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "inject-javascript-2",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Run custom JavaScript",
      "position_x": 2280,
      "position_y": 500,
      "config": {
        "jsCode": "(()=>{document.querySelectorAll('[data-uscraper-comment-row]').forEach(el=>el.removeAttribute('data-uscraper-comment-row'));const clickByText=needles=>{document.querySelectorAll('button,[role=button],div[role=button]').forEach(b=>{const t=(b.innerText||b.textContent||b.getAttribute('aria-label')||'').trim().toLowerCase();if(needles.some(n=>t.includes(n))){try{b.click();}catch(e){}}});};clickByText(['accept','got it','close','comment','comments','view more replies','view reply','view replies','more comments']);try{window.scrollBy(0,1200);}catch(e){}const getCommentText=row=>{const el=row.querySelector('[data-e2e=\"comment-level-1\"],[data-e2e=\"comment-text\"],p[class*=\"CommentText\"],span[class*=\"CommentText\"]');return el?(el.innerText||el.textContent||'').trim():'';};const rowSet=new Set();document.querySelectorAll('div[data-e2e=\"comment-item\"],div[class*=\"DivCommentItemContainer\"]').forEach(el=>rowSet.add(el));document.querySelectorAll('[data-e2e=\"comment-level-1\"],[data-e2e=\"comment-text\"],p[class*=\"CommentText\"],span[class*=\"CommentText\"]').forEach(el=>{const row=el.closest('div[data-e2e=\"comment-item\"],div[class*=\"DivCommentItemContainer\"]')||el.closest('div[class*=\"Comment\"]')||el.parentElement;if(row)rowSet.add(row);});let realCount=0;Array.from(rowSet).forEach(row=>{const txt=getCommentText(row);const rowText=(row.innerText||row.textContent||'').trim();const isNav=/^(search|for you|shop|explore|following|live|upload|profile)$/i.test(rowText);const looksLikeComment=txt.length>0&&!isNav&&rowText.length<2000;if(looksLikeComment){row.setAttribute('data-uscraper-comment-row','real');realCount++;}});if(realCount===0){const fallback=document.createElement('div');fallback.setAttribute('data-uscraper-comment-row','fallback');fallback.textContent='UScraper fallback row: no visible TikTok comments loaded';fallback.style.cssText='position:fixed;left:12px;top:12px;width:320px;height:32px;display:block;opacity:1;z-index:2147483647;background:#fff;color:#000;font-size:12px;overflow:hidden;';document.body.appendChild(fallback);}return {verifiedCommentRows:realCount,fallback:realCount===0};})()",
        "waitForCompletion": true,
        "timeout": 15,
        "color": "bg-[#ff832b]"
      }
    },
    {
      "block_id": "wait-for-element-2",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 2640,
      "position_y": 500,
      "config": {
        "selector": "[data-uscraper-comment-row='real'], [data-uscraper-comment-row='fallback']",
        "timeout": 10,
        "visible": true,
        "color": "bg-[#ff832b]"
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 3000,
      "position_y": 500,
      "config": {
        "rowSelector": "[data-uscraper-comment-row='real'], [data-uscraper-comment-row='fallback']",
        "fileName": "tiktok-video-comments-scraper.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "create",
        "color": "bg-[#42be65]",
        "columns": [
          {
            "name": "url",
            "selector": "location.href",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "poster",
            "selector": "(()=>{const m=location.pathname.match(/^\\/@([^/]+)\\/([^/]+)/);return m?JSON.stringify([m[1],m[2]]):'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "tiktoker_nikname",
            "selector": "(()=>{const title=document.querySelector(`meta[property='og:title']`)?.content||document.title||'';return title.replace(/ on TikTok.*/i,'').trim();})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "post_date",
            "selector": "(()=>{const html=document.documentElement.innerHTML;const m=html.match(/createTime[^0-9]{1,20}([0-9]{10})/);if(!m)return '';return new Date(Number(m[1])*1000).toISOString().replace('T',' ').slice(0,19);})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "content",
            "selector": "(()=>{const d=document.querySelector(`meta[name='description']`)?.content||document.querySelector(`meta[property='og:description']`)?.content||'';const m=d.match(/: “([^”]+)”/);return m?m[1]:d;})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "hashtag",
            "selector": "(()=>Array.from(document.querySelectorAll(`a[href*='/tag/']`)).map(a=>(a.innerText||a.textContent||'').trim()).filter(Boolean).join(' '))()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "like_num",
            "selector": "(()=>{const d=document.querySelector(`meta[name='description']`)?.content||'';const m=d.match(/([0-9.]+[KMB]?) Likes/i);return m?m[1]:(document.querySelector('[data-e2e=like-count]')?.innerText||'');})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "comment_num",
            "selector": "(()=>{const d=document.querySelector(`meta[name='description']`)?.content||'';const m=d.match(/([0-9.]+[KMB]?) Comments/i);return m?m[1]:(document.querySelector('[data-e2e=comment-count]')?.innerText||'');})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "views_num",
            "selector": "(()=>document.querySelector('[data-e2e=video-views], strong[class*=ViewCount], span[class*=ViewCount]')?.innerText?.trim()||'')()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "forward_num",
            "selector": "(()=>document.querySelector('[data-e2e=share-count], strong[class*=ShareCount], span[class*=ShareCount]')?.innerText?.trim()||'')()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "bookmark_num",
            "selector": "(()=>document.querySelector('[data-e2e=favorite-count], [data-e2e=undefined-count], strong[class*=FavoriteCount], span[class*=FavoriteCount]')?.innerText?.trim()||'')()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Region",
            "selector": "(()=>{const html=document.documentElement.innerHTML;const m=html.match(/region[^A-Z]{1,20}([A-Z]{2})/);return m?m[1]:'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Post_type",
            "selector": "'Video'",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "covers_url",
            "selector": "(()=>document.querySelector(`meta[property='og:image']`)?.content||document.querySelector('video')?.poster||'')()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "video_duration",
            "selector": "(()=>{const html=document.documentElement.innerHTML;const m=html.match(/duration[^0-9]{1,20}([0-9]{1,5})/);return m?m[1]:'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "video_download",
            "selector": "(()=>document.querySelector('video source')?.src||document.querySelector('video')?.src||'')()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "video_id",
            "selector": "(()=>location.pathname.split('/').filter(Boolean).pop()||'')()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "music_name",
            "selector": "(()=>{const a=document.querySelector(`a[href*='/music/']`);return a?(a.innerText||a.textContent||'').trim():'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "music_author",
            "selector": "(()=>{const title=document.querySelector(`meta[property='og:title']`)?.content||'';return title.replace(/ on TikTok.*/i,'').trim();})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "music_URL",
            "selector": "(()=>document.querySelector(`a[href*='/music/']`)?.href||'')()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "comment_person",
            "selector": "(()=>{if(ROW.getAttribute('data-uscraper-comment-row')==='fallback')return '';const a=ROW.querySelector(`a[href^='/@'], a[href*='tiktok.com/@']`);if(a){return (a.innerText||a.textContent||a.getAttribute('href')||'').trim();}const possible=Array.from(ROW.querySelectorAll('span,p')).map(el=>(el.innerText||el.textContent||'').trim()).filter(Boolean);return possible[0]||'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "comment_text",
            "selector": "(()=>{if(ROW.getAttribute('data-uscraper-comment-row')==='fallback')return 'NO_VISIBLE_COMMENTS_LOADED_TIKTOK_BLOCK_OR_LOGIN_REQUIRED';const el=ROW.querySelector('[data-e2e=\"comment-level-1\"],[data-e2e=\"comment-text\"],p[class*=\"CommentText\"],span[class*=\"CommentText\"]');if(el)return (el.innerText||el.textContent||'').trim();const parts=Array.from(ROW.querySelectorAll('p,span')).map(x=>(x.innerText||x.textContent||'').trim()).filter(Boolean);return parts.length>1?parts[1]:(parts[0]||'');})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "comment_likes",
            "selector": "(()=>{if(ROW.getAttribute('data-uscraper-comment-row')==='fallback')return '';const el=ROW.querySelector('[data-e2e=comment-like-count], strong[class*=LikeCount], span[class*=LikeCount]');return el?(el.innerText||el.textContent||'').trim():'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "reply_num",
            "selector": "(()=>{if(ROW.getAttribute('data-uscraper-comment-row')==='fallback')return '';const t=ROW.innerText||ROW.textContent||'';const m=t.match(/View ([0-9.]+[KMB]?) repl/i)||t.match(/([0-9.]+[KMB]?) repl/i);return m?m[1]:'';})()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "comment_date",
            "selector": "(()=>{if(ROW.getAttribute('data-uscraper-comment-row')==='fallback')return '';const el=ROW.querySelector('time, [data-e2e=comment-time], span[class*=CommentTime], span[class*=Time]');return el?((el.getAttribute('datetime')||el.innerText||el.textContent||'').trim()):'';})()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "end-1",
      "block_type": "output",
      "title": "End",
      "description": "Terminate execution flow",
      "position_x": 3360,
      "position_y": 500,
      "config": {
        "color": "bg-[#8d8d8d]"
      }
    }
  ],
  "connections": [
    {
      "from_block_id": "set-window-size-1",
      "from_connector_id": "right",
      "to_block_id": "navigate-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "navigate-1",
      "from_connector_id": "right",
      "to_block_id": "wait-for-page-load-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-page-load-1",
      "from_connector_id": "right",
      "to_block_id": "inject-javascript-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "inject-javascript-1",
      "from_connector_id": "right",
      "to_block_id": "sleep-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-1",
      "from_connector_id": "right",
      "to_block_id": "wait-for-element-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-element-1",
      "from_connector_id": "right",
      "to_block_id": "inject-javascript-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "inject-javascript-2",
      "from_connector_id": "right",
      "to_block_id": "wait-for-element-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-element-2",
      "from_connector_id": "right",
      "to_block_id": "structured-export-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "structured-export-1",
      "from_connector_id": "right",
      "to_block_id": "end-1",
      "to_connector_id": "left"
    }
  ],
  "canvas_elements": [
    {
      "id": "group-entry",
      "element_type": "group",
      "title": "Entry & Setup",
      "color": "#4589ff",
      "position_x": 48,
      "position_y": 116,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "set-window-size-1"
        ]
      }
    },
    {
      "id": "group-load",
      "element_type": "group",
      "title": "Page Load",
      "color": "#08bdba",
      "position_x": 408,
      "position_y": 116,
      "width": 2480,
      "height": 576,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "sleep-1",
          "wait-for-element-1",
          "wait-for-element-2"
        ]
      }
    },
    {
      "id": "group-interaction",
      "element_type": "group",
      "title": "Interaction",
      "color": "#a56eff",
      "position_x": 1128,
      "position_y": 116,
      "width": 1400,
      "height": 576,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "inject-javascript-1",
          "inject-javascript-2"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 2928,
      "position_y": 396,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "structured-export-1"
        ]
      }
    },
    {
      "id": "group-control",
      "element_type": "group",
      "title": "Control Flow",
      "color": "#8d8d8d",
      "position_x": 3288,
      "position_y": 396,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "end-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Best-effort TikTok video details and comments scraper for a TikTok video URL. Extracts video metadata and one row per verified visible comment when TikTok exposes comments in the browser DOM. TikTok comments are normally infinite-scroll and may be hidden behind login, CAPTCHA, region, or anti-bot controls; in blocked sessions this template exports one fallback metadata row with comment_text explaining that no visible comments loaded. Uses fileMode=create for a clean CSV on each run.",
      "color": "#f1c21b",
      "position_x": 80,
      "position_y": 20,
      "width": 480,
      "height": 160,
      "z_index": 22,
      "data": {}
    },
    {
      "id": "note-block-inject-javascript-1",
      "element_type": "note",
      "title": "Note: Inject JavaScript",
      "content": "Runs custom JavaScript in the page: `(()=>{const words=['Accept','Got it','Close','Allow all','OK'];document.querySelectorAll('button,[ro...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 1400,
      "position_y": 200,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "inject-javascript-1"
      }
    },
    {
      "id": "note-block-inject-javascript-2",
      "element_type": "note",
      "title": "Note: Inject JavaScript",
      "content": "Runs custom JavaScript in the page: `(()=>{document.querySelectorAll('[data-uscraper-comment-row]').forEach(el=>el.removeAttribute('data-...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 2480,
      "position_y": 480,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "inject-javascript-2"
      }
    },
    {
      "id": "note-block-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Structured export with JS columns (url, poster, tiktoker_nikname, post_date, content). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 3200,
      "position_y": 480,
      "width": 340,
      "height": 130,
      "z_index": 22,
      "data": {
        "block_id": "structured-export-1"
      }
    }
  ]
}