Greasy Fork

Greasy Fork is available in English.

Mananelo/Mangakakalot/Manganato/Manga4life Bookmarks Export

Writes Mangakakalot, Manganelo, Manganato Bookmarks (name and visited number) to "manga_bookmarks.txt" on "Export Bookmarks" button click

当前为 2025-03-03 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Mananelo/Mangakakalot/Manganato/Manga4life Bookmarks Export
// @namespace    http://smoondev.com/
// @version      2.30
// @description  Writes Mangakakalot, Manganelo, Manganato Bookmarks (name and visited number) to "manga_bookmarks.txt" on "Export Bookmarks" button click
// @author       Shawn Moon
// @match        https://*.mangakakalot.gg/bookmark*
// @match        https://*.nelomanga.com/bookmark*
// @match        https://*.natomanga.com/bookmark*
// @match        https://*.manganato.gg/bookmark*
// @match        https://mangakakalot.fun/user/bookmarks
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js
// ==/UserScript==

(function () {
  'use strict'

    function addBookarkStyles(css) {
    const head = document.head || document.getElementsByTagName('head')[0]
    if (!head) return
    const style = document.createElement('style')
    style.type = 'text/css'
    style.innerHTML = css
    head.appendChild(style)
  }

  addBookarkStyles(`
#export_container_nato, #export_container_kakalot, #export_container_m4l {
  color: #000;
  cursor: pointer;
  float: right;
}

#export_container_fun {
  display: inline-flex;
  vertical-align: bottom;
  align-items: baseline;
  margin-top: 20px;
  text-align: right;
  float: right;
  margin-right: 20px;
}

#export_container_kakalot {
  margin-right: 10px;
}

#export_nato:hover, #export_kakalot:hover, #export_m4l:hover {
  background-color: #b6e4e3;
  color: #000;
  cursor: pointer;
}

#export_nato, #export_kakalot, #export_m4l {
  border-radius: 5px;
  text-decoration: none;
  color: #fff;
  background-color: #76cdcb;
  border: none;
  font-weight: 600;
}

#export_nato, #export_kakalot {
  padding: 4px 8px;
}

#export_m4l {
  padding: 1px 12px;
  font-size: 16.5px;
}

#export_fun {
  color: #f05759;
  background-color: #fff;
  border: 1px solid #f05759;
  display: inline-block;
  margin-bottom: 0;
  font-weight: 400;
  text-align: center;
  touch-action: manipulation;
  cursor: pointer;
  white-space: nowrap;
  padding: 6px 12px;
  border-radius: 0;
  user-select: none;
  transition: all .2s ease-in-out;
}

#export_fun:hover {
  color: #fff;
  background-color: #f05759;
}

#inclURL_nato, #inclURL_kakalot, #inclURL_fun, #inclURL_m4l {
  margin-left: 10px;
}

.inclURL_kakalot, .inclURL_m4l {
  color: #ffffff;
}

.inclURL_m4l {
  color: #ffffff;
  font-size: 15px;
  font-weight: 500;
}

.inclURL_fun {
  font-weight:normal;
}

#temp_data {
  position: absolute;
  top: -9999px;
  left: -9999px;
}
`)

  let pageI,
    bmTag,
    bmTitle,
    lastViewed,
    btnContainer,
    exportButtonID,
    inclURL,
    bookmarkedTitles = '',
    exportContainer,
    pageCount = 0,
    domain = window.location.hostname,
    tld = domain.replace('www.', ''),
    bmLabel = 'Bookmarks'

  let mangagakalotDomains = [
    'mangakakalot.gg',
    'nelomanga.com',
    'natomanga.com',
    'manganato.gg'
  ]

  if (mangagakalotDomains.includes(tld)) {
    pageI = '.group-page a'
    bmTag = '.user-bookmark-item-right'
    bmTitle = '.bm-title'
    lastViewed = 'span:nth-of-type(2) a'
    btnContainer = '.breadcrumbs p'
    inclURL = 'inclURL_kakalot'

    let pageElList = document.querySelectorAll(pageI)
    if (pageElList.length > 0) {
      let lastText = pageElList[pageElList.length - 1].textContent
      pageCount = parseInt(lastText.replace(/\D+/g, ''), 10) || 0
    }
    exportButtonID = 'export_kakalot'
    exportContainer = 'export_container_kakalot'
  } else if (domain.indexOf('mangakakalot.fun') !== -1) {
    bmTag = '.list-group-item'
    bmTitle = '.media-heading a'
    lastViewed = '.media-body p a'
    btnContainer = '.container-fluid:first-child div:last-child'
    inclURL = 'inclURL_fun'
    exportButtonID = 'export_fun'
    exportContainer = 'export_container_fun'
  }

  const waitFor = delay => new Promise(resolve => setTimeout(resolve, delay))

  const saveFile = saveData => {
    const fileData = new Blob([saveData], { type: 'application/octet-stream' })
    saveAs(fileData, 'manga_bookmarks.txt')
    const btn = document.getElementById(exportButtonID)
    if (btn) {
      btn.innerHTML = `Export ${bmLabel}`
      btn.disabled = false
    }
  }

  const deleteTemp = () => {
    const tempData = document.getElementById('temp_data')
    if (tempData) {
      tempData.remove()
    }
  }

  const getCookie = (name = 'user_acc') => {
    let returnVal = false
    const value = `; ${document.cookie}`
    try {
      const parts = value.split(`; ${name}=`)
      if (parts.length === 2) {
        let user = parts.pop().split(';').shift()
        if (name === 'user_acc') {
          user = JSON.parse(decodeURIComponent(user))
          returnVal = user.user_data
        }
      }
    } catch (e) {
      returnVal = false
    }
    return returnVal
  }

  const getBMs = async (userCookie, currentPage = 1) => {
    const urlencoded = new URLSearchParams()
    urlencoded.append('out_type', 'json')
    urlencoded.append('bm_source', 'manganato')
    urlencoded.append('bm_page', currentPage)
    urlencoded.append('user_data', userCookie)

    try {
      const response = await fetch('https://user.mngusr.com/bookmark_get_list_full', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: urlencoded,
        redirect: 'follow'
      })
      return await response.json()
    } catch (error) {
      console.log('ExportError', error)
    }
  }

  const getFunBMs = url => {
    const bmItems = document.querySelectorAll(bmTag)
    for (let i = 0; i < bmItems.length; i++) {
      const titleElem = bmItems[i].querySelector(bmTitle)
      const title = titleElem ? titleElem.textContent : ''
      const lastViewedElem = bmItems[i].querySelector('.media-body p a')
      const lastViewedText = lastViewedElem ? lastViewedElem.textContent.trim() : 'None'
      const linkText = (url && lastViewedElem && lastViewedElem.href) ? `- ${lastViewedElem.href}` : ''
      bookmarkedTitles += `${title} || Viewed: ${lastViewedText} ${linkText} \n`
    }
    saveFile(bookmarkedTitles)
  }

  if (!document.getElementById(exportContainer)) {
    const btnContElem = document.querySelector(btnContainer)
    if (btnContElem) {
      btnContElem.insertAdjacentHTML(
        'beforeend',
        `<div id='${exportContainer}'>
           <button id='${exportButtonID}'>Export ${bmLabel}</button>
           <input type="checkbox" id="${inclURL}">
           <span style="margin-left: 5px;">
             <label for="${inclURL}" class='${inclURL}'>Add URL</label>
           </span>
         </div>`
      )
    }
  }

  const getBookmarks = (url, bookmarkHeader) => {
    deleteTemp()
    document.body.insertAdjacentHTML('beforeend', "<div id='temp_data'></div>")
    let bookmarkedContent = bookmarkHeader
    const tempData = document.getElementById('temp_data')
    const fetches = []
    for (let i = 0; i < pageCount; i++) {
      const pageId = `page${i + 1}`
      const pageDiv = document.createElement('div')
      pageDiv.id = pageId
      tempData.appendChild(pageDiv)
      const fetchPromise = fetch(`https://${domain}/bookmark?page=${i + 1}`)
        .then(response => response.text())
        .then(htmlText => {
          const parser = new DOMParser()
          const doc = parser.parseFromString(htmlText, 'text/html')
          const items = doc.querySelectorAll(bmTag)
          pageDiv.innerHTML = Array.from(items)
            .map(item => item.outerHTML)
            .join('')
        })
        .catch(error => console.error('ExportError', error))
      fetches.push(fetchPromise)
    }
    Promise.all(fetches).then(() => {
      const bmItems = document.querySelectorAll(`#temp_data ${bmTag}`)
      bmItems.forEach(item => {
        const titleElem = item.querySelector(bmTitle)
        if (titleElem && titleElem.textContent) {
          const lastViewedElem = item.querySelector(lastViewed)
          const viewedText = lastViewedElem
            ? lastViewedElem.textContent.trim()
            : 'Not Found'
          const linkPart =
            url && lastViewedElem && lastViewedElem.href
              ? `- ${lastViewedElem.href}`
              : ''
          bookmarkedContent += `${titleElem.textContent.trim()} ||  Viewed: ${viewedText} ${linkPart} \n`
        }
      })
      saveFile(bookmarkedContent)
      deleteTemp()
    })
  }

  const exportButton = document.getElementById(exportButtonID)
  if (exportButton) {
    exportButton.addEventListener('click', async function () {
      let bookmarkHeader = `===========================\n${domain} ${bmLabel}\n===========================\n`
      bookmarkedTitles = bookmarkHeader
      const inclURLCheck = document.getElementById(inclURL).checked

      if (mangagakalotDomains.includes(tld)) {
        exportButton.innerHTML = 'Generating File...'
        exportButton.disabled = true
        getBookmarks(inclURLCheck, bookmarkedTitles)
      } else if (domain === 'mangakakalot.fun') {
        getFunBMs(inclURLCheck)
      }
    })
  }
})()