Greasy Fork

Greasy Fork is available in English.

目录内添加条目增强

为 bangumi 增加在目录内搜索条目并添加的功能,添加后跳转至对应位置,兼容“目录批量添加与编辑”

当前为 2024-11-16 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         目录内添加条目增强
// @namespace    https://bgm.tv/group/topic/409246
// @version      0.1.1
// @description  为 bangumi 增加在目录内搜索条目并添加的功能,添加后跳转至对应位置,兼容“目录批量添加与编辑”
// @author       mmm
// @include      http*://bgm.tv/index/*
// @include      http*://chii.in/index/*
// @include      http*://bangumi.tv/index/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=bgm.tv
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const box = document.querySelector('#newIndexRelatedForm');

    const input = box.querySelector('.inputtext');
    input.style.position = 'sticky';
    input.style.top = 0;

    const result = document.createElement('div');
    result.classList.add('subjectListWrapper');
    box.append(result);

    const btn = makeBtn();
    btn.onclick = async () => {
        await btnHandler(input, result, input);
        document.querySelector('#TB_ajaxContent').style.height = '250px';
        document.querySelector('#TB_window').style.height = 'unset';
    }
    box.querySelector('#submitBtnO').append(btn);

    function makeBtn() {
        const btn = document.createElement('a');
        btn.classList.add('fancyBtn');
        btn.href = 'javascript:;';
        btn.innerText = '搜索';
        return btn;
    }

    async function btnHandler(input, result, target, append) {
        const keyword = input.value;
        if (keyword === '') return;
        result.innerText = '搜索中……';
        const list = await search(keyword);
        renderList(list, result, target, append);
    }

    async function search(keyword) {
        try {
            const response = await fetch(`https://api.bgm.tv/search/subject/${encodeURI(keyword)}`);
            if (!response.ok) throw new Error('API request failed');
            let { list } = await response.json();
            return list;
        } catch (error) {
            console.error('目录内搜索条目并添加: Error fetching subject info from API: ', error);
            result.innerText = '搜索错误,请重试!';
        }
    }

    function renderList(list, container, target, append=false) {
        const html = `<ul id="subjectList" class="subjectList ajaxSubjectList">
        ${ list.reduce((m, { id, url, type, images, name, name_cn }) => {
            type = ['书籍', '动画', '音乐', '游戏', '', '三次元'][type - 1];
            const { grid } = images;
            m += `<li class="clearit">
                    <a href="${url}" class="avatar h">
                      ${grid ? `<img src="${grid}" class="avatar ll">` : ''}
                    </a>
                    <div class="inner">
                      <small class="grey rr">${type}</small>
                      <p><a href="${url}" class="avatar h">${name}</a></p>
                      <small class="tip">${name_cn}</small>
                    </div>
                  </li>`;
            return m;
        }, '') }
        </ul>`;
        container.innerHTML = html;
        container.querySelectorAll('a').forEach(a => {
            a.addEventListener('click', e => {
                e.preventDefault();
                if (append) {
                    target.value += e.target.href + '\n';
                } else {
                    target.value = e.target.href;
                }
            })
        });
    }

    // 兼容“目录批量添加与编辑”(https://bgm.tv/dev/app/1037)
    const div2HTML = `<div style="width: 30%" id="indexSearchNew">
      <input class="inputtext" type="text"></input>
      <a class="fancyBtn" href="javascript:;">搜索</a>
      <div class="subjectListWrapper" style="height: 200px; overflow-y: scroll"></div>
    </div>`;
    const observer = monitorElement('.bibeBox', elem => {
        const container = document.createElement('div');
        container.style.display = 'flex';
        container.style.justifyContent = 'space-evenly';
        container.style.height = '300px';
        container.style.padding = '5px';
        elem.previousSibling.after(container);
        elem.parentNode.style.marginTop = '-150px';
        const div1 = document.createElement('div');
        div1.style.width = '60%';
        div1.append(elem, document.querySelector('#submit_list'));
        container.append(div1);
        container.insertAdjacentHTML('beforeend', div2HTML);
        const div2 = document.querySelector('#indexSearchNew');
        div2.querySelector('.fancyBtn').onclick = async () => {
            await btnHandler(div2.querySelector('input'), div2.querySelector('.subjectListWrapper'), elem.querySelector('textarea'), true);
        }
    });

    // Microsoft Copilot start
    function monitorElement(selector, callback) {
        const targetNode = document.body; // 监视整个文档的变化
        const config = { childList: true, subtree: true }; // 配置监视选项

        const observer = new MutationObserver((mutationsList, observer) => {
            for (let mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    const addedNodes = Array.from(mutation.addedNodes);
                    addedNodes.forEach(node => {
                        if (node.matches && node.matches(selector)) {
                            callback(node);
                        } else if (node.querySelectorAll) {
                            const matchingElements = node.querySelectorAll(selector);
                            matchingElements.forEach(matchingNode => callback(matchingNode));
                        }
                    });
                }
            }
        });

        observer.observe(targetNode, config);

        return observer; // 返回观察者实例,以便在需要时断开观察
    }
    // end

    // 添加后跳转
    const lastHref = sessionStorage.getItem('incheijs_indexsearch');
    if (lastHref) {
        const addedElem = document.querySelector(`a[href*="${lastHref}"]`);
        console.log(lastHref, addedElem)
        addedElem?.scrollIntoView({ behavior: 'smooth' });
        sessionStorage.removeItem('incheijs_indexsearch');
    }
    box.querySelectorAll('.inputBtn').forEach(btn => {
        btn.addEventListener('click', () => {
            const id = input.value.split('/').at(-1);
            if (!id) return;
            const type = ['subject', 'character', 'person', 'ep'][document.querySelector('.switchTab.focus').id[4]];
            sessionStorage.setItem('incheijs_indexsearch', `${type}/${id}`);
        });
    });

})();