├── imgs ├── demo.jpg └── douban.jpg ├── download ├── douban-movie-v1.0.crx ├── douban-movie-v1.1.crx └── douban-movie-v1.2.crx ├── gulpfile.js ├── package.json ├── LICENSE ├── .gitignore ├── README.md └── src ├── background.js └── core.js /imgs/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinple/douban-movie-extension/HEAD/imgs/demo.jpg -------------------------------------------------------------------------------- /imgs/douban.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinple/douban-movie-extension/HEAD/imgs/douban.jpg -------------------------------------------------------------------------------- /download/douban-movie-v1.0.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinple/douban-movie-extension/HEAD/download/douban-movie-v1.0.crx -------------------------------------------------------------------------------- /download/douban-movie-v1.1.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinple/douban-movie-extension/HEAD/download/douban-movie-v1.1.crx -------------------------------------------------------------------------------- /download/douban-movie-v1.2.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinple/douban-movie-extension/HEAD/download/douban-movie-v1.2.crx -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var uglify = require('gulp-uglify'); 3 | 4 | gulp.task('compress', function () { 5 | return gulp.src(['src/core.js', 'src/background.js']) 6 | .pipe(uglify()) 7 | .pipe(gulp.dest('tmp')) 8 | .pipe(gulp.dest('build')); 9 | }); 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "douban-movie-extension", 3 | "version": "1.2.0", 4 | "description": "妈妈再也不会担心我找不到资源了~", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/Neulana/douban-movie-extension.git" 8 | }, 9 | "author": "Neulana", 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/Neulana/douban-movie-extension/issues" 13 | }, 14 | "homepage": "https://github.com/Neulana/douban-movie-extension#readme", 15 | "devDependencies": { 16 | "gulp": "^4.0.0", 17 | "gulp-concat": "^2.6.1", 18 | "gulp-uglify": "^2.0.0", 19 | "gulp-watch": "^4.3.10", 20 | "pump": "^1.0.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Neulana 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | *.zip 64 | tmp 65 | 66 | *.pem -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | # 豆瓣电影传送门 4 | 5 | 我是一名豆瓣爱好者,常常在豆瓣APP上面标记想看的电影,然后在有空的时候再去找资源看。豆瓣最近新增了”在哪儿看这部电影“版块,但是仅仅收录了比较常见的几家视频网站,比如优酷、腾讯视频等,这些网站往往需要购买VIP才能观看,为了节约花在找资源上的时间,我写了这个”豆瓣电影传送门“Chrome插件,妈妈再也不会担心我找不到电影资源了~🍻🍻🍻 6 | 7 | # 使用效果 8 | 9 | 如图,在豆瓣电影页面的右边栏增加”豆瓣电影传送门“模块: 10 | 11 | ![demo](./imgs/demo.jpg) 12 | 13 | # TODO列表 14 | - 近日(2019年03月22日)发现本插件依赖的网站[neets](https://neets.cc/)在搜索结果里不再展示小网站(可能因为版权原因),严重影响了该插件的想要达到的效果。所以需要自己写搜索API来支持该插件。 15 | 16 | # 如何使用 17 | 18 | 提供两种下载方式。 19 | 20 | 方式一,Chrome官方商店: 21 | 22 | 需要翻墙,直接前往 [Chrome商店](https://chrome.google.com/webstore/detail/豆瓣电影传送门/pkidecliagangmpphpelecaoogfbnihi ) 下载; 23 | 24 | 方式二,手动安装: 25 | 26 | 1. 点击[download文件夹](https://github.com/Neulana/douban-movie-extension/tree/master/download)下载最新版本到本地; 27 | 2. 打开Chrome,在地址栏输入chrome://extensions/,回车; 28 | 3. 打开右上角的”开发者模式“开关; 29 | 4. 将已下载的crx文件拖入Chrome扩展程序窗口; 30 | 5. 打开任意电影的[豆瓣主页](https://movie.douban.com/subject/26647117/),右边就会出现”豆瓣电影传送门“版块 31 | 6. 你就可以愉快的看电影了🎉🎉🎉 32 | 33 | **说明**:有版本更新时,可在浏览器地址栏右侧找到本插件的图标,鼠标右键选择”从Chrome中移除...“,然后,再重复以上6个步骤即可,请关注本repo~ 34 | 35 | # 感谢 36 | 37 | - 灵感来自 [Ovilia](https://github.com/Ovilia/readfree-chrome-extension) ; 38 | - 感谢 [zhaopengme](https://github.com/zhaopengme) 帮忙发布到Chrome商店。 39 | 40 | 41 | # 反馈 42 | 43 | 如果使用过程中发现问题,请提 [issue](https://github.com/Neulana/douban-movie-extension/issues) 44 | 45 | 欢迎 **fork & star** 46 | 47 | 1. **Windows电脑上手动安装报错** 48 | 49 | 已解决,请参考:https://github.com/Neulana/douban-movie-extension/issues/1 50 | 51 | # 更新日志 52 | 53 | ## v1.2 54 | 55 | - 剔除AcFun和哔哩哔哩网站来源,因为这些网站常有电影剪辑视频,而非正片,这不是我想要的; 56 | - 列表默认展示的来源条目数量增加至15条。 57 | 58 | # 免责声明 59 | 该插件仅限个人学习使用,严禁用于商业用途,否则后果自负;视频来自第三方网站,与我本人无关。 60 | -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- 1 | var cached = {}; 2 | 3 | chrome.runtime.onConnect.addListener(function (port) { 4 | if (port.name == "douban-movie") { 5 | port.onMessage.addListener(function (msg) { 6 | if (cached[msg.url]) { 7 | port.postMessage(cached[msg.url]); 8 | return; 9 | } 10 | var xhr = new XMLHttpRequest(); 11 | xhr.onload = function () { 12 | if (xhr.status == 200) { 13 | var resp = JSON.parse(xhr.responseText); 14 | var resoures = resp['list']; 15 | if (resoures && resoures[0]) { 16 | msg.neetLinks = []; 17 | for (var i = 0; i < resoures.length; i++) { 18 | var themes = resoures[i]['themes']; 19 | if (themes && themes[0]) { 20 | var domainName = themes[0]['domainName']; 21 | var auxiliaryInfo = themes[0]['auxiliaryInfo']; 22 | var videoUrl = themes[0]['videoUrl']; 23 | msg.neetLinks.push([domainName, auxiliaryInfo, videoUrl]); 24 | } 25 | } 26 | msg.success = true; 27 | port.postMessage(msg); 28 | cached[msg.url] = msg; 29 | } else { 30 | msg.success = false; 31 | port.postMessage(msg); 32 | cached[msg.url] = msg; 33 | } 34 | } 35 | }; 36 | xhr.open('GET', msg.url, true); 37 | xhr.setRequestHeader('Content-type', 'text/html'); 38 | xhr.send(); 39 | }); 40 | } 41 | }); -------------------------------------------------------------------------------- /src/core.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var port = null; 4 | var dict = {}; 5 | var repoIssuesAddr = 'https://github.com/Neulana/douban-movie-extension/issues'; 6 | 7 | // search with movie name 8 | function search(movieName, parentTag) { 9 | if (!port || port.name != 'douban-movie') { 10 | port = chrome.runtime.connect({ 11 | name: 'douban-movie' 12 | }); 13 | port.onMessage.addListener(function (msg) { 14 | var url = msg.url; 15 | var num = url.search("&key="); 16 | var searchUrl = 'https://neets.cc/search?page-size=6&type=2&key=' + url.substring(num+5); 17 | var neetLinks = msg.neetLinks; 18 | if (!msg.success) { 19 | dict[url].found = false; 20 | return; 21 | } 22 | dict[url].found = true; 23 | var task; 24 | while (dict[url].tasks.length > 0) { 25 | task = dict[url].tasks.pop(); 26 | var panel = getPanel(searchUrl, neetLinks); 27 | task.parentTag.insertBefore(panel, task.parentTag.childNodes[0]); 28 | } 29 | }); 30 | } 31 | var url = 'https://smartapi.neets.cc/full-texts/grab-datas?pageNo=1&pageSize=15&seriesSize=9&themeSize=3&key=' + movieName; 32 | if (!dict[url]) { 33 | dict[url] = { 34 | tasks: [] 35 | }; 36 | } 37 | if (dict[url].found) { 38 | // var panel = getPanel(url); 39 | // parentTag.appendChild(panel); 40 | } else if (dict[url].found == undefined) { 41 | dict[url].tasks.push({ 42 | parentTag: parentTag 43 | }); 44 | port.postMessage({ 45 | url: url 46 | }); 47 | } 48 | } 49 | 50 | function getPanel(rawUrl, neetLinks) { 51 | var exclude_sites = ["acfun.cn", "bilibili.com"]; //避免抓取到同名剪辑短片 52 | var neetsDiv = document.createElement('div'); 53 | var neetsH2 = document.createElement('h2'); 54 | var feedBack = document.createElement('a'); 55 | neetsDiv.setAttribute('class', 'gray_ad'); 56 | neetsH2.innerText = '豆瓣电影传送门 · · · · · ·' 57 | feedBack.setAttribute('href', repoIssuesAddr); 58 | feedBack.setAttribute('target', '_blank'); 59 | feedBack.setAttribute('class', 'report-error'); 60 | feedBack.innerText = '反馈'; 61 | neetsDiv.appendChild(neetsH2); 62 | neetsDiv.appendChild(feedBack); 63 | var neetsUl = document.createElement('ul'); 64 | neetsUl.setAttribute('class', 'bs'); 65 | for (var i = 0; i < neetLinks.length; i++) { 66 | var href_url = neetLinks[i][2]; 67 | var exclude_mark = false; //用来跳出本轮循环 68 | for (var j = 0; j < exclude_sites.length; j++){ 69 | if (href_url.indexOf(exclude_sites[j]) != -1) { 70 | console.log("url: " + href_url); 71 | exclude_mark = true; 72 | } 73 | } 74 | if (exclude_mark == true) { 75 | continue; 76 | } 77 | var neetsLi = document.createElement('li'); 78 | var neetsMovie = document.createElement('a'); 79 | var neetsSpan = document.createElement('span'); 80 | var auxiliaryInfo = neetLinks[i][1]; 81 | neetsMovie.setAttribute('href', href_url); 82 | neetsMovie.setAttribute('target', '_blank'); 83 | neetsMovie.setAttribute('class', 'playBtn'); 84 | neetsMovie.innerHTML = neetLinks[i][0]; 85 | neetsSpan.setAttribute('class', 'buylink-price'); 86 | auxiliaryInfo = auxiliaryInfo.replace(' ', ''); 87 | if (auxiliaryInfo) { 88 | neetsSpan.innerHTML = auxiliaryInfo; 89 | } else { 90 | neetsSpan.innerHTML = '免费'; 91 | } 92 | neetsLi.appendChild(neetsMovie); 93 | neetsLi.appendChild(neetsSpan); 94 | neetsUl.appendChild(neetsLi); 95 | } 96 | var neetsLiMore = document.createElement('li'); 97 | var neetsMovieMore = document.createElement('a'); 98 | neetsMovieMore.setAttribute('href', rawUrl); 99 | neetsMovieMore.setAttribute('target', '_blank'); 100 | neetsMovieMore.setAttribute('class', 'playBtn'); 101 | neetsMovieMore.innerHTML = '发现更多>>>'; 102 | neetsLiMore.appendChild(neetsMovieMore); 103 | neetsUl.appendChild(neetsLiMore); 104 | neetsDiv.appendChild(neetsUl); 105 | return neetsDiv; 106 | } 107 | 108 | function runDouban() { 109 | var h1 = document.getElementsByTagName('h1')[0]; 110 | var movieName = h1.firstElementChild.innerText.split(' ')[0]; 111 | var aside = document.getElementsByClassName('aside'); 112 | if (aside) { 113 | search(movieName, aside[0]); 114 | } 115 | 116 | // douban book 117 | if (window.location.hostname === 'book.douban.com') { 118 | var menu = document.getElementsByClassName('nav-items'); 119 | if (menu && menu[0]) { 120 | // show link only when mouse hover 121 | menu[0].addEventListener("mouseover", function () { 122 | var link = document.getElementById('neets-menu'); 123 | if (link) { 124 | link.style['display'] = 'inline-block'; 125 | } 126 | }, false); 127 | menu[0].addEventListener("mouseout", function () { 128 | var link = document.getElementById('neets-menu'); 129 | if (link) { 130 | link.style['display'] = 'none'; 131 | } 132 | }, false); 133 | } 134 | } 135 | } 136 | 137 | function insertCss() { 138 | // insert css 139 | var style = document.createElement('style'); 140 | // webkit hack 141 | style.appendChild(document.createTextNode('')); 142 | // insert to head 143 | document.head.appendChild(style); 144 | 145 | // rules 146 | var rules = { 147 | '.buylink-price': { 148 | left: '130px', 149 | position: 'absolute', 150 | color: '#999' 151 | }, 152 | '.gray_ad .report-error': { 153 | color: '#999', 154 | display: 'none', 155 | position: 'absolute', 156 | top: '10px', 157 | right: '10px', 158 | background: 'transparent' 159 | }, 160 | '.gray_ad .report-error:hover': { 161 | display: 'inline' 162 | } 163 | }; 164 | for (var ele in rules) { 165 | var rulesStr = ele + '{'; 166 | for (var attr in rules[ele]) { 167 | rulesStr += attr + ': ' + rules[ele][attr] + ';'; 168 | } 169 | rulesStr += '} '; 170 | style.sheet.insertRule(rulesStr, 0); 171 | } 172 | } 173 | 174 | insertCss(); 175 | var host = window.location.hostname; 176 | if (host === 'movie.douban.com') { 177 | runDouban(); 178 | } 179 | })(); 180 | --------------------------------------------------------------------------------