豆瓣电影同步到Notion

抓取豆瓣电影信息,基于Notion搭建私人电影管理系统

// ==UserScript==
// @name         豆瓣电影同步到Notion
// @namespace    https://greasyfork.org/zh-CN/scripts/464467-%E8%B1%86%E7%93%A3%E8%AF%BB%E4%B9%A6%E5%90%8C%E6%AD%A5%E5%88%B0notion
// @version      1.6
// @description  抓取豆瓣电影信息,基于Notion搭建私人电影管理系统
// @author       @Yanwudong https://twitter.com/yanwudong
// @match        https://movie.douban.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=douban.com
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @license      GNU GPLv3
// ==/UserScript==
(function() {
    'use strict';
        // 添加CSS样式
    GM_addStyle(`
  .toast {
    position: fixed;
    top: 150px;
    right: 50%;
    z-index: 9999;
    opacity: 0;
    transition: opacity 0.2s ease-in-out;
  }
  .toast.show {
    opacity: 1;
  }

  .toast-body {
    background-color: #f2f8f2;
    //border: 1px solid #ccc;
    //border-radius: 3px;
    color:#4f946e;
    padding: 2px;
    padding-left:10px;
    padding-right:10px
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
  }
  #nlog {
    top: 130px;
    right: 50%;
    z-index: 9999;
    opacity: 0;
    transition: opacity 0.2s ease-in-out;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
    background: #fff;
    overflow: hiadden;
    padding: 40px 30px 30px;
    position: fixed;
    display : none;
  }
  #nlog.show {
    opacity: 1;
  }
  .form-group{
    margin-bottom: 10px;
  }
  .form-control{
    border: 1px solid #e4e6e5;
    border-radius: 3px;
    box-sizing: border-box;
    font-size: 13px;
    padding: 10px;
    width: 280px;
  }
  #exampleModalLabel{
    cursor: pointer;
    font-size: 18px;
    line-height: 2;
    text-align: center;
    border-bottom: 2px solid #494949;
    color: #333;
    font-weight: 600;
    margin-bottom:20px;
  }
  #twitter{
    margin-left:30px;
  }
  #saveBtn{
    background-color: #41ac52;
    border: 1px solid #b9dcd0;
    color: #fff;
    cursor: default;
    font-size: 15px;
    font-weight: 600;
    padding: 0;
    width:79%;
  }
  .modal-footer button{
    height:36px;
    width:19%;
    margin-bottom:20px;
  }
  #syncbt {
    display: inline-block;
    margin-left: 10px;
    font-size: 13px;
    color:#4f946e;
    background-color: #f2f8f2;
    padding: 2px;
    padding-left: 8px;
    padding-right: 8px;
    border: 1px solid #b9dcd0;
    border-radius:3px
  }
  #syncbt:hover {
    cursor: pointer;
  }
  #setbt {
    display: inline-block;
    margin-left: 5px;
    font-size: 13px;
    color:#4f946e;
    background-color: #f2f8f2;
    padding: 2px;
    border: 1px solid #b9dcd0;
    border-radius:3px
  }
     `);

// 创建一个 Modal
const modalHtml = `
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Notion Api 设置</h5>
      </div>
      <div class="modal-body">
        <form>
          <div class="form-group">
            <input type="text" class="form-control" id="apiInput" placeholder="token">
          </div>
          <div class="form-group">
            <input type="text" class="form-control" id="pageIdInput" placeholder="页面 ID">
          </div>
        </form>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-primary" id="saveBtn">保存设置</button>
        <button type="button" class="btn btn-secondary" id="closeBtn" data-dismiss="modal">关闭</button>
      </div>
      <div class="modal-bottom">
        <a href="https://seemly-pear-9fc.notion.site/Notion-e0ae1a1d391143abb9ff383730649149" id="refBtn" target="_blank";>操作说明</a>
        <a href="https://twitter.com/yanwudong" id="twitter" target="_blank";>推特</a>
      </div>
    </div>
  </div>
</div>
`;
    //GM_setValue('nToken', nToken);
    //GM_setValue('MdatabaseId', MdatabaseId);
    //GM_setValue('databaseId', databaseId);
    //初始化用户信息
    var nToken = GM_getValue('nToken');
    var pageId = GM_getValue('pageId');
    //电影数据库Id
    var MdatabaseId = GM_getValue('MdatabaseId');
    const notionVersion = '2022-06-28';

    // 创建一个轻提示框元素
    const toast = document.createElement('div');
    toast.id = 'toast';
    toast.className = 'toast';
    toast.innerHTML =`
     <div class="toast-body">
       🎉 同步成功!
     </div>
    `;
    // 将轻提示框添加到页面中
    document.body.appendChild(toast);

    //创建一个授权框,让用户输入ApiKey和PageId
    const nlog = document.createElement('div');
    nlog.id = 'nlog';
    nlog.innerHTML = modalHtml;
    document.body.appendChild(nlog);
    nlog.classList.add('show');

    // 等待页面加载完成后执行
    $(document).ready(function() {
        // 给按钮添加点击事件
        $('#saveBtn').click(function() {
            nToken = $('#apiInput').val();
            pageId = $('#pageIdInput').val();
            GM_setValue('nToken', nToken);
            GM_setValue('pageId', pageId);
            //用户第一次使用先创建数据库
            createDatabase();
            nlog.style.display = 'none';
        });
        $('#closeBtn').click(function(){
            nlog.style.display = 'none';
        });
    });

    //检查用户是否登录
    const checkUserInfo = () =>{
        // 如果用户没有输入过信息,弹出输入框让其输入
        if (!nToken || !pageId || !MdatabaseId ) {
            nlog.style.display = 'block';
        }else{
            syncToNotion();
        }
    }


    //添加同步按钮到页面
    function addButton(){
        const button = document.createElement('button');
        button.innerText = '同步到Notion';
        button.onclick = checkUserInfo;
        const actions = document.querySelector('#content > h1 > .year ');//在电影名称后面加按钮
        //debugger
        actions.insertAdjacentElement('afterend',button);
        button.id = 'syncbt';
    };


    //获取电影信息
    function getMovieInfo(){
        const infos = document.querySelectorAll('#info .pl');
        let movie = {};

        // 创建类型数组
        let type = '';
        let typeList = [];

        debugger
        movie['电影名'] = document.querySelector('#content > h1 > span').innerText;
        movie['封面'] = document.querySelector('#mainpic > a > img').src;
        const grade = document.querySelector('.rating_num');
        if(grade != null){
            movie['评分'] = document.querySelector('.rating_num').innerText;
        }
        movie['豆瓣链接']=window.location.href;
        movie['简介']=document.querySelector('#link-report-intra').innerText;
        debugger
        //循环遍历infos,并把每个元素赋值给info
        for(const info of infos){
            if(info.innerText === '导演'){
                movie['导演'] = info.nextSibling.nextSibling.innerText
            }else if(info.innerText === '编剧'){
                movie['编剧'] = info.nextSibling.nextSibling.innerText
            }else if(info.innerText === '主演'){
                movie['主演'] = info.nextSibling.nextSibling.innerText
            }else if(info.innerText === '上映日期:'){
                movie['上映日期'] = info.nextSibling.nextSibling.innerText
            }else if(info.innerText === '片长:'){
                movie['片长'] = info.nextSibling.nextSibling.innerText
            }else if(info.innerText === '类型:'){
                // 找到所有具有属性 "property" 且属性值为 "v:genre" 的元素
                const genreElements = document.querySelectorAll('span[property="v:genre"]');
                // 遍历每个元素,将其内容添加到数组中
                genreElements.forEach(function(element) {
                typeList.push(element.textContent.trim());
                });
                movie['类型'] = typeList
            }else{
               let prop = info.innerText.substr(0,info.innerText.length-1)
                movie[prop] = info.nextSibling.data
            }
        }
        return movie
    }

    // 同步电影信息到Notion
    function syncToNotion(){
        const movie = getMovieInfo();
        createMovieItem(movie);
    }

    //创建页面电影子数据库database
    async function createDatabase() {
        const body = {
            'parent': { 'type': 'page_id', 'page_id': pageId },
            'title': [
                {
                    'type': 'text',
                    'text': {
                        'content': 'MovieList',
                        'link': null
                    }
                }
            ],
            'icon':{
                'type':'emoji',
                'emoji':'🍿'
            },
            'properties': {
                '电影名': {
                    'title': {}
                },
                '类型':{
                    'multi_select':{
                        'options':[
                                                        {
                                'name':'其他',
                                'color':'default'
                            }
                        ]
                    }
                },
                '地区':{
                    'select':{
                        'options':[
                            {
                                'name':'其他',
                                'color':'default'
                            }
                        ]
                    }
                },
                '状态':{
                    'select':{
                        'options':[
                            {
                                'name':'🌑想看',
                                'color':'purple'
                            },
                            {
                                'name':'🌒在看',
                                'color':'orange'
                            },
                            {
                                'name':'🌕看过',
                                'color':'green'
                            }
                        ]
                    }
                },
                '个人打分':{
                    'select':{
                        'options':[
                            {
                                'name':'⭐️⭐️⭐️⭐️⭐️'
                            },
                            {
                                'name':'⭐️⭐️⭐️⭐️'
                            },
                            {
                                'name':'⭐️⭐️⭐️'
                            },
                            {
                                'name':'⭐️⭐️'
                            },
                            {
                                'name':'⭐️'
                            }
                        ]
                    }
                },
                '导演': {
                    'rich_text': {}
                },
                "创建时间": {
                    "type": "created_time",
                    "created_time": {}
                },
                '主演': {
                    'rich_text': {}
                },
                '简介': {
                    'rich_text': {}
                },
                '上映日期': {
                    'rich_text': {}
                },
                '片长': {
                    'number': {
                        'format': 'number'
                    }
                },
                '豆瓣评分': {
                    'number': {
                        'format': 'number'
                    }
                },
                'IMDb': {
                    'rich_text': {}
                },
                '封面': {
                    'files': {}
                },
                '豆瓣链接': {
                    'url': {}
                },
                '简介': {
                    'rich_text': {}
                },
            }
        };
        const options = {
            headers: {
                Authorization: 'Bearer ' + nToken,
                'Notion-Version': notionVersion,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(body)
        }
        GM_xmlhttpRequest({
            method: 'POST',
            url: 'https://api.notion.com/v1/databases',
            headers: options.headers,
            data: options.body,
            onload: function(response) {
                const res = JSON.parse(response.responseText);
                if (res.object === 'error') {
                    alert(res.message);
                } else {
                    GM_setValue('MdatabaseId',res.id);
                    MdatabaseId = res.id;
                    toast.innerHTML =`
                       <div class="toast-body">
                          🎉 配置成功!
                       </div>
                        `;
                    // 显示轻提示框
                    toast.classList.add('show');

                    // 3秒后隐藏轻提示框
                    setTimeout(() => {
                        toast.classList.remove('show');
                    }, 3000);
                }
            }
        });
    }

    //同步电影
    function createMovieItem(movie) {
        // 构建用于传递给 Notion 数据库的标签数据
        const tagsProperties = movie['类型'].map(tag => {
            return { name: tag };
        });

        const body = {
            'parent': { 'type': 'database_id', 'database_id': MdatabaseId },
            'icon':{
                'type':'emoji',
                'emoji':'🎞️'
            },
            'properties': {
                '电影名': {
                    'type': 'title',
                    'title': [{ 'type': 'text', 'text': { 'content': movie['电影名'] } }]
                },
                '封面': {
                    'files': [
                        {
                            'type': 'external',
                            'name': 'cover',
                            'external': { 'url': movie['封面']}
                        },
                    ]
                },
                'IMDb': {
                    'type': 'rich_text',
                    'rich_text': [{ 'type': 'text', 'text': { 'content': movie['IMDb'] ? movie['IMDb']:'' } }]
                },
                '片长': {
                    'number': parseInt(movie['片长'] ? movie['片长']:'' )
                },
                '状态':{
                    select:{
                        'name':'🌑想看'
                    }
                },
                '上映日期': {
                    'type':'rich_text',
                    'rich_text': [{ 'type': 'text', 'text': { 'content': movie['上映日期'] ? movie['上映日期']:'' } }]
                } ,
                '豆瓣评分': {
                    'number': parseFloat(movie['评分'])
                },
                '导演': {
                    'type': 'rich_text',
                    'rich_text': [{ 'type': 'text', 'text': { 'content': movie['导演'] ? movie['导演']:''} }]
                },
                '主演': {
                    'type': 'rich_text',
                    'rich_text': [{ 'type': 'text', 'text': { 'content': movie['主演'] ? movie['主演']:''} }]
                },
                '简介': {
                    'type': 'rich_text',
                    'rich_text': [{ 'type': 'text', 'text': { 'content': movie['简介'] ? movie['简介']:''} }]
                },

                '地区':{
                    select:{
                        'name':movie['制片国家/地区'] ? movie['制片国家/地区']:''
                    }
                },
                '类型': {
                      "multi_select":tagsProperties
                },

                '封面': {
                    'files': [
                        {
                            'type': 'external',
                            'name': 'cover',
                            'external': { 'url': movie['封面']}
                        },
                    ]
                },
                '豆瓣链接':{
                    'type':'url',
                    'url':movie['豆瓣链接']
                }

            },
        };
        //传参数
        const options = {
            headers: {
                Authorization: 'Bearer ' + nToken,
                'Notion-Version': notionVersion,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(body)
        }
        GM_xmlhttpRequest({
            method: 'POST',
            url: 'https://api.notion.com/v1/pages',
            headers: options.headers,
            data: options.body,
            onload: function(response) {
                debugger
                const res = JSON.parse(response.responseText);
                if (res.object === 'error') {
                    alert(res.message);
                } else {
                    toast.innerHTML =`
                       <div class="toast-body">
                          🎉 同步成功!
                       </div>
                        `;
                    // 显示轻提示框
                    toast.classList.add('show');

                    // 3秒后隐藏轻提示框
                    setTimeout(() => {
                        toast.classList.remove('show');
                    }, 3000);

                }
            }
        });
    }
    addButton();
})();