import diskusage from "diskusage";
|
|
import { readdir } from "fs/promises";
|
|
import Application from "../../Application"
|
|
import { MovieTicket, MovieTorrent } from "../../database/entities";
|
|
import Supervisor from "../Supervisor";
|
|
import TorrentClientIpc, { TorrentClientConnectionError } from "./TorrentClientIpc"
|
|
|
|
interface IPendingMovieTorrent {
|
|
link: string,
|
|
movie: MovieTicket
|
|
}
|
|
|
|
/**
|
|
* A mapping of available disks
|
|
*/
|
|
interface IDiskMap {
|
|
movies: string[],
|
|
// tvshows: string[]
|
|
}
|
|
|
|
export default class TorrentManager extends TorrentClientIpc
|
|
{
|
|
/**
|
|
* The queue of movies to add to the client
|
|
*/
|
|
protected pendingMovies: IPendingMovieTorrent[];
|
|
|
|
/**
|
|
* Indicate if the service is currently adding movies to the torrent client
|
|
*/
|
|
protected isAddingMovies: boolean;
|
|
|
|
/**
|
|
* Available movie disk names
|
|
*/
|
|
protected disks!: IDiskMap;
|
|
|
|
/**
|
|
* Create a new torrent manager instance
|
|
*/
|
|
public constructor(app: Application) {
|
|
super("Torrent Manager", app);
|
|
this.pendingMovies = [];
|
|
this.isAddingMovies = false;
|
|
}
|
|
|
|
/**
|
|
* Boot the Torrent Manager service
|
|
*/
|
|
public async boot() {
|
|
this.log("Booting the torrent manager");
|
|
await super.boot();
|
|
await this.loadDisks();
|
|
}
|
|
|
|
// Interface methods ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Add a movie to the queue
|
|
*/
|
|
public enqueueMovie(movie: MovieTicket, link: string) {
|
|
this.pendingMovies.push({movie, link});
|
|
this.addMovies();
|
|
}
|
|
|
|
// Volume Management ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Load available storage disks
|
|
*/
|
|
public async loadDisks() {
|
|
this.disks = {
|
|
movies: await readdir("/mnt/movies"),
|
|
// tvshows: await readdir("/mnt/tvshows")
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the disk with the most space available
|
|
*/
|
|
public async findMostEmptyDisk(diskType: "movies") {
|
|
let diskSizes = await Promise.all(this.disks[diskType].map(
|
|
async diskName => (await diskusage.check(`/mnt/${diskType}/${diskName}`)).available
|
|
));
|
|
let minIndex = 0;
|
|
for (let i = 1; i < diskSizes.length; i++) {
|
|
if (diskSizes[i] < diskSizes[minIndex]) {
|
|
minIndex = i;
|
|
}
|
|
}
|
|
return this.disks[diskType][minIndex];
|
|
}
|
|
|
|
// Movie Management ----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Add the movies in the queue to the torrent client
|
|
*/
|
|
protected async addMovies() {
|
|
if (this.isAddingMovies || !this.isConnected || this.pendingMovies.length == 0) {
|
|
return;
|
|
}
|
|
this.isAddingMovies = true;
|
|
let diskName = await this.findMostEmptyDisk("movies");
|
|
while (this.pendingMovies.length > 0) {
|
|
let {movie, link} = this.pendingMovies.splice(0, 1)[0];
|
|
try {
|
|
await this.addMovie(movie, link, diskName);
|
|
} catch(e) {
|
|
this.log("Failed to add torrent to client... Added to pending");
|
|
this.pendingMovies.push({movie, link});
|
|
break;
|
|
}
|
|
}
|
|
this.isAddingMovies = false;
|
|
}
|
|
|
|
/**
|
|
* Add a movie to the torrent client
|
|
*/
|
|
public async addMovie(movie: MovieTicket, link: string, diskName: string) {
|
|
try {
|
|
let infoHash = await this.add(link, `/mnt/movies/${diskName}/Downloads`);
|
|
let torrent = new MovieTorrent();
|
|
torrent.infoHash = infoHash;
|
|
torrent.diskName = diskName;
|
|
torrent.movieTicket = movie;
|
|
await torrent.save();
|
|
} catch(e) {
|
|
if (e instanceof TorrentClientConnectionError) {
|
|
throw e;
|
|
}
|
|
console.log("Failed download the torrent");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Event Handling ------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Invoked when the connection to the torrent client is established/re-established
|
|
*/
|
|
protected onConnect() {
|
|
super.onConnect();
|
|
this.addMovies();
|
|
}
|
|
|
|
/**
|
|
* Invoked when a torrent
|
|
*/
|
|
protected async onTorrentFinished(infoHash: string) {
|
|
let torrent = await MovieTorrent.findOne({
|
|
where: { infoHash }, relations: ["movieTicket"]
|
|
});
|
|
if (torrent !== undefined) {
|
|
let details = (await this.details(infoHash))[0];
|
|
this.app.service<Supervisor>("Supervisor").onMovieTorrentFinished(torrent, details);
|
|
}
|
|
// this.app.service<Supervisor>();
|
|
}
|
|
}
|