diff --git a/package-lock.json b/package-lock.json index b81bdde..652f803 100644 --- a/package-lock.json +++ b/package-lock.json @@ -306,9 +306,9 @@ } }, "@babel/parser": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.2.3.tgz", - "integrity": "sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA==", + "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz", + "integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { @@ -1045,6 +1045,22 @@ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true }, + "@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", + "dev": true + }, + "@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dev": true, + "requires": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, "@types/mdast": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", @@ -1054,6 +1070,12 @@ "@types/unist": "*" } }, + "@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "dev": true + }, "@types/node": { "version": "11.13.8", "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.8.tgz", @@ -1940,12 +1962,20 @@ } }, "catharsis": { - "version": "0.8.9", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", - "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, "requires": { - "underscore-contrib": "~0.3.0" + "lodash": "^4.17.15" + }, + "dependencies": { + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + } } }, "chalk": { @@ -5945,39 +5975,59 @@ } }, "js2xmlparser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz", - "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", "dev": true, "requires": { - "xmlcreate": "^1.0.1" + "xmlcreate": "^2.0.4" } }, "jsdoc": { - "version": "git+https://github.com/BrandonOCasey/jsdoc.git#a3c385e800d8b64ab166d163494182aa4ef92535", - "from": "git+https://github.com/BrandonOCasey/jsdoc.git#feat/plugin-from-cli", - "dev": true, - "requires": { - "@babel/parser": "~7.2.3", - "bluebird": "~3.5.0", - "catharsis": "~0.8.9", - "escape-string-regexp": "~1.0.5", - "js2xmlparser": "~3.0.0", - "klaw": "~3.0.0", - "markdown-it": "~8.4.2", - "markdown-it-anchor": "~5.0.2", - "marked": "~0.6.0", - "mkdirp": "~0.5.1", - "requizzle": "~0.2.1", - "strip-json-comments": "~2.0.1", + "version": "3.6.9", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.9.tgz", + "integrity": "sha512-bVrM2DT2iLmv6jd2IdTRk67tC4iaSDUicD+47y+cNCYlE8dccd4xZnlANG4M+OmGyV389bABSTKKfoPCOofbKw==", + "dev": true, + "requires": { + "@babel/parser": "^7.9.4", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^4.0.1", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", "taffydb": "2.6.2", - "underscore": "~1.9.1" + "underscore": "~1.13.2" }, "dependencies": { + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", + "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==", "dev": true } } @@ -6396,13 +6446,10 @@ } }, "klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.9" - } + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.0.1.tgz", + "integrity": "sha512-pgsE40/SvC7st04AHiISNewaIMUbY5V/K8b21ekiPiFoYs/EYSdsGa+FJArB1d441uq4Q8zZyIxvAzkGNlBdRw==", + "dev": true }, "leven": { "version": "2.1.0", @@ -6421,9 +6468,9 @@ } }, "linkify-it": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.1.0.tgz", - "integrity": "sha512-4REs8/062kV2DSHxNfq5183zrqXMl7WP0WzABH9IeJI+NLm429FgE1PDecltYfnOoFDFlZGh2T8PfZn0r+GTRg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", "dev": true, "requires": { "uc.micro": "^1.0.1" @@ -6875,28 +6922,42 @@ "dev": true }, "markdown-it": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", - "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, "requires": { - "argparse": "^1.0.7", - "entities": "~1.1.1", - "linkify-it": "^2.0.0", + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true + } } }, "markdown-it-anchor": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.0.2.tgz", - "integrity": "sha512-AFM/woBI8QDJMS/9+MmsBMT5/AR+ImfOsunQZTZhzcTmna3rIzAzbOh5E0l6mlFM/i9666BpUtkqQ9bS7WApCg==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.4.1.tgz", + "integrity": "sha512-sLODeRetZ/61KkKLJElaU3NuU2z7MhXf12Ml1WJMSdwpngeofneCRF+JBbat8HiSqhniOMuTemXMrsI7hA6XyA==", "dev": true }, "marked": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.2.tgz", - "integrity": "sha512-LqxwVH3P/rqKX4EKGz7+c2G9r98WeM/SW34ybhgNGhUQNKtf1GmmSkJ6cDGJ/t6tiyae49qRkpyTw2B9HOrgUA==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz", + "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==", "dev": true }, "matched": { @@ -8911,18 +8972,18 @@ "dev": true }, "requizzle": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", - "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", "dev": true, "requires": { - "underscore": "~1.6.0" + "lodash": "^4.17.14" }, "dependencies": { - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true } } @@ -9915,9 +9976,9 @@ "dev": true }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "structured-source": { @@ -10398,23 +10459,6 @@ "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", "dev": true }, - "underscore-contrib": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", - "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", - "dev": true, - "requires": { - "underscore": "1.6.0" - }, - "dependencies": { - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", - "dev": true - } - } - }, "unherit": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.1.tgz", @@ -10936,9 +10980,9 @@ } }, "xmlcreate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", - "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", "dev": true }, "xmlhttprequest-ssl": { diff --git a/package.json b/package.json index 3076569..55e21a4 100644 --- a/package.json +++ b/package.json @@ -1,98 +1,98 @@ { - "name": "videojs-contrib-eme", - "version": "4.0.0", - "description": "Supports Encrypted Media Extensions for playback of encrypted content in Video.js", - "main": "dist/videojs-contrib-eme.cjs.js", - "module": "dist/videojs-contrib-eme.es.js", - "scripts": { - "prebuild": "npm run clean", - "build": "npm-run-all -p build:*", - "build:js": "rollup -c scripts/rollup.config.js", - "clean": "shx rm -rf ./dist ./test/dist", - "postclean": "shx mkdir -p ./dist ./test/dist", - "docs": "npm-run-all docs:*", - "docs:api": "jsdoc src -g plugins/markdown -r -d docs/api", - "docs:toc": "doctoc README.md", - "lint": "vjsstandard", - "server": "karma start scripts/karma.conf.js --singleRun=false --auto-watch", - "start": "npm-run-all -p server watch", - "pretest": "npm-run-all lint build", - "test": "karma start scripts/karma.conf.js", - "posttest": "shx cat test/dist/coverage/text.txt", - "update-changelog": "conventional-changelog -p videojs -i CHANGELOG.md -s", - "preversion": "npm test", - "version": "is-prerelease || npm run update-changelog && git add CHANGELOG.md", - "watch": "npm-run-all -p watch:*", - "watch:js": "npm run build:js -- -w", - "prepublishOnly": "npm run build && vjsverify" - }, - "keywords": [ - "videojs", - "videojs-plugin" - ], - "copyright": "Copyright Brightcove, Inc. ", - "license": "Apache-2.0", - "vjsstandard": { - "ignore": [ - "dist", - "docs", - "test/dist" - ] - }, - "files": [ - "CONTRIBUTING.md", - "dist/", - "docs/", - "index.html", - "scripts/", - "src/", - "test/" - ], - "dependencies": { - "global": "^4.3.2", - "video.js": "^6 || ^7" - }, - "devDependencies": { - "conventional-changelog-cli": "^2.0.12", - "conventional-changelog-videojs": "^3.0.0", - "doctoc": "^1.3.1", - "husky": "^1.0.0-rc.13", - "jsdoc": "https://github.com/BrandonOCasey/jsdoc#feat/plugin-from-cli", - "karma": "^4.0.1", - "lint-staged": "^7.2.2", - "not-prerelease": "^1.0.1", - "npm-merge-driver-install": "^1.0.0", - "npm-run-all": "^4.1.3", - "pkg-ok": "^2.2.0", - "rollup": "^0.65.0", - "shx": "^0.3.2", - "sinon": "^6.1.5", - "videojs-generate-karma-config": "^5.1.0", - "videojs-generate-rollup-config": "^3.1.0", - "videojs-generator-verify": "^1.2.0", - "videojs-standard": "~9.0.1" - }, - "generator-videojs-plugin": { - "version": "7.3.2" - }, - "browserslist": [ - "defaults", - "ie 11" - ], - "author": "brandonocasey ", - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } - }, - "lint-staged": { - "*.js": [ - "vjsstandard --fix", - "git add" + "name": "videojs-contrib-eme", + "version": "4.0.0", + "description": "Supports Encrypted Media Extensions for playback of encrypted content in Video.js", + "main": "dist/videojs-contrib-eme.cjs.js", + "module": "dist/videojs-contrib-eme.es.js", + "scripts": { + "prebuild": "npm run clean", + "build": "npm-run-all -p build:*", + "build:js": "rollup -c scripts/rollup.config.js", + "clean": "shx rm -rf ./dist ./test/dist", + "postclean": "shx mkdir -p ./dist ./test/dist", + "docs": "npm-run-all docs:*", + "docs:api": "jsdoc src -g plugins/markdown -r -d docs/api", + "docs:toc": "doctoc README.md", + "lint": "vjsstandard", + "server": "karma start scripts/karma.conf.js --singleRun=false --auto-watch", + "start": "npm-run-all -p server watch", + "pretest": "npm-run-all lint build", + "test": "karma start scripts/karma.conf.js", + "posttest": "shx cat test/dist/coverage/text.txt", + "update-changelog": "conventional-changelog -p videojs -i CHANGELOG.md -s", + "preversion": "npm test", + "version": "is-prerelease || npm run update-changelog && git add CHANGELOG.md", + "watch": "npm-run-all -p watch:*", + "watch:js": "npm run build:js -- -w", + "prepublishOnly": "npm run build && vjsverify" + }, + "keywords": [ + "videojs", + "videojs-plugin" + ], + "copyright": "Copyright Brightcove, Inc. ", + "license": "Apache-2.0", + "vjsstandard": { + "ignore": [ + "dist", + "docs", + "test/dist" + ] + }, + "files": [ + "CONTRIBUTING.md", + "dist/", + "docs/", + "index.html", + "scripts/", + "src/", + "test/" ], - "README.md": [ - "npm run docs:toc", - "git add" - ] - } -} + "dependencies": { + "global": "^4.3.2", + "video.js": "^6 || ^7" + }, + "devDependencies": { + "conventional-changelog-cli": "^2.0.12", + "conventional-changelog-videojs": "^3.0.0", + "doctoc": "^1.3.1", + "husky": "^1.0.0-rc.13", + "jsdoc": "^3.6.9", + "karma": "^4.0.1", + "lint-staged": "^7.2.2", + "not-prerelease": "^1.0.1", + "npm-merge-driver-install": "^1.0.0", + "npm-run-all": "^4.1.3", + "pkg-ok": "^2.2.0", + "rollup": "^0.65.0", + "shx": "^0.3.2", + "sinon": "^6.1.5", + "videojs-generate-karma-config": "^5.1.0", + "videojs-generate-rollup-config": "^3.1.0", + "videojs-generator-verify": "^1.2.0", + "videojs-standard": "~9.0.1" + }, + "generator-videojs-plugin": { + "version": "7.3.2" + }, + "browserslist": [ + "defaults", + "ie 11" + ], + "author": "brandonocasey ", + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.js": [ + "vjsstandard --fix", + "git add" + ], + "README.md": [ + "npm run docs:toc", + "git add" + ] + } +} \ No newline at end of file diff --git a/src/eme.js b/src/eme.js index 8c70c7a..64e7a9e 100644 --- a/src/eme.js +++ b/src/eme.js @@ -1,15 +1,18 @@ -import videojs from 'video.js'; -import { requestPlayreadyLicense } from './playready'; -import window from 'global/window'; -import {uint8ArrayToString, mergeAndRemoveNull} from './utils'; -import {httpResponseHandler} from './http-handler.js'; +import videojs from "video.js"; +import { requestPlayreadyLicense } from "./playready"; +import window from "global/window"; +import { uint8ArrayToString, mergeAndRemoveNull } from "./utils"; +import { httpResponseHandler } from "./http-handler.js"; import { defaultGetCertificate as defaultFairplayGetCertificate, defaultGetLicense as defaultFairplayGetLicense, - defaultGetContentId as defaultFairplayGetContentId -} from './fairplay'; + defaultGetContentId as defaultFairplayGetContentId, +} from "./fairplay"; -const isFairplayKeySystem = (str) => str.startsWith('com.apple.fps'); +const isFairplayKeySystem = (str) => str.startsWith("com.apple.fps"); +const PERSISTENT_LICENSE = "persistent-license"; +const TEMPORARY_LICENSE = "temporary"; +const VJS_EME_STORAGE = "vjs_eme_storage"; /** * Returns an array of MediaKeySystemConfigurationObjects provided in the keySystem @@ -29,24 +32,27 @@ export const getSupportedConfigurations = (keySystem, keySystemOptions) => { const isFairplay = isFairplayKeySystem(keySystem); const supportedConfiguration = {}; - const initDataTypes = keySystemOptions.initDataTypes || + const initDataTypes = + keySystemOptions.initDataTypes || // fairplay requires an explicit initDataTypes - (isFairplay ? ['sinf'] : null); + (isFairplay ? ["sinf"] : null); const audioContentType = keySystemOptions.audioContentType; const audioRobustness = keySystemOptions.audioRobustness; - const videoContentType = keySystemOptions.videoContentType || + const videoContentType = + keySystemOptions.videoContentType || // fairplay requires an explicit videoCapabilities/videoContentType - (isFairplay ? 'video/mp4' : null); + (isFairplay ? "video/mp4" : null); const videoRobustness = keySystemOptions.videoRobustness; const persistentState = keySystemOptions.persistentState; + const sessionTypes = keySystemOptions.sessionTypes; if (audioContentType || audioRobustness) { supportedConfiguration.audioCapabilities = [ Object.assign( {}, - (audioContentType ? { contentType: audioContentType } : {}), - (audioRobustness ? { robustness: audioRobustness } : {}) - ) + audioContentType ? { contentType: audioContentType } : {}, + audioRobustness ? { robustness: audioRobustness } : {} + ), ]; } @@ -54,9 +60,9 @@ export const getSupportedConfigurations = (keySystem, keySystemOptions) => { supportedConfiguration.videoCapabilities = [ Object.assign( {}, - (videoContentType ? { contentType: videoContentType } : {}), - (videoRobustness ? { robustness: videoRobustness } : {}) - ) + videoContentType ? { contentType: videoContentType } : {}, + videoRobustness ? { robustness: videoRobustness } : {} + ), ]; } @@ -64,6 +70,10 @@ export const getSupportedConfigurations = (keySystem, keySystemOptions) => { supportedConfiguration.persistentState = persistentState; } + if (sessionTypes) { + supportedConfiguration.sessionTypes = sessionTypes; + } + if (initDataTypes) { supportedConfiguration.initDataTypes = initDataTypes; } @@ -78,21 +88,32 @@ export const getSupportedKeySystem = (keySystems) => { let promise; Object.keys(keySystems).forEach((keySystem) => { - const supportedConfigurations = getSupportedConfigurations(keySystem, keySystems[keySystem]); + const supportedConfigurations = getSupportedConfigurations( + keySystem, + keySystems[keySystem] + ); if (!promise) { - promise = - window.navigator.requestMediaKeySystemAccess(keySystem, supportedConfigurations); + promise = window.navigator.requestMediaKeySystemAccess( + keySystem, + supportedConfigurations + ); } else { promise = promise.catch((e) => - window.navigator.requestMediaKeySystemAccess(keySystem, supportedConfigurations)); + window.navigator.requestMediaKeySystemAccess( + keySystem, + supportedConfigurations + ) + ); } }); return promise; }; -export const makeNewRequest = (requestOptions) => { +var openedPersistentSessions = []; + +export const makeNewRequest = async (requestOptions) => { const { mediaKeys, initDataType, @@ -101,80 +122,158 @@ export const makeNewRequest = (requestOptions) => { getLicense, removeSession, eventBus, - contentId + contentId, + keySystem, } = requestOptions; - const keySession = mediaKeys.createSession(); + let sessionType = TEMPORARY_LICENSE; + let sessionTypes = options.keySystems[keySystem].sessionTypes; + if (sessionTypes && Array.isArray(sessionTypes)) { + sessionType = + sessionTypes.find((type) => type === PERSISTENT_LICENSE) || sessionType; + } + + const openedSession = openedPersistentSessions.find( + (s) => s.src === options.src + ); + if (openedSession) { + await openedSession.keySession.close(); + openedPersistentSessions = openedPersistentSessions.filter( + (s) => s.src !== options.src + ); + } + + const keySession = mediaKeys.createSession(sessionType); - eventBus.trigger('keysessioncreated'); + eventBus.trigger("keysessioncreated"); return new Promise((resolve, reject) => { + keySession.addEventListener( + "message", + (event) => { + // all other types will be handled by keystatuseschange + if ( + event.messageType !== "license-request" && + event.messageType !== "license-renewal" + ) { + return; + } - keySession.addEventListener('message', (event) => { - // all other types will be handled by keystatuseschange - if (event.messageType !== 'license-request' && event.messageType !== 'license-renewal') { - return; - } + getLicense(options, event.message, contentId) + .then((license) => { + resolve(keySession.update(license)); + }) + .catch((err) => { + reject(err); + }); + }, + false + ); - getLicense(options, event.message, contentId) - .then((license) => { - resolve(keySession.update(license)); - }) - .catch((err) => { - reject(err); - }); - }, false); - - keySession.addEventListener('keystatuseschange', (event) => { - let expired = false; - - // based on https://www.w3.org/TR/encrypted-media/#example-using-all-events - keySession.keyStatuses.forEach((status, keyId) => { - // Trigger an event so that outside listeners can take action if appropriate. - // For instance, the `output-restricted` status should result in an - // error being thrown. - eventBus.trigger({ - keyId, - status, - target: keySession, - type: 'keystatuschange' + keySession.closed.then( + function (reason) { + console.log("Session", this.sessionId, "closed, reason", reason); + }.bind(keySession) + ); + + keySession.addEventListener( + "keystatuseschange", + (event) => { + let expired = false; + + // based on https://www.w3.org/TR/encrypted-media/#example-using-all-events + keySession.keyStatuses.forEach((status, keyId) => { + // Trigger an event so that outside listeners can take action if appropriate. + // For instance, the `output-restricted` status should result in an + // error being thrown. + eventBus.trigger({ + keyId, + status, + target: keySession, + type: "keystatuschange", + }); + switch (status) { + case "expired": + // If one key is expired in a session, all keys are expired. From + // https://www.w3.org/TR/encrypted-media/#dom-mediakeystatus-expired, "All other + // keys in the session must have this status." + expired = true; + break; + case "internal-error": + const message = + 'Key status reported as "internal-error." Leaving the session open since we ' + + "don't have enough details to know if this error is fatal."; + + // "This value is not actionable by the application." + // https://www.w3.org/TR/encrypted-media/#dom-mediakeystatus-internal-error + videojs.log.warn(message, event); + break; + } }); - switch (status) { - case 'expired': - // If one key is expired in a session, all keys are expired. From - // https://www.w3.org/TR/encrypted-media/#dom-mediakeystatus-expired, "All other - // keys in the session must have this status." - expired = true; - break; - case 'internal-error': - const message = - 'Key status reported as "internal-error." Leaving the session open since we ' + - 'don\'t have enough details to know if this error is fatal.'; - - // "This value is not actionable by the application." - // https://www.w3.org/TR/encrypted-media/#dom-mediakeystatus-internal-error - videojs.log.warn(message, event); - break; + + if (expired) { + // Close session and remove it from the session list to ensure that a new + // session can be created. + // + // TODO convert to videojs.log.debug and add back in + // https://github.com/videojs/video.js/pull/4780 + // videojs.log.debug('Session expired, closing the session.'); + keySession.close().then(() => { + removeSession(initData); + makeNewRequest(requestOptions); + }); } - }); + }, + false + ); - if (expired) { - // Close session and remove it from the session list to ensure that a new - // session can be created. - // - // TODO convert to videojs.log.debug and add back in - // https://github.com/videojs/video.js/pull/4780 - // videojs.log.debug('Session expired, closing the session.'); - keySession.close().then(() => { - removeSession(initData); - makeNewRequest(requestOptions); + let sessions = localStorage.getItem(VJS_EME_STORAGE); + try { + sessions = sessions ? JSON.parse(sessions) : {}; + } catch (error) { + console.log("error during sessions parse, re-init it", error); + sessions = {}; + } + if (sessionType === PERSISTENT_LICENSE && sessions[options.src]) { + keySession + .load(sessions[options.src]) + .then(function (loaded) { + if (!loaded) { + console.error( + "No stored session with the ID " + + sessions[options.src] + + " was found." + ); + // The application should remove its record of |sessionId|. + return; + } + openedPersistentSessions.push({ src: options.src, keySession }); + }) + .catch((error) => { + console.log( + `Unable to load or initialize the stored session with the ID ${ + sessions[options.src] + }: ${error}` + ); }); - } - }, false); + return; + } - keySession.generateRequest(initDataType, initData).catch(() => { - reject('Unable to create or initialize key session'); - }); + keySession + .generateRequest(initDataType, initData) + .then( + function () { + if (sessionType === PERSISTENT_LICENSE) { + // set session id with src video as key + sessions[options.src] = this.sessionId; + openedPersistentSessions.push({ src: options.src, keySession }); + localStorage.setItem(VJS_EME_STORAGE, JSON.stringify(sessions)); + } + }.bind(keySession) + ) + .catch(() => { + reject("Unable to create or initialize key session"); + }); }); }; @@ -213,7 +312,7 @@ export const addSession = ({ getLicense, contentId, removeSession, - eventBus + eventBus, }) => { const sessionData = { initDataType, @@ -222,7 +321,8 @@ export const addSession = ({ getLicense, removeSession, eventBus, - contentId + contentId, + keySystem: video.keySystem, }; if (video.mediaKeysObject) { @@ -257,7 +357,7 @@ export const addSession = ({ export const addPendingSessions = ({ video, certificate, - createdMediaKeys + createdMediaKeys, }) => { // save media keys on the video element to act as a reference for other functions so // that they don't recreate the keys @@ -271,16 +371,19 @@ export const addPendingSessions = ({ for (let i = 0; i < video.pendingSessionData.length; i++) { const data = video.pendingSessionData[i]; - promises.push(makeNewRequest({ - mediaKeys: video.mediaKeysObject, - initDataType: data.initDataType, - initData: data.initData, - options: data.options, - getLicense: data.getLicense, - removeSession: data.removeSession, - eventBus: data.eventBus, - contentId: data.contentId - })); + promises.push( + makeNewRequest({ + mediaKeys: video.mediaKeysObject, + initDataType: data.initDataType, + initData: data.initData, + options: data.options, + getLicense: data.getLicense, + removeSession: data.removeSession, + eventBus: data.eventBus, + contentId: data.contentId, + keySystem: video.keySystem, + }) + ); } video.pendingSessionData = []; @@ -290,32 +393,37 @@ export const addPendingSessions = ({ return Promise.all(promises); }; -const defaultPlayreadyGetLicense = (keySystemOptions) => (emeOptions, keyMessage, callback) => { - requestPlayreadyLicense(keySystemOptions, keyMessage, emeOptions, callback); -}; +const defaultPlayreadyGetLicense = + (keySystemOptions) => (emeOptions, keyMessage, callback) => { + requestPlayreadyLicense(keySystemOptions, keyMessage, emeOptions, callback); + }; -export const defaultGetLicense = (keySystemOptions) => (emeOptions, keyMessage, callback) => { - const headers = mergeAndRemoveNull( - {'Content-type': 'application/octet-stream'}, - emeOptions.emeHeaders, - keySystemOptions.licenseHeaders - ); +export const defaultGetLicense = + (keySystemOptions) => (emeOptions, keyMessage, callback) => { + const headers = mergeAndRemoveNull( + { "Content-type": "application/octet-stream" }, + emeOptions.emeHeaders, + keySystemOptions.licenseHeaders + ); - videojs.xhr({ - uri: keySystemOptions.url, - method: 'POST', - responseType: 'arraybuffer', - body: keyMessage, - headers - }, httpResponseHandler(callback, true)); -}; + videojs.xhr( + { + uri: keySystemOptions.url, + method: "POST", + responseType: "arraybuffer", + body: keyMessage, + headers, + }, + httpResponseHandler(callback, true) + ); + }; const promisifyGetLicense = (keySystem, getLicenseFn, eventBus) => { return (emeOptions, keyMessage, contentId) => { return new Promise((resolve, reject) => { - const callback = function(err, license) { + const callback = function (err, license) { if (eventBus) { - eventBus.trigger('licenserequestattempted'); + eventBus.trigger("licenserequestattempted"); } if (err) { reject(err); @@ -326,7 +434,12 @@ const promisifyGetLicense = (keySystem, getLicenseFn, eventBus) => { }; if (isFairplayKeySystem(keySystem)) { - getLicenseFn(emeOptions, contentId, new Uint8Array(keyMessage), callback); + getLicenseFn( + emeOptions, + contentId, + new Uint8Array(keyMessage), + callback + ); } else { getLicenseFn(emeOptions, keyMessage, callback); } @@ -335,7 +448,7 @@ const promisifyGetLicense = (keySystem, getLicenseFn, eventBus) => { }; const standardizeKeySystemOptions = (keySystem, keySystemOptions) => { - if (typeof keySystemOptions === 'string') { + if (typeof keySystemOptions === "string") { keySystemOptions = { url: keySystemOptions }; } @@ -344,17 +457,26 @@ const standardizeKeySystemOptions = (keySystem, keySystemOptions) => { } if (!keySystemOptions.url && !keySystemOptions.getLicense) { - throw new Error(`Missing url/licenseUri or getLicense in ${keySystem} keySystem configuration.`); + throw new Error( + `Missing url/licenseUri or getLicense in ${keySystem} keySystem configuration.` + ); } const isFairplay = isFairplayKeySystem(keySystem); - if (isFairplay && keySystemOptions.certificateUri && !keySystemOptions.getCertificate) { - keySystemOptions.getCertificate = defaultFairplayGetCertificate(keySystemOptions); + if ( + isFairplay && + keySystemOptions.certificateUri && + !keySystemOptions.getCertificate + ) { + keySystemOptions.getCertificate = + defaultFairplayGetCertificate(keySystemOptions); } if (isFairplay && !keySystemOptions.getCertificate) { - throw new Error(`Missing getCertificate or certificateUri in ${keySystem} keySystem configuration.`); + throw new Error( + `Missing getCertificate or certificateUri in ${keySystem} keySystem configuration.` + ); } if (isFairplay && !keySystemOptions.getContentId) { @@ -362,8 +484,9 @@ const standardizeKeySystemOptions = (keySystem, keySystemOptions) => { } if (keySystemOptions.url && !keySystemOptions.getLicense) { - if (keySystem === 'com.microsoft.playready') { - keySystemOptions.getLicense = defaultPlayreadyGetLicense(keySystemOptions); + if (keySystem === "com.microsoft.playready") { + keySystemOptions.getLicense = + defaultPlayreadyGetLicense(keySystemOptions); } else if (isFairplay) { keySystemOptions.getLicense = defaultFairplayGetLicense(keySystemOptions); } else { @@ -381,7 +504,7 @@ export const standard5July2016 = ({ keySystemAccess, options, removeSession, - eventBus + eventBus, }) => { let keySystemPromise = Promise.resolve(); const keySystem = keySystemAccess.keySystem; @@ -397,10 +520,11 @@ export const standard5July2016 = ({ return Promise.reject(e); } - const contentId = keySystemOptions.getContentId ? - keySystemOptions.getContentId(options, uint8ArrayToString(initData)) : null; + const contentId = keySystemOptions.getContentId + ? keySystemOptions.getContentId(options, uint8ArrayToString(initData)) + : null; - if (typeof video.mediaKeysObject === 'undefined') { + if (typeof video.mediaKeysObject === "undefined") { // Prevent entering this path again. video.mediaKeysObject = null; @@ -428,28 +552,34 @@ export const standard5July2016 = ({ resolve(); }); - }).then(() => { - return keySystemAccess.createMediaKeys(); - }).then((createdMediaKeys) => { - return addPendingSessions({ - video, - certificate, - createdMediaKeys + }) + .then(() => { + return keySystemAccess.createMediaKeys(); + }) + .then((createdMediaKeys) => { + return addPendingSessions({ + video, + certificate, + createdMediaKeys, + }); + }) + .catch((err) => { + // if we have a specific error message, use it, otherwise show a more + // generic one + if (err) { + return Promise.reject(err); + } + return Promise.reject( + "Failed to create and initialize a MediaKeys object" + ); }); - }).catch((err) => { - // if we have a specific error message, use it, otherwise show a more - // generic one - if (err) { - return Promise.reject(err); - } - return Promise.reject('Failed to create and initialize a MediaKeys object'); - }); } return keySystemPromise.then(() => { // if key system has not been determined then addSession doesn't need getLicense - const getLicense = video.keySystem ? - promisifyGetLicense(keySystem, keySystemOptions.getLicense, eventBus) : null; + const getLicense = video.keySystem + ? promisifyGetLicense(keySystem, keySystemOptions.getLicense, eventBus) + : null; return addSession({ video, @@ -459,7 +589,7 @@ export const standard5July2016 = ({ getLicense, contentId, removeSession, - eventBus + eventBus, }); }); };