pages/post/how-to-automate-jellyfin-issue-handling/index.html
2024-02-22 19:01:49 +00:00

1255 lines
206 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en" dir="auto">
<head><script src="/livereload.js?mindelay=10&amp;v=2&amp;port=1313&amp;path=livereload" data-no-instant defer></script><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="index, follow">
<title>How To Automate Jellyfin Issue Handling | Mafyuh&#39;s Blog</title>
<meta name="keywords" content="">
<meta name="description" content="I wanted a way to automate when users tell me a video on my Jellyfin server has an issue. After alot of trial and error, ChatGPT, Bard and I came up with this automation.
Requirements My only requirements when making this was that it was free and self-hostable. Not even any NPM extensions are required in AP. Actual Software requirements are:
Sonarr Radarr Overseerr/Jellyseerr Optional
SMTP server or ability to send SMTP messages (can also use discord) ActivePieces or any other automation platform that supports TS.">
<meta name="author" content="Mafyuh">
<link rel="canonical" href="http://localhost:1313/post/how-to-automate-jellyfin-issue-handling/">
<link crossorigin="anonymous" href="/assets/css/stylesheet.b609c58d5c11bb90b1a54e04005d74ad1ddf22165eb79f5533967e57df9c3b50.css" integrity="sha256-tgnFjVwRu5CxpU4EAF10rR3fIhZet59VM5Z&#43;V9&#43;cO1A=" rel="preload stylesheet" as="style">
<link rel="icon" href="http://localhost:1313/assets/favicon/favicon.svg">
<link rel="icon" type="image/png" sizes="16x16" href="http://localhost:1313/assets/img/newlogo.png">
<link rel="icon" type="image/png" sizes="32x32" href="http://localhost:1313/assets/img/newlogo.png">
<link rel="apple-touch-icon" href="http://localhost:1313/apple-touch-icon.png">
<link rel="mask-icon" href="http://localhost:1313/safari-pinned-tab.svg">
<meta name="theme-color" content="#2e2e33">
<meta name="msapplication-TileColor" content="#2e2e33">
<link rel="alternate" hreflang="en" href="http://localhost:1313/post/how-to-automate-jellyfin-issue-handling/">
<noscript>
<style>
#theme-toggle,
.top-link {
display: none;
}
</style>
<style>
@media (prefers-color-scheme: dark) {
:root {
--theme: rgb(29, 30, 32);
--entry: rgb(46, 46, 51);
--primary: rgb(218, 218, 219);
--secondary: rgb(155, 156, 157);
--tertiary: rgb(65, 66, 68);
--content: rgb(196, 196, 197);
--code-block-bg: rgb(46, 46, 51);
--code-bg: rgb(55, 56, 62);
--border: rgb(51, 51, 51);
}
.list {
background: var(--theme);
}
.list:not(.dark)::-webkit-scrollbar-track {
background: 0 0;
}
.list:not(.dark)::-webkit-scrollbar-thumb {
border-color: var(--theme);
}
}
</style>
</noscript><meta property="og:title" content="How To Automate Jellyfin Issue Handling" />
<meta property="og:description" content="I wanted a way to automate when users tell me a video on my Jellyfin server has an issue. After alot of trial and error, ChatGPT, Bard and I came up with this automation.
Requirements My only requirements when making this was that it was free and self-hostable. Not even any NPM extensions are required in AP. Actual Software requirements are:
Sonarr Radarr Overseerr/Jellyseerr Optional
SMTP server or ability to send SMTP messages (can also use discord) ActivePieces or any other automation platform that supports TS." />
<meta property="og:type" content="article" />
<meta property="og:url" content="http://localhost:1313/post/how-to-automate-jellyfin-issue-handling/" /><meta property="article:section" content="post" />
<meta property="article:published_time" content="2023-11-11T16:20:00+00:00" />
<meta property="article:modified_time" content="2023-11-11T16:20:00+00:00" /><meta property="og:site_name" content="Mafyuh&#39;s Blog" />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="How To Automate Jellyfin Issue Handling"/>
<meta name="twitter:description" content="I wanted a way to automate when users tell me a video on my Jellyfin server has an issue. After alot of trial and error, ChatGPT, Bard and I came up with this automation.
Requirements My only requirements when making this was that it was free and self-hostable. Not even any NPM extensions are required in AP. Actual Software requirements are:
Sonarr Radarr Overseerr/Jellyseerr Optional
SMTP server or ability to send SMTP messages (can also use discord) ActivePieces or any other automation platform that supports TS."/>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1 ,
"name": "Posts",
"item": "http://localhost:1313/post/"
},
{
"@type": "ListItem",
"position": 2 ,
"name": "How To Automate Jellyfin Issue Handling",
"item": "http://localhost:1313/post/how-to-automate-jellyfin-issue-handling/"
}
]
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "How To Automate Jellyfin Issue Handling",
"name": "How To Automate Jellyfin Issue Handling",
"description": "I wanted a way to automate when users tell me a video on my Jellyfin server has an issue. After alot of trial and error, ChatGPT, Bard and I came up with this automation.\nRequirements My only requirements when making this was that it was free and self-hostable. Not even any NPM extensions are required in AP. Actual Software requirements are:\nSonarr Radarr Overseerr/Jellyseerr Optional\nSMTP server or ability to send SMTP messages (can also use discord) ActivePieces or any other automation platform that supports TS.",
"keywords": [
],
"articleBody": "I wanted a way to automate when users tell me a video on my Jellyfin server has an issue. After alot of trial and error, ChatGPT, Bard and I came up with this automation.\nRequirements My only requirements when making this was that it was free and self-hostable. Not even any NPM extensions are required in AP. Actual Software requirements are:\nSonarr Radarr Overseerr/Jellyseerr Optional\nSMTP server or ability to send SMTP messages (can also use discord) ActivePieces or any other automation platform that supports TS. (Zapier, n8n, etc) Heres a great AP setup and how-to video:\nNote: I didnt do any of the ngrok stuff. I just have Nginx Proxy manager setup with a wildcard certificate. Then just give a domain name and point and its ip:8080. No special Nginx config needed. Make sure you set AP_FRONTEND_URL in .env\nThis blog post is rather long, if you prefer to see the code on git you can find all this code here.\nHow it Works Whenever a user Reports an Issue in Jellyseerr, a webhook is sent to activepieces (AP) with the Issue data, this triggers the automation to mark as failed, delete file, re-search, refresh Jellyfin Libraries and Resolve the original issue with comment. There is an optional feature to approve or deny the automation.\nWorks across Radarr and Sonarr, as the issue reported can be either Movie or TV show.\nOnly caveat is if the issue is an entire Season , we just mark the issue as resolved and leave a comment saying to submit an issue for each episode individually\nWorks on my Jellyfin, Jellyseer, Radarr and Sonarr setup. I dont use Plex but all you would have to change is the Jellyfin Refresh Library Request to match Plexs equivalent.\nHere is a pic of the full automation.\nEverything of value is logged to the console so check there for errors. Lets start breaking it down.\n#1 Jellyseer Issue Reported First thing is create a flow in AP, select a trigger, and search for webhook. This will give you the webhook URL for Jellyseerr. Next, in Jellyseerr, under Settings - Users - Default Permissions make sure Report Issues is checked and save changes. Then under Settings - Notifications - Webhook make a webhook notification, with the URL from AP, and just enabling Issue Reported and Issue Reopened. This should look as follows (dont worry about my payload showing mediaId, this has since been deleted)\nHere is my full JSON payload just in case:\n{ \"notification_type\": \"{{notification_type}}\", \"event\": \"{{event}}\", \"subject\": \"{{subject}}\", \"message\": \"{{message}}\", \"image\": \"{{image}}\", \"{{media}}\": { \"media_type\": \"{{media_type}}\", \"tmdbId\": \"{{media_tmdbid}}\", \"tvdbId\": \"{{media_tvdbid}}\", \"status\": \"{{media_status}}\", \"status4k\": \"{{media_status4k}}\" }, \"{{request}}\": { \"request_id\": \"{{request_id}}\", \"requestedBy_email\": \"{{requestedBy_email}}\", \"requestedBy_username\": \"{{requestedBy_username}}\", \"requestedBy_avatar\": \"{{requestedBy_avatar}}\", \"requestedBy_settings_discordId\": \"{{requestedBy_settings_discordId}}\", \"requestedBy_settings_telegramChatId\": \"{{requestedBy_settings_telegramChatId}}\" }, \"{{issue}}\": { \"issue_id\": \"{{issue_id}}\", \"issue_type\": \"{{issue_type}}\", \"issue_status\": \"{{issue_status}}\", \"reportedBy_email\": \"{{reportedBy_email}}\", \"reportedBy_username\": \"{{reportedBy_username}}\", \"reportedBy_avatar\": \"{{reportedBy_avatar}}\", \"reportedBy_settings_discordId\": \"{{reportedBy_settings_discordId}}\", \"reportedBy_settings_telegramChatId\": \"{{reportedBy_settings_telegramChatId}}\" }, \"{{comment}}\": { \"comment_message\": \"{{comment_message}}\", \"commentedBy_email\": \"{{commentedBy_email}}\", \"commentedBy_username\": \"{{commentedBy_username}}\", \"commentedBy_avatar\": \"{{commentedBy_avatar}}\", \"commentedBy_settings_discordId\": \"{{commentedBy_settings_discordId}}\", \"commentedBy_settings_telegramChatId\": \"{{commentedBy_settings_telegramChatId}}\" }, \"{{extra}}\": [] } You should be able to Report an issue on a random movie in Jellyseerr and then go to the webhook trigger and choose Generate sample data, and you should be able to see the data from the request. I recommend doing this and creating an issue for an example movie, TV series( All Seasons) and a TV Series (1 Season)\n(Optional) #2 Create Approval Links In AP add the next step and search Approval, then create approval links.\n(Optional) #3 Send Email This is so I can either approve or deny the file from being deleted. Maybe its a client issue and I know for a fact my file is good and I dont want deleted. Thus the links are sent to me along with the some data from the request, so I know what I am approving/denying.\nYou can use the core SMTP feature but its limited to text. I wanted some more customizability so I chose Resend (supports html) and set up an acct there with one of my extra domains.\nYou dont have to use email, you can use Discord, SMS, any generic http request or whatever you want. I just use email since I pay for my domains and pay Proton Mail for emails so might as well use em.\nNot gonna get too into this, I dont care too much about it atm, customize your email to your liking, but Ill post my somewhat working HTML body here. I literally just copied what Bard gave me, added in data from response and tested and said looks good enough, glitches on my mobile too.\n\u003c!DOCTYPE html\u003e \u003chtml lang=\"en\"\u003e \u003chead\u003e \u003cmeta charset=\"UTF-8\"\u003e \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e \u003ctitle\u003eJellyseerr Issue Reported\u003c/title\u003e \u003cstyle\u003e body { font-family: sans-serif; margin: 0; padding: 0; background-color: #222; color: #fff; } .container { width: 80%; margin: 0 auto; padding: 20px; background-color: #333; border-radius: 10px; box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1); } .header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 20px; border-bottom: 1px solid #555; } .header h1 { font-size: 24px; font-weight: bold; margin: 0; color: #fff; } .header img { width: 50px; height: 50px; border-radius: 50%; object-fit: cover; } .content { margin: 0 auto; text-align: center; } .issue-subject { font-size: 18px; font-weight: bold; margin-bottom: 10px; color: #fff; } .issue-message { font-size: 16px; line-height: 1.5; margin-bottom: 20px; color: #ccc; } .issue-image { width: 100%; height: auto; margin-bottom: 20px; } .buttons { display: flex; justify-content: space-between; } .button { background-color: #007bff; color: #fff; padding: 10px 20px; border-radius: 5px; cursor: pointer; text-decoration: none; } .button:hover { background-color: #0056b3; } .disapprove-button { background-color: #dc3545; color: #fff; padding: 10px 20px; border-radius: 5px; cursor: pointer; text-decoration: none; } .disapprove-button:hover { background-color: #bd2830; } \u003c/style\u003e \u003c/head\u003e \u003cbody\u003e \u003cdiv class=\"container\"\u003e \u003cdiv class=\"header\"\u003e \u003cimg src=\"https://your-logo-url\" alt=\"Jellyseerr Logo\"\u003e \u003ch1\u003eJellyseerr Issue Reported\u003c/h1\u003e \u003c/div\u003e \u003cdiv class=\"content\"\u003e \u003cdiv class=\"issue-subject\"\u003e Jellyseerr Issue Reported \u003c/div\u003e \u003cdiv class=\"issue-message\"\u003e This issue was submitted by 1. Jellyseerr Issue Reported body issue reportedBy_username. \u003cbr\u003e The reason for the issue:1. Jellyseerr Issue Reported body message \u003cbr\u003e Please review the issue and take appropriate action. \u003cbr\u003e \u003cimg src=\" 1. Jellyseerr Issue Reported body image \"\u003e \u003c/div\u003e \u003cdiv class=\"buttons\"\u003e \u003ca href=\"2. Create Approval Links approvalLink \"\u003e\u003cbutton class=\"button\"\u003eApprove\u003c/button\u003e\u003c/a\u003e \u003ca href=\"2. Create Approval Links disapprovalLink \"\u003e\u003cbutton class=\"disapprove-button\"\u003eDeny\u003c/button\u003e\u003c/a\u003e \u003c/div\u003e \u003c/div\u003e \u003c/div\u003e \u003c/body\u003e \u003c/html\u003e And heres what an email looks like:\n(Optional) #4 Wait for Approval Pauses flow until I approve or deny.\n#5 Radarr/Sonarr Branch As stated previously, I wanted this to work regardless if Movie or TV show. So using the core Branch feature we just say that if the media_type value from the issue contains the text movie, its true.\n#6 Radarr Mark As Failed All I do here is the Code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code (CASE SENSITIVE)\nHere is the code. Just replace api key and base url:\nexport const code = async (inputs) =\u003e { const issueSubject = inputs.issue.subject; const movieNameRegex = /(.*)\\s\\((\\d{4})\\)/; const match = movieNameRegex.exec(issueSubject); if (match) { const movieName = match[1]; const year = match[2]; const tmdbId = inputs.issue.media.tmdbId; console.log(`Movie name: ${movieName}`); console.log(`Year: ${year}`); console.log(`TMDB ID: ${tmdbId}`); // Define your Radarr API key and base URL const radarrApiKey = 'your-api-key'; // Replace with your Radarr API key const radarrBaseUrl = 'https://radarr.example.com/api/v3/'; // Define a function to make API requests to Radarr const makeRadarrRequest = async (endpoint, method = 'GET') =\u003e { const apiUrl = radarrBaseUrl + endpoint; console.log(`Calling Radarr API: ${apiUrl}`); const response = await fetch(apiUrl, { method, headers: { 'X-Api-Key': radarrApiKey, }, }); if (response.ok) { return await response.json(); } else { console.error(`Radarr API request failed: ${response.statusText}`); return null; } }; // Use Radarr's API to look up the movie by TMDB ID const radarrApiResponseData = await makeRadarrRequest(`movie?tmdbId=${tmdbId}`); if (radarrApiResponseData \u0026\u0026 radarrApiResponseData.length \u003e 0) { const movieId = radarrApiResponseData[0].id; // Get the Radarr ID of the first movie console.log('Radarr Movie ID:', movieId); // Use the Radarr movie ID to get the history of the movie const historyApiResponseData = await makeRadarrRequest(`history/movie?movieId=${movieId}`); if (historyApiResponseData \u0026\u0026 historyApiResponseData.length \u003e 0) { const historyId = historyApiResponseData[0].id; // Get the history ID console.log('History ID:', historyId); // Use the history ID to mark the movie as failed const markFailedResponse = await makeRadarrRequest(`history/failed/${historyId}`, 'POST'); if (markFailedResponse) { console.log('Movie successfully marked as failed.'); } else { console.error('Failed to mark movie as failed'); } } else { console.error('No history found for movie ID:', movieId); } } else { console.error('No movies found for TMDB ID:', tmdbId); } } }; #7 Delay 5 seconds Give time to process.\n#8 Delete Movie File I didnt want to delete the actual movie from Radarr, but just the file itself, thus alot of trial and error, but a working script. All I do here is the Code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code\nexport const code = async (inputs) =\u003e { const issueSubject = inputs.issue.subject; const movieNameRegex = /(.*)\\s\\((\\d{4})\\)/; const match = movieNameRegex.exec(issueSubject); if (match) { const movieName = match[1]; const year = match[2]; const tmdbId = inputs.issue.media.tmdbId; console.log(`Movie name: ${movieName}`); console.log(`Year: ${year}`); console.log(`TMDB ID: ${tmdbId}`); // Define your Radarr API key const radarrApiKey = 'your-api-key'; // Replace with your Radarr API key const radarrBaseUrl = 'https://radarr.example.com/api/v3'; // Use Radarr's API to look up the movie by TMDB ID and get the Radarr ID const radarrApiUrl = `${radarrBaseUrl}/movie?tmdbId=${tmdbId}`; console.log('Calling Radarr API to look up the movie...'); const radarrApiResponse = await fetch(radarrApiUrl, { method: 'GET', headers: { 'X-Api-Key': radarrApiKey, }, }); if (radarrApiResponse.ok) { console.log('Radarr API lookup successful.'); const radarrApiResponseData = await radarrApiResponse.json(); if (radarrApiResponseData.length \u003e 0) { // If the response is an array, you should loop through the results // and access the Radarr ID for each movie. for (const movie of radarrApiResponseData) { const radarrMovieId = movie.movieFile.id; console.log('Radarr Movie ID:', radarrMovieId); // Use the Radarr movie ID to delete the corresponding movie file const deleteMovieFileUrl = `${radarrBaseUrl}/movieFile/${radarrMovieId}`; console.log(`Calling Radarr API to delete movie file: ${deleteMovieFileUrl}`); const deleteMovieFileResponse = await fetch(deleteMovieFileUrl, { method: 'DELETE', headers: { 'X-Api-Key': radarrApiKey, }, }); if (deleteMovieFileResponse.ok) { console.log(`Movie file successfully deleted.`); } else { console.error(`Failed to delete movie file: ${deleteMovieFileResponse.statusText}`); } } } else { console.error('No movies found for TMDB ID:', tmdbId); } } else { console.error('Radarr API lookup failed:', radarrApiResponse.statusText); } } }; #9 Delay 5 seconds #10 Search in Radarr Researches for movie just deleted.\nAll I do here is the Code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code\nexport const code = async (inputs) =\u003e { const issueSubject = inputs.issue.subject; const movieNameRegex = /(.*)\\s\\((\\d{4})\\)/; const match = movieNameRegex.exec(issueSubject); if (match) { const movieName = match[1]; const year = match[2]; const tmdbId = inputs.issue.media.tmdbId; console.log(`Movie name: ${movieName}`); console.log(`Year: ${year}`); console.log(`TMDB ID: ${tmdbId}`); // Define your Radarr API key const radarrApiKey = 'your-api-key'; // Replace with your Radarr API key const radarrBaseUrl = 'https://radarr.example.com/api/v3' // Use Radarr's API to look up the movie by TMDB ID and get the Radarr ID const radarrApiUrl = `${radarrBaseUrl}/movie?tmdbId=${tmdbId}`; console.log('Calling Radarr API to look up the movie...'); const radarrApiResponse = await fetch(radarrApiUrl, { method: 'GET', headers: { 'X-Api-Key': radarrApiKey, }, }); if (radarrApiResponse.ok) { console.log('Radarr API lookup successful.'); const radarrApiResponseData = await radarrApiResponse.json(); if (radarrApiResponseData.length \u003e 0) { const movieId = radarrApiResponseData[0].id; // Get the Radarr ID of the first movie console.log('Radarr Movie ID:', movieId); // Trigger Radarr to search for the movie and download const searchUrl = `${radarrBaseUrl}/command`; console.log(`Calling Radarr API to search for the movie: ${searchUrl}`); const searchRequestBody = { name: 'MoviesSearch', movieIds: [movieId], }; const searchResponse = await fetch(searchUrl, { method: 'POST', headers: { 'X-Api-Key': radarrApiKey, 'Content-Type': 'application/json', }, body: JSON.stringify(searchRequestBody), }); if (searchResponse.ok) { console.log('Radarr movie search initiated.'); } else { console.error(`Failed to initiate movie search: ${searchResponse.statusText}`); } } else { console.error('No movies found for TMDB ID:', tmdbId); } } else { console.error('Radarr API lookup failed:', radarrApiResponse.statusText); } } }; #11 Delay 4 minutes This gives your download client time to download and transfer file to mapped directory. I have Gig+ internet and 99% of the time everything is done in 4 minutes.\n#12 Scan JF Libraries Using core HTTP feature, send a http POST request to https://jellyfin.domain.com/Library/Refresh with Headers X-MediaBrowser-Token and value is your Jellyfin API Key\nI only do this as Jellyfin doesnt scan my NAS whenever I add a new file.\n#13 Add Comment/Resolve Issue This just automatically resolves the issue in Jellyseerr and adds a comment letting the user know action was taken.\nAll I do here is the Code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code\nexport const code = async (inputs) =\u003e { const issueId = inputs.issue.issue_id; const apiKey = 'your-api-key'; // Replace with your actual API key const baseURL = 'https://jellyseerr.example.com/api/v1' const commentApiUrl = `${baseURL}/issue/${issueId}/comment`; const statusApiUrl = `${baseURL}/issue/${issueId}/resolved`; const headers = { 'Content-Type': 'application/json', 'X-Api-Key': apiKey, }; const commentData = { message: 'Your issue has been approved and a new version of the content has been automatically downloaded and updated in Jellyfin. Your issue has been set to Resolved. If you are still experiencing problems, re-open your issue.', }; const commentRequestOptions = { method: 'POST', headers: headers, body: JSON.stringify(commentData), }; try { // Post comment const commentResponse = await fetch(commentApiUrl, commentRequestOptions); const commentData = await commentResponse.json(); console.log(commentData); // Update status const statusRequestOptions = { method: 'POST', // or PUT depending on your API headers: headers, // Add any additional data required to update the status }; const statusResponse = await fetch(statusApiUrl, statusRequestOptions); const statusData = await statusResponse.json(); console.log(statusData); return true; } catch (error) { console.error(error); return false; } }; We are now done with the Radarr flow. Moving onto Sonarr.\n#14 Branch Episodes and Seasons With the issue data, we also get an “extra” field which is where the requests Affected Episode Number and Affected Season Number are. What this branch does is see if there is an affected Episode Number by seeing if that field in the data exists. You will have to create an issue for a TV show and say an entire season is affected. Then use that sample data, go back to this branch and add the value\nJellyseerr Issue Reported body extra 1 as pictured #15 Add Comment/Resolve Issue This path meant the user reported an issue on an entire season and basically sends a response to them telling them to do it individually. I probably could have gotten a script working for this but I spent a few hours on it and frustratingly gave up. Maybe I will update this in the future but for now idrc.\nAgain, all I do here is the code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code\nexport const code = async (inputs) =\u003e { const issueId = inputs.issue.issue_id; const apiKey = 'your-api-key'; // Replace with your actual API key const baseURL = 'https://jellyseerr.example.com/api/v1' const commentApiUrl = `${baseURL}/issue/${issueId}/comment`; const statusApiUrl = `${baseURL}/issue/${issueId}/resolved`; const headers = { 'Content-Type': 'application/json', 'X-Api-Key': apiKey, }; const commentData = { message: 'Please do not report an entire season as the issue. Specify each Episode number. Please delete this issue and resubmit. Your issue has been automatically marked as Resolved.', }; const commentRequestOptions = { method: 'POST', headers: headers, body: JSON.stringify(commentData), }; try { // Post comment const commentResponse = await fetch(commentApiUrl, commentRequestOptions); const commentData = await commentResponse.json(); console.log(commentData); // Update status const statusRequestOptions = { method: 'POST', headers: headers, }; const statusResponse = await fetch(statusApiUrl, statusRequestOptions); const statusData = await statusResponse.json(); console.log(statusData); return true; } catch (error) { console.error(error); return false; } }; #16 Mark as Failed Sonarr Again, all I do here is the code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code\nexport const code = async (inputs) =\u003e { const issueSubject = inputs.issue.subject; const tvShowNameRegex = /(.*)\\s\\((\\d{4})\\)/; const match = tvShowNameRegex.exec(issueSubject); if (match) { const tvShowName = match[1]; const year = match[2]; const tvdbId = inputs.issue.media.tvdbId; // Using TVDB ID for TV shows console.log(`TV Show name: ${tvShowName}`); console.log(`Year: ${year}`); console.log(`TVDB ID: ${tvdbId}`); // Define your Sonarr API key and base URL const sonarrApiKey = 'your-api-key'; // Replace with your Sonarr API key const sonarrBaseUrl = 'https://sonarr.example.com/api/v3'; // Use Sonarr's API to look up the series by TVDB ID and get the Sonarr ID const seriesResponse = await fetch(`${sonarrBaseUrl}/series/lookup?term=tvdb:${tvdbId}`, { method: 'GET', headers: { 'X-Api-Key': sonarrApiKey, }, }); if (seriesResponse.ok) { const seriesData = await seriesResponse.json(); if (seriesData.length \u003e 0) { const seriesId = seriesData[0].id; // Find the affected season and episode numbers const affectedSeason = parseInt(inputs.issue.extra.find(item =\u003e item.name === 'Affected Season')?.value); const affectedEpisode = parseInt(inputs.issue.extra.find(item =\u003e item.name === 'Affected Episode')?.value); console.log(\"Season ID = \" + affectedSeason); console.log(\"Episode ID = \" + affectedEpisode); // Get the history of the series const historyResponse = await fetch(`${sonarrBaseUrl}/history/series?seriesId=${seriesId}`, { method: 'GET', headers: { 'X-Api-Key': sonarrApiKey, }, }); if (historyResponse.ok) { const historyData = await historyResponse.json(); // Find the most recent entry that matches the affected season and episode const recentEntry = historyData.find(entry =\u003e { const sourceTitleMatch = /S(\\d+)E(\\d+)/.exec(entry.sourceTitle); if (sourceTitleMatch) { const sourceSeason = parseInt(sourceTitleMatch[1]); const sourceEpisode = parseInt(sourceTitleMatch[2]); return sourceSeason === affectedSeason \u0026\u0026 sourceEpisode === affectedEpisode; } return false; }); if (recentEntry) { const episodeId = recentEntry.episodeId; const id = recentEntry.id; // This is the ID you need for marking as failed console.log(\"Found Episode ID = \" + episodeId); console.log(\"Found Most Recent Download ID = \" + id); // Use the episode ID to mark the episode as failed const markFailedUrl = `${sonarrBaseUrl}/history/failed/${id}`; console.log(`Calling Sonarr API to mark episode as failed: ${markFailedUrl}`); const markFailedResponse = await fetch(markFailedUrl, { method: 'POST', headers: { 'X-Api-Key': sonarrApiKey, }, body: JSON.stringify({ status: 'failed' }), }); if (markFailedResponse.ok) { console.log('Episode successfully marked as failed in Sonarr.'); } else { console.error(`Failed to mark episode as failed in Sonarr: ${markFailedResponse.statusText}`); } } else { console.error('No matching entry found in the series history for the affected episode.'); } } else { console.error('Failed to fetch series history:', historyResponse.statusText); } } else { console.error('No series found for the provided TVDB ID:', tvdbId); } } else { console.error('Failed to fetch series data:', seriesResponse.statusText); } } }; You may have to play around a bit and see if when you run this it auto searches for the file. My Sonarr does but my Radarr doesnt, couldnt find any setting. Regardless I include a search command and even if Sonarr searches 2 times it appears 1 will cancel out. This is why no time delay between this code and file deletion.\n#17 Delete File Sonarr Again, all I do here is the code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code\nexport const code = async (inputs) =\u003e { const issueSubject = inputs.issue.subject; const tvShowNameRegex = /(.*)\\s\\((\\d{4})\\)/; const match = tvShowNameRegex.exec(issueSubject); if (match) { const tvShowName = match[1]; const year = match[2]; const tvdbId = inputs.issue.media.tvdbId; console.log(`TV Show name: ${tvShowName}`); console.log(`Year: ${year}`); console.log(`TVDB ID: ${tvdbId}`); const sonarrApiKey = 'your-api-key'; const sonarrBaseUrl = 'https://sonarr.example.com/api/v3'; const seriesResponse = await fetch(`${sonarrBaseUrl}/series/lookup?term=tvdb:${tvdbId}`, { method: 'GET', headers: { 'X-Api-Key': sonarrApiKey, }, }); if (seriesResponse.ok) { const seriesData = await seriesResponse.json(); if (seriesData.length \u003e 0) { const seriesId = seriesData[0].id; const affectedSeason = parseInt(inputs.issue.extra.find(item =\u003e item.name === 'Affected Season')?.value); const affectedEpisode = parseInt(inputs.issue.extra.find(item =\u003e item.name === 'Affected Episode')?.value); const episodeFilesResponse = await fetch(`${sonarrBaseUrl}/episodefile?seriesId=${seriesId}`, { method: 'GET', headers: { 'X-Api-Key': sonarrApiKey, }, }); if (episodeFilesResponse.ok) { const episodeFilesData = await episodeFilesResponse.json(); const targetEpisode = episodeFilesData.find(episode =\u003e { const parsedPath = episode.relativePath.match(/S(\\d+)E(\\d+)/); if (parsedPath) { const episodeSeason = parseInt(parsedPath[1]); const episodeNumber = parseInt(parsedPath[2]); return episodeSeason === affectedSeason \u0026\u0026 episodeNumber === affectedEpisode; } return false; }); if (targetEpisode) { const targetEpisodeId = targetEpisode.id; console.log(\"Found Episode ID = \" + targetEpisodeId); // Delete the target episode file const deleteEpisodeUrl = `${sonarrBaseUrl}/episodefile/${targetEpisodeId}`; const deleteEpisodeResponse = await fetch(deleteEpisodeUrl, { method: 'DELETE', headers: { 'X-Api-Key': sonarrApiKey, }, }); if (deleteEpisodeResponse.ok) { console.log('Episode file successfully deleted in Sonarr.'); } else { console.error(`Failed to delete episode file in Sonarr: ${deleteEpisodeResponse.statusText}`); } } else { console.error('No matching episode found in the episode files for the affected season and episode.'); } } else { console.error('Failed to fetch episode files:', episodeFilesResponse.statusText); } } else { console.error('No series found for the provided TVDB ID:', tvdbId); } } else { console.error('Failed to fetch series data:', seriesResponse.statusText); } } }; #18 Re-search in Sonarr Again, all I do here is the code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code\nexport const code = async (inputs) =\u003e { const issueSubject = inputs.issue.subject; const tvShowNameRegex = /(.*)\\s\\((\\d{4})\\)/; const match = tvShowNameRegex.exec(issueSubject); if (match) { const tvShowName = match[1]; const year = match[2]; const tvdbId = inputs.issue.media.tvdbId; console.log(`TV Show name: ${tvShowName}`); console.log(`Year: ${year}`); console.log(`TVDB ID: ${tvdbId}`); const sonarrApiKey = 'your-api-key'; const sonarrBaseUrl = 'https://sonarr.example.com/api/v3'; const seriesResponse = await fetch(`${sonarrBaseUrl}/series/lookup?term=tvdb:${tvdbId}`, { method: 'GET', headers: { 'X-Api-Key': sonarrApiKey, }, }); if (seriesResponse.ok) { const seriesData = await seriesResponse.json(); if (seriesData.length \u003e 0) { const seriesId = seriesData[0].id; const affectedSeason = parseInt(inputs.issue.extra.find(item =\u003e item.name === 'Affected Season')?.value); const affectedEpisode = parseInt(inputs.issue.extra.find(item =\u003e item.name === 'Affected Episode')?.value); const historyResponse = await fetch(`${sonarrBaseUrl}/history/series?seriesId=${seriesId}`, { method: 'GET', headers: { 'X-Api-Key': sonarrApiKey, }, }); if (historyResponse.ok) { const historyData = await historyResponse.json(); const recentEntry = historyData.find(entry =\u003e { const sourceTitleMatch = /S(\\d+)E(\\d+)/.exec(entry.sourceTitle); if (sourceTitleMatch) { const sourceSeason = parseInt(sourceTitleMatch[1]); const sourceEpisode = parseInt(sourceTitleMatch[2]); return sourceSeason === affectedSeason \u0026\u0026 sourceEpisode === affectedEpisode; } return false; }); if (recentEntry) { const episodeId = recentEntry.episodeId; console.log(\"Found Episode ID = \" + episodeId); // Perform the episode search const searchPayload = { name: 'EpisodeSearch', episodeIds: [episodeId], }; const searchResponse = await fetch(`${sonarrBaseUrl}/command`, { method: 'POST', headers: { 'X-Api-Key': sonarrApiKey, 'Content-Type': 'application/json', }, body: JSON.stringify(searchPayload), }); if (searchResponse.ok) { console.log('Episode search command successfully sent to Sonarr.'); } else { console.error(`Failed to send episode search command to Sonarr: ${searchResponse.statusText}`); } } else { console.error('No matching entry found in the series history for the affected episode.'); } } else { console.error('Failed to fetch series history:', historyResponse.statusText); } } else { console.error('No series found for the provided TVDB ID:', tvdbId); } } else { console.error('Failed to fetch series data:', seriesResponse.statusText); } } }; #19 Delay for 4 Minutes Waiting for media to download and transfer.\n#20 Add Comment/Resolve Issue Again, all I do here is the code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code\nexport const code = async (inputs) =\u003e { const issueId = inputs.issue.issue_id; const apiKey = 'your-api-key'; // Replace with your actual API key const baseURL = 'https://jellyseerr.example.com/api/v1' const commentApiUrl = `${baseURL}/issue/${issueId}/comment`; const statusApiUrl = `${baseURL}/issue/${issueId}/resolved`; const headers = { 'Content-Type': 'application/json', 'X-Api-Key': apiKey, }; const commentData = { message: 'Your issue has been approved and a new version of the content has been automatically downloaded and updated in Jellyfin. Your issue has been set to Resolved. If you are still experiencing problems, re-open your issue.', }; const commentRequestOptions = { method: 'POST', headers: headers, body: JSON.stringify(commentData), }; try { // Post comment const commentResponse = await fetch(commentApiUrl, commentRequestOptions); const commentData = await commentResponse.json(); console.log(commentData); // Update status const statusRequestOptions = { method: 'POST', headers: headers, }; const statusResponse = await fetch(statusApiUrl, statusRequestOptions); const statusData = await statusResponse.json(); console.log(statusData); return true; } catch (error) { console.error(error); return false; } }; #21 Same as #12 Conclusion Once all this is done you can publish the flow and try it out!\nIf you have any feedback you can DM on Reddit. Id love to see how you have edited this automation to your exact needs.\nNow the hard part, getting your users to actually report the issues in Jellyseerr and not reach out to you!\n",
"wordCount" : "3952",
"inLanguage": "en",
"datePublished": "2023-11-11T16:20:00Z",
"dateModified": "2023-11-11T16:20:00Z",
"author":{
"@type": "Person",
"name": "Mafyuh"
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "http://localhost:1313/post/how-to-automate-jellyfin-issue-handling/"
},
"publisher": {
"@type": "Organization",
"name": "Mafyuh's Blog",
"logo": {
"@type": "ImageObject",
"url": "http://localhost:1313/assets/favicon/favicon.svg"
}
}
}
</script>
</head>
<body class="" id="top">
<script>
if (localStorage.getItem("pref-theme") === "dark") {
document.body.classList.add('dark');
} else if (localStorage.getItem("pref-theme") === "light") {
document.body.classList.remove('dark')
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.body.classList.add('dark');
}
</script>
<header class="header">
<nav class="nav">
<div class="logo">
<a href="http://localhost:1313/" accesskey="h" title="Mafyuh&#39;s Blog (Alt + H)">
<img src="http://localhost:1313/assets/favicon/favicon.svg" alt="" aria-label="logo"
height="35">Mafyuh&#39;s Blog</a>
<div class="logo-switches">
<button id="theme-toggle" accesskey="t" title="(Alt + T)">
<svg id="moon" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
<svg id="sun" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</button>
<ul class="lang-switch"><li>|</li>
</ul>
</div>
</div>
<ul id="menu">
<li>
<a href="http://localhost:1313/guides/" title="Guides">
<span>Guides</span>
</a>
</li>
<li>
<a href="http://localhost:1313/tags/" title="Tags">
<span>Tags</span>
</a>
</li>
</ul>
</nav>
</header>
<main class="main">
<article class="post-single">
<header class="post-header">
<div class="breadcrumbs"><a href="http://localhost:1313/">Home</a>&nbsp;»&nbsp;<a href="http://localhost:1313/post/">Posts</a></div>
<h1 class="post-title entry-hint-parent">
How To Automate Jellyfin Issue Handling
</h1>
<div class="post-meta"><span title='2023-11-11 16:20:00 +0000 UTC'>November 11, 2023</span>&nbsp;·&nbsp;19 min&nbsp;·&nbsp;3952 words&nbsp;·&nbsp;Mafyuh
</div>
</header> <div class="toc">
<details >
<summary accesskey="c" title="(Alt + C)">
<span class="details">Table of Contents</span>
</summary>
<div class="inner"><nav id="TableOfContents">
<ul>
<li><a href="#requirements">Requirements</a></li>
<li><a href="#how-it-works">How it Works</a></li>
<li><a href="#1-jellyseer-issue-reported">#1 Jellyseer Issue Reported</a></li>
<li><a href="#optional-2-create-approval-links">(Optional) #2 Create Approval Links</a></li>
<li><a href="#optional-3-send-email">(Optional) #3 Send Email</a></li>
<li><a href="#optional-4-wait-for-approval">(Optional) #4 Wait for Approval</a></li>
<li><a href="#5-radarrsonarr-branch">#5 Radarr/Sonarr Branch</a></li>
<li><a href="#6-radarr-mark-as-failed">#6 Radarr Mark As Failed</a></li>
<li><a href="#7-delay-5-seconds">#7 Delay 5 seconds</a></li>
<li><a href="#8-delete-movie-file">#8 Delete Movie File</a></li>
<li><a href="#9-delay-5-seconds">#9 Delay 5 seconds</a></li>
<li><a href="#10-search-in-radarr">#10 Search in Radarr</a></li>
<li><a href="#11-delay-4-minutes">#11 Delay 4 minutes</a></li>
<li><a href="#12-scan-jf-libraries">#12 Scan JF Libraries</a></li>
<li><a href="#13-add-commentresolve-issue">#13 Add Comment/Resolve Issue</a></li>
<li><a href="#14-branch-episodes-and-seasons">#14 Branch Episodes and Seasons</a></li>
<li><a href="#15-add-commentresolve-issue">#15 Add Comment/Resolve Issue</a></li>
<li><a href="#16-mark-as-failed-sonarr">#16 Mark as Failed Sonarr</a></li>
<li><a href="#17-delete-file-sonarr">#17 Delete File Sonarr</a></li>
<li><a href="#18-re-search-in-sonarr">#18 Re-search in Sonarr</a></li>
<li><a href="#19-delay-for-4-minutes">#19 Delay for 4 Minutes</a></li>
<li><a href="#20-add-commentresolve-issue">#20 Add Comment/Resolve Issue</a></li>
<li><a href="#21-same-as-12">#21 Same as #12</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</nav>
</div>
</details>
</div>
<div class="post-content"><p>I wanted a way to automate when users tell me a video on my Jellyfin server has an issue. After alot of trial and error, ChatGPT, Bard and I came up with this automation.</p>
<h2 id="requirements">Requirements<a hidden class="anchor" aria-hidden="true" href="#requirements">#</a></h2>
<p>My only requirements when making this was that it was free and self-hostable. Not even any NPM extensions are required in AP.
Actual Software requirements are:</p>
<ol>
<li>Sonarr</li>
<li>Radarr</li>
<li>Overseerr/Jellyseerr</li>
</ol>
<p>Optional</p>
<ol>
<li>SMTP server or ability to send SMTP messages (can also use discord)</li>
<li>ActivePieces or any other automation platform that supports TS. (Zapier, n8n, etc)</li>
</ol>
<p>Here&rsquo;s a great AP setup and how-to video:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe src="https://www.youtube.com/embed/kVaCDc2CXik?si=geKXwgRjU6onDyjS" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>
<p>Note: I didn&rsquo;t do any of the ngrok stuff. I just have Nginx Proxy manager setup with a wildcard certificate. Then just give a domain name and point and its ip:8080. No special Nginx config needed. Make sure you set AP_FRONTEND_URL in .env</p>
<p>This blog post is rather long, if you prefer to see the code on git you can find all this code <a href="https://git.mafyuh.dev/mafyuh/ActivePieces">here</a>.</p>
<h2 id="how-it-works">How it Works<a hidden class="anchor" aria-hidden="true" href="#how-it-works">#</a></h2>
<p>Whenever a user Reports an Issue in Jellyseerr, a webhook is sent to activepieces (AP) with the Issue data, this triggers the automation to mark as failed, delete file, re-search, refresh Jellyfin Libraries and Resolve the original issue with comment. There is an optional feature to approve or deny the automation.</p>
<p>Works across Radarr and Sonarr, as the issue reported can be either Movie or TV show.</p>
<p>Only caveat is if the issue is an entire Season , we just mark the issue as resolved and leave a comment saying to submit an issue for each episode individually</p>
<p>Works on my Jellyfin, Jellyseer, Radarr and Sonarr setup. I dont use Plex but all you would have to change is the Jellyfin Refresh Library Request to match Plex&rsquo;s equivalent.</p>
<p>Here is a pic of the full automation.</p>
<p><img loading="lazy" src="/assets/img/automation.png" alt="Automation Image" />
</p>
<p>Everything of value is logged to the console so check there for errors. Lets start breaking it down.</p>
<h2 id="1-jellyseer-issue-reported">#1 Jellyseer Issue Reported<a hidden class="anchor" aria-hidden="true" href="#1-jellyseer-issue-reported">#</a></h2>
<p>First thing is create a flow in AP, select a trigger, and search for webhook. This will give you the webhook URL for Jellyseerr.
Next, in Jellyseerr, under <strong>Settings - Users - Default Permissions</strong> make sure <strong>Report Issues</strong> is checked and save changes.
Then under <strong>Settings - Notifications - Webhook</strong> make a webhook notification, with the URL from AP, and just enabling <strong>Issue Reported</strong> and <strong>Issue Reopened</strong>.
This should look as follows (dont worry about my payload showing mediaId, this has since been deleted)</p>
<p><img loading="lazy" src="/assets/img/seer.png" alt="Jellyseer Image" />
</p>
<p>Here is my full JSON payload just in case:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;notification_type&#34;</span><span class="p">:</span> <span class="s2">&#34;{{notification_type}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;event&#34;</span><span class="p">:</span> <span class="s2">&#34;{{event}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;subject&#34;</span><span class="p">:</span> <span class="s2">&#34;{{subject}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;{{message}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;image&#34;</span><span class="p">:</span> <span class="s2">&#34;{{image}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;{{media}}&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;media_type&#34;</span><span class="p">:</span> <span class="s2">&#34;{{media_type}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;tmdbId&#34;</span><span class="p">:</span> <span class="s2">&#34;{{media_tmdbid}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;tvdbId&#34;</span><span class="p">:</span> <span class="s2">&#34;{{media_tvdbid}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;{{media_status}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;status4k&#34;</span><span class="p">:</span> <span class="s2">&#34;{{media_status4k}}&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;{{request}}&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;request_id&#34;</span><span class="p">:</span> <span class="s2">&#34;{{request_id}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;requestedBy_email&#34;</span><span class="p">:</span> <span class="s2">&#34;{{requestedBy_email}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;requestedBy_username&#34;</span><span class="p">:</span> <span class="s2">&#34;{{requestedBy_username}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;requestedBy_avatar&#34;</span><span class="p">:</span> <span class="s2">&#34;{{requestedBy_avatar}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;requestedBy_settings_discordId&#34;</span><span class="p">:</span> <span class="s2">&#34;{{requestedBy_settings_discordId}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;requestedBy_settings_telegramChatId&#34;</span><span class="p">:</span> <span class="s2">&#34;{{requestedBy_settings_telegramChatId}}&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;{{issue}}&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;issue_id&#34;</span><span class="p">:</span> <span class="s2">&#34;{{issue_id}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;issue_type&#34;</span><span class="p">:</span> <span class="s2">&#34;{{issue_type}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;issue_status&#34;</span><span class="p">:</span> <span class="s2">&#34;{{issue_status}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;reportedBy_email&#34;</span><span class="p">:</span> <span class="s2">&#34;{{reportedBy_email}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;reportedBy_username&#34;</span><span class="p">:</span> <span class="s2">&#34;{{reportedBy_username}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;reportedBy_avatar&#34;</span><span class="p">:</span> <span class="s2">&#34;{{reportedBy_avatar}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;reportedBy_settings_discordId&#34;</span><span class="p">:</span> <span class="s2">&#34;{{reportedBy_settings_discordId}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;reportedBy_settings_telegramChatId&#34;</span><span class="p">:</span> <span class="s2">&#34;{{reportedBy_settings_telegramChatId}}&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;{{comment}}&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;comment_message&#34;</span><span class="p">:</span> <span class="s2">&#34;{{comment_message}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;commentedBy_email&#34;</span><span class="p">:</span> <span class="s2">&#34;{{commentedBy_email}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;commentedBy_username&#34;</span><span class="p">:</span> <span class="s2">&#34;{{commentedBy_username}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;commentedBy_avatar&#34;</span><span class="p">:</span> <span class="s2">&#34;{{commentedBy_avatar}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;commentedBy_settings_discordId&#34;</span><span class="p">:</span> <span class="s2">&#34;{{commentedBy_settings_discordId}}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;commentedBy_settings_telegramChatId&#34;</span><span class="p">:</span> <span class="s2">&#34;{{commentedBy_settings_telegramChatId}}&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;{{extra}}&#34;</span><span class="p">:</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>You should be able to Report an issue on a random movie in Jellyseerr and then go to the webhook trigger and choose Generate sample data, and you should be able to see the data from the request. I recommend doing this and creating an issue for an example movie, TV series( All Seasons) and a TV Series (1 Season)</p>
<h2 id="optional-2-create-approval-links">(Optional) #2 Create Approval Links<a hidden class="anchor" aria-hidden="true" href="#optional-2-create-approval-links">#</a></h2>
<p>In AP add the next step and search Approval, then create approval links.</p>
<h2 id="optional-3-send-email">(Optional) #3 Send Email<a hidden class="anchor" aria-hidden="true" href="#optional-3-send-email">#</a></h2>
<p>This is so I can either approve or deny the file from being deleted. Maybe it&rsquo;s a client issue and I know for a fact my file is good and I dont want deleted. Thus the links are sent to me along with the some data from the request, so I know what I am approving/denying.</p>
<p>You can use the core SMTP feature but its limited to text. I wanted some more customizability so I chose Resend (supports html) and set up an acct there with one of my extra domains.</p>
<p>You don&rsquo;t have to use email, you can use Discord, SMS, any generic http request or whatever you want. I just use email since I pay for my domains and pay Proton Mail for emails so might as well use em.</p>
<p>Not gonna get too into this, I dont care too much about it atm, customize your email to your liking, but I&rsquo;ll post my somewhat working HTML body here. I literally just copied what Bard gave me, added in data from response and tested and said looks good enough, glitches on my mobile too.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Jellyseerr Issue Reported<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">font-family</span><span class="p">:</span> <span class="kc">sans-serif</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">padding</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#222</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">container</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">width</span><span class="p">:</span> <span class="mi">80</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">padding</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#333</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">border-radius</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">box-shadow</span><span class="p">:</span> <span class="mi">0</span><span class="kt">px</span> <span class="mi">2</span><span class="kt">px</span> <span class="mi">5</span><span class="kt">px</span> <span class="nb">rgba</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">header</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">justify-content</span><span class="p">:</span> <span class="kc">space-between</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">padding-bottom</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">border-bottom</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#555</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">header</span> <span class="nt">h1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">font-size</span><span class="p">:</span> <span class="mi">24</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">header</span> <span class="nt">img</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">width</span><span class="p">:</span> <span class="mi">50</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">height</span><span class="p">:</span> <span class="mi">50</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">border-radius</span><span class="p">:</span> <span class="mi">50</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">object-fit</span><span class="p">:</span> <span class="kc">cover</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">content</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">issue-subject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">font-size</span><span class="p">:</span> <span class="mi">18</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">issue-message</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">font-size</span><span class="p">:</span> <span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">line-height</span><span class="p">:</span> <span class="mf">1.5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">color</span><span class="p">:</span> <span class="mh">#ccc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">issue-image</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">height</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">buttons</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">justify-content</span><span class="p">:</span> <span class="kc">space-between</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">button</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#007bff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">padding</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">border-radius</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">cursor</span><span class="p">:</span> <span class="kc">pointer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">text-decoration</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">button</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#0056b3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">disapprove-button</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#dc3545</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">padding</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">border-radius</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">cursor</span><span class="p">:</span> <span class="kc">pointer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">text-decoration</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">disapprove-button</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">background-color</span><span class="p">:</span> <span class="mh">#bd2830</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;header&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://your-logo-url&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;Jellyseerr Logo&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Jellyseerr Issue Reported<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;content&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;issue-subject&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">Jellyseerr Issue Reported
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;issue-message&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">This issue was submitted by 1. Jellyseerr Issue Reported body issue reportedBy_username.
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">br</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">The reason for the issue:1. Jellyseerr Issue Reported body message
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">br</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">Please review the issue and take appropriate action.
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">br</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34; 1. Jellyseerr Issue Reported body image &#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;buttons&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;2. Create Approval Links approvalLink &#34;</span><span class="p">&gt;&lt;</span><span class="nt">button</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;button&#34;</span><span class="p">&gt;</span>Approve<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;2. Create Approval Links disapprovalLink &#34;</span><span class="p">&gt;&lt;</span><span class="nt">button</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;disapprove-button&#34;</span><span class="p">&gt;</span>Deny<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>And here&rsquo;s what an email looks like:</p>
<p><img loading="lazy" src="/assets/img/email.png" alt="Email Look" />
</p>
<h2 id="optional-4-wait-for-approval">(Optional) #4 Wait for Approval<a hidden class="anchor" aria-hidden="true" href="#optional-4-wait-for-approval">#</a></h2>
<p>Pauses flow until I approve or deny.</p>
<h2 id="5-radarrsonarr-branch">#5 Radarr/Sonarr Branch<a hidden class="anchor" aria-hidden="true" href="#5-radarrsonarr-branch">#</a></h2>
<p>As stated previously, I wanted this to work regardless if Movie or TV show. So using the core Branch feature we just say that if the media_type value from the issue contains the text movie, its true.</p>
<p><img loading="lazy" src="/assets/img/radarr-branch.png" alt="Radarr AP Image" />
</p>
<h2 id="6-radarr-mark-as-failed">#6 Radarr Mark As Failed<a hidden class="anchor" aria-hidden="true" href="#6-radarr-mark-as-failed">#</a></h2>
<p>All I do here is the Code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code (CASE SENSITIVE)</p>
<p><img loading="lazy" src="/assets/img/radarr-mark-as-failed.png" alt="Radarr AP Image" />
</p>
<p>Here is the code. Just replace api key and base url:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">code</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">inputs</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">issueSubject</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">subject</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">movieNameRegex</span> <span class="o">=</span> <span class="sr">/(.*)\s\((\d{4})\)/</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">match</span> <span class="o">=</span> <span class="nx">movieNameRegex</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="nx">issueSubject</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">match</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">movieName</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">year</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tmdbId</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">media</span><span class="p">.</span><span class="nx">tmdbId</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Movie name: </span><span class="si">${</span><span class="nx">movieName</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Year: </span><span class="si">${</span><span class="nx">year</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`TMDB ID: </span><span class="si">${</span><span class="nx">tmdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Define your Radarr API key and base URL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">radarrApiKey</span> <span class="o">=</span> <span class="s1">&#39;your-api-key&#39;</span><span class="p">;</span> <span class="c1">// Replace with your Radarr API key
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">radarrBaseUrl</span> <span class="o">=</span> <span class="s1">&#39;https://radarr.example.com/api/v3/&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Define a function to make API requests to Radarr
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">makeRadarrRequest</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">endpoint</span><span class="p">,</span> <span class="nx">method</span> <span class="o">=</span> <span class="s1">&#39;GET&#39;</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">apiUrl</span> <span class="o">=</span> <span class="nx">radarrBaseUrl</span> <span class="o">+</span> <span class="nx">endpoint</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Calling Radarr API: </span><span class="si">${</span><span class="nx">apiUrl</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">apiUrl</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">radarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`Radarr API request failed: </span><span class="si">${</span><span class="nx">response</span><span class="p">.</span><span class="nx">statusText</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Use Radarr&#39;s API to look up the movie by TMDB ID
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">radarrApiResponseData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">makeRadarrRequest</span><span class="p">(</span><span class="sb">`movie?tmdbId=</span><span class="si">${</span><span class="nx">tmdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">radarrApiResponseData</span> <span class="o">&amp;&amp;</span> <span class="nx">radarrApiResponseData</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">movieId</span> <span class="o">=</span> <span class="nx">radarrApiResponseData</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">id</span><span class="p">;</span> <span class="c1">// Get the Radarr ID of the first movie
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Radarr Movie ID:&#39;</span><span class="p">,</span> <span class="nx">movieId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Use the Radarr movie ID to get the history of the movie
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">historyApiResponseData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">makeRadarrRequest</span><span class="p">(</span><span class="sb">`history/movie?movieId=</span><span class="si">${</span><span class="nx">movieId</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">historyApiResponseData</span> <span class="o">&amp;&amp;</span> <span class="nx">historyApiResponseData</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">historyId</span> <span class="o">=</span> <span class="nx">historyApiResponseData</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">id</span><span class="p">;</span> <span class="c1">// Get the history ID
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;History ID:&#39;</span><span class="p">,</span> <span class="nx">historyId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Use the history ID to mark the movie as failed
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">markFailedResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">makeRadarrRequest</span><span class="p">(</span><span class="sb">`history/failed/</span><span class="si">${</span><span class="nx">historyId</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="s1">&#39;POST&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">markFailedResponse</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Movie successfully marked as failed.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Failed to mark movie as failed&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;No history found for movie ID:&#39;</span><span class="p">,</span> <span class="nx">movieId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;No movies found for TMDB ID:&#39;</span><span class="p">,</span> <span class="nx">tmdbId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h2 id="7-delay-5-seconds">#7 Delay 5 seconds<a hidden class="anchor" aria-hidden="true" href="#7-delay-5-seconds">#</a></h2>
<p>Give time to process.</p>
<h2 id="8-delete-movie-file">#8 Delete Movie File<a hidden class="anchor" aria-hidden="true" href="#8-delete-movie-file">#</a></h2>
<p>I didn&rsquo;t want to delete the actual movie from Radarr, but just the file itself, thus alot of trial and error, but a working script.
All I do here is the Code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">code</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">inputs</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">issueSubject</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">subject</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">movieNameRegex</span> <span class="o">=</span> <span class="sr">/(.*)\s\((\d{4})\)/</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">match</span> <span class="o">=</span> <span class="nx">movieNameRegex</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="nx">issueSubject</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">match</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">movieName</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">year</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tmdbId</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">media</span><span class="p">.</span><span class="nx">tmdbId</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Movie name: </span><span class="si">${</span><span class="nx">movieName</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Year: </span><span class="si">${</span><span class="nx">year</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`TMDB ID: </span><span class="si">${</span><span class="nx">tmdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Define your Radarr API key
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">radarrApiKey</span> <span class="o">=</span> <span class="s1">&#39;your-api-key&#39;</span><span class="p">;</span> <span class="c1">// Replace with your Radarr API key
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">radarrBaseUrl</span> <span class="o">=</span> <span class="s1">&#39;https://radarr.example.com/api/v3&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Use Radarr&#39;s API to look up the movie by TMDB ID and get the Radarr ID
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">radarrApiUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">radarrBaseUrl</span><span class="si">}</span><span class="sb">/movie?tmdbId=</span><span class="si">${</span><span class="nx">tmdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Calling Radarr API to look up the movie...&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">radarrApiResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">radarrApiUrl</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;GET&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">radarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">radarrApiResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Radarr API lookup successful.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">radarrApiResponseData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">radarrApiResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">radarrApiResponseData</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// If the response is an array, you should loop through the results
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// and access the Radarr ID for each movie.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="nx">movie</span> <span class="k">of</span> <span class="nx">radarrApiResponseData</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">radarrMovieId</span> <span class="o">=</span> <span class="nx">movie</span><span class="p">.</span><span class="nx">movieFile</span><span class="p">.</span><span class="nx">id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Radarr Movie ID:&#39;</span><span class="p">,</span> <span class="nx">radarrMovieId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Use the Radarr movie ID to delete the corresponding movie file
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">deleteMovieFileUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">radarrBaseUrl</span><span class="si">}</span><span class="sb">/movieFile/</span><span class="si">${</span><span class="nx">radarrMovieId</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Calling Radarr API to delete movie file: </span><span class="si">${</span><span class="nx">deleteMovieFileUrl</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">deleteMovieFileResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">deleteMovieFileUrl</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;DELETE&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">radarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">deleteMovieFileResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Movie file successfully deleted.`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`Failed to delete movie file: </span><span class="si">${</span><span class="nx">deleteMovieFileResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;No movies found for TMDB ID:&#39;</span><span class="p">,</span> <span class="nx">tmdbId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Radarr API lookup failed:&#39;</span><span class="p">,</span> <span class="nx">radarrApiResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h2 id="9-delay-5-seconds">#9 Delay 5 seconds<a hidden class="anchor" aria-hidden="true" href="#9-delay-5-seconds">#</a></h2>
<h2 id="10-search-in-radarr">#10 Search in Radarr<a hidden class="anchor" aria-hidden="true" href="#10-search-in-radarr">#</a></h2>
<p>Researches for movie just deleted.</p>
<p>All I do here is the Code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">code</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">inputs</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">issueSubject</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">subject</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">movieNameRegex</span> <span class="o">=</span> <span class="sr">/(.*)\s\((\d{4})\)/</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">match</span> <span class="o">=</span> <span class="nx">movieNameRegex</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="nx">issueSubject</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">match</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">movieName</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">year</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tmdbId</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">media</span><span class="p">.</span><span class="nx">tmdbId</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Movie name: </span><span class="si">${</span><span class="nx">movieName</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Year: </span><span class="si">${</span><span class="nx">year</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`TMDB ID: </span><span class="si">${</span><span class="nx">tmdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Define your Radarr API key
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">radarrApiKey</span> <span class="o">=</span> <span class="s1">&#39;your-api-key&#39;</span><span class="p">;</span> <span class="c1">// Replace with your Radarr API key
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">radarrBaseUrl</span> <span class="o">=</span> <span class="s1">&#39;https://radarr.example.com/api/v3&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Use Radarr&#39;s API to look up the movie by TMDB ID and get the Radarr ID
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">radarrApiUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">radarrBaseUrl</span><span class="si">}</span><span class="sb">/movie?tmdbId=</span><span class="si">${</span><span class="nx">tmdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Calling Radarr API to look up the movie...&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">radarrApiResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">radarrApiUrl</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;GET&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">radarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">radarrApiResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Radarr API lookup successful.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">radarrApiResponseData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">radarrApiResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">radarrApiResponseData</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">movieId</span> <span class="o">=</span> <span class="nx">radarrApiResponseData</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">id</span><span class="p">;</span> <span class="c1">// Get the Radarr ID of the first movie
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Radarr Movie ID:&#39;</span><span class="p">,</span> <span class="nx">movieId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Trigger Radarr to search for the movie and download
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">searchUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">radarrBaseUrl</span><span class="si">}</span><span class="sb">/command`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Calling Radarr API to search for the movie: </span><span class="si">${</span><span class="nx">searchUrl</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">searchRequestBody</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;MoviesSearch&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">movieIds</span><span class="o">:</span> <span class="p">[</span><span class="nx">movieId</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">searchResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">searchUrl</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">radarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="s1">&#39;application/json&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nx">body</span>: <span class="kt">JSON.stringify</span><span class="p">(</span><span class="nx">searchRequestBody</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">searchResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Radarr movie search initiated.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`Failed to initiate movie search: </span><span class="si">${</span><span class="nx">searchResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;No movies found for TMDB ID:&#39;</span><span class="p">,</span> <span class="nx">tmdbId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Radarr API lookup failed:&#39;</span><span class="p">,</span> <span class="nx">radarrApiResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h2 id="11-delay-4-minutes">#11 Delay 4 minutes<a hidden class="anchor" aria-hidden="true" href="#11-delay-4-minutes">#</a></h2>
<p>This gives your download client time to download and transfer file to mapped directory. I have Gig+ internet and 99% of the time everything is done in 4 minutes.</p>
<h2 id="12-scan-jf-libraries">#12 Scan JF Libraries<a hidden class="anchor" aria-hidden="true" href="#12-scan-jf-libraries">#</a></h2>
<p>Using core HTTP feature, send a http POST request to <a href="https://jellyfin.domain.com/Library/Refresh">https://jellyfin.domain.com/Library/Refresh</a> with Headers
X-MediaBrowser-Token and value is your Jellyfin API Key</p>
<p>I only do this as Jellyfin doesn&rsquo;t scan my NAS whenever I add a new file.</p>
<h2 id="13-add-commentresolve-issue">#13 Add Comment/Resolve Issue<a hidden class="anchor" aria-hidden="true" href="#13-add-commentresolve-issue">#</a></h2>
<p>This just automatically resolves the issue in Jellyseerr and adds a comment letting the user know action was taken.</p>
<p>All I do here is the Code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">code</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">inputs</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">issueId</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">issue_id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">apiKey</span> <span class="o">=</span> <span class="s1">&#39;your-api-key&#39;</span><span class="p">;</span> <span class="c1">// Replace with your actual API key
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">baseURL</span> <span class="o">=</span> <span class="s1">&#39;https://jellyseerr.example.com/api/v1&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentApiUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">baseURL</span><span class="si">}</span><span class="sb">/issue/</span><span class="si">${</span><span class="nx">issueId</span><span class="si">}</span><span class="sb">/comment`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">statusApiUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">baseURL</span><span class="si">}</span><span class="sb">/issue/</span><span class="si">${</span><span class="nx">issueId</span><span class="si">}</span><span class="sb">/resolved`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">headers</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="s1">&#39;application/json&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">apiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentData</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">message</span><span class="o">:</span> <span class="s1">&#39;Your issue has been approved and a new version of the content has been automatically downloaded and updated in Jellyfin. Your issue has been set to Resolved. If you are still experiencing problems, re-open your issue.&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentRequestOptions</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span>: <span class="kt">headers</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">body</span>: <span class="kt">JSON.stringify</span><span class="p">(</span><span class="nx">commentData</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Post comment
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">commentResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">commentApiUrl</span><span class="p">,</span> <span class="nx">commentRequestOptions</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">commentResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">commentData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Update status
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">statusRequestOptions</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span> <span class="c1">// or PUT depending on your API
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">headers</span>: <span class="kt">headers</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Add any additional data required to update the status
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">statusResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">statusApiUrl</span><span class="p">,</span> <span class="nx">statusRequestOptions</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">statusData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">statusResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">statusData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>We are now done with the Radarr flow. Moving onto Sonarr.</p>
<h2 id="14-branch-episodes-and-seasons">#14 Branch Episodes and Seasons<a hidden class="anchor" aria-hidden="true" href="#14-branch-episodes-and-seasons">#</a></h2>
<p>With the issue data, we also get an &ldquo;extra&rdquo; field which is where the requests Affected Episode Number and Affected Season Number are. What this branch does is see if there is an affected Episode Number by seeing if that field in the data exists. You will have to create an issue for a TV show and say an entire season is affected. Then use that sample data, go back to this branch and add the value</p>
<ol>
<li>Jellyseerr Issue Reported body extra 1 as pictured
<img loading="lazy" src="/assets/img/branch-episodes.png" alt="Episode Branch Images" />
</li>
</ol>
<h2 id="15-add-commentresolve-issue">#15 Add Comment/Resolve Issue<a hidden class="anchor" aria-hidden="true" href="#15-add-commentresolve-issue">#</a></h2>
<p>This path meant the user reported an issue on an entire season and basically sends a response to them telling them to do it individually. I probably could have gotten a script working for this but I spent a few hours on it and frustratingly gave up. Maybe I will update this in the future but for now idrc.</p>
<p>Again, all I do here is the code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">code</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">inputs</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">issueId</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">issue_id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">apiKey</span> <span class="o">=</span> <span class="s1">&#39;your-api-key&#39;</span><span class="p">;</span> <span class="c1">// Replace with your actual API key
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">baseURL</span> <span class="o">=</span> <span class="s1">&#39;https://jellyseerr.example.com/api/v1&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentApiUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">baseURL</span><span class="si">}</span><span class="sb">/issue/</span><span class="si">${</span><span class="nx">issueId</span><span class="si">}</span><span class="sb">/comment`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">statusApiUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">baseURL</span><span class="si">}</span><span class="sb">/issue/</span><span class="si">${</span><span class="nx">issueId</span><span class="si">}</span><span class="sb">/resolved`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">headers</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="s1">&#39;application/json&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">apiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentData</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">message</span><span class="o">:</span> <span class="s1">&#39;Please do not report an entire season as the issue. Specify each Episode number. Please delete this issue and resubmit. Your issue has been automatically marked as Resolved.&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentRequestOptions</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span>: <span class="kt">headers</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">body</span>: <span class="kt">JSON.stringify</span><span class="p">(</span><span class="nx">commentData</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Post comment
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">commentResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">commentApiUrl</span><span class="p">,</span> <span class="nx">commentRequestOptions</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">commentResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">commentData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Update status
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">statusRequestOptions</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span>: <span class="kt">headers</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">statusResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">statusApiUrl</span><span class="p">,</span> <span class="nx">statusRequestOptions</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">statusData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">statusResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">statusData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h2 id="16-mark-as-failed-sonarr">#16 Mark as Failed Sonarr<a hidden class="anchor" aria-hidden="true" href="#16-mark-as-failed-sonarr">#</a></h2>
<p>Again, all I do here is the code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">code</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">inputs</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">issueSubject</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">subject</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tvShowNameRegex</span> <span class="o">=</span> <span class="sr">/(.*)\s\((\d{4})\)/</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">match</span> <span class="o">=</span> <span class="nx">tvShowNameRegex</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="nx">issueSubject</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">match</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tvShowName</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">year</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tvdbId</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">media</span><span class="p">.</span><span class="nx">tvdbId</span><span class="p">;</span> <span class="c1">// Using TVDB ID for TV shows
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`TV Show name: </span><span class="si">${</span><span class="nx">tvShowName</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Year: </span><span class="si">${</span><span class="nx">year</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`TVDB ID: </span><span class="si">${</span><span class="nx">tvdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Define your Sonarr API key and base URL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">sonarrApiKey</span> <span class="o">=</span> <span class="s1">&#39;your-api-key&#39;</span><span class="p">;</span> <span class="c1">// Replace with your Sonarr API key
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">sonarrBaseUrl</span> <span class="o">=</span> <span class="s1">&#39;https://sonarr.example.com/api/v3&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Use Sonarr&#39;s API to look up the series by TVDB ID and get the Sonarr ID
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">seriesResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">sonarrBaseUrl</span><span class="si">}</span><span class="sb">/series/lookup?term=tvdb:</span><span class="si">${</span><span class="nx">tvdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;GET&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">sonarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">seriesResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">seriesData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">seriesResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">seriesData</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">seriesId</span> <span class="o">=</span> <span class="nx">seriesData</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Find the affected season and episode numbers
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">affectedSeason</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">extra</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">item</span> <span class="o">=&gt;</span> <span class="nx">item</span><span class="p">.</span><span class="nx">name</span> <span class="o">===</span> <span class="s1">&#39;Affected Season&#39;</span><span class="p">)</span><span class="o">?</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">affectedEpisode</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">extra</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">item</span> <span class="o">=&gt;</span> <span class="nx">item</span><span class="p">.</span><span class="nx">name</span> <span class="o">===</span> <span class="s1">&#39;Affected Episode&#39;</span><span class="p">)</span><span class="o">?</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Season ID = &#34;</span> <span class="o">+</span> <span class="nx">affectedSeason</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Episode ID = &#34;</span> <span class="o">+</span> <span class="nx">affectedEpisode</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Get the history of the series
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">historyResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">sonarrBaseUrl</span><span class="si">}</span><span class="sb">/history/series?seriesId=</span><span class="si">${</span><span class="nx">seriesId</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;GET&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">sonarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">historyResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">historyData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">historyResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Find the most recent entry that matches the affected season and episode
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">recentEntry</span> <span class="o">=</span> <span class="nx">historyData</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">entry</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">sourceTitleMatch</span> <span class="o">=</span> <span class="sr">/S(\d+)E(\d+)/</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="nx">entry</span><span class="p">.</span><span class="nx">sourceTitle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">sourceTitleMatch</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">sourceSeason</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">sourceTitleMatch</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">sourceEpisode</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">sourceTitleMatch</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">sourceSeason</span> <span class="o">===</span> <span class="nx">affectedSeason</span> <span class="o">&amp;&amp;</span> <span class="nx">sourceEpisode</span> <span class="o">===</span> <span class="nx">affectedEpisode</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">recentEntry</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">episodeId</span> <span class="o">=</span> <span class="nx">recentEntry</span><span class="p">.</span><span class="nx">episodeId</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">id</span> <span class="o">=</span> <span class="nx">recentEntry</span><span class="p">.</span><span class="nx">id</span><span class="p">;</span> <span class="c1">// This is the ID you need for marking as failed
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Found Episode ID = &#34;</span> <span class="o">+</span> <span class="nx">episodeId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Found Most Recent Download ID = &#34;</span> <span class="o">+</span> <span class="nx">id</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Use the episode ID to mark the episode as failed
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">markFailedUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">sonarrBaseUrl</span><span class="si">}</span><span class="sb">/history/failed/</span><span class="si">${</span><span class="nx">id</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Calling Sonarr API to mark episode as failed: </span><span class="si">${</span><span class="nx">markFailedUrl</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">markFailedResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">markFailedUrl</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">sonarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nx">body</span>: <span class="kt">JSON.stringify</span><span class="p">({</span> <span class="nx">status</span><span class="o">:</span> <span class="s1">&#39;failed&#39;</span> <span class="p">}),</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">markFailedResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Episode successfully marked as failed in Sonarr.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`Failed to mark episode as failed in Sonarr: </span><span class="si">${</span><span class="nx">markFailedResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;No matching entry found in the series history for the affected episode.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Failed to fetch series history:&#39;</span><span class="p">,</span> <span class="nx">historyResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;No series found for the provided TVDB ID:&#39;</span><span class="p">,</span> <span class="nx">tvdbId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Failed to fetch series data:&#39;</span><span class="p">,</span> <span class="nx">seriesResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>You may have to play around a bit and see if when you run this it auto searches for the file. My Sonarr does but my Radarr doesn&rsquo;t, couldnt find any setting. Regardless I include a search command and even if Sonarr searches 2 times it appears 1 will cancel out. This is why no time delay between this code and file deletion.</p>
<h2 id="17-delete-file-sonarr">#17 Delete File Sonarr<a hidden class="anchor" aria-hidden="true" href="#17-delete-file-sonarr">#</a></h2>
<p>Again, all I do here is the code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">code</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">inputs</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">issueSubject</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">subject</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tvShowNameRegex</span> <span class="o">=</span> <span class="sr">/(.*)\s\((\d{4})\)/</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">match</span> <span class="o">=</span> <span class="nx">tvShowNameRegex</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="nx">issueSubject</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">match</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tvShowName</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">year</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tvdbId</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">media</span><span class="p">.</span><span class="nx">tvdbId</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`TV Show name: </span><span class="si">${</span><span class="nx">tvShowName</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Year: </span><span class="si">${</span><span class="nx">year</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`TVDB ID: </span><span class="si">${</span><span class="nx">tvdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">sonarrApiKey</span> <span class="o">=</span> <span class="s1">&#39;your-api-key&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">sonarrBaseUrl</span> <span class="o">=</span> <span class="s1">&#39;https://sonarr.example.com/api/v3&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">seriesResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">sonarrBaseUrl</span><span class="si">}</span><span class="sb">/series/lookup?term=tvdb:</span><span class="si">${</span><span class="nx">tvdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;GET&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">sonarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">seriesResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">seriesData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">seriesResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">seriesData</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">seriesId</span> <span class="o">=</span> <span class="nx">seriesData</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">affectedSeason</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">extra</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">item</span> <span class="o">=&gt;</span> <span class="nx">item</span><span class="p">.</span><span class="nx">name</span> <span class="o">===</span> <span class="s1">&#39;Affected Season&#39;</span><span class="p">)</span><span class="o">?</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">affectedEpisode</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">extra</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">item</span> <span class="o">=&gt;</span> <span class="nx">item</span><span class="p">.</span><span class="nx">name</span> <span class="o">===</span> <span class="s1">&#39;Affected Episode&#39;</span><span class="p">)</span><span class="o">?</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">episodeFilesResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">sonarrBaseUrl</span><span class="si">}</span><span class="sb">/episodefile?seriesId=</span><span class="si">${</span><span class="nx">seriesId</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;GET&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">sonarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">episodeFilesResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">episodeFilesData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">episodeFilesResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">targetEpisode</span> <span class="o">=</span> <span class="nx">episodeFilesData</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">episode</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">parsedPath</span> <span class="o">=</span> <span class="nx">episode</span><span class="p">.</span><span class="nx">relativePath</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/S(\d+)E(\d+)/</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">parsedPath</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">episodeSeason</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">parsedPath</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">episodeNumber</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">parsedPath</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">episodeSeason</span> <span class="o">===</span> <span class="nx">affectedSeason</span> <span class="o">&amp;&amp;</span> <span class="nx">episodeNumber</span> <span class="o">===</span> <span class="nx">affectedEpisode</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">targetEpisode</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">targetEpisodeId</span> <span class="o">=</span> <span class="nx">targetEpisode</span><span class="p">.</span><span class="nx">id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Found Episode ID = &#34;</span> <span class="o">+</span> <span class="nx">targetEpisodeId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Delete the target episode file
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">deleteEpisodeUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">sonarrBaseUrl</span><span class="si">}</span><span class="sb">/episodefile/</span><span class="si">${</span><span class="nx">targetEpisodeId</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">deleteEpisodeResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">deleteEpisodeUrl</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;DELETE&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">sonarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">deleteEpisodeResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Episode file successfully deleted in Sonarr.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`Failed to delete episode file in Sonarr: </span><span class="si">${</span><span class="nx">deleteEpisodeResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;No matching episode found in the episode files for the affected season and episode.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Failed to fetch episode files:&#39;</span><span class="p">,</span> <span class="nx">episodeFilesResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;No series found for the provided TVDB ID:&#39;</span><span class="p">,</span> <span class="nx">tvdbId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Failed to fetch series data:&#39;</span><span class="p">,</span> <span class="nx">seriesResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h2 id="18-re-search-in-sonarr">#18 Re-search in Sonarr<a hidden class="anchor" aria-hidden="true" href="#18-re-search-in-sonarr">#</a></h2>
<p>Again, all I do here is the code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">code</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">inputs</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">issueSubject</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">subject</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tvShowNameRegex</span> <span class="o">=</span> <span class="sr">/(.*)\s\((\d{4})\)/</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">match</span> <span class="o">=</span> <span class="nx">tvShowNameRegex</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="nx">issueSubject</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">match</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tvShowName</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">year</span> <span class="o">=</span> <span class="nx">match</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">tvdbId</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">media</span><span class="p">.</span><span class="nx">tvdbId</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`TV Show name: </span><span class="si">${</span><span class="nx">tvShowName</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Year: </span><span class="si">${</span><span class="nx">year</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`TVDB ID: </span><span class="si">${</span><span class="nx">tvdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">sonarrApiKey</span> <span class="o">=</span> <span class="s1">&#39;your-api-key&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">sonarrBaseUrl</span> <span class="o">=</span> <span class="s1">&#39;https://sonarr.example.com/api/v3&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">seriesResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">sonarrBaseUrl</span><span class="si">}</span><span class="sb">/series/lookup?term=tvdb:</span><span class="si">${</span><span class="nx">tvdbId</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;GET&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">sonarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">seriesResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">seriesData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">seriesResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">seriesData</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">seriesId</span> <span class="o">=</span> <span class="nx">seriesData</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">affectedSeason</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">extra</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">item</span> <span class="o">=&gt;</span> <span class="nx">item</span><span class="p">.</span><span class="nx">name</span> <span class="o">===</span> <span class="s1">&#39;Affected Season&#39;</span><span class="p">)</span><span class="o">?</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">affectedEpisode</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">extra</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">item</span> <span class="o">=&gt;</span> <span class="nx">item</span><span class="p">.</span><span class="nx">name</span> <span class="o">===</span> <span class="s1">&#39;Affected Episode&#39;</span><span class="p">)</span><span class="o">?</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">historyResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">sonarrBaseUrl</span><span class="si">}</span><span class="sb">/history/series?seriesId=</span><span class="si">${</span><span class="nx">seriesId</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;GET&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">sonarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">historyResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">historyData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">historyResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">recentEntry</span> <span class="o">=</span> <span class="nx">historyData</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">entry</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">sourceTitleMatch</span> <span class="o">=</span> <span class="sr">/S(\d+)E(\d+)/</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="nx">entry</span><span class="p">.</span><span class="nx">sourceTitle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">sourceTitleMatch</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">sourceSeason</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">sourceTitleMatch</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">sourceEpisode</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">sourceTitleMatch</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">sourceSeason</span> <span class="o">===</span> <span class="nx">affectedSeason</span> <span class="o">&amp;&amp;</span> <span class="nx">sourceEpisode</span> <span class="o">===</span> <span class="nx">affectedEpisode</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">recentEntry</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">episodeId</span> <span class="o">=</span> <span class="nx">recentEntry</span><span class="p">.</span><span class="nx">episodeId</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Found Episode ID = &#34;</span> <span class="o">+</span> <span class="nx">episodeId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Perform the episode search
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">searchPayload</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;EpisodeSearch&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">episodeIds</span><span class="o">:</span> <span class="p">[</span><span class="nx">episodeId</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">searchResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">sonarrBaseUrl</span><span class="si">}</span><span class="sb">/command`</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">sonarrApiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="s1">&#39;application/json&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nx">body</span>: <span class="kt">JSON.stringify</span><span class="p">(</span><span class="nx">searchPayload</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">searchResponse</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Episode search command successfully sent to Sonarr.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`Failed to send episode search command to Sonarr: </span><span class="si">${</span><span class="nx">searchResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;No matching entry found in the series history for the affected episode.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Failed to fetch series history:&#39;</span><span class="p">,</span> <span class="nx">historyResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;No series found for the provided TVDB ID:&#39;</span><span class="p">,</span> <span class="nx">tvdbId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">&#39;Failed to fetch series data:&#39;</span><span class="p">,</span> <span class="nx">seriesResponse</span><span class="p">.</span><span class="nx">statusText</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h2 id="19-delay-for-4-minutes">#19 Delay for 4 Minutes<a hidden class="anchor" aria-hidden="true" href="#19-delay-for-4-minutes">#</a></h2>
<p>Waiting for media to download and transfer.</p>
<h2 id="20-add-commentresolve-issue">#20 Add Comment/Resolve Issue<a hidden class="anchor" aria-hidden="true" href="#20-add-commentresolve-issue">#</a></h2>
<p>Again, all I do here is the code function with 1 input which is the whole body message of the request, this is assigned to inputs.issue in the code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">code</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">inputs</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">issueId</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">.</span><span class="nx">issue</span><span class="p">.</span><span class="nx">issue_id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">apiKey</span> <span class="o">=</span> <span class="s1">&#39;your-api-key&#39;</span><span class="p">;</span> <span class="c1">// Replace with your actual API key
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">baseURL</span> <span class="o">=</span> <span class="s1">&#39;https://jellyseerr.example.com/api/v1&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentApiUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">baseURL</span><span class="si">}</span><span class="sb">/issue/</span><span class="si">${</span><span class="nx">issueId</span><span class="si">}</span><span class="sb">/comment`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">statusApiUrl</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">baseURL</span><span class="si">}</span><span class="sb">/issue/</span><span class="si">${</span><span class="nx">issueId</span><span class="si">}</span><span class="sb">/resolved`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">headers</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;Content-Type&#39;</span><span class="o">:</span> <span class="s1">&#39;application/json&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s1">&#39;X-Api-Key&#39;</span><span class="o">:</span> <span class="nx">apiKey</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentData</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">message</span><span class="o">:</span> <span class="s1">&#39;Your issue has been approved and a new version of the content has been automatically downloaded and updated in Jellyfin. Your issue has been set to Resolved. If you are still experiencing problems, re-open your issue.&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentRequestOptions</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span>: <span class="kt">headers</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">body</span>: <span class="kt">JSON.stringify</span><span class="p">(</span><span class="nx">commentData</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Post comment
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">commentResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">commentApiUrl</span><span class="p">,</span> <span class="nx">commentRequestOptions</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">commentData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">commentResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">commentData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Update status
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">statusRequestOptions</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">method</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">headers</span>: <span class="kt">headers</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">statusResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">statusApiUrl</span><span class="p">,</span> <span class="nx">statusRequestOptions</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">statusData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">statusResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">statusData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h2 id="21-same-as-12">#21 Same as #12<a hidden class="anchor" aria-hidden="true" href="#21-same-as-12">#</a></h2>
<h2 id="conclusion">Conclusion<a hidden class="anchor" aria-hidden="true" href="#conclusion">#</a></h2>
<p>Once all this is done you can publish the flow and try it out!</p>
<p>If you have any feedback you can DM on Reddit. I&rsquo;d love to see how you have edited this automation to your exact needs.</p>
<p>Now the hard part, getting your users to actually report the issues in Jellyseerr and not reach out to you!</p>
</div>
<footer class="post-footer">
<ul class="post-tags">
</ul>
<nav class="paginav">
<a class="prev" href="http://localhost:1313/post/proton-mail-authentik-social-login-setup/">
<span class="title">« Prev</span>
<br>
<span>Proton Mail - SimpleLogin authentik Social Login Setup</span>
</a>
<a class="next" href="http://localhost:1313/post/how-to-authenticate-guacamole-authentik-nginxproxymanager/">
<span class="title">Next »</span>
<br>
<span>How to authenticate Guacamole via authentik with Cloudflare and Nginx Proxy Manager</span>
</a>
</nav>
</footer>
</article>
</main>
<footer class="footer">
<span>&copy; 2024 <a href="http://localhost:1313/">Mafyuh&#39;s Blog</a></span>
<span>
Powered by
<a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
<a href="https://github.com/adityatelange/hugo-PaperMod/" rel="noopener" target="_blank">PaperMod</a>
</span>
</footer>
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
<path d="M12 6H0l6-6z" />
</svg>
</a>
<script>
let menu = document.getElementById('menu')
if (menu) {
menu.scrollLeft = localStorage.getItem("menu-scroll-position");
menu.onscroll = function () {
localStorage.setItem("menu-scroll-position", menu.scrollLeft);
}
}
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener("click", function (e) {
e.preventDefault();
var id = this.getAttribute("href").substr(1);
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
behavior: "smooth"
});
} else {
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView();
}
if (id === "top") {
history.replaceState(null, null, " ");
} else {
history.pushState(null, null, `#${id}`);
}
});
});
</script>
<script>
var mybutton = document.getElementById("top-link");
window.onscroll = function () {
if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
mybutton.style.visibility = "visible";
mybutton.style.opacity = "1";
} else {
mybutton.style.visibility = "hidden";
mybutton.style.opacity = "0";
}
};
</script>
<script>
document.getElementById("theme-toggle").addEventListener("click", () => {
if (document.body.className.includes("dark")) {
document.body.classList.remove('dark');
localStorage.setItem("pref-theme", 'light');
} else {
document.body.classList.add('dark');
localStorage.setItem("pref-theme", 'dark');
}
})
</script>
<script>
document.querySelectorAll('pre > code').forEach((codeblock) => {
const container = codeblock.parentNode.parentNode;
const copybutton = document.createElement('button');
copybutton.classList.add('copy-code');
copybutton.innerHTML = 'copy';
function copyingDone() {
copybutton.innerHTML = 'copied!';
setTimeout(() => {
copybutton.innerHTML = 'copy';
}, 2000);
}
copybutton.addEventListener('click', (cb) => {
if ('clipboard' in navigator) {
navigator.clipboard.writeText(codeblock.textContent);
copyingDone();
return;
}
const range = document.createRange();
range.selectNodeContents(codeblock);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
try {
document.execCommand('copy');
copyingDone();
} catch (e) { };
selection.removeRange(range);
});
if (container.classList.contains("highlight")) {
container.appendChild(copybutton);
} else if (container.parentNode.firstChild == container) {
} else if (codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName == "TABLE") {
codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(copybutton);
} else {
codeblock.parentNode.appendChild(copybutton);
}
});
</script>
</body>
</html>