├── .browserslistrc ├── .editorconfig ├── .gitignore ├── .nvmrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── angular.json ├── package-lock.json ├── package.json ├── projects └── ngx-youtube-player │ ├── README.md │ ├── ng-package.json │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── ngx-youtube-player.component.spec.ts │ │ ├── ngx-youtube-player.component.ts │ │ ├── ngx-youtube-player.service.spec.ts │ │ ├── ngx-youtube-player.service.ts │ │ └── types.ts │ ├── public_api.ts │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.spec.json │ └── tslint.json ├── src ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ └── app.component.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── styles.css ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json └── tslint.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /.angular 8 | 9 | # dependencies 10 | /node_modules/ 11 | projects/ngx-youtube-player/node_modules/ 12 | # profiling files 13 | chrome-profiler-events.json 14 | speed-measure-plugin.json 15 | 16 | # IDEs and editors 17 | /.idea 18 | .project 19 | .classpath 20 | .c9/ 21 | *.launch 22 | .settings/ 23 | *.sublime-workspace 24 | 25 | # IDE - VSCode 26 | .vscode/* 27 | !.vscode/settings.json 28 | !.vscode/tasks.json 29 | !.vscode/launch.json 30 | !.vscode/extensions.json 31 | 32 | # misc 33 | /.sass-cache 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | npm-debug.log 38 | yarn-error.log 39 | testem.log 40 | /typings 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 10.16.0 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | # addons: 4 | # apt: 5 | # sources: 6 | # - google-chrome 7 | # packages: 8 | # - google-chrome-stable 9 | language: node_js 10 | node_js: 11 | - "10" 12 | install: 13 | - npm i 14 | - npm run build:lib 15 | script: 16 | - npm run test:lib:ci 17 | before_script: 18 | - export DISPLAY=:99.0 19 | - sh -e /etc/init.d/xvfb start 20 | - sleep 3 21 | notifications: 22 | email: false 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## v 18.0.0 (2024/06/20) 3 | 4 | - upgrade - bump version to 18 5 | 6 | ## v 17.1.0 (2024/06/20) 7 | 8 | - [NEW] - now using standalone component (not module) 9 | 10 | ## v 17.0.0 (2024/06/20) 11 | 12 | - upgrade - bump to version 17 13 | - chore - now using "ivy partial" mode 14 | 15 | ## v 13.0.0 (2024/06/20) 16 | 17 | - upgrade - bump to version 13 18 | 19 | ## v 12.0.2 (2022/12/12) 20 | 21 | - Fix - update peer deps #95 22 | - Fix - fixes youtube types error #98 23 | 24 | ## v 12.0.1 (2020/09/23) 25 | 26 | - [FIX] - apply pr #80 fix - allow multiple instances 27 | 28 | ## v 12.0.0 (2020/09/23) 29 | 30 | - [CHORE] - upgrade to latest v12.0. 31 | 32 | ## v 11.0.0 (2020/09/23) 33 | 34 | - [CHORE] - upgrade to latest v11.0. 35 | 36 | ## v 10.2.0 (2020/09/23) 37 | 38 | - [CHORE] - upgrade to latest v10.2 39 | 40 | ## v 9.1.0 (2020/09/23) 41 | 42 | - [CHORE] - upgrade to latest v9 43 | - [FIX] - allow mutiple instances fix #80 44 | 45 | ## v 7.1.5 (2020/04/17) 46 | 47 | - [CHORE] - adding scarf as a dependency 48 | 49 | ## v 7.1.4 (2020/04/15) 50 | 51 | - [CHORE] - adding scarf to published package 52 | 53 | ## v 7.1.3 (2020/04/15) 54 | 55 | - [CHORE] - updating package name to display info on npm 56 | 57 | ## v 7.1.2 (2020/04/12) 58 | 59 | - [CHORE] - added scarf for stats 60 | 61 | ## v 7.1.1 (2019/03/27) 62 | 63 | - [NEW] - now using "NgxYoutubePlayerModule" as the module name 64 | - [NEW] - now using "forRoot" for module 65 | - [UPGRADE] - official support for Angular 7, rxjs 6.3.3 66 | - [CHORE] - now using official angular library 67 | 68 | ## v 6.0.0 (2018/05/30) 69 | 70 | - [UPGRADE] - official support for Angular 6, rxjs 6 71 | - [REFACTOR] - "@types/youtube" is a dependency now 72 | - [REFACTOR] - Inputs height & width are assigned with the default sizes now 73 | 74 | ## v 0.1.0 (2018/01/08) 75 | 76 | - [UPGRADE] - official support for Angular 5 77 | - [UPDATE] - updated repo to ngx-youtube-player 78 | - [REFACTOR] - added more unit tests and increased coverage 79 | 80 | ## v 0.0.51 (2017/12/29) 81 | 82 | - [FIX] - fixes #27 - youtube player iframe api loaded with each instance 83 | 84 | ## v 0.0.4 (2017/10/03) 85 | 86 | - RENAMED npm package to "ngx-youtube-player" 87 | - updated to be usign Renderer2 for DOM Access to support NodeJS 88 | - fixes #8 89 | 90 | ## v 0.0.3 (2017/05/15) 91 | 92 | - updated to work with ng4 93 | - auto setting for youtube player api protocol (http or https) 94 | - using angular-library-starter 95 | - added support for "http" method 96 | 97 | ## v 0.0.2 (2017/03/29) 98 | 99 | - added examples 100 | 101 | ## v 0.0.1 (2017/05/10) 102 | 103 | ### Updates 104 | 105 | - First release 106 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Oren Farhi 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![npm version](https://badge.fury.io/js/ngx-youtube-player.svg)](https://badge.fury.io/js/ngx-youtube-player) 3 | [![npm downloads a month](https://img.shields.io/npm/dm/ngx-youtube-player.svg)](https://img.shields.io/npm/dm/ngx-youtube-player.svg) 4 | [![npm downloads a week](https://img.shields.io/npm/dt/ngx-youtube-player.svg)](https://img.shields.io/npm/dt/ngx-youtube-player.svg) 5 | 6 | # Install 7 | 8 | `npm i ngx-youtube-player` 9 | 10 | # Angular Youtube Player Component (Standalone) 11 | 12 | This is an Angular component based on [youtube player iframe api](https://developers.google.com/youtube/iframe_api_reference). 13 | This component came out as a result of the [open source project Echoes Player](http://github.com/orizens/echoes-player) - an alternative player for watching and listening to media from youtube. 14 | 15 | ## Breaking Change with v17 16 | 17 | | Before < v17 | After >= v17.1.0 | 18 | | --------------------- | ---------------------------------- | 19 | | `YoutubePlayerModule.forRoot()` | as standalone version | 20 | 21 | ## Angular Support 22 | 23 | **Starting with version 17.1.0**, `YoutubePlayerComponent` is a standalone component 24 | 25 | ## LICENSE 26 | 27 | Angular Youtube Component includes 2 optional licenses: 28 | 29 | 1. **Free** - for open source projects - includes standard play features, released under **MIT** license. 30 | 2. **Commercial (Enterprize)** - **you must purchase a license**, includes the following features: 31 | 32 | - License types: 33 | - app developer (\$200) - a license for each developer working with this component for one product only 34 | - platform developer (\$550) - a license for each developer developer working with component for all products in one company 35 | 36 | To purchase a license, please contact at 37 | 38 | ## Installation 39 | 40 | `npm install ngx-youtube-player` 41 | 42 | ## Supported API 43 | 44 | Currently supported attributes: 45 | 46 | ### Inputs 47 | 48 | - **height** (number) - optional height for the player 49 | - **width** (number) - optional width for the player 50 | - **videoId** (string) - will load the specified video by id 51 | 52 | ### outputs 53 | 54 | - **ready** (YT.Player) - implements youtube's player onReady event -> sends a the new created yt player instance 55 | - **change** - a state change event channeling the youtube's player instance state event object 56 | 57 | ## DEMO 58 | 59 | [A Live Demo In StackBlitz](https://stackblitz.com/edit/ngx-youtube-player?file=src%2Fapp%2Fapp.module.ts) 60 | 61 | ## Usage 62 | 63 | First, import the YoutubePlayerModule to your module: 64 | 65 | ```typescript 66 | import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; 67 | import { AppComponent } from "./app"; 68 | 69 | bootstrapApplication(AppComponent); 70 | ``` 71 | 72 | Next, use the **youtube-player** component. A Unique Id will be created for this player's instance: 73 | 74 | ```typescript 75 | import { Component } from "@angular/core"; 76 | 77 | @Component({ 78 | standalone: true, 79 | selector: "app", 80 | template: ` 81 | 86 | `, 87 | imports: [YoutubePlayerComponent] 88 | }) 89 | export class AppComponent { 90 | player: YT.Player; 91 | private id: string = "qDuKsiwS5xw"; 92 | 93 | savePlayer(player) { 94 | this.player = player; 95 | console.log("player instance", player); 96 | } 97 | onStateChange(event) { 98 | console.log("player state", event.data); 99 | } 100 | } 101 | ``` 102 | 103 | ## Testing (DISABLED) 104 | 105 | To start developing tdd/bdd style: `npm run dev` 106 | This will: compile ts files, watch for changes and start the test task. Whenever a ts file is changed, it will rerun the tests. 107 | 108 | # Showcase Examples 109 | 110 | - [Echoes Player](http://orizens.github.io/echoes-player) ([github repo for echoes player](http://github.com/orizens/echoes-player)) 111 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-youtube-player-project": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:application", 15 | "options": { 16 | "outputPath": { 17 | "base": "dist/ngx-youtube-player-project" 18 | }, 19 | "index": "src/index.html", 20 | "polyfills": [ 21 | "src/polyfills.ts" 22 | ], 23 | "tsConfig": "src/tsconfig.app.json", 24 | "assets": [ 25 | "src/favicon.ico", 26 | "src/assets" 27 | ], 28 | "styles": [ 29 | "src/styles.css" 30 | ], 31 | "scripts": [], 32 | "extractLicenses": false, 33 | "sourceMap": true, 34 | "optimization": false, 35 | "namedChunks": true, 36 | "browser": "src/main.ts" 37 | }, 38 | "configurations": { 39 | "production": { 40 | "fileReplacements": [ 41 | { 42 | "replace": "src/environments/environment.ts", 43 | "with": "src/environments/environment.prod.ts" 44 | } 45 | ], 46 | "optimization": true, 47 | "outputHashing": "all", 48 | "sourceMap": false, 49 | "namedChunks": false, 50 | "extractLicenses": true, 51 | "budgets": [ 52 | { 53 | "type": "initial", 54 | "maximumWarning": "2mb", 55 | "maximumError": "5mb" 56 | }, 57 | { 58 | "type": "anyComponentStyle", 59 | "maximumWarning": "6kb" 60 | } 61 | ] 62 | } 63 | }, 64 | "defaultConfiguration": "" 65 | }, 66 | "serve": { 67 | "builder": "@angular-devkit/build-angular:dev-server", 68 | "options": { 69 | "buildTarget": "ngx-youtube-player-project:build" 70 | }, 71 | "configurations": { 72 | "production": { 73 | "buildTarget": "ngx-youtube-player-project:build:production" 74 | } 75 | } 76 | }, 77 | "extract-i18n": { 78 | "builder": "@angular-devkit/build-angular:extract-i18n", 79 | "options": { 80 | "buildTarget": "ngx-youtube-player-project:build" 81 | } 82 | }, 83 | "test": { 84 | "builder": "@angular-devkit/build-angular:karma", 85 | "options": { 86 | "main": "src/test.ts", 87 | "polyfills": "src/polyfills.ts", 88 | "tsConfig": "src/tsconfig.spec.json", 89 | "karmaConfig": "src/karma.conf.js", 90 | "styles": [ 91 | "src/styles.css" 92 | ], 93 | "scripts": [], 94 | "assets": [ 95 | "src/favicon.ico", 96 | "src/assets" 97 | ] 98 | } 99 | }, 100 | "lint": { 101 | "builder": "@angular-devkit/build-angular:tslint", 102 | "options": { 103 | "tsConfig": [ 104 | "src/tsconfig.app.json", 105 | "src/tsconfig.spec.json" 106 | ], 107 | "exclude": [ 108 | "**/node_modules/**" 109 | ] 110 | } 111 | } 112 | } 113 | }, 114 | "ngx-youtube-player-project-e2e": { 115 | "root": "e2e/", 116 | "projectType": "application", 117 | "prefix": "", 118 | "architect": { 119 | "e2e": { 120 | "builder": "@angular-devkit/build-angular:protractor", 121 | "options": { 122 | "protractorConfig": "e2e/protractor.conf.js", 123 | "devServerTarget": "ngx-youtube-player-project:serve" 124 | }, 125 | "configurations": { 126 | "production": { 127 | "devServerTarget": "ngx-youtube-player-project:serve:production" 128 | } 129 | } 130 | }, 131 | "lint": { 132 | "builder": "@angular-devkit/build-angular:tslint", 133 | "options": { 134 | "tsConfig": "e2e/tsconfig.e2e.json", 135 | "exclude": [ 136 | "**/node_modules/**" 137 | ] 138 | } 139 | } 140 | } 141 | }, 142 | "ngx-youtube-player": { 143 | "root": "projects/ngx-youtube-player", 144 | "sourceRoot": "projects/ngx-youtube-player/src", 145 | "projectType": "library", 146 | "prefix": "lib", 147 | "architect": { 148 | "build": { 149 | "builder": "@angular-devkit/build-angular:ng-packagr", 150 | "options": { 151 | "tsConfig": "projects/ngx-youtube-player/tsconfig.lib.json", 152 | "project": "projects/ngx-youtube-player/ng-package.json" 153 | }, 154 | "configurations": { 155 | "production": { 156 | "tsConfig": "projects/ngx-youtube-player/tsconfig.lib.prod.json" 157 | } 158 | } 159 | }, 160 | "test": { 161 | "builder": "@angular-devkit/build-angular:karma", 162 | "options": { 163 | "main": "projects/ngx-youtube-player/src/test.ts", 164 | "tsConfig": "projects/ngx-youtube-player/tsconfig.spec.json", 165 | "karmaConfig": "projects/ngx-youtube-player/karma.conf.js" 166 | } 167 | }, 168 | "lint": { 169 | "builder": "@angular-devkit/build-angular:tslint", 170 | "options": { 171 | "tsConfig": [ 172 | "projects/ngx-youtube-player/tsconfig.lib.json", 173 | "projects/ngx-youtube-player/tsconfig.spec.json" 174 | ], 175 | "exclude": [ 176 | "**/node_modules/**" 177 | ] 178 | } 179 | } 180 | } 181 | } 182 | }, 183 | "cli": { 184 | "analytics": false 185 | } 186 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-youtube-player", 3 | "version": "18.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "build:lib": "ng build ngx-youtube-player", 9 | "dev:lib": "ng build ngx-youtube-player --watch", 10 | "test": "ng test", 11 | "test:lib": "ng test ngx-youtube-player", 12 | "test:lib:ci": "CI=true ng test ngx-youtube-player", 13 | "publish:lib": "npm publish ./dist/ngx-youtube-player", 14 | "publish:lib:next": "npm publish --tag next ./dist/ngx-youtube-player", 15 | "lint": "ng lint", 16 | "e2e": "ng e2e" 17 | }, 18 | "private": true, 19 | "dependencies": { 20 | "@angular/animations": "^18.0.3", 21 | "@angular/common": "^18.0.3", 22 | "@angular/compiler": "^18.0.3", 23 | "@angular/core": "^18.0.3", 24 | "@angular/forms": "^18.0.3", 25 | "@angular/platform-browser": "^18.0.3", 26 | "@angular/platform-browser-dynamic": "^18.0.3", 27 | "@angular/router": "^18.0.3", 28 | "@scarf/scarf": "^1.3.0", 29 | "@types/youtube": "^0.0.50", 30 | "core-js": "^2.5.4", 31 | "rxjs": "^7.8.1", 32 | "tslib": "^2.6.3", 33 | "zone.js": "~0.14.7" 34 | }, 35 | "devDependencies": { 36 | "@angular-devkit/build-angular": "^18.0.5", 37 | "@angular/cli": "^18.0.5", 38 | "@angular/compiler-cli": "^18.0.3", 39 | "@angular/language-service": "^18.0.3", 40 | "@types/jasmine": "~3.6.0", 41 | "@types/jasminewd2": "^2.0.13", 42 | "@types/node": "^12.11.1", 43 | "codelyzer": "^6.0.0", 44 | "jasmine-core": "~3.6.0", 45 | "jasmine-spec-reporter": "~5.0.0", 46 | "ng-packagr": "^18.0.0", 47 | "ts-node": "~7.0.0", 48 | "typescript": "~5.4.5" 49 | }, 50 | "repository": { 51 | "type": "git", 52 | "url": "https://github.com/orizens/ngx-youtube-player.git" 53 | }, 54 | "bugs": { 55 | "url": "https://github.com/orizens/ngx-youtube-player/issues" 56 | }, 57 | "homepage": "https://github.com/orizens/ngx-youtube-player" 58 | } -------------------------------------------------------------------------------- /projects/ngx-youtube-player/README.md: -------------------------------------------------------------------------------- 1 | 2 | [![npm version](https://badge.fury.io/js/ngx-youtube-player.svg)](https://badge.fury.io/js/ngx-youtube-player) 3 | [![npm downloads a month](https://img.shields.io/npm/dm/ngx-youtube-player.svg)](https://img.shields.io/npm/dm/ngx-youtube-player.svg) 4 | [![npm downloads a week](https://img.shields.io/npm/dt/ngx-youtube-player.svg)](https://img.shields.io/npm/dt/ngx-youtube-player.svg) 5 | 6 | # Install 7 | 8 | `npm i ngx-youtube-player` 9 | 10 | # Angular Youtube Player Component (Standalone) 11 | 12 | This is an Angular component based on [youtube player iframe api](https://developers.google.com/youtube/iframe_api_reference). 13 | This component came out as a result of the [open source project Echoes Player](http://github.com/orizens/echoes-player) - an alternative player for watching and listening to media from youtube. 14 | 15 | ## Breaking Change with v17 16 | 17 | | Before < v17 | After >= v17.1.0 | 18 | | --------------------- | ---------------------------------- | 19 | | `YoutubePlayerModule.forRoot()` | as standalone version | 20 | 21 | ## Angular Support 22 | 23 | **Starting with version 17.1.0**, `YoutubePlayerComponent` is a standalone component 24 | 25 | ## LICENSE 26 | 27 | Angular Youtube Component includes 2 optional licenses: 28 | 29 | 1. **Free** - for open source projects - includes standard play features, released under **MIT** license. 30 | 2. **Commercial (Enterprize)** - **you must purchase a license**, includes the following features: 31 | 32 | - License types: 33 | - app developer (\$200) - a license for each developer working with this component for one product only 34 | - platform developer (\$550) - a license for each developer developer working with component for all products in one company 35 | 36 | To purchase a license, please contact at 37 | 38 | ## Installation 39 | 40 | `npm install ngx-youtube-player` 41 | 42 | ## Supported API 43 | 44 | Currently supported attributes: 45 | 46 | ### Inputs 47 | 48 | - **height** (number) - optional height for the player 49 | - **width** (number) - optional width for the player 50 | - **videoId** (string) - will load the specified video by id 51 | 52 | ### outputs 53 | 54 | - **ready** (YT.Player) - implements youtube's player onReady event -> sends a the new created yt player instance 55 | - **change** - a state change event channeling the youtube's player instance state event object 56 | 57 | ## DEMO 58 | 59 | [A Live Demo In StackBlitz](https://stackblitz.com/edit/ngx-youtube-player?file=src%2Fapp%2Fapp.module.ts) 60 | 61 | ## Usage 62 | 63 | First, import the YoutubePlayerModule to your module: 64 | 65 | ```typescript 66 | import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; 67 | import { AppComponent } from "./app"; 68 | 69 | bootstrapApplication(AppComponent); 70 | ``` 71 | 72 | Next, use the **youtube-player** component. A Unique Id will be created for this player's instance: 73 | 74 | ```typescript 75 | import { Component } from "@angular/core"; 76 | 77 | @Component({ 78 | standalone: true, 79 | selector: "app", 80 | template: ` 81 | 86 | `, 87 | imports: [YoutubePlayerComponent] 88 | }) 89 | export class AppComponent { 90 | player: YT.Player; 91 | private id: string = "qDuKsiwS5xw"; 92 | 93 | savePlayer(player) { 94 | this.player = player; 95 | console.log("player instance", player); 96 | } 97 | onStateChange(event) { 98 | console.log("player state", event.data); 99 | } 100 | } 101 | ``` 102 | 103 | ## Testing (DISABLED) 104 | 105 | To start developing tdd/bdd style: `npm run dev` 106 | This will: compile ts files, watch for changes and start the test task. Whenever a ts file is changed, it will rerun the tests. 107 | 108 | # Showcase Examples 109 | 110 | - [Echoes Player](http://orizens.github.io/echoes-player) ([github repo for echoes player](http://github.com/orizens/echoes-player)) 111 | -------------------------------------------------------------------------------- /projects/ngx-youtube-player/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "allowedNonPeerDependencies": [ 4 | "@types/youtube", 5 | "@scarf/scarf" 6 | ], 7 | "dest": "../../dist/ngx-youtube-player", 8 | "lib": { 9 | "entryFile": "src/public_api.ts" 10 | } 11 | } -------------------------------------------------------------------------------- /projects/ngx-youtube-player/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-youtube-player", 3 | "version": "18.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "ngx-youtube-player", 9 | "version": "18.0.0", 10 | "dependencies": { 11 | "@types/youtube": "^0.0.50", 12 | "tslib": "^2.6.3" 13 | }, 14 | "peerDependencies": { 15 | "@angular/common": "^18.0.0", 16 | "@angular/core": "^18.0.0" 17 | } 18 | }, 19 | "node_modules/@angular/common": { 20 | "version": "18.0.3", 21 | "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.0.3.tgz", 22 | "integrity": "sha512-lmT9QbWHduqzpsB0osQFHeSwvQB1iUeNwTVUyMtcs6i46l4qOPtAt2/9DvHUWEUp01EBDxyi385ZI3vD+FHH/w==", 23 | "peer": true, 24 | "dependencies": { 25 | "tslib": "^2.3.0" 26 | }, 27 | "engines": { 28 | "node": "^18.19.1 || ^20.11.1 || >=22.0.0" 29 | }, 30 | "peerDependencies": { 31 | "@angular/core": "18.0.3", 32 | "rxjs": "^6.5.3 || ^7.4.0" 33 | } 34 | }, 35 | "node_modules/@angular/core": { 36 | "version": "18.0.3", 37 | "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.0.3.tgz", 38 | "integrity": "sha512-376hijhEqNpeA+qKncpVTIaZXRdBT6RctEBnFhJ2l57aHPH5S3oaSBQu1k3TEi07FlKOD4XF1+NzX9dvdup1eg==", 39 | "peer": true, 40 | "dependencies": { 41 | "tslib": "^2.3.0" 42 | }, 43 | "engines": { 44 | "node": "^18.19.1 || ^20.11.1 || >=22.0.0" 45 | }, 46 | "peerDependencies": { 47 | "rxjs": "^6.5.3 || ^7.4.0", 48 | "zone.js": "~0.14.0" 49 | } 50 | }, 51 | "node_modules/@types/youtube": { 52 | "version": "0.0.50", 53 | "resolved": "https://registry.npmjs.org/@types/youtube/-/youtube-0.0.50.tgz", 54 | "integrity": "sha512-d4GpH4uPYp9W07kc487tiq6V/EUHl18vZWFMbQoe4Sk9LXEWzFi/BMf9x7TI4m7/j7gU3KeX8H6M8aPBgykeLw==" 55 | }, 56 | "node_modules/rxjs": { 57 | "version": "7.8.1", 58 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", 59 | "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", 60 | "peer": true, 61 | "dependencies": { 62 | "tslib": "^2.1.0" 63 | } 64 | }, 65 | "node_modules/tslib": { 66 | "version": "2.6.3", 67 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", 68 | "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" 69 | }, 70 | "node_modules/zone.js": { 71 | "version": "0.14.7", 72 | "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.7.tgz", 73 | "integrity": "sha512-0w6DGkX2BPuiK/NLf+4A8FLE43QwBfuqz2dVgi/40Rj1WmqUskCqj329O/pwrqFJLG5X8wkeG2RhIAro441xtg==", 74 | "peer": true 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /projects/ngx-youtube-player/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-youtube-player", 3 | "version": "18.0.0", 4 | "author": "Oren Farhi (orizens.com)", 5 | "peerDependencies": { 6 | "@angular/common": "^18.0.0", 7 | "@angular/core": "^18.0.0" 8 | }, 9 | "dependencies": { 10 | "@types/youtube": "^0.0.50", 11 | "tslib": "^2.6.3" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/orizens/ngx-youtube-player.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/orizens/ngx-youtube-player/issues" 19 | }, 20 | "homepage": "https://github.com/orizens/ngx-youtube-player" 21 | } -------------------------------------------------------------------------------- /projects/ngx-youtube-player/src/lib/ngx-youtube-player.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { YoutubePlayerComponent } from './ngx-youtube-player.component'; 4 | 5 | describe('NgxYoutubePlayerComponent', () => { 6 | let component: YoutubePlayerComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [YoutubePlayerComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(YoutubePlayerComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create a component', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /projects/ngx-youtube-player/src/lib/ngx-youtube-player.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterContentInit, 3 | ChangeDetectionStrategy, 4 | Component, 5 | ElementRef, 6 | EventEmitter, 7 | Input, 8 | Output, 9 | Renderer2, 10 | } from '@angular/core'; 11 | import { 12 | YoutubePlayerService, 13 | defaultSizes 14 | } from './ngx-youtube-player.service'; 15 | 16 | @Component({ 17 | standalone: true, 18 | changeDetection: ChangeDetectionStrategy.OnPush, 19 | selector: 'youtube-player', 20 | template: ` 21 |
22 | `, 23 | providers: [YoutubePlayerService], 24 | }) 25 | export class YoutubePlayerComponent implements AfterContentInit { 26 | @Input() videoId = ''; 27 | @Input() height = defaultSizes.height; 28 | @Input() width = defaultSizes.width; 29 | /** 30 | * @description sets the protocol by the navigator object 31 | * if there is no window, it sets a default http protocol 32 | * unless the protocol is set from outside 33 | */ 34 | @Input() protocol: string = this.getProtocol(); 35 | @Input() playerVars: YT.PlayerVars = {}; 36 | 37 | // player created and initialized - sends instance of the player 38 | @Output() ready = new EventEmitter(); 39 | // state change: send the YT event with its state 40 | @Output() change = new EventEmitter(); 41 | 42 | constructor( 43 | public playerService: YoutubePlayerService, 44 | private elementRef: ElementRef, 45 | private renderer: Renderer2 46 | ) {} 47 | 48 | ngAfterContentInit() { 49 | const htmlId = this.playerService.generateUniqueId(); 50 | const playerSize = { height: this.height, width: this.width }; 51 | const container = this.renderer.selectRootElement( 52 | '#yt-player-ngx-component' 53 | ); 54 | this.renderer.setAttribute(container, 'id', htmlId); 55 | this.playerService.loadPlayerApi({ 56 | protocol: this.protocol 57 | }); 58 | this.playerService.setupPlayer( 59 | htmlId, 60 | { 61 | change: this.change, 62 | ready: this.ready 63 | }, 64 | playerSize, 65 | this.videoId, 66 | this.playerVars 67 | ); 68 | } 69 | 70 | getProtocol() { 71 | const hasWindow = window && window.location; 72 | const protocol = hasWindow 73 | ? window.location.protocol.replace(':', '') 74 | : 'http'; 75 | return protocol; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /projects/ngx-youtube-player/src/lib/ngx-youtube-player.service.spec.ts: -------------------------------------------------------------------------------- 1 | // import { TestBed } from '@angular/core/testing'; 2 | 3 | // import { NgxYoutubePlayerService } from './ngx-youtube-player.service'; 4 | 5 | // describe('NgxYoutubePlayerService', () => { 6 | // beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | // it('should be created', () => { 9 | // const service: NgxYoutubePlayerService = TestBed.get(NgxYoutubePlayerService); 10 | // expect(service).toBeTruthy(); 11 | // }); 12 | // }); 13 | import { 14 | YoutubePlayerService, 15 | defaultSizes, 16 | win, 17 | YouTubePlayerRef 18 | } from './ngx-youtube-player.service'; 19 | import { ReplaySubject } from 'rxjs'; 20 | 21 | const zone = jasmine.createSpyObj('zone', ['run']); 22 | 23 | describe('YoutubePlayerService', () => { 24 | global['YT'] = { 25 | Player: jasmine 26 | .createSpy('YTPlayer') 27 | .and.callFake((id: string, params: any) => params), 28 | PlayerState: 1 29 | } as any; 30 | 31 | it('should create an instance of YoutubePlayerService', () => { 32 | const service = new YoutubePlayerService(zone); 33 | const actual = service; 34 | const expected = jasmine.any(YoutubePlayerService); 35 | expect(actual).toEqual(expected); 36 | }); 37 | 38 | it('should create an api subject', () => { 39 | const service = new YoutubePlayerService(zone); 40 | const actual = service.api; 41 | const expected = jasmine.any(ReplaySubject); 42 | expect(actual).toEqual(expected); 43 | }); 44 | 45 | it('should emit the YT player when youtube iframe api is ready', () => { 46 | const service = new YoutubePlayerService(zone); 47 | const actual = service.api; 48 | spyOn(service.api, 'next'); 49 | win()['onYouTubeIframeAPIReady'](); 50 | expect(service.api.next).toHaveBeenCalledWith(global['YT'] as any); 51 | }); 52 | 53 | it('should generate a unique id', () => { 54 | const service = new YoutubePlayerService(zone); 55 | const actual = service.generateUniqueId(); 56 | expect(actual).toBeDefined(); 57 | expect(actual.length).toBeGreaterThan(1); 58 | }); 59 | 60 | describe('YT Player Creation', () => { 61 | let params, service, actual; 62 | 63 | beforeEach(() => { 64 | params = { 65 | id: 'testing-id', 66 | outputs: {}, 67 | playerVars: {}, 68 | sizes: { 69 | height: 1000, 70 | width: 2000 71 | }, 72 | videoId: '' 73 | }; 74 | service = new YoutubePlayerService(zone); 75 | actual = service.createPlayer( 76 | params.id, 77 | params.outputs, 78 | params.sizes, 79 | params.videoId, 80 | params.playerVars 81 | ); 82 | }); 83 | 84 | it('should create a player using YT Api', () => { 85 | const expected = actual; 86 | expect(YouTubePlayerRef()).toHaveBeenCalledWith(params.id, expected); 87 | }); 88 | 89 | it('should create a player with given sizes', () => { 90 | const expected = params.sizes; 91 | expect(actual.height).toBeDefined(expected.height); 92 | expect(actual.width).toBeDefined(expected.width); 93 | }); 94 | 95 | it('should create a player with default sizes', () => { 96 | const expected = defaultSizes; 97 | expect(actual.height).toBeDefined(expected.height); 98 | expect(actual.width).toBeDefined(expected.width); 99 | }); 100 | }); 101 | 102 | describe('YT Player functionality exposure', () => { 103 | let player; 104 | 105 | beforeEach(() => { 106 | player = jasmine.createSpyObj('ytplayer', [ 107 | 'playVideo', 108 | 'pauseVideo', 109 | 'loadVideoById', 110 | 'getPlayerState', 111 | 'setSize' 112 | ]); 113 | }); 114 | it('should play the video', () => { 115 | const service = new YoutubePlayerService(zone); 116 | service.play(player); 117 | expect(player.playVideo).toHaveBeenCalledTimes(1); 118 | }); 119 | it('should pause the video', () => { 120 | const service = new YoutubePlayerService(zone); 121 | service.pause(player); 122 | expect(player.pauseVideo).toHaveBeenCalledTimes(1); 123 | }); 124 | it('should tell a video is playing', () => { 125 | const service = new YoutubePlayerService(zone); 126 | const media = { id: 'testing' }; 127 | service.playVideo(media, player); 128 | expect(player.loadVideoById).toHaveBeenCalledTimes(1); 129 | expect(player.playVideo).toHaveBeenCalledTimes(1); 130 | }); 131 | it('should tell a video is playing using the player state', () => { 132 | const service = new YoutubePlayerService(zone); 133 | const media = { id: 'testing' }; 134 | service.isPlaying(player); 135 | expect(player.getPlayerState).toHaveBeenCalledTimes(1); 136 | }); 137 | it('should tell a video is NOT playing', () => { 138 | const service = new YoutubePlayerService(zone); 139 | const media = { id: 'testing' }; 140 | const actual = service.isPlaying({} as YT.Player); 141 | const expected = false; 142 | expect(player.getPlayerState).not.toHaveBeenCalled(); 143 | expect(actual).toBe(expected); 144 | }); 145 | it('should setSize to defaults when in fullscreen', () => { 146 | const service = new YoutubePlayerService(zone); 147 | const actual = service.toggleFullScreen(player, true); 148 | expect(player.setSize).toHaveBeenCalledTimes(1); 149 | }); 150 | it('should setSize to fullscreen when NOT in fullscreen', () => { 151 | const service = new YoutubePlayerService(zone); 152 | global['innerHeight'] = 1000; 153 | global['innerWidth'] = 2000; 154 | const actual = service.toggleFullScreen(player, false); 155 | expect(player.setSize).toHaveBeenCalledWith( 156 | global['innerWidth'], 157 | global['innerHeight'] 158 | ); 159 | }); 160 | }); 161 | }); 162 | -------------------------------------------------------------------------------- /projects/ngx-youtube-player/src/lib/ngx-youtube-player.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NgZone } from "@angular/core"; 2 | import { ReplaySubject } from "rxjs"; 3 | import { IPlayerApiScriptOptions, IPlayerOutputs, IPlayerSize } from "./types"; 4 | 5 | export function win() { 6 | return window; 7 | } 8 | 9 | export function YouTubeRef(): any { 10 | return win()["YT"] as any; 11 | } 12 | 13 | export function YouTubePlayerRef() { 14 | return YouTubeRef().Player as any; 15 | } 16 | 17 | export const defaultSizes = { 18 | height: 270, 19 | width: 367, 20 | }; 21 | 22 | @Injectable({ 23 | providedIn: "root", 24 | }) 25 | export class YoutubePlayerService { 26 | api: ReplaySubject; 27 | 28 | static ytApiLoaded = false; 29 | 30 | constructor(private zone: NgZone) { 31 | this.api = new ReplaySubject(1); 32 | this.createApi(); 33 | } 34 | 35 | loadPlayerApi(options: IPlayerApiScriptOptions) { 36 | const doc = win().document; 37 | if (!YoutubePlayerService["ytApiLoaded"]) { 38 | YoutubePlayerService.ytApiLoaded = true; 39 | const playerApiScript = doc.createElement("script"); 40 | playerApiScript.type = "text/javascript"; 41 | playerApiScript.src = `${options.protocol}://www.youtube.com/iframe_api`; 42 | doc.body.appendChild(playerApiScript); 43 | } 44 | } 45 | 46 | setupPlayer( 47 | elementId: string, 48 | outputs: IPlayerOutputs, 49 | sizes: IPlayerSize, 50 | videoId = "", 51 | playerVars: YT.PlayerVars 52 | ) { 53 | const createPlayer = () => { 54 | if (YouTubePlayerRef) { 55 | this.createPlayer(elementId, outputs, sizes, videoId, playerVars); 56 | } 57 | }; 58 | this.api.subscribe(createPlayer); 59 | } 60 | 61 | play(player: YT.Player) { 62 | player.playVideo(); 63 | } 64 | 65 | pause(player: YT.Player) { 66 | player.pauseVideo(); 67 | } 68 | 69 | playVideo(media: any, player: YT.Player) { 70 | const id = media.id.videoId ? media.id.videoId : media.id; 71 | player.loadVideoById(id); 72 | this.play(player); 73 | } 74 | 75 | isPlaying(player: YT.Player) { 76 | // because YT is not loaded yet 1 is used - YT.PlayerState.PLAYING 77 | const isPlayerReady: any = player && player.getPlayerState; 78 | const playerState = isPlayerReady ? player.getPlayerState() : {}; 79 | const isPlayerPlaying = isPlayerReady 80 | ? playerState !== YouTubeRef().PlayerState.ENDED && 81 | playerState !== YouTubeRef().PlayerState.PAUSED 82 | : false; 83 | return isPlayerPlaying; 84 | } 85 | 86 | createPlayer( 87 | elementId: string, 88 | outputs: IPlayerOutputs, 89 | sizes: IPlayerSize, 90 | videoId = "", 91 | playerVars: YT.PlayerVars = {} 92 | ) { 93 | const playerSize = { 94 | height: sizes.height || defaultSizes.height, 95 | width: sizes.width || defaultSizes.width, 96 | }; 97 | const ytPlayer = YouTubePlayerRef(); 98 | return new ytPlayer(elementId, { 99 | ...playerSize, 100 | events: { 101 | onReady: (ev: YT.PlayerEvent) => { 102 | this.zone.run(() => outputs.ready && outputs.ready.next(ev.target)); 103 | }, 104 | onStateChange: (ev: YT.PlayerEvent) => { 105 | this.zone.run(() => outputs.change && outputs.change.next(ev)); 106 | }, 107 | }, 108 | playerVars, 109 | videoId, 110 | }); 111 | } 112 | 113 | toggleFullScreen( 114 | player: YT.Player, 115 | isFullScreen: boolean | null | undefined 116 | ) { 117 | let { height, width } = defaultSizes; 118 | 119 | if (!isFullScreen) { 120 | height = window.innerHeight; 121 | width = window.innerWidth; 122 | } 123 | player.setSize(width, height); 124 | } 125 | 126 | // adpoted from uid 127 | generateUniqueId(): string { 128 | const len = 7; 129 | return Math.random().toString(35).substr(2, len); 130 | } 131 | 132 | private createApi() { 133 | const onYouTubeIframeAPIReady = () => { 134 | if (win()) { 135 | win()["onYouTubeIframeAPIReadyCalled"] = true; 136 | this.api.next(YouTubePlayerRef()); 137 | } 138 | }; 139 | win()["onYouTubeIframeAPIReady"] = onYouTubeIframeAPIReady; 140 | /** 141 | * If onYouTubeIframeAPIReady is called in other place, then just trigger next 142 | * This is to prevent player not initializing issue when another player got initialized in other place 143 | */ 144 | if (win()["onYouTubeIframeAPIReadyCalled"]) { 145 | this.api.next(YouTubePlayerRef()); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /projects/ngx-youtube-player/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | import 'youtube'; 2 | import { EventEmitter } from '@angular/core'; 3 | 4 | export interface IPlayerOutputs { 5 | ready?: EventEmitter; 6 | change?: EventEmitter; 7 | } 8 | 9 | export interface IPlayerSize { 10 | height?: number; 11 | width?: number; 12 | } 13 | 14 | export interface IPlayerApiScriptOptions { 15 | protocol?: string; 16 | } 17 | -------------------------------------------------------------------------------- /projects/ngx-youtube-player/src/public_api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of ngx-youtube-player 3 | */ 4 | 5 | export * from './lib/ngx-youtube-player.service'; 6 | export * from './lib/ngx-youtube-player.component'; 7 | -------------------------------------------------------------------------------- /projects/ngx-youtube-player/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'core-js/es7/reflect'; 4 | import 'zone.js'; 5 | import 'zone.js/testing'; 6 | import { getTestBed } from '@angular/core/testing'; 7 | import { 8 | BrowserDynamicTestingModule, 9 | platformBrowserDynamicTesting 10 | } from '@angular/platform-browser-dynamic/testing'; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting(), { 16 | teardown: { destroyAfterEach: false } 17 | } 18 | ); 19 | -------------------------------------------------------------------------------- /projects/ngx-youtube-player/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "declarationMap": true, 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "types": [], 14 | "lib": [ 15 | "dom", 16 | "es2018" 17 | ] 18 | }, 19 | "angularCompilerOptions": { 20 | "skipTemplateCodegen": true, 21 | "strictMetadataEmit": true, 22 | "fullTemplateTypeCheck": true, 23 | "strictInjectionParameters": true, 24 | "enableResourceInlining": true, 25 | "compilationMode": "partial" 26 | }, 27 | "exclude": [ 28 | "src/test.ts", 29 | "**/*.spec.ts" 30 | ] 31 | } -------------------------------------------------------------------------------- /projects/ngx-youtube-player/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false 5 | }, 6 | "angularCompilerOptions": { 7 | "compilationMode": "partial", 8 | } 9 | } -------------------------------------------------------------------------------- /projects/ngx-youtube-player/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-youtube-player/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [true, "attribute", "lib", "camelCase"], 5 | "component-selector": [true, "element", "kebab-case"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orizens/ngx-youtube-player/325b769c550b95032ad417106fde2c2dae8dc9a4/src/app/app.component.css -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Angular Youtube Player Component

4 |

This is a simple usage of ngx-youtube-player component.

5 |
6 | 7 |
8 |
Demo
9 |
10 | 15 | 16 |
17 |
18 |
19 | 25 | 31 |
32 |
33 | 34 |
35 |
36 |
37 |
Player State
38 |
{{ ytEvent }}
39 |
40 |
41 |
42 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | // import { TestBed, async } from '@angular/core/testing'; 2 | // import { AppComponent } from './app.component'; 3 | 4 | // describe('AppComponent', () => { 5 | // beforeEach(async(() => { 6 | // TestBed.configureTestingModule({ 7 | // declarations: [ 8 | // AppComponent 9 | // ], 10 | // }).compileComponents(); 11 | // })); 12 | 13 | // it('should create the app', () => { 14 | // const fixture = TestBed.createComponent(AppComponent); 15 | // const app = fixture.debugElement.componentInstance; 16 | // expect(app).toBeTruthy(); 17 | // }); 18 | 19 | // it(`should have as title 'ngx-youtube-player-project'`, () => { 20 | // const fixture = TestBed.createComponent(AppComponent); 21 | // const app = fixture.debugElement.componentInstance; 22 | // expect(app.title).toEqual('ngx-youtube-player-project'); 23 | // }); 24 | 25 | // it('should render title in a h1 tag', () => { 26 | // const fixture = TestBed.createComponent(AppComponent); 27 | // fixture.detectChanges(); 28 | // const compiled = fixture.debugElement.nativeElement; 29 | // expect(compiled.querySelector('h1').textContent).toContain('Welcome to ngx-youtube-player-project!'); 30 | // }); 31 | // }); 32 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { YoutubePlayerComponent } from "ngx-youtube-player"; 3 | 4 | @Component({ 5 | standalone: true, 6 | selector: "app-root", 7 | templateUrl: "./app.component.html", 8 | styleUrls: ["./app.component.css"], 9 | imports: [YoutubePlayerComponent] 10 | }) 11 | export class AppComponent { 12 | id = "V462IsOV3js"; 13 | playerVars = { 14 | cc_lang_pref: "en", 15 | }; 16 | private player; 17 | public ytEvent; 18 | 19 | constructor() {} 20 | onStateChange(event) { 21 | this.ytEvent = event.data; 22 | } 23 | savePlayer(player) { 24 | this.player = player; 25 | } 26 | 27 | playVideo() { 28 | this.player.playVideo(); 29 | } 30 | 31 | pauseVideo() { 32 | this.player.pauseVideo(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orizens/ngx-youtube-player/325b769c550b95032ad417106fde2c2dae8dc9a4/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orizens/ngx-youtube-player/325b769c550b95032ad417106fde2c2dae8dc9a4/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NgxYoutubePlayerProject 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { bootstrapApplication } from '@angular/platform-browser'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | bootstrapApplication(AppComponent) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | // First, initialize the Angular testing environment. 11 | getTestBed().initTestEnvironment( 12 | BrowserDynamicTestingModule, 13 | platformBrowserDynamicTesting(), { 14 | teardown: { destroyAfterEach: false } 15 | } 16 | ); 17 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "main.ts", 9 | "polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "esModuleInterop": true, 7 | "sourceMap": true, 8 | "declaration": false, 9 | "module": "es2020", 10 | "moduleResolution": "node", 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "target": "ES2022", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ], 21 | "paths": { 22 | "ngx-youtube-player": [ 23 | "dist/ngx-youtube-player" 24 | ], 25 | "ngx-youtube-player/*": [ 26 | "dist/ngx-youtube-player/*" 27 | ] 28 | }, 29 | "useDefineForClassFields": false 30 | } 31 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-redundant-jsdoc": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-output-on-prefix": true, 120 | "no-inputs-metadata-property": true, 121 | "no-outputs-metadata-property": true, 122 | "no-host-metadata-property": true, 123 | "no-input-rename": true, 124 | "no-output-rename": true, 125 | "use-lifecycle-interface": true, 126 | "use-pipe-transform-interface": true, 127 | "component-class-suffix": true, 128 | "directive-class-suffix": true 129 | } 130 | } 131 | --------------------------------------------------------------------------------