]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - shared/extra-utils/ffprobe.ts
Don't inject untrusted input
[github/Chocobozzz/PeerTube.git] / shared / extra-utils / ffprobe.ts
index 53a3aa0010758bd47f974e79941855668514355e..7efc58a0d944950e5c8c6ae7287d4a62ceb0af7d 100644 (file)
@@ -1,5 +1,6 @@
 import { ffprobe, FfprobeData } from 'fluent-ffmpeg'
-import { VideoFileMetadata } from '@shared/models/videos'
+import { forceNumber } from '@shared/core-utils'
+import { VideoFileMetadata, VideoResolution } from '@shared/models/videos'
 
 /**
  *
@@ -17,10 +18,29 @@ function ffprobePromise (path: string) {
   })
 }
 
+// ---------------------------------------------------------------------------
+// Audio
+// ---------------------------------------------------------------------------
+
+const imageCodecs = new Set([
+  'ansi', 'apng', 'bintext', 'bmp', 'brender_pix', 'dpx', 'exr', 'fits', 'gem', 'gif', 'jpeg2000', 'jpgls', 'mjpeg', 'mjpegb', 'msp2',
+  'pam', 'pbm', 'pcx', 'pfm', 'pgm', 'pgmyuv', 'pgx', 'photocd', 'pictor', 'png', 'ppm', 'psd', 'sgi', 'sunrast', 'svg', 'targa', 'tiff',
+  'txd', 'webp', 'xbin', 'xbm', 'xface', 'xpm', 'xwd'
+])
+
 async function isAudioFile (path: string, existingProbe?: FfprobeData) {
-  const videoStream = await getVideoStreamFromFile(path, existingProbe)
+  const videoStream = await getVideoStream(path, existingProbe)
+  if (!videoStream) return true
+
+  if (imageCodecs.has(videoStream.codec_name)) return true
 
-  return !videoStream
+  return false
+}
+
+async function hasAudioStream (path: string, existingProbe?: FfprobeData) {
+  const { audioStream } = await getAudioStream(path, existingProbe)
+
+  return !!audioStream
 }
 
 async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) {
@@ -36,7 +56,7 @@ async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) {
       return {
         absolutePath: data.format.filename,
         audioStream,
-        bitrate: parseInt(audioStream['bit_rate'] + '', 10)
+        bitrate: forceNumber(audioStream['bit_rate'])
       }
     }
   }
@@ -78,29 +98,34 @@ function getMaxAudioBitrate (type: 'aac' | 'mp3' | string, bitrate: number) {
   }
 }
 
-async function getVideoStreamSize (path: string, existingProbe?: FfprobeData): Promise<{ width: number, height: number }> {
-  const videoStream = await getVideoStreamFromFile(path, existingProbe)
-
-  return videoStream === null
-    ? { width: 0, height: 0 }
-    : { width: videoStream.width, height: videoStream.height }
-}
+// ---------------------------------------------------------------------------
+// Video
+// ---------------------------------------------------------------------------
 
-async function getVideoFileResolution (path: string, existingProbe?: FfprobeData) {
-  const size = await getVideoStreamSize(path, existingProbe)
+async function getVideoStreamDimensionsInfo (path: string, existingProbe?: FfprobeData) {
+  const videoStream = await getVideoStream(path, existingProbe)
+  if (!videoStream) {
+    return {
+      width: 0,
+      height: 0,
+      ratio: 0,
+      resolution: VideoResolution.H_NOVIDEO,
+      isPortraitMode: false
+    }
+  }
 
   return {
-    width: size.width,
-    height: size.height,
-    ratio: Math.max(size.height, size.width) / Math.min(size.height, size.width),
-    resolution: Math.min(size.height, size.width),
-    isPortraitMode: size.height > size.width
+    width: videoStream.width,
+    height: videoStream.height,
+    ratio: Math.max(videoStream.height, videoStream.width) / Math.min(videoStream.height, videoStream.width),
+    resolution: Math.min(videoStream.height, videoStream.width),
+    isPortraitMode: videoStream.height > videoStream.width
   }
 }
 
-async function getVideoFileFPS (path: string, existingProbe?: FfprobeData) {
-  const videoStream = await getVideoStreamFromFile(path, existingProbe)
-  if (videoStream === null) return 0
+async function getVideoStreamFPS (path: string, existingProbe?: FfprobeData) {
+  const videoStream = await getVideoStream(path, existingProbe)
+  if (!videoStream) return 0
 
   for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) {
     const valuesText: string = videoStream[key]
@@ -116,19 +141,19 @@ async function getVideoFileFPS (path: string, existingProbe?: FfprobeData) {
   return 0
 }
 
-async function getMetadataFromFile (path: string, existingProbe?: FfprobeData) {
+async function buildFileMetadata (path: string, existingProbe?: FfprobeData) {
   const metadata = existingProbe || await ffprobePromise(path)
 
   return new VideoFileMetadata(metadata)
 }
 
-async function getVideoFileBitrate (path: string, existingProbe?: FfprobeData): Promise<number> {
-  const metadata = await getMetadataFromFile(path, existingProbe)
+async function getVideoStreamBitrate (path: string, existingProbe?: FfprobeData): Promise<number> {
+  const metadata = await buildFileMetadata(path, existingProbe)
 
   let bitrate = metadata.format.bit_rate as number
   if (bitrate && !isNaN(bitrate)) return bitrate
 
-  const videoStream = await getVideoStreamFromFile(path, existingProbe)
+  const videoStream = await getVideoStream(path, existingProbe)
   if (!videoStream) return undefined
 
   bitrate = videoStream?.bit_rate
@@ -137,51 +162,30 @@ async function getVideoFileBitrate (path: string, existingProbe?: FfprobeData):
   return undefined
 }
 
-async function getDurationFromVideoFile (path: string, existingProbe?: FfprobeData) {
-  const metadata = await getMetadataFromFile(path, existingProbe)
+async function getVideoStreamDuration (path: string, existingProbe?: FfprobeData) {
+  const metadata = await buildFileMetadata(path, existingProbe)
 
   return Math.round(metadata.format.duration)
 }
 
-async function getVideoStreamFromFile (path: string, existingProbe?: FfprobeData) {
-  const metadata = await getMetadataFromFile(path, existingProbe)
-
-  return metadata.streams.find(s => s.codec_type === 'video') || null
-}
-
-async function canDoQuickAudioTranscode (path: string, probe?: FfprobeData): Promise<boolean> {
-  const parsedAudio = await getAudioStream(path, probe)
-
-  if (!parsedAudio.audioStream) return true
-
-  if (parsedAudio.audioStream['codec_name'] !== 'aac') return false
-
-  const audioBitrate = parsedAudio.bitrate
-  if (!audioBitrate) return false
-
-  const maxAudioBitrate = getMaxAudioBitrate('aac', audioBitrate)
-  if (maxAudioBitrate !== -1 && audioBitrate > maxAudioBitrate) return false
-
-  const channelLayout = parsedAudio.audioStream['channel_layout']
-  // Causes playback issues with Chrome
-  if (!channelLayout || channelLayout === 'unknown') return false
+async function getVideoStream (path: string, existingProbe?: FfprobeData) {
+  const metadata = await buildFileMetadata(path, existingProbe)
 
-  return true
+  return metadata.streams.find(s => s.codec_type === 'video')
 }
 
 // ---------------------------------------------------------------------------
 
 export {
-  getVideoStreamSize,
-  getVideoFileResolution,
-  getMetadataFromFile,
+  getVideoStreamDimensionsInfo,
+  buildFileMetadata,
   getMaxAudioBitrate,
-  getVideoStreamFromFile,
-  getDurationFromVideoFile,
+  getVideoStream,
+  getVideoStreamDuration,
   getAudioStream,
-  getVideoFileFPS,
+  getVideoStreamFPS,
   isAudioFile,
   ffprobePromise,
-  getVideoFileBitrate,
-  canDoQuickAudioTranscode
+  getVideoStreamBitrate,
+  hasAudioStream
 }