// ==UserScript==
// @name Lofter 网页版查看合集
// @license GPLv3
// @namespace http://tampermonkey.net/
// @version 0.2
// @description 在 Lofter 网页版查看作者的合集内容
// @author SrakhiuMeow
// @match *://*.lofter.com/
// @exclude *://www.lofter.com/
// @grant GM.xmlHttpRequest
// @connect api.lofter.com
// @run-at document-start
// @downloadURL https://update.greasyfork.icu/scripts/532099/Lofter%20%E7%BD%91%E9%A1%B5%E7%89%88%E6%9F%A5%E7%9C%8B%E5%90%88%E9%9B%86.user.js
// @updateURL https://update.greasyfork.icu/scripts/532099/Lofter%20%E7%BD%91%E9%A1%B5%E7%89%88%E6%9F%A5%E7%9C%8B%E5%90%88%E9%9B%86.meta.js
// ==/UserScript==
(function () {
'use strict';
const authkey = getCookie("LOFTER-PHONE-LOGIN-AUTH");
const blogdomain = window.location.hostname;
const publisher = blogdomain.split('.')[0];
var doc = document;
function subscribe(authkey, collectionId, mode = true) {
// 订阅合集
// mode = true 订阅, mode = false 取消订阅
const url = new URL("https://api.lofter.com/v2.0/subscribeCollection.api");
const params = {
'method': mode ? 'subscribe' : 'unSubscribe',
// 'offset': offset,
// 'limit': limit,
// 'order': 1,
// 'collectionid': collectionId,
'collectionId': collectionId,
'product': 'lofter-android-7.6.12'
};
Object.keys(params).forEach(key =>
url.searchParams.append(key, params[key])
);
return new Promise((resolve, reject) => {
GM.xmlHttpRequest({
method: "GET",
url: url.toString(),
headers: {
'Accept-Encoding': "br,gzip",
'content-type': "application/x-www-form-urlencoded; charset=utf-8",
'lofter-phone-login-auth': authkey,
},
onload: function (response) {
try {
// console.log(response);
const data = JSON.parse(response.responseText);
resolve(data.response);
} catch (e) {
reject(e);
}
},
onerror: function (error) {
reject(error);
}
});
});
}
function getCollection(authkey, blogid, blogdomain, offset = 0, limit = 20) {
// 获取用户合集列表
const url = new URL("https://api.lofter.com/v1.1/postCollection.api");
const params = {
'method': 'getCollectionList',
'needViewCount': 1,
// 'blogid': blogid,
'blogdomain': blogdomain,
'product': 'lofter-android-7.6.12'
};
Object.keys(params).forEach(key =>
url.searchParams.append(key, params[key])
);
// console.log(authkey);
return new Promise((resolve, reject) => {
GM.xmlHttpRequest({
method: "GET",
url: url.toString(),
headers: {
'Accept-Encoding': "br,gzip",
'content-type': "application/x-www-form-urlencoded; charset=utf-8",
'lofter-phone-login-auth': authkey,
},
onload: function (response) {
try {
// console.log(response);
const data = JSON.parse(response.responseText);
resolve(data.response);
} catch (e) {
reject(e);
}
},
onerror: function (error) {
reject(error);
}
});
});
}
function getCollectionDetail(authkey, collectionId, offset, limit = 15, order = 1) {
// 获取某一合集详情
const url = new URL("https://api.lofter.com/v1.1/postCollection.api");
const params = {
'method': 'getCollectionDetail',
'product': 'lofter-android-7.6.12',
'offset': offset,
'limit': limit,
'collectionid': collectionId,
'order': order,
};
Object.keys(params).forEach(key =>
url.searchParams.append(key, params[key])
);
// console.log(authkey);
return new Promise((resolve, reject) => {
GM.xmlHttpRequest({
method: "GET",
url: url.toString(),
headers: {
'Accept-Encoding': "br,gzip",
'content-type': "application/x-www-form-urlencoded; charset=utf-8",
'lofter-phone-login-auth': authkey,
},
onload: function (response) {
try {
// console.log(response);
const data = JSON.parse(response.responseText);
resolve(data.response);
} catch (e) {
reject(e);
}
},
onerror: function (error) {
reject(error);
}
});
});
}
function getCookie(name) {
const cookies = document.cookie.split('; ');
for (const cookie of cookies) {
const [key, value] = cookie.split('=');
if (key === name) {
return decodeURIComponent(value); // 解码 Cookie 值
}
}
return null; // 如果未找到 Cookie,返回 null
}
function formatTimestamp(timestamp) {
const date = new Date(timestamp);
const pad = num => num.toString().padStart(2, '0');
// return `${date.getFullYear()}-${pad(date.getMonth()+1)}-${pad(date.getDate())} ` +
// `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
return `${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
}
// const controlFrame = document.getElementById('control_frame');
// subscribe(authkey, '')
// .then(response => {
// console.log("subscribe response:", response);
// })
// .catch(error => {
// console.error("Error subscribing:", error);
// });
function displayCollection(collections) {
// 显示合集列表
const postwrapper = doc.querySelector('div.postwrapper');
// const page = doc.querySelector('div.page');
collections.forEach(collection => {
const block = doc.createElement('div');
block.className = 'block article';
const side = doc.createElement('div');
side.className = 'side';
const main = doc.createElement('div');
main.className = 'main';
block.appendChild(side);
block.appendChild(main);
const content = doc.createElement('div');
content.className = 'content';
const tag = doc.createElement('div');
tag.className = 'tag';
const link = doc.createElement('div');
link.className = 'link';
main.appendChild(content);
main.appendChild(tag);
main.appendChild(link);
const tags = collection.tags.split(',');
tags.forEach(tagg => {
const tagElement = doc.createElement('a');
tagElement.href = "https://www.lofter.com/tag/" + tagg;
tagElement.innerHTML = `● ${tagg}`;
tagElement.target = '_blank';
tag.appendChild(tagElement);
});
const text = doc.createElement('div');
text.className = 'text';
const img = doc.createElement('div');
img.className = 'img';
content.appendChild(text);
side.appendChild(img);
const collectionDetail = `https://www.lofter.com/collection/${publisher}/?op=collectionDetail&collectionId=${collection.id}&sort=0`;
const collectionUrl = `https://www.lofter.com/front/blog/collection/share?collectionId=${collection.id}`;
img.innerHTML = ``;
text.innerHTML = `
${collection.description}
`; link.innerHTML = ` 复制ID ${collection.postCount}篇 ${collection.viewCount}浏览 ${collection.postCollectionHot}热度 ${formatTimestamp(collection.updateTime)}更新 订阅合集 查看详情 `; const copyButton = link.querySelector('a:first-child'); copyButton.addEventListener('click', () => { navigator.clipboard.writeText(collection.id).then(() => { // console.log('已将合集ID复制到剪切板:', collection.id); alert('已将合集ID复制到剪切板:' + collection.id); }).catch(err => { console.error('复制失败:', err); }); }); copyButton.style.cursor = 'pointer'; const subscribeButton = link.querySelector('#subscribe'); subscribeButton.style.cursor = 'pointer'; subscribeButton.addEventListener('click', () => { if (subscribeButton.innerHTML === '订阅合集') { subscribe(authkey, collection.id, true) .then(response => { // console.log("subscribe response:", response); subscribeButton.innerHTML = '取消订阅'; }) .catch(error => { console.error("Error subscribing:", error); }); } else { subscribe(authkey, collection.id, true) .then(response => { // console.log("unsubscribe response:", response); subscribeButton.innerHTML = '订阅合集'; }) .catch(error => { console.error("Error unsubscribing:", error); }); } }); const detailButton = link.querySelector('a:last-child'); const list = doc.createElement('div'); main.appendChild(list); if (collection.postCount === 0) { detailButton.innerHTML = '没有内容了'; detailButton.style.cursor = 'not-allowed'; detailButton.removeEventListener('click', this); detailButton.remove(); return; } const br = doc.createElement('br'); list.appendChild(br); detailOffsets[collection.id] = 0; detailButton.addEventListener('click', () => { getCollectionDetail(authkey, collection.id, detailOffsets[collection.id]) .then(response => { // console.log("collection detail response:", response); subscribeButton.style.display = 'block'; if (response.subscribed) { subscribeButton.innerHTML = '取消订阅'; } else { subscribeButton.innerHTML = '订阅合集'; } // 处理合集详情 displayCollectionDetail(response.items, list); if (detailOffsets[collection.id] >= collection.postCount) { detailButton.innerHTML = '没有更多内容了'; detailButton.style.cursor = 'not-allowed'; const clone = detailButton.cloneNode(true); detailButton.parentNode.replaceChild(clone, detailButton); return; } else { detailButton.innerHTML = '加载更多'; detailButton.style.cursor = 'pointer'; } }) .catch(error => console.error(error)); detailOffsets[collection.id] += 15; }); detailButton.style.cursor = 'pointer'; // postwrapper.insertBefore(block, page); postwrapper.appendChild(block); }); } const detailOffsets = {}; function displayCollectionDetail(items, list) { // 显示合集详情 items.forEach(item => { const link = doc.createElement('a'); link.href = item.post.blogPageUrl; link.target = '_blank'; link.innerHTML = item.post.title == '' ? item.post.noticeLinkTitle : item.post.title; list.appendChild(link); list.innerHTML += '