import GithubAPI from '@server/api/github' ;
import PushoverAPI from '@server/api/pushover' ;
import TheMovieDb from '@server/api/themoviedb' ;
import type {
TmdbMovieResult ,
TmdbTvResult ,
} from '@server/api/themoviedb/interfaces' ;
import { getRepository } from '@server/datasource' ;
import DiscoverSlider from '@server/entity/DiscoverSlider' ;
import type { StatusResponse } from '@server/interfaces/api/settingsInterfaces' ;
import { Permission } from '@server/lib/permissions' ;
import { getSettings } from '@server/lib/settings' ;
import logger from '@server/logger' ;
import { checkUser , isAuthenticated } from '@server/middleware/auth' ;
import { mapWatchProviderDetails } from '@server/models/common' ;
import { mapProductionCompany } from '@server/models/Movie' ;
import { mapNetwork } from '@server/models/Tv' ;
import settingsRoutes from '@server/routes/settings' ;
import { appDataPath , appDataStatus } from '@server/utils/appDataVolume' ;
import { getAppVersion , getCommitTag } from '@server/utils/appVersion' ;
import restartFlag from '@server/utils/restartFlag' ;
import { isPerson } from '@server/utils/typeHelpers' ;
import { Router } from 'express' ;
import authRoutes from './auth' ;
import collectionRoutes from './collection' ;
import discoverRoutes , { createTmdbWithRegionLanguage } from './discover' ;
import issueRoutes from './issue' ;
import issueCommentRoutes from './issueComment' ;
import mediaRoutes from './media' ;
import movieRoutes from './movie' ;
import personRoutes from './person' ;
import requestRoutes from './request' ;
import searchRoutes from './search' ;
import serviceRoutes from './service' ;
import tvRoutes from './tv' ;
import user from './user' ;
const router = Router ( ) ;
router . use ( checkUser ) ;
router . get < unknown , StatusResponse > ( '/status' , async ( req , res ) = > {
const githubApi = new GithubAPI ( ) ;
const currentVersion = getAppVersion ( ) ;
const commitTag = getCommitTag ( ) ;
let updateAvailable = false ;
let commitsBehind = 0 ;
if ( currentVersion . startsWith ( 'develop-' ) && commitTag !== 'local' ) {
const commits = await githubApi . getOverseerrCommits ( ) ;
if ( commits . length ) {
const filteredCommits = commits . filter (
( commit ) = > ! commit . commit . message . includes ( '[skip ci]' )
) ;
if ( filteredCommits [ 0 ] . sha !== commitTag ) {
updateAvailable = true ;
}
const commitIndex = filteredCommits . findIndex (
( commit ) = > commit . sha === commitTag
) ;
if ( updateAvailable ) {
commitsBehind = commitIndex ;
}
}
} else if ( commitTag !== 'local' ) {
const releases = await githubApi . getOverseerrReleases ( ) ;
if ( releases . length ) {
const latestVersion = releases [ 0 ] ;
if ( ! latestVersion . name . includes ( currentVersion ) ) {
updateAvailable = true ;
}
}
}
return res . status ( 200 ) . json ( {
version : getAppVersion ( ) ,
commitTag : getCommitTag ( ) ,
updateAvailable ,
commitsBehind ,
restartRequired : restartFlag.isSet ( ) ,
} ) ;
} ) ;
router . get ( '/status/appdata' , ( _req , res ) = > {
return res . status ( 200 ) . json ( {
appData : appDataStatus ( ) ,
appDataPath : appDataPath ( ) ,
} ) ;
} ) ;
router . use ( '/user' , isAuthenticated ( ) , user ) ;
router . get ( '/settings/public' , async ( req , res ) = > {
const settings = getSettings ( ) ;
if ( ! ( req . user ? . settings ? . notificationTypes . webpush ? ? true ) ) {
return res
. status ( 200 )
. json ( { . . . settings . fullPublicSettings , enablePushRegistration : false } ) ;
} else {
return res . status ( 200 ) . json ( settings . fullPublicSettings ) ;
}
} ) ;
router . get ( '/settings/discover' , isAuthenticated ( ) , async ( _req , res ) = > {
const sliderRepository = getRepository ( DiscoverSlider ) ;
const sliders = await sliderRepository . find ( { order : { order : 'ASC' } } ) ;
return res . json ( sliders ) ;
} ) ;
router . get (
'/settings/notifications/pushover/sounds' ,
isAuthenticated ( ) ,
async ( req , res , next ) = > {
const pushoverApi = new PushoverAPI ( ) ;
try {
if ( ! req . query . token ) {
throw new Error ( 'Pushover application token missing from request' ) ;
}
const sounds = await pushoverApi . getSounds ( req . query . token as string ) ;
res . status ( 200 ) . json ( sounds ) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving Pushover sounds' , {
label : 'API' ,
errorMessage : e.message ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve Pushover sounds.' ,
} ) ;
}
}
) ;
router . use ( '/settings' , isAuthenticated ( Permission . ADMIN ) , settingsRoutes ) ;
router . use ( '/search' , isAuthenticated ( ) , searchRoutes ) ;
router . use ( '/discover' , isAuthenticated ( ) , discoverRoutes ) ;
router . use ( '/request' , isAuthenticated ( ) , requestRoutes ) ;
router . use ( '/movie' , isAuthenticated ( ) , movieRoutes ) ;
router . use ( '/tv' , isAuthenticated ( ) , tvRoutes ) ;
router . use ( '/media' , isAuthenticated ( ) , mediaRoutes ) ;
router . use ( '/person' , isAuthenticated ( ) , personRoutes ) ;
router . use ( '/collection' , isAuthenticated ( ) , collectionRoutes ) ;
router . use ( '/service' , isAuthenticated ( ) , serviceRoutes ) ;
router . use ( '/issue' , isAuthenticated ( ) , issueRoutes ) ;
router . use ( '/issueComment' , isAuthenticated ( ) , issueCommentRoutes ) ;
router . use ( '/auth' , authRoutes ) ;
router . get ( '/regions' , isAuthenticated ( ) , async ( req , res , next ) = > {
const tmdb = new TheMovieDb ( ) ;
try {
const regions = await tmdb . getRegions ( ) ;
return res . status ( 200 ) . json ( regions ) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving regions' , {
label : 'API' ,
errorMessage : e.message ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve regions.' ,
} ) ;
}
} ) ;
router . get ( '/languages' , isAuthenticated ( ) , async ( req , res , next ) = > {
const tmdb = new TheMovieDb ( ) ;
try {
const languages = await tmdb . getLanguages ( ) ;
return res . status ( 200 ) . json ( languages ) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving languages' , {
label : 'API' ,
errorMessage : e.message ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve languages.' ,
} ) ;
}
} ) ;
router . get < { id : string } > ( '/studio/:id' , async ( req , res , next ) = > {
const tmdb = new TheMovieDb ( ) ;
try {
const studio = await tmdb . getStudio ( Number ( req . params . id ) ) ;
return res . status ( 200 ) . json ( mapProductionCompany ( studio ) ) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving studio' , {
label : 'API' ,
errorMessage : e.message ,
studioId : req.params.id ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve studio.' ,
} ) ;
}
} ) ;
router . get < { id : string } > ( '/network/:id' , async ( req , res , next ) = > {
const tmdb = new TheMovieDb ( ) ;
try {
const network = await tmdb . getNetwork ( Number ( req . params . id ) ) ;
return res . status ( 200 ) . json ( mapNetwork ( network ) ) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving network' , {
label : 'API' ,
errorMessage : e.message ,
networkId : req.params.id ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve network.' ,
} ) ;
}
} ) ;
router . get ( '/genres/movie' , isAuthenticated ( ) , async ( req , res , next ) = > {
const tmdb = new TheMovieDb ( ) ;
try {
const genres = await tmdb . getMovieGenres ( {
language : ( req . query . language as string ) ? ? req . locale ,
} ) ;
return res . status ( 200 ) . json ( genres ) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving movie genres' , {
label : 'API' ,
errorMessage : e.message ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve movie genres.' ,
} ) ;
}
} ) ;
router . get ( '/genres/tv' , isAuthenticated ( ) , async ( req , res , next ) = > {
const tmdb = new TheMovieDb ( ) ;
try {
const genres = await tmdb . getTvGenres ( {
language : ( req . query . language as string ) ? ? req . locale ,
} ) ;
return res . status ( 200 ) . json ( genres ) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving series genres' , {
label : 'API' ,
errorMessage : e.message ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve series genres.' ,
} ) ;
}
} ) ;
router . get ( '/backdrops' , async ( req , res , next ) = > {
const tmdb = createTmdbWithRegionLanguage ( ) ;
try {
const data = (
await tmdb . getAllTrending ( {
page : 1 ,
timeWindow : 'week' ,
} )
) . results . filter ( ( result ) = > ! isPerson ( result ) ) as (
| TmdbMovieResult
| TmdbTvResult
) [ ] ;
return res
. status ( 200 )
. json (
data
. map ( ( result ) = > result . backdrop_path )
. filter ( ( backdropPath ) = > ! ! backdropPath )
) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving backdrops' , {
label : 'API' ,
errorMessage : e.message ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve backdrops.' ,
} ) ;
}
} ) ;
router . get ( '/keyword/:keywordId' , async ( req , res , next ) = > {
const tmdb = createTmdbWithRegionLanguage ( ) ;
try {
const result = await tmdb . getKeywordDetails ( {
keywordId : Number ( req . params . keywordId ) ,
} ) ;
return res . status ( 200 ) . json ( result ) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving keyword data' , {
label : 'API' ,
errorMessage : e.message ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve keyword data.' ,
} ) ;
}
} ) ;
router . get ( '/watchproviders/regions' , async ( req , res , next ) = > {
const tmdb = createTmdbWithRegionLanguage ( ) ;
try {
const result = await tmdb . getAvailableWatchProviderRegions ( { } ) ;
return res . status ( 200 ) . json ( result ) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving watch provider regions' , {
label : 'API' ,
errorMessage : e.message ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve watch provider regions.' ,
} ) ;
}
} ) ;
router . get ( '/watchproviders/movies' , async ( req , res , next ) = > {
const tmdb = createTmdbWithRegionLanguage ( ) ;
try {
const result = await tmdb . getMovieWatchProviders ( {
watchRegion : req.query.watchRegion as string ,
} ) ;
return res . status ( 200 ) . json ( mapWatchProviderDetails ( result ) ) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving movie watch providers' , {
label : 'API' ,
errorMessage : e.message ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve movie watch providers.' ,
} ) ;
}
} ) ;
router . get ( '/watchproviders/tv' , async ( req , res , next ) = > {
const tmdb = createTmdbWithRegionLanguage ( ) ;
try {
const result = await tmdb . getTvWatchProviders ( {
watchRegion : req.query.watchRegion as string ,
} ) ;
return res . status ( 200 ) . json ( mapWatchProviderDetails ( result ) ) ;
} catch ( e ) {
logger . debug ( 'Something went wrong retrieving tv watch providers' , {
label : 'API' ,
errorMessage : e.message ,
} ) ;
return next ( {
status : 500 ,
message : 'Unable to retrieve tv watch providers.' ,
} ) ;
}
} ) ;
router . get ( '/' , ( _req , res ) = > {
return res . status ( 200 ) . json ( {
api : 'Overseerr API' ,
version : '1.0' ,
} ) ;
} ) ;
export default router ;