// ==UserScript==
// @name Pixiv Collection Plus
// @namespace niaier pixiv collection
// @license MIT
// @version 0.1
// @description pixiv收藏拓展
// @author niaier
// @match *://www.pixiv.net/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant GM_addStyle
// @grant GM_getResourceText
// @grant GM_xmlhttpRequest
// @grant GM_download
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_setClipboard
// @grant unsafeWindow
// @grant window.close
// @grant window.focus
// @grant window.onurlchange
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.3/cpexcel.js
// @require https://cdnjs.cloudflare.com/ajax/libs/dexie/3.2.1/dexie.min.js
// @resource css https://unpkg.com/element-ui/lib/theme-chalk/index.css
// @require https://cdn.jsdelivr.net/npm/vue@2.6.14
// @require https://unpkg.com/element-ui/lib/index.js
// @require https://cdn.jsdelivr.net/npm/echarts@5.2.2/dist/echarts.min.js
// @connect *
// ==/UserScript==
window.onload = function () {
// 替换element字体源
const styleText = GM_getResourceText("css").replace('fonts/element-icons.woff', 'https://unpkg.com/element-ui@2.13.0/lib/theme-chalk/fonts/element-icons.woff').replace('fonts/element-icons.ttf', 'https://unpkg.com/element-ui@2.13.0/lib/theme-chalk/fonts/element-icons.ttf')
// GM_addStyle(GM_getResourceText("css"));
GM_addStyle(styleText);
class Database extends Dexie {
constructor() {
super('pixiv');
this.version('1.0').stores({
production: '++id,&url,title,custom_category,tag',
collection: '++id,url,title,custom_category,tag,collection_list,collection_list_id',
collection_class_list: '++id,list_name'
});
this.collection = this.table('collection');
this.collectionClassList = this.table('collection_class_list');
}
// 当前有关收藏
async getCurCollection (url) {
let curCollection = []
curCollection = await this.collection.where('url').equals(url).toArray()
return curCollection
}
// 添加收藏
async addToCollection (data) {
return await this.collection.add(data)
}
// 删除收藏
async deleteCollection (id, url) {
console.log(id, url);
return await this.collection
.where({ 'collection_list_id': id, url }).delete()
}
// 获取收藏列表
async getCollectionList () {
let collectionClassList = []
collectionClassList = await this.collection_class_list.orderBy('id').toArray();
return collectionClassList
}
// 添加收藏列表
async addCollectionList (data) {
return await this.collection_class_list.add(data)
}
//删除收藏列表
async deleteCollectionList (id) {
await this.collection_class_list.where('id').equals(id).delete()
await this.collection.where('collection_list_id').equals(id).delete()
return
}
//获取收藏列表下的藏品
async getCollectionListContent (collection_list_id) {
// 参数 收藏列表id collection_list_id
let collectionListContent = []
collectionListContent = await this.collection.where('collection_list_id').equals(collection_list_id).toArray()
return collectionListContent
}
// 获取自定义标签
async getCustomCategory (url) {
let custom_category = []
const production = await this.production.where('url').equals(url).toArray()
custom_category = production[0] ? production[0].custom_category : []
console.log(custom_category);
return custom_category
}
// 添加自定义标签
// 添加作品
async putProduction (data) {
return await this.production.put(data)
}
//获取作品
async getProduction (url) {
let arr = []
arr = await this.production.where('url').equals(url).toArray()
return arr[0]
}
// 修改自定义标签
async putCustomCategory (url, custom_category) {
console.log(url, custom_category);
await this.production.where('url').equals(url).modify({
custom_category
})
}
async getAllData () {
const production = await this.production.toArray()
const collection = await this.collection.toArray()
const collection_class_list = await this.collection_class_list.toArray()
return { production, collection, collection_class_list }
}
async importAllData (data) {
// 先清空
// await this.production.delete()
// await this.collection.delete()
// await this.collection_class_list.delete()
await this.production.bulkPut(data.production)
await this.collection.bulkPut(data.collection)
await this.collection_class_list.bulkPut(data.collection_class_list)
}
}
const viewCollection =
`
<div id="viewCollection">
<el-button type="primary" size="small" :style="{
'margin-left': '10px'
}" @click="showCollection">查看收藏</el-button>
<el-dialog title="查看收藏列表" :visible.sync="collectionVisible" width="60%" center append-to-body :style="{
height: '80%',
}">
<el-container>
<el-aside width="200px">
<h5>收藏列表</h5>
<el-menu default-active="0" class="el-menu-vertical-demo">
<el-menu-item :index="index" :key="item.id" v-for="(item, index) in collectionList">
<i class="el-icon-menu"></i>
<span slot="title" @click="getCollectionListContent(item)">{{ item.list_name }}</span>
</el-menu-item>
</el-menu>
</el-aside>
<el-main>
<div>
<div
:style="{
'margin-bottom': '10px'
}"
>
<h5>收藏展示</h5>
<el-button type="primary" @click="exportJson" size="small">导出json</el-button>
<input type="file" name="file" id="importJson" v-show="false" @change="handleImport">
<el-button size="small" type="primary" @click="importJson" :style="{
'margin-left': '10px',
}">导入Json</el-button>
</div>
<el-row :gutter="10">
<el-col :span="8" :key="item.id" v-for="item in collectionListContent">
<div :style="{ height: 250 }">
<div class="pic" :style="{ 'text-align': 'center' }">
<img :src="item.preview_url" alt="" height="184" />
</div>
<div class="title" :style="{
'display':'flex',
'justify-content':'center'
}">
<h5 :style="{width:'130px'}">
<el-link :href="item.url" target="_blank">
{{ item.title }}
</el-link>
</h5>
</div>
</el-col>
</el-row>
</div>
</el-main>
</el-container>
</el-dialog>
</div>
`
const addCollection =
` <div id="addCollection">
<el-button
type="primary"
size="small"
@click="showCollectionList"
:style="{
'margin-right': '10px'
}"
>
添加到收藏</el-button
>
<el-dialog
title="收藏列表"
:visible.sync="collectionListVisible"
width="30%"
center
append-to-body
>
<div class="currentTag">
<h5
:style="{
'margin-bottom': '10px',
}"
>
当前标签:
</h5>
<span>
<el-tag
v-for="item in tag"
:key="item"
type="info"
size="mimi"
:style="{
'margin-left': '10px',
'margin-bottom': '10px',
}"
>
{{ item.originTag + " | " + item.translatedTag }}
</el-tag>
</span>
</div>
<div
class="customTag"
:style="{
'margin-top': '10px',
'margin-bottom': '10px',
}"
>
<h5
:style="{
'margin-bottom': '10px',
}"
>
自定义标签:
</h5>
<span>
<el-tag
:key="item"
size="mimi"
v-for="item in custom_category"
closable
:disable-transitions="false"
@close="deleteCustomCategory(item)"
:style="{
'margin-left': '10px',
'margin-button': '10px',
}"
>
{{ item.originTag + " | " + item.translatedTag }}
</el-tag>
<el-input
class="input-new-tag"
v-if="customCategoryInputVisible"
v-model="selfCategory"
ref="saveTagInput"
size="small"
@keyup.enter.native="addSelfCategory"
>
</el-input>
<el-button
v-else
class="button-new-tag"
size="small"
@click="showSelfCategoryInput"
>+ New Tag</el-button
>
</span>
</div>
<el-input
placeholder="新建收藏列表"
v-model="inputListName"
class="input-with-select"
:style="{
'margin-top': '10px',
'margin-button': '10px',
}"
>
<el-button
slot="append"
@click="addCollectionList"
size="small"
type="primary"
icon="el-icon-plus"
></el-button>
</el-input>
<div class="collectionList">
<el-row>
<el-col
:span="24"
v-for="(item, index) in collectionList"
:key="index"
:style="{
'display':'flex',
'justify-content':'space-between',
'margin-top':'10px'
}"
>
<el-checkbox
v-model="item.checked"
@change="changeChecked(index, item)"
>{{ item.list_name }}</el-checkbox
>
<i
class="el-icon-delete"
@click="deleteCollectionList(item)"
:style="{
cursor:'pointer'
}"
></i>
</el-col>
</el-row>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="collectionListVisible = false"
>取 消</el-button
>
<el-button
type="primary"
@click="collectionListVisible = false"
>确 定</el-button
>
</span>
</el-dialog>
</div>`
$('#root > div:nth-child(2) > div.sc-12xjnzy-0.dIGjtZ > div:nth-child(1) > div:nth-child(1) > div > div.sc-4nj1pr-2.fDmigA').append(viewCollection)
$('#root > div:nth-child(2) > div.sc-1nr368f-0.beQeCv > div > div.sc-1nr368f-3.dkdRNk > main > section > div.sc-171jvz-0.gcrJTU > div > div.sc-rsntqo-0.fPeueM > div > div.sc-ye57th-1.lbzRfC > section').append(addCollection)
new Vue({
el: '#viewCollection',
data: () => ({
collectionVisible: false,
collectionList: [],
collectionListContent: []
}),
created () {
this.db = new Database()
},
methods: {
showCollection () {
this.collectionVisible = true
this.getCollectionList()
},
async getCollectionList () {
const collectionList = await this.db.getCollectionList()
this.collectionList = collectionList
},
async getCollectionListContent (item) {
const collection_list_id = item.id
const collectionListContent = await this.db.getCollectionListContent(collection_list_id)
this.collectionListContent = collectionListContent
console.log('collectionListContent', collectionListContent)
},
// 导出
async exportJson () {
const curDate = moment().format('YYYY_MM_hh_mm_ss');
const jsonObj = await this.db.getAllData()
const data = JSON.stringify(jsonObj)
let blob = new Blob([data], { type: 'text/json' })
let a = document.createElement('a')
a.download = `pixiv收藏数据${curDate}.json`
a.href = window.URL.createObjectURL(blob)
document.body.appendChild(a);
a.click()
document.body.removeChild(a);
},
//导入
importJson () {
const input = document.querySelector('#importJson')
input.click()
},
handleImport () {
const that = this
const selectedFile = document.querySelector('#importJson').files[0]
const reader = new FileReader()
reader.readAsText(selectedFile)
let JsonObj = {}
reader.onload = function () {
JsonObj = JSON.parse(this.result)
that.db.importAllData(JsonObj)
}
}
}
})
new Vue({
el: '#addCollection',
data: () => ({
collectionListVisible: false,
inputListName: '',
defaultChecked: false,
collectionList: [],
db: {},
url: window.location.href,
title: '',
tag: [],
selfCategory: '',
custom_category: [],
customCategoryInputVisible: false,
production: {},
previewUrl: ''
}),
created () {
this.db = new Database()
},
mounted () {
this.getTitle()
this.getTag()
this.initData()
this.getPreviewUrl()
},
methods: {
initData () {
this.getProduction()
this.getCollectionList()
this.getCurCollection()
},
showCollectionList () {
this.collectionListVisible = true
this.initData()
},
getTitle () {
const title = document.querySelector("#root > div:nth-child(2) > div.sc-1nr368f-0.beQeCv > div > div.sc-1nr368f-3.dkdRNk > main > section > div.sc-171jvz-0.gcrJTU > div > figcaption > div > div > h1").innerText
this.title = title
console.log(this.title);
},
getTag () {
const tagList = []
const liList = document.querySelectorAll('.sc-pj1a4x-0 .sc-h7k48h-0.lhUcKZ')
liList.forEach(item => {
originTag = item.querySelector('.gtm-new-work-tag-event-click').innerText
translatedTag = item.querySelector('.gtm-new-work-translate-tag-event-click') ? item.querySelector('.gtm-new-work-translate-tag-event-click').innerText : ''
const obj = {
originTag,
translatedTag
}
tagList.push(obj)
})
this.tag = tagList
console.log(this.tag);
},
getCustomCategory () {
console.log("this.production", this.production);
this.custom_category = this.production ? this.production.custom_category : []
},
getPreviewUrl () {
this.previewUrl = document.querySelector("a.gtm-expand-full-size-illust").href
},
async putCustomCategory () {
console.log('custom_category', this.custom_category);
await this.db.putCustomCategory(window.location.href, this.custom_category)
},
showSelfCategoryInput () {
this.customCategoryInputVisible = true
},
addSelfCategory () {
const categoryList = this.selfCategory.split("|")
let originTag = ''
let translatedTag = ''
if (categoryList.length > 1) {
originTag = categoryList[0]
translatedTag = categoryList[1]
} else if (categoryList.length == 1) {
originTag = categoryList[0]
translatedTag = ''
}
const obj = {
originTag,
translatedTag
}
this.custom_category.push(obj)
this.custom_category = Array.from(new Set(this.custom_category))
this.putCustomCategory()
this.customCategoryInputVisible = false
this.initData()
},
deleteCustomCategory (tag) {
this.custom_category.splice(this.custom_category.indexOf(tag), 1);
this.putCustomCategory()
this.initData()
},
changeChecked (index, item) {
if (item.checked == true) {
this.addToCollection(item)
} else {
this.deleteCollection(item.id, window.location.href)
}
},
async getProduction () {
this.production = await this.db.getProduction(window.location.href)
this.getCustomCategory()
this.production = this.production || {}
},
async putProduction () {
const data = {
id: this.production.id,
title: this.title,
url: window.location.href,
tag: this.tag,
custom_category: this.custom_category,
preview_url: this.previewUrl
}
console.log('putProduction', data);
await this.db.putProduction(data)
},
async addToCollection (item) {
const data = {
title: this.title,
url: window.location.href,
tag: this.tag,
custom_category: this.custom_category,
collection_list: item.list_name,
collection_list_id: item.id,
preview_url: this.previewUrl
}
await this.db.addToCollection(data);
this.putProduction()
this.getCurCollection()
},
async deleteCollection (id, url) {
await this.db.deleteCollection(id, url)
this.getCurCollection()
},
// 获取当前作品相关的收藏记录
async getCurCollection () {
const url = window.location.href
const curCollection = await this.db.getCurCollection(url)
this.curCollection = curCollection
this.handleCurChecked()
},
async getCollectionList () {
const collectionList = await this.db.getCollectionList()
this.collectionList = collectionList
},
// 添加列表
async addCollectionList () {
const listName = this.inputListName
const data = {
list_name: listName
}
await this.db.addCollectionList(data)
this.getCollectionList()
},
//删除列表
async deleteCollectionList (item) {
console.log('deleteCollectionList', item.id);
this.db.deleteCollectionList(item.id)
this.initData()
this.getCurCollection()
},
// 处理收藏列表的显示效果
handleCurChecked () {
const arr = this.collectionList.map(item1 => {
this.curCollection.forEach(item2 => {
if (item1.id == item2.collection_list_id) {
item1.checked = true
}
})
return item1
})
console.log(arr);
this.collectionList = arr
},
}
})
}