mirror of
https://github.com/Viren070/AIOStreams.git
synced 2025-12-01 23:14:04 +01:00
Merge branch 'main' into fix/comet-rewrite
This commit is contained in:
Generated
+218
-7
@@ -329,6 +329,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0"
|
||||
},
|
||||
"node_modules/@colors/colors": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
|
||||
"integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
@@ -342,6 +351,17 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@dabh/diagnostics": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
|
||||
"integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"colorspace": "1.1.x",
|
||||
"enabled": "2.0.x",
|
||||
"kuler": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
|
||||
@@ -2242,6 +2262,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/triple-beam": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
|
||||
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.18.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.2.tgz",
|
||||
@@ -2903,6 +2929,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/async-file": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/async-file/-/async-file-2.0.2.tgz",
|
||||
@@ -3301,7 +3333,6 @@
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/color-string": {
|
||||
@@ -3309,12 +3340,46 @@
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/colorspace": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
|
||||
"integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color": "^3.1.3",
|
||||
"text-hex": "1.0.x"
|
||||
}
|
||||
},
|
||||
"node_modules/colorspace/node_modules/color": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
||||
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.3",
|
||||
"color-string": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/colorspace/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/colorspace/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz",
|
||||
@@ -3706,6 +3771,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/enabled": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
|
||||
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
@@ -4572,6 +4643,12 @@
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fecha": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
|
||||
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/file-entry-cache": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||
@@ -4675,6 +4752,12 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/fn.name": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
|
||||
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/for-each": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||
@@ -5158,8 +5241,7 @@
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-async-function": {
|
||||
"version": "2.0.0",
|
||||
@@ -5443,7 +5525,6 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -5691,6 +5772,12 @@
|
||||
"json-buffer": "3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/kuler": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
|
||||
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/language-subtag-registry": {
|
||||
"version": "0.3.23",
|
||||
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
|
||||
@@ -5754,6 +5841,23 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/logform": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
|
||||
"integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@colors/colors": "1.6.0",
|
||||
"@types/triple-beam": "^1.3.2",
|
||||
"fecha": "^4.2.0",
|
||||
"ms": "^2.1.1",
|
||||
"safe-stable-stringify": "^2.3.1",
|
||||
"triple-beam": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
@@ -6299,6 +6403,15 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/one-time": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
|
||||
"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fn.name": "1.x.x"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.4",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||
@@ -6730,6 +6843,20 @@
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
|
||||
@@ -7034,6 +7161,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-stable-stringify": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
|
||||
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
@@ -7363,7 +7499,6 @@
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
@@ -7420,6 +7555,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stack-trace": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/stackback": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
||||
@@ -7473,6 +7617,15 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string.prototype.includes": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
|
||||
@@ -7673,6 +7826,12 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/text-hex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
|
||||
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinybench": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
|
||||
@@ -7739,6 +7898,15 @@
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/triple-beam": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
|
||||
"integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
|
||||
@@ -8006,6 +8174,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
@@ -8724,6 +8898,42 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/winston": {
|
||||
"version": "3.17.0",
|
||||
"resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz",
|
||||
"integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@colors/colors": "^1.6.0",
|
||||
"@dabh/diagnostics": "^2.0.2",
|
||||
"async": "^3.2.3",
|
||||
"is-stream": "^2.0.0",
|
||||
"logform": "^2.7.0",
|
||||
"one-time": "^1.0.0",
|
||||
"readable-stream": "^3.4.0",
|
||||
"safe-stable-stringify": "^2.3.1",
|
||||
"stack-trace": "0.0.x",
|
||||
"triple-beam": "^1.3.0",
|
||||
"winston-transport": "^4.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/winston-transport": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
|
||||
"integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"logform": "^2.7.0",
|
||||
"readable-stream": "^3.6.2",
|
||||
"triple-beam": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
@@ -9389,7 +9599,8 @@
|
||||
"name": "@aiostreams/utils",
|
||||
"version": "1.13.8",
|
||||
"dependencies": {
|
||||
"jsoncrush": "^1.1.8"
|
||||
"jsoncrush": "^1.1.8",
|
||||
"winston": "^3.17.0"
|
||||
}
|
||||
},
|
||||
"packages/wrappers": {
|
||||
|
||||
+142
-69
@@ -33,9 +33,12 @@ import {
|
||||
getMediaFlowPublicIp,
|
||||
getTimeTakenSincePoint,
|
||||
Settings,
|
||||
createLogger,
|
||||
} from '@aiostreams/utils';
|
||||
import { errorStream } from './responses';
|
||||
|
||||
const logger = createLogger('addon');
|
||||
|
||||
export class AIOStreams {
|
||||
private config: Config;
|
||||
|
||||
@@ -74,17 +77,18 @@ export class AIOStreams {
|
||||
}
|
||||
this.config.requestingIp = ip;
|
||||
break;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`|ERR| addon > getStreams: Failed to get requesting IP: ${error}, retrying ${ipRequestCount + 1}/3`
|
||||
);
|
||||
} catch (error: any) {
|
||||
logger.error(error, {
|
||||
func: 'getRequestingIp',
|
||||
});
|
||||
ipRequestCount++;
|
||||
if (ipRequestCount < 3) {
|
||||
logger.info(`Retrying ${ipRequestCount}/3...`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ipRequestCount === 3) {
|
||||
console.error(
|
||||
'|ERR| addon > getStreams: Failed to get requesting IP after 3 attempts'
|
||||
);
|
||||
logger.error('Failed to get requesting IP after 3 attempts');
|
||||
if (this.config.mediaFlowConfig?.mediaFlowEnabled) {
|
||||
return [
|
||||
errorStream('Aborted request after failing to get requesting IP'),
|
||||
@@ -94,8 +98,24 @@ export class AIOStreams {
|
||||
const { parsedStreams, errorStreams } =
|
||||
await this.getParsedStreams(streamRequest);
|
||||
|
||||
console.log(
|
||||
`|INF| addon > getStreams: Got ${parsedStreams.length} parsed streams and ${errorStreams.length} error streams in ${getTimeTakenSincePoint(startTime)}`
|
||||
const skipReasons = {
|
||||
excludeKeywords: 0,
|
||||
requiredKeywords: 0,
|
||||
excludeLanguages: 0,
|
||||
excludeResolutions: 0,
|
||||
excludeQualities: 0,
|
||||
excludeEncodes: 0,
|
||||
excludeAudioTags: 0,
|
||||
excludeVisualTags: 0,
|
||||
excludeStreamTypes: 0,
|
||||
excludeCached: 0,
|
||||
sizeFilters: 0,
|
||||
duplicateStreams: 0,
|
||||
streamLimiters: 0,
|
||||
};
|
||||
|
||||
logger.info(
|
||||
`Got ${parsedStreams.length} parsed streams and ${errorStreams.length} error streams in ${getTimeTakenSincePoint(startTime)}`
|
||||
);
|
||||
const filterStartTime = new Date().getTime();
|
||||
|
||||
@@ -120,8 +140,8 @@ export class AIOStreams {
|
||||
: null;
|
||||
|
||||
excludeRegex || strictIncludeRegex
|
||||
? console.log(
|
||||
`|INF| addon > getStreams: Created regex filters: excludeRegex: ${excludeRegex}, strictIncludeRegex: ${strictIncludeRegex}`
|
||||
? logger.debug(
|
||||
`Using keyword regex filters: excludeRegex: ${excludeRegex}, strictIncludeRegex: ${strictIncludeRegex}`
|
||||
)
|
||||
: null;
|
||||
|
||||
@@ -129,17 +149,26 @@ export class AIOStreams {
|
||||
const streamTypeFilter = this.config.streamTypes?.find(
|
||||
(streamType) => streamType[parsedStream.type] === false
|
||||
);
|
||||
if (this.config.streamTypes && streamTypeFilter) return false;
|
||||
if (this.config.streamTypes && streamTypeFilter) {
|
||||
skipReasons.excludeStreamTypes++;
|
||||
return false;
|
||||
}
|
||||
|
||||
const resolutionFilter = this.config.resolutions?.find(
|
||||
(resolution) => resolution[parsedStream.resolution] === false
|
||||
);
|
||||
if (resolutionFilter) return false;
|
||||
if (resolutionFilter) {
|
||||
skipReasons.excludeResolutions++;
|
||||
return false;
|
||||
}
|
||||
|
||||
const qualityFilter = this.config.qualities?.find(
|
||||
(quality) => quality[parsedStream.quality] === false
|
||||
);
|
||||
if (this.config.qualities && qualityFilter) return false;
|
||||
if (this.config.qualities && qualityFilter) {
|
||||
skipReasons.excludeQualities++;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for HDR and DV tags in the parsed stream
|
||||
const hasHDR = parsedStream.visualTags.some((tag) =>
|
||||
@@ -156,6 +185,7 @@ export class AIOStreams {
|
||||
|
||||
if (hasHDRAndDV) {
|
||||
if (!HDRAndDVEnabled) {
|
||||
skipReasons.excludeVisualTags++;
|
||||
return false;
|
||||
}
|
||||
} else if (hasHDR) {
|
||||
@@ -166,16 +196,21 @@ export class AIOStreams {
|
||||
(tag) => isTagDisabled(tag) === true
|
||||
);
|
||||
if (disabledTags.length > 0) {
|
||||
return false;
|
||||
skipReasons.excludeVisualTags++;
|
||||
return;
|
||||
}
|
||||
} else if (hasDV && isTagDisabled('DV')) {
|
||||
skipReasons.excludeVisualTags++;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check other visual tags for explicit disabling
|
||||
for (const tag of parsedStream.visualTags) {
|
||||
if (tag.startsWith('HDR') || tag === 'DV') continue;
|
||||
if (isTagDisabled(tag)) return false;
|
||||
if (isTagDisabled(tag)) {
|
||||
skipReasons.excludeVisualTags++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// apply excludedLanguages filter
|
||||
@@ -186,6 +221,7 @@ export class AIOStreams {
|
||||
excludedLanguages.includes(lang)
|
||||
)
|
||||
) {
|
||||
skipReasons.excludeLanguages++;
|
||||
return false;
|
||||
}
|
||||
} else if (
|
||||
@@ -193,74 +229,94 @@ export class AIOStreams {
|
||||
excludedLanguages.includes('Unknown') &&
|
||||
parsedStream.languages.length === 0
|
||||
) {
|
||||
skipReasons.excludeLanguages++;
|
||||
return false;
|
||||
}
|
||||
|
||||
const audioTagFilter = parsedStream.audioTags.find((tag) =>
|
||||
this.config.audioTags.some((audioTag) => audioTag[tag] === false)
|
||||
);
|
||||
if (audioTagFilter) return false;
|
||||
if (audioTagFilter) {
|
||||
skipReasons.excludeAudioTags++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
parsedStream.encode &&
|
||||
this.config.encodes.some(
|
||||
(encode) => encode[parsedStream.encode] === false
|
||||
)
|
||||
)
|
||||
) {
|
||||
skipReasons.excludeEncodes++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
this.config.onlyShowCachedStreams &&
|
||||
parsedStream.provider &&
|
||||
!parsedStream.provider.cached
|
||||
)
|
||||
) {
|
||||
skipReasons.excludeCached++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
this.config.minSize &&
|
||||
parsedStream.size &&
|
||||
parsedStream.size < this.config.minSize
|
||||
)
|
||||
) {
|
||||
skipReasons.sizeFilters++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
this.config.maxSize &&
|
||||
parsedStream.size &&
|
||||
parsedStream.size > this.config.maxSize
|
||||
)
|
||||
) {
|
||||
skipReasons.sizeFilters++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
streamRequest.type === 'movie' &&
|
||||
this.config.maxMovieSize &&
|
||||
parsedStream.size &&
|
||||
parsedStream.size > this.config.maxMovieSize
|
||||
)
|
||||
) {
|
||||
skipReasons.sizeFilters++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
streamRequest.type === 'movie' &&
|
||||
this.config.minMovieSize &&
|
||||
parsedStream.size &&
|
||||
parsedStream.size < this.config.minMovieSize
|
||||
)
|
||||
) {
|
||||
skipReasons.sizeFilters++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
streamRequest.type === 'series' &&
|
||||
this.config.maxEpisodeSize &&
|
||||
parsedStream.size &&
|
||||
parsedStream.size > this.config.maxEpisodeSize
|
||||
)
|
||||
) {
|
||||
skipReasons.sizeFilters++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
streamRequest.type === 'series' &&
|
||||
this.config.minEpisodeSize &&
|
||||
parsedStream.size &&
|
||||
parsedStream.size < this.config.minEpisodeSize
|
||||
)
|
||||
) {
|
||||
skipReasons.sizeFilters++;
|
||||
return false;
|
||||
}
|
||||
|
||||
// apply keyword filters
|
||||
if (
|
||||
@@ -269,9 +325,11 @@ export class AIOStreams {
|
||||
excludeRegex
|
||||
) {
|
||||
if (parsedStream.filename && excludeRegex.test(parsedStream.filename)) {
|
||||
skipReasons.excludeKeywords++;
|
||||
return false;
|
||||
}
|
||||
if (parsedStream.indexers && excludeRegex.test(parsedStream.indexers)) {
|
||||
skipReasons.excludeKeywords++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -285,14 +343,15 @@ export class AIOStreams {
|
||||
parsedStream.filename &&
|
||||
!strictIncludeRegex.test(parsedStream.filename)
|
||||
) {
|
||||
skipReasons.requiredKeywords++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
console.log(
|
||||
`|INF| addon > getStreams: Initial filter to ${filteredResults.length} streams in ${getTimeTakenSincePoint(filterStartTime)}`
|
||||
logger.info(
|
||||
`Initial filter to ${filteredResults.length} streams in ${getTimeTakenSincePoint(filterStartTime)}`
|
||||
);
|
||||
|
||||
if (this.config.cleanResults) {
|
||||
@@ -332,16 +391,14 @@ export class AIOStreams {
|
||||
const cleanResultsStartTime = new Date().getTime();
|
||||
// Deduplication by normalised filename
|
||||
const cleanResultsByFilenameStartTime = new Date().getTime();
|
||||
console.log(
|
||||
`|INF| addon > cleaner: Received ${initialStreams.length} streams to clean`
|
||||
);
|
||||
logger.info(`Received ${initialStreams.length} streams to clean`);
|
||||
const streamsGroupedByFilename = groupStreamsByKey(
|
||||
initialStreams,
|
||||
(stream) => normaliseFilename(stream.filename)
|
||||
);
|
||||
|
||||
console.log(
|
||||
`|INF| addon > cleaner: Found ${Object.keys(streamsGroupedByFilename).length} unique filenames`
|
||||
logger.info(
|
||||
`Found ${Object.keys(streamsGroupedByFilename).length} unique filenames`
|
||||
);
|
||||
|
||||
// Process grouped streams by filename
|
||||
@@ -349,8 +406,8 @@ export class AIOStreams {
|
||||
streamsGroupedByFilename
|
||||
);
|
||||
|
||||
console.log(
|
||||
`|INF| addon > cleaner: Deduplicated streams by filename to ${cleanedStreamsByFilename.length} streams in ${getTimeTakenSincePoint(cleanResultsByFilenameStartTime)}`
|
||||
logger.info(
|
||||
`Deduplicated streams by filename to ${cleanedStreamsByFilename.length} streams in ${getTimeTakenSincePoint(cleanResultsByFilenameStartTime)}`
|
||||
);
|
||||
|
||||
// Deduplication by hash
|
||||
@@ -360,22 +417,24 @@ export class AIOStreams {
|
||||
cleanedStreamsByFilename,
|
||||
(stream) => stream._infoHash
|
||||
);
|
||||
console.log(
|
||||
`|INF| addon > cleaner: Found ${Object.keys(streamsGroupedByHash).length} unique hashes with ${cleanedStreamsByFilename.length - Object.values(streamsGroupedByHash).reduce((sum, group) => sum + group.length, 0)} streams not grouped`
|
||||
logger.info(
|
||||
`Found ${Object.keys(streamsGroupedByHash).length} unique hashes with ${cleanedStreamsByFilename.length - Object.values(streamsGroupedByHash).reduce((sum, group) => sum + group.length, 0)} streams not grouped`
|
||||
);
|
||||
|
||||
// Process grouped streams by hash
|
||||
const cleanedStreamsByHash =
|
||||
await this.processGroupedStreams(streamsGroupedByHash);
|
||||
|
||||
console.log(
|
||||
`|INF| addon > cleaner: Deduplicated streams by hash to ${cleanedStreamsByHash.length} streams in ${getTimeTakenSincePoint(cleanResultsByHashStartTime)}`
|
||||
logger.info(
|
||||
`Deduplicated streams by hash to ${cleanedStreamsByHash.length} streams in ${getTimeTakenSincePoint(cleanResultsByHashStartTime)}`
|
||||
);
|
||||
|
||||
cleanedStreams.push(...cleanedStreamsByHash);
|
||||
console.log(
|
||||
`|INF| addon > cleaner: Deduplicated streams to ${cleanedStreams.length} streams in ${getTimeTakenSincePoint(cleanResultsStartTime)}`
|
||||
logger.info(
|
||||
`Deduplicated streams to ${cleanedStreams.length} streams in ${getTimeTakenSincePoint(cleanResultsStartTime)}`
|
||||
);
|
||||
skipReasons.duplicateStreams =
|
||||
filteredResults.length - cleanedStreams.length;
|
||||
filteredResults = cleanedStreams;
|
||||
}
|
||||
// Apply sorting
|
||||
@@ -403,9 +462,7 @@ export class AIOStreams {
|
||||
return 0;
|
||||
});
|
||||
|
||||
console.log(
|
||||
`|INF| addon > getStreams: Sorted results in ${getTimeTakenSincePoint(sortStartTime)}`
|
||||
);
|
||||
logger.info(`Sorted results in ${getTimeTakenSincePoint(sortStartTime)}`);
|
||||
|
||||
// apply config.maxResultsPerResolution
|
||||
if (this.config.maxResultsPerResolution) {
|
||||
@@ -423,14 +480,34 @@ export class AIOStreams {
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
skipReasons.streamLimiters =
|
||||
filteredResults.length - limitedResults.length;
|
||||
filteredResults = limitedResults;
|
||||
|
||||
console.log(
|
||||
`|INF| addon > getStreams: Limited results to ${limitedResults.length} streams after applying maxResultsPerResolution in ${new Date().getTime() - startTime}ms`
|
||||
logger.info(
|
||||
`Limited results to ${limitedResults.length} streams after applying maxResultsPerResolution in ${new Date().getTime() - startTime}ms`
|
||||
);
|
||||
}
|
||||
|
||||
const totalSkipped = Object.values(skipReasons).reduce(
|
||||
(acc, val) => acc + val,
|
||||
0
|
||||
);
|
||||
const reportLines = [
|
||||
'╔═══════════════════════╤════════════╗',
|
||||
'║ Skip Reason │ Count ║',
|
||||
'╟───────────────────────┼────────────╢',
|
||||
...Object.entries(skipReasons).map(
|
||||
([reason, count]) =>
|
||||
`║ ${reason.padEnd(21)} │ ${String(count).padStart(10)} ║`
|
||||
),
|
||||
'╟───────────────────────┼────────────╢',
|
||||
`║ Total Skipped │ ${String(totalSkipped).padStart(10)} ║`,
|
||||
'╚═══════════════════════╧════════════╝',
|
||||
];
|
||||
|
||||
logger.info('\n' + reportLines.join('\n'));
|
||||
|
||||
// Create stream objects
|
||||
const streamsStartTime = new Date().getTime();
|
||||
const streamObjects = await Promise.all(
|
||||
@@ -443,11 +520,11 @@ export class AIOStreams {
|
||||
...errorStreams.map((e) => errorStream(e.error, e.addon.name))
|
||||
);
|
||||
|
||||
console.log(
|
||||
`|INF| addon > getStreams: Created ${streams.length} stream objects in ${getTimeTakenSincePoint(streamsStartTime)}`
|
||||
logger.info(
|
||||
`Created ${streams.length} stream objects in ${getTimeTakenSincePoint(streamsStartTime)}`
|
||||
);
|
||||
console.log(
|
||||
`|INF| addon > getStreams: Total time taken to serve streams: ${getTimeTakenSincePoint(startTime)}`
|
||||
logger.info(
|
||||
`Total time taken to get streams: ${getTimeTakenSincePoint(startTime)}`
|
||||
);
|
||||
return streams;
|
||||
}
|
||||
@@ -458,8 +535,9 @@ export class AIOStreams {
|
||||
description: string
|
||||
): Stream {
|
||||
if (!parsedStream.url) {
|
||||
console.error(
|
||||
`|ERR| addon > createMediaFlowStream: Stream URL is missing, cannot proxy a stream without a URL`
|
||||
logger.error(
|
||||
`Stream URL is missing, cannot proxy a stream without a URL`,
|
||||
{ func: 'createMediaFlowStream' }
|
||||
);
|
||||
throw new Error('Stream URL is missing');
|
||||
}
|
||||
@@ -506,8 +584,8 @@ export class AIOStreams {
|
||||
if (!mediaFlowConfig.mediaFlowEnabled) return false;
|
||||
if (!stream.url) return false;
|
||||
// // now check if mediaFlowConfig.proxiedAddons or mediaFlowConfig.proxiedServices is not null
|
||||
// console.log(this.config.mediaFlowConfig?.proxiedAddons);
|
||||
// console.log(stream.addon.id);
|
||||
// logger.info(this.config.mediaFlowConfig?.proxiedAddons);
|
||||
// logger.info(stream.addon.id);
|
||||
if (
|
||||
mediaFlowConfig.proxiedAddons &&
|
||||
mediaFlowConfig.proxiedAddons.length > 0 &&
|
||||
@@ -603,9 +681,7 @@ export class AIOStreams {
|
||||
}
|
||||
return mediaFlowStream;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`|ERR| addon > createStreamObject: Failed to create MediaFlow stream URL: ${error}`
|
||||
);
|
||||
logger.error(`Failed to create MediaFlow stream URL: ${error}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -892,7 +968,6 @@ export class AIOStreams {
|
||||
addon.options.overrideName ||
|
||||
addonDetails.find((addonDetail) => addonDetail.id === addon.id)?.name ||
|
||||
addon.id;
|
||||
console.log(addon.options);
|
||||
const addonId = `${addon.id}-${JSON.stringify(addon.options)}`;
|
||||
try {
|
||||
const startTime = new Date().getTime();
|
||||
@@ -908,13 +983,11 @@ export class AIOStreams {
|
||||
addon: { id: addonId, name: addonName },
|
||||
}))
|
||||
);
|
||||
console.log(
|
||||
`|INF| addon > getParsedStreams: Got ${addonStreams.length} streams ${addonErrors.length > 0 ? `and ${addonErrors.length} errors ` : ''}from addon ${addonName} in ${getTimeTakenSincePoint(startTime)}`
|
||||
logger.info(
|
||||
`Got ${addonStreams.length} streams ${addonErrors.length > 0 ? `and ${addonErrors.length} errors ` : ''}from addon ${addonName} in ${getTimeTakenSincePoint(startTime)}`
|
||||
);
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
`|ERR| addon > getParsedStreams: Failed to get streams from ${addonName}: ${error}`
|
||||
);
|
||||
logger.error(`Failed to get streams from ${addonName}: ${error}`);
|
||||
errorStreams.push({
|
||||
error: formatError(error.message ?? error ?? 'Unknown error'),
|
||||
addon: {
|
||||
@@ -1075,16 +1148,16 @@ export class AIOStreams {
|
||||
return;
|
||||
}
|
||||
|
||||
/*console.log(
|
||||
/*logger.info(
|
||||
`==================\nDetermining unique streams for ${groupedStreams[0].filename} from ${groupedStreams.length} total duplicates`
|
||||
);
|
||||
console.log(
|
||||
logger.info(
|
||||
groupedStreams.map(
|
||||
(stream) =>
|
||||
`Addon ID: ${stream.addon.id}, Provider ID: ${stream.provider?.id}, Provider Cached: ${stream.provider?.cached}, type: ${stream.torrent ? 'torrent' : 'usenet'}`
|
||||
)
|
||||
);
|
||||
console.log('==================');*/
|
||||
logger.info('==================');*/
|
||||
// Separate streams into categories
|
||||
const cachedStreams = groupedStreams.filter(
|
||||
(stream) => stream.provider?.cached || (!stream.provider && stream.url)
|
||||
@@ -1119,7 +1192,7 @@ export class AIOStreams {
|
||||
return aIndex - bIndex;
|
||||
})[0];
|
||||
});
|
||||
//selectedUncachedStreams.forEach(stream => console.log(`Selected uncached stream for provider ${stream.provider!.id}: Addon ID: ${stream.addon.id}`));
|
||||
//selectedUncachedStreams.forEach(stream => logger.info(`Selected uncached stream for provider ${stream.provider!.id}: Addon ID: ${stream.addon.id}`));
|
||||
|
||||
// Select cached streams by provider and addon priority
|
||||
const selectedCachedStream = cachedStreams.sort((a, b) => {
|
||||
@@ -1175,11 +1248,11 @@ export class AIOStreams {
|
||||
|
||||
// Combine selected streams for this group
|
||||
if (selectedNoProviderStream) {
|
||||
//console.log(`Selected no provider stream: Addon ID: ${selectedNoProviderStream.addon.id}`);
|
||||
//logger.info(`Selected no provider stream: Addon ID: ${selectedNoProviderStream.addon.id}`);
|
||||
uniqueStreams.push(selectedNoProviderStream);
|
||||
}
|
||||
if (selectedCachedStream) {
|
||||
//console.log(`Selected cached stream for provider ${selectedCachedStream.provider!.id} from Addon ID: ${selectedCachedStream.addon.id}`);
|
||||
//logger.info(`Selected cached stream for provider ${selectedCachedStream.provider!.id} from Addon ID: ${selectedCachedStream.addon.id}`);
|
||||
uniqueStreams.push(selectedCachedStream);
|
||||
}
|
||||
uniqueStreams.push(...selectedUncachedStreams);
|
||||
|
||||
@@ -19,17 +19,21 @@ import {
|
||||
decompressData,
|
||||
decryptData,
|
||||
uncrushJson,
|
||||
loadSecretKey,
|
||||
createLogger,
|
||||
getTimeTakenSincePoint,
|
||||
} from '@aiostreams/utils';
|
||||
|
||||
const logger = createLogger('server');
|
||||
|
||||
const app = express();
|
||||
console.log(`|INF| server > init: Starting server and loading settings...`);
|
||||
//logger.info(`Starting server and loading settings...`);
|
||||
logger.info('Starting server and loading settings...', { func: 'init' });
|
||||
Object.entries(Settings).forEach(([key, value]) => {
|
||||
switch (key) {
|
||||
case 'SECRET_KEY':
|
||||
if (value) {
|
||||
console.log(
|
||||
`|INF| server > init: ${key} = ${value.replace(/./g, '*').slice(0, 32)}`
|
||||
);
|
||||
logger.info(`${key} = ${value.replace(/./g, '*').slice(0, 64)}`);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -39,30 +43,36 @@ Object.entries(Settings).forEach(([key, value]) => {
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`|INF| server > init: ${key} = ${value}`);
|
||||
logger.info(`${key} = ${value}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (!Settings.SECRET_KEY) {
|
||||
console.warn(
|
||||
'|WRN| server > init: SECRET_KEY is not set, data encryption is disabled!'
|
||||
);
|
||||
}
|
||||
|
||||
let CUSTOM_CONFIGS: Record<string, string> = {};
|
||||
if (Settings.CUSTOM_CONFIGS) {
|
||||
try {
|
||||
CUSTOM_CONFIGS = JSON.parse(Settings.CUSTOM_CONFIGS);
|
||||
console.log(
|
||||
`|INF| server > init: Loaded ${Object.keys(CUSTOM_CONFIGS).length} custom configs under aliases ${Object.keys(CUSTOM_CONFIGS).join(', ')}`
|
||||
logger.info(
|
||||
`Loaded ${Object.keys(CUSTOM_CONFIGS).length} custom configs under aliases ${Object.keys(CUSTOM_CONFIGS).join(', ')}`
|
||||
);
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
`|ERR| server > init: CUSTOM_CONFIGS is not valid JSON: ${error.message}`
|
||||
);
|
||||
logger.error(`CUSTOM_CONFIGS is not valid JSON: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to load the secret key
|
||||
try {
|
||||
if (Settings.SECRET_KEY) loadSecretKey();
|
||||
} catch (error: any) {
|
||||
// determine command to run based on system OS
|
||||
const command =
|
||||
process.platform === 'win32'
|
||||
? '[System.Guid]::NewGuid().ToString("N").Substring(0, 32) + [System.Guid]::NewGuid().ToString("N").Substring(0, 32)'
|
||||
: 'openssl rand -hex 32';
|
||||
logger.error(
|
||||
`The secret key is invalid. You will not be able to generate configurations. You can generate a new secret key by running the following command\n${command}`
|
||||
);
|
||||
}
|
||||
|
||||
const cache = new Cache(Settings.MAX_CACHE_SIZE);
|
||||
|
||||
// Built-in middleware for parsing JSON
|
||||
@@ -72,16 +82,19 @@ app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// unhandled errors
|
||||
app.use((err: any, req: Request, res: Response, next: any) => {
|
||||
console.error(`|ERR| server > ${err.message}`);
|
||||
logger.error(`${err.message}`);
|
||||
res.status(500).send('Internal server error');
|
||||
});
|
||||
|
||||
app.use((req, res, next) => {
|
||||
res.append('Access-Control-Allow-Origin', '*');
|
||||
res.append('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
|
||||
console.log(
|
||||
`|DBG| server > ${req.method} ${req.path.replace(/\/eyJ[\w\=]+/g, '/*******').replace(/\/E2?-[\w-\%]+/g, '/*******')}`
|
||||
);
|
||||
const start = Date.now();
|
||||
res.on('finish', () => {
|
||||
logger.info(
|
||||
`${req.method} ${req.path.replace(/\/ey[JI][\w\=]+/g, '/*******').replace(/\/(E2?|B)?-[\w-\%]+/g, '/*******')} - ${res.statusCode} - ${getTimeTakenSincePoint(start)}`
|
||||
);
|
||||
});
|
||||
next();
|
||||
});
|
||||
|
||||
@@ -118,9 +131,7 @@ app.get('/:config/configure', (req, res) => {
|
||||
try {
|
||||
let configJson = extractJsonConfig(config);
|
||||
if (config.startsWith('E-') || config.startsWith('E2-')) {
|
||||
console.log(
|
||||
`|DBG| server > Encrypted config detected, encrypting credentials`
|
||||
);
|
||||
logger.info(`Encrypted config detected, encrypting credentials`);
|
||||
configJson = encryptInfoInConfig(configJson);
|
||||
}
|
||||
const base64Config = Buffer.from(JSON.stringify(configJson)).toString(
|
||||
@@ -128,7 +139,7 @@ app.get('/:config/configure', (req, res) => {
|
||||
);
|
||||
res.redirect(`/${encodeURIComponent(base64Config)}/configure`);
|
||||
} catch (error: any) {
|
||||
console.error(`|ERR| server > Failed to extract config: ${error.message}`);
|
||||
logger.error(`Failed to extract config: ${error.message}`);
|
||||
res.status(400).send('Invalid config');
|
||||
}
|
||||
});
|
||||
@@ -142,24 +153,22 @@ app.get('/:config/manifest.json', (req, res) => {
|
||||
let configJson: Config;
|
||||
try {
|
||||
configJson = extractJsonConfig(config);
|
||||
console.log(`|DBG| server > Extracted config for manifest request`);
|
||||
logger.info(`Extracted config for manifest request`);
|
||||
configJson = decryptEncryptedInfoFromConfig(configJson);
|
||||
if (Settings.LOG_SENSITIVE_INFO) {
|
||||
console.log(`|DBG| server > Final config: ${JSON.stringify(configJson)}`);
|
||||
logger.info(`Final config: ${JSON.stringify(configJson)}`);
|
||||
}
|
||||
console.log(
|
||||
`|DBG| server > Successfully removed or decrypted sensitive info`
|
||||
);
|
||||
logger.info(`Successfully removed or decrypted sensitive info`);
|
||||
const { valid, errorMessage } = validateConfig(configJson);
|
||||
if (!valid) {
|
||||
console.error(
|
||||
`|ERR| server > Received invalid config for manifest request: ${errorMessage}`
|
||||
logger.error(
|
||||
`Received invalid config for manifest request: ${errorMessage}`
|
||||
);
|
||||
res.status(400).json({ error: 'Invalid config', message: errorMessage });
|
||||
return;
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(`|ERR| server > Failed to extract config: ${error.message}`);
|
||||
logger.error(`Failed to extract config: ${error.message}`);
|
||||
res.status(400).json({ error: 'Invalid config' });
|
||||
return;
|
||||
}
|
||||
@@ -184,16 +193,14 @@ app.get('/:config/stream/:type/:id.json', (req, res: Response): void => {
|
||||
let configJson: Config;
|
||||
try {
|
||||
configJson = extractJsonConfig(config);
|
||||
console.log(`|DBG| server > Extracted config for stream request`);
|
||||
logger.info(`Extracted config for stream request`);
|
||||
configJson = decryptEncryptedInfoFromConfig(configJson);
|
||||
if (Settings.LOG_SENSITIVE_INFO) {
|
||||
console.log(`|DBG| server > Final config: ${JSON.stringify(configJson)}`);
|
||||
logger.info(`Final config: ${JSON.stringify(configJson)}`);
|
||||
}
|
||||
console.log(
|
||||
`|DBG| server > Successfully removed or decrypted sensitive info`
|
||||
);
|
||||
logger.info(`Successfully removed or decrypted sensitive info`);
|
||||
} catch (error: any) {
|
||||
console.error(`|ERR| server > Failed to extract config: ${error.message}`);
|
||||
logger.error(`Failed to extract config: ${error.message}`);
|
||||
res.json(
|
||||
errorResponse(
|
||||
`${error.message}, please check the logs or click this stream to create an issue on GitHub`,
|
||||
@@ -205,10 +212,10 @@ app.get('/:config/stream/:type/:id.json', (req, res: Response): void => {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`|DBG| server > Requesting streams for ${type} ${id}`);
|
||||
logger.info(`Requesting streams for ${type} ${id}`);
|
||||
|
||||
if (type !== 'movie' && type !== 'series') {
|
||||
console.error(`|ERR| server > Invalid type for stream request`);
|
||||
logger.error(`Invalid type for stream request`);
|
||||
res.json(
|
||||
errorResponse(
|
||||
'Invalid type for stream request, must be movie or series',
|
||||
@@ -223,9 +230,7 @@ app.get('/:config/stream/:type/:id.json', (req, res: Response): void => {
|
||||
try {
|
||||
const { valid, errorCode, errorMessage } = validateConfig(configJson);
|
||||
if (!valid) {
|
||||
console.error(
|
||||
`|ERR| server > Received invalid config: ${errorCode} - ${errorMessage}`
|
||||
);
|
||||
logger.error(`Received invalid config: ${errorCode} - ${errorMessage}`);
|
||||
res.json(
|
||||
errorResponse(errorMessage ?? 'Unknown', rootUrl(req), '/configure')
|
||||
);
|
||||
@@ -244,7 +249,7 @@ app.get('/:config/stream/:type/:id.json', (req, res: Response): void => {
|
||||
res.json({ streams: streams });
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error(`|ERR| server > Internal addon error: ${error.message}`);
|
||||
logger.error(`Internal addon error: ${error.message}`);
|
||||
res.json(
|
||||
errorResponse(
|
||||
'An unexpected error occurred, please check the logs or create an issue on GitHub',
|
||||
@@ -255,7 +260,7 @@ app.get('/:config/stream/:type/:id.json', (req, res: Response): void => {
|
||||
);
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error(`|ERR| server > Internal addon error: ${error.message}`);
|
||||
logger.error(`Internal addon error: ${error.message}`);
|
||||
res.json(
|
||||
errorResponse(
|
||||
'An unexpected error occurred, please check the logs or create an issue on GitHub',
|
||||
@@ -271,7 +276,7 @@ app.post('/encrypt-user-data', (req, res) => {
|
||||
const { data } = req.body;
|
||||
let finalString: string = '';
|
||||
if (!data) {
|
||||
console.error('|ERR| server > /encrypt-user-data: No data provided');
|
||||
logger.error('/encrypt-user-data: No data provided');
|
||||
res.json({ success: false, message: 'No data provided' });
|
||||
return;
|
||||
}
|
||||
@@ -280,16 +285,14 @@ app.post('/encrypt-user-data', (req, res) => {
|
||||
const config = JSON.parse(data);
|
||||
const { valid, errorCode, errorMessage } = validateConfig(config);
|
||||
if (!valid) {
|
||||
console.error(
|
||||
`|ERR| server > generateConfig: Invalid config: ${errorCode} - ${errorMessage}`
|
||||
logger.error(
|
||||
`generateConfig: Invalid config: ${errorCode} - ${errorMessage}`
|
||||
);
|
||||
res.json({ success: false, message: errorMessage, error: errorMessage });
|
||||
return;
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
`|ERR| server > /encrypt-user-data: Invalid JSON: ${error.message}`
|
||||
);
|
||||
logger.error(`/encrypt-user-data: Invalid JSON: ${error.message}`);
|
||||
res.json({ success: false, message: 'Malformed configuration' });
|
||||
return;
|
||||
}
|
||||
@@ -306,28 +309,28 @@ app.post('/encrypt-user-data', (req, res) => {
|
||||
finalString = `E2-${encodeURIComponent(iv)}-${encodeURIComponent(data)}`;
|
||||
}
|
||||
|
||||
console.log(
|
||||
logger.info(
|
||||
`|INF| server > /encrypt-user-data: Encrypted user data, compression report:`
|
||||
);
|
||||
console.log(`+--------------------------------------------+`);
|
||||
console.log(`| Original: ${data.length} bytes`);
|
||||
console.log(`| URL Encoded: ${encodeURIComponent(data).length} bytes`);
|
||||
console.log(`| Minified: ${JSON.stringify(minified).length} bytes`);
|
||||
console.log(`| Crushed: ${crushed.length} bytes`);
|
||||
console.log(`| Compressed: ${compressed.length} bytes`);
|
||||
console.log(`| Final String: ${finalString.length} bytes`);
|
||||
console.log(
|
||||
logger.info(`+--------------------------------------------+`);
|
||||
logger.info(`| Original: ${data.length} bytes`);
|
||||
logger.info(`| URL Encoded: ${encodeURIComponent(data).length} bytes`);
|
||||
logger.info(`| Minified: ${JSON.stringify(minified).length} bytes`);
|
||||
logger.info(`| Crushed: ${crushed.length} bytes`);
|
||||
logger.info(`| Compressed: ${compressed.length} bytes`);
|
||||
logger.info(`| Final String: ${finalString.length} bytes`);
|
||||
logger.info(
|
||||
`| Ratio: ${((finalString.length / data.length) * 100).toFixed(2)}%`
|
||||
);
|
||||
console.log(
|
||||
logger.info(
|
||||
`| Reduction: ${data.length - finalString.length} bytes (${(((data.length - finalString.length) / data.length) * 100).toFixed(2)}%)`
|
||||
);
|
||||
console.log(`+--------------------------------------------+`);
|
||||
logger.info(`+--------------------------------------------+`);
|
||||
|
||||
res.json({ success: true, data: finalString });
|
||||
} catch (error: any) {
|
||||
console.error(`|ERR| server > /encrypt-user-data: ${error.message}`);
|
||||
console.error(error);
|
||||
logger.error(`/encrypt-user-data: ${error.message}`);
|
||||
logger.error(error);
|
||||
res.json({ success: false, message: error.message });
|
||||
}
|
||||
});
|
||||
@@ -351,7 +354,7 @@ app.use((req, res) => {
|
||||
});
|
||||
|
||||
app.listen(Settings.PORT, () => {
|
||||
console.log(`|INF| server > init: Listening on port ${Settings.PORT}`);
|
||||
logger.info(`Listening on port ${Settings.PORT}`);
|
||||
});
|
||||
|
||||
function extractJsonConfig(config: string): Config {
|
||||
@@ -374,8 +377,8 @@ function extractJsonConfig(config: string): Config {
|
||||
function extractCustomConfig(config: string): Config | undefined {
|
||||
const customConfig = CUSTOM_CONFIGS?.[config];
|
||||
if (!customConfig) return undefined;
|
||||
console.log(
|
||||
`|DBG| server > Found custom config for alias ${config}, attempting to extract config`
|
||||
logger.info(
|
||||
`Found custom config for alias ${config}, attempting to extract config`
|
||||
);
|
||||
return extractEncryptedOrEncodedConfig(
|
||||
decodeURIComponent(customConfig),
|
||||
@@ -391,7 +394,7 @@ function extractEncryptedOrEncodedConfig(
|
||||
try {
|
||||
if (config.startsWith('E-')) {
|
||||
// compressed and encrypted (hex)
|
||||
console.log(`|DBG| server > Extracting encrypted (v1) config`);
|
||||
logger.info(`Extracting encrypted (v1) config`);
|
||||
const parts = config.split('-');
|
||||
if (parts.length !== 3) {
|
||||
throw new Error('Invalid encrypted config format');
|
||||
@@ -401,7 +404,7 @@ function extractEncryptedOrEncodedConfig(
|
||||
decodedConfig = JSON.parse(decompressData(decryptData(data, iv)));
|
||||
} else if (config.startsWith('E2-')) {
|
||||
// minified, crushed, compressed and encrypted (base64)
|
||||
console.log(`|DBG| server > Extracting encrypted (v2) config`);
|
||||
logger.info(`Extracting encrypted (v2) config`);
|
||||
const parts = config.split('-');
|
||||
if (parts.length !== 3) {
|
||||
throw new Error('Invalid encrypted config format');
|
||||
@@ -414,9 +417,7 @@ function extractEncryptedOrEncodedConfig(
|
||||
decodedConfig = unminifyConfig(JSON.parse(minifiedConfig));
|
||||
} else if (config.startsWith('B-')) {
|
||||
// minifed, crushed, compressed, base64 encoded
|
||||
console.log(
|
||||
`|DBG| server > Extracting base64 encoded and compressed config`
|
||||
);
|
||||
logger.info(`Extracting base64 encoded and compressed config`);
|
||||
decodedConfig = unminifyConfig(
|
||||
JSON.parse(
|
||||
uncrushJson(decompressData(Buffer.from(config.slice(2), 'base64')))
|
||||
@@ -424,15 +425,17 @@ function extractEncryptedOrEncodedConfig(
|
||||
);
|
||||
} else {
|
||||
// plain base64 encoded
|
||||
console.log(`|DBG| server > Extracting plain base64 encoded config`);
|
||||
logger.info(`Extracting plain base64 encoded config`);
|
||||
decodedConfig = JSON.parse(
|
||||
Buffer.from(config, 'base64').toString('utf-8')
|
||||
);
|
||||
}
|
||||
return decodedConfig;
|
||||
} catch (error: any) {
|
||||
console.error(`|ERR| Failed to parse ${label}: ${error.message}`);
|
||||
console.error(error);
|
||||
logger.error(`Failed to parse ${label}: ${error.message}`, {
|
||||
func: 'extractJsonConfig',
|
||||
});
|
||||
logger.error(error, { func: 'extractJsonConfig' });
|
||||
throw new Error(`Failed to parse ${label}`);
|
||||
}
|
||||
}
|
||||
@@ -556,7 +559,7 @@ function processObjectValues(
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const value = obj[key];
|
||||
if (condition(key, value)) {
|
||||
console.log(`|DBG| Processing ${labelPrefix} ${key}`);
|
||||
logger.debug(`Processing ${labelPrefix} ${key}`);
|
||||
obj[key] = decrypt
|
||||
? decryptValue(value, `${labelPrefix} ${key}`)
|
||||
: encryptValue(value, `${labelPrefix} ${key}`);
|
||||
@@ -570,7 +573,8 @@ function encryptValue(value: any, label: string): any {
|
||||
const { iv, data } = encryptData(compressData(value));
|
||||
return `E2-${iv}-${data}`;
|
||||
} catch (error: any) {
|
||||
console.error(`Failed to encrypt ${label}`);
|
||||
logger.error(`Failed to encrypt ${label}`, { func: 'encryptValue' });
|
||||
logger.error(error, { func: 'encryptValue' });
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -584,7 +588,10 @@ function decryptValue(value: any, label: string): any {
|
||||
if (decrypted === null) throw new Error('Decryption failed');
|
||||
return decrypted;
|
||||
} catch (error: any) {
|
||||
console.error(`|ERR| Failed to decrypt ${label}: ${error.message}`);
|
||||
logger.error(`Failed to decrypt ${label}: ${error.message}`, {
|
||||
func: 'decryptValue',
|
||||
});
|
||||
logger.error(error, { func: 'decryptValue' });
|
||||
throw new Error('Failed to decrypt config');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,4 @@
|
||||
"typescript": "^5.5.2",
|
||||
"wrangler": "^3.99.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,14 @@ const nextConfig: NextConfig = {
|
||||
env: {
|
||||
NEXT_PUBLIC_BRANDING: branding,
|
||||
},
|
||||
webpack(config) {
|
||||
config.resolve.fallback = {
|
||||
...config.resolve.fallback, // if you miss it, all the other options in fallback, specified
|
||||
// by next.js will be dropped. Doesn't make much sense, but how it is
|
||||
fs: false, // the solution
|
||||
};
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
@@ -69,6 +69,7 @@ const defaultVisualTags: VisualTag[] = [
|
||||
{ '3D': true },
|
||||
{ IMAX: true },
|
||||
{ AI: true },
|
||||
{ SDR: true },
|
||||
];
|
||||
|
||||
const defaultAudioTags: AudioTag[] = [
|
||||
@@ -487,6 +488,7 @@ export default function Configure() {
|
||||
|
||||
function loadFromConfig(decodedConfig: Config) {
|
||||
console.log('Loaded config', decodedConfig);
|
||||
setOverrideName(decodedConfig.overrideName || '');
|
||||
setStreamTypes(
|
||||
loadValidValuesFromObject(decodedConfig.streamTypes, defaultStreamTypes)
|
||||
);
|
||||
|
||||
@@ -45,6 +45,7 @@ export const PARSE_REGEX = {
|
||||
'3D': createRegex('(bd)?(3|three)[ .\\-_]?(d(imension)?(al)?)'),
|
||||
IMAX: createRegex('imax'),
|
||||
AI: createRegex('ai[ .\\-_]?(upscale|enhanced|remaster)?'),
|
||||
SDR: createRegex('sdr'),
|
||||
},
|
||||
audioTags: {
|
||||
Atmos: createRegex('atmos'),
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
},
|
||||
"description": "Library to parse a torrent filename ",
|
||||
"dependencies": {
|
||||
"jsoncrush": "^1.1.8"
|
||||
"jsoncrush": "^1.1.8",
|
||||
"winston": "^3.17.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,36 @@ import {
|
||||
import { deflateSync, inflateSync } from 'zlib';
|
||||
import { Settings } from './settings';
|
||||
import JSONCrush from 'jsoncrush';
|
||||
import { createLogger } from './logger';
|
||||
|
||||
const logger = createLogger('crypto');
|
||||
|
||||
export const loadSecretKey = (): Buffer | string => {
|
||||
const secretKey = Settings.SECRET_KEY;
|
||||
if (!secretKey) {
|
||||
console.error('No secret key provided');
|
||||
throw new Error('No secret key provided');
|
||||
}
|
||||
// must be 64 characters long and hex
|
||||
|
||||
if (secretKey.length === 32) {
|
||||
// backwards compatibility
|
||||
logger.warn(
|
||||
'Secret key is 32 characters long, consider updating to a 64 character key and reconfiguring for better security'
|
||||
);
|
||||
return secretKey;
|
||||
} else if (secretKey.length !== 64) {
|
||||
logger.error('Secret key must be 64 characters long');
|
||||
throw new Error('Secret key must be 64 characters long');
|
||||
}
|
||||
|
||||
if (!/^[0-9a-fA-F]+$/.test(secretKey)) {
|
||||
logger.error('Secret key must be a hex string (0-9, a-f)');
|
||||
throw new Error('Secret key must be a hex string (0-9, a-f)');
|
||||
}
|
||||
|
||||
return Buffer.from(secretKey, 'hex');
|
||||
};
|
||||
|
||||
const pad = (data: Buffer, blockSize: number): Buffer => {
|
||||
const padding = blockSize - (data.length % blockSize);
|
||||
@@ -37,11 +67,7 @@ export const decompressData = (data: Buffer): string => {
|
||||
};
|
||||
|
||||
export const encryptData = (data: Buffer): { iv: string; data: string } => {
|
||||
const secretKey = Settings.SECRET_KEY;
|
||||
if (!secretKey) {
|
||||
console.error('|ERR| crypto > compressAndEncrypt > No secret key provided');
|
||||
throw new Error('No secret key provided');
|
||||
}
|
||||
const secretKey = loadSecretKey();
|
||||
|
||||
// Then encrypt the compressed data
|
||||
const iv = randomBytes(16);
|
||||
@@ -61,13 +87,7 @@ export const encryptData = (data: Buffer): { iv: string; data: string } => {
|
||||
};
|
||||
|
||||
export const decryptData = (encryptedData: Buffer, iv: Buffer): Buffer => {
|
||||
const secretKey = Settings.SECRET_KEY;
|
||||
if (!secretKey) {
|
||||
console.error(
|
||||
'|ERR| crypto > decryptAndDecompress > No secret key provided'
|
||||
);
|
||||
throw new Error('No secret key provided');
|
||||
}
|
||||
const secretKey = loadSecretKey();
|
||||
const decipher = createDecipheriv('aes-256-cbc', secretKey, iv);
|
||||
|
||||
// Decrypt the data
|
||||
@@ -100,9 +120,7 @@ export function parseAndDecryptString(data: string): string | null {
|
||||
}
|
||||
return data;
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
`|ERR| crypto > parseAndDecryptString > Failed to decrypt data: ${error.message}`
|
||||
);
|
||||
logger.error(`Failed to decrypt data: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,3 +4,4 @@ export * from './settings';
|
||||
export * from './mediaflow';
|
||||
export * from './cache';
|
||||
export * from './general';
|
||||
export * from './logger';
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import winston from 'winston';
|
||||
import { Settings } from './settings';
|
||||
|
||||
// Map log levels to short labels and emojis
|
||||
const levelMap: { [key: string]: string } = {
|
||||
error: 'ERR',
|
||||
warn: 'WRN',
|
||||
info: 'INF',
|
||||
debug: 'DBG',
|
||||
};
|
||||
|
||||
const moduleMap: { [key: string]: string } = {
|
||||
server: '🌐 SERVER',
|
||||
wrappers: '📦 WRAPPERS',
|
||||
crypto: '🔒 CRYPTO',
|
||||
addon: '🧩 ADDON',
|
||||
parser: '🔍 PARSER',
|
||||
mediaflow: '🌊 MEDIAFLOW',
|
||||
};
|
||||
|
||||
// Define colors for each log level
|
||||
const levelColors: { [key: string]: string } = {
|
||||
ERR: 'red',
|
||||
WRN: 'yellow',
|
||||
INF: 'cyan',
|
||||
DBG: 'magenta',
|
||||
};
|
||||
|
||||
const emojiLevelMap: { [key: string]: string } = {
|
||||
error: '🛑',
|
||||
warn: '⚠️ ',
|
||||
info: '🔵',
|
||||
debug: '🐞',
|
||||
};
|
||||
|
||||
// Apply colors to Winston
|
||||
winston.addColors(levelColors);
|
||||
|
||||
export const createLogger = (module: string) => {
|
||||
// cloudflare workers do not play nice with winston
|
||||
// so we disable winston logs in cloudflare workers
|
||||
try {
|
||||
__dirname;
|
||||
} catch (e) {
|
||||
return console;
|
||||
}
|
||||
const isJsonFormat = Settings.LOG_FORMAT === 'json';
|
||||
return winston.createLogger({
|
||||
level: Settings.LOG_LEVEL || 'info', // Default to 'info' if not set
|
||||
format: isJsonFormat
|
||||
? winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.json() // Use JSON format when LOG_FORMAT=json
|
||||
)
|
||||
: winston.format.combine(
|
||||
winston.format.timestamp({
|
||||
format: 'YYYY-MM-DD HH:mm:ss.SSS',
|
||||
}),
|
||||
winston.format.printf(({ timestamp, level, message, func }) => {
|
||||
const emoji = emojiLevelMap[level] || '';
|
||||
const formattedModule = moduleMap[module] || module;
|
||||
level = levelMap[level] || level.toUpperCase();
|
||||
// Apply color to the level
|
||||
const coloredLevel = winston.format
|
||||
.colorize()
|
||||
.colorize(level, `${level}`);
|
||||
const formatLine = (line: unknown) => {
|
||||
return `${emoji} [${timestamp}] | ${coloredLevel} | ${formattedModule}${
|
||||
func ? ' (' + func + ')' : ''
|
||||
} > ${line}`;
|
||||
};
|
||||
if (typeof message === 'string') {
|
||||
return message.split('\n').map(formatLine).join('\n');
|
||||
}
|
||||
return formatLine(message);
|
||||
})
|
||||
),
|
||||
transports: [new winston.transports.Console()],
|
||||
});
|
||||
};
|
||||
@@ -3,6 +3,9 @@ import path from 'path';
|
||||
import { Settings } from './settings';
|
||||
import { getTextHash } from './crypto';
|
||||
import { Cache } from './cache';
|
||||
import { createLogger } from './logger';
|
||||
|
||||
const logger = createLogger('mediaflow');
|
||||
|
||||
const PRIVATE_CIDR = /^(10\.|127\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)/;
|
||||
|
||||
@@ -15,21 +18,15 @@ export function createProxiedMediaFlowUrl(
|
||||
}
|
||||
) {
|
||||
if (!url) {
|
||||
console.error(
|
||||
'|ERR| mediaflow > createProxiedMediaFlowUrl > streamUrl is missing, could not create proxied URL'
|
||||
);
|
||||
logger.error('streamUrl is missing, could not create proxied URL');
|
||||
throw new Error('Stream URL is missing');
|
||||
}
|
||||
if (!mediaFlowConfig) {
|
||||
console.error(
|
||||
'|ERR| mediaflow > createProxiedMediaFlowUrl > mediaFlowConfig is missing'
|
||||
);
|
||||
logger.error('mediaFlowConfig is missing');
|
||||
throw new Error('MediaFlow configuration is missing');
|
||||
}
|
||||
if (!mediaFlowConfig?.proxyUrl || !mediaFlowConfig?.apiPassword) {
|
||||
console.error(
|
||||
'|ERR| mediaflow > createProxiedMediaFlowUrl > mediaFlowUrl or API password is missing'
|
||||
);
|
||||
logger.error('mediaFlowUrl or API password is missing');
|
||||
throw new Error('MediaFlow URL or API password is missing');
|
||||
}
|
||||
|
||||
@@ -69,16 +66,12 @@ export async function getMediaFlowPublicIp(
|
||||
) {
|
||||
try {
|
||||
if (!mediaFlowConfig) {
|
||||
console.error(
|
||||
'|ERR| mediaflow > getMediaFlowPublicIp > mediaFlowConfig is missing'
|
||||
);
|
||||
logger.error('mediaFlowConfig is missing');
|
||||
throw new Error('MediaFlow configuration is missing');
|
||||
}
|
||||
|
||||
if (!mediaFlowConfig?.proxyUrl) {
|
||||
console.error(
|
||||
'|ERR| mediaflow > getMediaFlowPublicIp > mediaFlowUrl is missing'
|
||||
);
|
||||
logger.error('mediaFlowUrl is missing');
|
||||
throw new Error('MediaFlow URL is missing');
|
||||
}
|
||||
|
||||
@@ -89,8 +82,8 @@ export async function getMediaFlowPublicIp(
|
||||
const mediaFlowUrl = new URL(mediaFlowConfig.proxyUrl.replace(/\/$/, ''));
|
||||
if (PRIVATE_CIDR.test(mediaFlowUrl.hostname)) {
|
||||
// MediaFlow proxy URL is a private IP address
|
||||
console.debug(
|
||||
'|DBG| mediaflow > getMediaFlowPublicIp > MediaFlow proxy URL is a private IP address so returning null'
|
||||
logger.debug(
|
||||
'MediaFlow proxy URL is a private IP address so returning null'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -100,9 +93,7 @@ export async function getMediaFlowPublicIp(
|
||||
);
|
||||
const cachedPublicIp = cache ? cache.get(cacheKey) : null;
|
||||
if (cachedPublicIp) {
|
||||
console.debug(
|
||||
`|DBG| mediaflow > getMediaFlowPublicIp > Returning cached public IP`
|
||||
);
|
||||
logger.debug(`Returning cached public IP`);
|
||||
return cachedPublicIp;
|
||||
}
|
||||
|
||||
@@ -114,13 +105,9 @@ export async function getMediaFlowPublicIp(
|
||||
}).toString();
|
||||
|
||||
if (Settings.LOG_SENSITIVE_INFO) {
|
||||
console.debug(
|
||||
`|DBG| mediaflow > getMediaFlowPublicIp > GET ${proxyIpUrl.toString()}`
|
||||
);
|
||||
logger.debug(`GET ${proxyIpUrl.toString()}`);
|
||||
} else {
|
||||
console.debug(
|
||||
'|DBG| mediaflow > getMediaFlowPublicIp > GET /proxy/ip?api_password=***'
|
||||
);
|
||||
logger.debug('GET /proxy/ip?api_password=***');
|
||||
}
|
||||
|
||||
const response = await fetch(proxyIpUrl.toString(), {
|
||||
@@ -142,7 +129,7 @@ export async function getMediaFlowPublicIp(
|
||||
}
|
||||
return publicIp;
|
||||
} catch (error: any) {
|
||||
console.error(`|ERR| mediaflow > getMediaFlowPublicIp > ${error.message}`);
|
||||
logger.error(`${error.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import path from 'path';
|
||||
try {
|
||||
dotenv.config({ path: path.resolve(__dirname, '../../../.env') });
|
||||
} catch (error) {
|
||||
console.error('Error loading .env file:', error);
|
||||
console.error('Error loading .env file', error);
|
||||
}
|
||||
|
||||
export class Settings {
|
||||
@@ -40,6 +40,12 @@ export class Settings {
|
||||
public static readonly DISABLE_TORRENTIO_MESSAGE =
|
||||
process.env.DISABLE_TORRENTIO_MESSAGE ||
|
||||
'The Torrentio addon has been disabled, please remove it to use this addon.';
|
||||
public static readonly LOG_LEVEL = process.env.LOG_LEVEL || 'info';
|
||||
public static readonly LOG_FORMAT = process.env.LOG_FORMAT
|
||||
? process.env.LOG_FORMAT === 'json'
|
||||
? 'json'
|
||||
: 'text'
|
||||
: 'text';
|
||||
|
||||
// Cache settings
|
||||
public static readonly CACHE_STREAM_RESULTS = process.env.CACHE_STREAM_RESULTS
|
||||
|
||||
@@ -8,10 +8,17 @@ import {
|
||||
ParseResult,
|
||||
} from '@aiostreams/types';
|
||||
import { parseFilename } from '@aiostreams/parser';
|
||||
import { getTextHash, serviceDetails, Settings } from '@aiostreams/utils';
|
||||
import { fetch as uFetch, ProxyAgent } from 'undici';
|
||||
import {
|
||||
getTextHash,
|
||||
serviceDetails,
|
||||
Settings,
|
||||
createLogger,
|
||||
} from '@aiostreams/utils';
|
||||
// import { fetch as uFetch, ProxyAgent } from 'undici';
|
||||
import { emojiToLanguage, codeToLanguage } from '@aiostreams/formatters';
|
||||
|
||||
const logger = createLogger('wrappers');
|
||||
|
||||
export class BaseWrapper {
|
||||
private readonly streamPath: string = 'stream/{type}/{id}.json';
|
||||
private indexerTimeout: number;
|
||||
@@ -78,8 +85,10 @@ export class BaseWrapper {
|
||||
let hostname: string;
|
||||
try {
|
||||
hostname = new URL(url).hostname;
|
||||
} catch (e) {
|
||||
console.error(`|ERR| utils > shouldProxyRequest Error parsing URL`);
|
||||
} catch (e: any) {
|
||||
logger.error(`Error parsing URL: ${this.getLoggableUrl(url)}`, {
|
||||
func: 'shouldProxyRequest',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (!Settings.ADDON_PROXY) {
|
||||
@@ -90,8 +99,11 @@ export class BaseWrapper {
|
||||
for (const rule of Settings.ADDON_PROXY_CONFIG.split(',')) {
|
||||
const [ruleHost, enabled] = rule.split(':');
|
||||
if (['true', 'false'].includes(enabled) === false) {
|
||||
console.error(
|
||||
`|ERR| utils > shouldProxyRequest > Invalid rule: ${rule}`
|
||||
logger.error(
|
||||
`Invalid rule: ${rule}. Rule must be in the format host:enabled`,
|
||||
{
|
||||
func: 'shouldProxyRequest',
|
||||
}
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -111,7 +123,7 @@ export class BaseWrapper {
|
||||
return useProxy;
|
||||
}
|
||||
|
||||
private getLoggableUrl(url: string): string {
|
||||
protected getLoggableUrl(url: string): string {
|
||||
let urlObj = new URL(url);
|
||||
return `${urlObj.protocol}//${urlObj.hostname}/${urlObj.pathname
|
||||
.split('/')
|
||||
@@ -132,8 +144,8 @@ export class BaseWrapper {
|
||||
let sanitisedUrl = this.getLoggableUrl(url);
|
||||
let useProxy = this.shouldProxyRequest(url);
|
||||
|
||||
console.log(
|
||||
`|DBG| wrappers > base > ${this.addonName}: Making a ${useProxy ? 'proxied' : 'direct'} request to ${sanitisedUrl} with user IP ${
|
||||
logger.info(
|
||||
`Making a ${useProxy ? 'proxied' : 'direct'} request to ${this.addonName} (${sanitisedUrl}) with user IP ${
|
||||
userIp
|
||||
? Settings.LOG_SENSITIVE_INFO
|
||||
? userIp
|
||||
@@ -143,8 +155,8 @@ export class BaseWrapper {
|
||||
);
|
||||
|
||||
let response = useProxy
|
||||
? uFetch(url, {
|
||||
dispatcher: new ProxyAgent(Settings.ADDON_PROXY),
|
||||
? fetch(url, {
|
||||
// dispatcher: new ProxyAgent(Settings.ADDON_PROXY),
|
||||
method: 'GET',
|
||||
headers: headers,
|
||||
signal: AbortSignal.timeout(this.indexerTimeout),
|
||||
@@ -163,8 +175,8 @@ export class BaseWrapper {
|
||||
const requestCacheKey = getTextHash(url);
|
||||
const cachedStreams = cache ? cache.get(requestCacheKey) : undefined;
|
||||
if (cachedStreams) {
|
||||
console.debug(
|
||||
`|DBG| wrappers > base > ${this.addonName}: Returning cached streams for ${this.getLoggableUrl(url)}`
|
||||
logger.info(
|
||||
`Returning cached streams for ${this.addonName} (${this.getLoggableUrl(url)})`
|
||||
);
|
||||
return cachedStreams;
|
||||
}
|
||||
@@ -195,10 +207,10 @@ export class BaseWrapper {
|
||||
let message = error.message;
|
||||
if (error.name === 'TimeoutError') {
|
||||
message = `The stream request to ${this.addonName} timed out after ${this.indexerTimeout}ms`;
|
||||
return Promise.reject(new Error(message));
|
||||
return Promise.reject(message);
|
||||
}
|
||||
console.error(`|ERR| wrappers > base > ${this.addonName}: ${message}`);
|
||||
console.error(error);
|
||||
logger.error(`Error during fetch for ${this.addonName}: ${message}`);
|
||||
logger.error(error);
|
||||
return Promise.reject(error.message);
|
||||
}
|
||||
}
|
||||
@@ -274,8 +286,8 @@ export class BaseWrapper {
|
||||
errorRegex.test(stream.title || '') ||
|
||||
errorRegex.test(stream.description || '')
|
||||
) {
|
||||
console.log(
|
||||
`|ERR| wrappers > base > ${this.addonName}: ${stream.title || stream.description} was detected as an error`
|
||||
logger.debug(
|
||||
`Result from ${this.addonName} (${(stream.title || stream.description).split('\n').join(' ')}) was detected as an error`
|
||||
);
|
||||
return {
|
||||
type: 'error',
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { AddonDetail, ParseResult, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { addonDetails, createLogger } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
|
||||
const logger = createLogger('wrappers');
|
||||
|
||||
export class Comet extends BaseWrapper {
|
||||
constructor(
|
||||
configString: string | null,
|
||||
@@ -152,9 +154,7 @@ export async function getCometStreams(
|
||||
}
|
||||
const errorMessages: string[] = [];
|
||||
const streamPromises = servicesToUse.map(async (service) => {
|
||||
console.log(
|
||||
`|INF| wrappers > comet: Getting Comet streams for service: ${service.id}`
|
||||
);
|
||||
logger.info(`Getting Comet streams for ${service.id}`, { func: 'comet' });
|
||||
const cometConfig = getCometConfig(service.id, service.credentials.apiKey);
|
||||
const configString = Buffer.from(JSON.stringify(cometConfig)).toString(
|
||||
'base64'
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { AddonDetail, ParseResult, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { addonDetails, createLogger } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
|
||||
const logger = createLogger('wrappers');
|
||||
|
||||
export class Debridio extends BaseWrapper {
|
||||
constructor(
|
||||
configString: string | null,
|
||||
@@ -136,9 +138,9 @@ export async function getDebridioStreams(
|
||||
const addonErrors: string[] = [];
|
||||
|
||||
const streamPromises = servicesToUse.map(async (service) => {
|
||||
console.log(
|
||||
`|INF| wrappers > debridio: Getting Debridio streams for ${service.name}`
|
||||
);
|
||||
logger.info(`Getting Debridio streams for ${service.name}`, {
|
||||
func: 'debridio',
|
||||
});
|
||||
const debridioConfigString = getDebridioConfigString(
|
||||
service.id,
|
||||
service.credentials.apiKey
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { AddonDetail, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { addonDetails, createLogger } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
|
||||
const logger = createLogger('wrappers');
|
||||
|
||||
// name, title, url
|
||||
export class Jackettio extends BaseWrapper {
|
||||
constructor(
|
||||
@@ -156,9 +158,9 @@ export async function getJackettioStreams(
|
||||
}
|
||||
|
||||
const streamPromises = servicesToUse.map(async (service) => {
|
||||
console.log(
|
||||
`|INF| wrappers > jackettio: Getting Jackettio streams for ${service.name}`
|
||||
);
|
||||
logger.info(`Getting Jackettio streams for ${service.name}`, {
|
||||
func: 'jackettio',
|
||||
});
|
||||
const configString = getJackettioConfigString(
|
||||
service.id,
|
||||
service.credentials.apiKey
|
||||
|
||||
@@ -6,8 +6,11 @@ import {
|
||||
getTextHash,
|
||||
getTimeTakenSincePoint,
|
||||
Settings,
|
||||
createLogger,
|
||||
} from '@aiostreams/utils';
|
||||
|
||||
const logger = createLogger('wrappers');
|
||||
|
||||
export class MediaFusion extends BaseWrapper {
|
||||
constructor(
|
||||
configString: string | null,
|
||||
@@ -74,16 +77,19 @@ export async function getMediafusionStreams(
|
||||
const cacheKey = getTextHash(`mediafusionConfig:${JSON.stringify(data)}`);
|
||||
const cachedConfig = cache ? cache.get(cacheKey) : null;
|
||||
if (cachedConfig) {
|
||||
console.log(
|
||||
`|DBG| wrappers > mediafusion: Returning cached config string`
|
||||
);
|
||||
logger.info(`Returning cached config string`, {
|
||||
func: 'mediafusion.getConfigString',
|
||||
});
|
||||
return cachedConfig;
|
||||
}
|
||||
try {
|
||||
const encryptedStr = await _getConfigString(data);
|
||||
|
||||
console.log(
|
||||
`|INF| wrappers > mediafusion: Config encryption took ${getTimeTakenSincePoint(startTime)}`
|
||||
logger.info(
|
||||
`Config encryption took ${getTimeTakenSincePoint(startTime)}`,
|
||||
{
|
||||
func: 'mediafusion.getConfigString',
|
||||
}
|
||||
);
|
||||
cache?.set(cacheKey, encryptedStr, Settings.CACHE_MEDIAFUSION_CONFIG_TTL);
|
||||
return encryptedStr;
|
||||
@@ -198,9 +204,9 @@ export async function getMediafusionStreams(
|
||||
service.id,
|
||||
service.credentials
|
||||
);
|
||||
console.log(
|
||||
`|INF| wrappers > mediafusion: Getting MediaFusion streams for service: ${service.id}`
|
||||
);
|
||||
logger.info(`Getting MediaFusion streams for ${service.id}`, {
|
||||
func: 'mediafusion',
|
||||
});
|
||||
let encryptedStr: string = '';
|
||||
try {
|
||||
encryptedStr = await getConfigString(mediafusionConfig);
|
||||
@@ -210,7 +216,7 @@ export async function getMediafusionStreams(
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(`|ERR| wrappers > mediafusion: ${error.message}`);
|
||||
logger.error(`${error.message}`, { func: 'mediafusion' });
|
||||
throw new Error(error.message);
|
||||
}
|
||||
const mediafusion = new MediaFusion(
|
||||
|
||||
@@ -6,9 +6,11 @@ import {
|
||||
} from '@aiostreams/types';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { addonDetails, createLogger } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
|
||||
const logger = createLogger('wrappers');
|
||||
|
||||
// name, title, url
|
||||
export class OrionStremioAddon extends BaseWrapper {
|
||||
constructor(
|
||||
@@ -142,9 +144,7 @@ export async function getOrionStreams(
|
||||
|
||||
// otherwise, pass all the services to orion
|
||||
const debridServices = usableServices.map((service) => service.id);
|
||||
console.log(
|
||||
`|DBG| wrappers > orion > using debrid services: ${debridServices}`
|
||||
);
|
||||
logger.info(`Using Orion with debrid services: ${debridServices}`);
|
||||
const configString = getOrionConfigString(
|
||||
orionApiKey,
|
||||
orionOptions.linkLimit,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { AddonDetail, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { addonDetails, createLogger } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
|
||||
const logger = createLogger('wrappers');
|
||||
|
||||
export class Peerflix extends BaseWrapper {
|
||||
constructor(
|
||||
configString: string | null,
|
||||
@@ -46,7 +48,6 @@ export async function getPeerflixStreams(
|
||||
const indexerTimeout = peerflixOptions.indexerTimeout
|
||||
? parseInt(peerflixOptions.indexerTimeout)
|
||||
: undefined;
|
||||
console.log(JSON.stringify(peerflixOptions));
|
||||
// If overrideUrl is provided, use it to get streams and skip all other steps
|
||||
if (peerflixOptions.overrideUrl) {
|
||||
const peerflix = new Peerflix(
|
||||
@@ -64,8 +65,9 @@ export async function getPeerflixStreams(
|
||||
const usableServices = config.services.filter(
|
||||
(service) => supportedServices.includes(service.id) && service.enabled
|
||||
);
|
||||
console.log(
|
||||
`|DBG| wrappers > peerflix: Found ${usableServices.length} usable services: ${usableServices.map((service) => service.id).join(', ')}`
|
||||
logger.debug(
|
||||
`Found ${usableServices.length} usable services: ${usableServices.map((service) => service.id).join(', ')}`,
|
||||
{ func: 'peerflix' }
|
||||
);
|
||||
|
||||
// if no usable services found, use peerflix without any configuration
|
||||
@@ -95,9 +97,9 @@ export async function getPeerflixStreams(
|
||||
if (peerflixOptions.useMultipleInstances === 'true') {
|
||||
let retrievedP2PStreams = false;
|
||||
const promises = usableServices.map(async (service) => {
|
||||
console.log(
|
||||
`|DBG| wrappers > peerflix: Getting Peerflix streams for ${service.name}`
|
||||
);
|
||||
logger.info(`Getting Peerflix streams for ${service.name}`, {
|
||||
func: 'peerflix',
|
||||
});
|
||||
let configPairs = [getServicePair(service.id, service.credentials)];
|
||||
if (peerflixOptions.showP2PStreams === 'true' && !retrievedP2PStreams) {
|
||||
configPairs.push(['debridoptions', 'torrentlinks']);
|
||||
|
||||
@@ -6,9 +6,11 @@ import {
|
||||
} from '@aiostreams/types';
|
||||
import { ParsedStream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { addonDetails, createLogger } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
|
||||
const logger = createLogger('wrappers');
|
||||
|
||||
// name, title, url
|
||||
export class StremioJackett extends BaseWrapper {
|
||||
constructor(
|
||||
@@ -61,8 +63,10 @@ export class StremioJackett extends BaseWrapper {
|
||||
url.pathname = components.join('/');
|
||||
}
|
||||
}
|
||||
console.error(`
|
||||
|ERR| wrappers > stremioJackett: Error parsing stream config for ${url}`);
|
||||
logger.error(
|
||||
`Error parsing stream config for playback URL: ${this.getLoggableUrl(url.toString())}`,
|
||||
{ func: 'stremio-jackett' }
|
||||
);
|
||||
}
|
||||
}
|
||||
return parseResult;
|
||||
@@ -201,9 +205,9 @@ export async function getStremioJackettStreams(
|
||||
}
|
||||
|
||||
const streamPromises = servicesToUse.map(async (service) => {
|
||||
console.log(
|
||||
`|INF| wrappers > stremioJackett: Getting StremioJackett streams for ${service.name}`
|
||||
);
|
||||
logger.info(`Getting Stremio-Jackett streams for ${service.name}`, {
|
||||
func: 'stremio-jackett',
|
||||
});
|
||||
const configString = getStremioJackettConfigString(
|
||||
service.id,
|
||||
service.credentials.apiKey,
|
||||
|
||||
@@ -8,7 +8,9 @@ import {
|
||||
StreamRequest,
|
||||
} from '@aiostreams/types';
|
||||
import { parseFilename } from '@aiostreams/parser';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
import { createLogger, Settings } from '@aiostreams/utils';
|
||||
|
||||
const logger = createLogger('wrappers');
|
||||
|
||||
interface TorboxStream extends Stream {
|
||||
name: string;
|
||||
@@ -49,9 +51,9 @@ export class Torbox extends BaseWrapper {
|
||||
let type = stream.type;
|
||||
let personal = false;
|
||||
if (stream.name.includes('Your Media')) {
|
||||
console.log(
|
||||
`|INF| wrappers > torbox > detected personal stream in ${stream.name}`
|
||||
);
|
||||
logger.debug(`${stream.name} was detected as a personal stream.`, {
|
||||
func: 'torbox',
|
||||
});
|
||||
personal = true;
|
||||
}
|
||||
const [dQuality, dFilename, dSize, dLanguage, dAgeOrSeeders] =
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { AddonDetail, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { addonDetails, createLogger } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
|
||||
const logger = createLogger('wrappers');
|
||||
|
||||
export class Torrentio extends BaseWrapper {
|
||||
constructor(
|
||||
configString: string | null,
|
||||
@@ -90,9 +92,9 @@ export async function getTorrentioStreams(
|
||||
|
||||
if (torrentioOptions.useMultipleInstances === 'true') {
|
||||
const promises = usableServices.map(async (service) => {
|
||||
console.log(
|
||||
`|INF| wrappers > torrentio: Getting Torrentio streams for ${service.name}`
|
||||
);
|
||||
logger.info(`Getting Torrentio streams for ${service.name}`, {
|
||||
func: 'torrentio',
|
||||
});
|
||||
let configString = getServicePair(service.id, service.credentials);
|
||||
const torrentio = new Torrentio(
|
||||
configString,
|
||||
|
||||
Reference in New Issue
Block a user