diff --git a/.gitignore b/.gitignore index d911e40..14a3ee9 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ npm-debug.log* # Dependency directories bower_components/ node_modules/ +hls-fetcher/ # Build-related directories dist/ diff --git a/package-lock.json b/package-lock.json index 063afb1..d740ede 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,14 @@ "js-tokens": "^4.0.0" } }, + "@babel/runtime": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz", + "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, "@samverschueren/stream-to-observable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", @@ -34,9 +42,9 @@ } }, "@textlint/ast-node-types": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-4.2.3.tgz", - "integrity": "sha512-5zqbMpeWk1B3AUkAmI5d3Y6CWqQNXeLKbdOGkwkvM0NSvhnlpAVooJ348lyfk8AwEQwzRVtAacQVXmIhdCwLGA==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-4.2.4.tgz", + "integrity": "sha512-ggiixpScxgdMY42b6UafD1iUboSvl9k3vGA9kynP+kd6mEhTDzxtb1aHPDAnV+DpAEw4qpHMz72GBFkX/iOSFw==", "dev": true }, "@textlint/markdown-to-ast": { @@ -54,6 +62,16 @@ "unified": "^6.1.6" } }, + "@videojs/vhs-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-1.1.0.tgz", + "integrity": "sha512-J+1rLDj3iA1qejmCU3b+UOdTpT9w00XaxyJis+JUfTOZ5o5t8BHa5Uhmk8pXDNsjSJNJyMMgtJ6+z8g/Fxsuzg==", + "requires": { + "@babel/runtime": "^7.5.5", + "global": "^4.3.2", + "url-toolkit": "^2.1.6" + } + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -77,9 +95,9 @@ "dev": true }, "acorn-jsx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", - "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", + "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", "dev": true }, "add-stream": { @@ -89,13 +107,14 @@ "dev": true }, "aes-decrypter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-3.0.0.tgz", - "integrity": "sha1-eEihwUW5/b9Xrj4rWxvHzwZEqPs=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-3.0.1.tgz", + "integrity": "sha512-bnuOEvfgTucocsfx9RAxv9S91lJhFkQsTqDCVnhV4BXk6baO+WOLeipdPTiBNg8QyikBWRuJry7Rf9oY6OWy9A==", "requires": { - "commander": "^2.9.0", + "@babel/runtime": "^7.5.5", + "@videojs/vhs-utils": "^1.0.0", "global": "^4.3.2", - "pkcs7": "^1.0.2" + "pkcs7": "^1.0.3" } }, "ajv": { @@ -736,7 +755,8 @@ "commander": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true }, "comment-parser": { "version": "0.5.5", @@ -811,22 +831,22 @@ } }, "conventional-changelog": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.9.tgz", - "integrity": "sha512-JbNVm1iGZ3aXxcFZjqKNDNfdgchQjSltWc8rvSniMrkHLsub9Wn20/JLdJNTBM74dt1IA2M+v/mzServ6N37YA==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.10.tgz", + "integrity": "sha512-6RDj31hL39HUkpqvPjRlOxAwJRwur8O2qu9m6R0FBNDGwCJyy4SYH9NfyshozxYSeklrauKRf3oSbyoEZVzu9Q==", "dev": true, "requires": { "conventional-changelog-angular": "^5.0.3", "conventional-changelog-atom": "^2.0.1", "conventional-changelog-codemirror": "^2.0.1", - "conventional-changelog-conventionalcommits": "^4.0.0", - "conventional-changelog-core": "^3.2.3", + "conventional-changelog-conventionalcommits": "^4.1.0", + "conventional-changelog-core": "^4.0.0", "conventional-changelog-ember": "^2.0.2", "conventional-changelog-eslint": "^3.0.2", "conventional-changelog-express": "^2.0.1", "conventional-changelog-jquery": "^3.0.4", "conventional-changelog-jshint": "^2.0.1", - "conventional-changelog-preset-loader": "^2.1.1" + "conventional-changelog-preset-loader": "^2.2.0" } }, "conventional-changelog-angular": { @@ -849,14 +869,14 @@ } }, "conventional-changelog-cli": { - "version": "2.0.22", - "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.0.22.tgz", - "integrity": "sha512-aLUQmyomfXV0xauIpQtsLPXnqZMumm4+1P6Mv2CjrlKF8XQ8Jn1LnMUScaLnqvDOzeGIMwPIY5DVA7hGFD6ddw==", + "version": "2.0.23", + "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.0.23.tgz", + "integrity": "sha512-a/jDZHEUpSHQMAqeDrmrFhz9CKHBKhBGpJyc38BCfNjFA1RKchpq/Qqbo1BZwRLWrW/PX7IGsUicTyhniqUH9g==", "dev": true, "requires": { "add-stream": "^1.0.0", - "conventional-changelog": "^3.1.9", - "lodash": "^4.2.1", + "conventional-changelog": "^3.1.10", + "lodash": "^4.14.14", "meow": "^4.0.0", "tempfile": "^1.1.1" } @@ -871,9 +891,9 @@ } }, "conventional-changelog-conventionalcommits": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.0.0.tgz", - "integrity": "sha512-hPkVd+OXFqXhKgi/D2i4+lCbAtI7RqzDtAb7MhDsTer2pNA7mmkrwNdN6ph3iC/r3F0Wf10JYheEElRf/Q5qJw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.1.0.tgz", + "integrity": "sha512-J3xolGrH8PTxpCqueHOuZtv3Cp73SQOWiBQzlsaugZAZ+hZgcJBonmC+1bQbfGs2neC2S18p2L1Gx+nTEglJTQ==", "dev": true, "requires": { "compare-func": "^1.3.1", @@ -881,18 +901,18 @@ } }, "conventional-changelog-core": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.2.3.tgz", - "integrity": "sha512-LMMX1JlxPIq/Ez5aYAYS5CpuwbOk6QFp8O4HLAcZxe3vxoCtABkhfjetk8IYdRB9CDQGwJFLR3Dr55Za6XKgUQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.0.0.tgz", + "integrity": "sha512-+bZMeBUdjKxfyX2w6EST9U7zb85wxrGS3IV4H7SqPya44osNQbm3P+vyqfLs6s57FkoEamC93ioDEiguVLWmSQ==", "dev": true, "requires": { - "conventional-changelog-writer": "^4.0.6", + "conventional-changelog-writer": "^4.0.7", "conventional-commits-parser": "^3.0.3", "dateformat": "^3.0.0", "get-pkg-repo": "^1.0.0", "git-raw-commits": "2.0.0", "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^2.0.3", + "git-semver-tags": "^3.0.0", "lodash": "^4.2.1", "normalize-package-data": "^2.3.5", "q": "^1.5.1", @@ -948,9 +968,9 @@ } }, "conventional-changelog-preset-loader": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.1.1.tgz", - "integrity": "sha512-K4avzGMLm5Xw0Ek/6eE3vdOXkqnpf9ydb68XYmCc16cJ99XMMbc2oaNMuPwAsxVK6CC1yA4/I90EhmWNj0Q6HA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.2.0.tgz", + "integrity": "sha512-zXB+5vF7D5Y3Cb/rJfSyCCvFphCVmF8mFqOdncX3BmjZwAtGAPfYrBcT225udilCKvBbHgyzgxqz2GWDB5xShQ==", "dev": true }, "conventional-changelog-videojs": { @@ -965,15 +985,15 @@ } }, "conventional-changelog-writer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.6.tgz", - "integrity": "sha512-ou/sbrplJMM6KQpR5rKFYNVQYesFjN7WpNGdudQSWNi6X+RgyFUcSv871YBYkrUYV9EX8ijMohYVzn9RUb+4ag==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.7.tgz", + "integrity": "sha512-p/wzs9eYaxhFbrmX/mCJNwJuvvHR+j4Fd0SQa2xyAhYed6KBiZ780LvoqUUvsayP4R1DtC27czalGUhKV2oabw==", "dev": true, "requires": { "compare-func": "^1.3.1", "conventional-commits-filter": "^2.0.2", "dateformat": "^3.0.0", - "handlebars": "^4.1.0", + "handlebars": "^4.1.2", "json-stringify-safe": "^5.0.1", "lodash": "^4.2.1", "meow": "^4.0.0", @@ -1044,9 +1064,9 @@ }, "dependencies": { "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -1150,10 +1170,18 @@ } }, "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.0.tgz", + "integrity": "sha512-ZbfWJq/wN1Z273o7mUSjILYqehAktR2NVoSrOukDkU9kg2v/Uv89yU4Cvz8seJeAmtN5oqiefKq8FPuXOboqLw==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } }, "deep-is": { "version": "0.1.3", @@ -1161,6 +1189,15 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -1251,13 +1288,27 @@ } }, "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz", + "integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==", "dev": true, "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "dev": true + }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", + "dev": true + } } }, "dom-walk": { @@ -1444,9 +1495,9 @@ } }, "inquirer": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz", - "integrity": "sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", "dev": true, "requires": { "ansi-escapes": "^3.2.0", @@ -1506,9 +1557,9 @@ "dev": true }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "string-width": { @@ -1589,18 +1640,18 @@ } }, "eslint-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.0.tgz", - "integrity": "sha512-7ehnzPaP5IIEh1r1tkjuIrxqhNkzUJa9z3R92tLJdZIVdWaczEhr3EbhGtsMrVxi1KeR8qA7Off6SWc5WNQqyQ==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", + "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", "dev": true, "requires": { "eslint-visitor-keys": "^1.0.0" } }, "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", "dev": true }, "espree": { @@ -1615,9 +1666,9 @@ }, "dependencies": { "acorn": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.0.tgz", - "integrity": "sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", "dev": true } } @@ -1647,15 +1698,15 @@ } }, "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "execa": { @@ -1989,6 +2040,12 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -2346,9 +2403,9 @@ } }, "git-semver-tags": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.3.tgz", - "integrity": "sha512-tj4FD4ww2RX2ae//jSrXZzrocla9db5h0V7ikPl1P/WwoZar9epdUhwR7XHXSgc+ZkNq72BEEerqQuicoEQfzA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-3.0.0.tgz", + "integrity": "sha512-T4C/gJ9k2Bnxz+PubtcyiMtUUKrC+Nh9Q4zaECcnmVMwJgPhrNyP/Rf+YpdRqsJbCV/+kYrCH24Xg+IeAmbOPg==", "dev": true, "requires": { "meow": "^4.0.0", @@ -2400,9 +2457,9 @@ "dev": true }, "graceful-fs": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", - "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "growl": { @@ -2437,6 +2494,15 @@ "har-schema": "^2.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -2511,9 +2577,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", "dev": true }, "htmlparser2": { @@ -2616,9 +2682,9 @@ } }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -2833,6 +2899,12 @@ "is-decimal": "^1.0.0" } }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -2874,6 +2946,12 @@ } } }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, "is-decimal": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.3.tgz", @@ -3006,6 +3084,15 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, "is-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", @@ -3532,10 +3619,12 @@ } }, "m3u8-parser": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-4.4.0.tgz", - "integrity": "sha512-iH2AygTFILtato+XAgnoPYzLHM4R3DjATj7Ozbk7EHdB2XoLF2oyOUguM7Kc4UVHbQHHL/QPaw98r7PbWzG0gg==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-4.4.2.tgz", + "integrity": "sha512-4JJ163xdmyBjyHIH+OzdT9ATpgLcBulW8tD/aLEcJ8vXxrmonQWjAAOXQ/0szRE3IJkh+ECTrZjOBRH/Ii77cw==", "requires": { + "@babel/runtime": "^7.5.5", + "@videojs/vhs-utils": "^1.1.0", "global": "^4.3.2" } }, @@ -3752,6 +3841,17 @@ "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", "dev": true }, + "mpd-parser": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-0.9.0.tgz", + "integrity": "sha512-odcncZQorJk6EgdiLOUkHeKMtABbCMiKpxMAtPN5T0xoNL/7qzO5fmPkNmsHsBccQg/3vOg9XXHadS/dIenHBg==", + "requires": { + "@babel/runtime": "^7.5.5", + "@videojs/vhs-utils": "^1.1.0", + "global": "^4.3.2", + "xmldom": "^0.1.27" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -3834,9 +3934,9 @@ "dev": true }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -3901,9 +4001,9 @@ }, "dependencies": { "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -3918,9 +4018,9 @@ }, "dependencies": { "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -4416,6 +4516,18 @@ } } }, + "object-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", + "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -4665,17 +4777,20 @@ } }, "pkcs7": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pkcs7/-/pkcs7-1.0.2.tgz", - "integrity": "sha1-ttulJ1KMKUK/wSLOLa/NteWQdOc=" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkcs7/-/pkcs7-1.0.3.tgz", + "integrity": "sha512-3MP+alokz148xIxdMpLovjxIUia1cMzxyJ6FjyZl2a1UPapjRk4Y8hni4KsZRGrdbD1woArGMKe/OsSB7ggzHQ==", + "requires": { + "@babel/runtime": "^7.5.5" + } }, "pkg-can-install": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pkg-can-install/-/pkg-can-install-1.0.3.tgz", - "integrity": "sha512-LRvLsj4XajSAf6RBO88H99SLEiDNeJ7lCF9rf42vQCCrmFtBEfbXflgJ5qRtwCdeA/Y+X3UYHoJkElT2BFEDGg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pkg-can-install/-/pkg-can-install-1.0.4.tgz", + "integrity": "sha512-we6BP4jR0ZQcDc9G9ptFvFJvYXoHlCT2PAinu98AotA4j8ZeazkUM/VSuFD4aQG3aPcuyBLLZJLxOEfzvEF7gw==", "dev": true, "requires": { - "shelljs": "^0.8.2" + "shelljs": "^0.8.3" } }, "pkg-dir": { @@ -4707,9 +4822,9 @@ } }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -4763,9 +4878,9 @@ } }, "please-upgrade-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz", - "integrity": "sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", "dev": true, "requires": { "semver-compare": "^1.0.0" @@ -4841,9 +4956,9 @@ "dev": true }, "psl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.2.0.tgz", - "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.3.0.tgz", + "integrity": "sha512-avHdspHO+9rQTLbv1RO+MPYeP/SzsCoxofjVnHanETfQhTJrmB0HlDoW+EiN/R+C0BZ+gERab9NY0lPN2TxNag==" }, "pump": { "version": "3.0.0", @@ -4928,6 +5043,11 @@ "strip-indent": "^2.0.0" } }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -4938,6 +5058,15 @@ "safe-regex": "^1.1.0" } }, + "regexp.prototype.flags": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", + "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2" + } + }, "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", @@ -5042,9 +5171,9 @@ } }, "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -5153,9 +5282,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", - "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "semver-compare": { @@ -5559,20 +5688,12 @@ } }, "string_decoder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", - "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } + "safe-buffer": "~5.2.0" } }, "stringify-object": { @@ -5660,9 +5781,9 @@ "dev": true }, "table": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.4.tgz", - "integrity": "sha512-IIfEAUx5QlODLblLrGTTLJA7Tk0iLSGBvgY8essPRVNGHAzThujww1YqHLs6h3HfTg55h++RzLHH5Xw/rfv+mg==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", "dev": true, "requires": { "ajv": "^6.10.2", @@ -6100,6 +6221,11 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url-toolkit": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.1.6.tgz", + "integrity": "sha512-UaZ2+50am4HwrV2crR/JAf63Q4VvPYphe63WGeoJxeu8gmOm0qxPt+KsukfakPNrX9aymGNEkkaoICwn+OuvBw==" + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -6113,9 +6239,9 @@ "dev": true }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" }, "validate-npm-package-license": { "version": "3.0.4", @@ -6292,6 +6418,11 @@ "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", "dev": true }, + "xmldom": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", + "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 38ac59f..5b9f892 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,9 @@ "aes-decrypter": "^3.0.0", "bluebird": "^3.4.0", "filenamify": "^4.1.0", - "m3u8-parser": "^4.3.0", + "m3u8-parser": "^4.4.2", "mkdirp": "^0.5.1", + "mpd-parser": "^0.9.0", "pessimist": "^0.3.5", "request": "^2.87.0", "requestretry": "^2.0.0" diff --git a/src/walk-manifest.js b/src/walk-manifest.js index 0113bff..055ed3e 100644 --- a/src/walk-manifest.js +++ b/src/walk-manifest.js @@ -1,5 +1,6 @@ /* eslint-disable no-console */ const m3u8 = require('m3u8-parser'); +const mpd = require('mpd-parser'); const request = require('requestretry'); const url = require('url'); const path = require('path'); @@ -55,7 +56,7 @@ const mediaGroupPlaylists = function(mediaGroups) { return playlists; }; -const parseManifest = function(content) { +const parseM3u8Manifest = function(content) { const parser = new m3u8.Parser(); parser.push(content); @@ -63,11 +64,55 @@ const parseManifest = function(content) { return parser.manifest; }; +const collectPlaylists = function(parsed) { + return [] + .concat(parsed.playlists || []) + .concat(mediaGroupPlaylists(parsed.mediaGroups || {}) || []) + .reduce(function(acc, p) { + acc.push(p); + + if (p.playlists) { + acc = acc.concat(collectPlaylists(p)); + } + return acc; + }, []); +}; + +const parseMpdManifest = function(content, srcUrl) { + const mpdPlaylists = mpd.toPlaylists(mpd.inheritAttributes(mpd.stringToMpdXml(content), { + manifestUri: srcUrl + })); + + const m3u8Result = mpd.toM3u8(mpdPlaylists); + const m3u8Playlists = collectPlaylists(m3u8Result); + + m3u8Playlists.forEach(function(m) { + const mpdPlaylist = m.attributes && mpdPlaylists.find(function(p) { + return p.attributes.id === m.attributes.NAME; + }); + + if (mpdPlaylist) { + m.dashattributes = mpdPlaylist.attributes; + } + // add sidx to segments + if (m.sidx) { + // fix init segment map if it has one + if (m.sidx.map && !m.sidx.map.uri) { + m.sidx.map.uri = m.sidx.map.resolvedUri; + } + + m.segments.push(m.sidx); + } + }); + + return m3u8Result; +}; + const parseKey = function(requestOptions, basedir, decrypt, resources, manifest, parent) { return new Promise(function(resolve, reject) { if (!manifest.parsed.segments[0] || !manifest.parsed.segments[0].key) { - resolve({}); + return resolve({}); } const key = manifest.parsed.segments[0].key; @@ -92,7 +137,7 @@ const parseKey = function(requestOptions, basedir, decrypt, resources, manifest, )); key.uri = keyUri; resources.push(key); - resolve(key); + return resolve(key); } requestOptions.url = keyUri; @@ -155,17 +200,21 @@ const walkPlaylist = function(options) { visitedUrls = [], requestTimeout = 1500, requestRetryMaxAttempts = 5, + dashPlaylist = null, requestRetryDelay = 5000 } = options; let resources = []; - const manifest = {}; + const manifest = {parent}; manifest.uri = uri; manifest.file = path.join(basedir, fsSanitize(path.basename(uri))); // if we are not the master playlist - if (parent) { + if (dashPlaylist && parent) { + manifest.file = parent.file; + manifest.uri = parent.uri; + } else if (parent) { manifest.file = path.join( path.dirname(parent.file), 'manifest' + manifestIndex, @@ -179,101 +228,124 @@ const walkPlaylist = function(options) { parent.content = Buffer.from(parent.content.toString().replace(uri, path.relative(path.dirname(parent.file), manifest.file))); } - if (visitedUrls.includes(manifest.uri)) { + if (!dashPlaylist && visitedUrls.includes(manifest.uri)) { console.error(`[WARN] Trying to visit the same uri again; skipping to avoid getting stuck in a cycle: ${manifest.uri}`); return resolve(resources); } - request({ - url: manifest.uri, - timeout: requestTimeout, - maxAttempts: requestRetryMaxAttempts, - retryDelay: requestRetryDelay - }) - .then(function(response) { - if (response.statusCode !== 200) { - const manifestError = new Error(response.statusCode + '|' + manifest.uri); + let requestPromise; - manifestError.reponse = response; - return onError(manifestError, manifest.uri, resources, resolve, reject); - } - // Only push manifest uris that get a non 200 and don't timeout + if (dashPlaylist) { + requestPromise = Promise.resolve({statusCode: 200}); + } else { + requestPromise = request({ + url: manifest.uri, + timeout: requestTimeout, + maxAttempts: requestRetryMaxAttempts, + retryDelay: requestRetryDelay + }); + } + + requestPromise.then(function(response) { + if (response.statusCode !== 200) { + const manifestError = new Error(response.statusCode + '|' + manifest.uri); + + manifestError.reponse = response; + return onError(manifestError, manifest.uri, resources, resolve, reject); + } + // Only push manifest uris that get a non 200 and don't timeout + let dash; + + if (!dashPlaylist) { resources.push(manifest); visitedUrls.push(manifest.uri); manifest.content = response.body; + if ((/^application\/dash\+xml/i).test(response.headers['content-type']) || (/^\<\?xml/i).test(response.body)) { + dash = true; + manifest.parsed = parseMpdManifest(manifest.content, manifest.uri); + } else { + manifest.parsed = parseM3u8Manifest(manifest.content); + } + } else { + dash = true; + manifest.parsed = dashPlaylist; + } - manifest.parsed = parseManifest(manifest.content); - manifest.parsed.segments = manifest.parsed.segments || []; - manifest.parsed.playlists = manifest.parsed.playlists || []; - manifest.parsed.mediaGroups = manifest.parsed.mediaGroups || {}; + manifest.parsed.segments = manifest.parsed.segments || []; + manifest.parsed.playlists = manifest.parsed.playlists || []; + manifest.parsed.mediaGroups = manifest.parsed.mediaGroups || {}; - const initSegments = []; + const initSegments = []; - manifest.parsed.segments.forEach(function(s) { - if (s.map && s.map.uri && !initSegments.some((m) => s.map.uri === m.uri)) { - manifest.parsed.segments.push(s.map); - initSegments.push(s.map); + manifest.parsed.segments.forEach(function(s) { + if (s.map && s.map.uri && !initSegments.some((m) => s.map.uri === m.uri)) { + manifest.parsed.segments.push(s.map); + initSegments.push(s.map); + } + }); + + const playlists = manifest.parsed.playlists.concat(mediaGroupPlaylists(manifest.parsed.mediaGroups)); + + parseKey({ + time: requestTimeout, + maxAttempts: requestRetryMaxAttempts, + retryDelay: requestRetryDelay + }, basedir, decrypt, resources, manifest, parent).then(function(key) { + // SEGMENTS + manifest.parsed.segments.forEach(function(s, i) { + if (!s.uri) { + return; } - }); + // put segments in manifest-name/segment-name.ts + s.file = path.join(path.dirname(manifest.file), fsSanitize(path.basename(s.uri))); - const playlists = manifest.parsed.playlists.concat(mediaGroupPlaylists(manifest.parsed.mediaGroups)); - - parseKey({ - time: requestTimeout, - maxAttempts: requestRetryMaxAttempts, - retryDelay: requestRetryDelay - }, basedir, decrypt, resources, manifest, parent).then(function(key) { - // SEGMENTS - manifest.parsed.segments.forEach(function(s, i) { - if (!s.uri) { - return; - } - // put segments in manifest-name/segment-name.ts - s.file = path.join(path.dirname(manifest.file), fsSanitize(path.basename(s.uri))); - if (!isAbsolute(s.uri)) { - s.uri = joinURI(path.dirname(manifest.uri), s.uri); - } - if (key) { - s.key = key; - s.key.iv = s.key.iv || new Uint32Array([0, 0, 0, manifest.parsed.mediaSequence, i]); - } + if (!isAbsolute(s.uri)) { + s.uri = joinURI(path.dirname(manifest.uri), s.uri); + } + if (key) { + s.key = key; + s.key.iv = s.key.iv || new Uint32Array([0, 0, 0, manifest.parsed.mediaSequence, i]); + } + if (manifest.content) { manifest.content = Buffer.from(manifest.content.toString().replace( s.uri, path.relative(path.dirname(manifest.file), s.file) )); - resources.push(s); - }); + } + resources.push(s); + }); - // SUB Playlists - const subs = playlists.map(function(p, z) { - if (!p.uri) { - return Promise().resolve(resources); - } - return walkPlaylist({ - decrypt, - basedir, - uri: p.uri, - parent: manifest, - manifestIndex: z, - onError, - visitedUrls, - requestTimeout, - requestRetryMaxAttempts, - requestRetryDelay - }); + // SUB Playlists + const subs = playlists.map(function(p, z) { + if (!p.uri && !dash) { + return Promise.resolve(resources); + } + return walkPlaylist({ + dashPlaylist: dash ? p : null, + decrypt, + basedir, + uri: p.uri, + parent: manifest, + manifestIndex: z, + onError, + visitedUrls, + requestTimeout, + requestRetryMaxAttempts, + requestRetryDelay }); + }); - Promise.all(subs).then(function(r) { - const flatten = [].concat.apply([], r); + Promise.all(subs).then(function(r) { + const flatten = [].concat.apply([], r); - resources = resources.concat(flatten); - resolve(resources); - }).catch(function(err) { - onError(err, manifest.uri, resources, resolve, reject); - }); + resources = resources.concat(flatten); + resolve(resources); + }).catch(function(err) { + onError(err, manifest.uri, resources, resolve, reject); }); - }) + }); + }) .catch(function(err) { onError(err, manifest.uri, resources, resolve, reject); }); diff --git a/src/write-data.js b/src/write-data.js index dc2d84b..0153e5b 100644 --- a/src/write-data.js +++ b/src/write-data.js @@ -35,25 +35,19 @@ const requestFile = function(uri) { }); }; -const toArrayBuffer = function(buffer) { - const ab = new ArrayBuffer(buffer.length); - const view = new Uint8Array(ab); - - for (let i = 0; i < buffer.length; ++i) { - view[i] = buffer[i]; - } - return ab; +const toUint8Array = function(nodeBuffer) { + return new Uint8Array(nodeBuffer.buffer, nodeBuffer.byteOffset, nodeBuffer.byteLength / Uint8Array.BYTES_PER_ELEMENT); }; const decryptFile = function(content, encryption) { return new Promise(function(resolve, reject) { /* eslint-disable no-new */ // this is how you use it, its kind of bad but :shrug: - new AesDecrypter(toArrayBuffer(content), encryption.bytes, encryption.iv, function(err, bytes) { + new AesDecrypter(toUint8Array(content), encryption.bytes, encryption.iv, function(err, bytes) { if (err) { return reject(err); } - return resolve(new Buffer(bytes)); + return resolve(Buffer.from(bytes)); }); /* eslint-enable no-new */ }); @@ -68,7 +62,7 @@ const WriteData = function(decrypt, concurrency, resources) { operations.push(function() { return writeFile(r.file, r.content); }); - } else if (r.key && decrypt) { + } else if (r.uri && r.key && decrypt) { operations.push(function() { return requestFile(r.uri).then(function(content) { return decryptFile(content, r.key); @@ -76,7 +70,7 @@ const WriteData = function(decrypt, concurrency, resources) { return writeFile(r.file, content); }); }); - } else if (inProgress.indexOf(r.uri) === -1) { + } else if (r.uri && inProgress.indexOf(r.uri) === -1) { operations.push(function() { return requestFile(r.uri).then(function(content) { return writeFile(r.file, content); diff --git a/test/resources/dash.mpd b/test/resources/dash.mpd new file mode 100644 index 0000000..0e92f34 --- /dev/null +++ b/test/resources/dash.mpd @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/resources/sidx.mpd b/test/resources/sidx.mpd new file mode 100644 index 0000000..2092d57 --- /dev/null +++ b/test/resources/sidx.mpd @@ -0,0 +1,32 @@ + + + ./ + + + + German_Forest_SHORT_1v0-avc1.42c01e-68s-848x476-h264-1500000bps_seg.mp4 + + + + + + + + + German_Forest_Short_Poem_english-en-68s-2-lc-128000bps_seg.mp4 + + + + + + + + + German_Forest_Short_Poem_german-de-68s-2-lc-128000bps_seg.mp4 + + + + + + + diff --git a/test/unit/walk-manifest.spec.js b/test/unit/walk-manifest.spec.js index 6164d06..eeaab37 100644 --- a/test/unit/walk-manifest.spec.js +++ b/test/unit/walk-manifest.spec.js @@ -149,12 +149,10 @@ describe('walk-manifest', function() { assert.equal(setResources.size, 13); setResources.forEach(function(item) { - assert( - item.uri.includes('.ts') || + assert(item.uri.includes('.ts') || item.uri.includes('.m3u8') || item.uri.includes('.mp4') || - item.uri.includes('.m4s') - ); + item.uri.includes('.m4s')); }); @@ -628,5 +626,75 @@ describe('walk-manifest', function() { done(); }); }); + + it('should return segments and playlists for mpd', function() { + nock(TEST_URL) + .get('/dash.mpd') + .replyWithFile(200, `${process.cwd()}/test/resources/dash.mpd`); + + const options = {decrypt: false, basedir: '.', uri: TEST_URL + '/dash.mpd', requestRetryMaxAttempts: 0}; + + return walker(options) + .then(function(resources) { + // m3u8 and 13 segments + const setResources = new Set(resources); + const count = {mp4: 0, m4v: 0, m4a: 0, mpd: 0}; + + assert.equal(setResources.size, 37); + setResources.forEach(function(item) { + if (item.uri.includes('.mp4')) { + count.mp4 += 1; + } else if (item.uri.includes('.m4v')) { + count.m4v += 1; + } else if (item.uri.includes('.m4a')) { + count.m4a += 1; + } else if (item.uri.includes('.mpd')) { + count.mpd += 1; + } else { + assert(false, `items uri ${item.uri} was unexpected`); + return; + } + + assert(true, 'items uri was expected'); + }); + + assert.equal(count.mp4, 6, 'mp4 count as expected'); + assert.equal(count.mpd, 1, 'mpd count as expected'); + assert.equal(count.m4v, 25, 'm4v count as expected'); + assert.equal(count.m4a, 5, 'm4a count as expected'); + }); + }); + }); + + it('should return segments and playlists for mpd with sidx', function() { + nock(TEST_URL) + .get('/sidx.mpd') + .replyWithFile(200, `${process.cwd()}/test/resources/sidx.mpd`); + + const options = {decrypt: false, basedir: '.', uri: TEST_URL + '/sidx.mpd', requestRetryMaxAttempts: 0}; + + return walker(options) + .then(function(resources) { + // m3u8 and 13 segments + const setResources = new Set(resources); + const count = {mp4: 0, m4v: 0, m4a: 0, mpd: 0}; + + assert.equal(setResources.size, 7); + setResources.forEach(function(item) { + if (item.uri.includes('.mp4')) { + count.mp4 += 1; + } else if (item.uri.includes('.mpd')) { + count.mpd += 1; + } else { + assert(false, `items uri ${item.uri} was unexpected`); + return; + } + + assert(true, 'items uri was expected'); + }); + + assert.equal(count.mp4, 6, 'mp4 count as expected'); + assert.equal(count.mpd, 1, 'mpd count as expected'); + }); }); });