11const express = require ( 'express' ) ;
22const axios = require ( 'axios' ) ;
3+ const path = require ( 'path' ) ;
34const app = express ( ) ;
45
6+
7+
8+ /*==================================== SETTINGS =====================================*/
9+
10+ // RADARR SERVER SETTINGS
11+ const DEFAULT_RADARR_SERVER_IP = '127.0.0.1' // Radarr server IP/URL
12+ const DEFAULT_RADARR_SERVER_PORT = 7878 // Radarr server port
13+ const DEFAULT_RADARR_API_KEY = '1234abcdxxxxxxxxxxxxxxxxxxxxxxxx' // Radarr API Key
14+
15+ // SONARR SERVER SETTINGS
16+ const DEFAULT_SONARR_SERVER_IP = '127.0.0.1' // Sonarr server IP/URL
17+ const DEFAULT_SONARR_SERVER_PORT = 8989 // Sonarr server port
18+ const DEFAULT_SONARR_API_KEY = '1234abcdxxxxxxxxxxxxxxxxxxxxxxxx' // Sonarr API Key
19+
20+ // DOWNLOAD VIEWARR SETTINGS
21+ const DEFAULT_SERVER_PORT = 8888 // Port to view download status page
22+ const DEFAULT_ENABLE_DRIVE_STATUS = "FALSE" // True or False - Enable media drive(s) status.
23+
524/*==================================== SETTINGS =====================================*/
25+
26+
27+
28+
29+ // Don't edit below this line
30+
631const settings = {
732 movies : {
8- apiServerIP : process . env . RADARR_SERVER_IP || '127.0.0.1' , // Radarr server IP (Docker: from env var, Native: default to localhost)
9- apiServerPort : process . env . RADARR_SERVER_PORT || 7878 , // Radarr server port
10- apiKey : process . env . RADARR_API_KEY || '1234abcdxxxxxxxxxxxxxxxxxxxxxxxx' , // Radarr API Key
33+ apiServerIP : process . env . RADARR_SERVER_IP || DEFAULT_RADARR_SERVER_IP ,
34+ apiServerPort : process . env . RADARR_SERVER_PORT || DEFAULT_RADARR_SERVER_PORT ,
35+ apiKey : process . env . RADARR_API_KEY || DEFAULT_RADARR_API_KEY ,
1136 } ,
1237 tvshows : {
13- apiServerIP : process . env . SONARR_SERVER_IP || '127.0.0.1' , // Sonarr server IP (Docker: from env var, Native: default to localhost)
14- apiServerPort : process . env . SONARR_SERVER_PORT || 8989 , // Sonarr server port
15- apiKey : process . env . SONARR_API_KEY || '1234abcdxxxxxxxxxxxxxxxxxxxxxxxx' , // Sonarr API Key
38+ apiServerIP : process . env . SONARR_SERVER_IP || DEFAULT_SONARR_SERVER_IP ,
39+ apiServerPort : process . env . SONARR_SERVER_PORT || DEFAULT_SONARR_SERVER_PORT ,
40+ apiKey : process . env . SONARR_API_KEY || DEFAULT_SONARR_API_KEY ,
1641 } ,
17- nodeServerPort : process . env . NODE_SERVER_PORT || 8888 , // Port to view download page
42+ nodeServerPort : process . env . NODE_SERVER_PORT || DEFAULT_SERVER_PORT ,
43+ enableDriveStatus : ( process . env . ENABLE_DRIVE_STATUS || DEFAULT_ENABLE_DRIVE_STATUS ) . toLowerCase ( ) ,
1844} ;
19- /*==================================== SETTINGS =====================================*/
20-
2145
2246// Middleware to enable CORS
2347app . use ( ( req , res , next ) => {
@@ -29,6 +53,107 @@ app.use((req, res, next) => {
2953// Serve static files from the "public" directory
3054app . use ( express . static ( 'public' ) ) ;
3155
56+ // Function to fetch root folders from Radarr or Sonarr
57+ async function fetchRootFolders ( apiBaseUrl , apiKey ) {
58+ try {
59+ const response = await axios . get ( `${ apiBaseUrl } /rootfolder` , {
60+ headers : { 'X-Api-Key' : apiKey } ,
61+ } ) ;
62+ return response . data . map ( folder => folder . path ) ; // Extract folder paths
63+ } catch ( error ) {
64+ console . error ( 'Error fetching root folders:' , error . message ) ;
65+ return [ ] ;
66+ }
67+ }
68+
69+ // Function to fetch disk space information from Radarr or Sonarr APIs
70+ async function fetchDiskSpace ( apiBaseUrl , apiKey ) {
71+ try {
72+ const response = await axios . get ( `${ apiBaseUrl } /diskspace?apikey=${ apiKey } ` ) ;
73+ return response . data ; // Return the API response data
74+ } catch ( error ) {
75+ console . error ( `Error fetching disk space from API: ${ error . message } ` ) ;
76+ return null ; // Return null on error
77+ }
78+ }
79+
80+ app . get ( '/api/drive-space' , async ( req , res ) => {
81+ if ( ! [ "true" , "yes" ] . includes ( settings . enableDriveStatus ) ) {
82+ return res . status ( 403 ) . send ( 'Drive status is disabled.' ) ;
83+ }
84+
85+ try {
86+ // Fetch root folders for Movies and TV Shows
87+ const radarrRootFolders = await fetchRootFolders (
88+ `http://${ settings . movies . apiServerIP } :${ settings . movies . apiServerPort } /api/v3` ,
89+ settings . movies . apiKey
90+ ) ;
91+ const sonarrRootFolders = await fetchRootFolders (
92+ `http://${ settings . tvshows . apiServerIP } :${ settings . tvshows . apiServerPort } /api/v3` ,
93+ settings . tvshows . apiKey
94+ ) ;
95+
96+ const allFolders = [ ...new Set ( [ ...radarrRootFolders , ...sonarrRootFolders ] ) ] ;
97+
98+ // Fetch disk space data from Radarr API first, fallback to Sonarr API if Radarr fails
99+ let diskSpaces = await fetchDiskSpace (
100+ `http://${ settings . movies . apiServerIP } :${ settings . movies . apiServerPort } /api/v3` ,
101+ settings . movies . apiKey
102+ ) ;
103+
104+ if ( ! diskSpaces ) {
105+ diskSpaces = await fetchDiskSpace (
106+ `http://${ settings . tvshows . apiServerIP } :${ settings . tvshows . apiServerPort } /api/v3` ,
107+ settings . tvshows . apiKey
108+ ) ;
109+ }
110+
111+ if ( ! diskSpaces ) {
112+ return res . status ( 500 ) . send ( 'Failed to fetch disk space data' ) ;
113+ }
114+
115+ // Filter disk spaces to only include those with a matching root folder
116+ const filteredDiskSpaces = diskSpaces . filter ( disk =>
117+ allFolders . some ( folder => folder . startsWith ( disk . path ) )
118+ ) ;
119+
120+ // Deduplicate results based on the root drive (e.g., "F:\\")
121+ const uniqueDriveSpaces = Object . values (
122+ filteredDiskSpaces . reduce ( ( acc , drive ) => {
123+ const rootDrive = drive . path . split ( ':' ) [ 0 ] + ':\\' ; // Extract root drive
124+ if ( ! acc [ rootDrive ] ) {
125+ acc [ rootDrive ] = {
126+ path : rootDrive ,
127+ label : drive . label ,
128+ freeSpace : drive . freeSpace ,
129+ totalSpace : drive . totalSpace ,
130+ } ;
131+ } else {
132+ // If already exists, sum up the free and total space
133+ acc [ rootDrive ] . freeSpace += drive . freeSpace ;
134+ acc [ rootDrive ] . totalSpace += drive . totalSpace ;
135+ }
136+ return acc ;
137+ } , { } )
138+ ) ;
139+
140+ // Format the response
141+ const formattedDriveSpaces = uniqueDriveSpaces . map ( drive => ( {
142+ path : drive . path ,
143+ label : drive . label ,
144+ freeSpace : `${ ( drive . freeSpace / 1e9 ) . toFixed ( 2 ) } GB` ,
145+ totalSpace : `${ ( drive . totalSpace / 1e9 ) . toFixed ( 2 ) } GB` ,
146+ usedSpace : `${ ( ( drive . totalSpace - drive . freeSpace ) / 1e9 ) . toFixed ( 2 ) } GB` ,
147+ percentageUsed : Math . round ( ( ( drive . totalSpace - drive . freeSpace ) / drive . totalSpace ) * 100 )
148+ } ) ) ;
149+
150+ res . json ( formattedDriveSpaces ) ; // Return the formatted drive space data
151+ } catch ( error ) {
152+ console . error ( 'Error in /api/drive-space:' , error . message ) ;
153+ res . status ( 500 ) . send ( 'Failed to fetch drive space data' ) ;
154+ }
155+ } ) ;
156+
32157// Function to fetch data from the external API
33158async function fetchData ( type ) {
34159 try {
@@ -91,5 +216,6 @@ app.get('/api/queue/downloading', async (req, res) => {
91216
92217// Start the server
93218app . listen ( settings . nodeServerPort , ( ) => {
219+ console . log ( `Drive status enabled: ${ settings . enableDriveStatus } ` ) ;
94220 console . log ( `Server running at http://localhost:${ settings . nodeServerPort } ` ) ;
95221} ) ;
0 commit comments