Download Links Helper - 下载链接助手

批量复制页面下载链接到剪贴板,支持自定义规则

// ==UserScript==
// @name         Download Links Helper - 下载链接助手
// @namespace    shangandeyu
// @version      1.0.1
// @description  批量复制页面下载链接到剪贴板,支持自定义规则
// @author       shangandeyu
// @license      GPL-3.0
// @run-at       document-end
// @include      *
// @require      https://code.jquery.com/jquery-3.4.1.min.js
// @grant        GM_setClipboard
// @grant        unsafeWindow
// @grant        GM_listValues
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function() {
    'use strict';
    $('head').append(`<style>.dlh-navbar {
  overflow: hidden;
  background-color: #333;
}

.dlh-navbar a {
  float: left;
  font-size: 16px;
  color: white;
  text-align: center;
  padding: 14px 16px;
  text-decoration: none;
  cursor: pointer;
}

.dlh-dropdown {
  float: left;
  overflow: hidden;
}

.dlh-dropdown .dropbtn {
  font-size: 16px;
  border: none;
  outline: none;
  color: white;
  padding: 14px 16px;
  background-color: inherit;
  font-family: inherit;
  margin: 0;
}

.down-arr {
  transform: rotate(45deg);
  -webkit-transform: rotate(45deg);
  border: solid white;
  border-width: 0 3px 3px 0;
  display: inline-block;
  padding: 2px;
margin-left:5px;
}

.dlh-navbar a:hover, .dlh-dropdown:hover .dropbtn {
  background-color: red;
}

.dlh-dropdown-content {
  display: none;
  position: absolute;
  background-color: #f9f9f9;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  z-index: 1;
}

.dlh-dropdown-content a {
  float: none;
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
  text-align: left;
}

.dlh-dropdown-content a:hover {
  background-color: #ddd;
}

.dlh-dropdown:hover .dlh-dropdown-content {
  display: block;
}

table {
  border-collapse: collapse;
  border-spacing: 0;
  width: 100%;
  border: 1px solid #ddd;
}

th, td {
  text-align: left;
  padding: 8px;
}

tr:nth-child(even){background-color: #f2f2f2}

.dlh-button {
  background-color: #ddd;
  border: none;
  color: black;
  padding: 10px 20px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  margin: 4px 2px;
  cursor: pointer;
  border-radius: 16px;
}

.dlh-button:hover {
  background-color: #f1f1f1;
}

.dlh-form-inline {
  display: flex;
  flex-flow: row wrap;
  align-items: center;
}

.dlh-form-inline input {
  vertical-align: middle;
  margin: 5px 10px 5px 0;
  padding: 10px;
  background-color: #fff;
  border: 1px solid #ddd;
}

.dlh-form-inline button {
  padding: 10px 20px;
  background-color: dodgerblue;
  border: 1px solid #ddd;
  color: white;
  cursor: pointer;
}

.dlh-form-inline button:hover {
  background-color: royalblue;
}

@media (max-width: 800px) {
  .dlh-form-inline input {
    margin: 10px 0;
  }

  .dlh-form-inline {
    flex-direction: column;
    align-items: stretch;
  }
}

/* The Modal (background) */
.dlh-modal {
  display: none; /* Hidden by default */
  position: fixed; /* Stay in place */
  z-index: 1; /* Sit on top */
  padding-top: 100px; /* Location of the box */
  left: 0;
  top: 0;
  width: 100%; /* Full width */
  height: 100%; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(0,0,0); /* Fallback color */
  background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}

/* Modal Content */
.dlh-modal-content {
  position: relative;
  background-color: #fefefe;
  margin: auto;
  padding: 0;
  border: 1px solid #888;
  width: 80%;
  box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
  -webkit-animation-name: animatetop;
  -webkit-animation-duration: 0.4s;
  animation-name: animatetop;
  animation-duration: 0.4s
}

/* Add Animation */
@-webkit-keyframes animatetop {
  from {top:-300px; opacity:0}
  to {top:0; opacity:1}
}

@keyframes animatetop {
  from {top:-300px; opacity:0}
  to {top:0; opacity:1}
}

/* The Close Button */
.dlh-close {
  color: white;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.dlh-close:hover,
.dlh-close:focus {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}

.dlh-modal-header {
  padding: 2px 16px;
  background-color: #5cb85c;
  color: white;
}

.dlh-modal-body {padding: 2px 16px;}

.dlh-modal-footer {
  padding: 2px 16px;
  background-color: #5cb85c;
  color: white;
}</style>`);

    $('body').prepend(`<!-- The Modal -->
<div id="myModal" class="dlh-modal">
  <!-- Modal content -->
  <div class="dlh-modal-content">
    <div class="dlh-modal-header">
      <span class="dlh-close">&times;</span>
      <h2>Download Links Helper - 下载链接助手</h2>
    </div>
    <div class="dlh-modal-body">
      <div class="dlh-navbar">
<div id="linkTag" class="dlh-dropdown">
    <button class="dropbtn">
    </button>
    <div id="linkTypeSelect" class="dlh-dropdown-content">
    </div>
  </div>
  <a id="ruleTag" onclick="ruleTab()">规则配置</a>
</div>
<div id="dlh_div">
    </div>
</div>
    <div class="dlh-modal-footer">

    </div>
  </div>
</div>`);

    let modal = document.getElementById("myModal");

    let span = document.getElementsByClassName("dlh-close")[0];

    span.onclick = function() {
        modal.style.display = "none";
    }

    window.onclick = function(event) {
        if(event.target == modal) {
            modal.style.display = "none";
        }
    }
    let clhBtn = function() {
        modal.style.zIndex = [...document.all].reduce((r, e) => Math.max(r, +window.getComputedStyle(e).zIndex || 0), 0) + 1;
        modal.style.display = 'block';
    }
    GM_registerMenuCommand("复制下载链接", clhBtn);
    initLinkTab();
})();

unsafeWindow.newRule = function() {
    let len = $('div[name="dlh_rule_div"]').length;
    if(len > 0) {
        $('#rule_div_' + (len - 1)).after(`<div id="rule_div_` + len + `" name="dlh_rule_div" class="dlh-form-inline">
  <input type="text" id="rule_name_` + len + `" placeholder="名称" />
  <input type="text" id="rule_ex_` + len + `" placeholder="正则表达式" />
  <button type="submit" onclick="saveRule(null, ` + len + `)">保存</button>
  <button type="submit" onclick="delRule(null, ` + len + `)">删除</button>
</div>`);
    } else {
        $(`<div id="rule_div_0" name="dlh_rule_div" class="dlh-form-inline">
  <input type="text" id="rule_name_0" placeholder="名称" />
  <input type="text" id="rule_ex_0" placeholder="正则表达式" />
  <button type="submit" onclick="saveRule(null, 0)">保存</button>
  <button type="submit" onclick="delRule(null, 0)">删除</button>
</div>`).insertBefore($('#dlh_div').children(':first'));
    }
}

unsafeWindow.delRule = function(id, i) {
    if(id != null) {
        GM_deleteValue(id);
    }
    $('#rule_div_' + i).remove();
    unsafeWindow.ruleTab();
    initLinkTab();
}

unsafeWindow.saveRule = function(id, i) {
    let str = '{"ruleName":"' + $('#rule_name_' + i).val() + '","ruleEx":"' + escape($('#rule_ex_' + i).val()) + '"}';
    if(id == null) {
        GM_setValue(getUUID(), JSON.parse(str));
    } else {
        GM_setValue(id, JSON.parse(str));
    }
    unsafeWindow.ruleTab();
    initLinkTab();
}

unsafeWindow.ruleTab = function() {
    let emptyRuleHtml = `<div id="rule_div_0" name="dlh_rule_div" class="dlh-form-inline">
  <input type="text" id="rule_name_0" placeholder="名称" />
  <input type="text" id="rule_ex_0" placeholder="正则表达式" />
  <button type="submit" onclick="saveRule(null, 0)">保存</button>
  <button type="submit" onclick="delRule(null, 0)">删除</button>
</div>`;
    let ids = GM_listValues();
    let html = '';
    if(ids == null || ids.length == 0) {
        html = emptyRuleHtml;
    } else {
        for(let i = 0; i < ids.length; i++) {
            html += `<div id="rule_div_` + i + `" name="dlh_rule_div" class="dlh-form-inline">
  <input type="text" id="rule_name_` + i + `" placeholder="名称" value="` + GM_getValue(ids[i]).ruleName + `" />
  <input type="text" id="rule_ex_` + i + `" placeholder="正则表达式" value="` + unescape(GM_getValue(ids[i]).ruleEx) + `" />
  <button type="submit" onclick="saveRule('` + ids[i] + `',` + i + `)">保存</button>
  <button type="submit" onclick="delRule('` + ids[i] + `',` + i + `)">删除</button>
</div>`;
        }
    }
    html += '<button class="dlh-button" onclick="newRule()">新建</button>';
    $('#dlh_div').html(html);
    $('#linkTypeSelect').css('z-index', $('#rule_name_0').css('z-index') + 1)
};

unsafeWindow.copyLinks = function() {
    let links = '';
    $.each($('input[name="dlh_sub"]:checkbox:checked'), function() {
        links += $(this).val() + '\n';
    });
    GM_setClipboard(links, 'text');
    alert('已复制到剪贴板,去粘贴吧!');
};

unsafeWindow.showLinks = function(ruleEx) {
    ruleEx = eval(ruleEx);
    let html = '';
    html += `<div style="overflow-x:auto;max-height:350px">
  <table>
    <tr>
    <th><input id="dlh_all" type="checkbox"></th>
      <th>编号</th>
      <th>标题</th>
      <th>链接</th>
    </tr>`;
    let index = 1;
    let trs = '';
    $('a').each(function() {
        if(ruleEx.test($(this).attr('href'))) {
            trs += '<tr><td><input name="dlh_sub" type="checkbox" value="' + $(this).attr('href') + '"></td><td>' + (index++) + '</td><td>' + $(this).text() + '</td><td>' + $(this).attr('href') + '</td></tr>';
        }
    });
    $('img').each(function() {
        if(ruleEx.test($(this).attr('src'))) {
            trs += '<tr><td><input name="dlh_sub" type="checkbox" value="' + $(this).attr('src') + '"></td><td>' + (index++) + '</td><td>' + $(this).text() + '</td><td>' + $(this).attr('src') + '</td></tr>';
        }
    });
    if(trs == '') {
        trs = '<tr><td colspan="5">无</td></tr>';
    }
    html += trs + `</table></div>
<button class="dlh-button" onclick="copyLinks()">复制</button>`;
    $('#dlh_div').html(html);
    $("#dlh_all").on('click', function() {
        $("input[name='dlh_sub']").prop("checked", this.checked);
    });
    $("input[name='dlh_sub']").on('click', function() {
        var $subs = $("input[name='dlh_sub']");
        $("#dlh_all").prop("checked", $subs.length == $subs.filter(":checked").length ? true : false);
    });
}

function initLinkTab() {
    let obj = $('#linkTag');
    let html = '';
    let ids = GM_listValues();
    if(ids == null || ids.length == 0) {
        obj.children().first().html('请设置规则<i class="down-arr"></i>');
    } else {
        obj.children().first().html('请选择规则<i class="down-arr"></i>');
        for(let i = 0; i < ids.length; i++) {
            html += '<a onclick="showLinks(\'' + unescape(GM_getValue(ids[i]).ruleEx) + '\')">' + GM_getValue(ids[i]).ruleName + '</a>';
        }
        $('#linkTypeSelect').html(html);
        //         return unescape(GM_getValue(ids[0]).ruleEx);
    }
}

function getUUID() {
    let s = [];
    let hexDigits = '0123456789abcdef';
    for(let i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
    s[8] = s[13] = s[18] = s[23] = '-';
    let uuid = s.join('');
    return uuid;
}