// ==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)
}
})
}
})()