Skip to content

Commit b689a7b

Browse files
authored
Automatic import from remote drive (#86)
* Add initial files for handling automatic importing from seedboxes * Initial structure and storage driver integration * Add method to find files recursively * Remove . from file extensions * Add sample config for seedboxes * Filter which movies need to be import from remote storage * Basic importing of movies from remote storage * Add support for importing episodes * Update package-lock.json * Update package-lock.json * Add rest endpoint to import media from remote storage * Only import matched episodes and store episodes in series directories * Add option to disable an auto importer config
1 parent bfe5736 commit b689a7b

File tree

19 files changed

+3648
-6528
lines changed

19 files changed

+3648
-6528
lines changed

package-lock.json

Lines changed: 3269 additions & 6506 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"fluent-ffmpeg": "^2.1.2",
3535
"guessit-exec": "^0.0.1",
3636
"guessit-wrapper": "^1.0.1",
37+
"jsftp": "^2.1.3",
3738
"jsonwebtoken": "^8.5.1",
3839
"mime-types": "^2.1.30",
3940
"mkdirp": "^1.0.4",
@@ -47,7 +48,7 @@
4748
"restify-cors-middleware": "^1.1.1",
4849
"restify-errors": "^8.0.2",
4950
"sequelize": "^6.6.2",
50-
"sharp": "^0.28.1",
51+
"sharp": "^0.29.1",
5152
"socket.io": "^2.3.0",
5253
"sqlite3": "^5.0.2",
5354
"uuid": "^8.3.2",
@@ -61,9 +62,9 @@
6162
"@types/bcrypt": "^3.0.1",
6263
"@types/restify-errors": "^4.3.3",
6364
"@types/socket.io": "^2.1.13",
65+
"chai": "^4.3.3",
6466
"eslint": "^7.25.0",
6567
"eslint-plugin-jsdoc": "^32.3.3",
66-
"chai": "^4.3.3",
6768
"expect.js": "^0.3.1",
6869
"jshint": "^2.12.0",
6970
"mocha": "^8.3.2",

res/config.json

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,12 @@
8181
},
8282
"fileExtensions": {
8383
"video": [
84-
".mp4",
85-
".avi",
86-
".iso",
87-
".m4v",
88-
".mkv",
89-
".mk3d"
84+
"mp4",
85+
"avi",
86+
"iso",
87+
"m4v",
88+
"mkv",
89+
"mk3d"
9090
]
9191
},
9292
"authentication": {
@@ -126,5 +126,24 @@
126126
"key": "/etc/oblecto/keys/tria.pub"
127127
}
128128
}
129-
}
129+
},
130+
"seedboxes": [
131+
{
132+
"name": "Main seedbox",
133+
"storageDriver": "ftp",
134+
"storageDriverOptions": {
135+
"host": "",
136+
"port": 21,
137+
"username": "username",
138+
"password": "password"
139+
},
140+
"mediaImport": {
141+
"movieDirectory": "/downloads/finished/movie/",
142+
"seriesDirectory": "/downloads/finished/series/"
143+
},
144+
"automaticImport": true,
145+
"deleteOnImport": false,
146+
"enabled": false
147+
}
148+
]
130149
}

src/lib/artwork/movies/MovieArtworkCollector.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Movie } from '../../../models/movie';
22
import { fileExists } from '../../../submodules/utils';
3+
import logger from '../../../submodules/logger';
34

45
/**
56
* @typedef {import('../../oblecto').default} Oblecto
@@ -20,7 +21,7 @@ export default class MovieArtworkCollector {
2021
* @returns {Promise<void>}
2122
*/
2223
async collectArtworkMovieFanart(movie) {
23-
if (!await fileExists(this.oblecto.artworkUtils.movieFanartPath(movie))) return;
24+
if (await fileExists(this.oblecto.artworkUtils.movieFanartPath(movie))) return;
2425

2526
this.oblecto.queue.queueJob('downloadMovieFanart', movie);
2627
}
@@ -31,7 +32,7 @@ export default class MovieArtworkCollector {
3132
* @returns {Promise<void>}
3233
*/
3334
async collectArtworkMoviePoster(movie) {
34-
if (!await fileExists(this.oblecto.artworkUtils.moviePosterPath(movie))) return;
35+
if (await fileExists(this.oblecto.artworkUtils.moviePosterPath(movie))) return;
3536

3637
this.oblecto.queue.queueJob('downloadMoviePoster', movie);
3738
}
@@ -41,6 +42,8 @@ export default class MovieArtworkCollector {
4142
* @returns {Promise<void>}
4243
*/
4344
async collectAllMovieFanart() {
45+
logger.log('DEBUG', 'Collecting Movie fanart to download');
46+
4447
let movies = await Movie.findAll();
4548

4649
for (let movie of movies) {
@@ -53,6 +56,8 @@ export default class MovieArtworkCollector {
5356
* @returns {Promise<void>}
5457
*/
5558
async collectAllMoviePosters() {
59+
logger.log('DEBUG', 'Collecting Movie posters to download');
60+
5661
let movies = await Movie.findAll();
5762

5863
for (let movie of movies) {

src/lib/indexers/files/FileIndexer.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ export default class FileIndexer {
2020
this.oblecto.queue.registerJob('indexFileStreams', this.indexVideoFileStreams);
2121
}
2222

23+
/**
24+
*
25+
* @param videoPath
26+
* @return {File}
27+
*/
2328
async indexVideoFile(videoPath) {
2429
let parsedPath = Path.parse(videoPath);
2530

src/lib/indexers/movies/MovieCollector.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default class MovieCollector {
3333
* @returns {Promise<void>}
3434
*/
3535
async collectFile(file) {
36-
let extension = path.parse(file).ext.toLowerCase();
36+
let extension = path.parse(file).ext.toLowerCase().replace('.', '');
3737

3838
if (this.oblecto.config.fileExtensions.video.indexOf(extension) !== -1) {
3939
this.oblecto.queue.queueJob('indexMovie',{ path: file });

src/lib/indexers/series/SeriesCollector.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import recursive from 'recursive-readdir';
2-
import path from 'path';
2+
import { extname } from 'path';
33

44
/**
55
* @typedef {import('../../oblecto').default} Oblecto
@@ -41,7 +41,7 @@ export default class SeriesCollector {
4141
* @returns {Promise<void>}
4242
*/
4343
async collectFile(file) {
44-
let extension = path.parse(file).ext.toLowerCase();
44+
let extension = extname(file).toLowerCase().replace('.','');
4545

4646
if (this.oblecto.config.fileExtensions.video.indexOf(extension) !== -1) {
4747
this.oblecto.queue.queueJob('indexEpisode',{ path: file });

src/lib/indexers/series/SeriesIndexer.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Op } from 'sequelize';
1+
import {Op} from 'sequelize';
22

33
import AggregateIdentifier from '../../common/AggregateIdentifier';
44

@@ -7,16 +7,17 @@ import TmdbEpisodeIdentifier from './identifiers/TmdbEpisodeIdentifier';
77
import TvdbSeriesIdentifier from './identifiers/TvdbSeriesIdentifier';
88
import TvdbEpisodeIdentifier from './identifiers/TvdbEpisodeIdentifier';
99

10-
import { Series } from '../../../models/series';
11-
import { Episode } from '../../../models/episode';
12-
import { File } from '../../../models/file';
10+
import {Series} from '../../../models/series';
11+
import {Episode} from '../../../models/episode';
12+
import {File} from '../../../models/file';
1313

1414
import IdentificationError from '../../errors/IdentificationError';
1515
import logger from '../../../submodules/logger';
1616
import guessit from '../../../submodules/guessit';
1717

1818
/**
1919
* @typedef {import('../../oblecto').default} Oblecto
20+
* @typedef {import('../../../submodules/guessit').GuessitIdentification} GuessitIdentification
2021
*/
2122

2223
/**
@@ -98,6 +99,14 @@ export default class SeriesIndexer {
9899
return series;
99100
}
100101

102+
async identify(episodePath) {
103+
const guessitIdentification = await guessit.identify(episodePath);
104+
const seriesIdentification = await this.seriesIdentifier.identify(episodePath, guessitIdentification);
105+
const episodeIdentification = await this.episodeIdentifer.identify(episodePath, guessitIdentification, seriesIdentification);
106+
107+
return { ...seriesIdentification, ...episodeIdentification };
108+
}
109+
101110
/**
102111
* Index a specific file and identify it as a series
103112
*
@@ -112,8 +121,6 @@ export default class SeriesIndexer {
112121
*/
113122
const guessitIdentification = await guessit.identify(episodePath);
114123

115-
console.log(guessitIdentification.source);
116-
117124
// Some single season shows usually don't have a season in the title,
118125
// therefore whe should set it to 1 by default.
119126
if (!guessitIdentification.season) {

src/lib/oblecto/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import FileIndexer from '../indexers/files/FileIndexer';
4242

4343
import { initDatabase } from '../../submodules/database';
4444
import StreamSessionController from '../streamSessions/StreamSessionController';
45+
import SeedboxController from '../seedbox/SeedboxController';
4546

4647
export default class Oblecto {
4748
constructor(config) {
@@ -88,6 +89,9 @@ export default class Oblecto {
8889

8990
this.streamSessionController = new StreamSessionController(this);
9091

92+
this.seedboxController = new SeedboxController(this);
93+
this.seedboxController.loadAllSeedboxes();
94+
9195
if (config.federation.enable) {
9296
this.fedartionController = new FederationController(this);
9397
this.federationClientController = new FederationClientController(this);

src/lib/queue/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default class Queue {
1313
if (!this.jobs[job.id]) return callback();
1414

1515
let jobTimeout = setTimeout(() => {
16-
logger.log('WARN', `Job ${job.id} is taking a long time. Maybe something is wrong?`, JSON.stringify(job));
16+
logger.log('DEBUG', `Job ${job.id} is taking a long time. Maybe something is wrong?`, JSON.stringify(job));
1717
}, 20000);
1818

1919
this.jobs[job.id](job.attr)

0 commit comments

Comments
 (0)