diff --git a/Scripts/Flow/Applications/Radarr/Radarr - Rename.js b/Scripts/Flow/Applications/Radarr/Radarr - Rename.js index 65981a5..82d11d2 100644 --- a/Scripts/Flow/Applications/Radarr/Radarr - Rename.js +++ b/Scripts/Flow/Applications/Radarr/Radarr - Rename.js @@ -1,10 +1,10 @@ import { Radarr } from 'Shared/Radarr'; /** - * This script will send a rename command to Radarr + * @author Anthony Clerici * @author Shaun Agius * @uid bd6f02c8-e650-4916-bcae-46f382d20388 - * @version 1.0.0 - * @revision 4 + * @descripttion This script will send a rename command to Radarr + * @revision 5 * @param {string} URI Radarr root URI and port (e.g. http://radarr:1234) * @param {string} ApiKey API Key * @output Item renamed @@ -12,13 +12,125 @@ import { Radarr } from 'Shared/Radarr'; */ function Script(URI, ApiKey) { let radarr = new Radarr(URI, ApiKey); - let movie = radarr.getMovieByFile(Variables.file.Name); - if (!movie) + let folderPath = Variables.folder.FullName; + let filePath = Variables.file.FullName; + let currentFileName = Variables.file.Name; + let newFileName = null; + + // Find movie name from radarr + let movie = findMovie(folderPath, radarr); + + if (!movie) { + Logger.WLog('Movie not found for path: ' + folderPath); + Logger.ILog('Returning 2'); return 2; - Logger.ILog(`Renaming ${movie.title}`); - let endpoint = `rename`; - let queryParmeters = `movieId=${movie.id}` - let response = radarr.fetchJson(endpoint, queryParmeters); - Logger.ILog(`Response ${response}`); - return 1; -} \ No newline at end of file + } + + // Get Movie File info + let movieFiles = radarr.findMovieFiles(movie.id, radarr); + if (!movieFiles) { + Logger.ILog(`No files found for movie ${movie.id}`); + return -1 + } + + let fileList = []; + movieFiles.forEach(file => { + fileList.push(file.id); + }); + + try { + // Ensure movie is refreshed before renaming + let refreshBody = { + movieId: movie.id + } + let refreshData = radarr.sendCommand('RescanMovie', refreshBody) + Logger.ILog(`Movie refreshed: ${JSON.stringify(refreshData)}`); + + // Wait for the completion of the refresh scan + let refreshCompleted = radarr.waitForCompletion(refreshData.id); + if (!refreshCompleted) { + Logger.ILog('Refresh not completed'); + return -1; + } + + // Get what radarr wants to rename the movie + let renamedMovies = radarr.fetchRenamedMovies(movie.id, radarr); + let renamedMovie = null; + if (!renamedMovies) { + Logger.ILog('No movies need to be renamed'); + Logger.ILog('Returning 2'); + return 2; + } + + renamedMovies.every(element => { + if (element.existingPath.endsWith(currentFileName)) { + renamedMovie = element; + return false + } + return true; + }); + + // Ensure movie is found + if (!renamedMovie) { + Logger.ILog(`Current file not found in list to be renamed for movie ${movie.id}`) + Logger.ILog('Returning 2'); + return 2; + } + + newFileName = System.IO.Path.GetFileName(renamedMovie.newPath); + Logger.ILog(`Found it, renaming file to ${newFileName}`); + + if (newFileName === null) { + Logger.WLog('No matching movie found to rename.'); + Logger.ILog('Returning 2'); + return 2; + } + + // Now rename the file to what Radarr specifies + let renameBody = { + movieId: movie.id, + files: fileList + } + Logger.ILog(renameBody); + let renameResponse = radarr.sendCommand('RenameFiles', renameBody); + let renameCompleted = radarr.waitForCompletion(renameResponse.id); + + if (!renameCompleted) { + Logger.ILog('Rename not completed'); + return -1; + } + Logger.ILog(`Movie ${movie.id} successfully renamed. Setting as working file.`) + + // Radarr has successfully renamed the file, set new filename as working directory + let newFilePath = System.IO.Path.Combine(Variables.folder.FullName, newFileName); + Flow.SetWorkingFile(newFilePath); + Logger.ILog('Returning 1'); + return 1; + + } catch (error) { + Logger.WLog('Error: ' + error.message); + Logger.ILog('Returning -1'); + return -1; + } +} + +// Function to repeatedly try finding a movie by shortening the path +function findMovie(filePath, radarr) { + let currentPath = filePath; + let movie = null; + + while (currentPath) { + movie = radarr.getMovieByPath(currentPath); + if (movie) { + Logger.ILog('Movie found: ' + movie.id); + return movie; + } + + // If no movie is found go 1 dir up + currentPath = System.IO.Path.GetDirectoryName(currentPath); + } + + Logger.WLog('Unable to find movie file at path ' + filePath); + return null; +} + diff --git a/Scripts/Shared/Radarr.js b/Scripts/Shared/Radarr.js index 96d7ffe..703bd97 100644 --- a/Scripts/Shared/Radarr.js +++ b/Scripts/Shared/Radarr.js @@ -165,4 +165,84 @@ export class Radarr } return movie.originalLanguage?.name; } -} \ No newline at end of file + + /** + * Returns movie files info for an already identified movie + * @param {int} movieId ID of previously identified movie + * @returns list of radarr movieFile objects + */ + findMovieFiles(movieId) { + let endpoint = 'moviefile'; + let queryParams = `movieId=${movieId}`; + let response = this.fetchJson(endpoint, queryParams); + + Logger.ILog(`Movie found: ${movieId}`); + return response; + } + + /** + * Returns files under a movie that need to be renamed + * @param {int} movieId Previously determined ID of the movie + * @returns list of radarr rename movie objects + */ + fetchRenamedMovies(movieId) + { + let endpoint = 'rename'; + let queryParams = `movieId=${movieId}`; + let response = this.fetchJson(endpoint, queryParams); + return response; + } + + /** + * Specifies a command for Sonarr to run. see sonarr rename script for usage + * @param {string} commandName the name of the command to be run + * @param {object} commandBody the body of the command to be sent + * @returns {object} JSON of the response or null if unsuccessful + */ + sendCommand(commandName, commandBody) { + let endpoint = `${this.URL}/api/v3/command`; + commandBody['name'] = commandName; + + let jsonData = JSON.stringify(commandBody); + http.DefaultRequestHeaders.Add("X-API-Key", this.ApiKey); + let response = http.PostAsync(endpoint, JsonContent(jsonData)).Result; + + http.DefaultRequestHeaders.Remove("X-API-Key"); + + if (response.IsSuccessStatusCode) { + let responseData = JSON.parse(response.Content.ReadAsStringAsync().Result); + return responseData; + } else { + let error = response.Content.ReadAsStringAsync().Result; + Logger.WLog("API error: " + error); + return null; + } + } + + /** + * Sleeps, waiting for a command to complete + * @param {int} commandId ID of command being run + * @returns bool whether the coommand ran successfully + */ + waitForCompletion(commandId) + { + const startTime = new Date().getTime(); + const timeout = 30000; // 30 seconds in milliseconds + const endpoint = `command/${commandId}`; + + while (new Date().getTime() - startTime <= timeout) { + let response = this.fetchJson(endpoint, ''); + if (response.status === 'completed') { + Logger.ILog('Scan completed!'); + return true; + } else if (response.status === 'failed') { + Logger.WLog(`Command ${commandId} failed`) + return false; + } + Logger.ILog(`Checking status: ${response.status}`); + Sleep(100); // Delay before next check + } + Logger.WLog('Timeout: Scan did not complete within 30 seconds.'); + return false; + } +} diff --git a/Scripts/Shared/Sonarr.js b/Scripts/Shared/Sonarr.js index 47af593..1ef1f8f 100644 --- a/Scripts/Shared/Sonarr.js +++ b/Scripts/Shared/Sonarr.js @@ -231,4 +231,4 @@ export class Sonarr } return language[1]; } -} +} \ No newline at end of file