Advertisement
Shuraken007

kakao_saver.js

Mar 25th, 2025 (edited)
466
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Kakao Saver
  3. // @namespace    http://tampermonkey.net/
  4. // @version      2025-03-25
  5. // @description  try to take over the world!
  6. // @author       You
  7. // @match        https://page.kakao.com/content/*/viewer/*
  8. // @icon         https://www.google.com/s2/favicons?sz=64&domain=kakao.com
  9. // @grant        none
  10. // ==/UserScript==
  11.  
  12. (function () {
  13.    'use strict';
  14.  
  15.    async function delay(ms) {
  16.       return new Promise(resolve => setTimeout(resolve, ms))
  17.    }
  18.  
  19.    function is_cover() {
  20.       let root = [...document.querySelectorAll('div')].filter(x => x.shadowRoot)
  21.       if (root.length === 0) return null
  22.       let cover = root[0].shadowRoot.firstChild.querySelector('div.cover')
  23.       if (!cover) return false
  24.       return true
  25.    }
  26.  
  27.    function get_next_btn() {
  28.       let next_btn_arr = [...document.querySelectorAll('div.cursor-pointer')].filter(x => x.firstChild.alt === "다음")
  29.       if (next_btn_arr.length === 0) return null
  30.       return next_btn_arr[0]
  31.    }
  32.  
  33.    function get_next_chapter_btn() {
  34.       return document.querySelector('div[data-test="viewer-navbar-next-button"]')
  35.    }
  36.  
  37.    function get_paragraphs() {
  38.       let root = [...document.querySelectorAll('div')].filter(x => x.shadowRoot)
  39.       if (root.length === 0) return null
  40.       let paragraphs = [...root[0].shadowRoot.firstChild.getElementsByTagName('p')]
  41.       if (paragraphs.length === 0) return null
  42.       return paragraphs
  43.    }
  44.  
  45.    function allow_copy() {
  46.       let root = [...document.querySelectorAll('div')].filter(x => x.shadowRoot)
  47.       if (root.length === 0) return null
  48.       root[0].shadowRoot.firstChild.style.userSelect = 'text'
  49.    }
  50.  
  51.    function get_book_chapter() {
  52.       let path_arr = window.location.href.split('/')
  53.       let chapter = path_arr.pop()
  54.       path_arr.pop()
  55.       let book = path_arr.pop()
  56.       return [book, chapter]
  57.    }
  58.  
  59.    function save_paragraphs(paragraphs) {
  60.       let [book, chapter] = get_book_chapter()
  61.       let storage = localStorage.getItem(`kakao_saver_${book}`) || '{}'
  62.       storage = JSON.parse(storage)
  63.       storage[chapter] = paragraphs.map(x => x.textContent).join('\n')
  64.       localStorage.setItem(`kakao_saver_${book}`, JSON.stringify(storage))
  65.       console.log(`saved ${paragraphs.length} paragraphs for ${chapter} chapter`)
  66.    }
  67.  
  68.    function save() {
  69.       let [book, _] = get_book_chapter()
  70.       let storage = localStorage.getItem(`kakao_saver_${book}`) || '{}'
  71.       storage = JSON.parse(storage)
  72.       let sorted_pairs = Object.entries(storage).sort(([, a], [, b]) => Number(b) - Number(a))
  73.       if (sorted_pairs.length === 0) {
  74.          console.log("nothing to print, no chapters")
  75.          return
  76.       }
  77.       let chapters = []
  78.       let chapter_rel_number = 1
  79.       for (let [chapter_abs_number, chapter] of sorted_pairs) {
  80.          chapters.push(`Глава ${chapter_rel_number} | ${chapter_abs_number}\n\n`)
  81.          chapters.push(chapter)
  82.          chapters.push("\n\n=======================\n\n")
  83.          chapter_rel_number++;
  84.       }
  85.       save_to_file(chapters, book, sorted_pairs.length)
  86.       console.log(`printed ${sorted_pairs.length} chapters`)
  87.       console.log(`first started with ${sorted_pairs[0][1].substring(0, 10)}`)
  88.       console.log(`last ended with ${sorted_pairs[sorted_pairs.length - 1][1].slice(-10, -1)}`)
  89.    }
  90.  
  91.    function save_to_file(str_arr, book, length) {
  92.       const blob = new Blob(str_arr, { type: 'text/plain;charset=utf-8' });
  93.       const link = document.createElement('a');
  94.       link.href = URL.createObjectURL(blob);
  95.       link.download = `kakao-${book}_book-${length}_chapters-${Date.now()}.txt`;
  96.       document.body.appendChild(link);
  97.       link.click();
  98.       document.body.removeChild(link);
  99.    }
  100.  
  101.    let states = {
  102.       cover: "cover",
  103.       text: "text",
  104.       next: "next",
  105.    }
  106.  
  107.    async function auto() {
  108.       window.navigation.removeEventListener("navigate", run)
  109.       let state = states.cover
  110.       let start = Date.now()
  111.       let last_change = Date.now()
  112.       const max_wait_sec = 30
  113.       while (true) {
  114.          switch (state) {
  115.             case states.cover:
  116.                if (!is_cover()) break
  117.                let btn = get_next_btn()
  118.                if (!btn) break
  119.                btn.click()
  120.                state = states.text
  121.                last_change = Date.now()
  122.                break;
  123.             case states.text:
  124.                if (is_cover()) {
  125.                   state = states.cover
  126.                   break
  127.                }
  128.                let paragraphs = get_paragraphs()
  129.                if (!paragraphs) break
  130.                save_paragraphs(paragraphs)
  131.                state = states.next
  132.                last_change = Date.now()
  133.                break;
  134.             case states.next:
  135.                let next_chapter_btn = get_next_chapter_btn()
  136.                if (!next_chapter_btn) break
  137.                next_chapter_btn.click()
  138.                state = states.cover
  139.                last_change = Date.now()
  140.                break;
  141.          }
  142.          await delay(200)
  143.          if (Date.now() - last_change > max_wait_sec * 1000)
  144.             break
  145.       }
  146.       let total_sec = Math.ceil((Date.now() - start) / 1000) - max_wait_sec
  147.       console.log(`loaded in ${total_sec} sec`)
  148.    }
  149.  
  150.    async function run() {
  151.       await delay(2000) // avoid save prev. chapter data
  152.       let paragraphs;
  153.       while (!paragraphs || is_cover()) {
  154.          paragraphs = get_paragraphs()
  155.          await delay(200)
  156.       }
  157.       save_paragraphs(paragraphs)
  158.       allow_copy()
  159.    }
  160.  
  161.    run()
  162.    window.navigation.addEventListener("navigate", run)
  163.  
  164.    // ручное управление
  165.    Object.assign(window, {
  166.       kk_save: save,
  167.       kk_auto: auto,
  168.    });
  169. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement