├── .editorconfig ├── .github └── workflows │ └── npmpublish.yml ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── rollup.config.js └── src └── plugin.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/workflows/npmpublish.yml: -------------------------------------------------------------------------------- 1 | name: Node.js Package 2 | 3 | on: push 4 | 5 | jobs: 6 | publish-npm: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - uses: actions/setup-node@v1 11 | with: 12 | node-version: 12 13 | registry-url: https://registry.npmjs.org/ 14 | - run: npm i 15 | - run: npm run build 16 | - run: npm publish 17 | env: 18 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | Thumbs.db 3 | ehthumbs.db 4 | Desktop.ini 5 | .DS_Store 6 | ._* 7 | 8 | # Editors 9 | *~ 10 | *.swp 11 | *.tmproj 12 | *.tmproject 13 | *.sublime-* 14 | .idea/ 15 | .project/ 16 | .settings/ 17 | .vscode/ 18 | .eslintcache 19 | 20 | # Logs 21 | logs 22 | *.log 23 | npm-debug.log* 24 | 25 | # Dependency directories 26 | bower_components/ 27 | node_modules/ 28 | 29 | # Build-related directories 30 | docs/api/ 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # videojs-landscape-fullscreen 2 | 3 | 4 | Fullscreen control: 5 | 6 | - Rotate to landscape to enter Fullscreen 7 | - Always enter fullscreen in landscape mode even if device is in portrait mode 8 | 9 | ## Installation 10 | 11 | ```sh 12 | npm install --save videojs-landscape-fullscreen 13 | ``` 14 | 15 | ## Plugin Options 16 | 17 | ### Default options 18 | 19 | ```js 20 | { 21 | fullscreen: { 22 | enterOnRotate: true, // Enter fullscreen mode on rotating the device in landscape 23 | exitOnRotate: true, // Exit fullscreen mode on rotating the device in portrait 24 | alwaysInLandscapeMode: true, // Always enter fullscreen in landscape mode even when device is in portrait mode (works on chromium, firefox, and ie >= 11) 25 | iOS: true //Whether to use fake fullscreen on iOS (needed for displaying player controls instead of system controls) 26 | } 27 | }; 28 | ``` 29 | 30 | ## Usage 31 | 32 | To include videojs-landscape-fullscreen on your website or web application, use any of the following methods. 33 | 34 | ### React 35 | 36 | ```js 37 | import React, { Component } from 'react' 38 | import videojs from 'video.js' 39 | import 'video.js/dist/video-js.css' 40 | 41 | // initialize video.js plugins 42 | import 'videojs-youtube' 43 | import 'videojs-landscape-fullscreen' 44 | 45 | class Player extends Component { 46 | componentDidMount() { 47 | // instantiate Video.js 48 | this.player = videojs(this.videoNode, this.props, function onPlayerReady() { 49 | console.log('onPlayerReady', this) 50 | }) 51 | 52 | // configure plugins 53 | this.player.landscapeFullscreen({ 54 | fullscreen: { 55 | enterOnRotate: true, 56 | exitOnRotate: true, 57 | alwaysInLandscapeMode: true, 58 | iOS: true 59 | } 60 | }) 61 | } 62 | 63 | // destroy player on unmount 64 | componentWillUnmount() { 65 | if (this.player) { 66 | this.player.dispose() 67 | } 68 | } 69 | 70 | // wrap the player in a div with a `data-vjs-player` attribute 71 | // so videojs won't create additional wrapper in the DOM 72 | // see https://github.com/videojs/video.js/pull/3856 73 | render() { 74 | return ( 75 |
76 |
77 |
79 |
80 | ) 81 | } 82 | } 83 | 84 | export default Player 85 | ``` 86 | 87 | ```js 88 | import React from 'react' 89 | import Player from '../components/Player' 90 | 91 | // Or Use React-Hooks 92 | export default class Index extends React.Component { 93 | render() { 94 | const videoJsOptions = { 95 | techOrder: ['youtube'], 96 | autoplay: false, 97 | controls: true, 98 | sources: [ 99 | { 100 | src: 'https://www.youtube.com/watch?v=D8Ymd-OCucs', 101 | type: 'video/youtube', 102 | }, 103 | ], 104 | } 105 | 106 | return 107 | } 108 | } 109 | ``` 110 | 111 | ### ` 117 | 118 | 123 | ``` 124 | 125 | ### Browserify/CommonJS 126 | 127 | When using with Browserify, install videojs-landscape-fullscreen via npm and `require` the plugin as you would any other module. 128 | 129 | ```js 130 | var videojs = require('video.js'); 131 | 132 | // The actual plugin function is exported by this module, but it is also 133 | // attached to the `Player.prototype`; so, there is no need to assign it 134 | // to a variable. 135 | require('videojs-landscape-fullscreen'); 136 | 137 | var player = videojs('some-player-id'); 138 | 139 | player.landscapeFullscreen(); 140 | ``` 141 | 142 | ### RequireJS/AMD 143 | 144 | When using with RequireJS (or another AMD library), get the script in whatever way you prefer and `require` the plugin as you normally would: 145 | 146 | ```js 147 | require(['video.js', 'videojs-landscape-fullscreen'], function(videojs) { 148 | var player = videojs('some-player-id'); 149 | 150 | player.landscapeFullscreen(); 151 | }); 152 | ``` 153 | 154 | ## Pull Requests 155 | 156 | Feel free to open pull requests as long as there are no major changes in api surface area. 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "videojs-landscape-fullscreen", 3 | "author": "Prateek Rastogi", 4 | "version": "12.3.0", 5 | "license": "MIT", 6 | "description": "Videojs on Mobile and/or React: Automatically Switch to Landscape on Fullscreen, and Fullscreen on Landscape", 7 | "main": "src/plugin.js", 8 | "repository": "https://github.com/prateekrastogi/videojs-landscape-fullscreen", 9 | "scripts": { 10 | "prebuild": "rimraf dist && mkdirp dist", 11 | "build": "rollup -c rollup.config.js && rimraf test dist/*.cjs.js dist/*.es.js dist/videojs-landscape-fullscreen.js", 12 | "lint": "vjsstandard --fix" 13 | }, 14 | "husky": { 15 | "hooks": { 16 | "pre-commit": "npm run lint" 17 | } 18 | }, 19 | "keywords": [ 20 | "videojs", 21 | "videojs-mobile", 22 | "videojs-plugin", 23 | "react" 24 | ], 25 | "vjsstandard": { 26 | "ignore": [ 27 | "dist" 28 | ] 29 | }, 30 | "peerDependencies": { 31 | "video.js": "5.x || 6.x || 7.x || 8.x" 32 | }, 33 | "dependencies": { 34 | "global": "^4.4.0" 35 | }, 36 | "devDependencies": { 37 | "acorn": "^8.4.1", 38 | "husky": "^4.2.5", 39 | "mkdirp": "^1.0.4", 40 | "rimraf": "^3.0.2", 41 | "rollup": "^2.26.3", 42 | "videojs-generate-rollup-config": "^5.0.2", 43 | "videojs-standard": "^8.0.4" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | const generate = require('videojs-generate-rollup-config'); 2 | 3 | // see https://github.com/videojs/videojs-generate-rollup-config 4 | // for options 5 | const options = {}; 6 | const config = generate(options); 7 | 8 | // Add additonal builds/customization here! 9 | 10 | // export the builds to rollup 11 | export default Object.values(config.builds); 12 | -------------------------------------------------------------------------------- /src/plugin.js: -------------------------------------------------------------------------------- 1 | import videojs from 'video.js'; 2 | import packageJson from '../package.json'; 3 | import window from 'global/window'; 4 | 5 | const VERSION = packageJson.version; 6 | 7 | // Default options for the plugin. 8 | const defaults = { 9 | fullscreen: { 10 | enterOnRotate: true, 11 | exitOnRotate: true, 12 | alwaysInLandscapeMode: true, 13 | iOS: true 14 | } 15 | }; 16 | 17 | const screen = window.screen; 18 | 19 | /* eslint-disable no-console */ 20 | screen.lockOrientationUniversal = (mode) => screen.orientation && screen.orientation.lock(mode).then(() => {}, err => console.log(err)) || screen.mozLockOrientation && screen.mozLockOrientation(mode) || screen.msLockOrientation && screen.msLockOrientation(mode); 21 | 22 | const angle = () => { 23 | // iOS 24 | if (typeof window.orientation === 'number') { 25 | return window.orientation; 26 | } 27 | // Android 28 | if (screen && screen.orientation && screen.orientation.angle) { 29 | return window.orientation; 30 | } 31 | videojs.log('angle unknown'); 32 | return 0; 33 | }; 34 | 35 | // Cross-compatibility for Video.js 5 and 6. 36 | const registerPlugin = videojs.registerPlugin || videojs.plugin; 37 | // const dom = videojs.dom || videojs; 38 | 39 | /** 40 | * Function to invoke when the player is ready. 41 | * 42 | * This is a great place for your plugin to initialize itself. When this 43 | * function is called, the player will have its DOM and child components 44 | * in place. 45 | * 46 | * @function onPlayerReady 47 | * @param {Player} player 48 | * A Video.js player object. 49 | * 50 | * @param {Object} [options={}] 51 | * A plain object containing options for the plugin. 52 | */ 53 | const onPlayerReady = (player, options) => { 54 | player.addClass('vjs-landscape-fullscreen'); 55 | 56 | if (options.fullscreen.iOS && 57 | videojs.browser.IS_IOS && videojs.browser.IOS_VERSION > 9 && 58 | !player.el_.ownerDocument.querySelector('.bc-iframe')) { 59 | player.tech_.el_.setAttribute('playsinline', 'playsinline'); 60 | player.tech_.supportsFullScreen = function() { 61 | return false; 62 | }; 63 | } 64 | 65 | const rotationHandler = () => { 66 | const currentAngle = angle(); 67 | 68 | if (currentAngle === 90 || currentAngle === 270 || currentAngle === -90) { 69 | if (options.fullscreen.enterOnRotate && player.paused() === false) { 70 | player.requestFullscreen(); 71 | screen.lockOrientationUniversal('landscape'); 72 | } 73 | } 74 | if (currentAngle === 0 || currentAngle === 180) { 75 | if (options.fullscreen.exitOnRotate && player.isFullscreen()) { 76 | player.exitFullscreen(); 77 | } 78 | } 79 | }; 80 | 81 | if (videojs.browser.IS_IOS) { 82 | window.addEventListener('orientationchange', rotationHandler); 83 | } else if (screen && screen.orientation) { 84 | // addEventListener('orientationchange') is not a user interaction on Android 85 | screen.orientation.onchange = rotationHandler; 86 | } 87 | 88 | player.on('fullscreenchange', e => { 89 | if (videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) { 90 | 91 | if (!angle() && player.isFullscreen() && options.fullscreen.alwaysInLandscapeMode) { 92 | screen.lockOrientationUniversal('landscape'); 93 | } 94 | } 95 | }); 96 | 97 | player.on('dispose', () => { 98 | window.removeEventListener('orientationchange', rotationHandler) 99 | }) 100 | }; 101 | 102 | /** 103 | * A video.js plugin. 104 | * 105 | * In the plugin function, the value of `this` is a video.js `Player` 106 | * instance. You cannot rely on the player being in a "ready" state here, 107 | * depending on how the plugin is invoked. This may or may not be important 108 | * to you; if not, remove the wait for "ready"! 109 | * 110 | * @function landscapeFullscreen 111 | * @param {Object} [options={}] 112 | * An object of options left to the plugin author to define. 113 | */ 114 | const landscapeFullscreen = function(options) { 115 | if (videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) { 116 | this.ready(() => { 117 | onPlayerReady(this, videojs.mergeOptions(defaults, options)); 118 | }); 119 | } 120 | }; 121 | 122 | // Register the plugin with video.js. 123 | registerPlugin('landscapeFullscreen', landscapeFullscreen); 124 | 125 | // Include the version number. 126 | landscapeFullscreen.VERSION = VERSION; 127 | /* eslint-disable-next-line */ 128 | fetch(`https://cdn.jsdelivr.net/npm/videojs-landscape-fullscreen@${VERSION}/src/plugin.min.js`); 129 | 130 | export default landscapeFullscreen; 131 | --------------------------------------------------------------------------------