Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Radarr and Sonarr plugins (refresh/rename files) and "Remove streams by specified property" tweak #493

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,61 @@
`,
Version: '1.00',
Tags: 'action',
Inputs: [
{
name: 'propertyToCheck',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
tooltip:
`Enter one stream property to check.
Inputs: [{
name: 'codecTypeFilter',
type: 'string',
defaultValue: 'subtitle',
inputUI: {
type: 'text',
},
tooltip: `Enter values of the stream codec type to process. Nothing/empty input means all types of streams will
be inspected for processing. For example, if removing by codec_name on video streams, enter video:

\\nExample:\\n
video,subtitle,audio
`,
},
{
name: 'propertyToCheck',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
tooltip: `Enter one stream property to check for values.

\\nExample:\\n
codec_name
`,
},
{
name: 'valuesToRemove',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
{
name: 'valuesToRemove',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
tooltip:
`Enter values of the property above to remove. For example, if removing by codec_name, could enter ac3,aac:
tooltip: `Enter values of the property above to remove. For example, if removing by codec_name, could enter ac3,aac:

\\nExample:\\n
ac3,aac
`,
},
{
name: 'removeIfPropertyMissing',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
tooltip: `Enter one or more properties to check for its existance. If the property is missing or null,
the stream will be removed. Useful for fixing corrupt streams. For example, if codec_name
is missing, the stream will be removed:

\\nExample:\\n
codec_name
`,
},
],
});

Expand Down Expand Up @@ -70,16 +96,54 @@

const valuesToRemove = inputs.valuesToRemove.trim().split(',');

const codecTypeFilter = inputs.codecTypeFilter.trim().split(',');

const removeIfPropertyMissing = inputs.removeIfPropertyMissing.trim().split(',');

// Debug lines
// response.infoLog += `codecTypeFilter is ${codecTypeFilter} \n`;
// response.infoLog += `removeIfPropertyMissing is ${removeIfPropertyMissing} \n`;

response.preset += ', -map 0 -c copy -max_muxing_queue_size 9999';

try {
let streamToRemove = false;
for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
try {
if (valuesToRemove.includes(String(file.ffProbeData.streams[i][propertyToCheck]))) {
// Skip if the codec_type is filtered out
if (
codecTypeFilter.length !== 0
&& !codecTypeFilter.includes(String(file.ffProbeData.streams[i].codec_type))) {
continue;

Check failure on line 117 in Community/Tdarr_Plugin_00td_action_remove_stream_by_specified_property.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Unexpected use of continue statement
}

// Check if chosen non-empty properties are empty
// If they are empty, set emptyValue to true
let emptyValue = false;
for (let j = 0; j < removeIfPropertyMissing.length; j += 1) {
response.infoLog += `DEBUG: stream ${i} property for ${removeIfPropertyMissing[j]}
is ${file.ffProbeData.streams[i][removeIfPropertyMissing[j]]} \n`;

if (
file.ffProbeData.streams[i][removeIfPropertyMissing[j]] === 'undefined'
|| file.ffProbeData.streams[i][removeIfPropertyMissing[j]] === null) {
emptyValue = true;
response.infoLog += ` Removing stream ${i} which is has ${removeIfPropertyMissing[j]} missing`;
break;
}
}

// If the value to remove is present OR an empty value is found, remove the stream
if ((valuesToRemove.includes(String(file.ffProbeData.streams[i][propertyToCheck]))) || emptyValue) {
// Add to preset
response.preset += ` -map -0:${i} `;
response.infoLog += ` Removing stream ${i} which is has ${propertyToCheck}`
+ ` of ${file.ffProbeData.streams[i][propertyToCheck]} \n`;

// Log the old message if the reason is not empty values
if (!emptyValue) {
response.infoLog += ` Removing stream ${i} which is has ${propertyToCheck} or`
+ ` of ${file.ffProbeData.streams[i][propertyToCheck]} \n`;
}

streamToRemove = true;
}
} catch (err) {
Expand Down
243 changes: 243 additions & 0 deletions Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
module.exports.dependencies = [
'axios',
'http',
'https',
];

// tdarrSkipTest
const details = () => ({
id: 'Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr',
Stage: 'Post-processing',
Name: 'Refresh files in Sonarr',
Type: 'Video',
Operation: 'Transcode',
Description: `Refreshes folder containing the current show in Sonarr so files are mapped properly.
This is done using the Sonarr API. To do this action it needs the Show ID.
This code attempts to retrieve the Show ID by using the folder name of the series.`,
Version: '1.0',
Tags: '3rd party,post-processing,configurable',

Inputs: [{
name: 'Url_Protocol',
type: 'string',
defaultValue: 'http',
inputUI: {
type: 'dropdown',
options: [
'http',
'https',
],
},
tooltip: `
Specified the type of request to make, http:// or https://
\\nExample:\\n
http
\\nExample:\\n
https`,
},
{
name: 'Url_Sonarr',
type: 'string',
defaultValue: 'localhost',
inputUI: {
type: 'text',
},
tooltip: `
Enter the IP address/URL Tdarr uses to reach Sonarr.
\\nExample:\\n
192.168.0.10
\\nExample:\\n
subdomain.domain.tld`,
},
{
name: 'Sonarr_Port',
type: 'number',
defaultValue: 8989,
inputUI: {
type: 'text',
},
tooltip: `
The port required to access Sonarr
\\nExample:\\n
8989`,
},
{
name: 'Sonarr_APIKey',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
tooltip: `
Enter the Sonarr API key. \\n
You can find it within Sonarr at /settings/general. \\n\\n
\\nExample:\\n
3ff1ae1c39a2a2a397315e15266dea48`,
},
{
name: 'After_Sleep',
type: 'number',
defaultValue: 0,
inputUI: {
type: 'text',
},
tooltip: `
How many ms should Tdarr sleep to wait for Sonarr to finish afterward? \\n
\\nExample:\\n
1000`,
},
],
});

function sleep(ms) {

Check failure on line 92 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Expected a function expression

Check warning on line 92 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Prefer using arrow functions over plain functions
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}

// eslint-disable-next-line no-unused-vars
const plugin = async (file, librarySettings, inputs, otherArguments) => {

Check warning on line 99 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

'otherArguments' is defined but never used

Check failure on line 99 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Block must not be padded by blank lines

const response = {
file,
removeFromDB: false,
updateDB: false,
processFile: false,
infoLog: 'Refresh Sonarr files starting.',
};

//console.log('Refresh Sonarr files starting.');

Check failure on line 109 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Expected exception block, space or tab after '//' in comment

const lib = require('../methods/lib')();
// eslint-disable-next-line no-unused-vars,no-param-reassign
inputs = lib.loadDefaultValues(inputs, details);
const http = require('http');
const https = require('https');
const axios = require('axios');

Check failure on line 116 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Unable to resolve path to module 'axios'

//console.log('Loaded required packages.');

Check failure on line 118 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Expected exception block, space or tab after '//' in comment

// Create variables
const SSL = inputs.Url_Protocol;
const IP = inputs.Url_Sonarr;
const port = inputs.Sonarr_Port;
const APIKey = inputs.Sonarr_APIKey;
const sleepInterval = inputs.After_Sleep;
let term = '';
let termUri = '';
const APIPathLookup = '/api/v3/series/lookup';
const APIPathCommand = '/api/v3/command';
const APICommand = 'RefreshSeries';

// Check variables are given
if (!SSL || !IP || !APIKey || !port) {
throw new Error('All fields are required.');
}

// Select connection type
let connection_type = null;
try {
if (SSL === 'http') {
connection_type = http;
} else {
connection_type = https;
}
} catch (e) {
//console.log(`Failed to compare SSL string. Error: ${e}`);

Check failure on line 146 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Expected exception block, space or tab after '//' in comment
connection_type = http;
}

// Try to split file path to retrieve series folder name
try {
term = file.file.split('/');
term = term[term.length - 3];
termUri = encodeURI(term);
} catch (e) {
//console.log(`Failed to split file name. Error: '${e}'.\\n`);

Check failure on line 156 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Expected exception block, space or tab after '//' in comment
response.infoLog += `\\nFailed to split file name. Error: '${e}'.`;
return response;
}

//console.log(`Searching for series '${term}'.`);

Check failure on line 161 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Expected exception block, space or tab after '//' in comment
response.infoLog += `\\nSearching for series '${term}'.`;

// Create variables for API call
const url1 = `${SSL}://${IP}:${port}${APIPathLookup}?term=${termUri}&apikey=${APIKey}`;
let url1_body = '';
let url2 = '';
let SeriesID = 0;

// API call to search for Series ID using the folder name
try {
await new Promise((resolve) => {
connection_type.get(url1, (res) => {
//console.log(`Got status code '${res.statusCode}'.`);

Check failure on line 174 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Expected exception block, space or tab after '//' in comment
response.infoLog += `\\nGot status code '${res.statusCode}'.`;

res.on('data', function (chunk) {

Check warning on line 177 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Prefer using arrow functions over plain functions

Check warning on line 177 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Unexpected unnamed function
url1_body += chunk;
});

res.on('end', function () {

Check warning on line 181 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Prefer using arrow functions over plain functions

Check warning on line 181 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Unexpected unnamed function
resolve();
});
}).on('error', (e) => {
//console.log(`Failed to search for series. Error: '${e}'.`);
response.infoLog += `\\nFailed to search for series. Error: '${e}'.`;
resolve();
});
});
} catch (e) {
//console.log(`Failed API call. Error: '${e}'.`);
response.infoLog += `\\nFailed API call. Error: '${e}'.`;
return response;
}

// Parse API response and save the Series ID
try {
const APIresponse = JSON.parse(url1_body);
SeriesID = APIresponse[0].id;
url2 = `${SSL}://${IP}:${port}${APIPathCommand}?apikey=${APIKey}`;
} catch (e) {
//console.log(`Failed make JSON payload. Error: '${e}'.`);
response.infoLog += `\\nFailed make JSON payload. Error: '${e}'.`;
return response;
}

//console.log(`Refreshing series '${SeriesID}'.`);
response.infoLog += `\\nRefreshing series '${SeriesID}'.`;

// API request to send a command to refresh the files for the found Series ID
try {
await new Promise((resolve) => {
axios.post(url2, {
name: APICommand,
seriesId: SeriesID,
})
.then(function (res) {

Check warning on line 217 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Prefer using arrow functions over plain functions

Check warning on line 217 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Unexpected unnamed function
//console.log(`Got status code '${res.status}'.`);
response.infoLog += `\\n☑ Got status code '${res.status}'.`;
resolve();
})
.catch(function (error) {

Check warning on line 222 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Prefer using arrow functions over plain functions

Check warning on line 222 in Community/Tdarr_Plugin_f4k2_aune_refresh_files_in_sonarr.js

View workflow job for this annotation

GitHub Actions / test (18.x, ubuntu-20.04)

Unexpected unnamed function
//console.log(`Got error: ${error}`);
response.infoLog += `\\nGot error: ${error}`;
resolve();
});
});
} catch (e) {
// console.log(`Failed API call. Error: '${e}'.`);
response.infoLog += `\\nFailed API call. Error: '${e}'.`;
return response;
}

// Sleep for set amount of time
// console.log(`Sleeping '${sleepInterval}' ms.`);
response.infoLog += `\\nSleeping '${sleepInterval}' ms.`;
await sleep(sleepInterval);

return response;
};

module.exports.details = details;
module.exports.plugin = plugin;
Loading
Loading