You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

162 lines
4.0 KiB

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>();
}
}