diff --git a/Scripts/Flow/Applications/Sonarr/Sonarr - Rename.js b/Scripts/Flow/Applications/Sonarr/Sonarr - Rename.js index 4ae5483..9688a27 100644 --- a/Scripts/Flow/Applications/Sonarr/Sonarr - Rename.js +++ b/Scripts/Flow/Applications/Sonarr/Sonarr - Rename.js @@ -1,42 +1,117 @@ import { Sonarr } from 'Shared/Sonarr'; /** - * This script will send a rename command to Sonarr + * @author Anthony Clerici * @author Shaun Agius * @uid 5ac44abd-cfe9-4a84-904b-9424908509de + * @description This script will send a rename command to Sonarr + * @revision 6 * @version 1.0.0 - * @revision 5 * @param {string} URI Sonarr root URI and port (e.g. http://sonarr:1234) * @param {string} ApiKey API Key * @output Item renamed * @output Item not found */ + function Script(URI, ApiKey) { let sonarr = new Sonarr(URI, ApiKey); - let folder = Variables.folder.FullName - var season - if (folder.includes("Season")) - season = folder.indexOf('Season')-1 - else - { - if (folder.includes("/")) - season = folder.lastIndexOf("/") - else if (folder.includes("\\")) - season = folder.lastIndexOf("\\") - } - let folder2 = folder.substring(0, season) - var slash - if (folder2.includes("/")) - slash = folder2.lastIndexOf("/") +1 - else if (folder2.includes("\\")) - slash = folder2.lastIndexOf("\\") +1 - let final = folder2.substring(slash) - let series = sonarr.getShowByPath(final); - if (!series) + const folderPath = Variables.folder.FullName; + const currentFileName = Variables.file.Name; + let newFileName = null; + let episodeFileId = null; + + // Find series name from sonarr + let series = findSeries(folderPath, sonarr); + + if (!series) { + Logger.WLog('Series not found for path: ' + folderPath); return 2; - Logger.ILog(`Renaming ${series.title}`); - let endpoint = `rename`; - let queryParmeters = `seriesId=${series.id}` - let response = sonarr.fetchJson(endpoint, queryParmeters); - Logger.ILog(`Response ${response}`); - return 1; + } + + try { + // Ensure series is refreshed before renaming + let refreshBody = { + seriesId: series.id + } + let refreshData = sonarr.sendCommand('RescanSeries', refreshBody) + Logger.ILog(`Series refreshed`); + + // Wait for the completion of the refresh scan + let refreshCompleted = sonarr.waitForCompletion(refreshData.id, sonarr); + if (!refreshCompleted) { + Logger.ILog('Refresh not completed'); + return -1; + } + + let renamedEpisodes = fetchRenamedFiles(series.id, sonarr); + if (!renamedEpisodes) { + Logger.ILog('No episodes need to be renamed'); + return 2; + } + + Logger.ILog(`Searching for an episode previously named ${currentFileName}`); + renamedEpisodes.every((episode) => { + if (episode.existingPath.endsWith(currentFileName)) { + episodeFileId = episode.episodeFileId; + newFileName = System.IO.Path.GetFileName(episode.newPath); + Logger.ILog(`Found it, renaming file ${episodeFileId} to ${newFileName}`); + return false; + } + return true; + }); + + if (newFileName === null) { + Logger.WLog('No matching episode found to rename.'); + return 2; + } + + let renameBody = { + seriesId: series.id, + files: [episodeFileId] + } + let renameResponse = sonarr.sendCommand('RenameFiles', renameBody, URI, ApiKey); + let renameCompleted = sonarr.waitForCompletion(renameResponse.id); + + if (!renameCompleted) { + Logger.ILog('Rename not completed'); + return -1; + } + Logger.ILog(`Episode ${episodeFileId} successfully renamed. Setting as working file.`) + + // Sonarr has successfully renamed the file, set new filename as working directory + let newFilePath = System.IO.Path.Combine(Variables.folder.FullName, newFileName); + Flow.SetWorkingFile(newFilePath); + return 1; + + } catch (error) { + Logger.WLog('Error: ' + error.message); + return -1; + } +} + +// Repeatedly try finding a show by shortening the path +function findSeries(filePath, sonarr) { + let currentPath = filePath; + let show = null; + + while (currentPath) { + show = sonarr.getShowByPath(currentPath); + if (show) { + Logger.ILog('Show found: ' + show.id); + return show; + } + + // If no show is found, go up 1 dir + currentPath = System.IO.Path.GetDirectoryName(currentPath); + if (currentPath === null || currentPath === "") { + Logger.WLog('Unable to find show file at path ' + filePath); + return null; + } + } +} + +function fetchRenamedFiles(seriesId, sonarr) { + let endpoint = 'rename'; + let queryParams = `seriesId=${seriesId}`; + let response = sonarr.fetchJson(endpoint, queryParams); + return response; } \ No newline at end of file diff --git a/Scripts/Shared/Sonarr.js b/Scripts/Shared/Sonarr.js index 1ef1f8f..ca4a14d 100644 --- a/Scripts/Shared/Sonarr.js +++ b/Scripts/Shared/Sonarr.js @@ -2,7 +2,7 @@ * @name Sonarr * @uid 0f5836c0-d20b-4740-9824-f81b5200ec3d * @description Class that interacts with Sonarr - * @revision 6 + * @revision 7 * @minimumVersion 1.0.0.0 */ export class Sonarr @@ -231,4 +231,59 @@ export class Sonarr } return language[1]; } -} \ No newline at end of file + + /** + * 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); + Logger.ILog(`${commandName} command sent successfully`); + 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; + 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); + } + Logger.WLog('Timeout: Scan did not complete within 30 seconds.'); + return false; + } +}