├── .gitignore ├── .vscode └── settings.json ├── 3.x ├── _locales │ ├── en │ │ └── messages.json │ └── zh_CN │ │ └── messages.json ├── background.js ├── bootstrap.min.css ├── bootstrap.min.js ├── coffee │ ├── coffee.sh │ └── popup.coffee ├── contentscript.js ├── icon128.png ├── icon16.png ├── icon19.png ├── icon48.png ├── jclipboard.js ├── jquery-3.3.1.min.js ├── manifest.json ├── popup.html ├── popup.js └── test_html.html ├── README.md ├── backup ├── background.js ├── contentscript.js ├── copy.js ├── jclipboard.js ├── jquery.js └── popup.js ├── index.html ├── package-lock.json ├── package.json ├── public └── _locales │ ├── en │ └── messages.json │ └── zh_CN │ └── messages.json ├── src ├── App.vue ├── assets │ ├── icon128.png │ ├── icon16.png │ ├── icon19.png │ ├── icon48.png │ └── logo.png ├── background.ts ├── components │ └── HelloWorld.vue ├── contentscript.ts ├── env.d.ts ├── manifest.json ├── popup.html ├── popup.ts ├── shims-vue.d.ts └── types.ts ├── tsconfig.json ├── vetur.config.js ├── vite.config.ts └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | .redcar 3 | .DS_Store 4 | *.swp 5 | *.swo 6 | .rvmrc 7 | *.vim 8 | *.jas 9 | *.bak 10 | 11 | node_modules/ 12 | dist/ 13 | .idea 14 | 15 | *.tmp 16 | 17 | 18 | .idea/ 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "vetur.validation.script": false 3 | } -------------------------------------------------------------------------------- /3.x/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension_name":{ 3 | "message":"Ed2k&MagnetHelper", 4 | "description":"Extension Name" 5 | }, 6 | "extension_description":{ 7 | "message":"It helps you to deal with ed2k/magnet links, with functions of select range and filename search.", 8 | "description":"Extension Description" 9 | }, 10 | "error_scope":{ 11 | "message":"Select range error. Please enter a correct number." 12 | }, 13 | "error_overlimit":{ 14 | "message":"Value exceeds valid range. Please enter a correct number." 15 | }, 16 | "error_unselected":{ 17 | "message":"You haven't selected!" 18 | }, 19 | "select_scope":{ 20 | "message":"Range" 21 | }, 22 | "confirm_scope":{ 23 | "message":"Confirm" 24 | }, 25 | "clean_scope":{ 26 | "message":"Clear" 27 | }, 28 | "scope_tips":{ 29 | "message":"(Items will be selected after you confirm.)" 30 | }, 31 | "search_filename":{ 32 | "message":"Search" 33 | }, 34 | "search_placeholder":{ 35 | "message":"Filename" 36 | }, 37 | "button_return":{ 38 | "message":"Return" 39 | }, 40 | "search_tips":{ 41 | "message":"(Selected items won't be cleared after return.)" 42 | }, 43 | "th_select":{ 44 | "message":"Select" 45 | }, 46 | "th_number":{ 47 | "message":"#" 48 | }, 49 | "th_filename":{ 50 | "message":"Filename" 51 | }, 52 | "th_size":{ 53 | "message":"Size" 54 | }, 55 | "button_selectall":{ 56 | "message":"Select All" 57 | }, 58 | "button_selectopposite":{ 59 | "message":"Inverse" 60 | }, 61 | "button_cleanselect":{ 62 | "message":"Deselect All" 63 | }, 64 | "button_confirmcopy":{ 65 | "message":"Copy" 66 | }, 67 | "result_ed2k_links":{ 68 | "message":"ed2k~" 69 | }, 70 | "result_magnet_links":{ 71 | "message":"magnet~" 72 | }, 73 | "result_links":{ 74 | "message":"Links" 75 | }, 76 | "result_copy":{ 77 | "message":"Copy" 78 | }, 79 | "copy_success":{ 80 | "message":"The content has been successfully copied to the clipboard! The following is a copy results" 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /3.x/_locales/zh_CN/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension_name":{ 3 | "message":"Ed2k&Magnet链接复制助手", 4 | "description":"插件名称" 5 | }, 6 | "extension_description":{ 7 | "message":"这是一个方便复制处理页面内大量的ed2k和magnet链接的应用,带有范围选择与文件名搜索选择的功能", 8 | "description":"插件描述" 9 | }, 10 | "error_scope":{ 11 | "message":"范围输入有问题,请输入正确数字" 12 | }, 13 | "error_overlimit":{ 14 | "message":"最大范围超出有效选项数,请输入正确数字" 15 | }, 16 | "error_unselected":{ 17 | "message":"你还没有选择!" 18 | }, 19 | "select_scope":{ 20 | "message":"选择范围" 21 | }, 22 | "confirm_scope":{ 23 | "message":"确定" 24 | }, 25 | "clean_scope":{ 26 | "message":"清除" 27 | }, 28 | "scope_tips":{ 29 | "message":"(确定后范围内的项目将会被选中)" 30 | }, 31 | "search_filename":{ 32 | "message":"搜索" 33 | }, 34 | "search_placeholder":{ 35 | "message":"文件名" 36 | }, 37 | "button_return":{ 38 | "message":"返回" 39 | }, 40 | "search_tips":{ 41 | "message":"(你在搜索时选择的选项再返回后依然存在)" 42 | }, 43 | "th_select":{ 44 | "message":"选择" 45 | }, 46 | "th_number":{ 47 | "message":"编号" 48 | }, 49 | "th_filename":{ 50 | "message":"文件名" 51 | }, 52 | "th_size":{ 53 | "message":"大小" 54 | }, 55 | "button_selectall":{ 56 | "message":"全选" 57 | }, 58 | "button_selectopposite":{ 59 | "message":"反选" 60 | }, 61 | "button_cleanselect":{ 62 | "message":"清除" 63 | }, 64 | "button_confirmcopy":{ 65 | "message":"复制" 66 | }, 67 | "result_ed2k_links":{ 68 | "message":"ed2k结果" 69 | }, 70 | "result_magnet_links":{ 71 | "message":"magnet结果" 72 | }, 73 | "result_copy":{ 74 | "message":"复制结果" 75 | }, 76 | "copy_success":{ 77 | "message":"内容已经成功复制到粘贴板!以下是复制结果" 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /3.x/background.js: -------------------------------------------------------------------------------- 1 | var result=""; 2 | var showPopup, createCopy; 3 | 4 | function onRequest(request, sender, sendResponse) { 5 | if(request.ask=="ed2klinks"){ 6 | showPopup(request.result,sender.tab.id); 7 | } 8 | }; 9 | 10 | chrome.extension.onRequest.addListener(onRequest); 11 | chrome.tabs.onSelectionChanged.addListener(function(id,obj){ 12 | chrome.tabs.sendRequest(id,{ask:"getResult"},function(response){ 13 | if (!chrome.runtime.lastError) { 14 | console.log(response.result); 15 | result = response.result; 16 | } 17 | }); 18 | }); 19 | 20 | showPopup = function(re,id){ 21 | result = re; 22 | chrome.pageAction.show(id); 23 | } 24 | 25 | createCopy = function(re){ 26 | chrome.tabs.create({url:"copy.html"},callback); 27 | function callback(tab){ 28 | chrome.tabs.sendRequest(tab.id,re,function(response){}); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /3.x/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v4.4.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t=t||self).bootstrap={},t.jQuery,t.Popper)}(this,function(t,g,u){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Y.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Se,popperConfig:null},Fe="show",Ue="out",We={HIDE:"hide"+Oe,HIDDEN:"hidden"+Oe,SHOW:"show"+Oe,SHOWN:"shown"+Oe,INSERTED:"inserted"+Oe,CLICK:"click"+Oe,FOCUSIN:"focusin"+Oe,FOCUSOUT:"focusout"+Oe,MOUSEENTER:"mouseenter"+Oe,MOUSELEAVE:"mouseleave"+Oe},qe="fade",Me="show",Ke=".tooltip-inner",Qe=".arrow",Be="hover",Ve="focus",Ye="click",ze="manual",Xe=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Me))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&g(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(qe);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,this._getPopperConfig(a)),g(o).addClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===Ue&&e._leave(null,e)};if(g(this.tip).hasClass(qe)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){function e(){n._hoverState!==Fe&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),g(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()}var n=this,i=this.getTipElement(),o=g.Event(this.constructor.Event.HIDE);if(g(this.element).trigger(o),!o.isDefaultPrevented()){if(g(i).removeClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ye]=!1,this._activeTrigger[Ve]=!1,this._activeTrigger[Be]=!1,g(this.tip).hasClass(qe)){var r=_.getTransitionDurationFromElement(i);g(i).one(_.TRANSITION_END,e).emulateTransitionEnd(r)}else e();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Pe+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(Ke)),this.getTitle()),g(t).removeClass(qe+" "+Me)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=we(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t=t||("function"==typeof this.config.title?this.config.title.call(this.element):this.config.title)},t._getPopperConfig=function(t){var e=this;return l({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Qe},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},{},this.config.popperConfig)},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,{},e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return Re[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==ze){var e=t===Be?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Be?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),this._hideModalHandler=function(){i.element&&i.hide()},g(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");!this.element.getAttribute("title")&&"string"==t||(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Ve:Be]=!0),g(e.getTipElement()).hasClass(Me)||e._hoverState===Fe?e._hoverState=Fe:(clearTimeout(e._timeout),e._hoverState=Fe,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===Fe&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Ve:Be]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=Ue,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===Ue&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=g(this.element).data();return Object.keys(e).forEach(function(t){-1!==je.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,{},e,{},"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_.typeCheckConfig(Ae,t,this.constructor.DefaultType),t.sanitize&&(t.template=we(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Le);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(qe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ne),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ne,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.4.1"}},{key:"Default",get:function(){return xe}},{key:"NAME",get:function(){return Ae}},{key:"DATA_KEY",get:function(){return Ne}},{key:"Event",get:function(){return We}},{key:"EVENT_KEY",get:function(){return Oe}},{key:"DefaultType",get:function(){return He}}]),i}();g.fn[Ae]=Xe._jQueryInterface,g.fn[Ae].Constructor=Xe,g.fn[Ae].noConflict=function(){return g.fn[Ae]=ke,Xe._jQueryInterface};var $e="popover",Ge="bs.popover",Je="."+Ge,Ze=g.fn[$e],tn="bs-popover",en=new RegExp("(^|\\s)"+tn+"\\S+","g"),nn=l({},Xe.Default,{placement:"right",trigger:"click",content:"",template:''}),on=l({},Xe.DefaultType,{content:"(string|element|function)"}),rn="fade",sn="show",an=".popover-header",ln=".popover-body",cn={HIDE:"hide"+Je,HIDDEN:"hidden"+Je,SHOW:"show"+Je,SHOWN:"shown"+Je,INSERTED:"inserted"+Je,CLICK:"click"+Je,FOCUSIN:"focusin"+Je,FOCUSOUT:"focusout"+Je,MOUSEENTER:"mouseenter"+Je,MOUSELEAVE:"mouseleave"+Je},hn=function(t){function i(){return t.apply(this,arguments)||this}!function(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}(i,t);var e=i.prototype;return e.isWithContent=function(){return this.getTitle()||this._getContent()},e.addAttachmentClass=function(t){g(this.getTipElement()).addClass(tn+"-"+t)},e.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},e.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(an),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(ln),e),t.removeClass(rn+" "+sn)},e._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},e._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(en);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t 11 | result = chrome.extension.getBackgroundPage().result 12 | console.log "result:", result 13 | window.ed2k_bitch = @ed2k_result = dealEd2k(result.ed2k_result) 14 | window.magnet_bitch = @magnet_result = dealMagnet(result.magnet_result) 15 | console.log @ed2k_result, @magnet_result 16 | that = @ 17 | 18 | for prefix in types 19 | table_append_tr($("##{prefix}_table"), @["#{prefix}_result"]) 20 | 21 | $("span[i18n]").each(()-> 22 | $(this).html(chrome.i18n.getMessage($(this).attr('i18n'))) 23 | ) 24 | 25 | $("input[i18n]").each(()-> 26 | $(this).val( chrome.i18n.getMessage( $(this).attr('i18n') ) ) 27 | ) 28 | 29 | data_map = { 30 | "confirmScope": confirmScope, 31 | "cleanSearch":cleanSearch, 32 | "cleanScope":cleanScope, 33 | "selectAll":selectAll, 34 | "selectOpposite":selectOpposite, 35 | "cleanSelect":cleanSelect, 36 | "confirmCopy":confirmCopy 37 | } 38 | 39 | $("input[data-map]").on("click",()-> 40 | data_map[$(this).attr("data-map")]() 41 | return 42 | ) 43 | 44 | $("#searchText").on("keyup", ()-> 45 | table = current_table() 46 | type = table.data("type") 47 | result = that["#{type}_result"] 48 | check_selected() 49 | st = $(this).val() 50 | templinkobj = {} 51 | for key,r of result 52 | templinkobj[key] = r if key.toLocaleLowerCase().indexOf(st.toLocaleLowerCase())>-1 53 | temptrtd = wrap_tr(templinkobj) 54 | table.find("tbody").remove() 55 | table.append(temptrtd) 56 | check_selected() 57 | ).attr("placeholder",chrome.i18n.getMessage("search_placeholder")) 58 | 59 | $("table").delegate("td[ee]","click",()-> 60 | dealChecked($(this)) 61 | scanColor() 62 | ).delegate("tr", "mouseenter",()-> 63 | $(this).css("cursor", "pointer") 64 | $(this).addClass("selected_tr") 65 | ).delegate("tr", "mouseout", ()-> 66 | cb = $(this).find("input[type='checkbox']") 67 | unless checked(cb) 68 | $(this).removeClass("selected_tr") 69 | ) 70 | 71 | console.log "resulted2kl:", result.ed2k_result.length 72 | console.log "resultedmgl:", result.magnet_result.length 73 | if result.ed2k_result.length == 0 and result.magnet_result.length > 0 74 | console.log "should click!" 75 | $("#switch_ul a[href='#magnet_links']").click() 76 | 77 | ) 78 | 79 | dealEd2k = (linkarr = [])-> 80 | re = {} 81 | count = 0 82 | for link in linkarr 83 | regex2 = /ed2k:\/\/\|file\|(.+?)\|(.+?)\|.+?\//ig 84 | tmp = regex2.exec(link) 85 | continue if !tmp 86 | name = decodeURI(tmp[1]) 87 | continue if re[name]? 88 | count++ 89 | re[name] = id:count, link:tmp[0], big:(tmp[2]/(1024*1024)) 90 | $("#ed2k_table").data("maxid", count) 91 | re 92 | 93 | dealMagnet = (linkarr = [])-> 94 | re = {} 95 | count = 0 96 | for l in linkarr 97 | if (l.name) 98 | name = l.name 99 | link = l.link 100 | else 101 | link = l # 傻叉了,只顾 name 忘了最重要的 link 102 | name_regex = /dn=(.+?)&/ 103 | try 104 | name = name_regex.exec(l)[1] 105 | catch error 106 | name = l # 解析失败也显示磁力链,不然没法复制 107 | 108 | continue if !name 109 | 110 | size_regex = /xl=(.+?)&/ 111 | try 112 | size = size_regex.exec(l)[1] 113 | catch error 114 | size = 0 115 | 116 | name = decodeURI(name) 117 | continue if re[name]? 118 | count++ 119 | re[name] = id: count, link: link, big: (size/(1024*1024)) 120 | $("#magnet_table").data("maxid", count) 121 | re 122 | 123 | table_append_tr = (table, result)-> 124 | console.log "table:", table 125 | console.log "result", result 126 | table.append(wrap_tr(result)) 127 | 128 | wrap_tr = (linkobj)-> 129 | trtd = $("") 130 | for key,obj of linkobj 131 | tr = $("") 132 | tr.append("") 133 | tr.append("#{obj.id}") 134 | tr.append("#{key}") 135 | tr.append("#{dealBig(obj.big)}") 136 | trtd.append(tr) 137 | trtd 138 | 139 | dealBig = (num)-> 140 | return "no size data" if num == 0 141 | num = num.toFixed(3) 142 | numstr="" 143 | if num < 1 144 | numstr = "#{num*1000}KB" 145 | else if num>=1 and num<10 146 | numstr = "#{Math.round(num*10)/10}MB" 147 | else 148 | numstr = "#{Math.floor(num)}MB" 149 | numstr 150 | 151 | #把有的放进selectedIDS ... 过去写的什么垃圾代码 152 | check_selected = ()-> 153 | current_table().find("input[type='checkbox']").each(()-> 154 | if checked($(this)) 155 | id = $(this).attr("id") 156 | id = id.substring(3, id.length) 157 | selectedIDS[id]=0 158 | for key,selected_id of selectedIDS 159 | temp = $("#cb_#{key}") 160 | temp.prop("checked",true) if temp? 161 | ) 162 | 163 | dealChecked = (td) -> 164 | cb = td.siblings("td").find("input[type='checkbox']") 165 | toggle_check(cb) 166 | 167 | scanColor = ()-> 168 | $("tr").each(()-> 169 | cb = $(this).find("input[type='checkbox']") 170 | if checked(cb) 171 | $(this).addClass("selected_tr") 172 | else 173 | $(this).removeClass("selected_tr") 174 | ) 175 | 176 | confirmScope = ()-> 177 | maxid = parseInt( current_table().data("maxid") ) 178 | from = Math.floor($("#from").val()) 179 | to = Math.floor($("#to").val()) 180 | reg = /\d+/ 181 | if !(reg.test(from) and reg.test(to)) || (from<=0 || to <=0) || (from)>to 182 | alert("范围输入有问题,请输入正确数字") 183 | return false 184 | if(to > maxid) 185 | alert("最大范围超出有效选项数,请输入正确数字") 186 | return false 187 | for i in [from..to] 188 | selectedIDS[i]=0 189 | check_selected() 190 | scanColor() 191 | return true 192 | 193 | cleanScope = ()-> 194 | $("#from").val("") 195 | $("#to").val("") 196 | cleanSelect() 197 | 198 | selectAll = ()-> 199 | table = current_table() 200 | table.find("input[type='checkbox']").each(-> 201 | $(this).prop('checked',true) 202 | ) 203 | scanColor() 204 | 205 | selectOpposite = ()-> 206 | table = current_table() 207 | table.find("input[type='checkbox']").each(-> 208 | toggle_check($(this)) 209 | ) 210 | scanColor() 211 | 212 | cleanSearch = (result = {} )-> 213 | table = current_table() 214 | type = table.data("type") 215 | check_selected() 216 | $("#searchText").val("") 217 | table.find("tbody").remove() 218 | table_append_tr(table, window["#{type}_bitch"]) 219 | scanColor() 220 | 221 | cleanSelect = ()-> 222 | table = current_table() 223 | $("input[type='checkbox']").each(-> 224 | if $(this).prop('checked') 225 | $(this).prop("checked", false) 226 | ) 227 | scanColor() 228 | selectedIDS= {} 229 | 230 | confirmCopy = ()-> 231 | cpresult = "" 232 | count = 0 233 | table = current_table() 234 | table.find("input[type='checkbox']").each(-> 235 | if $(this).prop("checked") 236 | cpresult += "#{$(this).attr("ed2k")}\n" 237 | count++ 238 | ) 239 | if count==0 240 | alert(chrome.i18n.getMessage("error_unselected")) 241 | return false 242 | JClipboard.copy(cpresult) 243 | textarea = $("") 244 | textarea.val(cpresult) 245 | $("#copy").empty().append("#{chrome.i18n.getMessage("copy_success")}").append(textarea) 246 | $("a[href='#copy']").tab("show") 247 | $("#copy textarea").height($("#links").height()).focus().select() 248 | return true 249 | 250 | checked = (element)-> 251 | element.prop("checked") 252 | 253 | toggle_check = (checkbox)-> 254 | if checked(checkbox) 255 | checkbox.prop("checked", false) 256 | else 257 | checkbox.prop("checked", true) 258 | 259 | current_table = ()-> 260 | $("#" + $("#switch_ul > li > a.active").data("type") + "_table") 261 | 262 | 263 | -------------------------------------------------------------------------------- /3.x/contentscript.js: -------------------------------------------------------------------------------- 1 | var regex = /ed2k:\/\/\|file\|.+?\//ig; 2 | var magnet_regex = /magnet\:\?[^\"]+/ig; 3 | var magnet_name_regex = /dn=(.+?)&/ 4 | var result = ""; 5 | var ed2k_result = []; 6 | var magnet_result = []; 7 | 8 | function gen_magnet_result() { 9 | // 原逻辑:磁力链有 dn 时,直接返回磁力链,popup.js 去解析文件名 10 | var m_result = document.body.innerHTML.match(magnet_regex) 11 | m_result = m_result ? m_result.filter( onlyUnique ) : [] 12 | if (m_result && m_result[0] && m_result[0].match(magnet_name_regex)) { 13 | var xt_and_magnent = {}; 14 | for (var i = 0; i < m_result.length; i++) { 15 | // 相同 xt 视为同一个文件 16 | var magnet = m_result[i] 17 | var xt = magnet.split('&')[0] 18 | xt_and_magnent[xt] = magnet 19 | } 20 | return Object.values(xt_and_magnent); 21 | } 22 | // 磁力链没 dn 时,用磁力链所在超链接 text 作为文件名 23 | m_result = []; 24 | var magnet_elements = $("a").filter(function() {return this.href.match(magnet_regex) && onlyUnique;}) || []; 25 | for (var i = 0; i < magnet_elements.length; i++) { 26 | name = magnet_elements[i].text; 27 | var previous_element = m_result[i - 1] 28 | var current_link = magnet_elements[i]['href'] 29 | if (i > 0 && previous_element.name === name) {// 解决 text 重复 30 | previous_element.name += i.toString(); 31 | name += (i + 1).toString(); 32 | } 33 | m_result.push({link: current_link, name: name}); 34 | } 35 | return m_result; 36 | } 37 | 38 | function gen_ed2k_result() { 39 | ed2k_result = document.body.innerHTML.match(regex); 40 | ed2k_result = ed2k_result ? ed2k_result.filter( onlyUnique ) : []; 41 | return ed2k_result; 42 | } 43 | 44 | // 去重复 45 | function onlyUnique(value, index, self) { 46 | return self.indexOf(value) === index; 47 | } 48 | 49 | function init() { 50 | if (regex.test(document.body.innerHTML) || magnet_regex.test(document.body.innerHTML)) { 51 | ed2k_result = gen_ed2k_result(); 52 | magnet_result = gen_magnet_result(); 53 | result = {ed2k_result: ed2k_result, magnet_result: magnet_result}; 54 | 55 | chrome.extension.sendRequest({ask:"ed2klinks", result:result}, function(response) {}); 56 | } 57 | } 58 | 59 | function onRequest(request,sender,sendResponse){ 60 | if(request.ask=="getResult"){ 61 | if(result){ 62 | sendResponse({result:result}); 63 | }else{ 64 | ed2k_result = gen_ed2k_result(); 65 | magnet_result = gen_magnet_result(); 66 | result = {ed2k_result: ed2k_result, magnet_result: magnet_result}; 67 | 68 | sendResponse({result:result}); 69 | } 70 | } 71 | } 72 | init(); 73 | chrome.extension.onRequest.addListener(onRequest); 74 | 75 | 76 | -------------------------------------------------------------------------------- /3.x/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reducm/Ed2kChromePlugin/d8d5ec3acd6551c73e9f88c30db7c11eec747efd/3.x/icon128.png -------------------------------------------------------------------------------- /3.x/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reducm/Ed2kChromePlugin/d8d5ec3acd6551c73e9f88c30db7c11eec747efd/3.x/icon16.png -------------------------------------------------------------------------------- /3.x/icon19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reducm/Ed2kChromePlugin/d8d5ec3acd6551c73e9f88c30db7c11eec747efd/3.x/icon19.png -------------------------------------------------------------------------------- /3.x/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reducm/Ed2kChromePlugin/d8d5ec3acd6551c73e9f88c30db7c11eec747efd/3.x/icon48.png -------------------------------------------------------------------------------- /3.x/jclipboard.js: -------------------------------------------------------------------------------- 1 | JClipboard = { 2 | _createTextArea: function() { 3 | var textArea; 4 | textArea = document.createElement("textarea"); 5 | textArea.style.position = "absolute"; 6 | textArea.style.left = "-100%"; 7 | return textArea; 8 | }, 9 | copy: function(data) { 10 | var textArea; 11 | textArea = this._createTextArea(); 12 | textArea.value = data; 13 | document.body.appendChild(textArea); 14 | textArea.select(); 15 | document.execCommand("Copy"); 16 | return document.body.removeChild(textArea); 17 | }, 18 | paste: function() { 19 | var textArea, value; 20 | textArea = this._createTextArea(); 21 | document.body.appendChild(textArea); 22 | textArea.focus(); 23 | document.execCommand("Paste"); 24 | value = textArea.value; 25 | document.body.removeChild(textArea); 26 | return value; 27 | } 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /3.x/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__MSG_extension_name__", 3 | "version": "3.4", 4 | "manifest_version": 2, 5 | "default_locale": "en", 6 | "icons": { 7 | "19": "icon19.png", 8 | "16": "icon16.png", 9 | "48": "icon48.png", 10 | "128": "icon128.png" 11 | }, 12 | "background": { 13 | "scripts": ["background.js"] 14 | }, 15 | "description": "__MSG_extension_description__", 16 | 17 | "permissions": [ 18 | "http://*/*", 19 | "https://*/*", 20 | "tabs", 21 | "unlimitedStorage", 22 | "clipboardRead", 23 | "clipboardWrite", 24 | "notifications" 25 | ], 26 | 27 | "page_action": { 28 | "default_icon": "icon19.png", 29 | "default_title": "__MSG_extension_name__", 30 | "default_popup": "popup.html" 31 | }, 32 | "homepage_url": "https://github.com/reducm/Ed2kChromePlugin", 33 | "content_scripts": [{ 34 | "matches": ["http://*/*", "https://*/*"], 35 | "js": ["jquery-3.3.1.min.js", "contentscript.js"], 36 | "run_at": "document_end", 37 | "all_frames": false 38 | }] 39 | } 40 | -------------------------------------------------------------------------------- /3.x/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hi there 5 | 6 | 7 | 8 | 9 | 10 | 31 | 32 | 33 |
34 |
35 |
36 | 选择范围: 37 | - 38 | 39 | 40 |
41 |
42 | 搜索文件名: 43 | 44 |
45 |
46 | 47 |
48 | 53 | 54 |
55 | 66 | 67 | 78 | 79 |
80 | 81 |
82 |
83 |
84 | 85 |
86 | 87 | 88 | 89 | 90 |
91 | 92 |
93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /3.x/popup.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.3.2 2 | (function() { 3 | // TODO: fix bug "&" symbol show %26 4 | var check_selected, checked, cleanScope, cleanSearch, cleanSelect, confirmCopy, confirmScope, contabid, current_table, dealBig, dealChecked, dealEd2k, dealMagnet, magnet_regex, result, scanColor, selectAll, selectOpposite, selectedIDS, table_append_tr, toggle_check, types, wrap_tr; 5 | 6 | contabid = 0; 7 | 8 | types = ["ed2k", "magnet"]; 9 | 10 | result = []; 11 | 12 | selectedIDS = {}; 13 | 14 | magnet_regex = /magnet\:\?[^\"]+/ig; 15 | 16 | window.ed2k_bitch = {}; 17 | 18 | window.magnet_bitch = {}; 19 | 20 | $(document).ready(function() { 21 | var data_map, j, len, prefix, that; 22 | result = chrome.extension.getBackgroundPage().result; 23 | console.log("result:", result); 24 | window.ed2k_bitch = this.ed2k_result = dealEd2k(result.ed2k_result); 25 | window.magnet_bitch = this.magnet_result = dealMagnet(result.magnet_result); 26 | console.log(this.ed2k_result, this.magnet_result); 27 | that = this; 28 | for (j = 0, len = types.length; j < len; j++) { 29 | prefix = types[j]; 30 | table_append_tr($(`#${prefix}_table`), this[`${prefix}_result`]); 31 | } 32 | $("span[i18n]").each(function() { 33 | return $(this).html(chrome.i18n.getMessage($(this).attr('i18n'))); 34 | }); 35 | $("input[i18n]").each(function() { 36 | return $(this).val(chrome.i18n.getMessage($(this).attr('i18n'))); 37 | }); 38 | data_map = { 39 | "confirmScope": confirmScope, 40 | "cleanSearch": cleanSearch, 41 | "cleanScope": cleanScope, 42 | "selectAll": selectAll, 43 | "selectOpposite": selectOpposite, 44 | "cleanSelect": cleanSelect, 45 | "confirmCopy": confirmCopy 46 | }; 47 | $("input[data-map]").on("click", function() { 48 | data_map[$(this).attr("data-map")](); 49 | }); 50 | $("#searchText").on("keyup", function() { 51 | var key, r, st, table, templinkobj, temptrtd, type; 52 | table = current_table(); 53 | type = table.data("type"); 54 | result = that[`${type}_result`]; 55 | check_selected(); 56 | st = $(this).val(); 57 | templinkobj = {}; 58 | for (key in result) { 59 | r = result[key]; 60 | if (key.toLocaleLowerCase().indexOf(st.toLocaleLowerCase()) > -1) { 61 | templinkobj[key] = r; 62 | } 63 | } 64 | temptrtd = wrap_tr(templinkobj); 65 | table.find("tbody").remove(); 66 | table.append(temptrtd); 67 | return check_selected(); 68 | }).attr("placeholder", chrome.i18n.getMessage("search_placeholder")); 69 | $("table").delegate("td[ee]", "click", function() { 70 | dealChecked($(this)); 71 | return scanColor(); 72 | }).delegate("tr", "mouseenter", function() { 73 | $(this).css("cursor", "pointer"); 74 | return $(this).addClass("selected_tr"); 75 | }).delegate("tr", "mouseout", function() { 76 | var cb; 77 | cb = $(this).find("input[type='checkbox']"); 78 | if (!checked(cb)) { 79 | return $(this).removeClass("selected_tr"); 80 | } 81 | }); 82 | console.log("resulted2kl:", result.ed2k_result.length); 83 | console.log("resultedmgl:", result.magnet_result.length); 84 | if (result.ed2k_result.length === 0 && result.magnet_result.length > 0) { 85 | console.log("should click!"); 86 | return $("#switch_ul a[href='#magnet_links']").click(); 87 | } 88 | }); 89 | 90 | dealEd2k = function(linkarr = []) { 91 | var count, j, len, link, name, re, regex2, tmp; 92 | re = {}; 93 | count = 0; 94 | for (j = 0, len = linkarr.length; j < len; j++) { 95 | link = linkarr[j]; 96 | regex2 = /ed2k:\/\/\|file\|(.+?)\|(.+?)\|.+?\//ig; 97 | tmp = regex2.exec(link); 98 | if (!tmp) { 99 | continue; 100 | } 101 | name = decodeURI(tmp[1]); 102 | if (re[name] != null) { 103 | continue; 104 | } 105 | count++; 106 | re[name] = { 107 | id: count, 108 | link: tmp[0], 109 | big: tmp[2] / (1024 * 1024) 110 | }; 111 | } 112 | $("#ed2k_table").data("maxid", count); 113 | return re; 114 | }; 115 | 116 | dealMagnet = function(linkarr = []) { 117 | var count, error, j, l, len, link, name, name_regex, re, size, size_regex; 118 | re = {}; 119 | count = 0; 120 | for (j = 0, len = linkarr.length; j < len; j++) { 121 | l = linkarr[j]; 122 | if (l.name) { 123 | name = l.name; 124 | link = l.link; 125 | } else { 126 | link = l; // 傻叉了,只顾 name 忘了最重要的 link 127 | name_regex = /dn=(.+?)&/; 128 | try { 129 | name = name_regex.exec(l)[1]; 130 | if (re[name]) { // dn 重复了,会导致结果被同名链接覆盖 131 | name = `${name}${count + 1}` 132 | } 133 | } catch (error1) { 134 | error = error1; 135 | name = l; // 解析失败也显示磁力链,不然没法复制 136 | } 137 | } 138 | if (!name) { 139 | continue; 140 | } 141 | size_regex = /xl=(.+?)&/; 142 | try { 143 | size = size_regex.exec(l)[1]; 144 | } catch (error1) { 145 | error = error1; 146 | size = 0; 147 | } 148 | name = decodeURI(name); 149 | if (re[name] != null) { 150 | continue; 151 | } 152 | count++; 153 | re[name] = { 154 | id: count, 155 | link: link, 156 | big: size / (1024 * 1024) 157 | }; 158 | } 159 | $("#magnet_table").data("maxid", count); 160 | return re; 161 | }; 162 | 163 | table_append_tr = function(table, result) { 164 | console.log("table:", table); 165 | console.log("result", result); 166 | return table.append(wrap_tr(result)); 167 | }; 168 | 169 | wrap_tr = function(linkobj) { 170 | var key, obj, tr, trtd; 171 | trtd = $(""); 172 | for (key in linkobj) { 173 | obj = linkobj[key]; 174 | tr = $(``); 175 | tr.append(``); 176 | tr.append(`${obj.id}`); 177 | tr.append(`${key}`); 178 | tr.append(`${dealBig(obj.big)}`); 179 | trtd.append(tr); 180 | } 181 | return trtd; 182 | }; 183 | 184 | dealBig = function(num) { 185 | var numstr; 186 | if (num === 0) { 187 | return "no size data"; 188 | } 189 | num = num.toFixed(3); 190 | numstr = ""; 191 | if (num < 1) { 192 | numstr = `${num * 1000}KB`; 193 | } else if (num >= 1 && num < 10) { 194 | numstr = `${Math.round(num * 10) / 10}MB`; 195 | } else { 196 | numstr = `${Math.floor(num)}MB`; 197 | } 198 | return numstr; 199 | }; 200 | 201 | //把有的放进selectedIDS ... 过去写的什么垃圾代码 202 | check_selected = function() { 203 | return current_table().find("input[type='checkbox']").each(function() { 204 | var id, key, results, selected_id, temp; 205 | if (checked($(this))) { 206 | id = $(this).attr("id"); 207 | id = id.substring(3, id.length); 208 | selectedIDS[id] = 0; 209 | } 210 | results = []; 211 | for (key in selectedIDS) { 212 | selected_id = selectedIDS[key]; 213 | temp = $(`#cb_${key}`); 214 | if (temp != null) { 215 | results.push(temp.prop("checked", true)); 216 | } else { 217 | results.push(void 0); 218 | } 219 | } 220 | return results; 221 | }); 222 | }; 223 | 224 | dealChecked = function(td) { 225 | var cb; 226 | cb = td.siblings("td").find("input[type='checkbox']"); 227 | return toggle_check(cb); 228 | }; 229 | 230 | scanColor = function() { 231 | return $("tr").each(function() { 232 | var cb; 233 | cb = $(this).find("input[type='checkbox']"); 234 | if (checked(cb)) { 235 | return $(this).addClass("selected_tr"); 236 | } else { 237 | return $(this).removeClass("selected_tr"); 238 | } 239 | }); 240 | }; 241 | 242 | confirmScope = function() { 243 | var from, i, j, maxid, ref, ref1, reg, to; 244 | maxid = parseInt(current_table().data("maxid")); 245 | from = Math.floor($("#from").val()); 246 | to = Math.floor($("#to").val()); 247 | reg = /\d+/; 248 | if (!(reg.test(from) && reg.test(to)) || (from <= 0 || to <= 0) || from > to) { 249 | alert("范围输入有问题,请输入正确数字"); 250 | return false; 251 | } 252 | if (to > maxid) { 253 | alert("最大范围超出有效选项数,请输入正确数字"); 254 | return false; 255 | } 256 | for (i = j = ref = from, ref1 = to; (ref <= ref1 ? j <= ref1 : j >= ref1); i = ref <= ref1 ? ++j : --j) { 257 | selectedIDS[i] = 0; 258 | } 259 | check_selected(); 260 | scanColor(); 261 | return true; 262 | }; 263 | 264 | cleanScope = function() { 265 | $("#from").val(""); 266 | $("#to").val(""); 267 | return cleanSelect(); 268 | }; 269 | 270 | selectAll = function() { 271 | var table; 272 | table = current_table(); 273 | table.find("input[type='checkbox']").each(function() { 274 | return $(this).prop('checked', true); 275 | }); 276 | return scanColor(); 277 | }; 278 | 279 | selectOpposite = function() { 280 | var table; 281 | table = current_table(); 282 | table.find("input[type='checkbox']").each(function() { 283 | return toggle_check($(this)); 284 | }); 285 | return scanColor(); 286 | }; 287 | 288 | cleanSearch = function(result = {}) { 289 | var table, type; 290 | table = current_table(); 291 | type = table.data("type"); 292 | check_selected(); 293 | $("#searchText").val(""); 294 | table.find("tbody").remove(); 295 | table_append_tr(table, window[`${type}_bitch`]); 296 | return scanColor(); 297 | }; 298 | 299 | cleanSelect = function() { 300 | var table; 301 | table = current_table(); 302 | $("input[type='checkbox']").each(function() { 303 | if ($(this).prop('checked')) { 304 | return $(this).prop("checked", false); 305 | } 306 | }); 307 | scanColor(); 308 | return selectedIDS = {}; 309 | }; 310 | 311 | confirmCopy = function() { 312 | var count, cpresult, table, textarea; 313 | cpresult = ""; 314 | count = 0; 315 | table = current_table(); 316 | table.find("input[type='checkbox']").each(function() { 317 | if ($(this).prop("checked")) { 318 | cpresult += `${$(this).attr("ed2k")}\n`; 319 | return count++; 320 | } 321 | }); 322 | if (count === 0) { 323 | alert(chrome.i18n.getMessage("error_unselected")); 324 | return false; 325 | } 326 | JClipboard.copy(cpresult); 327 | textarea = $(""); 328 | textarea.val(cpresult); 329 | $("#copy").empty().append(`${chrome.i18n.getMessage("copy_success")}`).append(textarea); 330 | $("a[href='#copy']").tab("show"); 331 | $("#copy textarea").height($("#links").height()).focus().select(); 332 | return true; 333 | }; 334 | 335 | checked = function(element) { 336 | return element.prop("checked"); 337 | }; 338 | 339 | toggle_check = function(checkbox) { 340 | if (checked(checkbox)) { 341 | return checkbox.prop("checked", false); 342 | } else { 343 | return checkbox.prop("checked", true); 344 | } 345 | }; 346 | 347 | current_table = function() { 348 | return $("#" + $("#switch_ul > li > a.active").data("type") + "_table"); 349 | }; 350 | 351 | }).call(this); 352 | -------------------------------------------------------------------------------- /3.x/test_html.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Download Interstellar (2014) 1080p BrRip x264 - YIFY Torrent - KickassTorrents 6 | 7 | 8 | 9 | 10 | 11 | 12 | torrent name 13 | size 14 | files 15 | age 16 | seed 17 | leech 18 | 19 | 20 | 21 |
22 | 932 23 | 24 | 25 | 26 |
27 |
28 | 29 | 30 |
31 | 32 | Interstellar (2014) 720p BrRip x264 - YIFY 33 | 34 | 35 | Posted by YIFY in Movies > Highres Movies 36 |
37 |
38 | 1.02 GB 39 | 2 40 | 2 days 41 | 25680 42 | 13635 43 | 44 | 45 | 46 |
47 | 73 48 | 49 | 50 | 51 |
52 |
53 | 54 | 55 |
56 | 57 | Interstellar 2014 IMAX 1080p BRRip x264 DTS-JYK 58 | 59 | 60 | Posted by condors in Movies > Highres Movies 61 |
62 |
63 | 4.26 GB 64 | 12 65 | 2 days 66 | 4663 67 | 2948 68 | 69 | 70 | 71 |
72 | 11 73 | 74 | 75 | 76 |
77 |
78 | 79 | 80 |
81 | 82 | Interstellar (2014) IMAX BRRip XviD-MAXSPEED 83 | 84 | 85 | Posted by piratepedia in Movies 86 |
87 |
88 | 2.18 GB 89 | 4 90 | 2 days 91 | 1749 92 | 1353 93 | 94 | 95 | 96 |
97 | 5 98 | 99 | 100 | 101 |
102 |
103 | 104 | 105 |
106 | 107 | Interstellar 2014 BRRip XviD AC3-RARBG 108 | 109 | 110 | Posted by z0n321 in Movies 111 |
112 |
113 | 2.43 GB 114 | 5 115 | 2 days 116 | 944 117 | 1739 118 | 119 | 120 | 121 |
122 | 14 123 | 124 | 125 | 126 |
127 |
128 | 129 | 130 |
131 | 132 | Interstellar 2014 BRRip x264-RARBG 133 | 134 | 135 | Posted by z0n321 in Movies 136 |
137 |
138 | 1.46 GB 139 | 5 140 | 2 days 141 | 579 142 | 1599 143 | 144 | 145 | v class="zoea"> 146 | 156 | 157 |
158 |
159 |
下载地址1

非迅雷会员,请尽量使用QQ旋风下载,更加高速

160 |
174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ed2kChromePlugin 2 | 3 | it provides a easy way to find the ed2k_link in the pages, select and copy them. 4 | 5 | ## 4.0 6 | 7 | update to vue3 + element-plus + typescript + vite 8 | 9 | ## development 10 | 11 | `npm run dev` 入口是 `$PWD/index.html`, 注意需要修改`App.vue`里的`sendToContentScript`方法,找有下载link的 document.body.innerHTML测试, 注入到 `types.ts`里的 `TestString`, 有注释方法 12 | 13 | ## build extension 14 | 15 | `npm run build` 入口是 `$PWD/src/manifest`, 里面vite会解析`src/popup.html`, build完后chrome开发环境直接导入 `dist`目录即可 16 | 17 | ## In Google Webstore 18 | 19 | [click this link](https://chrome.google.com/webstore/detail/kmeeplonmihpchdbfccgmjhcnpecbppk) 20 | -------------------------------------------------------------------------------- /backup/background.js: -------------------------------------------------------------------------------- 1 | var result=""; 2 | var showPopup, createCopy; 3 | 4 | function onRequest(request, sender, sendResponse) { 5 | if(request.ask=="ed2klinks"){ 6 | showPopup(request.result,sender.tab.id); 7 | }else if(request.ask=="createCopy"){ 8 | createCopy(request.result); 9 | } 10 | }; 11 | 12 | chrome.extension.onRequest.addListener(onRequest); 13 | chrome.tabs.onSelectionChanged.addListener(function(id,obj){ 14 | chrome.tabs.sendRequest(id,{ask:"getResult"},function(response){ 15 | result = response.result; 16 | }); 17 | }); 18 | 19 | showPopup = function(re,id){ 20 | result = re; 21 | chrome.pageAction.show(id); 22 | } 23 | 24 | createCopy = function(re){ 25 | chrome.tabs.create({url:"copy.html"},callback); 26 | function callback(tab){ 27 | chrome.tabs.sendRequest(tab.id,re,function(response){}); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /backup/contentscript.js: -------------------------------------------------------------------------------- 1 | var regex = /ed2k:\/\/\|file\|.+?\//ig; 2 | var result =""; 3 | function init() { 4 | if (regex.test(document.body.innerHTML)) { 5 | result = document.body.innerHTML.match(regex); 6 | chrome.extension.sendRequest({ask:"ed2klinks",result:result}, 7 | function(response) { 8 | 9 | }); 10 | } 11 | } 12 | 13 | function onRequest(request,sender,sendResponse){ 14 | if(request.ask=="getResult"){ 15 | if(result){ 16 | sendResponse({result:result}); 17 | }else{ 18 | result = document.body.innerHTML.match(regex); 19 | sendResponse({result:result}); 20 | } 21 | } 22 | } 23 | init(); 24 | chrome.extension.onRequest.addListener(onRequest); 25 | 26 | 27 | -------------------------------------------------------------------------------- /backup/copy.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | $("input").bind('click',function(){ 3 | window.close(); 4 | }); 5 | }); 6 | function onRequest(request, sender, sendResponse) { 7 | var ta = $("#ta"); 8 | ta.val(request); 9 | ta.focus(); 10 | ta.select(); 11 | }; 12 | chrome.extension.onRequest.addListener(onRequest); 13 | 14 | 15 | -------------------------------------------------------------------------------- /backup/jclipboard.js: -------------------------------------------------------------------------------- 1 | JClipboard = { 2 | _createTextArea: function() { 3 | var textArea; 4 | textArea = document.createElement("textarea"); 5 | textArea.style.position = "absolute"; 6 | textArea.style.left = "-100%"; 7 | return textArea; 8 | }, 9 | copy: function(data) { 10 | var textArea; 11 | textArea = this._createTextArea(); 12 | textArea.value = data; 13 | document.body.appendChild(textArea); 14 | textArea.select(); 15 | document.execCommand("Copy"); 16 | return document.body.removeChild(textArea); 17 | }, 18 | paste: function() { 19 | var textArea, value; 20 | textArea = this._createTextArea(); 21 | document.body.appendChild(textArea); 22 | textArea.focus(); 23 | document.execCommand("Paste"); 24 | value = textArea.value; 25 | document.body.removeChild(textArea); 26 | return value; 27 | } 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /backup/popup.js: -------------------------------------------------------------------------------- 1 | var re=[]; 2 | var contabid=0; 3 | var thead = ""; 4 | var trs = ""; 5 | var selectedIDS = {}; 6 | var table={}; 7 | var maxid = 0; 8 | var data_map = { 9 | "confirmScope": confirmScope, 10 | "cleanSearch":cleanSearch, 11 | "cleanScope":cleanScope, 12 | "selectAll":selectAll, 13 | "selectOpposite":selectOpposite, 14 | "cleanSelect":cleanSelect, 15 | "confirmCopy":confirmCopy 16 | }; 17 | $(document).ready(function(){ 18 | re= chrome.extension.getBackgroundPage().result; 19 | re = dealEd2k(re); 20 | table = $("#table"); 21 | trs =wrap_tr(re); 22 | thead = table.html(); 23 | table.html(thead+trs); 24 | 25 | $("input[data-map]").live("click",function(){ 26 | data_map[($(this).attr("data-map"))](); 27 | }); 28 | 29 | $("#searchText").live("keyup",function(){ 30 | check_selected(); 31 | var st=$(this).val(); 32 | var templinkobj={}; 33 | var temptrtd=""; 34 | for(var i in re){ 35 | if(i.toLocaleLowerCase().indexOf(st.toLocaleLowerCase())>-1){ 36 | templinkobj[i] = re[i]; 37 | }; 38 | } 39 | temptrtd = wrap_tr(templinkobj); 40 | table.html(thead+temptrtd); 41 | check_selected(); 42 | }); 43 | 44 | $("td[ee]").live("click",function(){ 45 | dealChecked($(this)); 46 | scanColor(); 47 | }); 48 | 49 | $("tr").live("mouseover",function(){ 50 | $(this).css("cursor","pointer"); 51 | colorSelected($(this)); 52 | }); 53 | 54 | $("tr").live("mouseout",function(){ 55 | var cb = $(this).find("input[type='checkbox']"); 56 | if(cb.attr("checked")=="checked"){ 57 | return; 58 | } 59 | colorNotSelected($(this)); 60 | }); 61 | }); 62 | 63 | function check_selected(){ 64 | $("input[type='checkbox']").each(function(){ 65 | if($(this).attr("checked")=="checked") { 66 | var id = $(this).attr("id"); 67 | id = id.substring(3,id.length); 68 | selectedIDS[id]=0; 69 | } 70 | }); 71 | for(var i in selectedIDS){ 72 | var temp = $("#cb_"+i); 73 | if(temp){ 74 | temp.attr("checked",true); 75 | } 76 | } 77 | } 78 | 79 | function wrap_tr(linkobj){ 80 | var trtd = ""; 81 | for(var i in linkobj){ 82 | trtd += ""; 83 | trtd +=""+linkobj[i].id+""; 84 | trtd +=""+ 85 | i+ 86 | ""; 87 | trtd +=""+dealBig(linkobj[i].big)+""; 88 | trtd +=""; 89 | } 90 | return trtd; 91 | } 92 | 93 | function dealBig(num){ 94 | //alert(num); 95 | num = num.toFixed(3); 96 | var numstr=""; 97 | if(num<1) { 98 | numstr = num*1000+"KB"; 99 | }else if(num>=1 && num<10){ 100 | numstr = (Math.round(num*10))/10+"MB"; 101 | }else { 102 | numstr = Math.floor(num)+"MB"; 103 | } 104 | return numstr; 105 | } 106 | 107 | function dealEd2k(linkarr){ 108 | var result = {}; 109 | var count =0; 110 | //alert(linkarr[0]); 111 | for(var i=0;i=to )) { 133 | alert("范围输入有问题,请正确输入数字"); 134 | return; 135 | } 136 | if(to > maxid){ 137 | alert("最大范围超出有效选项数,请正确输入"); 138 | return; 139 | } 140 | 141 | for(var i=from; i<=to; i++) { 142 | selectedIDS[i] = 0; 143 | } 144 | check_selected(); 145 | scanColor(); 146 | return true; 147 | } 148 | 149 | function cleanScope() { 150 | var fromobj= document.getElementById("from"); 151 | var toobj=document.getElementById("to"); 152 | fromobj.value = ""; 153 | toobj.value = ""; 154 | } 155 | 156 | function selectAll() { 157 | $("input[type='checkbox']").each(function(){ 158 | $(this).attr("checked",true); 159 | }); 160 | scanColor(); 161 | } 162 | 163 | function selectOpposite() { 164 | $("input[type='checkbox']").each(function(){ 165 | if($(this).attr("checked")=="checked") { 166 | $(this).attr("checked",false); 167 | }else { 168 | $(this).attr("checked",true); 169 | } 170 | }); 171 | scanColor(); 172 | } 173 | 174 | function cleanSearch(){ 175 | check_selected(); 176 | document.getElementById("searchText").value = ""; 177 | table.html(thead+trs); 178 | check_selected(); 179 | scanColor(); 180 | } 181 | 182 | function cleanSelect(){ 183 | $("input[type='checkbox']").each(function(){ 184 | if($(this).attr("checked")=="checked") { 185 | $(this).attr("checked",false); 186 | } 187 | }); 188 | scanColor(); 189 | selectedIDS = {}; 190 | } 191 | 192 | function confirmCopy() { 193 | var cpresult=""; 194 | var count=0; 195 | $("input[type='checkbox']").each(function(){ 196 | if($(this).attr("checked")=="checked"){ 197 | cpresult += $(this).attr("ed2k")+"\n"; 198 | count++; 199 | } 200 | }); 201 | if(count==0){ 202 | alert("你还没有选择!"); 203 | return; 204 | } 205 | chrome.extension.sendRequest({ask:"createCopy",result:cpresult},function(response){}); 206 | //chrome.tabs.sendRequest(tabid,{fuck:"fuck"}); 207 | } 208 | 209 | function colorSelected(element){ 210 | element.children().each(function(){ 211 | $(this).css("background","#e3eaf2"); 212 | }); 213 | } 214 | 215 | function colorNotSelected(element){ 216 | element.children().each(function(){ 217 | $(this).css("background","#ffffff"); 218 | }); 219 | } 220 | 221 | function scanColor(){ 222 | $("tr").each(function(){ 223 | scanColor($(this)); 224 | }); 225 | } 226 | 227 | function scanColor(element){ 228 | if(isCheckedTr(element)){ 229 | colorSelected(element); 230 | }else{ 231 | colorNotSelected(element); 232 | } 233 | } 234 | 235 | function isCheckedTr(element){ 236 | var cb = element.find("input[type='checkbox']"); 237 | if(cb.attr("checked")=="checked"){ 238 | return true; 239 | }else { 240 | return false; 241 | } 242 | } 243 | 244 | function dealChecked(td){ 245 | var cb = td.siblings("td").find("input[type='checkbox']"); 246 | if(cb.attr("checked")=="checked"){ 247 | cb.attr("checked",false); 248 | }else { 249 | cb.attr("checked",true); 250 | } 251 | } 252 | 253 | function p(obj){ 254 | console.log(obj); 255 | alert(obj); 256 | } 257 | 258 | 259 | 260 | 261 | 262 | 263 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ed2k&Magnet 7 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ed2kmagnetchromeplugin", 3 | "version": "4.1.0", 4 | "author": "jas", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vue-tsc --noEmit && vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "@element-plus/icons-vue": "^0.2.4", 12 | "cheerio": "^1.0.0-rc.10", 13 | "core-js": "^3.6.5", 14 | "element-plus": "^1.2.0-beta.6", 15 | "lodash": "^4.17.21", 16 | "magnet-uri": "^6.2.0", 17 | "moment": "^2.29.1", 18 | "vue": "^3.2.25", 19 | "vue-router": "^4.0.11" 20 | }, 21 | "devDependencies": { 22 | "@types/chrome": "0.0.171", 23 | "@types/lodash": "^4.14.178", 24 | "@types/magnet-uri": "^5.1.3", 25 | "@types/node": "^16.11.12", 26 | "@vitejs/plugin-vue": "^2.0.0", 27 | "@vue/compiler-sfc": "^3.0.5", 28 | "less": "^4.1.2", 29 | "typescript": "^4.4.4", 30 | "vite": "^2.7.2", 31 | "vite-plugin-chrome-extension": "0.0.7", 32 | "vue-tsc": "^0.29.8" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /public/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension_name":{ 3 | "message":"Ed2k&MagnetHelper", 4 | "description":"Extension Name" 5 | }, 6 | "extension_description":{ 7 | "message":"It helps you to deal with ed2k/magnet links, with functions of select range and filename search.", 8 | "description":"Extension Description" 9 | }, 10 | "error_scope":{ 11 | "message":"Select range error. Please enter a correct number." 12 | }, 13 | "error_overlimit":{ 14 | "message":"Value exceeds valid range. Please enter a correct number." 15 | }, 16 | "error_unselected":{ 17 | "message":"You haven't selected!" 18 | }, 19 | "select_scope":{ 20 | "message":"Range" 21 | }, 22 | "confirm_scope":{ 23 | "message":"Confirm" 24 | }, 25 | "clean_scope":{ 26 | "message":"Clear" 27 | }, 28 | "scope_tips":{ 29 | "message":"(Items will be selected after you confirm.)" 30 | }, 31 | "search_filename":{ 32 | "message":"Search" 33 | }, 34 | "search_placeholder":{ 35 | "message":"Filename" 36 | }, 37 | "button_return":{ 38 | "message":"Return" 39 | }, 40 | "search_tips":{ 41 | "message":"(Selected items won't be cleared after return.)" 42 | }, 43 | "th_select":{ 44 | "message":"Select" 45 | }, 46 | "th_number":{ 47 | "message":"#" 48 | }, 49 | "th_filename":{ 50 | "message":"Filename" 51 | }, 52 | "th_size":{ 53 | "message":"Size" 54 | }, 55 | "button_selectall":{ 56 | "message":"Select All" 57 | }, 58 | "button_selectopposite":{ 59 | "message":"Inverse" 60 | }, 61 | "button_cleanselect":{ 62 | "message":"Deselect All" 63 | }, 64 | "button_confirmcopy":{ 65 | "message":"Copy" 66 | }, 67 | "result_ed2k_links":{ 68 | "message":"ed2k~" 69 | }, 70 | "result_magnet_links":{ 71 | "message":"magnet~" 72 | }, 73 | "result_links":{ 74 | "message":"Links" 75 | }, 76 | "result_copy":{ 77 | "message":"Copy" 78 | }, 79 | "copy_success":{ 80 | "message":"The content has been successfully copied to the clipboard! The following is a copy results" 81 | }, 82 | "no_data": { 83 | "message": "Refresh To Get Newest Data" 84 | }, 85 | "refresh": { 86 | "message": "refresh" 87 | } 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /public/_locales/zh_CN/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension_name":{ 3 | "message":"Ed2k&Magnet链接复制助手", 4 | "description":"插件名称" 5 | }, 6 | "extension_description":{ 7 | "message":"这是一个方便复制处理页面内大量的ed2k和magnet链接的应用,带有范围选择与文件名搜索选择的功能", 8 | "description":"插件描述" 9 | }, 10 | "error_scope":{ 11 | "message":"范围输入有问题,请输入正确数字" 12 | }, 13 | "error_overlimit":{ 14 | "message":"最大范围超出有效选项数,请输入正确数字" 15 | }, 16 | "error_unselected":{ 17 | "message":"你还没有选择!" 18 | }, 19 | "select_scope":{ 20 | "message":"选择范围" 21 | }, 22 | "confirm_scope":{ 23 | "message":"确定" 24 | }, 25 | "clean_scope":{ 26 | "message":"清除" 27 | }, 28 | "scope_tips":{ 29 | "message":"(确定后范围内的项目将会被选中)" 30 | }, 31 | "search_filename":{ 32 | "message":"搜索" 33 | }, 34 | "search_placeholder":{ 35 | "message":"文件名" 36 | }, 37 | "button_return":{ 38 | "message":"返回" 39 | }, 40 | "search_tips":{ 41 | "message":"(你在搜索时选择的选项再返回后依然存在)" 42 | }, 43 | "th_select":{ 44 | "message":"选择" 45 | }, 46 | "th_number":{ 47 | "message":"编号" 48 | }, 49 | "th_filename":{ 50 | "message":"文件名" 51 | }, 52 | "th_size":{ 53 | "message":"大小" 54 | }, 55 | "button_selectall":{ 56 | "message":"全选" 57 | }, 58 | "button_selectopposite":{ 59 | "message":"反选" 60 | }, 61 | "button_cleanselect":{ 62 | "message":"清除" 63 | }, 64 | "button_confirmcopy":{ 65 | "message":"复制" 66 | }, 67 | "result_ed2k_links":{ 68 | "message":"ed2k结果" 69 | }, 70 | "result_magnet_links":{ 71 | "message":"magnet结果" 72 | }, 73 | "result_copy":{ 74 | "message":"复制结果" 75 | }, 76 | "copy_success":{ 77 | "message":"以下是筛选结果" 78 | }, 79 | "no_data": { 80 | "message": "刷新读取数据" 81 | }, 82 | "refresh": { 83 | "message": "刷新" 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 135 | 136 | 139 | 140 | 509 | 510 | 511 | 513 | -------------------------------------------------------------------------------- /src/assets/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reducm/Ed2kChromePlugin/d8d5ec3acd6551c73e9f88c30db7c11eec747efd/src/assets/icon128.png -------------------------------------------------------------------------------- /src/assets/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reducm/Ed2kChromePlugin/d8d5ec3acd6551c73e9f88c30db7c11eec747efd/src/assets/icon16.png -------------------------------------------------------------------------------- /src/assets/icon19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reducm/Ed2kChromePlugin/d8d5ec3acd6551c73e9f88c30db7c11eec747efd/src/assets/icon19.png -------------------------------------------------------------------------------- /src/assets/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reducm/Ed2kChromePlugin/d8d5ec3acd6551c73e9f88c30db7c11eec747efd/src/assets/icon48.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reducm/Ed2kChromePlugin/d8d5ec3acd6551c73e9f88c30db7c11eec747efd/src/assets/logo.png -------------------------------------------------------------------------------- /src/background.ts: -------------------------------------------------------------------------------- 1 | chrome.runtime.onInstalled.addListener(() => console.log("background startup")) -------------------------------------------------------------------------------- /src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 35 | 36 | 53 | -------------------------------------------------------------------------------- /src/contentscript.ts: -------------------------------------------------------------------------------- 1 | import { DispatchMessageType, DocumentContentType, ResponseFunctionType } from "./types" 2 | 3 | chrome.runtime.onMessage.addListener((request: DispatchMessageType, sender, sendResponse: ResponseFunctionType) => { 4 | // 可写成switch形式 监听所有 5 | if (sender === "") { 6 | // do something 7 | } 8 | 9 | // console.log({ request }) 10 | 11 | const documentBody = document.body.innerHTML 12 | const response: DocumentContentType = {documentBody} 13 | 14 | // console.log({"contentjs的document": documentBody, response}) 15 | 16 | // 发送回传 17 | sendResponse(response); 18 | 19 | // 重发信息 20 | // chrome.runtime.sendMessage({ number: request.number + 1 }, (response) => { 21 | // console.log( 22 | // `content script -> background infos have been received. number: ${response.number}` 23 | // ); 24 | // }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import { DefineComponent } from 'vue' 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any> 7 | export default component 8 | } 9 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__MSG_extension_name__", 3 | "version": "4.5", 4 | "manifest_version": 3, 5 | "default_locale": "en", 6 | "description": "__MSG_extension_description__", 7 | "action": { 8 | "default_icon": { 9 | "19": "assets/icon19.png", 10 | "16": "assets/icon16.png", 11 | "48": "assets/icon48.png", 12 | "128": "assets/icon128.png" 13 | }, 14 | "default_title": "__MSG_extension_name__", 15 | "default_popup": "popup.html" 16 | }, 17 | "icons": { 18 | "19": "assets/icon19.png", 19 | "16": "assets/icon16.png", 20 | "48": "assets/icon48.png", 21 | "128": "assets/icon128.png" 22 | }, 23 | "background": { 24 | "service_worker": "background.ts" 25 | }, 26 | "permissions": [ 27 | "activeTab", 28 | "unlimitedStorage", 29 | "clipboardWrite" 30 | ], 31 | "homepage_url": "https://github.com/reducm/Ed2kChromePlugin", 32 | "host_permissions": ["https://*/*", "http://*/*"], 33 | "content_scripts": [ 34 | { 35 | "matches": [ 36 | "" 37 | ], 38 | "js": [ 39 | "contentscript.ts" 40 | ], 41 | "run_at": "document_end" 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /src/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ed2k&Magnet 7 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/popup.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import ElementPlus from 'element-plus' 4 | import cssString from 'element-plus/dist/index.css' 5 | 6 | 7 | const style = document.createElement('style'); 8 | document.head.append(style); 9 | style.textContent = cssString; 10 | 11 | createApp(App).use(ElementPlus).mount('#app') 12 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | // declare module '*.vue' { 2 | // import { DefineComponent } from 'vue' 3 | // const component: DefineComponent<{}, {}, any> 4 | // export default component 5 | // } 6 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | // const ed2k_regex = /ed2k:\/\/\|file\|.+?\//gi; 2 | const ed2k_regex = /ed2k:\/\/\|file\|(.+?)\|(.+?)\|.+?\//ig 3 | // const magnet_regex = /magnet\:\?[^\"]+/gi 4 | const magnet_name_regex = /dn=(.+?)&/ 5 | // 先查找全局匹配项, 这里匹配不到括号内的内容, 但能把整个文档素有magnet_link拿出, dn修复成贪婪到 [&或者\s], 适配dn后没有其他值的问问题 6 | const magnet_regex = /magnet:\?xt=urn:[a-z0-9]+:[a-z0-9]{32,40}(&dn=.+?[&\s])?/gi //tr不要,查看是否有dn, 用&结束的话,把最后的一个字符去掉 7 | // 这里去掉g下表,可以获取dn里面的内容 8 | const each_magnet_regex = /magnet:\?xt=urn:[a-z0-9]+:[a-z0-9]{32,40}(&dn=(.+))?[&\s]/i //tr不要,查看是否有dn, 用&结束的话,把最后的一个字符去掉 9 | const magnet_xt_reg = /xt=urn:btih:(.+?)&/ 10 | const magnet_xt_reg_with_no_end = /xt=urn:btih:(.+?)&?/ 11 | const magnet_dn_reg = /dn=(.+?)[\"&]/ 12 | 13 | 14 | interface DispatchMessageType { 15 | dispatch: string 16 | } 17 | 18 | interface DocumentContentType { 19 | documentBody: string 20 | } 21 | 22 | interface ResponseFunctionType { 23 | (x: DocumentContentType): void 24 | } 25 | 26 | interface Link { 27 | link: string; 28 | fileName?: string; 29 | fileSize?: string; 30 | sequence?: number; //选择的序号 31 | } 32 | 33 | const getDnByMagLink: Function = function (magLink: string): string { 34 | let match = magLink.match(new RegExp(magnet_dn_reg)) || [] 35 | return match[1] || "" 36 | } 37 | 38 | 39 | class MagnetLink implements Link { 40 | link: string 41 | fileName?: string 42 | fileSize?: string 43 | sequence: number 44 | 45 | constructor(link: string, sequence: number, name?: string) { 46 | this.link = link 47 | this.sequence = sequence + 1 48 | 49 | 50 | let countName: string = "" 51 | try { 52 | let magnetObj = getDnByMagLink(link) 53 | countName = magnetObj.dn as string 54 | } catch (e) { 55 | // do nothing 56 | } 57 | 58 | if (name) { //有传入的Atext 就用A textName 59 | this.fileName = name 60 | } else if (countName as string) { //否则用dn名 61 | this.fileName = countName as string 62 | } else { // 不然直接就用 link 63 | this.fileName = link 64 | } 65 | return this 66 | } 67 | } 68 | 69 | class Ed2kLink implements Link { 70 | link: string 71 | fileName?: string 72 | fileSize?: string 73 | sequence: number 74 | 75 | constructor(link: string, sequence: number, name?: string) { 76 | this.link = link 77 | this.sequence = sequence + 1 78 | // 每次需要重制一下 79 | let tempRegExp = new RegExp(ed2k_regex); 80 | let [tempLink, countName, big] = tempRegExp.exec(link) || [] 81 | // console.log("ed2kTestRegex: ", {tempLink, countName, big, link}) 82 | let fileSizeInt: number = big ? (parseInt(big) / (1024 * 1024)) : 0 83 | this.fileSize = this.dealBig(fileSizeInt) 84 | 85 | if (name) { //有传入的Atext 就用A textName 86 | this.fileName = name 87 | } else if (countName) { //否则用dn名 88 | this.fileName = countName 89 | 90 | } else { // 不然直接就用 link 91 | this.fileName = link 92 | } 93 | return this 94 | } 95 | 96 | dealBig(num: number): string { 97 | let numstr; 98 | if (num === 0) { 99 | return "nosize"; 100 | } 101 | 102 | num = parseInt(num.toFixed(3)); 103 | 104 | numstr = ""; 105 | if (num < 1) { 106 | numstr = `${num * 1000}KB`; 107 | } else if (num >= 1 && num < 10) { 108 | numstr = `${Math.round(num * 10) / 10}MB`; 109 | } else { 110 | numstr = `${Math.floor(num)}MB`; 111 | } 112 | return numstr; 113 | 114 | } 115 | } 116 | 117 | 118 | // 例如http://goubo.io/tv/603757f5b446a37527f4bfc3, 打开console, document.body.innerHTML 字符串复制到这里,修改App.vue下的sendToContentScript方法,即可mock 119 | const TestString = "" 120 | 121 | 122 | export { 123 | DispatchMessageType, 124 | DocumentContentType, 125 | ResponseFunctionType, 126 | MagnetLink, 127 | ed2k_regex, 128 | magnet_regex, 129 | each_magnet_regex, 130 | magnet_name_regex, 131 | Ed2kLink, 132 | TestString, 133 | magnet_xt_reg, 134 | magnet_dn_reg, 135 | magnet_xt_reg_with_no_end 136 | } 137 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | // "isolatedModules": true, 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true, 12 | "lib": [ 13 | "esnext", 14 | "dom" 15 | ], 16 | "types": [ 17 | "@types/chrome", 18 | "vite/client", 19 | "element-plus/global" 20 | ] 21 | }, 22 | "include": [ 23 | "src/**/*.ts", 24 | "src/**/*.d.ts", 25 | "src/**/*.tsx", 26 | "src/**/*.vue", 27 | "src/*.ts", 28 | "src/*.vue" 29 | ] 30 | } -------------------------------------------------------------------------------- /vetur.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // **optional** default: `{}` 3 | // override vscode settings 4 | // Notice: It only affects the settings used by Vetur. 5 | settings: { 6 | "vetur.useWorkspaceDependencies": true, 7 | "vetur.experimental.templateInterpolationService": true 8 | } 9 | } -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import { resolve } from 'path' 4 | // import { CssSyntaxError } from 'postcss' 5 | import { chromeExtension } from "vite-plugin-chrome-extension"; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig(({ command, mode }) => { 9 | console.log({ command, mode }) 10 | if (command == "serve") { 11 | // development 12 | return { 13 | // resolve: { 14 | // alias: { 15 | // "@": resolve(__dirname, "src") 16 | // } 17 | // }, 18 | server: { 19 | host: "127.0.0.1", 20 | port: 8080, 21 | }, 22 | // plugins: [vue(), chromeExtension()], 23 | plugins: [vue()], // dev 24 | build: { 25 | rollupOptions: { 26 | // input: "src/manifest.json" 27 | // input: { 28 | // index: "src/popup.html", //dev 29 | // } 30 | }, 31 | cssCodeSplit: false, 32 | chunkSizeWarningLimit: 5000, 33 | // 暂时不扰乱调试 34 | minify: false, 35 | } 36 | } 37 | } else { 38 | // 正式环境 build到dist, html用 src/popup.html 39 | return { 40 | resolve: { 41 | alias: { 42 | "@": resolve(__dirname, "src") 43 | } 44 | }, 45 | plugins: [vue(), chromeExtension()], 46 | // plugins: [vue()], // dev 47 | build: { 48 | rollupOptions: { 49 | input: "src/manifest.json" 50 | }, 51 | cssCodeSplit: false, 52 | chunkSizeWarningLimit: 5000, 53 | minify: true, 54 | } 55 | } 56 | } 57 | }) 58 | --------------------------------------------------------------------------------