├── .babelrc.js
├── .github
└── FUNDING.yml
├── .gitignore
├── .npmignore
├── .sgcrc
├── .snyk
├── .travis.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── __mocks__
├── filehound.js
└── fs.js
├── __tests__
├── __helpers__
│ └── _constants.ts
├── getters
│ ├── allFilesWithCategory.ts
│ ├── allMovies.ts
│ ├── allTvSeries.ts
│ ├── allTvSeriesNames.ts
│ └── constants.ts
└── methods
│ ├── addNewPath.ts
│ ├── createFromJSON.ts
│ ├── filterMovies.ts
│ ├── filterTvSeries.ts
│ ├── removeOldFiles.ts
│ ├── scan.ts
│ ├── toJSON.ts
│ └── toJSONObject.ts
├── config
├── release-rules.js
└── release.config.js
├── index.js
├── jest.config.js
├── package-lock.json
├── package.json
├── src
├── MediaScan.ts
├── MediaScanTypes.ts
├── filters
│ ├── filterBooleanProperty.ts
│ ├── filterNumberProperty.ts
│ ├── filterProperties.ts
│ └── filterStringProperty.ts
└── utils
│ └── utils_functions.ts
├── tsconfig.jest.json
├── tsconfig.json
└── tslint.json
/.babelrc.js:
--------------------------------------------------------------------------------
1 | // var env = process.env.BABEL_ENV || process.env.NODE_ENV; // Maybe later : for minify stuff
2 | var presets = [
3 | ["minify", {
4 | "mangle": {
5 | "keepClassName": true
6 | }
7 | }],
8 | ["@babel/env", { loose: true, "targets": { "node": 8 } } ],
9 | "@babel/preset-typescript"
10 | ];
11 | var plugins = [
12 | "@babel/plugin-proposal-class-properties",
13 | "@babel/proposal-object-rest-spread"
14 | ];
15 | var ignore = [];
16 | var comments = false;
17 |
18 | module.exports = {
19 | presets: presets,
20 | plugins: plugins,
21 | ignore: ignore,
22 | comments: comments
23 | };
24 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | custom: ['https://www.buymeacoffee.com/GPFR'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
4 | #github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
5 | #patreon: # Replace with a single Patreon username
6 | #open_collective: # Replace with a single Open Collective username
7 | #ko_fi: # Replace with a single Ko-fi username
8 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
9 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
10 | #liberapay: # Replace with a single Liberapay username
11 | #issuehunt: # Replace with a single IssueHunt username
12 | #otechie: # Replace with a single Otechie username
13 | #lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # babel src to lib
61 | lib
62 |
63 | # intellij files
64 | *.iml
65 | .idea
66 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # uncompiled files
2 | src
3 | # jest unit test folders
4 | __mocks__
5 | __tests__
--------------------------------------------------------------------------------
/.sgcrc:
--------------------------------------------------------------------------------
1 | {
2 | "scope": false,
3 | "body": true,
4 | "emoji": true,
5 | "lowercaseTypes": false,
6 | "initial-commit": {
7 | "isEnabled": true,
8 | "emoji": ":tada:",
9 | "message": "Initial commit"
10 | },
11 | "types": [
12 | {
13 | "emoji": ":wrench:",
14 | "type": "Chore:",
15 | "description": "Changes that affect the build system or external dependencies and moving files"
16 | },
17 | {
18 | "emoji": ":construction_worker:",
19 | "type": "CI:",
20 | "description": "Changes to our CI configuration files and scripts"
21 | },
22 | {
23 | "emoji": ":memo:",
24 | "type": "Docs:",
25 | "description": "Documentation only changes"
26 | },
27 | {
28 | "emoji": ":sparkles:",
29 | "type": "Feat:",
30 | "description": "New feature"
31 | },
32 | {
33 | "emoji": ":bug:",
34 | "type": "Fix:",
35 | "description": "Bug fix"
36 | },
37 | {
38 | "emoji": ":zap:",
39 | "type": "Perf:",
40 | "description": "Code change that improves performance"
41 | },
42 | {
43 | "emoji": ":hammer:",
44 | "type": "Refactor:",
45 | "description": "Code change that neither fixes a bug nor adds a feature"
46 | },
47 | {
48 | "emoji": ":art:",
49 | "type": "Style:",
50 | "description": "Changes that do not affect the meaning of the code"
51 | },
52 | {
53 | "emoji": ":white_check_mark:",
54 | "type": "Test:",
55 | "description": "Adding missing tests or correcting existing tests"
56 | },
57 | {
58 | "emoji": ":boom:",
59 | "type": "Breaking:",
60 | "description": "Breaking Change"
61 | },{
62 | "emoji": ":gem:",
63 | "type": "Upgrade:",
64 | "description": "Update dependencies"
65 | }
66 | ],
67 | "rules": {
68 | "max-char": 72,
69 | "min-char": 10,
70 | "end-with-dot": false
71 | }
72 | }
--------------------------------------------------------------------------------
/.snyk:
--------------------------------------------------------------------------------
1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
2 | version: v1.13.5
3 | ignore: {}
4 | # patches apply the minimum changes required to fix a vulnerability
5 | patch:
6 | 'npm:extend:20180424':
7 | - filehound > file-js > proper-lockfile > extend:
8 | patched: '2019-01-03T01:32:40.892Z'
9 | SNYK-JS-LODASH-450202:
10 | - snyk > snyk-nodejs-lockfile-parser > lodash:
11 | patched: '2019-07-03T17:53:49.046Z'
12 | - lodash:
13 | patched: '2019-07-03T17:53:49.046Z'
14 | - filehound > lodash:
15 | patched: '2019-07-03T17:53:49.046Z'
16 | - snyk > @snyk/dep-graph > lodash:
17 | patched: '2019-07-03T17:53:49.046Z'
18 | - snyk > snyk-config > lodash:
19 | patched: '2019-07-03T17:53:49.046Z'
20 | - snyk > snyk-mvn-plugin > lodash:
21 | patched: '2019-07-03T17:53:49.046Z'
22 | - snyk > lodash:
23 | patched: '2019-07-03T17:53:49.046Z'
24 | - snyk > snyk-nuget-plugin > lodash:
25 | patched: '2019-07-03T17:53:49.046Z'
26 | - snyk > snyk-php-plugin > lodash:
27 | patched: '2019-07-03T17:53:49.046Z'
28 | - snyk > inquirer > lodash:
29 | patched: '2019-07-03T17:53:49.046Z'
30 | - snyk > snyk-go-plugin > graphlib > lodash:
31 | patched: '2019-07-03T17:53:49.046Z'
32 | - snyk > snyk-nodejs-lockfile-parser > graphlib > lodash:
33 | patched: '2019-07-03T17:53:49.046Z'
34 | - snyk > @snyk/dep-graph > graphlib > lodash:
35 | patched: '2019-07-03T17:53:49.046Z'
36 | - snyk > lodash:
37 | patched: '2019-07-24T09:53:29.991Z'
38 | - filehound > lodash:
39 | patched: '2019-07-24T09:53:29.991Z'
40 | - snyk > @snyk/dep-graph > lodash:
41 | patched: '2019-07-24T09:53:29.991Z'
42 | - snyk > snyk-config > lodash:
43 | patched: '2019-07-24T09:53:29.991Z'
44 | - snyk > snyk-nodejs-lockfile-parser > lodash:
45 | patched: '2019-07-24T09:53:29.991Z'
46 | - snyk > snyk-nuget-plugin > lodash:
47 | patched: '2019-07-24T09:53:29.991Z'
48 | - snyk > inquirer > lodash:
49 | patched: '2019-07-24T09:53:29.991Z'
50 | - snyk > snyk-go-plugin > graphlib > lodash:
51 | patched: '2019-07-24T09:53:29.991Z'
52 | - snyk > snyk-nodejs-lockfile-parser > graphlib > lodash:
53 | patched: '2019-07-24T09:53:29.991Z'
54 | - snyk > @snyk/dep-graph > graphlib > lodash:
55 | patched: '2019-07-24T09:53:29.991Z'
56 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | dist: trusty
3 | sudo: required
4 | # keep the npm cache around to speed up installs
5 | cache:
6 | directories:
7 | - "$HOME/.npm"
8 | notifications:
9 | slack:
10 | on_success: change
11 | on_failure: always
12 | node_js:
13 | - 'node'
14 | - '10'
15 | - '8'
16 | os:
17 | - linux
18 | before_install:
19 | - npm install -g codecov
20 | - npm i -g travis-deploy-once
21 | - npm install -g greenkeeper-lockfile@latest
22 | install:
23 | - npm i -g npm@latest
24 | - npm install
25 | before_script:
26 | - npm prune
27 | - echo "Check if TypeScript files have valid syntax/types"
28 | - npm run type-check
29 | - echo "Check if TypeScript files have valid linting"
30 | - npm run lint
31 | - echo "GreenKeeper lockfile update"
32 | - greenkeeper-lockfile-update
33 | after_script:
34 | - greenkeeper-lockfile-upload
35 | after_success:
36 | - codecov
37 | - travis-deploy-once && npx -p node@8 npm run semantic-release
38 | branches:
39 | except:
40 | - /^v\d+\.\d+\.\d+$/
41 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [2.0.1](https://github.com/jy95/mediaScan/compare/v2.0.0...v2.0.1) (2019-08-02)
2 |
3 |
4 | ### CI
5 |
6 | * auto update of lockfile ([a3065977d7cf103502068be9ccb11926396b6aa3](https://github.com/jy95/mediaScan/commit/a3065977d7cf103502068be9ccb11926396b6aa3))
7 | * auto update of lockfile ([4912e459c461838e1a56cf7a1a1513d23899deeb](https://github.com/jy95/mediaScan/commit/4912e459c461838e1a56cf7a1a1513d23899deeb))
8 |
9 | ### fix
10 |
11 | * .snyk to reduce vulnerabilities ([f3e5e47315e45bcd6c01b7d818e337e255d0a5cc](https://github.com/jy95/mediaScan/commit/f3e5e47315e45bcd6c01b7d818e337e255d0a5cc))
12 | * .snyk, package.json & package-lock.json to reduce vulnerabilities ([42390ff4a13020408922aaf429ba8d9fae61b2a4](https://github.com/jy95/mediaScan/commit/42390ff4a13020408922aaf429ba8d9fae61b2a4))
13 | * .snyk, package.json & package-lock.json to reduce vulnerabilities ([689062ec471b5a0991498bc929f47e33639b13cb](https://github.com/jy95/mediaScan/commit/689062ec471b5a0991498bc929f47e33639b13cb))
14 | * configure Snyk protect to enable patches ([3c245bd370ba78980a35f180f8097939ed004805](https://github.com/jy95/mediaScan/commit/3c245bd370ba78980a35f180f8097939ed004805))
15 | * package.json & package-lock.json to reduce vulnerabilities ([e8ef8d70d9a52f569e9c296e80ff2111f69e4fd9](https://github.com/jy95/mediaScan/commit/e8ef8d70d9a52f569e9c296e80ff2111f69e4fd9))
16 |
17 | ### Fix
18 |
19 | * typings issues ([1d4282e6a37e140335b5e3b312a0ba5ea66ace00](https://github.com/jy95/mediaScan/commit/1d4282e6a37e140335b5e3b312a0ba5ea66ace00))
20 |
21 | ### Upgrade
22 |
23 | * Update @semantic-release/changelog to version 3.0.0 ([af3ed183246d3379ef891690f56f79eca5a174de](https://github.com/jy95/mediaScan/commit/af3ed183246d3379ef891690f56f79eca5a174de))
24 | * Update @semantic-release/git to version 6.0.0 ([f8125603b5e9fc9796dd13ce7bf466c33994f652](https://github.com/jy95/mediaScan/commit/f8125603b5e9fc9796dd13ce7bf466c33994f652))
25 | * Update @semantic-release/git to version 7.0.0 ([eac31ac5449834458a6d439dd83807fffdecdfb4](https://github.com/jy95/mediaScan/commit/eac31ac5449834458a6d439dd83807fffdecdfb4))
26 | * Update @types/jest to version 24.0.0 ([035ccd7b68148d5cc4bd361da6cfb1501d6ed679](https://github.com/jy95/mediaScan/commit/035ccd7b68148d5cc4bd361da6cfb1501d6ed679))
27 | * Update @types/node to version 11.9.0 ([73579977d90aec53a547aa014c6f2cdb0459c804](https://github.com/jy95/mediaScan/commit/73579977d90aec53a547aa014c6f2cdb0459c804))
28 | * Update @types/node to version 12.0.0 ([f7317713109b6390f7cf1c9448de2d0292e7bc22](https://github.com/jy95/mediaScan/commit/f7317713109b6390f7cf1c9448de2d0292e7bc22))
29 | * Update babel-minify to version 0.5.0 ([922b85d806f936cb38a849887d4ea6841191dda2](https://github.com/jy95/mediaScan/commit/922b85d806f936cb38a849887d4ea6841191dda2))
30 | * Update snyk to version 1.163.0 ([d859133271de8f42605c8bee4c171ef01e208b2f](https://github.com/jy95/mediaScan/commit/d859133271de8f42605c8bee4c171ef01e208b2f))
31 | * Update snyk to version 2.0.0 ([5a95ba1e603961c6412b082e8e7d6a9904acba27](https://github.com/jy95/mediaScan/commit/5a95ba1e603961c6412b082e8e7d6a9904acba27))
32 | * Update ts-jest to version 23.0.0 ([8f1bf4c0da85dac2e698861a0af2b529fb38402f](https://github.com/jy95/mediaScan/commit/8f1bf4c0da85dac2e698861a0af2b529fb38402f))
33 | * Update ts-jest to version 23.10.1 ([3287411bfd9e8afec131fc4a803d6822b753303c](https://github.com/jy95/mediaScan/commit/3287411bfd9e8afec131fc4a803d6822b753303c))
34 | * Update ts-jest to version 24.0.0 ([aa7a5dc8cb75dd27f5e0839d6c94d471e3a79cd5](https://github.com/jy95/mediaScan/commit/aa7a5dc8cb75dd27f5e0839d6c94d471e3a79cd5))
35 |
36 |
37 | # [2.0.0](https://github.com/jy95/mediaScan/compare/v1.2.4...v2.0.0) (2018-05-20)
38 |
39 |
40 | ### Breaking
41 |
42 | * Drop Support for Node 6 (EOL) ([ef84bc3b0e86684f09df7abdb37ec6af578bdb0c](https://github.com/jy95/mediaScan/commit/ef84bc3b0e86684f09df7abdb37ec6af578bdb0c))
43 |
44 | ### Chore
45 |
46 | * Fix typo ([8c652c0317a8de3ee13d5b31a9972086bb38251d](https://github.com/jy95/mediaScan/commit/8c652c0317a8de3ee13d5b31a9972086bb38251d))
47 |
48 | ### Upgrade
49 |
50 | * Babel beta 46 ([7453218893063930a85eb531e381ad7c4bc24c7f](https://github.com/jy95/mediaScan/commit/7453218893063930a85eb531e381ad7c4bc24c7f))
51 | * Lockfile ([b5bceeb7aedac1836729e9aa9cb9446bc75a9d88](https://github.com/jy95/mediaScan/commit/b5bceeb7aedac1836729e9aa9cb9446bc75a9d88))
52 | * Semantic-release ([25fe356638a50b5345e8940fe2c5b403df49d004](https://github.com/jy95/mediaScan/commit/25fe356638a50b5345e8940fe2c5b403df49d004))
53 |
54 |
55 | ## [1.2.4](https://github.com/jy95/mediaScan/compare/v1.2.3...v1.2.4) (2018-04-11)
56 |
57 |
58 | ### Docs
59 |
60 | * declaration only for ts files ([0183ce58749fe6a1f32b1a7730306e4994f56a3e](https://github.com/jy95/mediaScan/commit/0183ce58749fe6a1f32b1a7730306e4994f56a3e))
61 | * Fix typo in tsconfig ([0ca2e1fe077d3491bd3289b0532d2213971d78e3](https://github.com/jy95/mediaScan/commit/0ca2e1fe077d3491bd3289b0532d2213971d78e3))
62 |
63 | ### Perf
64 |
65 | * Replace eval by object function ([f1a30bbf6cd20fe0c0b850d818aa3337f12f9eaf](https://github.com/jy95/mediaScan/commit/f1a30bbf6cd20fe0c0b850d818aa3337f12f9eaf))
66 |
67 | ### Style
68 |
69 | * Follows tslint code conventions - unit tests ([013286eb01b9997028f97d13367da1a85ba6f2e9](https://github.com/jy95/mediaScan/commit/013286eb01b9997028f97d13367da1a85ba6f2e9))
70 | * Prettier all sources code ([e9b508e406e16e71e6409e829a33a1615acf6633](https://github.com/jy95/mediaScan/commit/e9b508e406e16e71e6409e829a33a1615acf6633))
71 | * Tslint fix errors ([802b823de097a219e1aa4fc3c47144cb22f2f072](https://github.com/jy95/mediaScan/commit/802b823de097a219e1aa4fc3c47144cb22f2f072))
72 |
73 | ### Test
74 |
75 | * Fix lost coverage (because of jest issue) ([90bd75ceefaadc2a71217974bb199ad98fbba551](https://github.com/jy95/mediaScan/commit/90bd75ceefaadc2a71217974bb199ad98fbba551))
76 | * Fix ts-jest mix up with Ts config ([3a9cebd51ea05a047e19c27452052304284c5ce3](https://github.com/jy95/mediaScan/commit/3a9cebd51ea05a047e19c27452052304284c5ce3))
77 |
78 | ### Upgrade
79 |
80 | * Update Babel to beta 44 ([2e4e16c14960eb65fc99a82afc41e07e2768d2fa](https://github.com/jy95/mediaScan/commit/2e4e16c14960eb65fc99a82afc41e07e2768d2fa))
81 | * Update dependancies ([45e3835025dfdeebb18220ccc948687e84913046](https://github.com/jy95/mediaScan/commit/45e3835025dfdeebb18220ccc948687e84913046))
82 |
83 |
84 | ## [1.2.3](https://github.com/jy95/mediaScan/compare/v1.2.2...v1.2.3) (2018-03-23)
85 |
86 |
87 | ### Refactor
88 |
89 | * Rewrite default parser integration ([6965bd15b6be2596ab0357cbeabbd4b336d8b010](https://github.com/jy95/mediaScan/commit/6965bd15b6be2596ab0357cbeabbd4b336d8b010))
90 |
91 | ### Style
92 |
93 | * Follows Tslint code conventions ([43dae497e40a27581b59076c57b2caf4d0b67261](https://github.com/jy95/mediaScan/commit/43dae497e40a27581b59076c57b2caf4d0b67261))
94 |
95 |
96 | ## [1.2.2](https://github.com/jy95/mediaScan/compare/v1.2.1...v1.2.2) (2018-03-21)
97 |
98 |
99 | ### Perf
100 |
101 | * Lodash FP version of removeOldFiles ([5029fd7faafb339fe64694a14a8bb2c0dccaaf03](https://github.com/jy95/mediaScan/commit/5029fd7faafb339fe64694a14a8bb2c0dccaaf03))
102 | * Use Array.prototype.push.apply instead of concat ([047118524790e172df3bb6990403d9e0e24a3a0d](https://github.com/jy95/mediaScan/commit/047118524790e172df3bb6990403d9e0e24a3a0d))
103 |
104 | ### Upgrade
105 |
106 | * Update semantic-release to version 15.1.3 ([da08546df874effc0e71d3d37433bd1fa7541991](https://github.com/jy95/mediaScan/commit/da08546df874effc0e71d3d37433bd1fa7541991)), closes [#21](https://github.com/jy95/mediaScan/issues/21)
107 |
108 |
109 | ## [1.2.1](https://github.com/jy95/mediaScan/compare/v1.2.0...v1.2.1) (2018-03-20)
110 |
111 |
112 | ### Perf
113 |
114 | * Use babel-minify@canary ([0a05799722e159499cd15e73f69155b7d234c8ea](https://github.com/jy95/mediaScan/commit/0a05799722e159499cd15e73f69155b7d234c8ea))
115 |
116 | ### Refactor
117 |
118 | * Array.prototype.push.apply instead of lodash concat ([cc462c5f9504559d8cadc7a6e16bcc6c5ffe8a76](https://github.com/jy95/mediaScan/commit/cc462c5f9504559d8cadc7a6e16bcc6c5ffe8a76))
119 | * less code version of removeOldFiles ([d6f0fe35d34083f548bef78560bcc3115b61fd36](https://github.com/jy95/mediaScan/commit/d6f0fe35d34083f548bef78560bcc3115b61fd36))
120 |
121 | ### Upgrade
122 |
123 | * Update ALL 20/03/2018 ([9e9df23e26ba5463bf38e249e297e07517e01cf6](https://github.com/jy95/mediaScan/commit/9e9df23e26ba5463bf38e249e297e07517e01cf6))
124 | * Update package-lock ([c604e484f5170efc16bc190977ba39cb8cb18e6e](https://github.com/jy95/mediaScan/commit/c604e484f5170efc16bc190977ba39cb8cb18e6e))
125 |
126 |
127 | # [1.2.0](https://github.com/jy95/mediaScan/compare/v1.1.6...v1.2.0) (2018-03-16)
128 |
129 |
130 | ### CI
131 |
132 | * cache only npm and not node modules ([3dbf969d3922c1e70e28278cb8f1b52c1b2b6595](https://github.com/jy95/mediaScan/commit/3dbf969d3922c1e70e28278cb8f1b52c1b2b6595))
133 | * Speed Up Build install step ([a909330eab2b977317c90e86ccf92c08cde8133c](https://github.com/jy95/mediaScan/commit/a909330eab2b977317c90e86ccf92c08cde8133c))
134 |
135 | ### Feat
136 |
137 | * getter allTvSeriesNames ([907e10eee43106faf0a3a3dd96a61aae04e92708](https://github.com/jy95/mediaScan/commit/907e10eee43106faf0a3a3dd96a61aae04e92708))
138 |
139 | ### Perf
140 |
141 | * quick return for filter functions ([5723838147a00398d44b11f7d3904ffedfb05926](https://github.com/jy95/mediaScan/commit/5723838147a00398d44b11f7d3904ffedfb05926))
142 | * simplify toJSONObject series key ([aa1746e36c91bbfb0b5e3d9367f6e0b7ea089f3f](https://github.com/jy95/mediaScan/commit/aa1746e36c91bbfb0b5e3d9367f6e0b7ea089f3f))
143 |
144 | ### Refactor
145 |
146 | * Use Lodash FP instead of transducers ([2ab89f038962e2c0ce8ec56acd2fff806e838671](https://github.com/jy95/mediaScan/commit/2ab89f038962e2c0ce8ec56acd2fff806e838671))
147 |
148 |
149 | ## [1.1.6](https://github.com/jy95/mediaScan/compare/v1.1.5...v1.1.6) (2018-03-08)
150 |
151 |
152 | ### Perf
153 |
154 | * Transducers for mapProperties ([d9e5d84d4597fe5adb404ca8e4e3aca1ca130355](https://github.com/jy95/mediaScan/commit/d9e5d84d4597fe5adb404ca8e4e3aca1ca130355))
155 | * Transducers version of removeOldFiles ([e40c721f7ed8ad7719eae94843556e882edbdb7c](https://github.com/jy95/mediaScan/commit/e40c721f7ed8ad7719eae94843556e882edbdb7c))
156 | * Use transducers.js to make filter functions faster ([0049824a7c0985a6f1de3f5fc44419b542db1891](https://github.com/jy95/mediaScan/commit/0049824a7c0985a6f1de3f5fc44419b542db1891))
157 |
158 | ### Test
159 |
160 | * Fix coverage issue in removeOldFiles ([b3351829fb91e92d8c6d4ce3fbb3ecffa875368b](https://github.com/jy95/mediaScan/commit/b3351829fb91e92d8c6d4ce3fbb3ecffa875368b))
161 |
162 |
163 | ## [1.1.5](https://github.com/jy95/mediaScan/compare/v1.1.4...v1.1.5) (2018-03-07)
164 |
165 |
166 | ### Refactor
167 |
168 | * Trying to fix typings for package ([627571c90c003ec94d4dda0cab15dcbeec426a2c](https://github.com/jy95/mediaScan/commit/627571c90c003ec94d4dda0cab15dcbeec426a2c))
169 |
170 |
171 | ## [1.1.4](https://github.com/jy95/mediaScan/compare/v1.1.3...v1.1.4) (2018-03-05)
172 |
173 |
174 | ### Chore
175 |
176 | * Update DevDependancies ([5a871e2be9b97d6000066f4eb9f3bcc45f098a5b](https://github.com/jy95/mediaScan/commit/5a871e2be9b97d6000066f4eb9f3bcc45f098a5b))
177 |
178 | ### Docs
179 |
180 | * Add TypeScript types for this lib ([46ea8e4a38419f76590a9a2a7676b9c7b25fd665](https://github.com/jy95/mediaScan/commit/46ea8e4a38419f76590a9a2a7676b9c7b25fd665))
181 |
182 | ### Refactor
183 |
184 | * Use implementation of toJSONObject for toJSON() ([a518fdf6639de1ccf4a1191bc2c7879ddde817c0](https://github.com/jy95/mediaScan/commit/a518fdf6639de1ccf4a1191bc2c7879ddde817c0))
185 |
186 |
187 | ## [1.1.3](https://github.com/jy95/mediaScan/compare/v1.1.2...v1.1.3) (2018-02-25)
188 |
189 |
190 | ### Perf
191 |
192 | * addNewFiles rewritten with Lodash ([3edb2294ce78036c539faad70508c7cdb403e099](https://github.com/jy95/mediaScan/commit/3edb2294ce78036c539faad70508c7cdb403e099))
193 | * removeOldFiles rewritten with Lodash ([95f784690110db972412ccee335eff0e0185fd1c](https://github.com/jy95/mediaScan/commit/95f784690110db972412ccee335eff0e0185fd1c))
194 |
195 | ### Refactor
196 |
197 | * Drop forEach to for ... of ([b2c87c594edb16141fecf36704a0d5b80ba88ed3](https://github.com/jy95/mediaScan/commit/b2c87c594edb16141fecf36704a0d5b80ba88ed3))
198 |
199 |
200 | ## [1.1.2](https://github.com/jy95/mediaScan/compare/v1.1.1...v1.1.2) (2018-02-25)
201 |
202 |
203 | ### Perf
204 |
205 | * Set specific babel settings ([068aa48b30f45f6057d56b774d5f604dd1f9f88b](https://github.com/jy95/mediaScan/commit/068aa48b30f45f6057d56b774d5f604dd1f9f88b))
206 |
207 | ### Test
208 |
209 | * Fix typo in test name ([592f5e0b8ecdb9884ff14c7066ba358407dfb781](https://github.com/jy95/mediaScan/commit/592f5e0b8ecdb9884ff14c7066ba358407dfb781))
210 |
211 |
212 | ## [1.1.1](https://github.com/jy95/mediaScan/compare/v1.1.0...v1.1.1) (2018-02-23)
213 |
214 |
215 | ### Refactor
216 |
217 | * filterMoviesByProperties rewritten ([a0a209b96a977e6a941b8a28370e88e5a9ce4ef3](https://github.com/jy95/mediaScan/commit/a0a209b96a977e6a941b8a28370e88e5a9ce4ef3))
218 | * filterTvSeriesByProperties Rewritten ([f0af6d4c9603d471341cba7dd25c6ee358ad97f4](https://github.com/jy95/mediaScan/commit/f0af6d4c9603d471341cba7dd25c6ee358ad97f4))
219 |
220 | ### Test
221 |
222 | * Expect direction ([bc738e49a088ddbf18e8469b44519b8f1d087d51](https://github.com/jy95/mediaScan/commit/bc738e49a088ddbf18e8469b44519b8f1d087d51))
223 |
224 |
225 | # [1.1.0](https://github.com/jy95/mediaScan/compare/v1.0.1...v1.1.0) (2018-02-22)
226 |
227 |
228 | ### Docs
229 |
230 | * Add Greenkeeper badge ([afb7bb20b983a55483d9ce5cf2a0dc595322d5ad](https://github.com/jy95/mediaScan/commit/afb7bb20b983a55483d9ce5cf2a0dc595322d5ad))
231 |
232 | ### Feat
233 |
234 | * toJSONObject method ([f625e4a6e46e1430cb0a359fd445ecc9ee1d7969](https://github.com/jy95/mediaScan/commit/f625e4a6e46e1430cb0a359fd445ecc9ee1d7969))
235 |
236 | ### Perf
237 |
238 | * Rewrite filter search Map in ES6 ([5fb3b4328a1e47120afd171a0cfba524cfed7155](https://github.com/jy95/mediaScan/commit/5fb3b4328a1e47120afd171a0cfba524cfed7155))
239 |
240 |
241 | ## [1.0.1](https://github.com/jy95/mediaScan/compare/v1.0.0...v1.0.1) (2018-02-22)
242 |
243 |
244 | ### Docs
245 |
246 | * add README.md ([964a48fc164bcd23dc0c9c571469452e5fb2f69b](https://github.com/jy95/mediaScan/commit/964a48fc164bcd23dc0c9c571469452e5fb2f69b))
247 |
248 | ### Perf
249 |
250 | * Rewritten filter functions ([9ac0a50ea8da2aafa83b18a699cd10d9968079db](https://github.com/jy95/mediaScan/commit/9ac0a50ea8da2aafa83b18a699cd10d9968079db))
251 |
252 | ### Style
253 |
254 | * change greenkeeper messages with emoji ([9671e75211f271107fb784f18a2451e1a63ca921](https://github.com/jy95/mediaScan/commit/9671e75211f271107fb784f18a2451e1a63ca921))
255 |
256 |
257 | # 1.0.0 (2018-02-21)
258 |
259 |
260 | ### CI
261 |
262 | * Set up Codecov ([ad9d8ede49a1f54592b1dc2117dd978d2505e9ad](https://github.com/jy95/mediaScan/commit/ad9d8ede49a1f54592b1dc2117dd978d2505e9ad))
263 |
264 | ### Fix
265 |
266 | * @babel/runtime -prod / @babel/plugin-transform-runtime -dev ([22fa8b5240d6140c169f30116d8a12d624b85db1](https://github.com/jy95/mediaScan/commit/22fa8b5240d6140c169f30116d8a12d624b85db1))
267 | * Fix Babel setup with TypeScript ([f6c31f71f16a6ce4fee1e90b0d3b803b9a0e41cb](https://github.com/jy95/mediaScan/commit/f6c31f71f16a6ce4fee1e90b0d3b803b9a0e41cb))
268 | * Fix Build for Node 6 ([aff1d7480da6b50e4311ef527918097031670e9d](https://github.com/jy95/mediaScan/commit/aff1d7480da6b50e4311ef527918097031670e9d))
269 | * Objects.entries not available in Node 6 - Real Fix ([08bb6a3cdc4222512911c9624b2769aeb18bcbdb](https://github.com/jy95/mediaScan/commit/08bb6a3cdc4222512911c9624b2769aeb18bcbdb))
270 |
271 | ### Perf
272 |
273 | * Add Babel Support (TODO) ([535f81cf9eb4cefc5c8622c7753882cee8ef9a08](https://github.com/jy95/mediaScan/commit/535f81cf9eb4cefc5c8622c7753882cee8ef9a08))
274 | * Rewrite lib in Typescript ([21c03ea65fc33c6408b515c2102e34c9aa9f572d](https://github.com/jy95/mediaScan/commit/21c03ea65fc33c6408b515c2102e34c9aa9f572d))
275 |
276 | ### Refactor
277 |
278 | * Scan method ([f299cedc2fa861686bea506c9f1e3e0d879933a6](https://github.com/jy95/mediaScan/commit/f299cedc2fa861686bea506c9f1e3e0d879933a6))
279 |
280 | ### Test
281 |
282 | * Fix tests ([eef74a80cc1fa1c332fe7bb3360e505c7701d2b5](https://github.com/jy95/mediaScan/commit/eef74a80cc1fa1c332fe7bb3360e505c7701d2b5))
283 | * Rewrite test in proper way ([e49ec418d32b3094ab0ed9b45ea36b0d4a7ccb5d](https://github.com/jy95/mediaScan/commit/e49ec418d32b3094ab0ed9b45ea36b0d4a7ccb5d))
284 | * Rewritten tests in Jest ([e4f7a1abf129384df78689095c5588b900e39bd2](https://github.com/jy95/mediaScan/commit/e4f7a1abf129384df78689095c5588b900e39bd2))
285 | * Trying to fix addNewPath issue ([3e67528bf6d02a1628ea74fdeb3e6898535d434d](https://github.com/jy95/mediaScan/commit/3e67528bf6d02a1628ea74fdeb3e6898535d434d))
286 | * Use toThrowError for rejected promises ([546f5f2aabb4ccdc41390bf315056873609cc292](https://github.com/jy95/mediaScan/commit/546f5f2aabb4ccdc41390bf315056873609cc292))
287 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at jacques.yakoub+github@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 jy95
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mediascan [](https://travis-ci.org/jy95/mediaScan) [](https://codecov.io/gh/jy95/mediaScan) [](https://david-dm.org/jy95/mediaScan) [](https://opensource.org/licenses/MIT) [](https://github.com/semantic-release/semantic-release) [](https://greenkeeper.io/) [](https://gitter.im/mediaScan/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
2 |
3 | > A scanner for media files that follows a user-provided naming convention
4 |
5 | ## What to do with this library ?
6 |
7 | A lot of things :
8 | * Basic listing purposes :
9 | * [List found movies](https://github.com/jy95/mediaScan/wiki/List-found-movies)
10 | * [List each found tv serie](https://github.com/jy95/mediaScan/wiki/List-each-found-tv-serie)
11 | * [Detect the category of each file](https://github.com/jy95/mediaScan/wiki/Detect-the-category-of-each-file)
12 | * Filtering purposes :
13 | * [Filter movies based on search parameters](https://github.com/jy95/mediaScan/wiki/Filter-movies-by-parameters)
14 | * [Filter tv-shown based on search parameters](https://github.com/jy95/mediaScan/wiki/Filter-tv-series-by-parameters)
15 | * Miscellaneous purposes
16 | * [Create custom playlist(s)](https://github.com/jy95/mediaScan/wiki/Create-custom-playlist(s))
17 | * ...
18 |
19 | Don't hesitate to suggest new features : it is always worthy :)
20 |
21 | ## FAQ
22 |
23 | ### Which naming convention can I use with this lib ?
24 |
25 | **ANYTHING**. All You have to do is to implement a parser function :
26 | A function that takes a single string argument `fullPathFile` (the full path to the file) that returns an object that minimal contains a `title` string property.
27 | For example :
28 |
29 | ```js
30 | const ptt = require("parse-torrent-title");
31 | const information = ptt.parse("Game.of.Thrones.S01E01.720p.HDTV.x264-CTU");
32 |
33 | console.log(information.title); // Game of Thrones
34 | console.log(information.season); // 1
35 | console.log(information.episode); // 1
36 | console.log(information.resolution); // 720p
37 | console.log(information.codec); // x264
38 | console.log(information.source); // HDTV
39 | console.log(information.group); // CTU
40 | ```
41 |
42 | This lib was tested with these parsers that follows torrent naming conventions (see their readme for more info) :
43 |
44 | * [parse-torrent-title](https://www.npmjs.com/package/parse-torrent-title) (the default in this lib)
45 | * [torrent-name-parser](https://www.npmjs.com/package/torrent-name-parser)
46 | * [torrent-name-parse](https://www.npmjs.com/package/torrent-name-parse)
47 |
48 | ### How the library detects the category of a media file ?
49 |
50 | The default implementation determines it is a tv-show if there is `season` and `episode` attributes can be found in the information provided by the parser.
51 | Here is a example if you want to implement one :
52 | ```ts
53 | // Default implementation to know which category is this file
54 | function defaultWhichCategoryFunction(object : MediaScanLib.TPN) : MediaScanLib.Category{
55 | // workaround : const string enum aren't compiled correctly with Babel
56 | return (checkProperties(object, ['season', 'episode']))
57 | ? 'TV_SERIES' as MediaScanLib.Category.TV_SERIES_TYPE : 'MOVIES' as MediaScanLib.Category.MOVIES_TYPE;
58 | }
59 | ```
60 |
61 | ### Using custom parameters in the lib
62 |
63 | Check the [constructor](https://github.com/jy95/mediaScan/blob/master/src/MediaScan.ts#L38) for more detail - an illustration :
64 |
65 | ```js
66 | const MediaScan = require("mediascan");
67 | let libInstance = new MediaScan({
68 | defaultPath = process.cwd(), // Default path to explore , if paths is empty
69 | paths = [], // all the paths that will be explored
70 | allFilesWithCategory = new Map(), // the mapping between file and Category
71 | movies = new Set(), // Set (all the movies)
72 | series = new Map(), // > (all the tv-series episodes)
73 | }, {
74 | parser = nameParser, // the explained parser
75 | whichCategory = defaultWhichCategoryFunction, // the previously explained detection function
76 | });
77 | ```
78 |
79 | ## Installation
80 |
81 | For npm users :
82 |
83 | ```shell
84 | $ npm install --save mediascan
85 | ```
86 |
87 | for Yarn :
88 | ```shell
89 | $ yarn add mediascan
90 | ```
91 |
92 | ## Test
93 |
94 | ```shell
95 | npm test
96 | ```
97 |
98 | # Types definitions
99 |
100 | If You want, You can have the types definitions used in this lib :
101 |
102 | ```shell
103 | npm install @types/mediascan
104 | ```
105 |
106 | ## Contributing
107 |
108 | * If you're unsure if a feature would make a good addition, you can always [create an issue](https://github.com/jy95/mediaScan/issues/new) first.
109 | * We aim for 100% test coverage. Please write tests for any new functionality or changes.
110 | * Any API changes should be fully documented.
111 | * Make sure your code meets our linting standards. Run `npm run lint` to check your code.
112 | * Be mindful of others when making suggestions and/or code reviewing.
--------------------------------------------------------------------------------
/__mocks__/filehound.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // need to manually mock these function as there is no solution : https://github.com/facebook/jest/issues/5589
4 | // here ; a short truncated copy of babel stuff of this class
5 | function _possibleConstructorReturn(self, call) {
6 | if (!self) {
7 | throw new ReferenceError(
8 | "this hasn't been initialised - super() hasn't been called"
9 | );
10 | }
11 | return call && (typeof call === "object" || typeof call === "function")
12 | ? call
13 | : self;
14 | }
15 |
16 | var _createClass = (function() {
17 | function defineProperties(target, props) {
18 | for (var i = 0; i < props.length; i++) {
19 | var descriptor = props[i];
20 | descriptor.enumerable = descriptor.enumerable || false;
21 | descriptor.configurable = true;
22 | if ("value" in descriptor) descriptor.writable = true;
23 | Object.defineProperty(target, descriptor.key, descriptor);
24 | }
25 | }
26 |
27 | return function(Constructor, protoProps, staticProps) {
28 | if (protoProps) defineProperties(Constructor.prototype, protoProps);
29 | if (staticProps) defineProperties(Constructor, staticProps);
30 | return Constructor;
31 | };
32 | })();
33 |
34 | // truncated version of this stuff
35 | var FileHound = (function() {
36 | // temp files
37 | let result;
38 |
39 | function FileHound() {
40 | var _this = _possibleConstructorReturn(
41 | this,
42 | (FileHound.__proto__ || Object.getPrototypeOf(FileHound)).call(this)
43 | );
44 | return _this;
45 | }
46 |
47 | _createClass(
48 | FileHound,
49 | [
50 | {
51 | key: "paths",
52 | value: function paths() {
53 | return this;
54 | }
55 | },
56 | {
57 | key: "ext",
58 | value: function ext() {
59 | return this;
60 | }
61 | },
62 | {
63 | key: "find",
64 | value: function find() {
65 | if (result !== undefined) {
66 | return Promise.resolve(result);
67 | }
68 | // if nothing provided , throw error
69 | return Promise.reject("VAUDOU");
70 | }
71 | }
72 | ],
73 | [
74 | {
75 | key: "create",
76 | value: function create() {
77 | return new FileHound();
78 | }
79 | },
80 | {
81 | key: "__setResult",
82 | value: function(ExpectedResult) {
83 | result = ExpectedResult;
84 | }
85 | }
86 | ]
87 | );
88 |
89 | return FileHound;
90 | })();
91 | module.exports = FileHound;
92 |
--------------------------------------------------------------------------------
/__mocks__/fs.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const fs = jest.genMockFromModule("fs");
3 |
4 | // temp files
5 | let mockPaths = [];
6 |
7 | function access(path, mode, callback) {
8 | if (!mockPaths.includes(path)) {
9 | callback(new Error("VAUDOU"));
10 | }
11 | callback();
12 | }
13 |
14 | function __setMockPaths(pathArray) {
15 | mockPaths = pathArray;
16 | }
17 |
18 | fs.access = access;
19 | fs.__setMockPaths = __setMockPaths;
20 |
21 | module.exports = fs;
22 |
--------------------------------------------------------------------------------
/__tests__/__helpers__/_constants.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-len */
2 | import {join} from "path";
3 |
4 | export const MediaScan = require("../../index.js");
5 |
6 | export const folders = ["folder1", "folder2"];
7 |
8 | export const files = [
9 | join(
10 | folders[0],
11 | "The.Blacklist.S04E21.FRENCH.WEBRip.XviD.avi",
12 | ),
13 | join(
14 | folders[1],
15 | "The.Blacklist.S04E14.FRENCH.WEBRip.XviD.avi",
16 | ),
17 | join(
18 | folders[0],
19 | "Bad.Ass.2012.REMASTERED.TRUEFRENCH.DVDRiP.XviD" +
20 | "-www.zone-telechargement.ws.avi",
21 | ),
22 | ];
23 |
--------------------------------------------------------------------------------
/__tests__/getters/allFilesWithCategory.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | "use strict";
3 | jest.mock("fs");
4 | jest.mock("filehound");
5 |
6 | import {files, folders, MediaScan} from "../__helpers__/_constants";
7 |
8 | describe("allFilesWithCategory", () => {
9 |
10 | beforeAll(() => {
11 | // Set up some mocked out file info before each test
12 | require("fs").__setMockPaths(folders);
13 | require("filehound").__setResult(files);
14 | });
15 |
16 | /** @test {TorrentLibrary#allFilesWithCategory} */
17 | test("Should correctly detect the category of each file", async () => {
18 | const libInstance = new MediaScan();
19 | await expect(libInstance.addNewPath(...folders)).resolves;
20 | await expect(libInstance.scan()).resolves;
21 | expect(libInstance.allFilesWithCategory).toEqual(new Map([
22 | [files[2], MediaScan.MOVIES_TYPE],
23 | [files[0], MediaScan.TV_SERIES_TYPE],
24 | [files[1], MediaScan.TV_SERIES_TYPE],
25 | ]));
26 | });
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/__tests__/getters/allMovies.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | "use strict";
3 | jest.mock("fs");
4 | jest.mock("filehound");
5 |
6 | // imports
7 | const basename = require("path").basename;
8 | import {parse as nameParser} from "parse-torrent-title";
9 | import {files, folders, MediaScan} from "../__helpers__/_constants";
10 |
11 | describe("allMovies", () => {
12 |
13 | beforeAll(() => {
14 | // Set up some mocked out file info before each test
15 | require("fs").__setMockPaths(folders);
16 | require("filehound").__setResult(files);
17 | });
18 |
19 | // TESTS
20 | /** @test {MediaScan#allMovies} */
21 | test("Returns the movies", async () => {
22 | const libInstance = new MediaScan();
23 | await expect(libInstance.addNewPath(...folders).resolves);
24 | await expect(libInstance.scan().resolves);
25 | expect(libInstance.allMovies).toEqual(
26 | new Set([
27 | Object.assign(
28 | nameParser(basename(files[2])),
29 | {filePath: files[2]},
30 | ),
31 | ]),
32 | );
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/__tests__/getters/allTvSeries.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | "use strict";
3 | jest.mock("fs");
4 | jest.mock("filehound");
5 |
6 | // imports
7 | const basename = require("path").basename;
8 | import {parse as nameParser} from "parse-torrent-title";
9 | import {files, folders, MediaScan} from "../__helpers__/_constants";
10 |
11 | describe("allTvSeries", () => {
12 |
13 | beforeAll(() => {
14 | // Set up some mocked out file info before each test
15 | require("fs").__setMockPaths(folders);
16 | require("filehound").__setResult(files);
17 | });
18 |
19 | // TESTS
20 | /** @test {TorrentLibrary#allTvSeries} */
21 | test("Returns the tv-shows", async () => {
22 | const libInstance = new MediaScan();
23 | await expect(libInstance.addNewPath(...folders)).resolves;
24 | await expect(libInstance.scan().resolves);
25 | expect(libInstance.allTvSeries).toEqual(
26 | new Map([
27 | [nameParser(basename(files[0])).title, new Set([
28 | Object.assign(
29 | nameParser(basename(files[0])),
30 | {filePath: files[0]},
31 | ),
32 | Object.assign(
33 | nameParser(basename(files[1])),
34 | {filePath: files[1]},
35 | ),
36 | ])],
37 | ]),
38 | );
39 | });
40 |
41 | });
42 |
--------------------------------------------------------------------------------
/__tests__/getters/allTvSeriesNames.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | "use strict";
3 | jest.mock("fs");
4 | jest.mock("filehound");
5 |
6 | // imports
7 | import {files, folders, MediaScan} from "../__helpers__/_constants";
8 |
9 | describe("allTvSeriesNames", () => {
10 |
11 | beforeAll(() => {
12 | // Set up some mocked out file info before each test
13 | require("fs").__setMockPaths(folders);
14 | require("filehound").__setResult(files);
15 | });
16 |
17 | test("Returns all the found tv series names", async () => {
18 | const libInstance = new MediaScan();
19 | await expect(libInstance.addNewPath(...folders)).resolves;
20 | await expect(libInstance.scan().resolves);
21 | expect(libInstance.allTvSeriesNames).toEqual(
22 | ["The Blacklist"],
23 | );
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/__tests__/getters/constants.ts:
--------------------------------------------------------------------------------
1 | import {MediaScan} from "../__helpers__/_constants";
2 |
3 | const videosExtension = require("video-extensions");
4 |
5 | describe("Constants", () => {
6 | /** @test {TorrentLibrary.MOVIES_TYPE} */
7 | test("Constant MOVIES_TYPE", () => {
8 | expect(MediaScan.MOVIES_TYPE).toBe("MOVIES");
9 | });
10 |
11 | /** @test {TorrentLibrary.TV_SERIES_TYPE} */
12 | test("Constant TV_SERIES", () => {
13 | expect(MediaScan.TV_SERIES_TYPE).toBe("TV_SERIES");
14 | });
15 |
16 | /** @test {TorrentLibrary.listVideosExtension} */
17 | test("List of videos extension", () => {
18 | expect(MediaScan.listVideosExtension()).toBe(videosExtension);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/__tests__/methods/addNewPath.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | jest.mock("fs");
3 | jest.mock("filehound");
4 |
5 | // imports
6 | import * as path from "path";
7 | import {files, folders, MediaScan} from "../__helpers__/_constants";
8 |
9 | describe("addNewPath", () => {
10 | beforeAll(() => {
11 | // Set up some mocked out file info before each test
12 | require("fs").__setMockPaths(folders);
13 | require("filehound").__setResult(files);
14 | });
15 |
16 | // TESTS
17 | /** @test {MediaScan#addNewPath} */
18 | test("missing parameter", async () => {
19 | const libInstance = new MediaScan();
20 | const eventSpy = jest.spyOn(libInstance, "addNewPath");
21 | await expect(libInstance.addNewPath()).rejects.toThrowError();
22 | expect(eventSpy).toHaveBeenCalled();
23 | expect(eventSpy).toHaveBeenCalledTimes(1);
24 | expect(libInstance.hasPathsProvidedByUser()).toBe(false);
25 | });
26 |
27 | /** @test {MediaScan#addNewPath} */
28 | test("Not an existent path", async () => {
29 | const libInstance = new MediaScan();
30 | const eventSpy = jest.spyOn(libInstance, "addNewPath");
31 | await expect(libInstance.addNewPath(path.join(__dirname, "wrongPath"))).rejects.toThrowError();
32 | expect(eventSpy).toHaveBeenCalled();
33 | expect(eventSpy).toHaveBeenCalledTimes(1);
34 | expect(libInstance.hasPathsProvidedByUser()).toBe(false);
35 | });
36 |
37 | /** @test {MediaScan#addNewPath} */
38 | test("existent paths", async () => {
39 | const libInstance = new MediaScan();
40 | const data = await libInstance.addNewPath(...folders);
41 | await expect(data).resolves;
42 | expect(libInstance.hasPathsProvidedByUser()).toBe(true);
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/__tests__/methods/createFromJSON.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | "use strict";
3 | jest.mock("fs");
4 | jest.mock("filehound");
5 |
6 | // imports
7 | import {files, folders, MediaScan} from "../__helpers__/_constants";
8 |
9 | describe("createFromJSON", () => {
10 | beforeAll(() => {
11 | // Set up some mocked out file info before each test
12 | require("fs").__setMockPaths(folders);
13 | require("filehound").__setResult(files);
14 | });
15 |
16 | /** @test {MediaScan.createFromJSON} */
17 | test("create a perfect copy of instance", async () => {
18 | const libInstance = new MediaScan();
19 | await expect(libInstance.addNewPath(...folders)).resolves;
20 | await expect(libInstance.scan()).resolves;
21 | const jsonFromLib = JSON.parse(libInstance.toJSON());
22 | const createdInstance = MediaScan.createFromJSON(jsonFromLib);
23 | expect(createdInstance.allFilesWithCategory).toEqual(libInstance.allFilesWithCategory);
24 | expect(createdInstance.allMovies).toEqual(libInstance.allMovies);
25 | expect(createdInstance.allTvSeries).toEqual(libInstance.allTvSeries);
26 | });
27 |
28 | // dummy test for ES6 code coverage
29 | /** @test {MediaScan.createFromJSON} */
30 | test("empty instance(s)", async () => {
31 | expect(MediaScan.createFromJSON({}, {})).toBeInstanceOf(MediaScan);
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/__tests__/methods/filterMovies.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | "use strict";
3 | jest.mock("fs");
4 | jest.mock("filehound");
5 |
6 | // imports
7 | import {parse as nameParser} from "parse-torrent-title";
8 | import * as path from "path";
9 | import {files, folders, MediaScan} from "../__helpers__/_constants";
10 |
11 | describe("filterMovies", () => {
12 |
13 | beforeAll(() => {
14 | // Set up some mocked out file info before each test
15 | require("fs").__setMockPaths(folders);
16 | require("filehound").__setResult(files);
17 | });
18 |
19 | /** @test {MediaScan#filterMovies} */
20 | test("Should work without parameters", async () => {
21 | const libInstance = new MediaScan();
22 | const eventSpy = jest.spyOn(libInstance, "scan");
23 | // whatever path that should exists
24 | await expect(libInstance.addNewPath(...folders)).resolves;
25 | await expect(libInstance.scan()).resolves;
26 | expect(eventSpy).toHaveBeenCalled();
27 | expect(eventSpy).toHaveBeenCalledTimes(1);
28 | expect(libInstance.filterMovies()).toEqual(
29 | new Set([
30 | Object.assign(
31 | nameParser(path.basename(files[2])),
32 | {filePath: files[2]},
33 | ),
34 | ]),
35 | );
36 | });
37 |
38 | /** @test {MediaScan#filterMovies} */
39 | test("default boolean parameters search", async () => {
40 | const libInstance = new MediaScan();
41 | const eventSpy = jest.spyOn(libInstance, "scan");
42 | // whatever path that should exists
43 | await expect(libInstance.addNewPath(...folders)).resolves;
44 | await expect(libInstance.scan()).resolves;
45 | expect(eventSpy).toHaveBeenCalled();
46 | expect(eventSpy).toHaveBeenCalledTimes(1);
47 |
48 | // A simple filter that should returns the only movie that we have
49 | expect(
50 | libInstance.filterMovies({
51 | remastered: true,
52 | }),
53 | ).toEqual(
54 | new Set([
55 | Object.assign(
56 | nameParser(path.basename(files[2])),
57 | {filePath: files[2]},
58 | ),
59 | ]),
60 | );
61 |
62 | // A complex filter that should returns nothing
63 | expect(libInstance.filterMovies({
64 | additionalProperties: [
65 | {type: "boolean", name: "AnotherField", value: true},
66 | ],
67 | convert: true,
68 | extended: true,
69 | hardcoded: true,
70 | proper: true,
71 | remastered: true,
72 | repack: true,
73 | retail: true,
74 | unrated: true,
75 | })).toEqual(new Set());
76 | });
77 |
78 | /** @test {MediaScan#filterMovies} */
79 | test("default number parameters search", async () => {
80 | const libInstance = new MediaScan();
81 | const eventSpy = jest.spyOn(libInstance, "scan");
82 | // whatever path that should exists
83 | await expect(libInstance.addNewPath(...folders)).resolves;
84 | await expect(libInstance.scan()).resolves;
85 | expect(eventSpy).toHaveBeenCalled();
86 | expect(eventSpy).toHaveBeenCalledTimes(1);
87 |
88 | // A simple filter that should returns the only movie that we have
89 | expect(libInstance.filterMovies({
90 | year: 2012,
91 | })).toEqual(new Set([
92 | Object.assign(
93 | nameParser(path.basename(files[2])),
94 | {filePath: files[2]},
95 | ),
96 | ]));
97 |
98 | // A complex filter that should returns nothing
99 | expect(libInstance.filterMovies({
100 | additionalProperties: [
101 | {type: "number", name: "whateverFieldThatDoesn'tExist", value: "<50"},
102 | {type: "number", name: "AnotherField", value: undefined},
103 | {type: "number", name: "AnotherField2", value: "<=25"},
104 | {type: "number", name: "AnotherField3", value: ">25"},
105 | {type: "number", name: "AnotherField4", value: "==25"},
106 | ],
107 | year: ">=2012",
108 | })).toEqual(new Set());
109 | });
110 |
111 | /** @test {MediaScan#filterMovies} */
112 | test("default string parameters search", async () => {
113 | const libInstance = new MediaScan();
114 | const eventSpy = jest.spyOn(libInstance, "scan");
115 | // whatever path that should exists
116 | await expect(libInstance.addNewPath(...folders)).resolves;
117 | await expect(libInstance.scan()).resolves;
118 | expect(eventSpy).toHaveBeenCalled();
119 | expect(eventSpy).toHaveBeenCalledTimes(1);
120 |
121 | // A simple filter that should returns the only movie that we have
122 | expect(new Set([
123 | Object.assign(
124 | nameParser(path.basename(files[2])),
125 | {filePath: files[2]},
126 | ),
127 | ])).toEqual(libInstance.filterMovies({
128 | title: "Bad Ass",
129 | }));
130 |
131 | // A complex filter that should returns nothing
132 | expect(libInstance.filterMovies({
133 | additionalProperties: [
134 | {
135 | name: "whateverField",
136 | type: "string",
137 | value: ["NothingExists"],
138 | },
139 | {
140 | name: "AnotherField",
141 | type: "string",
142 | value: ["NothingExists", "NothingExists"],
143 | },
144 | {type: "string", name: "AnotherField2", value: "<=25"},
145 | {type: "string", name: "AnotherField3", value: ">25"},
146 | ],
147 | title: "Bad Ass",
148 | })).toEqual(new Set());
149 | });
150 |
151 | });
152 |
--------------------------------------------------------------------------------
/__tests__/methods/filterTvSeries.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | "use strict";
3 | jest.mock("fs");
4 | jest.mock("filehound");
5 |
6 | // imports
7 | import {parse as nameParser} from "parse-torrent-title";
8 | import * as path from "path";
9 | import {files, folders, MediaScan} from "../__helpers__/_constants";
10 |
11 | describe("filterTvSeries", () => {
12 |
13 | beforeAll(() => {
14 | // Set up some mocked out file info before each test
15 | require("fs").__setMockPaths(folders);
16 | require("filehound").__setResult(files);
17 | });
18 |
19 | /** @test {MediaScan#filterTvSeries} */
20 | test("Should work without parameters", async () => {
21 | const libInstance = new MediaScan();
22 | const eventSpy = jest.spyOn(libInstance, "scan");
23 | // whatever path that should exists
24 | await expect(libInstance.addNewPath(...folders)).resolves;
25 | await expect(libInstance.scan()).resolves;
26 | expect(eventSpy).toHaveBeenCalled();
27 | expect(eventSpy).toHaveBeenCalledTimes(1);
28 | expect(libInstance.filterTvSeries()).toEqual(
29 | new Map([
30 | [nameParser(path.basename(files[0])).title, new Set([
31 | Object.assign(
32 | nameParser(path.basename(files[0])),
33 | {filePath: files[0]},
34 | ),
35 | Object.assign(
36 | nameParser(path.basename(files[1])),
37 | {filePath: files[1]},
38 | ),
39 | ])],
40 | ]),
41 | );
42 | });
43 |
44 | /** @test {MediaScan#filterTvSeries} */
45 | test("default boolean parameters search", async () => {
46 | const libInstance = new MediaScan();
47 | const eventSpy = jest.spyOn(libInstance, "scan");
48 | // whatever path that should exists
49 | await expect(libInstance.addNewPath(...folders)).resolves;
50 | await expect(libInstance.scan()).resolves;
51 | expect(eventSpy).toHaveBeenCalled();
52 | expect(eventSpy).toHaveBeenCalledTimes(1);
53 |
54 | // A complex filter that should returns nothing
55 | expect(libInstance.filterTvSeries({
56 | additionalProperties: [
57 | {type: "boolean", name: "AnotherField", value: true},
58 | ],
59 | convert: true,
60 | extended: true,
61 | hardcoded: true,
62 | proper: true,
63 | remastered: true,
64 | repack: true,
65 | retail: true,
66 | unrated: true,
67 | })).toEqual(new Map());
68 | });
69 |
70 | /** @test {MediaScan#filterTvSeries} */
71 | test("default number parameters search", async () => {
72 | const libInstance = new MediaScan();
73 | const eventSpy = jest.spyOn(libInstance, "scan");
74 | // whatever path that should exists
75 | await expect(libInstance.addNewPath(...folders)).resolves;
76 | await expect(libInstance.scan()).resolves;
77 | expect(eventSpy).toHaveBeenCalled();
78 | expect(eventSpy).toHaveBeenCalledTimes(1);
79 |
80 | // A simple filter that should returns the two tv series that we have
81 | expect(libInstance.filterTvSeries({
82 | season: 4,
83 | })).toEqual(
84 | new Map([
85 | [nameParser(path.basename(files[0])).title, new Set([
86 | Object.assign(
87 | nameParser(path.basename(files[0])),
88 | {filePath: files[0]},
89 | ),
90 | Object.assign(
91 | nameParser(path.basename(files[1])),
92 | {filePath: files[1]},
93 | ),
94 | ])],
95 | ]),
96 | );
97 |
98 | // A complex filter that should returns nothing
99 | expect((libInstance.filterTvSeries({
100 | additionalProperties: [
101 | {type: "number", name: "whateverFieldThatDoesn'tExist", value: "<50"},
102 | {type: "number", name: "AnotherField", value: undefined},
103 | {type: "number", name: "AnotherField2", value: "<=25"},
104 | {type: "number", name: "AnotherField3", value: ">25"},
105 | {type: "number", name: "AnotherField4", value: "==25"},
106 | ],
107 | season: ">=4",
108 | }))).toEqual(
109 | new Map(),
110 | );
111 | });
112 |
113 | /** @test {MediaScan#filterTvSeries} */
114 | test("default string parameters search", async () => {
115 | const libInstance = new MediaScan();
116 | const eventSpy = jest.spyOn(libInstance, "scan");
117 | // whatever path that should exists
118 | await expect(libInstance.addNewPath(...folders)).resolves;
119 | await expect(libInstance.scan()).resolves;
120 | expect(eventSpy).toHaveBeenCalled();
121 | expect(eventSpy).toHaveBeenCalledTimes(1);
122 |
123 | // A simple filter that should returns the only movie that we have
124 | expect(libInstance.filterTvSeries({
125 | title: "The Blacklist",
126 | })).toEqual(
127 | new Map([
128 | [nameParser(path.basename(files[0])).title, new Set([
129 | Object.assign(
130 | nameParser(path.basename(files[0])),
131 | {filePath: files[0]},
132 | ),
133 | Object.assign(
134 | nameParser(path.basename(files[1])),
135 | {filePath: files[1]},
136 | ),
137 | ])],
138 | ]),
139 | );
140 |
141 | // A complex filter that should returns nothing
142 | expect(libInstance.filterTvSeries({
143 | additionalProperties: [
144 | {
145 | name: "whateverField",
146 | type: "string",
147 | value: ["NothingExists"],
148 | },
149 | {
150 | name: "AnotherField",
151 | type: "string",
152 | value: ["NothingExists", "NothingExists"],
153 | },
154 | { name: "AnotherField2", type: "number", value: "<=25"},
155 | { name: "AnotherField3", type: "number", value: ">25"},
156 | ],
157 | title: "The Blacklist",
158 | })).toEqual(new Map());
159 | });
160 |
161 | });
162 |
--------------------------------------------------------------------------------
/__tests__/methods/removeOldFiles.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | "use strict";
3 | jest.mock("fs");
4 | jest.mock("filehound");
5 |
6 | // imports
7 | import {parse as nameParser} from "parse-torrent-title";
8 | import * as path from "path";
9 | import {basename} from "path";
10 | import {files, folders, MediaScan} from "../__helpers__/_constants";
11 |
12 | describe("removeOldFiles", () => {
13 |
14 | beforeAll(() => {
15 | // Set up some mocked out file info before each test
16 | require("fs").__setMockPaths(folders);
17 | require("filehound").__setResult(files);
18 | });
19 |
20 | /** @test {MediaScan#removeOldFiles} */
21 | test("Should not be able to remove not present files", async () => {
22 | const libInstance = new MediaScan();
23 | await expect(libInstance.addNewPath(...folders)).resolves;
24 | await expect(libInstance.scan()).resolves;
25 | const wrongFile = path.join(
26 | __dirname, "folder1",
27 | "The.Blacklist.S04E22.FRENCH.WEBRip.XviD.avi",
28 | );
29 | const allFiles = libInstance.allFilesWithCategory;
30 | const expectedTvSeriesMap = libInstance.allTvSeries;
31 | await expect(libInstance.removeOldFiles(wrongFile)).resolves;
32 | expect(libInstance.allFilesWithCategory).toEqual(allFiles);
33 | expect(libInstance.allTvSeries).toEqual(expectedTvSeriesMap);
34 | });
35 |
36 | /** @test {MediaScan#removeOldFiles} */
37 | test("Should be able to remove a movie", async () => {
38 | const libInstance = new MediaScan();
39 | await expect(libInstance.addNewPath(...folders)).resolves;
40 | await expect(libInstance.scan()).resolves;
41 | const allFilesWithoutMovie = libInstance.allFilesWithCategory;
42 | // files[2] ; Bad Ass
43 | allFilesWithoutMovie.delete(files[2]);
44 |
45 | const eventSpy = jest.spyOn(libInstance, "removeOldFiles");
46 | // files[2] ; Bad Ass
47 | await expect(libInstance.removeOldFiles(files[2])).resolves;
48 | expect(libInstance.allMovies).toEqual(new Set());
49 |
50 | expect(libInstance.allFilesWithCategory).toEqual(allFilesWithoutMovie);
51 | expect(eventSpy).toHaveBeenCalled();
52 | expect(eventSpy).toHaveBeenCalledTimes(1);
53 | });
54 |
55 | /** @test {MediaScan#removeOldFiles} */
56 | test("Should be able to remove an tv-serie episode", async () => {
57 | const libInstance = new MediaScan();
58 | await expect(libInstance.addNewPath(...folders)).resolves;
59 | await expect(libInstance.scan()).resolves;
60 | const allFilesWithoutIt = libInstance.allFilesWithCategory;
61 | // files[1] ; The.Blacklist.S04E21
62 | allFilesWithoutIt.delete(files[1]);
63 |
64 | const eventSpy = jest.spyOn(libInstance, "removeOldFiles");
65 | // files[1] ; The.Blacklist.S04E21
66 | await expect(libInstance.removeOldFiles(files[1])).resolves;
67 | expect(libInstance.allTvSeries).toEqual(new Map([
68 | [nameParser(path.basename(files[0])).title, new Set([
69 | Object.assign(
70 | nameParser(path.basename(files[0])),
71 | {filePath: files[0]},
72 | ),
73 | ])],
74 | ]));
75 |
76 | expect(libInstance.allFilesWithCategory).toEqual(allFilesWithoutIt);
77 | expect(eventSpy).toHaveBeenCalled();
78 | expect(eventSpy).toHaveBeenCalledTimes(1);
79 | });
80 |
81 | /** @test {MediaScan#removeOldFiles} */
82 | test("Should be able to remove multiples files : Tv-serie", async () => {
83 | const libInstance = new MediaScan();
84 | await expect(libInstance.addNewPath(...folders)).resolves;
85 | await expect(libInstance.scan()).resolves;
86 | const allFilesWithoutIt = new Map([[files[2], MediaScan.MOVIES_TYPE]]);
87 |
88 | const eventSpy = jest.spyOn(libInstance, "removeOldFiles");
89 | await expect(libInstance.removeOldFiles(...files.slice(0, 2))).resolves;
90 | expect(libInstance.allTvSeries).toEqual(new Map());
91 |
92 | expect(libInstance.allFilesWithCategory).toEqual(allFilesWithoutIt);
93 | expect(eventSpy).toHaveBeenCalled();
94 | expect(eventSpy).toHaveBeenCalledTimes(1);
95 | });
96 |
97 | // test to handle default parameters
98 | /** @test {MediaScan#removeOldFiles} */
99 | test("Should not be able to remove files : wrong custom parser", async () => {
100 | const libInstance = MediaScan.createFromJSON({
101 | allFilesWithCategory: [
102 | [
103 | files[2],
104 | "MOVIES",
105 | ],
106 | [
107 | files[0],
108 | "TV_SERIES",
109 | ],
110 | [
111 | files[1],
112 | "TV_SERIES",
113 | ],
114 | ],
115 | movies: [
116 | Object.assign(nameParser(basename(files[2])), {
117 | filePath: files[2],
118 | }),
119 | ],
120 | paths: [
121 | ...folders,
122 | ],
123 | series: [
124 | [
125 | "The Blacklist",
126 | [
127 | Object.assign(nameParser(basename(files[0])), {
128 | filePath: files[0],
129 | }),
130 | Object.assign(nameParser(basename(files[1])), {
131 | filePath: files[1],
132 | }),
133 | ],
134 | ],
135 | ],
136 | }, {
137 | parser : {},
138 | });
139 | const eventSpy = jest.spyOn(libInstance, "removeOldFiles");
140 | await expect(libInstance.removeOldFiles(...files)).rejects.toThrowError();
141 | expect(eventSpy).toHaveBeenCalled();
142 | expect(eventSpy).toHaveBeenCalledTimes(1);
143 | });
144 |
145 | });
146 |
--------------------------------------------------------------------------------
/__tests__/methods/scan.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | "use strict";
3 | jest.mock("fs");
4 | jest.mock("filehound");
5 | import {parse as nameParser} from "parse-torrent-title";
6 | import {files, folders, MediaScan} from "../__helpers__/_constants";
7 |
8 | describe("scan", () => {
9 |
10 | beforeAll(() => {
11 | // Set up some mocked out file info before each test
12 | require("fs").__setMockPaths(folders);
13 | require("filehound").__setResult(files);
14 | });
15 |
16 | // TESTS
17 | /** @test {MediaScan#scan} */
18 | test("Scan without user provided paths", async () => {
19 | const libInstance = new MediaScan();
20 | const eventSpy = jest.spyOn(libInstance, "scan");
21 | await expect(libInstance.scan().resolves);
22 | expect(eventSpy).toHaveBeenCalled();
23 | expect(eventSpy).toHaveBeenCalledTimes(1);
24 | });
25 |
26 | /** @test {MediaScan#scan} */
27 | test("Scan with user provided paths", async () => {
28 | const libInstance = new MediaScan();
29 | const eventSpy = jest.spyOn(libInstance, "scan");
30 | // whatever path that should exists
31 | await expect(libInstance.addNewPath(...folders).resolves);
32 | await expect(libInstance.scan().resolves);
33 | expect(eventSpy).toHaveBeenCalled();
34 | expect(eventSpy).toHaveBeenCalledTimes(1);
35 | });
36 |
37 | // test to handle default parameters
38 | /** @test {MediaScan#scan} */
39 | test("Scan with user provided paths and custom parser", async () => {
40 | const libInstance = new MediaScan({}, {parser: nameParser});
41 | const eventSpy = jest.spyOn(libInstance, "scan");
42 | // whatever path that should exists
43 | await expect(libInstance.addNewPath(...folders).resolves);
44 | await expect(libInstance.scan().resolves);
45 | expect(eventSpy).toHaveBeenCalled();
46 | expect(eventSpy).toHaveBeenCalledTimes(1);
47 | });
48 |
49 | /** @test {MediaScan#scan} */
50 | test("Scan with user provided paths and wrong custom parser", async () => {
51 | const libInstance = new MediaScan({}, {parser: {}});
52 | const eventSpy = jest.spyOn(libInstance, "scan");
53 | // whatever path that should exists
54 | await expect(libInstance.addNewPath(...folders).resolves);
55 | await expect(libInstance.scan()).rejects.toThrowError();
56 | expect(eventSpy).toHaveBeenCalled();
57 | expect(eventSpy).toHaveBeenCalledTimes(1);
58 | });
59 |
60 | });
61 |
--------------------------------------------------------------------------------
/__tests__/methods/toJSON.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | "use strict";
3 | jest.mock("fs");
4 | jest.mock("filehound");
5 |
6 | // imports
7 | import {parse as nameParser} from "parse-torrent-title";
8 | import {basename} from "path";
9 | import {files, folders, MediaScan} from "../__helpers__/_constants";
10 |
11 | describe("toJSON", () => {
12 |
13 | beforeAll(() => {
14 | // Set up some mocked out file info before each test
15 | require("fs").__setMockPaths(folders);
16 | require("filehound").__setResult(files);
17 | });
18 |
19 | /** @test {MediaScan#toJSON} */
20 | test("return a valid stringified JSON", async () => {
21 | const expectedJson = {
22 | allFilesWithCategory: [
23 | [
24 | files[0],
25 | "TV_SERIES",
26 | ],
27 | [
28 | files[1],
29 | "TV_SERIES",
30 | ],
31 | [
32 | files[2],
33 | "MOVIES",
34 | ],
35 | ],
36 | movies: [
37 | Object.assign(nameParser(basename(files[2])), {
38 | filePath: files[2],
39 | }),
40 | ],
41 | paths: folders,
42 | series: [
43 | [
44 | "The Blacklist",
45 | [
46 | Object.assign(nameParser(basename(files[0])), {
47 | filePath: files[0],
48 | }),
49 | Object.assign(nameParser(basename(files[1])), {
50 | filePath: files[1],
51 | }),
52 | ],
53 | ],
54 | ],
55 | };
56 | const libInstance = new MediaScan();
57 | const data = await libInstance.addNewPath(...folders);
58 | await expect(data).resolves;
59 | const scan = await libInstance.scan();
60 | await expect(scan).resolves;
61 | const dataFromInstance = libInstance.toJSON();
62 | expect(JSON.parse(dataFromInstance)).toEqual(expectedJson);
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/__tests__/methods/toJSONObject.ts:
--------------------------------------------------------------------------------
1 | // mock from jest
2 | "use strict";
3 | jest.mock("fs");
4 | jest.mock("filehound");
5 |
6 | // imports
7 | import {parse as nameParser} from "parse-torrent-title";
8 | import {basename} from "path";
9 | import {files, folders, MediaScan} from "../__helpers__/_constants";
10 |
11 | describe("toJSONObject", () => {
12 |
13 | beforeAll(() => {
14 | // Set up some mocked out file info before each test
15 | require("fs").__setMockPaths(folders);
16 | require("filehound").__setResult(files);
17 | });
18 |
19 | /** @test {MediaScan#toJSONObject} */
20 | test("return a valid JSON", async () => {
21 | const expectedJsonString = {
22 | allFilesWithCategory: [
23 | [
24 | files[0],
25 | "TV_SERIES",
26 | ],
27 | [
28 | files[1],
29 | "TV_SERIES",
30 | ],
31 | [
32 | files[2],
33 | "MOVIES",
34 | ],
35 | ],
36 | movies: [
37 | Object.assign(nameParser(basename(files[2])), {
38 | filePath: files[2],
39 | }),
40 | ],
41 | paths: folders,
42 | series: [
43 | [
44 | "The Blacklist",
45 | [
46 | Object.assign(nameParser(basename(files[0])), {
47 | filePath: files[0],
48 | }),
49 | Object.assign(nameParser(basename(files[1])), {
50 | filePath: files[1],
51 | }),
52 | ],
53 | ],
54 | ],
55 | };
56 | const libInstance = new MediaScan();
57 | const data = await libInstance.addNewPath(...folders);
58 | await expect(data).resolves;
59 | const scan = await libInstance.scan();
60 | await expect(scan).resolves;
61 | const dataFromInstance = libInstance.toJSONObject();
62 | expect(dataFromInstance).toEqual(expectedJsonString);
63 | });
64 |
65 | test("return a valid JSON | loose mode", async () => {
66 | const expectedJsonString = JSON.stringify({
67 | allFilesWithCategory: [
68 | [
69 | files[0],
70 | "TV_SERIES",
71 | ],
72 | [
73 | files[1],
74 | "TV_SERIES",
75 | ],
76 | [
77 | files[2],
78 | "MOVIES",
79 | ],
80 | ],
81 | });
82 | const libInstance = new MediaScan();
83 | const data = await libInstance.addNewPath(...folders);
84 | await expect(data).resolves;
85 | const scan = await libInstance.scan();
86 | await expect(scan).resolves;
87 | const dataFromInstance = libInstance.toJSONObject(true);
88 | expect(JSON.stringify(dataFromInstance)).toEqual(expectedJsonString);
89 | });
90 | });
91 |
--------------------------------------------------------------------------------
/config/release-rules.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | tag: 'Feat',
4 | release: 'minor',
5 | },
6 | {
7 | tag: 'Refactor',
8 | release: 'patch',
9 | },
10 | {
11 | tag: 'Fix',
12 | release: 'patch',
13 | },
14 | {
15 | tag: 'Perf',
16 | release: 'patch',
17 | },
18 | {
19 | tag: 'Breaking',
20 | release: 'major',
21 | },
22 | ];
23 |
--------------------------------------------------------------------------------
/config/release.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-useless-escape,max-len,no-template-curly-in-string */
2 | module.exports = {
3 | analyzeCommits: {
4 | preset: 'eslint',
5 | releaseRules: './config/release-rules.js',
6 | parserOpts: {
7 | headerPattern: /^(?::([\w-]*):)?\s*(\w*):\s*(.*)$/,
8 | headerCorrespondence: [
9 | 'emoji',
10 | 'tag',
11 | 'message',
12 | ],
13 | },
14 | },
15 | generateNotes: {
16 | preset: 'eslint',
17 | parserOpts: {
18 | headerPattern: /^(?::([\w-]*):)?\s*(\w*):\s*(.*)$/,
19 | headerCorrespondence: [
20 | 'emoji',
21 | 'tag',
22 | 'message',
23 | ],
24 | },
25 | },
26 | verifyConditions:
27 | ['@semantic-release/changelog', '@semantic-release/npm', '@semantic-release/git', '@semantic-release/github'],
28 | prepare:
29 | ['@semantic-release/changelog', {
30 | path: '@semantic-release/git',
31 | message: ':wrench: Chore: update package.json and CHANGELOG.md for release ${nextRelease.version} [skip ci]',
32 | }],
33 | };
34 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/MediaScan');
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | // jest.config.js
2 | module.exports = {
3 | verbose: true,
4 | "moduleFileExtensions": [
5 | "ts",
6 | "tsx",
7 | "js"
8 | ],
9 | "transform": {
10 | "^(?!.*\\.d\\.ts$).*\\.tsx?$": "ts-jest"
11 | },
12 | "testMatch": [
13 | "/__tests__/**/*.(ts|tsx|js)"
14 | ],
15 | "testPathIgnorePatterns": ["/node_modules/", "/__tests__/__helpers__/"],
16 | "collectCoverage": true,
17 | "globals": {
18 | "ts-jest": {
19 | "tsConfig": "./tsconfig.jest.json",
20 | "diagnostics": true
21 | }
22 | }
23 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mediascan",
3 | "version": "1.2.0",
4 | "description": "A scanner for media files that follows a user-provided naming convention",
5 | "main": "index.js",
6 | "files": [
7 | "lib/"
8 | ],
9 | "scripts": {
10 | "test": "jest",
11 | "semantic-release": "semantic-release -e ./config/release.config.js",
12 | "prepare": "npm run compile",
13 | "clean": "jest --clearCache",
14 | "compile": "babel src --out-dir lib --extensions \".ts\" --source-maps both",
15 | "lint": "tslint -c tslint.json -p tsconfig.json --project . --fix",
16 | "type-check": "tsc"
17 | },
18 | "engines": {
19 | "node": ">=8",
20 | "npm": ">=5"
21 | },
22 | "keywords": [
23 | "media files",
24 | "media",
25 | "media files scanner",
26 | "naming",
27 | "convention",
28 | "naming convention",
29 | "torrent",
30 | "library",
31 | "parser",
32 | "torrent file",
33 | "parse torrent",
34 | "parse torrent file",
35 | "parse torrent name"
36 | ],
37 | "author": "jy95",
38 | "license": "MIT",
39 | "repository": {
40 | "type": "git",
41 | "url": "https://github.com/jy95/mediaScan.git"
42 | },
43 | "dependencies": {
44 | "bluebird": "^3.7.2",
45 | "filehound": "^1.17.6",
46 | "lodash": "^4.17.21",
47 | "parse-torrent-title": "^1.4.0",
48 | "video-extensions": "^1.2.0"
49 | },
50 | "devDependencies": {
51 | "@babel/cli": "^7.0.0-beta.47",
52 | "@babel/core": "^7.5.5",
53 | "@babel/plugin-proposal-class-properties": "^7.0.0-beta.47",
54 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0-beta.47",
55 | "@babel/preset-env": "^7.0.0-beta.47",
56 | "@babel/preset-typescript": "^7.0.0-beta.47",
57 | "@semantic-release/changelog": "^5.0.0",
58 | "@semantic-release/git": "^7.0.0",
59 | "@types/jest": "^25.1.0",
60 | "@types/node": "^14.0.9",
61 | "babel-minify": "^0.5.0",
62 | "conventional-changelog-eslint": "^3.0.0",
63 | "jest": "^29.7.0",
64 | "semantic-release": "^17.0.8",
65 | "ts-jest": "^26.1.0",
66 | "tslint": "^5.9.1",
67 | "typescript": "^3.5.3"
68 | },
69 | "greenkeeper": {
70 | "commitMessages": {
71 | "initialBadge": ":memo: Docs: Add Greenkeeper badge",
72 | "initialDependencies": ":gem: Upgrade: Update dependencies",
73 | "initialBranches": ":tada: Build: Whitelist greenkeeper branches",
74 | "dependencyUpdate": ":gem: Upgrade: Update ${dependency} to version ${version}",
75 | "devDependencyUpdate": ":gem: Upgrade: Update ${dependency} to version ${version}",
76 | "dependencyPin": ":bug: Fix: Pin ${dependency} to ${oldVersion}",
77 | "devDependencyPin": ":bug: Fix: Pin ${dependency} to ${oldVersion}"
78 | }
79 | },
80 | "snyk": true
81 | }
82 |
--------------------------------------------------------------------------------
/src/MediaScan.ts:
--------------------------------------------------------------------------------
1 | // Imports
2 | import PromiseLib from "bluebird";
3 | import FileHound from "filehound";
4 | import {
5 | cloneDeep,
6 | difference,
7 | filter,
8 | forIn,
9 | has,
10 | includes,
11 | map,
12 | reduce,
13 | some,
14 | uniq,
15 | } from "lodash";
16 | import { normalize } from "path";
17 |
18 | const videosExtension = require("video-extensions");
19 | import { EventEmitter } from "events";
20 |
21 | import {
22 | compose,
23 | filter as filterFP,
24 | pluck,
25 | reduce as reduceFP,
26 | } from "lodash/fp";
27 |
28 | // local import
29 | import {
30 | filterMoviesByProperties,
31 | filterTvSeriesByProperties,
32 | } from "./filters/filterProperties";
33 | import * as MediaScanTypes from "./MediaScanTypes";
34 | import {
35 | defaultParser,
36 | defaultWhichCategoryFunction,
37 | promisifiedAccess,
38 | } from "./utils/utils_functions";
39 |
40 | /**
41 | * Class representing the MediaScan Library
42 | * @extends {EventEmitter}
43 | */
44 | class MediaScan extends EventEmitter {
45 | // constants getter for external purposes (example create a custom whichCategory function)
46 | public static readonly MOVIES_TYPE = MediaScanTypes.Category.MOVIES_TYPE;
47 | public static readonly TV_SERIES_TYPE = MediaScanTypes.Category
48 | .TV_SERIES_TYPE;
49 | // properties
50 | // Default path , if paths is empty
51 | protected defaultPath: string;
52 | // the parser to extract the useful data from name
53 | protected parser: MediaScanTypes.ParseFunction;
54 | // Function that tell us what is the category of the TPN
55 | protected whichCategory: MediaScanTypes.WhichCategoryFunction;
56 | // all the paths that will be explored
57 | protected paths: string[];
58 | // the mapping between file and Category
59 | protected categoryForFile: Map;
60 | // where I keep the result of Category
61 | protected stores: MediaScanTypes.MapSet<
62 | MediaScanTypes.TPN | MediaScanTypes.TPN_Extended
63 | >;
64 |
65 | constructor(
66 | {
67 | defaultPath = process.cwd(),
68 | paths = [],
69 | allFilesWithCategory = new Map(),
70 | movies = new Set(),
71 | series = new Map(),
72 | }: MediaScanTypes.DataParameters = {},
73 | {
74 | parser = defaultParser,
75 | whichCategory = defaultWhichCategoryFunction,
76 | }: MediaScanTypes.CustomFunctionsConfig = {},
77 | ) {
78 | super();
79 | this.parser = parser;
80 | this.whichCategory = whichCategory;
81 | this.defaultPath = defaultPath;
82 | this.paths = paths;
83 | this.stores = new Map();
84 | this.stores.set(MediaScan.MOVIES_TYPE, movies);
85 | this.stores.set(MediaScan.TV_SERIES_TYPE, series);
86 | this.categoryForFile = allFilesWithCategory;
87 | }
88 |
89 | private addNewFiles(files: string[]): Promise {
90 | return new PromiseLib((resolve, reject) => {
91 | try {
92 | // find the new files to be added
93 | const alreadyFoundFiles = [...this.categoryForFile.keys()];
94 | const newFiles = difference(files, alreadyFoundFiles);
95 |
96 | // process each file
97 | const scanningResult = reduce(
98 | newFiles,
99 | (result, file) => {
100 | const jsonFile = this.parser(file);
101 | // extend this object in order to be used by this library
102 | Object.assign(jsonFile, { filePath: file });
103 | // find out which type of this file
104 | // if it has not undefined properties (season and episode) => TV_SERIES , otherwise MOVIE
105 | const fileCategory = this.whichCategory(jsonFile);
106 | // add it in found files
107 | this.categoryForFile.set(file, fileCategory);
108 | // store the result for next usage
109 | if (has(result, fileCategory)) {
110 | Array.prototype.push.apply(result[fileCategory], [jsonFile]);
111 | } else {
112 | result[fileCategory] = [jsonFile];
113 | }
114 | return result;
115 | },
116 | {},
117 | );
118 |
119 | // add the found movies
120 | if (scanningResult[MediaScan.MOVIES_TYPE] !== undefined) {
121 | this.stores.set(
122 | MediaScan.MOVIES_TYPE,
123 | new Set([
124 | ...this.allMovies,
125 | ...scanningResult[MediaScan.MOVIES_TYPE],
126 | ]),
127 | );
128 | }
129 |
130 | // add the found tv-series
131 | if (scanningResult[MediaScan.TV_SERIES_TYPE] !== undefined) {
132 | // mapping for faster result(s)
133 | const newSeries = reduce(
134 | scanningResult[MediaScan.TV_SERIES_TYPE],
135 | (result, tvSeries) => {
136 | if (has(result, tvSeries.title)) {
137 | Array.prototype.push.apply(result[tvSeries.title], [tvSeries]);
138 | } else {
139 | result[tvSeries.title] = [tvSeries];
140 | }
141 | return result;
142 | },
143 | {},
144 | );
145 | // fastest way to update things
146 | const newTvSeries = this.allTvSeries;
147 | forIn(newSeries, (seriesArray, seriesName) => {
148 | const resultSet = newTvSeries.has(seriesName)
149 | ? newTvSeries.get(seriesName)
150 | : new Set();
151 | newTvSeries.set(
152 | seriesName,
153 | new Set([...resultSet, ...seriesArray]),
154 | );
155 | });
156 | // update the stores var
157 | this.stores.set(MediaScan.TV_SERIES_TYPE, newTvSeries);
158 | }
159 |
160 | resolve();
161 | } catch (err) {
162 | reject(err);
163 | }
164 | }).bind(this);
165 | }
166 |
167 | public static listVideosExtension(): string[] {
168 | return videosExtension;
169 | }
170 |
171 | public addNewPath(...paths: string[]): Promise {
172 | // the user should provide us at lest a path
173 | if (paths.length === 0) {
174 | this.emit("missing_parameter", {
175 | functionName: "addNewPath",
176 | });
177 | return Promise.reject(new Error("Missing parameter"));
178 | }
179 |
180 | return new PromiseLib((resolve, reject) => {
181 | PromiseLib.map(paths, (path) => promisifiedAccess(path))
182 | .then(() => {
183 | // keep only unique paths
184 | // use normalize for cross platform's code
185 | this.paths = uniq([...this.paths, ...paths.map(normalize)]);
186 | this.emit("addNewPath", { paths: this.paths });
187 | resolve("All paths were added!");
188 | })
189 | .catch((e) => {
190 | this.emit("error_in_function", {
191 | error: e.message,
192 | functionName: "addNewPath",
193 | });
194 | reject(e);
195 | });
196 | }).bind(this);
197 | }
198 |
199 | public hasPathsProvidedByUser(): boolean {
200 | return this.paths.length !== 0;
201 | }
202 |
203 | public scan(): Promise {
204 | return new PromiseLib((resolve, reject) => {
205 | FileHound.create()
206 | .paths(this.paths.length === 0 ? this.defaultPath : this.paths)
207 | .ext(videosExtension)
208 | .find()
209 | .then((files) =>
210 | PromiseLib.join(this.addNewFiles(files), () => {
211 | return Promise.resolve(files);
212 | }),
213 | )
214 | .then((files) => {
215 | this.emit("scan", { files });
216 | resolve("Scanning completed");
217 | })
218 | .catch((err) => {
219 | this.emit("error_in_function", {
220 | error: err.message,
221 | functionName: "scan",
222 | });
223 | reject(err);
224 | });
225 | }).bind(this);
226 | }
227 |
228 | public removeOldFiles(...files: string[]): Promise {
229 | return new PromiseLib((resolve, reject) => {
230 | try {
231 | // processing
232 | const mappedFiles = compose(
233 | filterFP((resultObject) => resultObject.category !== undefined),
234 | pluck((file) => {
235 | return { filePath: file, category: this.categoryForFile.get(file) };
236 | }),
237 | // to handle platform support paths
238 | pluck((file) => normalize(file)),
239 | )(files);
240 | const filterContentType = (requestedType) => (file) =>
241 | file.category === requestedType;
242 |
243 | // remove the mapping of each deleted file(s)
244 | for (const file of mappedFiles) {
245 | this.categoryForFile.delete(file.filePath);
246 | }
247 |
248 | // movies files
249 | const moviesFiles = filter(
250 | mappedFiles,
251 | filterContentType(MediaScan.MOVIES_TYPE),
252 | );
253 | const moviesFilePaths = map(moviesFiles, "filePath");
254 |
255 | // for movies, just an easy removal
256 | if (moviesFiles.length > 0) {
257 | // update the filtered Set
258 | this.stores.set(
259 | MediaScan.MOVIES_TYPE,
260 | new Set(
261 | filter(
262 | ...this.allMovies,
263 | (movie) => !some(moviesFilePaths, movie.filePath),
264 | ),
265 | ),
266 | );
267 | }
268 |
269 | // tv-series
270 | const seriesFiles = filter(
271 | mappedFiles,
272 | filterContentType(MediaScan.TV_SERIES_TYPE),
273 | );
274 |
275 | // for series , a bit more complex
276 | if (seriesFiles.length > 0) {
277 | // Get the series and their files that will be deleted
278 | const seriesShows = compose(
279 | reduceFP((acc, parsedFile) => {
280 | if (!has(acc, parsedFile.seriesName)) {
281 | acc[parsedFile.seriesName] = [];
282 | }
283 | Array.prototype.push.apply(acc[parsedFile.seriesName], [
284 | parsedFile.filePath,
285 | ]);
286 | return acc;
287 | }, {}),
288 | pluck((series) => {
289 | return {
290 | ...series,
291 | seriesName: this.parser(series.filePath).title,
292 | };
293 | }),
294 | )(seriesFiles);
295 |
296 | const newTvSeries = this.allTvSeries;
297 | // check if needed to store new Value
298 | let shouldUpdate = false;
299 | forIn(seriesShows, (seriesArray, seriesName) => {
300 | const previousSet = newTvSeries.has(seriesName)
301 | ? newTvSeries.get(seriesName)
302 | : new Set();
303 | const filteredSet: Set = new Set(
304 | filter(
305 | [...previousSet],
306 | (episode) => !includes(seriesArray, episode.filePath),
307 | ),
308 | );
309 | // should I update later ?
310 | if (previousSet.size !== filteredSet.size) {
311 | shouldUpdate = true;
312 | }
313 | // if the filtered set is empty => no more episodes for this series
314 | if (filteredSet.size === 0) {
315 | newTvSeries.delete(seriesName);
316 | } else {
317 | newTvSeries.set(seriesName, filteredSet);
318 | }
319 | });
320 | // save the updated map
321 | if (shouldUpdate) {
322 | this.stores.set(MediaScan.TV_SERIES_TYPE, newTvSeries);
323 | }
324 | }
325 |
326 | this.emit("removeOldFiles", { files });
327 | resolve({
328 | files,
329 | message: "The files have been deleted from the library",
330 | });
331 | } catch (err) {
332 | this.emit("error_in_function", {
333 | error: err.message,
334 | functionName: "removeOldFiles",
335 | });
336 | reject(err);
337 | }
338 | }).bind(this);
339 | }
340 |
341 | get allMovies(): Set {
342 | return this.stores.get(MediaScan.MOVIES_TYPE) as Set<
343 | MediaScanTypes.TPN_Extended
344 | >;
345 | }
346 |
347 | get allTvSeries(): Map> {
348 | return this.stores.get(MediaScan.TV_SERIES_TYPE) as Map<
349 | string,
350 | Set
351 | >;
352 | }
353 |
354 | get allFilesWithCategory(): Map {
355 | return cloneDeep(this.categoryForFile);
356 | }
357 |
358 | get allTvSeriesNames(): string[] {
359 | return [...this.allTvSeries.keys()];
360 | }
361 |
362 | // full data of lib as JSON string
363 | public toJSON(): string {
364 | return JSON.stringify(this.toJSONObject());
365 | }
366 |
367 | // data as a JSON object
368 | public toJSONObject(looseMode?: boolean): MediaScanTypes.LibAsJson {
369 | // if in loose Mode , the objects will only contains the mapping between filepath and Category
370 | const toBeSerialized = looseMode
371 | ? [["allFilesWithCategory", [...this.allFilesWithCategory]]]
372 | : [
373 | ["paths", [...this.paths]],
374 | ["allFilesWithCategory", [...this.allFilesWithCategory]],
375 | ["movies", [...this.allMovies]],
376 | [
377 | "series",
378 | this.allTvSeriesNames.reduce((acc, currentSeries) => {
379 | acc.push([
380 | currentSeries,
381 | [...this.allTvSeries.get(currentSeries)],
382 | ]);
383 | return acc;
384 | }, []),
385 | ],
386 | ];
387 | return toBeSerialized.reduce((result, [key, value]) => {
388 | result[key as string] = value;
389 | return result;
390 | }, {});
391 | }
392 |
393 | public static createFromJSON(
394 | json: MediaScanTypes.LibAsJson,
395 | customConfig?: MediaScanTypes.CustomFunctionsConfig,
396 | ): MediaScan {
397 | const config: MediaScanTypes.DataParameters = {};
398 | // transform the param
399 | /* istanbul ignore else */
400 | if (json.allFilesWithCategory) {
401 | config.allFilesWithCategory = new Map(json.allFilesWithCategory);
402 | }
403 | /* istanbul ignore else */
404 | if (json.movies) {
405 | config.movies = new Set(json.movies);
406 | }
407 | /* istanbul ignore else */
408 | if (json.series) {
409 | const createdMap = new Map();
410 | for (const [seriesTitle, setSeries] of json.series) {
411 | createdMap.set(seriesTitle, new Set(setSeries));
412 | }
413 | config.series = createdMap;
414 | }
415 | /* istanbul ignore else */
416 | if (json.paths) {
417 | config.paths = json.paths;
418 | }
419 | return new MediaScan(config, customConfig);
420 | }
421 |
422 | public filterMovies(searchParameters: MediaScanTypes.SearchParameters = {}) {
423 | // apply params based on types
424 | return filterMoviesByProperties(searchParameters, this.allMovies);
425 | }
426 |
427 | public filterTvSeries(
428 | searchParameters: MediaScanTypes.SearchParameters = {},
429 | ) {
430 | return filterTvSeriesByProperties(searchParameters, this.allTvSeries);
431 | }
432 | }
433 |
434 | // just to be sure Babel doesn't mess up common js
435 | module.exports = MediaScan;
436 |
--------------------------------------------------------------------------------
/src/MediaScanTypes.ts:
--------------------------------------------------------------------------------
1 | export interface TPN {
2 | title: string;
3 | year?: number;
4 | resolution?: string;
5 | extended?: boolean;
6 | unrated?: boolean;
7 | proper?: boolean;
8 | repack?: boolean;
9 | convert?: boolean;
10 | hardcoded?: boolean;
11 | retail?: boolean;
12 | remastered?: boolean;
13 | region?: string;
14 | container?: string;
15 | source?: string;
16 | codec?: string;
17 | audio?: string;
18 | group?: string;
19 | season?: number;
20 | episode?: number;
21 | language?: string;
22 | }
23 |
24 | // extended by my own purpose
25 | export interface TPN_Extended extends TPN {
26 | filePath: string;
27 | }
28 |
29 | // A parsing function to be used with this lib
30 | // fullPathFile is the full path to the file - useful if you want to extrapolate things
31 | export type ParseFunction = (fullPathFile: string) => TPN | TPN_Extended;
32 |
33 | // the media files categories
34 | // const enum
35 | export enum Category {
36 | MOVIES_TYPE = "MOVIES",
37 | TV_SERIES_TYPE = "TV_SERIES",
38 | }
39 |
40 | // which category is this file
41 | export type WhichCategoryFunction = (object: TPN) => Category;
42 |
43 | // The sub way to store all kind of media files found in paths
44 | export type MappedType = Map>;
45 | export type MapSet = Map | Set>;
46 |
47 | // example '<=25'
48 | export type NumberSearchSyntax = string;
49 |
50 | // to handle number operations
51 | export interface NumberExpressionObject {
52 | operator: "==" | ">" | "<" | ">=" | "<=";
53 | number: number;
54 | }
55 | // const enum
56 | export enum AdditionalPropertiesType {
57 | STRING = "string",
58 | NUMBER = "number",
59 | BOOLEAN = "boolean",
60 | }
61 |
62 | // additional Properties
63 | export interface AdditionalProperties {
64 | type: AdditionalPropertiesType;
65 | name: string;
66 | value: boolean | string | string[] | number | NumberSearchSyntax;
67 | }
68 |
69 | export interface MinimalSearchParameters {
70 | additionalProperties?: AdditionalProperties[];
71 | }
72 |
73 | export interface DefaultSearchParameters extends MinimalSearchParameters {
74 | extended?: boolean;
75 | unrated?: boolean;
76 | proper?: boolean;
77 | repack?: boolean;
78 | convert?: boolean;
79 | hardcoded?: boolean;
80 | retail?: boolean;
81 | remastered?: boolean;
82 | season?: number | NumberSearchSyntax;
83 | episode?: number | NumberSearchSyntax;
84 | year?: number | NumberSearchSyntax;
85 | title?: string | string[];
86 | resolution?: string | string[];
87 | codec?: string | string[];
88 | audio?: string | string[];
89 | group?: string | string[];
90 | region?: string | string[];
91 | container?: string | string[];
92 | language?: string | string[];
93 | source?: string | string[];
94 | }
95 |
96 | // search parameters for filter functions
97 | export type SearchParameters = Partial;
98 |
99 | // for filtering tuples inside SearchParameters
100 | export type filterTuple = [string, T];
101 |
102 | // for optimized filtering function
103 | export type filterFunctionTuple = [{(set: Set, propertiesMap: Map)}, Map];
104 |
105 | // for tuples inside json in createFromJSON
106 | export type mappingStringAndCategory = [string, Category];
107 | export type mappingStringAndTPNArray = [string, TPN[]];
108 |
109 | // json result to be used in createFromJSON
110 | export interface LibAsJson {
111 | allFilesWithCategory?: mappingStringAndCategory[];
112 | movies?: TPN[];
113 | series?: mappingStringAndTPNArray[];
114 | paths?: string[];
115 | }
116 |
117 | // the data parameters for constructor (aka first argument)
118 | export interface DataParameters {
119 | defaultPath?: string; // Default path , if paths is empty
120 | paths?: string[]; // all the paths that will be explored
121 | allFilesWithCategory?: Map; // the mapping between file and Category
122 | movies?: Set; // all the movies
123 | series?: Map>;
124 | }
125 |
126 | // the custom functions (in order to have a different behaviour) for constructor (aka second argument)
127 | export interface CustomFunctionsConfig {
128 | parser?: ParseFunction;
129 | whichCategory?: WhichCategoryFunction;
130 | }
131 |
--------------------------------------------------------------------------------
/src/filters/filterBooleanProperty.ts:
--------------------------------------------------------------------------------
1 | /** Provides a map with valid default properties */
2 | import * as MediaScanTypes from "../MediaScanTypes";
3 | import { filterDefaultProperties } from "../utils/utils_functions";
4 |
5 | export function filterDefaultBooleanProperties(
6 | searchObject: MediaScanTypes.DefaultSearchParameters,
7 | ): Array> {
8 | const propertiesNames = [
9 | "extended",
10 | "unrated",
11 | "proper",
12 | "repack",
13 | "convert",
14 | "hardcoded",
15 | "retail",
16 | "remastered",
17 | ];
18 | return filterDefaultProperties(
19 | propertiesNames,
20 | searchObject,
21 | (value) => {
22 | return meetBooleanSpec(value);
23 | },
24 | (key, value) => [key, value],
25 | );
26 | }
27 |
28 | /** Filter the set based on boolean properties */
29 | export function filterByBoolean(
30 | set: Set,
31 | propertiesMap: Map,
32 | ): Set {
33 | // first step : get an array so that we can do filter/reduce stuff
34 | // second step : iterate the propertiesMap and do filter and return the filtered array
35 | // val[0] : the key ; val[1] : the value
36 | return new Set(
37 | Array.from(propertiesMap.entries()).reduce(
38 | // eslint-disable-next-line max-len
39 | (currentMoviesArray, val) =>
40 | currentMoviesArray.filter((TPN) => TPN[val[0]] === val[1]),
41 | [...set],
42 | ),
43 | );
44 | }
45 |
46 | // Just for type check this type
47 | export function meetBooleanSpec(value) {
48 | return value === true || value === false;
49 | }
50 |
--------------------------------------------------------------------------------
/src/filters/filterNumberProperty.ts:
--------------------------------------------------------------------------------
1 | import * as MediaScanTypes from "../MediaScanTypes";
2 | import { filterDefaultProperties } from "../utils/utils_functions";
3 |
4 | const validExpression = /^(==|>|<|>=|<=)(\d+)$/;
5 |
6 | // operator functions
7 | const ops = {
8 | "<": (a, b) => a < b,
9 | "<=": (a, b) => a <= b,
10 | "==": (a, b) => a == b,
11 | ">": (a, b) => a > b,
12 | ">=": (a, b) => a >= b,
13 | };
14 |
15 | /**
16 | * Convert the param to valid expression object for filter function
17 | */
18 | export function convertToValidExpression(
19 | param: string | number,
20 | ): MediaScanTypes.NumberExpressionObject {
21 | let returnValue;
22 |
23 | switch (typeof param) {
24 | case "string":
25 | // if it is a valid number expression like the regex
26 | /* istanbul ignore else */
27 | if (validExpression.test(param as string)) {
28 | const result = (param as string).match(validExpression);
29 | returnValue = {
30 | number: Number(result[2]),
31 | operator: result[1],
32 | };
33 | }
34 | break;
35 |
36 | // if the param is a number
37 | case "number":
38 | returnValue = {
39 | number: param as number,
40 | operator: "==",
41 | };
42 | break;
43 | }
44 |
45 | return returnValue;
46 | }
47 |
48 | /**
49 | * Filter function for filterByNumber
50 | */
51 | function resolveExpression(
52 | property: string,
53 | expressionObject: MediaScanTypes.NumberExpressionObject,
54 | object: MediaScanTypes.TPN | MediaScanTypes.TPN_Extended,
55 | ): boolean {
56 | return ops[expressionObject.operator](
57 | object[property],
58 | expressionObject.number,
59 | );
60 | }
61 |
62 | export function filterDefaultNumberProperties(
63 | searchObject: MediaScanTypes.DefaultSearchParameters,
64 | ): Array> {
65 | const propertiesNames = ["season", "episode", "year"];
66 | return filterDefaultProperties(
67 | propertiesNames,
68 | searchObject,
69 | (value) => {
70 | return meetNumberSpec(value);
71 | },
72 | (key, value) => [key, convertToValidExpression(value)],
73 | );
74 | }
75 |
76 | /** Filter the set based on string properties */
77 | // export function filterByNumber(set: Set, propertiesMap: Map) : Set
78 | export function filterByNumber(
79 | set: Set,
80 | propertiesMap: Map,
81 | ): Set {
82 | // first step : get an array so that we can do filter/reduce stuff
83 | // second step : iterate the propertiesMap and do filter and return the filtered array
84 | // val[0] : the key ; val[1] : the value
85 | return new Set(
86 | Array.from(propertiesMap.entries()).reduce(
87 | // eslint-disable-next-line max-len
88 | (currentMoviesArray, val) =>
89 | currentMoviesArray.filter((TPN) =>
90 | resolveExpression(val[0], val[1], TPN),
91 | ),
92 | [...set],
93 | ),
94 | );
95 | }
96 |
97 | // Just for type check this type
98 | export function meetNumberSpec(value) {
99 | if (typeof value === "number") {
100 | return true;
101 | } else if (typeof value !== "string") {
102 | return false;
103 | } else {
104 | return validExpression.test(value as string);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/filters/filterProperties.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-useless-escape,max-len */
2 | import { isEmpty } from "lodash";
3 | import { compose, filter as filterFP, pluck } from "lodash/fp";
4 | /**
5 | * Boolean properties filter
6 | */
7 | import {
8 | filterByBoolean,
9 | filterDefaultBooleanProperties,
10 | meetBooleanSpec,
11 | } from "./filterBooleanProperty";
12 |
13 | /**
14 | * Number properties filter
15 | */
16 | import {
17 | convertToValidExpression,
18 | filterByNumber,
19 | filterDefaultNumberProperties,
20 | meetNumberSpec,
21 | } from "./filterNumberProperty";
22 |
23 | /**
24 | * String properties filter
25 | */
26 | import * as MediaScanTypes from "../MediaScanTypes";
27 | import {
28 | filterByString,
29 | filterDefaultStringProperties,
30 | meetStringSpec,
31 | } from "./filterStringProperty";
32 |
33 | function mapProperties(
34 | searchParameters: MediaScanTypes.SearchParameters,
35 | ): {
36 | booleanFieldsSearchMap: Map;
37 | numberFieldsSearchMap: Map;
38 | stringFieldsSearchMap: Map;
39 | } {
40 | // organize search based on field type : boolean - string - number . Now optimized by Me XD
41 | // add the optional new properties , optionally provided by user
42 | const additionalProperties =
43 | searchParameters.additionalProperties === undefined
44 | ? []
45 | : searchParameters.additionalProperties;
46 | const filterAdditionalProperties = (type) => (newProperty) =>
47 | newProperty.type === type;
48 | const booleanFieldsSearchArray = filterDefaultBooleanProperties(
49 | searchParameters,
50 | );
51 | const numberFieldsSearchArray = filterDefaultNumberProperties(
52 | searchParameters,
53 | );
54 | const stringFieldsSearchArray = filterDefaultStringProperties(
55 | searchParameters,
56 | );
57 |
58 | // add additional Properties into the proper array
59 | Array.prototype.push.apply(
60 | booleanFieldsSearchArray,
61 | compose(
62 | pluck(({ name, value }) => [name, value]),
63 | filterFP(({ value }) => meetBooleanSpec(value)),
64 | filterFP(
65 | filterAdditionalProperties(
66 | MediaScanTypes.AdditionalPropertiesType.BOOLEAN,
67 | ),
68 | ),
69 | )(additionalProperties),
70 | );
71 |
72 | Array.prototype.push.apply(
73 | numberFieldsSearchArray,
74 | compose(
75 | pluck(({ name, value }) => [
76 | name,
77 | convertToValidExpression(value as number | string),
78 | ]),
79 | filterFP(({ value }) => meetNumberSpec(value)),
80 | filterFP(
81 | filterAdditionalProperties(
82 | MediaScanTypes.AdditionalPropertiesType.NUMBER,
83 | ),
84 | ),
85 | )(additionalProperties),
86 | );
87 |
88 | Array.prototype.push.apply(
89 | stringFieldsSearchArray,
90 | compose(
91 | pluck(({ name, value }) => [name, value]),
92 | filterFP(({ value }) => meetStringSpec(value)),
93 | filterFP(
94 | filterAdditionalProperties(
95 | MediaScanTypes.AdditionalPropertiesType.STRING,
96 | ),
97 | ),
98 | )(additionalProperties),
99 | );
100 |
101 | return {
102 | booleanFieldsSearchMap: new Map(booleanFieldsSearchArray),
103 | numberFieldsSearchMap: new Map<
104 | string,
105 | MediaScanTypes.NumberExpressionObject
106 | >(numberFieldsSearchArray),
107 | stringFieldsSearchMap: new Map(
108 | stringFieldsSearchArray,
109 | ),
110 | };
111 | }
112 |
113 | /** Filter the movies based on search parameters */
114 | export function filterMoviesByProperties(
115 | searchParameters: MediaScanTypes.SearchParameters,
116 | allMovies: Set,
117 | ): Set {
118 | // Check if empty - for faster result
119 | if (isEmpty(searchParameters)) {
120 | return allMovies;
121 | }
122 |
123 | const {
124 | booleanFieldsSearchMap,
125 | stringFieldsSearchMap,
126 | numberFieldsSearchMap,
127 | } = mapProperties(searchParameters);
128 | const filterStuff: MediaScanTypes.filterFunctionTuple[] = [
129 | [filterByBoolean, booleanFieldsSearchMap],
130 | [filterByString, stringFieldsSearchMap],
131 | [filterByNumber, numberFieldsSearchMap],
132 | ];
133 |
134 | return filterStuff.reduce(
135 | (processingResult, [filterFunction, propertiesMap]) => {
136 | return processingResult.size > 0 && propertiesMap.size > 0
137 | ? filterFunction(processingResult, propertiesMap)
138 | : processingResult;
139 | },
140 | allMovies,
141 | );
142 | }
143 |
144 | /** Filter the tv series based on search parameters */
145 | export function filterTvSeriesByProperties(
146 | searchParameters: MediaScanTypes.SearchParameters,
147 | allTvSeries: Map>,
148 | ): Map> {
149 | // Check if empty for faster result
150 | if (isEmpty(searchParameters)) {
151 | return allTvSeries;
152 | }
153 |
154 | const {
155 | booleanFieldsSearchMap,
156 | stringFieldsSearchMap,
157 | numberFieldsSearchMap,
158 | } = mapProperties(searchParameters);
159 | const filterStuff: MediaScanTypes.filterFunctionTuple[] = [
160 | [filterByBoolean, booleanFieldsSearchMap],
161 | [filterByString, stringFieldsSearchMap],
162 | [filterByNumber, numberFieldsSearchMap],
163 | ];
164 |
165 | // apply the filters
166 | return new Map(
167 | [...allTvSeries].reduce((processingArray, [showName, showSet]) => {
168 | // execute the filter functions
169 | const filteredSet = filterStuff.reduce(
170 | (currentFilteredSet, [filterFunction, propertiesMap]) => {
171 | return currentFilteredSet.size > 0 && propertiesMap.size > 0
172 | ? filterFunction(currentFilteredSet, propertiesMap)
173 | : currentFilteredSet;
174 | },
175 | showSet,
176 | );
177 | // add this entry if there is soms episode(s) left
178 | if (filteredSet.size > 0) {
179 | processingArray.push([showName, filteredSet]);
180 | }
181 | // reducer call
182 | return processingArray;
183 | }, []),
184 | );
185 | }
186 |
--------------------------------------------------------------------------------
/src/filters/filterStringProperty.ts:
--------------------------------------------------------------------------------
1 | /** Provides a map with valid default properties */
2 | import * as MediaScanTypes from "../MediaScanTypes";
3 | import { filterDefaultProperties } from "../utils/utils_functions";
4 |
5 | export function filterDefaultStringProperties(
6 | searchObject: MediaScanTypes.DefaultSearchParameters,
7 | ): Array> {
8 | const propertiesNames = [
9 | "title",
10 | "resolution",
11 | "codec",
12 | "audio",
13 | "group",
14 | "region",
15 | "container",
16 | "language",
17 | "source",
18 | ];
19 | return filterDefaultProperties(
20 | propertiesNames,
21 | searchObject,
22 | (value) => {
23 | return meetStringSpec(value);
24 | },
25 | (key, value) => [key, value],
26 | );
27 | }
28 |
29 | /** Filter function for filterByString */
30 | function filterFunctionByType(
31 | property: string,
32 | expected: string[] | string,
33 | object: MediaScanTypes.TPN,
34 | ): boolean {
35 | if (Array.isArray(expected)) {
36 | return expected.includes(object[property]);
37 | }
38 | return object[property] === expected;
39 | }
40 |
41 | /** Filter the set based on string properties */
42 | export function filterByString(
43 | set: Set,
44 | propertiesMap: Map,
45 | ): Set {
46 | // first step : get an array so that we can do filter/reduce stuff
47 | // second step : iterate the propertiesMap and do filter and return the filtered array
48 | // val[0] : the key ; val[1] : the value
49 | return new Set(
50 | Array.from(propertiesMap.entries()).reduce(
51 | // eslint-disable-next-line max-len
52 | (currentMoviesArray, val) =>
53 | currentMoviesArray.filter((TPN) =>
54 | filterFunctionByType(val[0], val[1], TPN),
55 | ),
56 | [...set],
57 | ),
58 | );
59 | }
60 |
61 | // Just for type check this type
62 | export function meetStringSpec(value) {
63 | if (Array.isArray(value)) {
64 | return value.every((elem) => typeof elem === "string");
65 | } else {
66 | return typeof value === "string";
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/utils/utils_functions.ts:
--------------------------------------------------------------------------------
1 | // Check properties
2 | import PromiseLib from "bluebird";
3 | import { access, constants as FsConstants } from "fs";
4 | import { compose, filter as filterFP, pluck } from "lodash/fp";
5 | import { parse as nameParser } from "parse-torrent-title";
6 | import { basename } from "path";
7 | import * as MediaScanTypes from "../MediaScanTypes";
8 |
9 | export function checkProperties(obj, properties): boolean {
10 | return properties.every((x) => x in obj && obj[x]);
11 | }
12 |
13 | /**
14 | * Bluebird seems to have an issue with fs.access - Workaround function
15 | */
16 | export function promisifiedAccess(path): Promise {
17 | return new PromiseLib((resolve, reject) => {
18 | access(path, FsConstants.F_OK | FsConstants.R_OK, (err) => {
19 | if (err) {
20 | reject(err);
21 | }
22 | resolve();
23 | });
24 | });
25 | }
26 |
27 | // Default implementation to know which category is this file
28 | export function defaultWhichCategoryFunction(
29 | object: MediaScanTypes.TPN,
30 | ): MediaScanTypes.Category {
31 | // workaround : const string enum aren't compiled correctly with Babel
32 | return checkProperties(object, ["season", "episode"])
33 | ? MediaScanTypes.Category.TV_SERIES_TYPE
34 | : MediaScanTypes.Category.MOVIES_TYPE;
35 | }
36 |
37 | // Generic filter for default properties
38 | export function filterDefaultProperties(
39 | propertiesNames: string[],
40 | search: MediaScanTypes.SearchParameters,
41 | meetSpecFunction: (value) => boolean,
42 | transformFunction: (key: string, value) => MediaScanTypes.filterTuple,
43 | ): Array> {
44 | return compose(
45 | pluck((currentProperty) =>
46 | transformFunction(currentProperty, search[currentProperty]),
47 | ),
48 | filterFP((currentProperty) => meetSpecFunction(search[currentProperty])),
49 | )(propertiesNames);
50 | }
51 |
52 | // default parser
53 | export function defaultParser(fullPathFile: string) {
54 | return nameParser(basename(fullPathFile));
55 | }
56 |
--------------------------------------------------------------------------------
/tsconfig.jest.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "emitDeclarationOnly": false
6 | },
7 | "preset": "ts-jest"
8 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions":{
3 | "allowSyntheticDefaultImports":true,
4 | "module":"commonjs",
5 | "target":"es5",
6 | "lib":[
7 | "ES6",
8 | "ES2017",
9 | "ESNext"
10 | ],
11 | "downlevelIteration":true,
12 | "moduleResolution":"node",
13 | "declaration":true,
14 | "emitDeclarationOnly":true,
15 | "declarationDir":"./lib"
16 | },
17 | "exclude":[
18 | "node_modules"
19 | ],
20 | "include":[
21 | "src/**/*"
22 | ],
23 | "strict":true,
24 | "preset":"ts-jest"
25 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "error",
3 | "extends": [
4 | "tslint:recommended"
5 | ],
6 | "jsRules": {},
7 | "rules": {
8 | "class-name": false,
9 | "no-var-requires": false,
10 | "triple-equals": false,
11 | "no-bitwise": false,
12 | "interface-name": [
13 | "never-prefix"
14 | ],
15 | "member-ordering": [
16 | true,
17 | {
18 | "order": [
19 | "public-static-field",
20 | "public-constructor",
21 | "private-instance-method",
22 | "public-instance-method"
23 | ]
24 | }
25 | ]
26 | },
27 | "rulesDirectory": []
28 | }
--------------------------------------------------------------------------------