mirror of
https://github.com/sakaljurgis/kodi-api-server.git
synced 2026-07-08 20:37:41 +00:00
add ability to download to multiple locations, based which one has more space
This commit is contained in:
+12
-10
@@ -1,21 +1,23 @@
|
|||||||
PORT=3000
|
#needed only for docker-compose to map your filesystem, for docker run use "/srv/data/static" volume path mapping
|
||||||
|
|
||||||
STATIC_SERVE_FOLDER='/mnt/hdd/videos'
|
STATIC_SERVE_FOLDER='/mnt/hdd/videos'
|
||||||
|
#needed only for docker-compose to map your filesystem, for docker run use "/srv/data/requests-log" volume path mapping
|
||||||
LOG_FILE_REQUESTS_FOLDER='/mnt/hdd/videos'
|
LOG_FILE_REQUESTS_FOLDER='/mnt/hdd/videos'
|
||||||
LOG_FILE_REQUESTS_NAME="static_log.txt"
|
#needed only for docker-compose to map your filesystem, for docker run use "/srv/data/db" volume path mapping
|
||||||
|
|
||||||
DB_FOLDER='/mnt/hdd/videos/db'
|
DB_FOLDER='/mnt/hdd/videos/db'
|
||||||
DB_FILENAME="db.db"
|
#needed only for docker-compose to map your filesystem, for docker run use "/srv/data/recent-searches" volume path mapping
|
||||||
|
|
||||||
RECENT_SEARCHES_FOLDER="/mnt/hdd/videos/recent-searches"
|
RECENT_SEARCHES_FOLDER="/mnt/hdd/videos/recent-searches"
|
||||||
|
|
||||||
|
#actual env variables starts here
|
||||||
|
PORT=3000
|
||||||
|
LOG_FILE_REQUESTS_NAME="static_log.txt"
|
||||||
|
DB_FILENAME="db.db"
|
||||||
RECENT_SEARCHES_LIMIT=20
|
RECENT_SEARCHES_LIMIT=20
|
||||||
|
PATTERNS_TO_IGNORE='["AOE1/"]' #file path patterns to ignore from video files scan, must be in json format
|
||||||
|
|
||||||
|
#can be VIDEO_FILES_FOLDER_1, VIDEO_FILES_FOLDER_2, etc., but must be edited in docker-compose.yml as well
|
||||||
|
#also map to same folders in your transmission file system
|
||||||
VIDEO_FILES_FOLDER='/mnt/hdd/videos' #also torrent download dir
|
VIDEO_FILES_FOLDER='/mnt/hdd/videos' #also torrent download dir
|
||||||
|
|
||||||
VIDEO_FILES_EXT='[".mkv", ".mp4", ".avi"]'
|
|
||||||
|
|
||||||
PATTERNS_TO_IGNORE='["AOE1/"]' #file path patterns to ignore from video files scan
|
|
||||||
|
|
||||||
LINKOMANIJA_USERNAME=user
|
LINKOMANIJA_USERNAME=user
|
||||||
LINKOMANIJA_PASSWORD=passw
|
LINKOMANIJA_PASSWORD=passw
|
||||||
LINKOMANIJA_PASSKEY=123pass456key789
|
LINKOMANIJA_PASSKEY=123pass456key789
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { FileEntity } from '../../../Shared/Entity/file.entity';
|
|||||||
import { StreamProviderEnum } from '../../../Shared/Enum/stream-provider.enum';
|
import { StreamProviderEnum } from '../../../Shared/Enum/stream-provider.enum';
|
||||||
import { VideoFilesFacade } from '../../../VideoFiles/video-files.facade';
|
import { VideoFilesFacade } from '../../../VideoFiles/video-files.facade';
|
||||||
import { TorrentSeedClient } from '../../SeedClient/torrent-seed.client';
|
import { TorrentSeedClient } from '../../SeedClient/torrent-seed.client';
|
||||||
|
import process, { exec } from 'child_process';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WebtorrentClient {
|
export class WebtorrentClient {
|
||||||
@@ -42,9 +43,13 @@ export class WebtorrentClient {
|
|||||||
this.resumeNotStopped();
|
this.resumeNotStopped();
|
||||||
}
|
}
|
||||||
|
|
||||||
getNewTorrentOptions(): TorrentOptions {
|
async getNewTorrentOptions(): Promise<TorrentOptions> {
|
||||||
const torrentOptions: Record<string, any> = {};
|
const torrentOptions: Record<string, any> = {};
|
||||||
torrentOptions.path = this.configService.getTorrentDownloadDir();
|
//todo - risk! - torrent might be already downloaded on another folder
|
||||||
|
torrentOptions.path = await this.findPathWithMostSpace(
|
||||||
|
this.configService.getTorrentDownloadDirs(),
|
||||||
|
);
|
||||||
|
// torrentOptions.path = this.configService.getTorrentDownloadDir();
|
||||||
torrentOptions.getAnnounceOpts = function () {
|
torrentOptions.getAnnounceOpts = function () {
|
||||||
const that = this as Torrent;
|
const that = this as Torrent;
|
||||||
|
|
||||||
@@ -136,11 +141,9 @@ export class WebtorrentClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!torrent) {
|
if (!torrent) {
|
||||||
|
const torrentOptions = await this.getNewTorrentOptions();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const torrent = this.client.add(
|
const torrent = this.client.add(torrentEntity.magnet, torrentOptions);
|
||||||
torrentEntity.magnet,
|
|
||||||
this.getNewTorrentOptions(),
|
|
||||||
);
|
|
||||||
|
|
||||||
torrent.on('ready', () => {
|
torrent.on('ready', () => {
|
||||||
resolve(torrent);
|
resolve(torrent);
|
||||||
@@ -312,4 +315,52 @@ export class WebtorrentClient {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async findPathWithMostSpace(paths: string[]): Promise<string> {
|
||||||
|
const promises: Promise<{ path: string; free: number }>[] = [];
|
||||||
|
for (const path of paths) {
|
||||||
|
promises.push(this.getFreeSpace(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await Promise.all(promises);
|
||||||
|
let maxSpace = 0;
|
||||||
|
let maxSpacePath = '';
|
||||||
|
for (const result of results) {
|
||||||
|
console.log(`testing path ${result.path} with ${result.free}KB`);
|
||||||
|
if (result.free > maxSpace) {
|
||||||
|
maxSpace = result.free;
|
||||||
|
maxSpacePath = result.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxSpacePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFreeSpace(path: string): Promise<{ path: string; free: number }> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
exec(`df ${path}`, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
const rows = data.split('\n');
|
||||||
|
const fsData = rows[1].replace(/\s+/g, ' ').trim().split(' ');
|
||||||
|
const [
|
||||||
|
filesystem,
|
||||||
|
sizeInKB,
|
||||||
|
usedKB,
|
||||||
|
availableKB,
|
||||||
|
usePercentage,
|
||||||
|
mountPoint,
|
||||||
|
] = fsData;
|
||||||
|
|
||||||
|
const availableKbNum = Number(availableKB);
|
||||||
|
|
||||||
|
if (isNaN(availableKbNum)) {
|
||||||
|
return reject(`availableKB not a number: ${availableKB}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve({ path, free: availableKbNum });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,19 +6,26 @@ export class VideoFilesConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getVideoFilesFolders(): string[] {
|
public getVideoFilesFolders(): string[] {
|
||||||
return [this.configService.getEnv('VIDEO_FILES_FOLDER')];
|
const folders: string[] = [];
|
||||||
|
Object.keys(process.env).forEach((key) => {
|
||||||
|
if (key.startsWith('VIDEO_FILES_FOLDER') && process.env[key]) {
|
||||||
|
folders.push(process.env[key]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return folders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getVideoFilesExt(): string[] {
|
public getVideoFilesExt(): string[] {
|
||||||
return JSON.parse(this.configService.getEnv('VIDEO_FILES_EXT'));
|
return ['.mkv', '.mp4', '.avi'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPathPatternsToIgnore(): string[] {
|
public getPathPatternsToIgnore(): string[] {
|
||||||
return JSON.parse(this.configService.getEnv('PATTERNS_TO_IGNORE'));
|
return JSON.parse(this.configService.getEnv('PATTERNS_TO_IGNORE'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTorrentDownloadDir(): string {
|
public getTorrentDownloadDirs(): string[] {
|
||||||
return this.configService.getEnv('VIDEO_FILES_FOLDER');
|
return this.getVideoFilesFolders();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTransmissionOptions() {
|
public getTransmissionOptions() {
|
||||||
|
|||||||
Reference in New Issue
Block a user