├── docs ├── amo-button.png ├── h5vew-1280.png ├── screenshot.png ├── tutorials │ ├── tutorials.json │ └── h5vew-adding-new-website-support.md ├── vendor │ ├── font-awesome │ │ └── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ └── bootstrap │ │ └── css │ │ ├── bootstrap-reboot.min.css │ │ └── bootstrap-reboot.css └── index.html ├── icons ├── h5vew-128.png ├── h5vew-19.png ├── h5vew-32.png ├── h5vew-38.png ├── h5vew-64.png └── h5vew-1280.png ├── .eslintignore ├── .gitignore ├── spec ├── break.md ├── dailymotion.md ├── metacafe.md ├── facebook.md ├── vimeo.md └── youtube.md ├── scripts ├── pre-commit.hook └── post-commit.hook ├── .babelrc ├── .jsbeautifyrc ├── .github └── FUNDING.yml ├── .deploy ├── jsdoc.json ├── .eslintrc.js ├── options ├── options.css ├── options.js └── options.html ├── package.json ├── popup ├── dashboard.html ├── dashboard.css └── dashboard.js ├── content ├── Facebook.js ├── common.js ├── Metacafe.js ├── Break.js ├── Lego.js ├── Dailymotion.js ├── statics.js ├── Module.js ├── Options.js ├── Vimeo.js ├── video-player.js └── YouTube.js ├── background.js ├── .stylelintrc ├── test └── test.html ├── README.md ├── manifest.json ├── _locales ├── en │ └── messages.json └── pt_BR │ └── messages.json ├── gulpfile.js ├── LICENSE └── vendor └── iaextractor ├── youtube.js └── LICENSE /docs/amo-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/docs/amo-button.png -------------------------------------------------------------------------------- /docs/h5vew-1280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/docs/h5vew-1280.png -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/docs/screenshot.png -------------------------------------------------------------------------------- /icons/h5vew-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/icons/h5vew-128.png -------------------------------------------------------------------------------- /icons/h5vew-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/icons/h5vew-19.png -------------------------------------------------------------------------------- /icons/h5vew-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/icons/h5vew-32.png -------------------------------------------------------------------------------- /icons/h5vew-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/icons/h5vew-38.png -------------------------------------------------------------------------------- /icons/h5vew-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/icons/h5vew-64.png -------------------------------------------------------------------------------- /icons/h5vew-1280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/icons/h5vew-1280.png -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Always ignore node_modules 2 | **/node_modules/**/*.* 3 | **/vendor/**/*.* 4 | **/.git/** 5 | **/dist/** 6 | -------------------------------------------------------------------------------- /docs/tutorials/tutorials.json: -------------------------------------------------------------------------------- 1 | { 2 | "h5vew-adding-new-website-support": { 3 | "title": "Adding new website support" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/docs/vendor/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.xpi 2 | /*.zip 3 | /node_modules/ 4 | /web-ext-artifacts/ 5 | /dumps/ 6 | /dist/ 7 | /docs/api/ 8 | /builds/ 9 | /certs/ 10 | /package-lock.json 11 | -------------------------------------------------------------------------------- /docs/vendor/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/docs/vendor/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/vendor/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/docs/vendor/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/vendor/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/docs/vendor/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/vendor/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tikservices/html5-video-everywhere/HEAD/docs/vendor/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /spec/break.md: -------------------------------------------------------------------------------- 1 | **Milestone:** 2 | [Break Support](https://github.com/lejenome/html5-video-everywhere/milestones/Break%20Support) 3 | 4 | **URL:** 5 | - [X] `break.com/embed/` 6 | -------------------------------------------------------------------------------- /scripts/pre-commit.hook: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # INSTALL: ln -s $(pwd)/pre-commit.hook .git/hooks/pre-commit 3 | make || exit 1 4 | git diff --cached --name-only --diff-filter=ACM | while read F; do 5 | git add "$F" 6 | done 7 | # git update-index # --really-refresh 8 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { 5 | "firefox": 52, 6 | "chrome": 61 7 | }, 8 | modules: false 9 | }] 10 | ], 11 | "plugins": [], 12 | "ignore": [ 13 | 'node_modules/**', 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /spec/dailymotion.md: -------------------------------------------------------------------------------- 1 | **Milestone:** 2 | [Dailymotion Support](https://github.com/lejenome/html5-video-everywhere/milestones/Dailymotion%20Support) 3 | 4 | **URL:** 5 | - [x] `www.dailymotion.com/embed/video/` 6 | - [x] `www.dailymotion.com/video/` 7 | - [ ] `www.dailymotion.com/playlist//1#video=` 8 | - [ ] `www.dailymotion.com/` 9 | -------------------------------------------------------------------------------- /spec/metacafe.md: -------------------------------------------------------------------------------- 1 | **Milestone:** 2 | [Metacafe Support](https://github.com/lejenome/html5-video-everywhere/milestones/Metacafe%20Support) 3 | 4 | **URL:** 5 | - [x] `metacafe.com/watch//` 6 | - [x] `metacafe.com/<CHANNEL_NAME>` #6 7 | 8 | **SWF:** 9 | - [ ] `s.mcstatic.com/Flash/vp/mc/Portal_4.0.2.5.swf` 10 | - [ ] `metacafe.com/fplayer/<VIDEO_ID>/<TITLE>.swf` 11 | -------------------------------------------------------------------------------- /spec/facebook.md: -------------------------------------------------------------------------------- 1 | **Milestone:** 2 | [Facebook Support](https://github.com/lejenome/html5-video-everywhere/milestones/Facebook%20Support) 3 | 4 | **URL:** 5 | - [x] `facebook.com/video?v=<VIDEO_ID>` 6 | - [x] `www.facebook.com/video?v=<VIDEO_ID>` 7 | - [x] `beta.facebook.com/video?v=<VIDEO_ID>` 8 | - [x] `facebook.com/<USERNAME>/videos/<VIDEO_ID>` 9 | 10 | **IN PAGE EVENTS:** 11 | - [ ] showing a video on the PhotoViewer dialog 12 | -------------------------------------------------------------------------------- /.jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "indent_size": 2, 3 | "indent_char": " ", 4 | "indent_level": 0, 5 | "indent_with_tabs": false, 6 | "preserve_newlines": true, 7 | "max-preserve-newlines": 2, 8 | "end_with_newline": true, 9 | "wrap-line-length": 120, 10 | "css": { 11 | "preserve_newlines": false, 12 | "indent_size": 4 13 | }, 14 | "js": { 15 | "wrap-line-length": 120 16 | }, 17 | "html": { 18 | "wrap-line-length": 120 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [lejenome] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | custom: https://www.paypal.me/lejenome 10 | -------------------------------------------------------------------------------- /spec/vimeo.md: -------------------------------------------------------------------------------- 1 | **Milestone:** 2 | [Vimeo Support](https://github.com/lejenome/html5-video-everywhere/milestones/Vimeo%20Support) 3 | 4 | **URL:** 5 | - [x] `vimeo.com/channels/<CHANNEL_NAME>` 6 | - [x] `player.vimeo.com/video/<VIDEO_ID>` 7 | - [x] `vimeo.com/<VIDEO_ID>` 8 | - [x] `vimeo.com/<USER>` 9 | - [ ] `vimeo.com/couchmode/user<USER_ID>/videos/sort:<SORT_TYPE>/<VIDEO_ID>` #4 10 | 11 | **SWF:** 12 | - [ ] `vimeo.com/moogaloop.swf?clip_id=<VIDEO_ID>` (Flash player used by Facebook) #5 13 | 14 | **API:** 15 | more @ player.vimeo.com/playground 16 | -------------------------------------------------------------------------------- /.deploy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -x 3 | export PATH="$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 4 | 5 | # This script is used by h5vew.tik.tn to generate the static website content on 6 | # every new git commit push to the extension Github repository. 7 | # It generates docs/ content which is the root of h5vew.tik.tn 8 | 9 | # TODO: dev, production 10 | ENV=${1:-dev} 11 | 12 | if [ -e "package.json" ]; then 13 | yarn --link-duplicates --non-interactive --no-audit --ignore-engines install 14 | 15 | npx gulp docs 16 | fi 17 | -------------------------------------------------------------------------------- /jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": [ 5 | "jsdoc", 6 | "closure" 7 | ] 8 | }, 9 | "source": { 10 | "include": [ 11 | "content" 12 | ], 13 | "includePattern": ".+\\.js(doc|x)?$", 14 | "excludePattern": "(^|\\/|\\\\)_" 15 | }, 16 | "sourceType": "module", 17 | "plugins": [ 18 | "plugins/markdown" 19 | ], 20 | "templates": { 21 | "cleverLinks": false, 22 | "monospaceLinks": false 23 | }, 24 | "opts": { 25 | "template": "node_modules/minami", 26 | "encoding": "utf8", 27 | "destination": "docs/api", 28 | "recurse": true, 29 | "tutorials": "docs/tutorials", 30 | "readme": "README.md" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /scripts/post-commit.hook: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # INSTALL: ln -s $(pwd)/post-commit.hook .git/hooks/post-commit 3 | version=$(git diff HEAD^..HEAD -- "$(git rev-parse --show-toplevel)"/package.json \ 4 | | grep '^\+\s*"version"' \ 5 | | sed -s 's/[^0-9\.]//g') 6 | 7 | if [ "$version" != "" ]; then 8 | git tag -a "v$version" -m "$(git log -1 --format=%s)" 9 | echo "Created a new tag, v$version" 10 | [ -e ../lejenome.github.io ] \ 11 | && echo "Uploading XPI file to the site" \ 12 | && cp "$(git rev-parse --show-toplevel)"/*.xpi ../lejenome.github.io/tests \ 13 | && git -C ../lejenome.github.io add tests/\*.xpi \ 14 | && git -C ../lejenome.github.io commit -m "update xpi files" \ 15 | && git -C ../lejenome.github.io push 16 | fi 17 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "google", 3 | "env": { 4 | "browser": true, 5 | "es6": true, 6 | "webextensions": true 7 | }, 8 | "globals": { 9 | "require": false, 10 | "define": false, 11 | "$": false, 12 | "XMLHttpRequest": false 13 | }, 14 | "parserOptions": { 15 | "ecmaVersion": 2017, 16 | "sourceType": "module", 17 | }, 18 | "plugins": [ 19 | "html", 20 | "json", 21 | ], 22 | "rules": { 23 | "strict": [2, "global"], 24 | "semi": [2, "always"], 25 | "quotes": ["off", "double"], 26 | "max-len": [1, 120], 27 | "switch-colon-spacing": ["off", { 28 | "after": true, 29 | "before": false 30 | }], 31 | "require-jsdoc": "off", 32 | "indent": "off", 33 | "no-unused-vars": "off", 34 | "prefer-promise-reject-errors": "off", 35 | "prefer-const": "off", 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /spec/youtube.md: -------------------------------------------------------------------------------- 1 | **Milestone:** 2 | [YouTube Support](https://github.com/lejenome/html5-video-everywhere/milestones/YouTube%20Support) 3 | 4 | **URL:** 5 | - [x] `www.youtube.com/v/<VIDEO_ID>` 6 | - [x] `www.youtube.com/watch?v=<VIDEO_ID>` 7 | - [x] `www.youtube.com/embed/<VIDEO_ID>` 8 | - [ ] `www.youtube.com/apiplayer?video_id=<VIDEO_ID>&version=3` #2 9 | - [ ] `www.youtube.com/embed?listType=playlist&list=PL<PLAYLIST_ID>` #3 10 | - [ ] `www.youtube.com/embed?listType=user_uploads&list=<USERNAME>` #3 11 | - [ ] `www.youtube.com/embed?listType=search&list=<QUERY>` #3 12 | 13 | **QUERY:** 14 | - [x] autoplay 1,0 15 | - [ ] autohide 0,1,2 16 | - [ ] color 17 | - [ ] controls 0,1,2 18 | - [ ] enablejsapi 0,1 19 | - [ ] end 20 | - [ ] fs 0,1 21 | - [x] loop 0,1 22 | - [ ] playlist 23 | - [x] start 24 | - [x] t 25 | 26 | **API:** 27 | more @ https://developers.google.com/youtube/player_parameters 28 | -------------------------------------------------------------------------------- /options/options.css: -------------------------------------------------------------------------------- 1 | form { 2 | width: 100%; 3 | color: rgba(0, 0, 0, 0.8); 4 | box-sizing: border-box; 5 | } 6 | 7 | form>div { 8 | padding: 5px 8px; 9 | border-bottom: 1px solid rgba(0, 0, 0, 0.2); 10 | min-height: 30px !important; 11 | } 12 | 13 | form>div:last-of-type { 14 | border: none; 15 | } 16 | 17 | form>div>label { 18 | display: inline-block; 19 | width: 100%; 20 | min-height: 100%; 21 | line-height: 30px; 22 | text-shadow: 0 1px 1px #fefffe; 23 | } 24 | 25 | form>div>label>input, 26 | form>div>label>select { 27 | float: right; 28 | display: inline-block; 29 | min-width: 100px; 30 | width: calc(100% - 300px - 22px); 31 | height: 100%; 32 | } 33 | 34 | form>div>button { 35 | margin-top: 10px; 36 | float: right; 37 | width: 120px; 38 | height: 30px; 39 | font-weight: bold; 40 | color: rgba(20, 50, 185, 0.8); 41 | font-size: 90%; 42 | } 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "license": "MPL-2.0", 4 | "dependencies": { 5 | "bootstrap": "~4.4", 6 | "font-awesome": "~4.7.0" 7 | }, 8 | "devDependencies": { 9 | "babel-core": "^6.26.0", 10 | "babel-preset-env": "^1.6.0", 11 | "del": "^3.0.0", 12 | "eslint-config-google": "^0.12.0", 13 | "eslint-plugin-html": "^5.0", 14 | "eslint-plugin-json": "^1.2.0", 15 | "gulp": "^4.0", 16 | "gulp-better-rollup": "^3.4.0", 17 | "gulp-crx-pack": "^1.0.1", 18 | "gulp-eslint": "^5.0", 19 | "gulp-jsbeautifier": "^3.0", 20 | "gulp-jsdoc3": "^2.0", 21 | "gulp-rename": "^1.2.2", 22 | "gulp-sourcemaps": "^2.6.4", 23 | "gulp-stylelint": "^8.0", 24 | "gulp-watch": "^5.0", 25 | "jsdoc": "^3.5.5", 26 | "minami": "^1.2.3", 27 | "rollup": "^1.2", 28 | "rollup-plugin-babel": "^4.0.0", 29 | "rollup-stream": "^1.24.1", 30 | "stylelint": "^13.0.0", 31 | "vinyl-source-stream": "^2.0", 32 | "web-ext": "^4.0.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /popup/dashboard.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | 4 | <head> 5 | <meta charset="utf-8" /> 6 | <title>Dashboard | HTML5 Video Everywhere 7 | 8 | 9 | 10 | 11 |
12 |

13 |
14 |
15 | 27 |
28 | 29 | 30 | 31 | 32 |
33 |
34 | 35 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /content/Facebook.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Facebook website support module. 3 | * @author Moez Bouhlel 4 | * @license MPL-2.0 5 | * @copyright 2014-2017 Moez Bouhlel 6 | */ 7 | 8 | import Module from './Module.js'; 9 | import VP from './video-player.js'; 10 | 11 | class Facebook extends Module { 12 | constructor() { 13 | super("facebook"); 14 | } 15 | onInteractive() { 16 | let params = document.body.innerHTML.match(/"params",("[^"]*")/)[1]; 17 | params = JSON.parse(decodeURIComponent(JSON.parse(params))); 18 | // let container = document.getElementsByClassName("_53j5")[0]; 19 | let container = document.getElementsByClassName("stageContainer")[0]; 20 | let vp = new VP(container, this.options); 21 | this.log("params.video_data[0]:", params.video_data[0]); 22 | vp.srcs({ 23 | "medium/mp4": params.video_data[0].sd_src, 24 | "high/mp4": params.video_data[0].hd_src, 25 | }); 26 | vp.props({ 27 | controls: true, 28 | autoplay: this.options.isAutoPlay(true), 29 | preload: this.options.getPreload(), 30 | loop: this.options.isLoop(), 31 | }); 32 | vp.style({ 33 | width: "100%", 34 | heigth: "100%", 35 | }); 36 | vp.setup(); 37 | } 38 | } 39 | 40 | new Facebook().start(); 41 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Extension backgroun script. 3 | * @author Moez Bouhlel 4 | * @license MPL-2.0 5 | * @copyright 2014-2017 Moez Bouhlel 6 | */ 7 | 8 | /** 9 | * List of message exchange ports with other scripts. 10 | * @const {chrome.runtime.Port[]} 11 | * @private 12 | */ 13 | const ports = []; 14 | 15 | /** 16 | * New message handler. Message can be from content-script, options_ui or 17 | * popup script. 18 | * 19 | * @param {Object} msg - Message object recived. 20 | * @param {chrome.runtime.Port} port - Message exchange port. 21 | * @private 22 | */ 23 | function onMessage(msg, port) { 24 | console.log("New message:", msg); 25 | switch (msg.type) { 26 | case "inject": 27 | chrome.storage.sync.get(null, (opts) => 28 | port.postMessage({ 29 | "type": "options", 30 | "options": opts, 31 | })); 32 | break; 33 | default: 34 | break; 35 | } 36 | } 37 | 38 | function onDisconnect(port) {} 39 | 40 | chrome.runtime.onConnect.addListener((port) => { 41 | console.log("[h5vew] New connection:", port.name); 42 | ports.push(port); 43 | port.onMessage.addListener(onMessage); 44 | port.onDisconnect.addListener(onDisconnect); 45 | chrome.pageAction.show(port.sender.tab.id); 46 | }); 47 | -------------------------------------------------------------------------------- /popup/dashboard.css: -------------------------------------------------------------------------------- 1 | body { 2 | min-width: 250px; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | header { 8 | width: 100%; 9 | background-color: cadetblue; 10 | padding: 0 10px; 11 | color: aliceblue; 12 | height: 40px; 13 | text-align: center; 14 | } 15 | 16 | header>* { 17 | display: inline-block; 18 | margin-right: 5px; 19 | } 20 | 21 | form { 22 | width: 100%; 23 | color: rgba(0, 0, 0, 0.8); 24 | box-sizing: border-box; 25 | } 26 | 27 | form>div { 28 | padding: 5px 8px; 29 | box-sizing: border-box; 30 | border-bottom: 1px solid rgba(0, 0, 0, 0.2); 31 | min-height: 30px !important; 32 | } 33 | 34 | form>div:last-of-type { 35 | border: none; 36 | } 37 | 38 | form>div>label { 39 | display: inline-block; 40 | width: 100%; 41 | min-height: 100%; 42 | /* vertical-align: sub; 43 | -vertical-align: -moz-middle-with-baseline;*/ 44 | line-height: 30px; 45 | text-shadow: 0 1px 1px #fefffe; 46 | } 47 | 48 | form>div>label>input, 49 | form>div>label>select { 50 | float: right; 51 | display: inline-block; 52 | width: calc(100% - 300px - 22px); 53 | height: 100%; 54 | } 55 | 56 | form>div>button { 57 | /*float: right;*/ 58 | display: inline-block; 59 | min-width: 30px; 60 | height: 30px; 61 | font-weight: bold; 62 | color: rgba(20, 50, 185, 0.8); 63 | font-size: 90%; 64 | } 65 | 66 | .s12 { 67 | width: 100%; 68 | } 69 | 70 | .s4 { 71 | width: 32%; 72 | } 73 | 74 | .s3 { 75 | width: 23.8%; 76 | } 77 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "color-no-invalid-hex": true, 4 | "font-family-no-duplicate-names": true, 5 | "font-family-name-quotes": "always-where-recommended", 6 | "function-name-case": "lower", 7 | "function-url-no-scheme-relative": true, 8 | "function-url-quotes": "always", 9 | "number-no-trailing-zeros": true, 10 | "length-zero-no-unit": true, 11 | "unit-case": "lower", 12 | "unit-no-unknown": true, 13 | "property-case": "lower", 14 | "property-no-unknown": true, 15 | "keyframe-declaration-no-important": true, 16 | "declaration-block-no-shorthand-property-overrides": true, 17 | "declaration-block-single-line-max-declarations": 1, 18 | "declaration-block-trailing-semicolon": "always", 19 | "declaration-block-semicolon-newline-after": "always-multi-line", 20 | "block-no-empty": true, 21 | "selector-pseudo-class-no-unknown": true, 22 | "selector-pseudo-element-no-unknown": true, 23 | "selector-pseudo-element-case": "lower", 24 | "selector-type-case": "lower", 25 | "selector-type-no-unknown": true, 26 | "selector-max-empty-lines": 0, 27 | "media-feature-name-case": "lower", 28 | "media-feature-name-no-unknown": [true, { 29 | ignoreMediaFeatureNames: ["min--moz-device-pixel-ratio"] 30 | }], 31 | "comment-no-empty": true, 32 | "max-nesting-depth": 5, 33 | "no-invalid-double-slash-comments": true, 34 | "no-unknown-animations": true, 35 | "no-extra-semicolons": true, 36 | "no-missing-end-of-source-newline": true, 37 | "no-eol-whitespace": true 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /options/options.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Extension options ui script. 3 | * @author Moez Bouhlel 4 | * @license MPL-2.0 5 | * @copyright 2014-2017 Moez Bouhlel 6 | */ 7 | 8 | import Options from '../content/Options.js'; 9 | 10 | let options; 11 | const form = document.getElementById("options"); 12 | const elements = Array.from(form.elements) 13 | .filter((e) => e.type !== "submit" && e.type !== "button"); 14 | 15 | function saveOptions(e) { 16 | for (const el of elements) { 17 | if (el.type === "checkbox") { 18 | options.set(el.name, el.checked); 19 | } else { 20 | options.set(el.name, el.checked); 21 | } 22 | } 23 | chrome.storage.sync.set(options.getAll()); 24 | e.preventDefault(); 25 | } 26 | 27 | function changeOption(e) { 28 | let el = e.target; 29 | if (el.type === "checkbox") { 30 | options.set(el.name, el.checked); 31 | } else { 32 | options.set(el.name, el.value); 33 | } 34 | console.log("[h5vew:options] Change", el.name, options.get(el.name)); 35 | chrome.storage.sync.set({ 36 | [el.name]: options.get(el.name), 37 | }); 38 | e.preventDefault(); 39 | } 40 | 41 | function restoreOptions() { 42 | chrome.storage.sync.get(null, (res) => { 43 | options = new Options(res); 44 | for (const el of elements) { 45 | if (el.type === "checkbox") { 46 | el.checked = options.get(el.name); 47 | } else { 48 | el.value = options.get(el.name); 49 | } 50 | } 51 | }); 52 | } 53 | 54 | restoreOptions(); 55 | form.addEventListener("submit", saveOptions); 56 | form.addEventListener("change", changeOption); 57 | -------------------------------------------------------------------------------- /popup/dashboard.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Extension page popup script. 3 | * @author Moez Bouhlel 4 | * @license MPL-2.0 5 | * @copyright 2014-2017 Moez Bouhlel 6 | */ 7 | 8 | /* TODO: Show module special options on popup 9 | import Options from "../content/Options.js"; 10 | 11 | let options; 12 | const form = document.getElementById("form"); 13 | const elements = Array.from(form.elements).filter((e) => 14 | e.type !== "submit" && e.type !== "button" 15 | ); 16 | */ 17 | 18 | function onClick(evt) { 19 | switch (evt.target.name) { 20 | case "donate": 21 | chrome.tabs.create({ 22 | active: true, 23 | url: "https://www.paypal.me/lejenome", 24 | }); 25 | break; 26 | case "report": 27 | chrome.tabs.create({ 28 | active: true, 29 | url: "https://github.com/lejenome/html5-video-everywhere/issues/", 30 | }); 31 | break; 32 | case "about": 33 | chrome.tabs.create({ 34 | active: true, 35 | url: "https://h5vew.tik.tn/", 36 | }); 37 | break; 38 | case "options": 39 | chrome.runtime.openOptionsPage(); 40 | break; 41 | default: 42 | break; 43 | } 44 | } 45 | 46 | function restoreOptions() { 47 | chrome.storage.sync.get(null, (res) => { 48 | // options = new Options(res); 49 | }); 50 | } 51 | 52 | function main() { 53 | let manifest = chrome.runtime.getManifest(); 54 | document.getElementById("name").textContent = manifest.name; 55 | document.getElementById("version").textContent = manifest.version; 56 | document.addEventListener("click", onClick); 57 | restoreOptions(); 58 | } 59 | 60 | main(); 61 | -------------------------------------------------------------------------------- /content/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Common functions and helpers. 3 | * @author Moez Bouhlel 4 | * @license MPL-2.0 5 | * @copyright 2014-2017 Moez Bouhlel 6 | * @module 7 | */ 8 | 9 | /** 10 | * Send setClipboard message to thebackground script with text to add to 11 | * clipboard. 12 | * 13 | * @param {string} text - Text to add to clipboard. 14 | * 15 | * @deprecated Not ported to WebExtension api yet. 16 | */ 17 | export function setClipboard(text) { 18 | self.port.emit("setClipboard", text); 19 | } 20 | 21 | /** 22 | * Create new HTML element. 23 | * 24 | * @param {string} tag - Element tag name 25 | * @param {Object} properties - Mapping of element properties and its values 26 | * @param {Object} styles - Mapping of element styles to apply 27 | * @param {Object} dataset - Mapping of element data set. 28 | * 29 | * @return {HTMLElement} The created element. 30 | */ 31 | export function createNode(tag, properties, styles, dataset) { 32 | let node = document.createElement(tag); 33 | if (properties) Object.keys(properties).forEach((p) => node[p] = properties[p]); 34 | if (styles) Object.keys(styles).forEach((s) => node.style[s] = styles[s]); 35 | if (dataset) Object.keys(dataset).forEach((d) => node.dataset[d] = dataset[d]); 36 | return node; 37 | }; 38 | 39 | /** 40 | * Wrapper around fetch api to return the URL fetched content as text 41 | * 42 | * @param {string} url - URL to fetch its content. 43 | * @param {Object} headers - Headers to add to the fetch request. 44 | * 45 | * @return {Promise} Fetch promise with the response text as argument on 46 | * success 47 | */ 48 | export function asyncGet(url, headers = {}) { 49 | return fetch(url, { 50 | headers: headers, 51 | }).then((res) => { 52 | if (res.ok) return res.text(); 53 | else return Promise.reject(); 54 | }); 55 | }; 56 | 57 | /** 58 | * Remove HTML element all child elements 59 | * 60 | * @param {HTMLElement} parent - HTML element to remove it children 61 | */ 62 | export function rmChildren(parent) { 63 | while (parent && parent.firstChild) { 64 | parent.removeChild(parent.firstChild); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HTML5 Video EveryWhere Manual Test 6 | 17 | 34 | 35 | 36 | 37 | 54 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /content/Metacafe.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metacafe website support module. 3 | * @author Moez Bouhlel 4 | * @license MPL-2.0 5 | * @copyright 2014-2017 Moez Bouhlel 6 | */ 7 | 8 | import Module from './Module.js'; 9 | import VP from './video-player.js'; 10 | import { 11 | asyncGet, 12 | } from './common.js'; 13 | 14 | // eslint-disable-next-line no-unused-vars 15 | class Metacafe extends Module { 16 | constructor() { 17 | super("metacafe"); 18 | } 19 | 20 | onInteractive() { 21 | if (window.location.pathname.startsWith("/watch/")) { 22 | this.watchPage(); 23 | } else if (/[^\/]+\/?$/.test(location.pathname)) { 24 | this.channelPage(); 25 | } 26 | } 27 | 28 | watchPage() { 29 | let data; 30 | if (document.getElementById("json_video_data")) { 31 | data = JSON.parse(document.getElementById("json_video_data").textContent); 32 | } 33 | if (!data) return; 34 | let url = data.sources[0].src; 35 | let container = document.getElementsByClassName("mc-player"); 36 | if (container.length === 0) return; 37 | container = container[0]; 38 | let vp = new VP(container, this.options); 39 | vp.addSrc(url, "medium", "mp4"); 40 | vp.props({ 41 | autoplay: this.options.isAutoPlay(true), 42 | preload: this.options.getPreload(), 43 | loop: this.options.isLoop(), 44 | controls: true, 45 | }); 46 | vp.style({ 47 | width: "100%", 48 | }); 49 | vp.setup(); 50 | } 51 | 52 | channelPage() { 53 | let embed = document.getElementsByTagName("embed"); 54 | if (!embed) return; 55 | embed = embed[0]; 56 | let page = embed.src; 57 | page = page.replace("/fplayer/", "/watch/").replace(/.swf$/, ""); 58 | asyncGet(page).then((data) => { 59 | let url = this.getURL(data); 60 | let container = document.getElementById("ItemContainer"); 61 | // let container = embed.parentElement; 62 | let vp = new VP(container); 63 | vp.addSrc(url, "medium", "mp4"); 64 | vp.props({ 65 | autoplay: this.options.isAutoPlay(false), 66 | preload: this.options.getPreload(), 67 | loop: this.options.isLoop(), 68 | controls: true, 69 | }); 70 | vp.style({ 71 | width: "100%", 72 | }); 73 | vp.setup(); 74 | }); 75 | } 76 | 77 | getURL(e) { 78 | let data = decodeURIComponent(e.match(/&mediaData=([^&]*)&/)[1]); 79 | return JSON.parse(data).MP4.mediaURL; 80 | } 81 | } 82 | 83 | // new Metacafe().start(); // FIXME: needs HLS support 84 | -------------------------------------------------------------------------------- /content/Break.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Break.com website support module. 3 | * @author Moez Bouhlel 4 | * @license MPL-2.0 5 | * @copyright 2014-2017 Moez Bouhlel 6 | */ 7 | 8 | import Module from './Module.js'; 9 | import VP from './video-player.js'; 10 | 11 | /** 12 | * Break.com website support. 13 | * Break does host its video player within an iframe. It also embeds videos 14 | * from YouTube (and an other source?). 15 | * In case video is self-hosted (in iframe), configurations are available on 16 | * embedVars object. 17 | * Videos url are avaialbe on: 18 | * 19 | * ``` 20 | * embedVars.media[{ 21 | * url, // Absolute url to the video, 22 | * height, // height of video on pixels 23 | * width, // width of video on pixels 24 | * mediaPurpose, // equals to "play" in our case 25 | * }] 26 | * ``` 27 | * 28 | * Video poster image is available on `embedVars.thumbUri` 29 | * 30 | * URLs support: 31 | * - [x] `http[s]?://www.break.com/embed/` 32 | * 33 | * Other: 34 | * - [Milestone](https://github.com/lejenome/html5-video-everywhere/milestones/Break%20Support) 35 | * 36 | * @external 37 | */ 38 | class Break extends Module { 39 | constructor() { 40 | super("break"); 41 | } 42 | 43 | onInteractive() { 44 | this.log("onInteractive()"); 45 | let vp = new VP(document.body, this.options); 46 | vp.srcs(this.getSrcs()); 47 | rmChildren(document.head); 48 | vp.props({ 49 | controls: true, 50 | autoplay: this.options.isAutoPlay(true), 51 | preload: this.options.getPreload(), 52 | loop: this.options.isLoop(), 53 | poster: window.wrappedJSObject.embedVars.thumbUri, 54 | }); 55 | vp.style({ 56 | width: "100%", 57 | height: "100%", 58 | }); 59 | vp.setup(); 60 | } 61 | 62 | fallback() { 63 | // Just fallback method if the first one didn't work 64 | let urlRegex = /"videoUri":\s*"([^"]*)"/; 65 | let url = (document.head.innerHTML.match(urlRegex) || ["", ""])[1]; 66 | return url; 67 | } 68 | 69 | getSrcs() { 70 | let fmts = {}; 71 | const maps = [ 72 | ["1280x720", "higher/mp4"], 73 | ["848x480", "high/mp4"], 74 | ["640x360", "medium/mp4"], 75 | ["426x240", "low/mp4"], // fallback if no 301x232 76 | ["301x232", "low/mp4"], 77 | ]; 78 | let srcs = {}; 79 | Array.forEach(window.wrappedJSObject.embedVars.media, (v) => { 80 | if (v.mediaPurpose === "play") srcs[v.width + "x" + v.height] = v.uri; 81 | }); 82 | for (const [q, fmt] of maps) { 83 | if (srcs[q]) fmts[fmt] = srcs[q]; 84 | } 85 | return fmts; 86 | } 87 | } 88 | new Break().start(); 89 | -------------------------------------------------------------------------------- /content/Lego.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Lego.com website support website. 3 | * @author Moez Bouhlel 4 | * @author DoomTay 5 | * @license MPL-2.0 6 | * @copyright 2014-2017 Moez Bouhlel 7 | */ 8 | 9 | import Module from './Module.js'; 10 | import VP from './video-player.js'; 11 | 12 | /** 13 | * Lego website support. 14 | * Lego does host its video player within and iframe. 15 | * 16 | * Video informations are available as a json valid text on `video` attribute 17 | * of a div tag. Let's called the parsed object `data` for the rest of the 18 | * documentation. 19 | * 20 | * Videos url can be generated as follow: 21 | * - `data-video-progressive-url` attribute of the document element 22 | * - append `public/` 23 | * - append 2 first chars from `data.ItemId` + `/` + the next 2 chars if 24 | * `data.NetStoragePath` is not set 25 | * - append `data.NetStoragePath` if set. 26 | * - append `/` 27 | * - append `data.ItemId` + `_` 28 | * - append `data.VideoId` + `_` 29 | * - append `data.Locale` + `_` 30 | * - append `data.VideoVersion` + `_` 31 | * - append quality number (256, 512, 1024, 1536, 2560) 32 | * - append type extension (.mp4 or .webm) 33 | * 34 | * Video poster image is available on `data.CoverImageUrl`. 35 | * 36 | * URLs support: 37 | * - [x] `http[s]?://www.lego.com//mediaplayer/video/