Merge branch 'dev/1.4.x' into dev/1.5.x
This commit is contained in:
commit
1390a94642
2
index.js
2
index.js
@ -21,7 +21,7 @@ const ChannelDB = require("./src/dao/channel-db");
|
||||
const M3uService = require("./src/services/m3u-service");
|
||||
const FillerDB = require("./src/dao/filler-db");
|
||||
const CustomShowDB = require("./src/dao/custom-show-db");
|
||||
const TVGuideService = require("./src/tv-guide-service");
|
||||
const TVGuideService = require("./src/services/tv-guide-service");
|
||||
const EventService = require("./src/services/event-service");
|
||||
const onShutdown = require("node-graceful-shutdown").onShutdown;
|
||||
|
||||
|
||||
189
package-lock.json
generated
189
package-lock.json
generated
@ -1104,12 +1104,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"JSONStream": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
|
||||
"integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
|
||||
"dev": true,
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.0.5.tgz",
|
||||
"integrity": "sha1-TePmwDnKp/8Cc9jIG5mRd50sZMk=",
|
||||
"requires": {
|
||||
"jsonparse": "^1.2.0",
|
||||
"jsonparse": "^1.1.0",
|
||||
"through": ">=2.2.7 <3"
|
||||
}
|
||||
},
|
||||
@ -1173,9 +1172,9 @@
|
||||
}
|
||||
},
|
||||
"angular": {
|
||||
"version": "1.7.9",
|
||||
"resolved": "https://registry.npmjs.org/angular/-/angular-1.7.9.tgz",
|
||||
"integrity": "sha512-5se7ZpcOtu0MBFlzGv5dsM1quQDoDeUTwZrWjGtTNA7O88cD8TEk5IEKCTDa3uECV9XnvKREVUr7du1ACiWGFQ=="
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.0.tgz",
|
||||
"integrity": "sha512-VdaMx+Qk0Skla7B5gw77a8hzlcOakwF8mjlW13DpIWIDlfqwAbSSLfd8N/qZnzEmQF4jC4iofInd3gE7vL8ZZg=="
|
||||
},
|
||||
"angular-router-browserify": {
|
||||
"version": "0.0.2",
|
||||
@ -1419,11 +1418,11 @@
|
||||
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
|
||||
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"requires": {
|
||||
"follow-redirects": "1.5.10"
|
||||
"follow-redirects": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-dynamic-import-node": {
|
||||
@ -1844,15 +1843,36 @@
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz",
|
||||
"integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==",
|
||||
"version": "4.16.6",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
|
||||
"integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001043",
|
||||
"electron-to-chromium": "^1.3.413",
|
||||
"node-releases": "^1.1.53",
|
||||
"pkg-up": "^2.0.0"
|
||||
"caniuse-lite": "^1.0.30001219",
|
||||
"colorette": "^1.2.2",
|
||||
"electron-to-chromium": "^1.3.723",
|
||||
"escalade": "^3.1.1",
|
||||
"node-releases": "^1.1.71"
|
||||
},
|
||||
"dependencies": {
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001230",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz",
|
||||
"integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==",
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.742",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.742.tgz",
|
||||
"integrity": "sha512-ihL14knI9FikJmH2XUIDdZFWJxvr14rPSdOhJ7PpS27xbz8qmaRwCwyg/bmFwjWKmWK9QyamiCZVCvXm5CH//Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.72",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz",
|
||||
"integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
@ -2010,12 +2030,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001046",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001046.tgz",
|
||||
"integrity": "sha512-CsGjBRYWG6FvgbyGy+hBbaezpwiqIOLkxQPY4A4Ea49g1eNsnQuESB+n4QM0BKii1j80MyJ26Ir5ywTQkbRE4g==",
|
||||
"dev": true
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
@ -2175,6 +2189,12 @@
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||
"dev": true
|
||||
},
|
||||
"colorette": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
|
||||
"integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
|
||||
"dev": true
|
||||
},
|
||||
"combine-source-map": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz",
|
||||
@ -3188,25 +3208,33 @@
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.415",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.415.tgz",
|
||||
"integrity": "sha512-GbtYqKffx3sU8G0HxwXuJFfs58Q7+iwLa5rBwaULwET6jWW8IAQSrVnu7vEfiUIcMVfbYyFg7cw3zdm+EbBJmw==",
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
|
||||
"integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
|
||||
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
"brorand": "^1.0.1",
|
||||
"bn.js": "^4.11.9",
|
||||
"brorand": "^1.1.0",
|
||||
"hash.js": "^1.0.0",
|
||||
"hmac-drbg": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
"hmac-drbg": "^1.0.1",
|
||||
"inherits": "^2.0.4",
|
||||
"minimalistic-assert": "^1.0.1",
|
||||
"minimalistic-crypto-utils": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
|
||||
"dev": true
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"emoji-regex": {
|
||||
@ -3258,6 +3286,12 @@
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-goat": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz",
|
||||
@ -3667,22 +3701,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
|
||||
"integrity": "sha1-e3qfmuov3/NnhqlP9kPtB/T/Xio=",
|
||||
"requires": {
|
||||
"debug": "=3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
|
||||
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg=="
|
||||
},
|
||||
"for-in": {
|
||||
"version": "1.0.2",
|
||||
@ -3950,12 +3971,6 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
@ -4583,9 +4598,9 @@
|
||||
}
|
||||
},
|
||||
"hosted-git-info": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
|
||||
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||
"dev": true
|
||||
},
|
||||
"htmlescape": {
|
||||
@ -4688,9 +4703,9 @@
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"dev": true
|
||||
},
|
||||
"inline-source-map": {
|
||||
@ -5073,8 +5088,7 @@
|
||||
"jsonparse": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
|
||||
"integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
|
||||
"dev": true
|
||||
"integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.1",
|
||||
@ -5155,9 +5169,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "3.0.4",
|
||||
@ -5742,12 +5756,6 @@
|
||||
"resolved": "https://registry.npmjs.org/node-graceful-shutdown/-/node-graceful-shutdown-1.1.0.tgz",
|
||||
"integrity": "sha512-g1tq/R8ie/At5xRHGfF+chTge1jVPxf1NEClLpZIPxOPi6PJ9II81T35ms1u+s4N/mqOCp60CFd+ps+DIWRigQ=="
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.53",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.53.tgz",
|
||||
"integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node-ssdp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-ssdp/-/node-ssdp-4.0.0.tgz",
|
||||
@ -6397,26 +6405,6 @@
|
||||
"pinkie": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"pkg-up": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
|
||||
"integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"find-up": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"find-up": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
|
||||
"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"posix-character-classes": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
||||
@ -7544,8 +7532,7 @@
|
||||
"through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
|
||||
"dev": true
|
||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
|
||||
},
|
||||
"through2": {
|
||||
"version": "2.0.5",
|
||||
|
||||
11
package.json
11
package.json
@ -13,16 +13,17 @@
|
||||
"package": "sh ./make_dist.sh",
|
||||
"clean": "del-cli --force ./bin ./dist ./.dizquetv ./web/public/bundle.js"
|
||||
},
|
||||
"author": "Dan Ferguson",
|
||||
"license": "ISC",
|
||||
"author": "vexorian",
|
||||
"license": "Zlib",
|
||||
"dependencies": {
|
||||
"JSONStream": "1.0.5",
|
||||
"angular": "^1.7.9",
|
||||
"angular": "^1.8.0",
|
||||
"angular-router-browserify": "0.0.2",
|
||||
"angular-vs-repeat": "2.0.13",
|
||||
"axios": "^0.19.2",
|
||||
"axios": "^0.21.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"diskdb": "^0.1.17",
|
||||
"merge" : "2.1.1",
|
||||
"diskdb": "0.1.17",
|
||||
"express": "^4.17.1",
|
||||
"express-fileupload": "^1.2.1",
|
||||
"node-graceful-shutdown": "1.1.0",
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
* [ ] I have read the code of conduct.
|
||||
* [ ] I am submitting to the correct base branch
|
||||
<!--
|
||||
* Bug fixes must go to `dev/1.2.x`.
|
||||
* New features must go to `dev/1.4.x`.
|
||||
* Bug fixes must go to `dev/1.4.x`.
|
||||
* New features must go to `dev/1.5.x`.
|
||||
-->
|
||||
### Changes that modify the db structure
|
||||
|
||||
|
||||
38
src/api.js
38
src/api.js
@ -12,6 +12,7 @@ const Plex = require("./plex.js");
|
||||
|
||||
const timeSlotsService = require('./services/time-slots-service');
|
||||
const randomSlotsService = require('./services/random-slots-service');
|
||||
const throttle = require('./services/throttle');
|
||||
|
||||
function safeString(object) {
|
||||
let o = object;
|
||||
@ -280,10 +281,10 @@ function api(db, channelDB, fillerDB, customShowDB, xmltvInterval, guideService
|
||||
return res.status(404).send("Channel doesn't have programs?");
|
||||
}
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application.json'
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
let transformStream = JSONStream.stringify(); //false makes it not add 'separators'
|
||||
let transformStream = JSONStream.stringify();
|
||||
transformStream.pipe(res);
|
||||
|
||||
for (let i = 0; i < programs.length; i++) {
|
||||
@ -1002,7 +1003,7 @@ function api(db, channelDB, fillerDB, customShowDB, xmltvInterval, guideService
|
||||
console.error("time slots error: " + toolRes.userError);
|
||||
return res.status(400).send(toolRes.userError);
|
||||
}
|
||||
res.status(200).send(toolRes);
|
||||
await streamToolResult(toolRes, res);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
res.status(500).send("Internal error");
|
||||
@ -1016,7 +1017,7 @@ function api(db, channelDB, fillerDB, customShowDB, xmltvInterval, guideService
|
||||
console.error("random slots error: " + toolRes.userError);
|
||||
return res.status(400).send(toolRes.userError);
|
||||
}
|
||||
res.status(200).send(toolRes);
|
||||
await streamToolResult(toolRes, res);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
res.status(500).send("Internal error");
|
||||
@ -1068,13 +1069,32 @@ function api(db, channelDB, fillerDB, customShowDB, xmltvInterval, guideService
|
||||
channel.fallback.forEach( cleanUpProgram );
|
||||
}
|
||||
|
||||
async function streamToolResult(toolRes, res) {
|
||||
let programs = toolRes.programs;
|
||||
delete toolRes.programs;
|
||||
let s = JSON.stringify(toolRes);
|
||||
s = s.slice(0, -1);
|
||||
console.log( JSON.stringify(toolRes));
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
let transformStream = JSONStream.stringify(
|
||||
s + ',"programs":[',
|
||||
',' ,
|
||||
']}');
|
||||
transformStream.pipe(res);
|
||||
|
||||
for (let i = 0; i < programs.length; i++) {
|
||||
transformStream.write( programs[i] );
|
||||
await throttle();
|
||||
}
|
||||
transformStream.end();
|
||||
}
|
||||
|
||||
|
||||
return router
|
||||
}
|
||||
|
||||
|
||||
async function throttle() {
|
||||
return new Promise((resolve) => {
|
||||
setImmediate(() => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
@ -56,6 +56,7 @@ function createLineup(obj, channel, fillers, isFirst) {
|
||||
// When within 30 seconds of start time, just make the time 0 to smooth things out
|
||||
// Helps prevents loosing first few seconds of an episode upon lineup change
|
||||
let activeProgram = obj.program
|
||||
let beginningOffset = 0;
|
||||
|
||||
let lineup = []
|
||||
|
||||
@ -67,7 +68,8 @@ function createLineup(obj, channel, fillers, isFirst) {
|
||||
err: activeProgram.err,
|
||||
streamDuration: remaining,
|
||||
duration: remaining,
|
||||
start: 0
|
||||
start: 0,
|
||||
beginningOffset: beginningOffset,
|
||||
})
|
||||
return lineup;
|
||||
}
|
||||
@ -120,6 +122,7 @@ function createLineup(obj, channel, fillers, isFirst) {
|
||||
streamDuration: Math.max(1, Math.min(filler.duration - fillerstart, remaining) ),
|
||||
duration: filler.duration,
|
||||
fillerId: filler.fillerId,
|
||||
beginningOffset: beginningOffset,
|
||||
serverKey: filler.serverKey
|
||||
});
|
||||
return lineup;
|
||||
@ -133,14 +136,17 @@ function createLineup(obj, channel, fillers, isFirst) {
|
||||
type: 'offline',
|
||||
title: 'Channel Offline',
|
||||
streamDuration: remaining,
|
||||
beginningOffset: beginningOffset,
|
||||
duration: remaining,
|
||||
start: 0
|
||||
})
|
||||
return lineup;
|
||||
}
|
||||
let originalTimeElapsed = timeElapsed;
|
||||
if (timeElapsed < 30000) {
|
||||
timeElapsed = 0
|
||||
}
|
||||
beginningOffset = Math.max(0, originalTimeElapsed - timeElapsed);
|
||||
|
||||
return [ {
|
||||
type: 'program',
|
||||
@ -151,6 +157,7 @@ function createLineup(obj, channel, fillers, isFirst) {
|
||||
ratingKey: activeProgram.ratingKey,
|
||||
start: timeElapsed,
|
||||
streamDuration: activeProgram.duration - timeElapsed,
|
||||
beginningOffset: beginningOffset,
|
||||
duration: activeProgram.duration,
|
||||
serverKey: activeProgram.serverKey
|
||||
} ];
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
const constants = require("../constants");
|
||||
const getShowData = require("./get-show-data")();
|
||||
const random = require('../helperFuncs').random;
|
||||
const throttle = require('./throttle');
|
||||
|
||||
|
||||
const MINUTE = 60*1000;
|
||||
const DAY = 24*60*MINUTE;
|
||||
@ -192,12 +194,6 @@ module.exports = async( programs, schedule ) => {
|
||||
|
||||
// throttle so that the stream is not affected negatively
|
||||
let steps = 0;
|
||||
let throttle = async() => {
|
||||
if (steps++ == 10) {
|
||||
steps = 0;
|
||||
await _wait(1);
|
||||
}
|
||||
}
|
||||
|
||||
let showsById = {};
|
||||
let shows = [];
|
||||
|
||||
6
src/services/throttle.js
Normal file
6
src/services/throttle.js
Normal file
@ -0,0 +1,6 @@
|
||||
//Adds a slight pause so that long operations
|
||||
module.exports = function() {
|
||||
return new Promise((resolve) => {
|
||||
setImmediate(() => resolve());
|
||||
});
|
||||
}
|
||||
@ -3,6 +3,7 @@ const constants = require("../constants");
|
||||
|
||||
const getShowData = require("./get-show-data")();
|
||||
const random = require('../helperFuncs').random;
|
||||
const throttle = require('./throttle');
|
||||
|
||||
const MINUTE = 60*1000;
|
||||
const DAY = 24*60*MINUTE;
|
||||
@ -200,12 +201,6 @@ module.exports = async( programs, schedule ) => {
|
||||
|
||||
// throttle so that the stream is not affected negatively
|
||||
let steps = 0;
|
||||
let throttle = async() => {
|
||||
if (steps++ == 10) {
|
||||
steps = 0;
|
||||
await _wait(1);
|
||||
}
|
||||
}
|
||||
|
||||
let showsById = {};
|
||||
let shows = [];
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
|
||||
const constants = require("./constants");
|
||||
const constants = require("../constants");
|
||||
const FALLBACK_ICON = "https://raw.githubusercontent.com/vexorain/dizquetv/main/resources/dizquetv.png";
|
||||
const throttle = require('./throttle');
|
||||
|
||||
class TVGuideService
|
||||
{
|
||||
@ -18,6 +19,7 @@ class TVGuideService
|
||||
this.db = db;
|
||||
this.cacheImageService = cacheImageService;
|
||||
this.eventService = eventService;
|
||||
this._throttle = throttle;
|
||||
}
|
||||
|
||||
async get() {
|
||||
@ -355,11 +357,6 @@ class TVGuideService
|
||||
}
|
||||
}
|
||||
|
||||
_throttle() {
|
||||
return new Promise((resolve) => {
|
||||
setImmediate(() => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
async refreshXML() {
|
||||
let xmltvSettings = this.db['xmltv-settings'].find()[0];
|
||||
@ -254,15 +254,19 @@ function video( channelDB , fillerDB, db) {
|
||||
}
|
||||
let fillers = await fillerDB.getFillersFromChannel(brandChannel);
|
||||
let lineup = helperFuncs.createLineup(prog, brandChannel, fillers, isFirst)
|
||||
lineupItem = lineup.shift()
|
||||
lineupItem = lineup.shift();
|
||||
}
|
||||
|
||||
if ( !isLoading && (lineupItem != null) ) {
|
||||
let upperBound = 1000000000;
|
||||
let beginningOffset = 0;
|
||||
if (typeof(lineupItem.beginningOffset) !== 'undefined') {
|
||||
beginningOffset = lineupItem.beginningOffset;
|
||||
}
|
||||
//adjust upper bounds and record playbacks
|
||||
for (let i = redirectChannels.length-1; i >= 0; i--) {
|
||||
lineupItem = JSON.parse( JSON.stringify(lineupItem ));
|
||||
let u = upperBounds[i];
|
||||
let u = upperBounds[i] + beginningOffset;
|
||||
if (typeof(u) !== 'undefined') {
|
||||
let u2 = upperBound;
|
||||
if ( typeof(lineupItem.streamDuration) !== 'undefined') {
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
<body ng-app="myApp" style="min-width: 340px;">
|
||||
<div class="container-fluid">
|
||||
<h1>
|
||||
<a href="#!/guide"><img id='dizquetv-logo' src="images/dizquetv.png" alt="logo" /></a>
|
||||
<a href="#!/guide"><img id='dizquetv-logo' src="images/dizquetv.png" alt="logo" ></a>
|
||||
dizqueTV
|
||||
<small class="pull-right" style="padding: 5px;">
|
||||
<a href="https://github.com/vexorian/dizquetv" title='Git Repository'>
|
||||
@ -42,7 +42,7 @@
|
||||
<a href="/api/channels.m3u">M3U <span class="far fa-file-video"></span></a>
|
||||
</span>
|
||||
</span>
|
||||
<hr/>
|
||||
<hr></hr>
|
||||
<div ng-view></div>
|
||||
<toast-notifications></toast-notifications>
|
||||
</div>
|
||||
|
||||
@ -24,21 +24,21 @@
|
||||
|
||||
<div class='form-group'>
|
||||
<label class='form-label' >Channel Number:</label>
|
||||
<input type="text" class='form-control' type='number' ng-model="channel.number" id='channelNumber' aria-describedby="channelNumberHelp" />
|
||||
<input type="text" class='form-control' type='number' ng-model="channel.number" id='channelNumber' aria-describedby="channelNumberHelp"></input>
|
||||
|
||||
<small id='channelNumberHelp' class="text-danger" for='channelNumber'>{{error.number}}</small>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label class='form-label' >Channel Name:</label>
|
||||
<input type="text" class='form-control' ng-model="channel.name" id='channelName' aria-describedby="channelNameHelp" />
|
||||
<input type="text" class='form-control' ng-model="channel.name" id='channelName' aria-describedby="channelNameHelp"></input>
|
||||
|
||||
<small id='channelNumberHelp' class="text-danger" for='channelNumber'>{{error.name}}</small>
|
||||
</div>
|
||||
|
||||
<div class='form-group'>
|
||||
<label class='form-label' >Channel Group:</label>
|
||||
<input type="text" class='form-control' ng-model="channel.groupTitle" id='groupTitle' placeholder="dizqueTV" aria-describedby="groupTitleHelp" />
|
||||
<input type="text" class='form-control' ng-model="channel.groupTitle" id='groupTitle' placeholder="dizqueTV" aria-describedby="groupTitleHelp"></input>
|
||||
|
||||
<small id='groupTitleHelp' class="text-muted" for='channelNumber'>This is used by iptv clients to categorize the channels. You can leave it as dizqueTV if you don't need this sort of classification.</small>
|
||||
</div>
|
||||
@ -48,7 +48,7 @@
|
||||
<label for="channelIcon" class="small">Channel Icon</label>
|
||||
|
||||
<div class="input-group mb-1">
|
||||
<input name="channelIcon" id="channelIcon" class="form-control form-control-sm" type="url" ng-model="channel.icon" />
|
||||
<input name="channelIcon" id="channelIcon" class="form-control form-control-sm" type="url" ng-model="channel.icon"></input>
|
||||
<div class="input-group-append">
|
||||
<input type="file"
|
||||
accept="image/*"
|
||||
@ -60,10 +60,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<br></br>
|
||||
<div>
|
||||
<h6>Preview</h6>
|
||||
<img ng-if="channel.icon !== ''" ng-src="{{channel.icon}}" alt="{{channel.name}}" style="max-height: 120px;"/>
|
||||
<img ng-if="channel.icon !== ''" ng-src="{{channel.icon}}" alt="{{channel.name}}" style="max-height: 120px;"></img>
|
||||
<span ng-if="channel.icon === ''">{{channel.name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -75,12 +75,12 @@
|
||||
<div class='form-group row' >
|
||||
<label for="channelStartTime" class="small col-form-label col-md-auto">Programming Start:</label>
|
||||
<div class='col-md-auto'>
|
||||
<input id="channelStartTime" class="form-control form-control-sm col-md-auto" type="datetime-local" ng-model="channel.startTime" aria-describedby="startTimeHelp" />
|
||||
<input id="channelStartTime" class="form-control form-control-sm col-md-auto" type="datetime-local" ng-model="channel.startTime" aria-describedby="startTimeHelp"></input>
|
||||
<small class="text-danger" id='startTimeHelp'>{{error.startTime}}</small>
|
||||
</div>
|
||||
<label for="channelEndTime" class="small col-form-label col-md-auto">Programming End:</label>
|
||||
<div class='col-md-auto'>
|
||||
<input id="channelEndTime" class="form-control form-control-sm col-md-auto" type="datetime-local" ng-model="endTime" ng-disabled="true" aria-describedby="endTimeHelp" />
|
||||
<input id="channelEndTime" class="form-control form-control-sm col-md-auto" type="datetime-local" ng-model="endTime" ng-disabled="true" aria-describedby="endTimeHelp"></input>
|
||||
</div>
|
||||
<div class='col-md-auto'>
|
||||
<small class="text-muted form-text" id='endTimeHelp'>Programming will restart from the beginning.</small>
|
||||
@ -99,7 +99,7 @@
|
||||
<div class='programming-counter' ng-show='hasFlex' style='order:4'>
|
||||
<span class="small"><b>Fallback:</b> {{describeFallback()}}</span>
|
||||
</div>
|
||||
<div class='flex-pull-right' ng-style="{order: (reverseTools?3:4) }" />
|
||||
<div class='flex-pull-right' ng-style="{order: (reverseTools?3:4) }"></div>
|
||||
|
||||
|
||||
<div class="btn-group-toggle" data-toggle="buttons" ng-show='showShuffleOptions' ng-style="{order: (reverseTools?2:4) }"
|
||||
@ -178,7 +178,7 @@
|
||||
<div class="program-start">
|
||||
{{ dateForGuide(x.start) }}
|
||||
</div>
|
||||
<div ng-style="programSquareStyle(x)" />
|
||||
<div ng-style="programSquareStyle(x)"></div>
|
||||
|
||||
<div ng-hidden="x.isOffline" class='title' >
|
||||
{{ getProgramDisplayTitle(x) }}
|
||||
@ -318,9 +318,9 @@
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<select class="custom-select" ng-model="nightStart"
|
||||
ng-options="o.id as o.description for o in nightStartHours" />
|
||||
ng-options="o.id as o.description for o in nightStartHours" ></select>
|
||||
<select class="custom-select" ng-model="nightEnd"
|
||||
ng-options="o.id as o.description for o in nightEndHours" />
|
||||
ng-options="o.id as o.description for o in nightEndHours" ></select>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-warning form-control form-control-sm" type="button" ng-click="nightChannel(nightStart, nightEnd)" ng-disabled="nightStart==-1 || nightEnd==-1" title='Restrict Hours' >
|
||||
|
||||
@ -334,7 +334,7 @@
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<select class="custom-select" ng-model="paddingOption"
|
||||
ng-options="o as o.description for o in paddingOptions" />
|
||||
ng-options="o as o.description for o in paddingOptions" ></select>
|
||||
|
||||
</div>
|
||||
<button ng-disabled="disablePadding()" class="btn btn-sm btn-warning form-control form-control-sm" type="button" ng-click="padTimes(paddingOption.id, paddingOption.allow5)" title='Pad Times' >
|
||||
@ -348,11 +348,11 @@
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<select class="custom-select" style="width:5em" ng-model="breakAfter"
|
||||
ng-options="o.id as o.description for o in breakAfterOptions" />
|
||||
ng-options="o.id as o.description for o in breakAfterOptions" ></select>
|
||||
<select class="custom-select" style="width:5em" ng-model="minBreakSize"
|
||||
ng-options="o.id as o.description for o in minBreakSizeOptions" />
|
||||
ng-options="o.id as o.description for o in minBreakSizeOptions" ></select>
|
||||
<select class="custom-select" style="width:5em" ng-model="maxBreakSize"
|
||||
ng-options="o.id as o.description for o in maxBreakSizeOptions" />
|
||||
ng-options="o.id as o.description for o in maxBreakSizeOptions" ></select>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-warning form-control form-control-sm" type="button" ng-click="addBreaks(breakAfter, minBreakSize, maxBreakSize)" ng-disabled="breaksDisabled()" title='Add Breaks' >
|
||||
<i class='fa fa-coffee'></i> Add Breaks
|
||||
@ -553,18 +553,18 @@
|
||||
|
||||
<div class='row' ng-show="channel.offlineMode == 'pic'" >
|
||||
<div class="col-md-3">
|
||||
<img ng-src="{{channel.offlinePicture}}" alt="Fallback preview" style="max-height: 120px;"/>
|
||||
<img ng-src="{{channel.offlinePicture}}" alt="Fallback preview" style="max-height: 120px;"></img>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div>
|
||||
<label for="offlinePicture" class="small">
|
||||
Picture: <span class="text-danger pull-right">{{error.picture}}</span></label>
|
||||
<input name="offlinePicture" id="offlinePicture" class="form-control form-control-sm" type="url" ng-model="channel.offlinePicture" />
|
||||
<input name="offlinePicture" id="offlinePicture" class="form-control form-control-sm" type="url" ng-model="channel.offlinePicture"></input>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="offlineSound" class="small">Sound Track:<span class="text-danger pull-right">{{error.sound}}</span></label>
|
||||
<input name="offlineSound" id="offlineSound" class="form-control form-control-sm" type="url" ng-model="channel.offlineSoundtrack" placeholder="URL to a sound track that will loop during the offline screen, leave empty for silence." />
|
||||
<input name="offlineSound" id="offlineSound" class="form-control form-control-sm" type="url" ng-model="channel.offlineSoundtrack" placeholder="URL to a sound track that will loop during the offline screen, leave empty for silence."></input>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -580,7 +580,7 @@
|
||||
<div class="program-start" >
|
||||
{{durationString(x.duration)}}
|
||||
</div>
|
||||
<div ng-style="programSquareStyle(x, true)" />
|
||||
<div ng-style="programSquareStyle(x, true)"></div>
|
||||
<div style="margin-right: 5px;">
|
||||
<strong>Fallback:</strong> {{x.title}}
|
||||
</div>
|
||||
@ -594,14 +594,14 @@
|
||||
<div ng-show="channel.fallback.length === 0">
|
||||
<button class="btn btn-sm btn-warning form-control form-control-sm" type="button" ng-click="openFallbackLibrary()">Pick fallback</button>
|
||||
</div>
|
||||
<hr style='margin-top:0' />
|
||||
<hr style='margin-top:0'></hr>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h5 style="margin-top: 10px;">Filler</h5>
|
||||
<div>
|
||||
<label>Minimum time before replaying a filler (Minutes): </label>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="channel.fillerRepeatCooldownMinutes" ng-pattern="/^([1-9][0-9]*)$/" min='0' max='10080' />
|
||||
<input type="number" class="form-control form-control-sm" ng-model="channel.fillerRepeatCooldownMinutes" ng-pattern="/^([1-9][0-9]*)$/" min='0' max='10080'></input>
|
||||
|
||||
<span class="text-danger pull-right">{{error.blockRepeats}}</span>
|
||||
</div>
|
||||
@ -609,10 +609,10 @@
|
||||
<input id="overlayDiableIcon" type="checkbox" ng-model="channel.disableFillerOverlay">
|
||||
<label class="small" for="overlayDisableIcon" style="margin-bottom: 4px;"> Disable channel watermark when playing filler </label>
|
||||
</div>
|
||||
<hr />
|
||||
<hr></hr>
|
||||
<h6>Filler Lists</h6>
|
||||
<div id='fillerContainer'>
|
||||
<br />
|
||||
<br></br>
|
||||
|
||||
<div class="form-row" ng-repeat = "x in channel.fillerCollections" track-by = "$index">
|
||||
<div class='form-group col-md-5'>
|
||||
@ -681,14 +681,14 @@
|
||||
<br></br>
|
||||
<div class='form-group' ng-show='! channel.stealth'>
|
||||
<label class='form-label' >Placeholder program title:</label>
|
||||
<input type="text" class='form-control' ng-model="channel.guideFlexPlaceholder" placeholder="Leave empty so that it uses the channel's name" id='guideFlex' aria-describedby="guideFlexHelp" />
|
||||
<input type="text" class='form-control' ng-model="channel.guideFlexPlaceholder" placeholder="Leave empty so that it uses the channel's name" id='guideFlex' aria-describedby="guideFlexHelp"></input>
|
||||
|
||||
<small id='guideFlexHelp' class="text-muted" for='guideFlex'>This is the name of the fake program that will appear in the TV guide when there are no programs to display in that time slot guide. E.g when a large Flex block is scheduled.</small>
|
||||
</div>
|
||||
|
||||
<div class='form-group' ng-show='! channel.stealth'>
|
||||
<label class='form-label'>Minimum program duration to appear in the TV guide (seconds): </label>
|
||||
<input type="number" class="form-control" ng-model="channel.guideMinimumDurationSeconds" ng-pattern="/^([0-9][0-9]*)$/" min='0' max='36288000' id='guideFlexTime' aria-describedby="guideFlexTimeHelp" />
|
||||
<input type="number" class="form-control" ng-model="channel.guideMinimumDurationSeconds" ng-pattern="/^([0-9][0-9]*)$/" min='0' max='36288000' id='guideFlexTime' aria-describedby="guideFlexTimeHelp"></input>
|
||||
<small id='guideFlexTimeHelp' class="text-muted" for='guideFlexTime'>Programs shorter than this value will be treated the same as Flex time. Meaning that the TV Guide will try to meld them with the previous program or display the block of programs as the "place holder program" if they make a large continuous group. Use 0 to disable this feature or use a large value to make the channel report only the placeholder program and not the real programming.</small>
|
||||
</div>
|
||||
</div>
|
||||
@ -698,7 +698,7 @@
|
||||
|
||||
<div class="modal-body" ng-if="tab == 'ffmpeg'">
|
||||
<small class='text-info'>These features require ffmpeg transcoding to be enabled in FFmpeg settings</small>
|
||||
<hr />
|
||||
<hr></hr>
|
||||
<h6>Channel Watermark</h6>
|
||||
|
||||
<div class='form-check'>
|
||||
@ -716,7 +716,7 @@
|
||||
<div ng-style='getWatermarkPreviewOuter()' class='watermark-preview'>
|
||||
<div ng-style='getWatermarkPreviewRectangle(4,3)' class='alternate-aspect' ></div>
|
||||
<div ng-style='getWatermarkPreviewRectangle(16,9)' class='alternate-aspect' ></div>
|
||||
<img src='{{ getWatermarkSrc() }}' ng-style='getWatermarkPreviewInner()' />
|
||||
<img src='{{ getWatermarkSrc() }}' ng-style='getWatermarkPreviewInner()'></img>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -804,7 +804,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<hr></hr>
|
||||
<h6>Transcoding settings</h6>
|
||||
|
||||
|
||||
|
||||
@ -9,16 +9,16 @@
|
||||
</button>
|
||||
</h5>
|
||||
<h6>FFMPEG Executable Path (eg: C:\ffmpeg\bin\ffmpeg.exe || /usr/bin/ffmpeg)</h6>
|
||||
<input type="text" class="form-control form-control-sm" ria-describedby="ffmpegHelp" ng-model="settings.ffmpegPath"/>
|
||||
<input type="text" class="form-control form-control-sm" ria-describedby="ffmpegHelp" ng-model="settings.ffmpegPath"></input>
|
||||
<small id="ffmpegHelp" class="form-text text-muted">FFMPEG version 4.2+ required. Check by opening the version tab</small>
|
||||
<hr/>
|
||||
<hr></hr>
|
||||
<h6>Miscellaneous Options</h6>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<label>Threads</label>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.threads"/>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.threads"></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -26,7 +26,7 @@
|
||||
<div class="form-group">
|
||||
<label>Logging</label>
|
||||
<br>
|
||||
<input id="logFfmpeg" type="checkbox" ng-model="settings.logFfmpeg"/>
|
||||
<input id="logFfmpeg" type="checkbox" ng-model="settings.logFfmpeg"></input>
|
||||
<label for="logFfmpeg">Log FFMPEG to console</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -36,35 +36,35 @@
|
||||
<div class="form-group">
|
||||
<label>Video Buffer</label>
|
||||
<select ng-model="settings.concatMuxDelay" ria-describedby="concatMuxDelayHelp"
|
||||
ng-options="o.id as o.description for o in muxDelayOptions" />
|
||||
ng-options="o.id as o.description for o in muxDelayOptions" ></select>
|
||||
<small id="concatMuxDelayHelp" class="form-text text-muted">Note: If you experience playback issues upon stream start, try increasing this.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<hr></hr>
|
||||
<h6>Transcoding Features</h6>
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
<input id=enableFFMPEGTranscoding" type="checkbox" ng-model="settings.enableFFMPEGTranscoding" />
|
||||
<input id=enableFFMPEGTranscoding" type="checkbox" ng-model="settings.enableFFMPEGTranscoding" ></input>
|
||||
<label for="enableFFMPEGTranscoding">Enable FFMPEG Transcoding</label>
|
||||
<small class="form-text text-muted">Transcoding is required for some features like channel overlay and measures to prevent issues when switching episodes. The trade-off is quality loss and additional computing resource requirements.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br ></br>
|
||||
|
||||
<div class="form-group" ng-hide="isTranscodingNotNeeded()" >
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
<label>Preferred Resolution</label>
|
||||
<select ng-model="settings.targetResolution" ria-describedby="concatMuxDelayHelp" ng-options="o.id as o.description for o in resolutionOptions" />
|
||||
<select ng-model="settings.targetResolution" ria-describedby="concatMuxDelayHelp" ng-options="o.id as o.description for o in resolutionOptions" ></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<label>Video Encoder</label>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.videoEncoder" ria-describedby="videoEncoderHelp"/>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.videoEncoder" ria-describedby="videoEncoderHelp"></input>
|
||||
<small id="videoEncoderHelp" class="form-text text-muted">Some possible values are:</small>
|
||||
<small id="videoEncoderHelp" class="form-text text-muted">h264 with Intel Quick Sync: h264_qsv</small>
|
||||
<small id="videoEncoderHelp" class="form-text text-muted">MPEG2 with Intel Quick Sync: mpeg2_qsv</small>
|
||||
@ -73,10 +73,10 @@
|
||||
<small id="videoEncoderHelp" class="form-text text-muted">H264: libx264</small>
|
||||
<small id="videoEncoderHelp" class="form-text text-muted">MacOS: h264_videotoolbox</small>
|
||||
</div>
|
||||
<div class="col-sm-1" />
|
||||
<div class="col-sm-1" ></div>
|
||||
<div class="col-sm-4">
|
||||
<label>Audio Encoder</label>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.audioEncoder" ria-describedby="audioEncoderHelp"/>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.audioEncoder" ria-describedby="audioEncoderHelp"></input>
|
||||
<small id="audioEncoderHelp" class="form-text text-muted">Some possible values are:</small>
|
||||
<small id="audioEncoderHelp" class="form-text text-muted">aac</small>
|
||||
<small id="audioEncoderHelp" class="form-text text-muted">ac3 (default), ac3_fixed</small>
|
||||
@ -84,26 +84,26 @@
|
||||
<small id="audioEncoderHelp" class="form-text text-muted">libmp3lame</small>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br ></br>
|
||||
<div class="form-group">
|
||||
<label>Video Bitrate (k)</label>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.videoBitrate"/>
|
||||
<br />
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.videoBitrate"></input>
|
||||
<br ></br>
|
||||
<label>Video Buffer Size (k)</label>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.videoBufSize"/>
|
||||
<br />
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.videoBufSize"></input>
|
||||
<br ></br>
|
||||
<label>Max Frame Rate</label>
|
||||
<select class='form-control custom-select' ng-model="settings.maxFPS" ria-describedby="fpsHelp"
|
||||
ng-options="o.id as o.description for o in fpsOptions" ></select>
|
||||
<small id='fpsHelp' class='form-text text-muted'>Will transcode videos that have FPS higher than this.</small>
|
||||
|
||||
<br />
|
||||
<br ></br>
|
||||
<label>Scaling Algorithm</label>
|
||||
<select class='form-control custom-select' ng-model="settings.scalingAlgorithm" ria-describedby="scalingHelp"
|
||||
ng-options="o.id as o.description for o in scalingOptions" ></select>
|
||||
<small id='scalingHelp' class='form-text text-muted'>Scaling algorithm to use when the transcoder needs to change the video size.</small>
|
||||
|
||||
<br />
|
||||
<br ></br>
|
||||
<label>Deinterlace Filter</label>
|
||||
<select class='form-control custom-select' ng-model="settings.deinterlaceFilter" ria-describedby="deinterlaceHelp"
|
||||
ng-options="o.value as o.description for o in deinterlaceOptions" ></select>
|
||||
@ -112,20 +112,20 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label>Audio Bitrate (k)</label>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.audioBitrate"/>
|
||||
<br />
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.audioBitrate"></input>
|
||||
<br ></br>
|
||||
<label>Audio Buffer Size (k)</label>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.audioBufSize"/>
|
||||
<br />
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.audioBufSize"></input>
|
||||
<br ></br>
|
||||
<label>Audio Volume (%)</label>
|
||||
<input type="number" ria-describedby="volumeHelp" class="form-control form-control-sm" ng-model="settings.audioVolumePercent"/>
|
||||
<input type="number" ria-describedby="volumeHelp" class="form-control form-control-sm" ng-model="settings.audioVolumePercent"></input>
|
||||
<small id="volumeHelp" class="form-text text-muted">Values higher than 100 will boost the audio.</small>
|
||||
<br />
|
||||
<br ></br>
|
||||
<label>Audio Channels</label>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.audioChannels"/>
|
||||
<br />
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.audioChannels"></input>
|
||||
<br ></br>
|
||||
<label>Audio Sample Rate (k)</label>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.audioSampleRate"/>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.audioSampleRate"></input>
|
||||
</div>
|
||||
|
||||
|
||||
@ -133,10 +133,10 @@
|
||||
<div>
|
||||
<label>Error Screen:</label>
|
||||
<select ng-model="settings.errorScreen" ria-describedby="errorHelp"
|
||||
ng-options="o.value as o.description for o in errorScreens" />
|
||||
ng-options="o.value as o.description for o in errorScreens" ></select>
|
||||
<label>Audio:</label>
|
||||
<select ng-model="settings.errorAudio" ria-describedby="errorHelp"
|
||||
ng-options="o.value as o.description for o in errorAudios" />
|
||||
ng-options="o.value as o.description for o in errorAudios" ></select>
|
||||
</div>
|
||||
|
||||
<small id="errorHelp" class="form-text text-muted">If there are issues playing a video, dizqueTV will try to use an error screen as a placeholder while retrying loading the video every 60 seconds.</small>
|
||||
@ -146,7 +146,7 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
<div class="form-group">
|
||||
<input id="enableNormalizeResolution" type="checkbox" ng-model="settings.normalizeResolution" ng-disabled="isTranscodingNotNeeded()" />
|
||||
<input id="enableNormalizeResolution" type="checkbox" ng-model="settings.normalizeResolution" ng-disabled="isTranscodingNotNeeded()" ></input>
|
||||
<label for="enableNormalizeResolution">Normalize Resolution</label>
|
||||
<small class="form-text text-muted">Some clients experience issues when the video stream changes resolution. This option will make dizqueTV convert all videos to the preferred resolution selected above. Otherwise, the preferred resolution will be used as a maximum resolution for transcoding.
|
||||
</small>
|
||||
@ -154,16 +154,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<br ></br>
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-4">
|
||||
<input id="enableNormalizeVideoCodec" type="checkbox" ng-model="settings.normalizeVideoCodec" ng-disabled="isTranscodingNotNeeded()" />
|
||||
<input id="enableNormalizeVideoCodec" type="checkbox" ng-model="settings.normalizeVideoCodec" ng-disabled="isTranscodingNotNeeded()" ></input>
|
||||
<label for="enableNormalizeVideoCodec">Normalize Video Codec</label>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<input id="enableNormalizeAudioCodec" type="checkbox" ng-model="settings.normalizeAudioCodec" ng-disabled="isTranscodingNotNeeded()" />
|
||||
<input id="enableNormalizeAudioCodec" type="checkbox" ng-model="settings.normalizeAudioCodec" ng-disabled="isTranscodingNotNeeded()" ></input>
|
||||
<label for="enableNormalizeAudioCodec">Normalize Audio Codec</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -172,11 +172,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<br ></br>
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
<div class="form-group">
|
||||
<input id="enableAlignAudio" type="checkbox" ng-model="settings.normalizeAudio" ng-disabled="isTranscodingNotNeeded()" />
|
||||
<input id="enableAlignAudio" type="checkbox" ng-model="settings.normalizeAudio" ng-disabled="isTranscodingNotNeeded()" ></input>
|
||||
<label for="enableAlignAudio">Normalize Audio</label>
|
||||
<small class="form-text text-muted">This will force the preferred number of audio channels and sample rate, in addition it will align the lengths of the audio and video channels. This will prevent audio-related episode transition issues in many clients. Audio will always be transcoded.
|
||||
</small>
|
||||
@ -184,11 +184,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<br ></br>
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
<div class="form-group">
|
||||
<input id="disableOverlay" type="checkbox" ng-model="settings.disableChannelOverlay" ng-disabled="isTranscodingNotNeeded()" />
|
||||
<input id="disableOverlay" type="checkbox" ng-model="settings.disableChannelOverlay" ng-disabled="isTranscodingNotNeeded()" ></input>
|
||||
<label for="disableOverlay">Disable Channel Watermark Globally</label>
|
||||
<small class="form-text text-muted">Toggling this option will disable channel watermarks regardless of channel settings.
|
||||
</small>
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<div class="programming-counter small" ng-show="content.length > 0">
|
||||
<span class="small"><b>Total:</b> {{content.length}}</span>
|
||||
</div>
|
||||
<div class='flex-pull-right' />
|
||||
<div class='flex-pull-right' ></div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-secondary btn-programming-tools"
|
||||
ng-click="showTools = !showTools"
|
||||
@ -77,7 +77,7 @@
|
||||
<div class="program-start" >
|
||||
{{durationString(x.duration)}}
|
||||
</div>
|
||||
<div ng-style="programSquareStyle(x, false)" />
|
||||
<div ng-style="programSquareStyle(x, false)" ></div>
|
||||
<div class="title" >
|
||||
{{x.title}}
|
||||
</div>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
<div class="modal-body container">
|
||||
<label>Duration (Seconds): <span class="text-danger pull-right">{{error.duration}}</span></label>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="program.durationSeconds" ng-pattern="/^([1-9][0-9]*)$/"/>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="program.durationSeconds" ng-pattern="/^([1-9][0-9]*)$/"></input>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
Reset Options
|
||||
</button>
|
||||
</h5>
|
||||
<br/>
|
||||
<br></br>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<h6>Auto-Discovery</h6>
|
||||
@ -21,7 +21,7 @@
|
||||
<h6>Tuner Count
|
||||
<span class="pull-right text-danger">{{error.tunerCount}}</span>
|
||||
</h6>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.tunerCount"/>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.tunerCount"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -6,16 +6,16 @@
|
||||
<h5 class="modal-title">Library</h5>
|
||||
</div>
|
||||
<div class="model-body">
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br></br>
|
||||
<br></br>
|
||||
<br></br>
|
||||
<br></br>
|
||||
<p class="text-center">Configure your Plex Server(s) in <a href="/#!/settings#plex">Settings</a></p>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br></br>
|
||||
<br></br>
|
||||
<br></br>
|
||||
<br></br>
|
||||
<br></br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -24,18 +24,18 @@
|
||||
<h5 class="modal-title">Library</h5>
|
||||
<span class="pull-right">
|
||||
<label class="small" for="displayImages">Thumbnails</label>
|
||||
<input id="displayImages" type="checkbox" ng-model="displayImages" />
|
||||
<input id="displayImages" type="checkbox" ng-model="displayImages" ></input>
|
||||
</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<select class="form-control form-control-sm custom-select" ng-model="currentOrigin"
|
||||
ng-options="x.name for x in origins" ng-change="selectOrigin(currentOrigin)"></select>
|
||||
<hr />
|
||||
<hr ></hr>
|
||||
<ul ng-show="currentOrigin.type=='plex' " class="list-group list-group-root plex-panel" ng-init="setHeight = {'height': height + 'px'}" ng-style="setHeight" lazy-img-container>
|
||||
<li class="list-group-item" ng-repeat="a in libraries">
|
||||
<div class="flex-container {{ displayImages ? 'w_images' : 'wo_images' }}" ng-click="getNested(a, true);">
|
||||
<span class="fa {{ a.collapse ? 'fa-chevron-down' : 'fa-chevron-right' }} tab"></span>
|
||||
<img ng-if="displayImages" lazy-img="{{a.icon}}" />
|
||||
<img ng-if="displayImages" lazy-img="{{a.icon}}" ></img>
|
||||
<span>{{ displayTitle(a) }}</span><!-- Library -->
|
||||
<span ng-if="a.type === 'show' || a.type === 'movie' || a.type === 'artist'" class="flex-pull-right" ng-click='$event.stopPropagation(); selectLibrary(a)'>
|
||||
<span class="fa fa-plus btn"></span>
|
||||
@ -49,7 +49,7 @@
|
||||
<span ng-if="b.type === 'movie'" class="fa fa-plus-circle tab"></span>
|
||||
<span ng-if="b.type !== 'movie'" class="tab"></span>
|
||||
<span ng-if="b.type !== 'movie'" class="fa {{ b.collapse ? 'fa-chevron-down' : 'fa-chevron-right' }} tab"></span>
|
||||
<img ng-if="displayImages" lazy-img="{{ b.type === 'episode' ? b.episodeIcon : b.icon }}" />
|
||||
<img ng-if="displayImages" lazy-img="{{ b.type === 'episode' ? b.episodeIcon : b.icon }}" ></img>
|
||||
{{ displayTitle(b) }}
|
||||
<span ng-if="b.type === 'movie'" class="flex-pull-right">
|
||||
{{b.durationStr}}
|
||||
@ -74,7 +74,7 @@
|
||||
class="tab"></span>
|
||||
<span ng-if="c.type !== 'movie' && c.type !== 'episode' && c.type !== 'track'"
|
||||
class="fa {{ c.collapse ? 'fa-chevron-down' : 'fa-chevron-right' }} tab"></span>
|
||||
<img ng-if="displayImages" lazy-img="{{c.type === 'episode' ? c.episodeIcon : c.icon }}" />
|
||||
<img ng-if="displayImages" lazy-img="{{c.type === 'episode' ? c.episodeIcon : c.icon }}" ></img>
|
||||
{{ displayTitle(c) }}
|
||||
<span ng-if="c.type === 'movie' || c.type === 'episode' || c.type === 'track' "
|
||||
class="flex-pull-right">
|
||||
@ -89,7 +89,7 @@
|
||||
ng-repeat="d in c.nested">
|
||||
<div class="flex-container" ng-click="selectItem(d, true)">
|
||||
<span class="fa fa-plus-circle tab"></span>
|
||||
<img ng-if="displayImages" lazy-img="{{d.episodeIcon}}" />
|
||||
<img ng-if="displayImages" lazy-img="{{d.episodeIcon}}" ></img>
|
||||
{{ displayTitle(d) }}
|
||||
<span class="flex-pull-right">{{d.durationStr}}</span>
|
||||
<!-- Episode -->
|
||||
@ -112,7 +112,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr/>
|
||||
<hr></hr>
|
||||
<div class="loader" ng-if="pending > 0" ></div> <h6 style='display:inline-block'>Selected Items</h6>
|
||||
|
||||
<div class="text-info small" ng-show='selection.length > 10'>{{ selection.length }} elements added in total. Only the last 10 elements are displayed:</div>
|
||||
|
||||
@ -78,18 +78,18 @@
|
||||
<div class="row" >
|
||||
<div class="col-sm-3">
|
||||
<div class="form-group">
|
||||
<input id="debugLogging" type="checkbox" ng-model="settings.debugLogging"/>
|
||||
<input id="debugLogging" type="checkbox" ng-model="settings.debugLogging"></input>
|
||||
<label for="debugLogging">Debug logging</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Paths</label>
|
||||
<select ng-model="settings.streamPath"
|
||||
ng-options="o.id as o.description for o in pathOptions" />
|
||||
ng-options="o.id as o.description for o in pathOptions" ></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="form-group">
|
||||
<input id="updatePlayStatus" type="checkbox" ng-model="settings.updatePlayStatus" ria-describedby="updatePlayStatusHelp"/>
|
||||
<input id="updatePlayStatus" type="checkbox" ng-model="settings.updatePlayStatus" ria-describedby="updatePlayStatusHelp"></input>
|
||||
<label for="updatePlayStatus">Send play status to Plex</label>
|
||||
<small id="updatePlayStatusHelp" class="form-text text-muted">Note: This affects the "on deck" for your plex account.</small>
|
||||
</div>
|
||||
@ -108,36 +108,36 @@
|
||||
<h6 style="font-weight: bold">Video Options</h6>
|
||||
<div class="form-group">
|
||||
<label>Supported Video Formats</label>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.videoCodecs" ria-describedby="videoCodecsHelp"/>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.videoCodecs" ria-describedby="videoCodecsHelp"></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Max Playable Resolution</label>
|
||||
<select ng-model="settings.maxPlayableResolution"
|
||||
ng-options="o.id as o.description for o in resolutionOptions" />
|
||||
ng-options="o.id as o.description for o in resolutionOptions" ></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Max Transcode Resolution</label>
|
||||
<select ng-model="settings.maxTranscodeResolution"
|
||||
ng-options="o.id as o.description for o in resolutionOptions "/>
|
||||
ng-options="o.id as o.description for o in resolutionOptions "></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<h6 style="font-weight: bold">Audio Options</h6>
|
||||
<div class="form-group">
|
||||
<label>Supported Audio Formats</label>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.audioCodecs" ria-describedby="audioCodecsHelp" />
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.audioCodecs" ria-describedby="audioCodecsHelp" ></input>
|
||||
<small id="audioCodecsHelp" class="form-text text-muted">Comma separated list. Some possible values are 'ac3,aac,mp3'.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Maximum Audio Channels</label>
|
||||
<select ng-model="settings.maxAudioChannels"
|
||||
ng-options="o.id as o.description for o in maxAudioChannelsOptions" ria-describedby="maxAudioChannelsHelp"/>
|
||||
ng-options="o.id as o.description for o in maxAudioChannelsOptions" ria-describedby="maxAudioChannelsHelp"></select>
|
||||
<small id="maxAudioChannelsHelp" class="form-text text-muted">Note: 7.1 audio and on some clients, 6.1, is known to cause playback issues.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Audio Boost</label>
|
||||
<select ng-model="settings.audioBoost"
|
||||
ng-options="o.id as o.description for o in audioBoostOptions" ria-describedby="audioBoostHelp"/>
|
||||
ng-options="o.id as o.description for o in audioBoostOptions" ria-describedby="audioBoostHelp"></select>
|
||||
<small id="audioBoostHelp" class="form-text text-muted">Note: Only applies when downmixing to stereo.</small>
|
||||
</div>
|
||||
</div>
|
||||
@ -147,29 +147,29 @@
|
||||
<h6 style="font-weight: bold">Miscellaneous Options</h6>
|
||||
<div class="form-group">
|
||||
<label>Max Direct Stream Bitrate (Kbps)</label>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.directStreamBitrate" />
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.directStreamBitrate" ></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Max Transcode Bitrate (Kbps)</label>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.transcodeBitrate" aria-described-by="transcodebrhelp" />
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.transcodeBitrate" aria-described-by="transcodebrhelp" ></input>
|
||||
<small id="transcodebrhelp" class='text-muted form-text'>Plex will decide to transcode or direct play based on these settings and if Plex transcodes, it will try to match the transcode bitrate.</small>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Direct Stream Media Buffer Size</label>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.mediaBufferSize" />
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.mediaBufferSize" ></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Transcode Media Buffer Size</label>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.transcodeMediaBufferSize" />
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.transcodeMediaBufferSize" ></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Stream Protocol</label>
|
||||
<select ng-model="settings.streamProtocol"
|
||||
ng-options="o.id as o.description for o in streamProtocols" />
|
||||
ng-options="o.id as o.description for o in streamProtocols" ></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input id="forceDirectPlay" type="checkbox" ng-model="settings.forceDirectPlay" />
|
||||
<input id="forceDirectPlay" type="checkbox" ng-model="settings.forceDirectPlay" ></input>
|
||||
<label for="forceDirectPlay">Force Direct Play</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -177,10 +177,10 @@
|
||||
<h6 style="font-weight: bold">Subtitle Options</h6>
|
||||
<div class="form-group">
|
||||
<label>Subtitle Size</label>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.subtitleSize" />
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.subtitleSize" ></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input class="form-check-input" id="enableSubtitles" type="checkbox" ng-model="settings.enableSubtitles" ng-disabled="shouldDisableSubtitles()" />
|
||||
<input class="form-check-input" id="enableSubtitles" type="checkbox" ng-model="settings.enableSubtitles" ng-disabled="shouldDisableSubtitles()" ></input>
|
||||
<label class="form-check-label" for="enableSubtitles">Enable Subtitles (Requires Transcoding)</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -190,11 +190,11 @@
|
||||
<h6 style="font-weight: bold">Path Replacements</h6>
|
||||
<div class="form-group">
|
||||
<label>Original Plex path to replace:</label>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.pathReplace" />
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.pathReplace" ></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Replace Plex path with:</label>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.pathReplaceWith" />
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.pathReplaceWith" ></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -17,80 +17,80 @@
|
||||
<label>Track Title
|
||||
<span class="text-danger pull-right">{{error.title}}</span>
|
||||
</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.title"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.title"></input>
|
||||
<label>Subtitle</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.subtitle"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.subtitle"></input>
|
||||
<label>Summary</label>
|
||||
<textarea class="form-control form-control-sm" ng-model="program.summary"></textarea>
|
||||
<label>Rating</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.rating"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.rating"></input>
|
||||
<label>Icon</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.icon"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.icon"></input>
|
||||
<h6>Icon Preview</h6>
|
||||
<div class="text-center">
|
||||
<img class="img" ng-src="{{program.icon}}" style="max-width: 200px;"/>
|
||||
<img class="img" ng-src="{{program.icon}}" style="max-width: 200px;"></img>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="program.type === 'movie'">
|
||||
<label>Movie Title
|
||||
<span class="text-danger pull-right">{{error.title}}</span>
|
||||
</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.title"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.title"></input>
|
||||
<label>Subtitle</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.subtitle"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.subtitle"></input>
|
||||
<label>Summary</label>
|
||||
<textarea class="form-control form-control-sm" ng-model="program.summary"></textarea>
|
||||
<label>Rating</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.rating"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.rating"></input>
|
||||
<label>Icon</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.icon"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.icon"></input>
|
||||
<h6>Icon Preview</h6>
|
||||
<div class="text-center">
|
||||
<img class="img" ng-src="{{program.icon}}" style="max-width: 200px;"/>
|
||||
<img class="img" ng-src="{{program.icon}}" style="max-width: 200px;"></img>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="program.type === 'episode'">
|
||||
<label>Show Title
|
||||
<span class="text-danger pull-right">{{error.showTitle}}</span>
|
||||
</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.showTitle"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.showTitle"></input>
|
||||
<label>Episode Title
|
||||
<span class="text-danger pull-right">{{error.title}}</span>
|
||||
</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.title"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.title"></input>
|
||||
<label>Season
|
||||
<span class="text-danger pull-right">{{error.season}}</span>
|
||||
</label>
|
||||
<input class="form-control form-control-sm" type="number" ng-model="program.season"/>
|
||||
<input class="form-control form-control-sm" type="number" ng-model="program.season"></input>
|
||||
<label>Episode
|
||||
<span class="text-danger pull-right">{{error.episode}}</span>
|
||||
</label>
|
||||
<input class="form-control form-control-sm" type="number" ng-model="program.episode"/>
|
||||
<input class="form-control form-control-sm" type="number" ng-model="program.episode"></input>
|
||||
<label>Summary</label>
|
||||
<textarea class="form-control form-control-sm" ng-model="program.summary"></textarea>
|
||||
<label>Rating</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.rating"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.rating"></input>
|
||||
<label>Icon</label>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.icon"/>
|
||||
<input class="form-control form-control-sm" type="text" ng-model="program.icon"></input>
|
||||
<h6>Icon Preview</h6>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="text-center">
|
||||
<img class="img" ng-src="{{program.icon}}" style="max-width: 200px;"/>
|
||||
<img class="img" ng-src="{{program.icon}}" style="max-width: 200px;"></img>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 row" ng-if="program.showIcon">
|
||||
<div class="col-sm-6 text-center">
|
||||
<label>Show</label>
|
||||
<img class="img" ng-src="{{program.showIcon}}" style="max-width: 75px; cursor: pointer;" ng-click="program.icon = program.showIcon"/>
|
||||
<img class="img" ng-src="{{program.showIcon}}" style="max-width: 75px; cursor: pointer;" ng-click="program.icon = program.showIcon"></img>
|
||||
</div>
|
||||
<div class="col-sm-6 text-center">
|
||||
<label>Season</label>
|
||||
<img class="img" ng-src="{{program.seasonIcon}}" style="max-width: 75px; cursor: pointer;" ng-click="program.icon = program.seasonIcon"/>
|
||||
<img class="img" ng-src="{{program.seasonIcon}}" style="max-width: 75px; cursor: pointer;" ng-click="program.icon = program.seasonIcon"></img>
|
||||
</div>
|
||||
<div class="col-sm-12 text-center">
|
||||
<label>Episode</label>
|
||||
<img class="img" ng-src="{{program.episodeIcon}}" style="max-width: 150px; cursor: pointer;" ng-click="program.icon = program.episodeIcon"/>
|
||||
<img class="img" ng-src="{{program.episodeIcon}}" style="max-width: 150px; cursor: pointer;" ng-click="program.icon = program.episodeIcon"></img>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<div class="programming-counter small" ng-show="content.length > 0">
|
||||
<span class="small"><b>Total:</b> {{content.length}}</span>
|
||||
</div>
|
||||
<div class='flex-pull-right' />
|
||||
<div class='flex-pull-right' ></div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-secondary btn-programming-tools"
|
||||
ng-click="showTools = !showTools"
|
||||
@ -97,7 +97,7 @@
|
||||
<div class="program-start" >
|
||||
X{{ (x.$index + 1).toString().padStart(2, '0') }}
|
||||
</div>
|
||||
<div ng-style="programSquareStyle(x, false)" />
|
||||
<div ng-style="programSquareStyle(x, false)" ></div>
|
||||
<div class="title" >
|
||||
{{ getProgramDisplayTitle(x) }}
|
||||
</div>
|
||||
|
||||
@ -9,22 +9,22 @@
|
||||
</button>
|
||||
</h5>
|
||||
<h6>Output Path</h6>
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.file" aria-describedby="pathhelp" readonly />
|
||||
<input type="text" class="form-control form-control-sm" ng-model="settings.file" aria-describedby="pathhelp" readonly ></input>
|
||||
<small id="pathhelp" class="form-text text-muted">You can edit this location in file xmltv-settings.json.</small>
|
||||
<br/>
|
||||
<br></br>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<label>EPG Hours</label>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.cache" aria-describedby="cachehelp"/>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.cache" aria-describedby="cachehelp"></input>
|
||||
<small id="cachehelp" class="form-text text-muted">How many hours of programming to include in the xmltv file.</small>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label>Refresh Timer (hours)</label>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.refresh" aria-describedby="timerhelp"/>
|
||||
<input type="number" class="form-control form-control-sm" ng-model="settings.refresh" aria-describedby="timerhelp"></input>
|
||||
<small id="timerhelp" class="form-text text-muted">How often should the xmltv file be updated.</small>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br ></br>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" id="imageCache" aria-describedby="imageCacheHelp" ng-model='settings.enableImageCache'>
|
||||
<label class="form-check-label" for="stealth">Image Cache</label>
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
<span ng-show="!x.pending">{{x.number}}</span>
|
||||
</td>
|
||||
<td style="padding: 0" class="text-center">
|
||||
<img ng-if="x.icon !== ''" ng-src="{{x.icon}}" alt="{{x.name}}" style="max-height: 40px;"/>
|
||||
<img ng-if="x.icon !== ''" ng-src="{{x.icon}}" alt="{{x.name}}" style="max-height: 40px;"></img>
|
||||
<div ng-if="x.icon === ''" style="padding-top: 14px;"><small>{{x.name}}</small></div>
|
||||
</td>
|
||||
<td>{{x.name}} <span ng-if='x.stealth===true' class='text-muted'>(Stealth)</span></td>
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<br />
|
||||
<br ></br>
|
||||
<plex-settings ng-if="selected == 'plex'"></plex-settings>
|
||||
<ffmpeg-settings ng-if="selected == 'ffmpeg'"></ffmpeg-settings>
|
||||
<xmltv-settings ng-if="selected == 'xmltv'"></xmltv-settings>
|
||||
|
||||
@ -158,7 +158,7 @@ module.exports = function ($http, $window, $interval) {
|
||||
},
|
||||
getStreams: async (server, key) => {
|
||||
var client = new Plex(server)
|
||||
return client.Get(key).then((res) => {
|
||||
return client.Get(key).then((res) => {
|
||||
let streams = res.Metadata[0].Media[0].Part[0].Stream
|
||||
for (let i = 0, l = streams.length; i < l; i++) {
|
||||
if (typeof streams[i].key !== 'undefined') {
|
||||
@ -278,6 +278,29 @@ module.exports = function ($http, $window, $interval) {
|
||||
}
|
||||
if (typeof (res.Metadata[i].Collection) !== 'undefined') {
|
||||
let coll = res.Metadata[i].Collection;
|
||||
if (coll.length == 2) {
|
||||
// the /all endpoint returns incomplete data, so we
|
||||
// might have to complete the list of collections
|
||||
// when there are already 2 collections there.
|
||||
//console.log(res.Metadata[i]);
|
||||
let complete = {}
|
||||
try {
|
||||
complete = await client.Get(`/library/metadata/${res.Metadata[i].ratingKey}`);
|
||||
} catch (err) {
|
||||
console.error("Error attempting to load collections", err);
|
||||
}
|
||||
if (
|
||||
(typeof(complete.Metadata) !== 'undefined')
|
||||
&&
|
||||
(complete.Metadata.length == 1)
|
||||
&&
|
||||
(typeof(complete.Metadata[0].Collection) !== 'undefined')
|
||||
&&
|
||||
( complete.Metadata[0].Collection.length > 2)
|
||||
) {
|
||||
coll = complete.Metadata[0].Collection;
|
||||
}
|
||||
}
|
||||
for (let j = 0; j < coll.length; j++) {
|
||||
let tag = coll[j].tag;
|
||||
if ( (typeof(tag)!== "undefined") && (tag.length > 0) ) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user