diff options
author | morpheus65535 <[email protected]> | 2021-12-13 20:05:13 -0500 |
---|---|---|
committer | morpheus65535 <[email protected]> | 2021-12-13 20:05:13 -0500 |
commit | f38d03ce86723f46edcd50ef7fa78133ee6e337e (patch) | |
tree | f0080265e9045bb63e3c67cba5aa0c90fab6ee30 | |
parent | 8f58b8b6c70d4b05c1824dfbf72348288993e2c4 (diff) | |
parent | 3cc0d4bac4bcd643bf87a76fab9216052e05961d (diff) | |
download | bazarr-f38d03ce86723f46edcd50ef7fa78133ee6e337e.tar.gz bazarr-f38d03ce86723f46edcd50ef7fa78133ee6e337e.zip |
Merge remote-tracking branch 'origin/development' into developmentv1.0.2-beta.7
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | bazarr/config.py | 3 | ||||
-rw-r--r-- | bazarr/get_providers.py | 7 | ||||
-rw-r--r-- | frontend/package-lock.json | 851 | ||||
-rw-r--r-- | frontend/package.json | 1 | ||||
-rw-r--r-- | frontend/src/Settings/Providers/list.ts | 11 | ||||
-rwxr-xr-x | libs/fese/__init__.py | 401 | ||||
-rw-r--r-- | libs/subliminal/subtitle.py | 2 | ||||
-rw-r--r-- | libs/subliminal_patch/core.py | 19 | ||||
-rw-r--r-- | libs/subliminal_patch/providers/embeddedsubtitles.py | 162 | ||||
-rw-r--r-- | tests/subliminal_patch/data/file_1.mkv | bin | 0 -> 646450 bytes | |||
-rw-r--r-- | tests/subliminal_patch/data/file_2.mkv | bin | 0 -> 1248441 bytes | |||
-rw-r--r-- | tests/subliminal_patch/test_embeddedsubtitles.py | 115 |
13 files changed, 717 insertions, 856 deletions
@@ -46,6 +46,7 @@ If you need something that is not already part of Bazarr, feel free to create a * Assrt * BetaSeries * BSplayer +* Embedded Subtitles * GreekSubtitles * Hosszupuska * LegendasDivx diff --git a/bazarr/config.py b/bazarr/config.py index 78af8b39c..c27931344 100644 --- a/bazarr/config.py +++ b/bazarr/config.py @@ -190,6 +190,9 @@ defaults = { 'approved_only': 'False', 'multithreading': 'True' }, + 'embeddedsubtitles': { + 'include_ass': 'True', + }, 'subsync': { 'use_subsync': 'False', 'use_subsync_threshold': 'False', diff --git a/bazarr/get_providers.py b/bazarr/get_providers.py index bf230931e..9e1d652f6 100644 --- a/bazarr/get_providers.py +++ b/bazarr/get_providers.py @@ -13,6 +13,7 @@ import ast from get_args import args from config import settings, get_array_from from event_handler import event_stream +from utils import get_binary from subliminal_patch.exceptions import TooManyRequests, APIThrottled, ParseResponseError, IPAddressBlocked from subliminal.providers.opensubtitles import DownloadLimitReached from subliminal.exceptions import DownloadLimitExceeded, ServiceUnavailable @@ -198,6 +199,12 @@ def get_providers_auth(): 'email': settings.ktuvit.email, 'hashed_password': settings.ktuvit.hashed_password, }, + 'embeddedsubtitles': { + 'include_ass': settings.embeddedsubtitles.getboolean('include_ass'), + 'cache_dir': os.path.join(args.config_dir, "cache"), + 'ffprobe_path': get_binary("ffprobe"), + 'ffmpeg_path': get_binary("ffmpeg"), + } } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1f062865a..45fe602f6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,7 +20,6 @@ "bootstrap": "^4", "lodash": "^4", "moment": "^2.29.1", - "package.json": "^2.0.1", "rc-slider": "^9.7", "react": "^17", "react-bootstrap": "^1", @@ -4220,14 +4219,6 @@ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" }, - "node_modules/abs": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/abs/-/abs-1.3.14.tgz", - "integrity": "sha512-PrS26IzwKLWwuURpiKl8wRmJ2KdR/azaVrLEBWG/TALwT20Y7qjtYp1qcMLHA4206hBHY5phv3w4pjf9NPv4Vw==", - "dependencies": { - "ul": "^5.0.0" - } - }, "node_modules/accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -5891,14 +5882,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", @@ -6580,17 +6563,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, - "node_modules/create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "dependencies": { - "capture-stack-trace": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -7216,14 +7188,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -7357,14 +7321,6 @@ "which": "bin/which" } }, - "node_modules/deffy": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/deffy/-/deffy-2.2.4.tgz", - "integrity": "sha512-pLc9lsbsWjr6RxmJ2OLyvm+9l4j1yK69h+TML/gUit/t3vTijpkNGh8LioaJYTGO7F25m6HZndADcUOo2PsiUg==", - "dependencies": { - "typpy": "^2.0.0" - } - }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -7739,14 +7695,6 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dependencies": { - "readable-stream": "^2.0.2" - } - }, "node_modules/duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -7948,14 +7896,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/err": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/err/-/err-1.1.1.tgz", - "integrity": "sha1-65KOLhGjFmSPeCgz0PlyWLpDwvg=", - "dependencies": { - "typpy": "^2.2.0" - } - }, "node_modules/errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -9098,15 +9038,6 @@ "safe-buffer": "^5.1.1" } }, - "node_modules/exec-limiter": { - "version": "3.2.13", - "resolved": "https://registry.npmjs.org/exec-limiter/-/exec-limiter-3.2.13.tgz", - "integrity": "sha512-86Ri699bwiHZVBzTzNj8gspqAhCPchg70zPVWIh3qzUOA1pUMcb272Em3LPk8AE0mS95B9yMJhtqF8vFJAn0dA==", - "dependencies": { - "limit-it": "^3.0.0", - "typpy": "^2.1.0" - } - }, "node_modules/exec-sh": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", @@ -9960,14 +9891,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "node_modules/function.name": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/function.name/-/function.name-1.0.13.tgz", - "integrity": "sha512-mVrqdoy5npWZyoXl4DxCeuVF6delDcQjVS9aPdvLYlBxtMTZDR2B5GVEQEoM1jJyspCqg3C0v4ABkLE7tp9xFA==", - "dependencies": { - "noop6": "^1.0.1" - } - }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -10037,47 +9960,6 @@ "node": ">=0.10.0" } }, - "node_modules/git-package-json": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/git-package-json/-/git-package-json-1.4.10.tgz", - "integrity": "sha512-DRAcvbzd2SxGK7w8OgYfvKqhFliT5keX0lmSmVdgScgf1kkl5tbbo7Pam6uYoCa1liOiipKxQZG8quCtGWl/fA==", - "dependencies": { - "deffy": "^2.2.1", - "err": "^1.1.1", - "gry": "^5.0.0", - "normalize-package-data": "^2.3.5", - "oargv": "^3.4.1", - "one-by-one": "^3.1.0", - "r-json": "^1.2.1", - "r-package-json": "^1.0.0", - "tmp": "0.0.28" - } - }, - "node_modules/git-source": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/git-source/-/git-source-1.1.10.tgz", - "integrity": "sha512-XZZ7ZgnLL35oLgM/xjnLYgtlKlxJG0FohC1kWDvGkU7s1VKGXK0pFF/g1itQEwQ3D+uTQzBnzPi8XbqOv7Wc1Q==", - "dependencies": { - "git-url-parse": "^5.0.1" - } - }, - "node_modules/git-up": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-1.2.1.tgz", - "integrity": "sha1-JkSAoAax2EJhrB/gmjpRacV+oZ0=", - "dependencies": { - "is-ssh": "^1.0.0", - "parse-url": "^1.0.0" - } - }, - "node_modules/git-url-parse": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-5.0.1.tgz", - "integrity": "sha1-/j15xnRq4FBIz6UIyB553du6OEM=", - "dependencies": { - "git-up": "^1.0.0" - } - }, "node_modules/glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -10170,51 +10052,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/got": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-5.6.0.tgz", - "integrity": "sha1-ux1+4WO3gIK7yOuDbz85UATqb78=", - "dependencies": { - "create-error-class": "^3.0.1", - "duplexer2": "^0.1.4", - "is-plain-obj": "^1.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "node-status-codes": "^1.0.0", - "object-assign": "^4.0.1", - "parse-json": "^2.1.0", - "pinkie-promise": "^2.0.0", - "read-all-stream": "^3.0.0", - "readable-stream": "^2.0.5", - "timed-out": "^2.0.0", - "unzip-response": "^1.0.0", - "url-parse-lax": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/got/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/got/node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", @@ -10226,17 +10063,6 @@ "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "optional": true }, - "node_modules/gry": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/gry/-/gry-5.0.8.tgz", - "integrity": "sha512-meq9ZjYVpLzZh3ojhTg7IMad9grGsx6rUUKHLqPnhLXzJkRQvEL2U3tQpS5/WentYTtHtxkT3Ew/mb10D6F6/g==", - "dependencies": { - "abs": "^1.2.1", - "exec-limiter": "^3.0.0", - "one-by-one": "^3.0.0", - "ul": "^5.0.0" - } - }, "node_modules/gzip-size": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", @@ -11454,14 +11280,6 @@ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" }, - "node_modules/is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -11490,14 +11308,6 @@ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" }, - "node_modules/is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-root": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", @@ -11506,14 +11316,6 @@ "node": ">=6" } }, - "node_modules/is-ssh": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", - "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", - "dependencies": { - "protocols": "^1.1.0" - } - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -11714,11 +11516,6 @@ "node": ">=8" } }, - "node_modules/iterate-object": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/iterate-object/-/iterate-object-1.3.4.tgz", - "integrity": "sha512-4dG1D1x/7g8PwHS9aK6QV5V94+ZvyP4+d19qDv43EzImmrndysIl4prmJ1hWWIGCqrZHyaHBm6BSEWHOLnpoNw==" - }, "node_modules/jest": { "version": "26.6.0", "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.0.tgz", @@ -13433,14 +13230,6 @@ "node": ">= 0.8.0" } }, - "node_modules/limit-it": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/limit-it/-/limit-it-3.2.10.tgz", - "integrity": "sha512-T0NK99pHnkimldr1WUqvbGV1oWDku/xC9J/OqzJFsV1jeOS6Bwl8W7vkeQIBqwiON9dTALws+rX/XPMQqWerDQ==", - "dependencies": { - "typpy": "^2.0.0" - } - }, "node_modules/lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -13602,14 +13391,6 @@ "tslib": "^2.0.3" } }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -14255,19 +14036,6 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==" }, - "node_modules/node-status-codes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", - "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/noop6": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/noop6/-/noop6-1.0.9.tgz", - "integrity": "sha512-DB3Hwyd89dPr5HqEPg3YHjzvwh/mCqizC1zZ8vyofqc+TQRyPDnT4wgXXbLGF4z9YAzwwTLi8pNLhGqcbSjgkA==" - }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -14349,23 +14117,6 @@ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" }, - "node_modules/oargv": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/oargv/-/oargv-3.4.10.tgz", - "integrity": "sha512-SXaMANv9sr7S/dP0vj0+Ybipa47UE1ntTWQ2rpPRhC6Bsvfl+Jg03Xif7jfL0sWKOYWK8oPjcZ5eJ82t8AP/8g==", - "dependencies": { - "iterate-object": "^1.1.0", - "ul": "^5.0.0" - } - }, - "node_modules/obj-def": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/obj-def/-/obj-def-1.0.9.tgz", - "integrity": "sha512-bQ4ya3VYD6FAA1+s6mEhaURRHSmw4+sKaXE6UyXZ1XDYc5D+c7look25dFdydmLd18epUegh398gdDkMUZI9xg==", - "dependencies": { - "deffy": "^2.2.2" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -14616,15 +14367,6 @@ "wrappy": "1" } }, - "node_modules/one-by-one": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/one-by-one/-/one-by-one-3.2.8.tgz", - "integrity": "sha512-HR/pSzZdm46Xqj58K+Bu64kMbSTw8/u77AwWvV+rprO/OsuR++pPlkUJn+SmwqBGRgHKwSKQ974V3uls7crIeQ==", - "dependencies": { - "obj-def": "^1.0.0", - "sliced": "^1.0.1" - } - }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -14714,14 +14456,6 @@ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -14788,47 +14522,6 @@ "node": ">=6" } }, - "node_modules/package-json": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", - "integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=", - "dependencies": { - "got": "^5.0.0", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/package-json-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/package-json-path/-/package-json-path-1.0.9.tgz", - "integrity": "sha512-uNu7f6Ef7tQHZRnkyVnCtzdSYVN9uBtge/sG7wzcUaawFWkPYUq67iXxRGrQSg/q0tzxIB8jSyIYUKjG2Jn//A==", - "dependencies": { - "abs": "^1.2.1" - } - }, - "node_modules/package-json/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/package.json": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/package.json/-/package.json-2.0.1.tgz", - "integrity": "sha1-+IYFnSpJ7QduZIg2ldc7K0bSHW0=", - "deprecated": "Use pkg.json instead.", - "dependencies": { - "git-package-json": "^1.4.0", - "git-source": "^1.1.0", - "package-json": "^2.3.1" - } - }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -14893,15 +14586,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-url": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-1.3.11.tgz", - "integrity": "sha1-V8FUKKuKiSsfQ4aWRccR0OFEtVQ=", - "dependencies": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0" - } - }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -16542,9 +16226,9 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/pretty-quick": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.1.tgz", - "integrity": "sha512-ZYLGiMoV2jcaas3vTJrLvKAYsxDoXQBUn8OSTxkl67Fyov9lyXivJTl0+2WVh+y6EovGcw7Lm5ThYpH+Sh3XxQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.2.tgz", + "integrity": "sha512-T+fpTJrDjTzewql4p3lKrRA7z3MrNyjBK1MKeaBm5PpKwATgVm885TpY7TgY8KFt5Q1Qn3QDseRQcyX9AKTKkA==", "dev": true, "dependencies": { "chalk": "^3.0.0", @@ -16697,11 +16381,6 @@ "react": ">=0.14.0" } }, - "node_modules/protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==" - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -16848,20 +16527,6 @@ } ] }, - "node_modules/r-json": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/r-json/-/r-json-1.2.10.tgz", - "integrity": "sha512-hu9vyLjSlHXT62NAS7DjI9WazDlvjN0lgp3n431dCVnirVcLkZIpzSwA3orhZEKzdDD2jqNYI+w0yG0aFf4kpA==" - }, - "node_modules/r-package-json": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/r-package-json/-/r-package-json-1.0.9.tgz", - "integrity": "sha512-G4Vpf1KImWmmPFGdtWQTU0L9zk0SjqEC4qs/jE7AQ+Ylmr5kizMzGeC4wnHp5+ijPqNN+2ZPpvyjVNdN1CDVcg==", - "dependencies": { - "package-json-path": "^1.0.0", - "r-json": "^1.2.1" - } - }, "node_modules/raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -16917,20 +16582,6 @@ "node": ">= 0.8" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, "node_modules/rc-align": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-4.0.9.tgz", @@ -17026,14 +16677,6 @@ "react-dom": ">=16.9.0" } }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", @@ -17737,18 +17380,6 @@ "react-dom": ">=16.6.0" } }, - "node_modules/read-all-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", - "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", - "dependencies": { - "pinkie-promise": "^2.0.0", - "readable-stream": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -18055,26 +17686,6 @@ "node": ">=4" } }, - "node_modules/registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", - "dependencies": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "dependencies": { - "rc": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/regjsgen": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", @@ -19099,11 +18710,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/sliced": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", - "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" - }, "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -20514,14 +20120,6 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, - "node_modules/timed-out": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-2.0.0.tgz", - "integrity": "sha1-84sK6B03R9YoAB9B2vxlKs5nHAo=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -20548,17 +20146,6 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, - "node_modules/tmp": { - "version": "0.0.28", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz", - "integrity": "sha1-Fyc1t/YU6nrzlmT6hM8N5OUV0SA=", - "dependencies": { - "os-tmpdir": "~1.0.1" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -20802,23 +20389,6 @@ "node": ">=4.2.0" } }, - "node_modules/typpy": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/typpy/-/typpy-2.3.13.tgz", - "integrity": "sha512-vOxIcQz9sxHi+rT09SJ5aDgVgrPppQjwnnayTrMye1ODaU8gIZTDM19t9TxmEElbMihx2Nq/0/b/MtyKfayRqA==", - "dependencies": { - "function.name": "^1.0.3" - } - }, - "node_modules/ul": { - "version": "5.2.15", - "resolved": "https://registry.npmjs.org/ul/-/ul-5.2.15.tgz", - "integrity": "sha512-svLEUy8xSCip5IWnsRa0UOg+2zP0Wsj4qlbjTmX6GJSmvKMHADBuHOm1dpNkWqWPIGuVSqzUkV3Cris5JrlTRQ==", - "dependencies": { - "deffy": "^2.2.2", - "typpy": "^2.3.4" - } - }, "node_modules/unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -21012,14 +20582,6 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "node_modules/unzip-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", - "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=", - "engines": { - "node": ">=0.10" - } - }, "node_modules/upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", @@ -21104,17 +20666,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "dependencies": { - "prepend-http": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/url/node_modules/punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", @@ -25871,14 +25422,6 @@ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" }, - "abs": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/abs/-/abs-1.3.14.tgz", - "integrity": "sha512-PrS26IzwKLWwuURpiKl8wRmJ2KdR/azaVrLEBWG/TALwT20Y7qjtYp1qcMLHA4206hBHY5phv3w4pjf9NPv4Vw==", - "requires": { - "ul": "^5.0.0" - } - }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -27187,11 +26730,6 @@ "rsvp": "^4.8.4" } }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==" - }, "case-sensitive-paths-webpack-plugin": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", @@ -27741,14 +27279,6 @@ } } }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, "create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -28256,11 +27786,6 @@ "regexp.prototype.flags": "^1.2.0" } }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -28360,14 +27885,6 @@ } } }, - "deffy": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/deffy/-/deffy-2.2.4.tgz", - "integrity": "sha512-pLc9lsbsWjr6RxmJ2OLyvm+9l4j1yK69h+TML/gUit/t3vTijpkNGh8LioaJYTGO7F25m6HZndADcUOo2PsiUg==", - "requires": { - "typpy": "^2.0.0" - } - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -28663,14 +28180,6 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "requires": { - "readable-stream": "^2.0.2" - } - }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -28825,14 +28334,6 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" }, - "err": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/err/-/err-1.1.1.tgz", - "integrity": "sha1-65KOLhGjFmSPeCgz0PlyWLpDwvg=", - "requires": { - "typpy": "^2.2.0" - } - }, "errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -29637,15 +29138,6 @@ "safe-buffer": "^5.1.1" } }, - "exec-limiter": { - "version": "3.2.13", - "resolved": "https://registry.npmjs.org/exec-limiter/-/exec-limiter-3.2.13.tgz", - "integrity": "sha512-86Ri699bwiHZVBzTzNj8gspqAhCPchg70zPVWIh3qzUOA1pUMcb272Em3LPk8AE0mS95B9yMJhtqF8vFJAn0dA==", - "requires": { - "limit-it": "^3.0.0", - "typpy": "^2.1.0" - } - }, "exec-sh": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", @@ -30314,14 +29806,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "function.name": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/function.name/-/function.name-1.0.13.tgz", - "integrity": "sha512-mVrqdoy5npWZyoXl4DxCeuVF6delDcQjVS9aPdvLYlBxtMTZDR2B5GVEQEoM1jJyspCqg3C0v4ABkLE7tp9xFA==", - "requires": { - "noop6": "^1.0.1" - } - }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -30370,47 +29854,6 @@ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, - "git-package-json": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/git-package-json/-/git-package-json-1.4.10.tgz", - "integrity": "sha512-DRAcvbzd2SxGK7w8OgYfvKqhFliT5keX0lmSmVdgScgf1kkl5tbbo7Pam6uYoCa1liOiipKxQZG8quCtGWl/fA==", - "requires": { - "deffy": "^2.2.1", - "err": "^1.1.1", - "gry": "^5.0.0", - "normalize-package-data": "^2.3.5", - "oargv": "^3.4.1", - "one-by-one": "^3.1.0", - "r-json": "^1.2.1", - "r-package-json": "^1.0.0", - "tmp": "0.0.28" - } - }, - "git-source": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/git-source/-/git-source-1.1.10.tgz", - "integrity": "sha512-XZZ7ZgnLL35oLgM/xjnLYgtlKlxJG0FohC1kWDvGkU7s1VKGXK0pFF/g1itQEwQ3D+uTQzBnzPi8XbqOv7Wc1Q==", - "requires": { - "git-url-parse": "^5.0.1" - } - }, - "git-up": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-1.2.1.tgz", - "integrity": "sha1-JkSAoAax2EJhrB/gmjpRacV+oZ0=", - "requires": { - "is-ssh": "^1.0.0", - "parse-url": "^1.0.0" - } - }, - "git-url-parse": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-5.0.1.tgz", - "integrity": "sha1-/j15xnRq4FBIz6UIyB553du6OEM=", - "requires": { - "git-up": "^1.0.0" - } - }, "glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -30478,44 +29921,6 @@ "slash": "^3.0.0" } }, - "got": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-5.6.0.tgz", - "integrity": "sha1-ux1+4WO3gIK7yOuDbz85UATqb78=", - "requires": { - "create-error-class": "^3.0.1", - "duplexer2": "^0.1.4", - "is-plain-obj": "^1.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "node-status-codes": "^1.0.0", - "object-assign": "^4.0.1", - "parse-json": "^2.1.0", - "pinkie-promise": "^2.0.0", - "read-all-stream": "^3.0.0", - "readable-stream": "^2.0.5", - "timed-out": "^2.0.0", - "unzip-response": "^1.0.0", - "url-parse-lax": "^1.0.0" - }, - "dependencies": { - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "requires": { - "error-ex": "^1.2.0" - } - } - } - }, "graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", @@ -30527,17 +29932,6 @@ "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "optional": true }, - "gry": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/gry/-/gry-5.0.8.tgz", - "integrity": "sha512-meq9ZjYVpLzZh3ojhTg7IMad9grGsx6rUUKHLqPnhLXzJkRQvEL2U3tQpS5/WentYTtHtxkT3Ew/mb10D6F6/g==", - "requires": { - "abs": "^1.2.1", - "exec-limiter": "^3.0.0", - "one-by-one": "^3.0.0", - "ul": "^5.0.0" - } - }, "gzip-size": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", @@ -31426,11 +30820,6 @@ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" - }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -31450,24 +30839,11 @@ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" - }, "is-root": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" }, - "is-ssh": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz", - "integrity": "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==", - "requires": { - "protocols": "^1.1.0" - } - }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -31609,11 +30985,6 @@ "istanbul-lib-report": "^3.0.0" } }, - "iterate-object": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/iterate-object/-/iterate-object-1.3.4.tgz", - "integrity": "sha512-4dG1D1x/7g8PwHS9aK6QV5V94+ZvyP4+d19qDv43EzImmrndysIl4prmJ1hWWIGCqrZHyaHBm6BSEWHOLnpoNw==" - }, "jest": { "version": "26.6.0", "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.0.tgz", @@ -32911,14 +32282,6 @@ "type-check": "~0.4.0" } }, - "limit-it": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/limit-it/-/limit-it-3.2.10.tgz", - "integrity": "sha512-T0NK99pHnkimldr1WUqvbGV1oWDku/xC9J/OqzJFsV1jeOS6Bwl8W7vkeQIBqwiON9dTALws+rX/XPMQqWerDQ==", - "requires": { - "typpy": "^2.0.0" - } - }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -33054,11 +32417,6 @@ "tslib": "^2.0.3" } }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -33586,16 +32944,6 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==" }, - "node-status-codes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", - "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=" - }, - "noop6": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/noop6/-/noop6-1.0.9.tgz", - "integrity": "sha512-DB3Hwyd89dPr5HqEPg3YHjzvwh/mCqizC1zZ8vyofqc+TQRyPDnT4wgXXbLGF4z9YAzwwTLi8pNLhGqcbSjgkA==" - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -33661,23 +33009,6 @@ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" }, - "oargv": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/oargv/-/oargv-3.4.10.tgz", - "integrity": "sha512-SXaMANv9sr7S/dP0vj0+Ybipa47UE1ntTWQ2rpPRhC6Bsvfl+Jg03Xif7jfL0sWKOYWK8oPjcZ5eJ82t8AP/8g==", - "requires": { - "iterate-object": "^1.1.0", - "ul": "^5.0.0" - } - }, - "obj-def": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/obj-def/-/obj-def-1.0.9.tgz", - "integrity": "sha512-bQ4ya3VYD6FAA1+s6mEhaURRHSmw4+sKaXE6UyXZ1XDYc5D+c7look25dFdydmLd18epUegh398gdDkMUZI9xg==", - "requires": { - "deffy": "^2.2.2" - } - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -33857,15 +33188,6 @@ "wrappy": "1" } }, - "one-by-one": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/one-by-one/-/one-by-one-3.2.8.tgz", - "integrity": "sha512-HR/pSzZdm46Xqj58K+Bu64kMbSTw8/u77AwWvV+rprO/OsuR++pPlkUJn+SmwqBGRgHKwSKQ974V3uls7crIeQ==", - "requires": { - "obj-def": "^1.0.0", - "sliced": "^1.0.1" - } - }, "onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -33933,11 +33255,6 @@ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, "p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -33977,42 +33294,6 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, - "package-json": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", - "integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=", - "requires": { - "got": "^5.0.0", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "package-json-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/package-json-path/-/package-json-path-1.0.9.tgz", - "integrity": "sha512-uNu7f6Ef7tQHZRnkyVnCtzdSYVN9uBtge/sG7wzcUaawFWkPYUq67iXxRGrQSg/q0tzxIB8jSyIYUKjG2Jn//A==", - "requires": { - "abs": "^1.2.1" - } - }, - "package.json": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/package.json/-/package.json-2.0.1.tgz", - "integrity": "sha1-+IYFnSpJ7QduZIg2ldc7K0bSHW0=", - "requires": { - "git-package-json": "^1.4.0", - "git-source": "^1.1.0", - "package-json": "^2.3.1" - } - }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -34068,15 +33349,6 @@ "lines-and-columns": "^1.1.6" } }, - "parse-url": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-1.3.11.tgz", - "integrity": "sha1-V8FUKKuKiSsfQ4aWRccR0OFEtVQ=", - "requires": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0" - } - }, "parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -35399,9 +34671,9 @@ } }, "pretty-quick": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.1.tgz", - "integrity": "sha512-ZYLGiMoV2jcaas3vTJrLvKAYsxDoXQBUn8OSTxkl67Fyov9lyXivJTl0+2WVh+y6EovGcw7Lm5ThYpH+Sh3XxQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.2.tgz", + "integrity": "sha512-T+fpTJrDjTzewql4p3lKrRA7z3MrNyjBK1MKeaBm5PpKwATgVm885TpY7TgY8KFt5Q1Qn3QDseRQcyX9AKTKkA==", "dev": true, "requires": { "chalk": "^3.0.0", @@ -35517,11 +34789,6 @@ "warning": "^4.0.0" } }, - "protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==" - }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -35635,20 +34902,6 @@ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, - "r-json": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/r-json/-/r-json-1.2.10.tgz", - "integrity": "sha512-hu9vyLjSlHXT62NAS7DjI9WazDlvjN0lgp3n431dCVnirVcLkZIpzSwA3orhZEKzdDD2jqNYI+w0yG0aFf4kpA==" - }, - "r-package-json": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/r-package-json/-/r-package-json-1.0.9.tgz", - "integrity": "sha512-G4Vpf1KImWmmPFGdtWQTU0L9zk0SjqEC4qs/jE7AQ+Ylmr5kizMzGeC4wnHp5+ijPqNN+2ZPpvyjVNdN1CDVcg==", - "requires": { - "package-json-path": "^1.0.0", - "r-json": "^1.2.1" - } - }, "raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -35697,24 +34950,6 @@ } } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - } - } - }, "rc-align": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-4.0.9.tgz", @@ -36320,15 +35555,6 @@ "prop-types": "^15.6.2" } }, - "read-all-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", - "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", - "requires": { - "pinkie-promise": "^2.0.0", - "readable-stream": "^2.0.0" - } - }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -36584,23 +35810,6 @@ "unicode-match-property-value-ecmascript": "^1.2.0" } }, - "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "requires": { - "rc": "^1.0.1" - } - }, "regjsgen": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", @@ -37398,11 +36607,6 @@ "is-fullwidth-code-point": "^3.0.0" } }, - "sliced": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", - "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -38515,11 +37719,6 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, - "timed-out": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-2.0.0.tgz", - "integrity": "sha1-84sK6B03R9YoAB9B2vxlKs5nHAo=" - }, "timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -38543,14 +37742,6 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, - "tmp": { - "version": "0.0.28", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz", - "integrity": "sha1-Fyc1t/YU6nrzlmT6hM8N5OUV0SA=", - "requires": { - "os-tmpdir": "~1.0.1" - } - }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -38736,23 +37927,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==" }, - "typpy": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/typpy/-/typpy-2.3.13.tgz", - "integrity": "sha512-vOxIcQz9sxHi+rT09SJ5aDgVgrPppQjwnnayTrMye1ODaU8gIZTDM19t9TxmEElbMihx2Nq/0/b/MtyKfayRqA==", - "requires": { - "function.name": "^1.0.3" - } - }, - "ul": { - "version": "5.2.15", - "resolved": "https://registry.npmjs.org/ul/-/ul-5.2.15.tgz", - "integrity": "sha512-svLEUy8xSCip5IWnsRa0UOg+2zP0Wsj4qlbjTmX6GJSmvKMHADBuHOm1dpNkWqWPIGuVSqzUkV3Cris5JrlTRQ==", - "requires": { - "deffy": "^2.2.2", - "typpy": "^2.3.4" - } - }, "unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -38907,11 +38081,6 @@ } } }, - "unzip-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", - "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=" - }, "upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", @@ -38982,14 +38151,6 @@ "requires-port": "^1.0.0" } }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "requires": { - "prepend-http": "^1.0.1" - } - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 0ebd83d4a..4ec88d834 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,6 @@ "bootstrap": "^4", "lodash": "^4", "moment": "^2.29.1", - "package.json": "^2.0.1", "rc-slider": "^9.7", "react": "^17", "react-bootstrap": "^1", diff --git a/frontend/src/Settings/Providers/list.ts b/frontend/src/Settings/Providers/list.ts index dc076eb1c..c5defab07 100644 --- a/frontend/src/Settings/Providers/list.ts +++ b/frontend/src/Settings/Providers/list.ts @@ -47,6 +47,17 @@ export const ProviderList: Readonly<ProviderInfo[]> = [ name: "BSplayer", }, { + key: "embeddedsubtitles", + name: "Embedded Subtitles", + description: "Embedded Subtitles from your Media Files", + defaultKey: { + include_ass: true, + }, + keyNameOverride: { + include_ass: "Convert embedded ASS to SRT", + }, + }, + { key: "greeksubs", name: "GreekSubs", description: "Greek Subtitles Provider", diff --git a/libs/fese/__init__.py b/libs/fese/__init__.py new file mode 100755 index 000000000..b1024b8ff --- /dev/null +++ b/libs/fese/__init__.py @@ -0,0 +1,401 @@ +# -*- coding: utf-8 -*- +# License: GPL + +from __future__ import annotations + +import json +import logging +import os +import re +import subprocess +from typing import List, Optional + +from babelfish import Language +from babelfish.exceptions import LanguageError +import pysubs2 + +__version__ = "0.1.0" + +logger = logging.getLogger(__name__) + +# Paths to executables +FFPROBE_PATH = os.environ.get("FFPROBE_PATH", "ffprobe") +FFMPEG_PATH = os.environ.get("FFMPEG_PATH", "ffmpeg") + +FFMPEG_STATS = True +FF_LOG_LEVEL = "quiet" + + +class FeseError(Exception): + pass + + +class ExtractionError(FeseError): + pass + + +class InvalidFile(FeseError): + pass + + +class InvalidStream(FeseError): + pass + + +class InvalidSource(FeseError): + pass + + +class ConversionError(FeseError): + pass + + +class LanguageNotFound(FeseError): + pass + + +# Extensions + +SRT = "srt" +ASS = "ass" + + +class FFprobeSubtitleDisposition: + def __init__(self, data: dict): + self.default = False + self.generic = False + self.dub = False + self.original = False + self.comment = False + self.lyrics = False + self.karaoke = False + self.forced = False + self.hearing_impaired = False + self.visual_impaired = False + self.clean_effects = False + self.attached_pic = False + self.timed_thumbnails = False + self._content_type = None + + for key, val in data.items(): + if hasattr(self, key): + setattr(self, key, bool(val)) + + def update_from_tags(self, tags): + tag_title = tags.get("title") + if tag_title is None: + logger.debug("Title not found. Marking as generic") + self.generic = True + return None + + l_tag_title = tag_title.lower() + + for key, val in _content_types.items(): + if val.search(l_tag_title) is not None: + logger.debug("Found %s: %s", key, l_tag_title) + self._content_type = key + setattr(self, key, True) + return None + + logger.debug("Generic disposition title found: %s", l_tag_title) + self.generic = True + return None + + @property + def suffix(self): + if self._content_type is not None: + return f"-{self._content_type}" + + return "" + + def __str__(self): + return self.suffix.lstrip("-").upper() or "GENERIC" + + +class FFprobeSubtitleStream: + """Base class for FFprobe (FFmpeg) extractable subtitle streams.""" + + def __init__(self, stream: dict): + """ + :raises: LanguageNotFound + """ + self.index = int(stream.get("index", 0)) + self.codec_name = stream.get("codec_name", "Unknown") + self.extension = _subtitle_extensions.get(self.codec_name, self.codec_name) + self.r_frame_rate = stream.get("r_frame_rate") + self.avg_frame_rate = stream.get("avg_frame_rate") + self.time_base = stream.get("time_base") + self.tags = stream.get("tags", {}) + self.duration = float(stream.get("duration", 0)) + self.start_time = float(stream.get("start_time", 0)) + self.duration_ts = int(stream.get("duration_ts", 0)) + self.start_pts = int(stream.get("start_pts", 0)) + + self.disposition = FFprobeSubtitleDisposition(stream.get("disposition", {})) + + if self.tags: + self.disposition.update_from_tags(self.tags) + + self.language: Language = self._language() + + @property + def suffix(self): + lang = self.language.alpha2 + if self.language.country is not None: + lang = f"{lang}-{self.language.country}" + + return f"{lang}{self.disposition.suffix}.{self.extension}" + + def _language(self) -> Language: + og_lang = self.tags.get("language") + + if og_lang is not None: + if og_lang in _extra_languages: + extra = _extra_languages[og_lang] + title = self.tags.get("title", "n/a").lower() + if any(possible in title for possible in extra["matches"]): + logger.debug("Found extra language %s", extra["language_args"]) + return Language(*extra["language_args"]) + + try: + return Language.fromalpha3b(og_lang) + except LanguageError as error: + logger.debug("Error with '%s' language: %s", og_lang, error) + + raise LanguageNotFound(f"Couldn't detect language for stream: {self.tags}") + + def __repr__(self) -> str: + return f"<{self.codec_name.upper()}: {self.language}@{self.disposition}>" + + +# Helpers + + +class FFprobeVideoContainer: + def __init__(self, path: str): + self.path = path + + @property + def extension(self): + return os.path.splitext(self.path)[-1].lstrip(".") + + def get_subtitles(self, timeout: int = 600) -> List[FFprobeSubtitleStream]: + """Factory function to create subtitle instances from FFprobe. + + :param timeout: subprocess timeout in seconds (default: 600) + :raises: InvalidSource""" + + ff_command = [ + FFPROBE_PATH, + "-v", + FF_LOG_LEVEL, + "-print_format", + "json", + "-show_format", + "-show_streams", + self.path, + ] + try: + result = subprocess.run( + ff_command, stdout=subprocess.PIPE, check=True, timeout=timeout + ) + streams = json.loads(result.stdout)["streams"] + except _ffprobe_exceptions as error: + raise InvalidSource( + f"{error} trying to get information from {self.path}" + ) from error # We want to see the traceback + + subs = [] + for stream in streams: + if stream.get("codec_type", "n/a") != "subtitle": + continue + try: + subs.append(FFprobeSubtitleStream(stream)) + except LanguageNotFound: + pass + + if not subs: + logger.debug("Source doesn't have any subtitle valid streams") + return [] + + logger.debug("Found subtitle streams: %s", subs) + return subs + + def extract_subtitles( + self, + subtitles: List[FFprobeSubtitleStream], + custom_dir=None, + overwrite=True, + timeout=600, + ): + """Extracts a list of subtitles. Returns a dictionary of the extracted + filenames by index. + + :param subtitles: a list of FFprobeSubtitle instances + :param custom_dir: a custom directory to save the subtitles. Defaults to + same directory as the media file + :param overwrite: overwrite files with the same name (default: True) + :param timeout: subprocess timeout in seconds (default: 600) + :raises: ExtractionError, OSError + """ + extract_command = [FFMPEG_PATH, "-v", FF_LOG_LEVEL] + if FFMPEG_STATS: + extract_command.append("-stats") + extract_command.extend(["-y", "-i", self.path]) + + if custom_dir is not None: + # May raise OSError + os.makedirs(custom_dir, exist_ok=True) + + items = {} + collected_paths = set() + + for subtitle in subtitles: + sub_path = f"{os.path.splitext(self.path)[0]}.{subtitle.suffix}" + if custom_dir is not None: + sub_path = os.path.join(custom_dir, os.path.basename(sub_path)) + + if sub_path in collected_paths: + sub_path = ( + f"{sub_path.rstrip(f'.{subtitle.suffix}')}" + f"-{len(collected_paths)}.{subtitle.suffix}" + ) + + if not overwrite and os.path.isfile(sub_path): + logger.debug("Ignoring path (OVERWRITE TRUE): %s", sub_path) + continue + + extract_command.extend( + ["-map", f"0:{subtitle.index}", "-c", "copy", sub_path] + ) + logger.debug("Appending subtitle path: %s", sub_path) + + collected_paths.add(sub_path) + + items[subtitle.index] = sub_path + + if not items: + logger.debug("No subtitles to extract") + return {} + + logger.debug("Extracting subtitle with command %s", " ".join(extract_command)) + + try: + subprocess.run(extract_command, timeout=timeout, check=True) + except (subprocess.SubprocessError, FileNotFoundError) as error: + raise ExtractionError(f"Error calling ffmpeg: {error}") from error + + for path in items.values(): + if not os.path.isfile(path): + logger.debug("%s was not extracted", path) + + return items + + def __repr__(self) -> str: + return f"<FFprobeVideoContainer {self.extension}: {self.path}>" + + +def check_integrity( + subtitle: FFprobeSubtitleStream, path: str, sec_offset_threshold=900 +): + """A relative check for the integriy of a file. This can be used to find a failed + ffmpeg extraction where the final file might not be complete or might be corrupted. + Currently, only ASS and Subrip are supported. + + :param subtitle: FFprobeSubtitle instance + :param path: the path of the subtitle file (ass or srt) + :param sec_offset_threshold: the maximum seconds offset to determine if the file is complete + :raises: InvalidFile + """ + if subtitle.extension not in (ASS, SRT): + raise InvalidFile(f"Extension not supported: {subtitle.extension}") + + try: + sub = pysubs2.load(path) + except (pysubs2.Pysubs2Error, UnicodeError, OSError, FileNotFoundError) as error: + raise InvalidFile(error) from error + else: + off = abs(int(sub[-1].end) - subtitle.duration_ts) + if off > abs(sec_offset_threshold) * 1000: + raise InvalidFile( + f"The last subtitle timestamp ({sub[-1].end/1000} sec) is {off/1000} sec ahead" + f" from the subtitle stream total duration ({subtitle.duration} sec)" + ) + + logger.debug("Integrity check passed (%d sec offset)", off / 1000) + + +def to_srt( + source: str, output: Optional[str] = None, remove_source: bool = False +) -> str: + """Convert a subtitle to SubRip. Currently, only ASS is supported. SubRip + files will be silently ignored. + + raises: ConversionError, OSError""" + if source.endswith(".srt"): + return source + + split_path = os.path.splitext(source) + + if split_path[-1] not in (".ass"): + raise ConversionError( + f"No converter found for extension: {split_path[-1]}" + ) from None + + output = output or f"{split_path[0]}.srt" + + try: + parsed = pysubs2.load(source) + parsed.save(output) + except (pysubs2.Pysubs2Error, UnicodeError) as error: + raise ConversionError(f"Exception converting {output}: {error}") from error + + logger.debug("Converted: %s", output) + + if remove_source and source != output: + try: + os.remove(source) + except OSError as error: + logger.debug("Can't remove source: %s (%s)", source, error) + + return output + + +_subtitle_extensions = {"subrip": "srt", "ass": "ass"} + + +_content_types = { + "hearing_impaired": re.compile(r"sdh|hearing impaired"), + "forced": re.compile(r"forced"), + "comment": re.compile(r"comment"), + "visual_impaired": re.compile(r"signs|visual impair"), + "karaoke": re.compile(r"karaoke|songs"), +} + + +_ffprobe_exceptions = ( + subprocess.SubprocessError, + json.JSONDecodeError, + FileNotFoundError, + KeyError, +) + +_extra_languages = { + "spa": { + "matches": ( + "es-la", + "spa-la", + "spl", + "mx", + "latin", + "mexic", + "argent", + "latam", + ), + "language_args": ("spa", "MX"), + }, + "por": { + "matches": ("pt-br", "pob", "pb", "brazilian", "brasil", "brazil"), + "language_args": ("por", "BR"), + }, +} diff --git a/libs/subliminal/subtitle.py b/libs/subliminal/subtitle.py index 792df1a1c..2f30f43da 100644 --- a/libs/subliminal/subtitle.py +++ b/libs/subliminal/subtitle.py @@ -50,6 +50,8 @@ class Subtitle(object): #: Encoding to decode with when accessing :attr:`text` self.encoding = None + self.release_info = None + # validate the encoding if encoding: try: diff --git a/libs/subliminal_patch/core.py b/libs/subliminal_patch/core.py index aa82db1f9..f4605b1eb 100644 --- a/libs/subliminal_patch/core.py +++ b/libs/subliminal_patch/core.py @@ -187,16 +187,15 @@ class SZProviderPool(ProviderPool): if (str(provider), str(s.id)) in self.blacklist: logger.info("Skipping blacklisted subtitle: %s", s) continue - if hasattr(s, 'release_info'): - if s.release_info is not None: - if any([x for x in self.ban_list["must_not_contain"] - if re.search(x, s.release_info, flags=re.IGNORECASE) is not None]): - logger.info("Skipping subtitle because release name contains prohibited string: %s", s) - continue - if any([x for x in self.ban_list["must_contain"] - if re.search(x, s.release_info, flags=re.IGNORECASE) is None]): - logger.info("Skipping subtitle because release name does not contains required string: %s", s) - continue + if s.release_info is not None: + if any([x for x in self.ban_list["must_not_contain"] + if re.search(x, s.release_info, flags=re.IGNORECASE) is not None]): + logger.info("Skipping subtitle because release name contains prohibited string: %s", s) + continue + if any([x for x in self.ban_list["must_contain"] + if re.search(x, s.release_info, flags=re.IGNORECASE) is None]): + logger.info("Skipping subtitle because release name does not contains required string: %s", s) + continue if s.id in seen: continue s.plex_media_fps = float(video.fps) if video.fps else None diff --git a/libs/subliminal_patch/providers/embeddedsubtitles.py b/libs/subliminal_patch/providers/embeddedsubtitles.py new file mode 100644 index 000000000..bf89340c5 --- /dev/null +++ b/libs/subliminal_patch/providers/embeddedsubtitles.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- + +import logging +import os +import shutil +import tempfile + +from babelfish import language_converters +import fese +from fese import check_integrity +from fese import FFprobeSubtitleStream +from fese import FFprobeVideoContainer +from fese import to_srt +from subliminal.subtitle import fix_line_ending +from subliminal_patch.core import Episode +from subliminal_patch.core import Movie +from subliminal_patch.providers import Provider +from subliminal_patch.subtitle import Subtitle +from subzero.language import Language + +logger = logging.getLogger(__name__) + +# Replace Babelfish's Language with Subzero's Language +fese.Language = Language + + +class EmbeddedSubtitle(Subtitle): + provider_name = "embeddedsubtitles" + hash_verifiable = False + + def __init__(self, stream, container, matches): + super().__init__(stream.language, stream.disposition.hearing_impaired) + self.stream: FFprobeSubtitleStream = stream + self.container: FFprobeVideoContainer = container + self._matches: set = matches + self.page_link = self.container.path + self.release_info = os.path.basename(self.page_link) + + def get_matches(self, video): + if self.hearing_impaired: + self._matches.add("hearing_impaired") + + self._matches.add("hash") + return self._matches + + @property + def id(self): + return f"{self.container.path}_{self.stream.index}" + + +class EmbeddedSubtitlesProvider(Provider): + provider_name = "embeddedsubtitles" + + languages = {Language("por", "BR"), Language("spa", "MX")} | { + Language.fromalpha2(l) for l in language_converters["alpha2"].codes + } + languages.update(set(Language.rebuild(lang, hi=True) for lang in languages)) + + # TODO: add forced support + # languages.update(set(Language.rebuild(lang, forced=True) for lang in languages)) + + video_types = (Episode, Movie) + subtitle_class = EmbeddedSubtitle + + def __init__( + self, include_ass=True, cache_dir=None, ffprobe_path=None, ffmpeg_path=None + ): + self._include_ass = include_ass + self._cache_dir = os.path.join( + cache_dir or tempfile.gettempdir(), self.__class__.__name__.lower() + ) + self._cached_paths = {} + + fese.FFPROBE_PATH = ffprobe_path or fese.FFPROBE_PATH + fese.FFMPEG_PATH = ffmpeg_path or fese.FFMPEG_PATH + + if logger.getEffectiveLevel() == logging.DEBUG: + fese.FF_LOG_LEVEL = "warning" + else: + # Default is True + fese.FFMPEG_STATS = False + + def initialize(self): + os.makedirs(self._cache_dir, exist_ok=True) + + def terminate(self): + # Remove leftovers + shutil.rmtree(self._cache_dir, ignore_errors=True) + + def query(self, path: str, languages): + video = FFprobeVideoContainer(path) + + try: + streams = video.get_subtitles() + except fese.InvalidSource as error: + logger.error("Error trying to get subtitles for %s: %s", video, error) + streams = [] + + if not streams: + logger.debug("No subtitles found for container: %s", video) + + subtitles = [] + + for stream in streams: + # Only subrip and ass are currently supported + if stream.codec_name not in ("subrip", "ass"): + logger.debug("Ignoring codec: %s", stream) + continue + + if not self._include_ass and stream.codec_name == "ass": + logger.debug("Ignoring ASS subtitle: %s", stream) + continue + + if stream.language not in languages: + continue + + disposition = stream.disposition + if disposition.generic or disposition.hearing_impaired: + logger.debug("Appending subtitle: %s", stream) + subtitles.append(EmbeddedSubtitle(stream, video, {"hash"})) + else: + logger.debug("Ignoring unwanted subtitle: %s", stream) + + return subtitles + + def list_subtitles(self, video, languages): + return self.query(video.name, languages) + + def download_subtitle(self, subtitle): + path = self._get_subtitle_path(subtitle) + with open(path, "rb") as sub: + content = sub.read() + subtitle.content = fix_line_ending(content) + + def _get_subtitle_path(self, subtitle: EmbeddedSubtitle): + container = subtitle.container + + # Check if the container is not already in the instance + if container.path not in self._cached_paths: + # Extract all subittle streams to avoid reading the entire + # container over and over + streams = filter(_check_allowed_extensions, container.get_subtitles()) + extracted = container.extract_subtitles(list(streams), self._cache_dir) + # Add the extracted paths to the containter path key + self._cached_paths[container.path] = extracted + + cached_path = self._cached_paths[container.path] + # Get the subtitle file by index + subtitle_path = cached_path[subtitle.stream.index] + + check_integrity(subtitle.stream, subtitle_path) + + # Convert to SRT if the subtitle is ASS + new_subtitle_path = to_srt(subtitle_path, remove_source=True) + if new_subtitle_path != subtitle_path: + cached_path[subtitle.stream.index] = new_subtitle_path + + return new_subtitle_path + + +def _check_allowed_extensions(subtitle: FFprobeSubtitleStream): + return subtitle.extension in ("ass", "srt") diff --git a/tests/subliminal_patch/data/file_1.mkv b/tests/subliminal_patch/data/file_1.mkv Binary files differnew file mode 100644 index 000000000..61112e0c2 --- /dev/null +++ b/tests/subliminal_patch/data/file_1.mkv diff --git a/tests/subliminal_patch/data/file_2.mkv b/tests/subliminal_patch/data/file_2.mkv Binary files differnew file mode 100644 index 000000000..eefabffe9 --- /dev/null +++ b/tests/subliminal_patch/data/file_2.mkv diff --git a/tests/subliminal_patch/test_embeddedsubtitles.py b/tests/subliminal_patch/test_embeddedsubtitles.py new file mode 100644 index 000000000..59ffaf0f2 --- /dev/null +++ b/tests/subliminal_patch/test_embeddedsubtitles.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +import os + +import fese +import pytest +from subliminal_patch.core import Episode, Movie +from subliminal_patch.providers.embeddedsubtitles import EmbeddedSubtitlesProvider +from subzero.language import Language + +_DATA = os.path.join(os.path.abspath(os.path.dirname(__file__)), "data") + + +fese.Language = Language + + +def video_single_language(): + # Has only ASS streams in english + return Episode( + os.path.join(_DATA, "file_1.mkv"), + "Serial Experiments Lain", + 1, + 1, + source="Web", + ) + + +def video_multiple_languages(): + # Has SubRip streams in multiple languages + return Movie( + os.path.join(_DATA, "file_2.mkv"), + "I'm No Longer Here", + year=2019, + source="Web", + ) + + +def video_inexistent(tmpdir): + return Movie( + os.path.join(tmpdir, "inexistent_video.mkv"), + "Dummy", + year=2021, + source="Web", + ) + + +def test_inexistent_video(video_inexistent): + with EmbeddedSubtitlesProvider() as provider: + subtitles = provider.list_subtitles(video_inexistent, {}) + assert len(subtitles) == 0 + + +def test_list_subtitles_single_language(video_single_language): + with EmbeddedSubtitlesProvider() as provider: + subs = provider.list_subtitles( + video_single_language, {Language.fromalpha2("en")} + ) + + for sub in subs: + assert sub.language == Language.fromalpha2("en") + + +def test_list_subtitles_multiple_languages(video_multiple_languages): + with EmbeddedSubtitlesProvider() as provider: + languages = {Language.fromalpha2(code) for code in ("en", "it", "fr", "es")} | { + Language("por", "BR") + } + + subs = provider.list_subtitles(video_multiple_languages, languages) + for expected in languages: + assert any(sub.language == expected for sub in subs) + + +def test_list_subtitles_wo_ass(video_single_language): + with EmbeddedSubtitlesProvider(include_ass=False) as provider: + subs = provider.list_subtitles( + video_single_language, {Language.fromalpha2("en")} + ) + assert not subs + + +def test_download_subtitle_multiple(video_multiple_languages): + with EmbeddedSubtitlesProvider() as provider: + languages = {Language.fromalpha2(code) for code in ("en", "it", "fr")} | { + Language("por", "BR") + } + + subs = provider.list_subtitles(video_multiple_languages, languages) + for sub in subs: + provider.download_subtitle(sub) + assert sub.content is not None + + +def test_download_subtitle_single(video_single_language): + with EmbeddedSubtitlesProvider() as provider: + subtitle = provider.list_subtitles( + video_single_language, {Language.fromalpha2("en")} + )[0] + provider.download_subtitle(subtitle) + assert subtitle.content is not None + + +def test_download_invalid_subtitle(video_single_language): + with EmbeddedSubtitlesProvider() as provider: + subtitle = provider.list_subtitles( + video_single_language, {Language.fromalpha2("en")} + )[0] + + provider._cached_paths[subtitle.container.path] = { + subtitle.stream.index: "dummy.srt" + } + with pytest.raises(fese.InvalidFile): + provider.download_subtitle(subtitle) |