From 99f804d733efe19c0d3549f02d273ed46226a2ff Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Sat, 4 Jul 2026 17:45:43 +0200 Subject: [PATCH] api: generate most of OpenAPI automatically (#5918) enums and structs are now generated automatically. This eliminates some inconsistencies and makes development easier. --- .github/workflows/lint.yml | 16 +- Makefile | 1 + api/openapi.yaml | 3120 +++++++++++---------- internal/apidocsgen/enums.go | 178 ++ internal/apidocsgen/main.go | 129 + internal/apidocsgen/openapi.template.yaml | 1820 ++++++++++++ internal/apidocsgen/structs.go | 525 ++++ internal/defs/api_srt.go | 2 +- internal/linters/go2api/go2api_test.go | 560 ---- scripts/apidocs.mk | 2 + scripts/lint.mk | 14 +- 11 files changed, 4236 insertions(+), 2131 deletions(-) create mode 100644 internal/apidocsgen/enums.go create mode 100644 internal/apidocsgen/main.go create mode 100644 internal/apidocsgen/openapi.template.yaml create mode 100644 internal/apidocsgen/structs.go delete mode 100644 internal/linters/go2api/go2api_test.go create mode 100644 scripts/apidocs.mk diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a013dac1..38dbbc1d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -52,18 +52,6 @@ jobs: - run: make lint-conf - go2api: - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v7 - - - uses: actions/setup-go@v6 - with: - go-version: "1.26" - - - run: make lint-go2api - docslinks: runs-on: ubuntu-24.04 @@ -88,13 +76,13 @@ jobs: - run: make lint-docsorder - api_docs: + apidocs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v7 - - run: make lint-api-docs + - run: make lint-apidocs other: runs-on: ubuntu-24.04 diff --git a/Makefile b/Makefile index d18038a2..da29477c 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ help: @echo " lint run linters" @echo " binaries build binaries for all supported platforms" @echo " dockerhub build and push images to Docker Hub" + @echo " apidocs generate API documentation" @echo "" blank := diff --git a/api/openapi.yaml b/api/openapi.yaml index dbd68c21..47681ba6 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -15,71 +15,42 @@ security: [] components: schemas: - OKStatus: + HLSVariant: type: string - enum: [ok] + enum: [mpegts, fmp4, lowLatency] + + LogDestination: + type: string + enum: [stdout, file, syslog] + + LogLevel: + type: string + enum: [error, warn, info, debug] + + RTSPAuthMethod: + type: string + enum: [basic, digest] + + RTSPTransport: + type: string + enum: [udp, multicast, tcp, automatic] + + PathTrackCodecProps: + oneOf: + - $ref: "#/components/schemas/PathTrackCodecPropsAV1" + - $ref: "#/components/schemas/PathTrackCodecPropsVP9" + - $ref: "#/components/schemas/PathTrackCodecPropsH265" + - $ref: "#/components/schemas/PathTrackCodecPropsH264" + - $ref: "#/components/schemas/PathTrackCodecPropsOpus" + - $ref: "#/components/schemas/PathTrackCodecPropsMPEG4Audio" + - $ref: "#/components/schemas/PathTrackCodecPropsAC3" + - $ref: "#/components/schemas/PathTrackCodecPropsG711" + - $ref: "#/components/schemas/PathTrackCodecPropsLPCM" ErrorStatus: - type: string - enum: [error] - - PathSourceType: type: string enum: - - hlsSource - - redirect - - rpiCameraSource - - rtmpConn - - rtmpsConn - - rtmpSource - - rtspSession - - rtspSource - - rtspsSession - - srtConn - - srtSource - - mpegtsSource - - rtpSource - - webRTCSession - - webRTCSource - - PathReaderType: - type: string - enum: - - hlsSession - - rtmpConn - - rtmpsConn - - rtspConn - - rtspSession - - rtspsConn - - rtspsSession - - srtConn - - webRTCSession - - PathTrackCodec: - type: string - enum: - - AV1 - - VP9 - - VP8 - - H265 - - H264 - - MPEG-4 Video - - MPEG-1/2 Video - - M-JPEG - - Opus - - Vorbis - - MPEG-4 Audio - - MPEG-4 Audio LATM - - MPEG-1/2 Audio - - AC3 - - Speex - - G726 - - G722 - - G711 - - LPCM - - MPEG-TS - - KLV - - Generic + - error AlwaysAvailableTrackCodec: type: string @@ -105,97 +76,162 @@ components: AuthMethod: type: string - enum: [internal, http, jwt] + enum: + - internal + - http + - jwt Encryption: type: string - enum: [no, optional, strict] - - HLSVariant: - type: string - enum: [mpegts, fmp4, lowLatency] - - LogDestination: - type: string - enum: [stdout, file, syslog] - - LogLevel: - type: string - enum: [error, warn, info, debug] - - RecordFormat: - type: string - enum: [fmp4, mpegts] - - RTSPAuthMethod: - type: string - enum: [basic, digest] - - RTSPRangeType: - type: string - enum: ["", clock, npt, smpte] - - RTSPTransport: - type: string - enum: [udp, multicast, tcp, automatic] - - RTMPConnState: - type: string - enum: [idle, read, publish] - - RTSPSessionState: - type: string - enum: [idle, read, publish] - - SRTConnState: - type: string - enum: [idle, read, publish] - - WebRTCSessionState: - type: string - enum: [read, publish] + enum: + - "no" + - optional + - strict MoQSessionState: type: string - enum: [idle, read, publish] + enum: + - idle + - read + - publish - OK: + OKStatus: + type: string + enum: + - ok + + PathReaderType: + type: string + enum: + - hlsSession + - rtmpConn + - rtmpsConn + - rtspConn + - rtspSession + - rtspsConn + - rtspsSession + - srtConn + - webRTCSession + - moqSession + - hidden + + PathSourceType: + type: string + enum: + - hlsSource + - redirect + - rpiCameraSource + - rtmpConn + - rtmpsConn + - rtmpSource + - rtspSession + - rtspSource + - rtspsSession + - srtConn + - srtSource + - mpegtsSource + - rtpSource + - webRTCSession + - webRTCSource + - moqSession + + PathTrackCodec: + type: string + enum: + - AV1 + - VP9 + - VP8 + - H265 + - H264 + - MPEG-4 Video + - MPEG-1/2 Video + - M-JPEG + - Opus + - FLAC + - Vorbis + - MPEG-4 Audio + - MPEG-4 Audio LATM + - MPEG-1/2 Audio + - AC3 + - Speex + - G726 + - G722 + - G711 + - LPCM + - MPEG-TS + - KLV + - Generic + + RTMPConnState: + type: string + enum: + - idle + - read + - publish + + RTSPRangeType: + type: string + enum: + - "" + - clock + - npt + - smpte + + RTSPSessionState: + type: string + enum: + - idle + - read + - publish + + RecordFormat: + type: string + enum: + - fmp4 + - mpegts + + SRTConnState: + type: string + enum: + - idle + - read + - publish + + WebRTCSessionState: + type: string + enum: + - read + - publish + + AlwaysAvailableTrack: type: object properties: - status: - $ref: "#/components/schemas/OKStatus" - - Error: - type: object - properties: - status: - $ref: "#/components/schemas/ErrorStatus" - error: - type: string - - Info: - type: object - properties: - version: - type: string - started: - type: string + channelCount: + type: integer + format: int64 + codec: + $ref: "#/components/schemas/AlwaysAvailableTrackCodec" + muLaw: + type: boolean + sampleRate: + type: integer + format: int64 AuthInternalUser: type: object properties: - user: - type: string - pass: - type: string ips: type: array items: type: string + pass: + type: string permissions: type: array items: $ref: "#/components/schemas/AuthInternalUserPermission" + user: + type: string AuthInternalUserPermission: type: object @@ -205,98 +241,22 @@ components: path: type: string + Error: + type: object + properties: + error: + type: string + status: + $ref: "#/components/schemas/ErrorStatus" + GlobalConf: type: object properties: - # General - logLevel: - $ref: "#/components/schemas/LogLevel" - logDestinations: - type: array - items: - $ref: "#/components/schemas/LogDestination" - logStructured: - type: boolean - logFile: - type: string - sysLogPrefix: - type: string - dumpPackets: - type: boolean - readTimeout: - type: string - writeTimeout: - type: string - readBufferCount: - type: integer - format: int64 - nullable: true - deprecated: true - writeQueueSize: - type: integer - format: int64 - udpMaxPayloadSize: - type: integer - format: int64 - udpReadBufferSize: - type: integer - format: uint64 - runOnConnect: - type: string - runOnConnectRestart: - type: boolean - runOnDisconnect: - type: string - - # Authentication - authMethod: - $ref: "#/components/schemas/AuthMethod" - authInternalUsers: - type: array - items: - $ref: "#/components/schemas/AuthInternalUser" - authHTTPAddress: - type: string - externalAuthenticationURL: - type: string - nullable: true - deprecated: true - authHTTPFingerprint: - type: string - authHTTPExclude: - type: array - items: - $ref: "#/components/schemas/AuthInternalUserPermission" - authJWTJWKS: - type: string - authJWTJWKSFingerprint: - type: string - authJWTClaimKey: - type: string - authJWTInHTTPQuery: - type: boolean - nullable: true - deprecated: true - authJWTIssuer: - type: string - authJWTAudience: - type: string - authJWTExclude: - type: array - items: - $ref: "#/components/schemas/AuthInternalUserPermission" - - # Control API api: type: boolean + description: Control API apiAddress: type: string - apiEncryption: - type: boolean - apiServerKey: - type: string - apiServerCert: - type: string apiAllowOrigin: type: string nullable: true @@ -305,197 +265,72 @@ components: type: array items: type: string + apiEncryption: + type: boolean + apiServerCert: + type: string + apiServerKey: + type: string apiTrustedProxies: type: array items: type: string - - # Metrics - metrics: - type: boolean - metricsAddress: + authHTTPAddress: type: string - metricsEncryption: - type: boolean - metricsServerKey: - type: string - metricsServerCert: - type: string - metricsAllowOrigin: - type: string - nullable: true - deprecated: true - metricsAllowOrigins: + authHTTPExclude: type: array items: - type: string - metricsTrustedProxies: + $ref: "#/components/schemas/AuthInternalUserPermission" + authHTTPFingerprint: + type: string + authInternalUsers: type: array items: - type: string - - # PPROF - pprof: - type: boolean - pprofAddress: + $ref: "#/components/schemas/AuthInternalUser" + authJWTAudience: type: string - pprofEncryption: - type: boolean - pprofServerKey: + authJWTClaimKey: type: string - pprofServerCert: - type: string - pprofAllowOrigin: - type: string - nullable: true - deprecated: true - pprofAllowOrigins: + authJWTExclude: type: array items: - type: string - pprofTrustedProxies: - type: array - items: - type: string - - # Playback server - playback: - type: boolean - playbackAddress: - type: string - playbackEncryption: - type: boolean - playbackServerKey: - type: string - playbackServerCert: - type: string - playbackAllowOrigin: - type: string - nullable: true - deprecated: true - playbackAllowOrigins: - type: array - items: - type: string - playbackTrustedProxies: - type: array - items: - type: string - - # RTSP server - rtsp: - type: boolean - rtspDisable: + $ref: "#/components/schemas/AuthInternalUserPermission" + authJWTInHTTPQuery: type: boolean nullable: true deprecated: true - protocols: - type: array - nullable: true - items: - type: string - enum: [udp, multicast, tcp] - deprecated: true - rtspTransports: - type: array - items: - type: string - enum: [udp, multicast, tcp] - encryption: + authJWTIssuer: type: string - nullable: true - deprecated: true - allOf: - - $ref: "#/components/schemas/Encryption" - rtspEncryption: - $ref: "#/components/schemas/Encryption" - rtspAddress: + authJWTJWKS: type: string - rtspsAddress: - type: string - rtpAddress: - type: string - rtcpAddress: - type: string - multicastIPRange: - type: string - multicastRTPPort: - type: integer - format: int64 - multicastRTCPPort: - type: integer - format: int64 - srtpAddress: - type: string - srtcpAddress: - type: string - multicastSRTPPort: - type: integer - format: int64 - multicastSRTCPPort: - type: integer - format: int64 - rtspServerKey: - type: string - rtspServerCert: + authJWTJWKSFingerprint: type: string + authMethod: + $ref: "#/components/schemas/AuthMethod" + description: Authentication authMethods: type: array nullable: true + deprecated: true items: $ref: "#/components/schemas/RTSPAuthMethod" - deprecated: true - rtspAuthMethods: - type: array - items: - $ref: "#/components/schemas/RTSPAuthMethod" - rtspTrustedProxies: - type: array - items: - type: string - rtspUDPReadBufferSize: - type: integer - format: uint64 + dumpPackets: + type: boolean + encryption: + type: string + allOf: + - $ref: "#/components/schemas/Encryption" nullable: true deprecated: true - - # RTMP server - rtmp: - type: boolean - rtmpDisable: - type: boolean + externalAuthenticationURL: + type: string nullable: true deprecated: true - rtmpEncryption: - $ref: "#/components/schemas/Encryption" - rtmpAddress: - type: string - rtmpsAddress: - type: string - rtmpServerKey: - type: string - rtmpServerCert: - type: string - rtmpTrustedProxies: - type: array - items: - type: string - - # HLS server hls: type: boolean - hlsDisable: - type: boolean - nullable: true - deprecated: true + description: HLS server hlsAddress: type: string - hlsEncryption: - type: boolean - hlsServerKey: - type: string - hlsServerCert: - type: string hlsAllowOrigin: type: string nullable: true @@ -504,44 +339,298 @@ components: type: array items: type: string - hlsTrustedProxies: - type: array - items: - type: string hlsAlwaysRemux: type: boolean - hlsVariant: - $ref: "#/components/schemas/HLSVariant" + hlsCDNSecret: + type: string + hlsDirectory: + type: string + hlsDisable: + type: boolean + nullable: true + deprecated: true + hlsEncryption: + type: boolean + hlsMuxerCloseAfter: + type: string + hlsPartDuration: + type: string hlsSegmentCount: type: integer format: int64 hlsSegmentDuration: type: string - hlsPartDuration: - type: string hlsSegmentMaxSize: type: string - hlsDirectory: + hlsServerCert: type: string - hlsMuxerCloseAfter: + hlsServerKey: type: string - hlsCDNSecret: + hlsTrustedProxies: + type: array + items: + type: string + hlsVariant: + $ref: "#/components/schemas/HLSVariant" + logDestinations: + type: array + items: + $ref: "#/components/schemas/LogDestination" + logFile: type: string - - # WebRTC server - webrtc: + logLevel: + $ref: "#/components/schemas/LogLevel" + description: General + logStructured: type: boolean - webrtcDisable: + metrics: + type: boolean + description: Metrics + metricsAddress: + type: string + metricsAllowOrigin: + type: string + nullable: true + deprecated: true + metricsAllowOrigins: + type: array + items: + type: string + metricsEncryption: + type: boolean + metricsServerCert: + type: string + metricsServerKey: + type: string + metricsTrustedProxies: + type: array + items: + type: string + moq: + type: boolean + description: MoQ server + moqAllowOrigins: + type: array + items: + type: string + moqHTTP2Address: + type: string + moqHTTP3Address: + type: string + moqHTTPS2Address: + type: string + nullable: true + deprecated: true + moqHTTPS3Address: + type: string + nullable: true + deprecated: true + moqServerCert: + type: string + moqServerKey: + type: string + moqTrustedProxies: + type: array + items: + type: string + multicastIPRange: + type: string + multicastRTCPPort: + type: integer + format: int64 + multicastRTPPort: + type: integer + format: int64 + multicastSRTCPPort: + type: integer + format: int64 + multicastSRTPPort: + type: integer + format: int64 + playback: + type: boolean + description: Playback + playbackAddress: + type: string + playbackAllowOrigin: + type: string + nullable: true + deprecated: true + playbackAllowOrigins: + type: array + items: + type: string + playbackEncryption: + type: boolean + playbackServerCert: + type: string + playbackServerKey: + type: string + playbackTrustedProxies: + type: array + items: + type: string + pprof: + type: boolean + description: PPROF + pprofAddress: + type: string + pprofAllowOrigin: + type: string + nullable: true + deprecated: true + pprofAllowOrigins: + type: array + items: + type: string + pprofEncryption: + type: boolean + pprofServerCert: + type: string + pprofServerKey: + type: string + pprofTrustedProxies: + type: array + items: + type: string + protocols: + type: array + nullable: true + deprecated: true + items: + type: string + enum: + - udp + - multicast + - tcp + readBufferCount: + type: integer + format: int64 + nullable: true + deprecated: true + readTimeout: + type: string + record: + type: boolean + description: Record (deprecated) + nullable: true + deprecated: true + recordDeleteAfter: + type: string + nullable: true + deprecated: true + recordFormat: + type: string + allOf: + - $ref: "#/components/schemas/RecordFormat" + nullable: true + deprecated: true + recordPartDuration: + type: string + nullable: true + deprecated: true + recordPath: + type: string + nullable: true + deprecated: true + recordSegmentDuration: + type: string + nullable: true + deprecated: true + rtcpAddress: + type: string + rtmp: + type: boolean + description: RTMP server + rtmpAddress: + type: string + rtmpDisable: type: boolean nullable: true deprecated: true - webrtcAddress: + rtmpEncryption: + $ref: "#/components/schemas/Encryption" + rtmpServerCert: type: string - webrtcEncryption: + rtmpServerKey: + type: string + rtmpTrustedProxies: + type: array + items: + type: string + rtmpsAddress: + type: string + rtpAddress: + type: string + rtsp: type: boolean - webrtcServerKey: + description: RTSP server + rtspAddress: type: string - webrtcServerCert: + rtspAuthMethods: + type: array + items: + $ref: "#/components/schemas/RTSPAuthMethod" + rtspDisable: + type: boolean + nullable: true + deprecated: true + rtspEncryption: + $ref: "#/components/schemas/Encryption" + rtspServerCert: + type: string + rtspServerKey: + type: string + rtspTransports: + type: array + items: + type: string + enum: + - udp + - multicast + - tcp + rtspTrustedProxies: + type: array + items: + type: string + rtspUDPReadBufferSize: + type: integer + format: uint64 + nullable: true + deprecated: true + rtspsAddress: + type: string + runOnConnect: + type: string + runOnConnectRestart: + type: boolean + runOnDisconnect: + type: string + srt: + type: boolean + description: SRT server + srtAddress: + type: string + srtcpAddress: + type: string + srtpAddress: + type: string + sysLogPrefix: + type: string + udpMaxPayloadSize: + type: integer + format: int64 + udpReadBufferSize: + type: integer + format: uint64 + webrtc: + type: boolean + description: WebRTC server + webrtcAdditionalHosts: + type: array + items: + type: string + webrtcAddress: type: string webrtcAllowOrigin: type: string @@ -551,677 +640,74 @@ components: type: array items: type: string - webrtcTrustedProxies: - type: array - items: - type: string - webrtcLocalUDPAddress: - type: string - webrtcLocalTCPAddress: - type: string - webrtcIPsFromInterfaces: + webrtcDisable: type: boolean - webrtcIPsFromInterfacesList: + nullable: true + deprecated: true + webrtcEncryption: + type: boolean + webrtcHandshakeTimeout: + type: string + webrtcICEHostNAT1To1IPs: type: array + nullable: true + deprecated: true items: type: string - webrtcAdditionalHosts: + webrtcICEServers: type: array + nullable: true + deprecated: true items: type: string webrtcICEServers2: type: array items: $ref: "#/components/schemas/WebRTCICEServer" - webrtcSTUNGatherTimeout: - type: string - webrtcHandshakeTimeout: - type: string - webrtcTrackGatherTimeout: - type: string - webrtcICEUDPMuxAddress: - type: string - nullable: true - deprecated: true webrtcICETCPMuxAddress: type: string nullable: true deprecated: true - webrtcICEHostNAT1To1IPs: - type: array + webrtcICEUDPMuxAddress: + type: string nullable: true - items: - type: string deprecated: true - webrtcICEServers: - type: array - nullable: true - items: - type: string - deprecated: true - - # SRT server - srt: + webrtcIPsFromInterfaces: type: boolean - srtAddress: - type: string - - # MoQ server - moq: - type: boolean - moqHTTP2Address: - type: string - moqHTTP3Address: - type: string - moqServerKey: - type: string - moqServerCert: - type: string - moqAllowOrigins: + webrtcIPsFromInterfacesList: type: array items: type: string - moqTrustedProxies: + webrtcLocalTCPAddress: + type: string + webrtcLocalUDPAddress: + type: string + webrtcSTUNGatherTimeout: + type: string + webrtcServerCert: + type: string + webrtcServerKey: + type: string + webrtcTrackGatherTimeout: + type: string + webrtcTrustedProxies: type: array items: type: string - moqHTTPS2Address: - type: string - nullable: true - deprecated: true - moqHTTPS3Address: - type: string - nullable: true - deprecated: true - - # Record (deprecated) - record: - type: boolean - nullable: true - deprecated: true - recordPath: - type: string - nullable: true - deprecated: true - recordFormat: - type: string - nullable: true - deprecated: true - allOf: - - $ref: "#/components/schemas/RecordFormat" - recordPartDuration: - type: string - nullable: true - deprecated: true - recordSegmentDuration: - type: string - nullable: true - deprecated: true - recordDeleteAfter: - type: string - nullable: true - deprecated: true - - PathConf: - type: object - properties: - name: - type: string - - # General - source: - type: string - sourceFingerprint: - type: string - sourceOnDemand: - type: boolean - sourceOnDemandStartTimeout: - type: string - sourceOnDemandCloseAfter: - type: string - maxReaders: + writeQueueSize: type: integer format: int64 - srtReadPassphrase: + writeTimeout: type: string - fallback: - type: string - nullable: true - deprecated: true - useAbsoluteTimestamp: - type: boolean - - # Always available - alwaysAvailable: - type: boolean - alwaysAvailableTracks: - type: array - items: - $ref: "#/components/schemas/AlwaysAvailableTrack" - alwaysAvailableFile: - type: string - - # Record - record: - type: boolean - playback: - type: boolean - nullable: true - deprecated: true - recordPath: - type: string - recordFormat: - $ref: "#/components/schemas/RecordFormat" - recordPartDuration: - type: string - recordMaxPartSize: - type: string - recordSegmentDuration: - type: string - recordDeleteAfter: - type: string - - # Authentication (deprecated) - publishUser: - type: string - nullable: true - deprecated: true - publishPass: - type: string - nullable: true - deprecated: true - publishIPs: - type: array - nullable: true - items: - type: string - deprecated: true - readUser: - type: string - nullable: true - deprecated: true - readPass: - type: string - nullable: true - deprecated: true - readIPs: - type: array - nullable: true - items: - type: string - deprecated: true - - # Publisher source - overridePublisher: - type: boolean - disablePublisherOverride: - type: boolean - nullable: true - deprecated: true - srtPublishPassphrase: - type: string - rtspDemuxMpegts: - type: boolean - - # RTSP source - rtspTransport: - $ref: "#/components/schemas/RTSPTransport" - rtspAnyPort: - type: boolean - sourceProtocol: - type: string - nullable: true - deprecated: true - allOf: - - $ref: "#/components/schemas/RTSPTransport" - sourceAnyPortEnable: - type: boolean - nullable: true - deprecated: true - rtspRangeType: - $ref: "#/components/schemas/RTSPRangeType" - rtspRangeStart: - type: string - rtspScale: - type: string - rtspUDPReadBufferSize: - type: integer - format: uint64 - nullable: true - deprecated: true - rtspUDPSourcePortRange: - type: array - minItems: 2 - maxItems: 2 - items: - type: integer - format: uint64 - - # MPEG-TS source - mpegtsUDPReadBufferSize: - type: integer - format: uint64 - nullable: true - deprecated: true - - # RTP source - rtpSDP: - type: string - rtpUDPReadBufferSize: - type: integer - format: uint64 - nullable: true - deprecated: true - - # WHEP source - whepBearerToken: - type: string - whepSTUNGatherTimeout: - type: string - whepHandshakeTimeout: - type: string - whepTrackGatherTimeout: - type: string - - # Redirect source - sourceRedirect: - type: string - - # Raspberry Pi Camera source - rpiCameraCamID: - type: integer - format: uint64 - rpiCameraSecondary: - type: boolean - rpiCameraWidth: - type: integer - format: uint64 - rpiCameraHeight: - type: integer - format: uint64 - rpiCameraHFlip: - type: boolean - rpiCameraVFlip: - type: boolean - rpiCameraBrightness: - type: number - format: double - rpiCameraContrast: - type: number - format: double - rpiCameraSaturation: - type: number - format: double - rpiCameraSharpness: - type: number - format: double - rpiCameraExposure: - type: string - rpiCameraAWB: - type: string - rpiCameraAWBGains: - type: array - minItems: 2 - maxItems: 2 - items: - type: number - format: double - rpiCameraDenoise: - type: string - rpiCameraShutter: - type: integer - format: uint64 - rpiCameraMetering: - type: string - rpiCameraGain: - type: number - format: double - rpiCameraEV: - type: number - format: double - rpiCameraROI: - type: string - rpiCameraHDR: - type: boolean - rpiCameraTuningFile: - type: string - rpiCameraMode: - type: string - rpiCameraFPS: - type: number - format: double - rpiCameraAfMode: - type: string - rpiCameraAfRange: - type: string - rpiCameraAfSpeed: - type: string - rpiCameraLensPosition: - type: number - format: double - rpiCameraAfWindow: - type: string - rpiCameraFlickerPeriod: - type: integer - format: uint64 - rpiCameraTextOverlayEnable: - type: boolean - rpiCameraTextOverlay: - type: string - rpiCameraCodec: - type: string - rpiCameraIDRPeriod: - type: integer - format: uint64 - rpiCameraBitrate: - type: integer - format: uint64 - rpiCameraProfile: - type: string - nullable: true - deprecated: true - rpiCameraLevel: - type: string - nullable: true - deprecated: true - rpiCameraHardwareH264Profile: - type: string - nullable: true - deprecated: true - rpiCameraHardwareH264Level: - type: string - nullable: true - deprecated: true - rpiCameraSoftwareH264Profile: - type: string - nullable: true - deprecated: true - rpiCameraSoftwareH264Level: - type: string - nullable: true - deprecated: true - rpiCameraH264Profile: - type: string - rpiCameraH264Level: - type: string - rpiCameraJPEGQuality: - type: integer - format: uint64 - nullable: true - deprecated: true - rpiCameraMJPEGQuality: - type: integer - format: uint64 - - # Hooks - runOnInit: - type: string - runOnInitRestart: - type: boolean - runOnDemand: - type: string - runOnDemandRestart: - type: boolean - runOnDemandStartTimeout: - type: string - runOnDemandCloseAfter: - type: string - runOnUnDemand: - type: string - runOnReady: - type: string - runOnReadyRestart: - type: boolean - runOnNotReady: - type: string - runOnRead: - type: string - runOnReadRestart: - type: boolean - runOnUnread: - type: string - runOnRecordSegmentCreate: - type: string - runOnRecordSegmentComplete: - type: string - - PathConfList: - type: object - properties: - pageCount: - type: integer - format: int64 - itemCount: - type: integer - format: int64 - items: - type: array - items: - $ref: "#/components/schemas/PathConf" - - Path: - type: object - properties: - name: - type: string - confName: - type: string - source: - type: object - nullable: true - allOf: - - $ref: "#/components/schemas/PathSource" - ready: - type: boolean - deprecated: true - readyTime: - type: string - nullable: true - deprecated: true - available: - type: boolean - availableTime: - type: string - nullable: true - online: - type: boolean - onlineTime: - type: string - nullable: true - tracks: - type: array - deprecated: true - items: - $ref: "#/components/schemas/PathTrackCodec" - tracks2: - type: array - items: - $ref: "#/components/schemas/PathTrack" - inboundBytes: - type: integer - format: uint64 - outboundBytes: - type: integer - format: uint64 - inboundFramesInError: - type: integer - format: uint64 - bytesReceived: - type: integer - format: uint64 - deprecated: true - bytesSent: - type: integer - format: uint64 - deprecated: true - readers: - type: array - items: - $ref: "#/components/schemas/PathReader" - - PathList: - type: object - properties: - pageCount: - type: integer - format: int64 - itemCount: - type: integer - format: int64 - items: - type: array - items: - $ref: "#/components/schemas/Path" - - PathSource: - type: object - properties: - type: - $ref: "#/components/schemas/PathSourceType" - id: - type: string - - PathReader: - type: object - properties: - type: - $ref: "#/components/schemas/PathReaderType" - id: - type: string - - PathTrack: - type: object - properties: - codec: - $ref: "#/components/schemas/PathTrackCodec" - codecProps: - type: object - nullable: true - allOf: - - $ref: "#/components/schemas/PathTrackCodecProps" - - PathTrackCodecProps: - oneOf: - - $ref: "#/components/schemas/PathTrackCodecPropsAV1" - - $ref: "#/components/schemas/PathTrackCodecPropsVP9" - - $ref: "#/components/schemas/PathTrackCodecPropsH265" - - $ref: "#/components/schemas/PathTrackCodecPropsH264" - - $ref: "#/components/schemas/PathTrackCodecPropsOpus" - - $ref: "#/components/schemas/PathTrackCodecPropsMPEG4Audio" - - $ref: "#/components/schemas/PathTrackCodecPropsAC3" - - $ref: "#/components/schemas/PathTrackCodecPropsG711" - - $ref: "#/components/schemas/PathTrackCodecPropsLPCM" - - PathTrackCodecPropsAV1: - type: object - properties: - width: - type: integer - format: int64 - height: - type: integer - format: int64 - profile: - type: integer - format: int64 - level: - type: integer - format: int64 - tier: - type: integer - format: int64 - PathTrackCodecPropsVP9: - type: object - properties: - profile: - type: integer - format: int64 - - PathTrackCodecPropsH265: - type: object - properties: - width: - type: integer - format: int64 - height: - type: integer - format: int64 - profile: - type: string - level: - type: string - - PathTrackCodecPropsH264: - type: object - properties: - width: - type: integer - format: int64 - height: - type: integer - format: int64 - profile: - type: string - level: - type: string - - PathTrackCodecPropsOpus: - type: object - properties: - channelCount: - type: integer - format: int64 - - PathTrackCodecPropsMPEG4Audio: - type: object - properties: - sampleRate: - type: integer - format: int64 - channelCount: - type: integer - format: int64 - - PathTrackCodecPropsAC3: - type: object - properties: - sampleRate: - type: integer - format: int64 - channelCount: - type: integer - format: int64 - - PathTrackCodecPropsG711: - type: object - properties: - muLaw: - type: boolean - sampleRate: - type: integer - format: int64 - channelCount: - type: integer - format: int64 - - PathTrackCodecPropsLPCM: - type: object - properties: - bitDepth: - type: integer - format: int64 - sampleRate: - type: integer - format: int64 - channelCount: - type: integer - format: int64 HLSMuxer: type: object properties: - path: - type: string + bytesSent: + type: integer + format: uint64 + description: deprecated + deprecated: true created: type: string lastRequest: @@ -1232,17 +718,12 @@ components: outboundFramesDiscarded: type: integer format: uint64 - bytesSent: - type: integer - format: uint64 - deprecated: true + path: + type: string HLSMuxerList: type: object properties: - pageCount: - type: integer - format: int64 itemCount: type: integer format: int64 @@ -1250,37 +731,37 @@ components: type: array items: $ref: "#/components/schemas/HLSMuxer" + pageCount: + type: integer + format: int64 HLSSession: type: object properties: + created: + type: string id: type: string format: uuid - created: - type: string - remoteAddr: - type: string - path: - type: string - query: - type: string - user: - type: string - userAgent: - type: string isCDN: type: boolean outboundBytes: type: integer format: uint64 + path: + type: string + query: + type: string + remoteAddr: + type: string + user: + type: string + userAgent: + type: string HLSSessionList: type: object properties: - pageCount: - type: integer - format: int64 itemCount: type: integer format: int64 @@ -1288,6 +769,592 @@ components: type: array items: $ref: "#/components/schemas/HLSSession" + pageCount: + type: integer + format: int64 + + Info: + type: object + properties: + started: + type: string + version: + type: string + + MoQSession: + type: object + properties: + created: + type: string + id: + type: string + format: uuid + inboundBytes: + type: integer + format: uint64 + outboundBytes: + type: integer + format: uint64 + path: + type: string + query: + type: string + remoteAddr: + type: string + state: + $ref: "#/components/schemas/MoQSessionState" + userAgent: + type: string + + MoQSessionList: + type: object + properties: + itemCount: + type: integer + format: int64 + items: + type: array + items: + $ref: "#/components/schemas/MoQSession" + pageCount: + type: integer + format: int64 + + OK: + type: object + properties: + status: + $ref: "#/components/schemas/OKStatus" + + Path: + type: object + properties: + available: + type: boolean + availableTime: + type: string + nullable: true + bytesReceived: + type: integer + format: uint64 + description: deprecated + deprecated: true + bytesSent: + type: integer + format: uint64 + deprecated: true + confName: + type: string + inboundBytes: + type: integer + format: uint64 + inboundFramesInError: + type: integer + format: uint64 + name: + type: string + online: + type: boolean + onlineTime: + type: string + nullable: true + outboundBytes: + type: integer + format: uint64 + readers: + type: array + items: + $ref: "#/components/schemas/PathReader" + ready: + type: boolean + deprecated: true + readyTime: + type: string + nullable: true + deprecated: true + source: + type: object + allOf: + - $ref: "#/components/schemas/PathSource" + nullable: true + tracks: + type: array + deprecated: true + items: + $ref: "#/components/schemas/PathTrackCodec" + tracks2: + type: array + items: + $ref: "#/components/schemas/PathTrack" + + PathConf: + type: object + properties: + alwaysAvailable: + type: boolean + description: Always available + alwaysAvailableFile: + type: string + alwaysAvailableTracks: + type: array + items: + $ref: "#/components/schemas/AlwaysAvailableTrack" + disablePublisherOverride: + type: boolean + nullable: true + deprecated: true + fallback: + type: string + nullable: true + deprecated: true + maxReaders: + type: integer + format: int64 + mpegtsUDPReadBufferSize: + type: integer + format: uint64 + description: MPEG-TS source + nullable: true + deprecated: true + name: + type: string + description: filled by Validate() + overridePublisher: + type: boolean + description: Publisher source + playback: + type: boolean + nullable: true + deprecated: true + publishIPs: + type: array + nullable: true + deprecated: true + items: + type: string + publishPass: + type: string + nullable: true + deprecated: true + publishUser: + type: string + description: Authentication (deprecated) + nullable: true + deprecated: true + readIPs: + type: array + nullable: true + deprecated: true + items: + type: string + readPass: + type: string + nullable: true + deprecated: true + readUser: + type: string + nullable: true + deprecated: true + record: + type: boolean + description: Record + recordDeleteAfter: + type: string + recordFormat: + $ref: "#/components/schemas/RecordFormat" + recordMaxPartSize: + type: string + recordPartDuration: + type: string + recordPath: + type: string + recordSegmentDuration: + type: string + rpiCameraAWB: + type: string + rpiCameraAWBGains: + type: array + items: + type: number + format: double + rpiCameraAfMode: + type: string + rpiCameraAfRange: + type: string + rpiCameraAfSpeed: + type: string + rpiCameraAfWindow: + type: string + rpiCameraBitrate: + type: integer + format: uint64 + rpiCameraBrightness: + type: number + format: double + rpiCameraCamID: + type: integer + format: uint64 + description: Raspberry Pi Camera source + rpiCameraCodec: + type: string + rpiCameraContrast: + type: number + format: double + rpiCameraDenoise: + type: string + rpiCameraEV: + type: number + format: double + rpiCameraExposure: + type: string + rpiCameraFPS: + type: number + format: double + rpiCameraFlickerPeriod: + type: integer + format: uint64 + rpiCameraGain: + type: number + format: double + rpiCameraH264Level: + type: string + rpiCameraH264Profile: + type: string + rpiCameraHDR: + type: boolean + rpiCameraHFlip: + type: boolean + rpiCameraHardwareH264Level: + type: string + nullable: true + deprecated: true + rpiCameraHardwareH264Profile: + type: string + nullable: true + deprecated: true + rpiCameraHeight: + type: integer + format: uint64 + rpiCameraIDRPeriod: + type: integer + format: uint64 + rpiCameraJPEGQuality: + type: integer + format: uint64 + nullable: true + deprecated: true + rpiCameraLensPosition: + type: number + format: double + rpiCameraLevel: + type: string + nullable: true + deprecated: true + rpiCameraMJPEGQuality: + type: integer + format: uint64 + rpiCameraMetering: + type: string + rpiCameraMode: + type: string + rpiCameraProfile: + type: string + nullable: true + deprecated: true + rpiCameraROI: + type: string + rpiCameraSaturation: + type: number + format: double + rpiCameraSecondary: + type: boolean + rpiCameraSharpness: + type: number + format: double + rpiCameraShutter: + type: integer + format: uint64 + rpiCameraSoftwareH264Level: + type: string + nullable: true + deprecated: true + rpiCameraSoftwareH264Profile: + type: string + nullable: true + deprecated: true + rpiCameraTextOverlay: + type: string + rpiCameraTextOverlayEnable: + type: boolean + rpiCameraTuningFile: + type: string + rpiCameraVFlip: + type: boolean + rpiCameraWidth: + type: integer + format: uint64 + rtpSDP: + type: string + description: RTP source + rtpUDPReadBufferSize: + type: integer + format: uint64 + nullable: true + deprecated: true + rtspAnyPort: + type: boolean + rtspDemuxMpegts: + type: boolean + rtspRangeStart: + type: string + rtspRangeType: + $ref: "#/components/schemas/RTSPRangeType" + rtspScale: + type: string + rtspTransport: + $ref: "#/components/schemas/RTSPTransport" + description: RTSP source + rtspUDPReadBufferSize: + type: integer + format: uint64 + nullable: true + deprecated: true + rtspUDPSourcePortRange: + type: array + items: + type: integer + format: uint64 + runOnDemand: + type: string + runOnDemandCloseAfter: + type: string + runOnDemandRestart: + type: boolean + runOnDemandStartTimeout: + type: string + runOnInit: + type: string + description: Hooks + runOnInitRestart: + type: boolean + runOnNotReady: + type: string + runOnRead: + type: string + runOnReadRestart: + type: boolean + runOnReady: + type: string + runOnReadyRestart: + type: boolean + runOnRecordSegmentComplete: + type: string + runOnRecordSegmentCreate: + type: string + runOnUnDemand: + type: string + runOnUnread: + type: string + source: + type: string + description: General + sourceAnyPortEnable: + type: boolean + nullable: true + deprecated: true + sourceFingerprint: + type: string + sourceOnDemand: + type: boolean + sourceOnDemandCloseAfter: + type: string + sourceOnDemandStartTimeout: + type: string + sourceProtocol: + type: string + allOf: + - $ref: "#/components/schemas/RTSPTransport" + nullable: true + deprecated: true + sourceRedirect: + type: string + description: Redirect source + srtPublishPassphrase: + type: string + srtReadPassphrase: + type: string + useAbsoluteTimestamp: + type: boolean + whepBearerToken: + type: string + description: WHEP source + whepHandshakeTimeout: + type: string + whepSTUNGatherTimeout: + type: string + whepTrackGatherTimeout: + type: string + + PathConfList: + type: object + properties: + itemCount: + type: integer + format: int64 + items: + type: array + items: + $ref: "#/components/schemas/PathConf" + pageCount: + type: integer + format: int64 + + PathList: + type: object + properties: + itemCount: + type: integer + format: int64 + items: + type: array + items: + $ref: "#/components/schemas/Path" + pageCount: + type: integer + format: int64 + + PathReader: + type: object + properties: + id: + type: string + type: + $ref: "#/components/schemas/PathReaderType" + + PathSource: + type: object + properties: + id: + type: string + type: + $ref: "#/components/schemas/PathSourceType" + + PathTrack: + type: object + properties: + codec: + $ref: "#/components/schemas/PathTrackCodec" + codecProps: + type: object + allOf: + - $ref: "#/components/schemas/PathTrackCodecProps" + nullable: true + + PathTrackCodecPropsAC3: + type: object + properties: + channelCount: + type: integer + format: int64 + sampleRate: + type: integer + format: int64 + + PathTrackCodecPropsAV1: + type: object + properties: + height: + type: integer + format: int64 + level: + type: integer + format: int64 + profile: + type: integer + format: int64 + tier: + type: integer + format: int64 + width: + type: integer + format: int64 + + PathTrackCodecPropsG711: + type: object + properties: + channelCount: + type: integer + format: int64 + muLaw: + type: boolean + sampleRate: + type: integer + format: int64 + + PathTrackCodecPropsH264: + type: object + properties: + height: + type: integer + format: int64 + level: + type: string + profile: + type: string + width: + type: integer + format: int64 + + PathTrackCodecPropsH265: + type: object + properties: + height: + type: integer + format: int64 + level: + type: string + profile: + type: string + width: + type: integer + format: int64 + + PathTrackCodecPropsLPCM: + type: object + properties: + bitDepth: + type: integer + format: int64 + channelCount: + type: integer + format: int64 + sampleRate: + type: integer + format: int64 + + PathTrackCodecPropsMPEG4Audio: + type: object + properties: + channelCount: + type: integer + format: int64 + sampleRate: + type: integer + format: int64 + + PathTrackCodecPropsOpus: + type: object + properties: + channelCount: + type: integer + format: int64 + + PathTrackCodecPropsVP9: + type: object + properties: + profile: + type: integer + format: int64 Recording: type: object @@ -1302,9 +1369,6 @@ components: RecordingList: type: object properties: - pageCount: - type: integer - format: int64 itemCount: type: integer format: int64 @@ -1312,6 +1376,9 @@ components: type: array items: $ref: "#/components/schemas/Recording" + pageCount: + type: integer + format: int64 RecordingSegment: type: object @@ -1322,23 +1389,20 @@ components: RTMPConn: type: object properties: + bytesReceived: + type: integer + format: uint64 + description: deprecated + deprecated: true + bytesSent: + type: integer + format: uint64 + deprecated: true + created: + type: string id: type: string format: uuid - created: - type: string - remoteAddr: - type: string - state: - $ref: "#/components/schemas/RTMPConnState" - path: - type: string - query: - type: string - user: - type: string - userAgent: - type: string inboundBytes: type: integer format: uint64 @@ -1348,6 +1412,36 @@ components: outboundFramesDiscarded: type: integer format: uint64 + path: + type: string + query: + type: string + remoteAddr: + type: string + state: + $ref: "#/components/schemas/RTMPConnState" + user: + type: string + userAgent: + type: string + + RTMPConnList: + type: object + properties: + itemCount: + type: integer + format: int64 + items: + type: array + items: + $ref: "#/components/schemas/RTMPConn" + pageCount: + type: integer + format: int64 + + RTSPConn: + type: object + properties: bytesReceived: type: integer format: uint64 @@ -1356,29 +1450,17 @@ components: type: integer format: uint64 deprecated: true - - RTMPConnList: - type: object - properties: - pageCount: - type: integer - format: int64 - itemCount: - type: integer - format: int64 - items: - type: array - items: - $ref: "#/components/schemas/RTMPConn" - - RTSPConn: - type: object - properties: + created: + type: string id: type: string format: uuid - created: - type: string + inboundBytes: + type: integer + format: uint64 + outboundBytes: + type: integer + format: uint64 remoteAddr: type: string session: @@ -1387,27 +1469,10 @@ components: nullable: true tunnel: type: string - inboundBytes: - type: integer - format: uint64 - outboundBytes: - type: integer - format: uint64 - bytesReceived: - type: integer - format: uint64 - deprecated: true - bytesSent: - type: integer - format: uint64 - deprecated: true RTSPConnList: type: object properties: - pageCount: - type: integer - format: int64 itemCount: type: integer format: int64 @@ -1415,45 +1480,42 @@ components: type: array items: $ref: "#/components/schemas/RTSPConn" + pageCount: + type: integer + format: int64 RTSPSession: type: object properties: - id: - type: string - format: uuid - created: - type: string - remoteAddr: - type: string - state: - $ref: "#/components/schemas/RTSPSessionState" - path: - type: string - query: - type: string - user: - type: string - userAgent: - type: string - transport: - type: string - nullable: true - profile: - type: string - nullable: true + bytesReceived: + type: integer + format: uint64 + description: deprecated + deprecated: true + bytesSent: + type: integer + format: uint64 + deprecated: true conns: type: array items: type: string format: uuid + created: + type: string + id: + type: string + format: uuid inboundBytes: type: integer format: uint64 - inboundRTPPackets: + inboundRTCPPackets: type: integer format: uint64 - inboundRTPPacketsLost: + inboundRTCPPacketsInError: + type: integer + format: uint64 + inboundRTPPackets: type: integer format: uint64 inboundRTPPacketsInError: @@ -1462,44 +1524,42 @@ components: inboundRTPPacketsJitter: type: number format: double - inboundRTCPPackets: - type: integer - format: uint64 - inboundRTCPPacketsInError: + inboundRTPPacketsLost: type: integer format: uint64 outboundBytes: type: integer format: uint64 - outboundRTPPackets: + outboundRTCPPackets: type: integer format: uint64 - outboundRTPPacketsReportedLost: + outboundRTPPackets: type: integer format: uint64 outboundRTPPacketsDiscarded: type: integer format: uint64 - outboundRTCPPackets: + outboundRTPPacketsReportedLost: type: integer format: uint64 - bytesReceived: + path: + type: string + profile: + type: string + nullable: true + query: + type: string + remoteAddr: + type: string + rtcpPacketsInError: type: integer format: uint64 deprecated: true - bytesSent: + rtcpPacketsReceived: type: integer format: uint64 deprecated: true - rtpPacketsReceived: - type: integer - format: uint64 - deprecated: true - rtpPacketsSent: - type: integer - format: uint64 - deprecated: true - rtpPacketsLost: + rtcpPacketsSent: type: integer format: uint64 deprecated: true @@ -1511,363 +1571,7 @@ components: type: number format: double deprecated: true - rtcpPacketsReceived: - type: integer - format: uint64 - deprecated: true - rtcpPacketsSent: - type: integer - format: uint64 - deprecated: true - rtcpPacketsInError: - type: integer - format: uint64 - deprecated: true - - RTSPSessionList: - type: object - properties: - pageCount: - type: integer - format: int64 - itemCount: - type: integer - format: int64 - items: - type: array - items: - $ref: "#/components/schemas/RTSPSession" - - SRTConn: - type: object - properties: - id: - type: string - format: uuid - created: - type: string - remoteAddr: - type: string - state: - $ref: "#/components/schemas/SRTConnState" - path: - type: string - query: - type: string - user: - type: string - packetsSent: - type: integer - format: uint64 - description: The total number of sent DATA packets, including retransmitted packets - packetsReceived: - type: integer - format: uint64 - description: The total number of received DATA packets, including retransmitted packets - packetsReceivedBelated: - type: integer - format: uint64 - packetsSentUnique: - type: integer - format: uint64 - description: The total number of unique DATA packets sent by the SRT sender - packetsReceivedUnique: - type: integer - format: uint64 - description: The total number of unique original, retransmitted or recovered by the packet filter DATA packets received in time, decrypted without errors and, as a result, scheduled for delivery to the upstream application by the SRT receiver. - packetsSendLoss: - type: integer - format: uint64 - description: The total number of data packets considered or reported as lost at the sender side. Does not correspond to the packets detected as lost at the receiver side. - packetsReceivedLoss: - type: integer - format: uint64 - description: The total number of SRT DATA packets detected as presently missing (either reordered or lost) at the receiver side - packetsRetrans: - type: integer - format: uint64 - description: The total number of retransmitted packets sent by the SRT sender - packetsReceivedRetrans: - type: integer - format: uint64 - description: The total number of retransmitted packets registered at the receiver side - packetsSentACK: - type: integer - format: uint64 - description: The total number of sent ACK (Acknowledgement) control packets - packetsReceivedACK: - type: integer - format: uint64 - description: The total number of received ACK (Acknowledgement) control packets - packetsSentNAK: - type: integer - format: uint64 - description: The total number of sent NAK (Negative Acknowledgement) control packets - packetsReceivedNAK: - type: integer - format: uint64 - description: The total number of received NAK (Negative Acknowledgement) control packets - packetsSentKM: - type: integer - format: uint64 - description: The total number of sent KM (Key Material) control packets - packetsReceivedKM: - type: integer - format: uint64 - description: The total number of received KM (Key Material) control packets - usSndDuration: - type: integer - format: uint64 - description: The total accumulated time in microseconds, during which the SRT sender has some data to transmit, including packets that have been sent, but not yet acknowledged - packetsSendDrop: - type: integer - format: uint64 - description: The total number of dropped by the SRT sender DATA packets that have no chance to be delivered in time - packetsReceivedDrop: - type: integer - format: uint64 - description: The total number of dropped by the SRT receiver and, as a result, not delivered to the upstream application DATA packets - packetsReceivedUndecrypt: - type: integer - format: uint64 - description: The total number of packets that failed to be decrypted at the receiver side - bytesSent: - type: integer - format: uint64 - description: Same as packetsSent, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - bytesReceived: - type: integer - format: uint64 - description: Same as packetsReceived, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - bytesReceivedBelated: - type: integer - format: uint64 - bytesSentUnique: - type: integer - format: uint64 - description: Same as packetsSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - bytesReceivedUnique: - type: integer - format: uint64 - description: Same as packetsReceivedUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - bytesReceivedLoss: - type: integer - format: uint64 - description: Same as packetsReceivedLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), bytes for the presently missing (either reordered or lost) packets' payloads are estimated based on the average packet size - bytesRetrans: - type: integer - format: uint64 - description: Same as packetsRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - bytesReceivedRetrans: - type: integer - format: uint64 - description: Same as packetsReceivedRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - bytesSendDrop: - type: integer - format: uint64 - description: Same as packetsSendDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - bytesReceivedDrop: - type: integer - format: uint64 - description: Same as packetsReceivedDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - bytesReceivedUndecrypt: - type: integer - format: uint64 - description: Same as packetsReceivedUndecrypt, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - usPacketsSendPeriod: - type: number - format: double - description: Current minimum time interval between which consecutive packets are sent, in microseconds - packetsFlowWindow: - type: integer - format: uint64 - description: The maximum number of packets that can be "in flight" - packetsFlightSize: - type: integer - format: uint64 - description: The number of packets in flight - msRTT: - type: number - format: double - description: Smoothed round-trip time (SRTT), an exponentially-weighted moving average (EWMA) of an endpoint's RTT samples, in milliseconds - mbpsSendRate: - type: number - format: double - description: Current transmission bandwidth, in Mbps - mbpsReceiveRate: - type: number - format: double - description: Current receiving bandwidth, in Mbps - mbpsLinkCapacity: - type: number - format: double - description: Estimated capacity of the network link, in Mbps - bytesAvailSendBuf: - type: integer - format: uint64 - description: The available space in the sender's buffer, in bytes - bytesAvailReceiveBuf: - type: integer - format: uint64 - description: The available space in the receiver's buffer, in bytes - mbpsMaxBW: - type: number - format: double - description: Transmission bandwidth limit, in Mbps - byteMSS: - type: integer - format: uint64 - description: Maximum Segment Size (MSS), in bytes - packetsSendBuf: - type: integer - format: uint64 - description: The number of packets in the sender's buffer that are already scheduled for sending or even possibly sent, but not yet acknowledged - bytesSendBuf: - type: integer - format: uint64 - description: Instantaneous (current) value of packetsSndBuf, but expressed in bytes, including payload and all headers (IP, TCP, SRT) - msSendBuf: - type: integer - format: uint64 - description: The timespan (msec) of packets in the sender's buffer (unacknowledged packets) - msSendTsbPdDelay: - type: integer - format: uint64 - description: Timestamp-based Packet Delivery Delay value of the peer - packetsReceiveBuf: - type: integer - format: uint64 - description: The number of acknowledged packets in receiver's buffer - bytesReceiveBuf: - type: integer - format: uint64 - description: Instantaneous (current) value of packetsRcvBuf, expressed in bytes, including payload and all headers (IP, TCP, SRT) - msReceiveBuf: - type: integer - format: uint64 - description: The timespan (msec) of acknowledged packets in the receiver's buffer - msReceiveTsbPdDelay: - type: integer - format: uint64 - description: Timestamp-based Packet Delivery Delay value set on the socket via SRTO_RCVLATENCY or SRTO_LATENCY - packetsReorderTolerance: - type: integer - format: uint64 - description: Instant value of the packet reorder tolerance - packetsReceivedAvgBelatedTime: - type: integer - format: uint64 - description: Accumulated difference between the current time and the time-to-play of a packet that is received late - packetsSendLossRate: - type: number - format: double - description: Percentage of resent data vs. sent data - packetsReceivedLossRate: - type: number - format: double - description: Percentage of retransmitted data vs. received data - outboundFramesDiscarded: - type: integer - format: uint64 - - SRTConnList: - type: object - properties: - pageCount: - type: integer - format: int64 - itemCount: - type: integer - format: int64 - items: - type: array - items: - $ref: "#/components/schemas/SRTConn" - - AlwaysAvailableTrack: - type: object - properties: - codec: - $ref: "#/components/schemas/AlwaysAvailableTrackCodec" - sampleRate: - type: integer - format: int64 - channelCount: - type: integer - format: int64 - muLaw: - type: boolean - - WebRTCICEServer: - type: object - properties: - url: - type: string - username: - type: string - password: - type: string - clientOnly: - type: boolean - - WebRTCSession: - type: object - properties: - id: - type: string - format: uuid - created: - type: string - remoteAddr: - type: string - peerConnectionEstablished: - type: boolean - localCandidate: - type: string - remoteCandidate: - type: string - state: - $ref: "#/components/schemas/WebRTCSessionState" - path: - type: string - query: - type: string - user: - type: string - userAgent: - type: string - inboundBytes: - type: integer - format: uint64 - inboundRTPPackets: - type: integer - format: uint64 - inboundRTPPacketsLost: - type: integer - format: uint64 - inboundRTPPacketsJitter: - type: number - format: double - inboundRTCPPackets: - type: integer - format: uint64 - outboundBytes: - type: integer - format: uint64 - outboundRTPPackets: - type: integer - format: uint64 - outboundRTCPPackets: - type: integer - format: uint64 - outboundFramesDiscarded: - type: integer - format: uint64 - bytesReceived: - type: integer - format: uint64 - deprecated: true - bytesSent: + rtpPacketsLost: type: integer format: uint64 deprecated: true @@ -1879,14 +1583,345 @@ components: type: integer format: uint64 deprecated: true - rtpPacketsLost: + state: + $ref: "#/components/schemas/RTSPSessionState" + transport: + type: string + nullable: true + user: + type: string + userAgent: + type: string + + RTSPSessionList: + type: object + properties: + itemCount: + type: integer + format: int64 + items: + type: array + items: + $ref: "#/components/schemas/RTSPSession" + pageCount: + type: integer + format: int64 + + SRTConn: + type: object + properties: + byteMSS: + type: integer + format: uint64 + description: Maximum Segment Size (MSS), in bytes + bytesAvailReceiveBuf: + type: integer + format: uint64 + description: The available space in the receiver's buffer, in bytes + bytesAvailSendBuf: + type: integer + format: uint64 + description: The available space in the sender's buffer, in bytes + bytesReceiveBuf: + type: integer + format: uint64 + description: Instantaneous (current) value of packetsRcvBuf, expressed in bytes, including payload and all headers (IP, TCP, SRT) + bytesReceived: + type: integer + format: uint64 + description: Same as packetsReceived, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + bytesReceivedBelated: + type: integer + format: uint64 + description: Same as PacketsReceivedBelated, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + bytesReceivedDrop: + type: integer + format: uint64 + description: Same as packetsReceivedDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + bytesReceivedLoss: + type: integer + format: uint64 + description: Same as packetsReceivedLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), bytes for the presently missing (either reordered or lost) packets' payloads are estimated based on the average packet size + bytesReceivedRetrans: + type: integer + format: uint64 + description: Same as packetsReceivedRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + bytesReceivedUndecrypt: + type: integer + format: uint64 + description: Same as packetsReceivedUndecrypt, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + bytesReceivedUnique: + type: integer + format: uint64 + description: Same as packetsReceivedUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + bytesRetrans: + type: integer + format: uint64 + description: Same as packetsRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + bytesSendBuf: + type: integer + format: uint64 + description: Instantaneous (current) value of packetsSndBuf, but expressed in bytes, including payload and all headers (IP, TCP, SRT) + bytesSendDrop: + type: integer + format: uint64 + description: Same as packetsSendDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + bytesSent: + type: integer + format: uint64 + description: Same as packetsSent, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + bytesSentUnique: + type: integer + format: uint64 + description: Same as packetsSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + created: + type: string + id: + type: string + format: uuid + mbpsLinkCapacity: + type: number + format: double + description: Estimated capacity of the network link, in Mbps + mbpsMaxBW: + type: number + format: double + description: Transmission bandwidth limit, in Mbps + mbpsReceiveRate: + type: number + format: double + description: Current receiving bandwidth, in Mbps + mbpsSendRate: + type: number + format: double + description: Current transmission bandwidth, in Mbps + msRTT: + type: number + format: double + description: Smoothed round-trip time (SRTT), an exponentially-weighted moving average (EWMA) of an endpoint's RTT samples, in milliseconds + msReceiveBuf: + type: integer + format: uint64 + description: The timespan (msec) of acknowledged packets in the receiver's buffer + msReceiveTsbPdDelay: + type: integer + format: uint64 + description: Timestamp-based Packet Delivery Delay value set on the socket via SRTO_RCVLATENCY or SRTO_LATENCY + msSendBuf: + type: integer + format: uint64 + description: The timespan (msec) of packets in the sender's buffer (unacknowledged packets) + msSendTsbPdDelay: + type: integer + format: uint64 + description: Timestamp-based Packet Delivery Delay value of the peer + outboundFramesDiscarded: + type: integer + format: uint64 + packetsFlightSize: + type: integer + format: uint64 + description: The number of packets in flight + packetsFlowWindow: + type: integer + format: uint64 + description: The maximum number of packets that can be "in flight" + packetsReceiveBuf: + type: integer + format: uint64 + description: The number of acknowledged packets in receiver's buffer + packetsReceived: + type: integer + format: uint64 + description: The total number of received DATA packets, including retransmitted packets + packetsReceivedACK: + type: integer + format: uint64 + description: The total number of received ACK (Acknowledgement) control packets + packetsReceivedAvgBelatedTime: + type: integer + format: uint64 + description: Accumulated difference between the current time and the time-to-play of a packet that is received late + packetsReceivedBelated: + type: integer + format: uint64 + packetsReceivedDrop: + type: integer + format: uint64 + description: The total number of dropped by the SRT receiver and, as a result, not delivered to the upstream application DATA packets + packetsReceivedKM: + type: integer + format: uint64 + description: The total number of received KM (Key Material) control packets + packetsReceivedLoss: + type: integer + format: uint64 + description: The total number of SRT DATA packets detected as presently missing (either reordered or lost) at the receiver side + packetsReceivedLossRate: + type: number + format: double + description: Percentage of retransmitted data vs. received data + packetsReceivedNAK: + type: integer + format: uint64 + description: The total number of received NAK (Negative Acknowledgement) control packets + packetsReceivedRetrans: + type: integer + format: uint64 + description: The total number of retransmitted packets registered at the receiver side + packetsReceivedUndecrypt: + type: integer + format: uint64 + description: The total number of packets that failed to be decrypted at the receiver side + packetsReceivedUnique: + type: integer + format: uint64 + description: The total number of unique original, retransmitted or recovered by the packet filter DATA packets received in time, decrypted without errors and, as a result, scheduled for delivery to the upstream application by the SRT receiver. + packetsReorderTolerance: + type: integer + format: uint64 + description: Instant value of the packet reorder tolerance + packetsRetrans: + type: integer + format: uint64 + description: The total number of retransmitted packets sent by the SRT sender + packetsSendBuf: + type: integer + format: uint64 + description: The number of packets in the sender's buffer that are already scheduled for sending or even possibly sent, but not yet acknowledged + packetsSendDrop: + type: integer + format: uint64 + description: The total number of dropped by the SRT sender DATA packets that have no chance to be delivered in time + packetsSendLoss: + type: integer + format: uint64 + description: The total number of data packets considered or reported as lost at the sender side. Does not correspond to the packets detected as lost at the receiver side. + packetsSendLossRate: + type: number + format: double + description: Percentage of resent data vs. sent data + packetsSent: + type: integer + format: uint64 + description: The total number of sent DATA packets, including retransmitted packets + packetsSentACK: + type: integer + format: uint64 + description: The total number of sent ACK (Acknowledgement) control packets + packetsSentKM: + type: integer + format: uint64 + description: The total number of sent KM (Key Material) control packets + packetsSentNAK: + type: integer + format: uint64 + description: The total number of sent NAK (Negative Acknowledgement) control packets + packetsSentUnique: + type: integer + format: uint64 + description: The total number of unique DATA packets sent by the SRT sender + path: + type: string + query: + type: string + remoteAddr: + type: string + state: + $ref: "#/components/schemas/SRTConnState" + usPacketsSendPeriod: + type: number + format: double + description: Current minimum time interval between which consecutive packets are sent, in microseconds + usSndDuration: + type: integer + format: uint64 + description: The total accumulated time in microseconds, during which the SRT sender has some data to transmit, including packets that have been sent, but not yet acknowledged + user: + type: string + + SRTConnList: + type: object + properties: + itemCount: + type: integer + format: int64 + items: + type: array + items: + $ref: "#/components/schemas/SRTConn" + pageCount: + type: integer + format: int64 + + WebRTCICEServer: + type: object + properties: + clientOnly: + type: boolean + password: + type: string + url: + type: string + username: + type: string + + WebRTCSession: + type: object + properties: + bytesReceived: + type: integer + format: uint64 + description: deprecated + deprecated: true + bytesSent: type: integer format: uint64 deprecated: true - rtpPacketsJitter: + created: + type: string + id: + type: string + format: uuid + inboundBytes: + type: integer + format: uint64 + inboundRTCPPackets: + type: integer + format: uint64 + inboundRTPPackets: + type: integer + format: uint64 + inboundRTPPacketsJitter: type: number format: double - deprecated: true + inboundRTPPacketsLost: + type: integer + format: uint64 + localCandidate: + type: string + outboundBytes: + type: integer + format: uint64 + outboundFramesDiscarded: + type: integer + format: uint64 + outboundRTCPPackets: + type: integer + format: uint64 + outboundRTPPackets: + type: integer + format: uint64 + path: + type: string + peerConnectionEstablished: + type: boolean + query: + type: string + remoteAddr: + type: string + remoteCandidate: + type: string rtcpPacketsReceived: type: integer format: uint64 @@ -1895,13 +1930,32 @@ components: type: integer format: uint64 deprecated: true + rtpPacketsJitter: + type: number + format: double + deprecated: true + rtpPacketsLost: + type: integer + format: uint64 + deprecated: true + rtpPacketsReceived: + type: integer + format: uint64 + deprecated: true + rtpPacketsSent: + type: integer + format: uint64 + deprecated: true + state: + $ref: "#/components/schemas/WebRTCSessionState" + user: + type: string + userAgent: + type: string WebRTCSessionList: type: object properties: - pageCount: - type: integer - format: int64 itemCount: type: integer format: int64 @@ -1909,39 +1963,9 @@ components: type: array items: $ref: "#/components/schemas/WebRTCSession" - - MoQSession: - type: object - properties: - id: - type: string - format: uuid - created: - type: string - remoteAddr: - type: string - state: - $ref: "#/components/schemas/MoQSessionState" - path: - type: string - query: - type: string - userAgent: - type: string - - MoQSessionList: - type: object - properties: pageCount: type: integer format: int64 - itemCount: - type: integer - format: int64 - items: - type: array - items: - $ref: "#/components/schemas/MoQSession" paths: /v3/info: @@ -3478,7 +3502,7 @@ paths: /v3/moqsessions/list: get: operationId: moqSessionsList - tags: [MoQ] + tags: [Media-over-QUIC] summary: returns all MoQ sessions. description: "" parameters: @@ -3517,7 +3541,7 @@ paths: /v3/moqsessions/get/{id}: get: operationId: moqSessionsGet - tags: [MoQ] + tags: [Media-over-QUIC] summary: returns a MoQ session. description: "" parameters: @@ -3556,7 +3580,7 @@ paths: /v3/moqsessions/kick/{id}: post: operationId: moqSessionsKick - tags: [MoQ] + tags: [Media-over-QUIC] summary: kicks out a MoQ session from the server. description: "" parameters: diff --git a/internal/apidocsgen/enums.go b/internal/apidocsgen/enums.go new file mode 100644 index 00000000..2699680c --- /dev/null +++ b/internal/apidocsgen/enums.go @@ -0,0 +1,178 @@ +// Package main contains a tool to generate openapi.yaml. +package main + +import ( + goast "go/ast" + goparser "go/parser" + gotoken "go/token" + "path/filepath" + "strings" + + "github.com/goccy/go-yaml" + "github.com/goccy/go-yaml/ast" +) + +var enums = []struct { + externalName string + internalName string + File string +}{ + { + externalName: "ErrorStatus", + internalName: "APIErrorStatus", + File: filepath.Join("internal", "defs", "api.go"), + }, + { + externalName: "AlwaysAvailableTrackCodec", + internalName: "AlwaysAvailableTrackCodec", + File: filepath.Join("internal", "conf", "always_available_track_codec.go"), + }, + { + externalName: "AuthAction", + internalName: "AuthAction", + File: filepath.Join("internal", "conf", "auth_action.go"), + }, + { + externalName: "AuthMethod", + internalName: "AuthMethod", + File: filepath.Join("internal", "conf", "auth_method.go"), + }, + { + externalName: "Encryption", + internalName: "Encryption", + File: filepath.Join("internal", "conf", "encryption.go"), + }, + { + externalName: "MoQSessionState", + internalName: "APIMoQSessionState", + File: filepath.Join("internal", "defs", "api_moq.go"), + }, + { + externalName: "OKStatus", + internalName: "APIOKStatus", + File: filepath.Join("internal", "defs", "api.go"), + }, + { + externalName: "PathReaderType", + internalName: "APIPathReaderType", + File: filepath.Join("internal", "defs", "api_path.go"), + }, + { + externalName: "PathSourceType", + internalName: "APIPathSourceType", + File: filepath.Join("internal", "defs", "api_path.go"), + }, + { + externalName: "PathTrackCodec", + internalName: "Label", + File: filepath.Join("internal", "formatlabel", "label.go"), + }, + { + externalName: "RTMPConnState", + internalName: "APIRTMPConnState", + File: filepath.Join("internal", "defs", "api_rtmp.go"), + }, + { + externalName: "RTSPRangeType", + internalName: "RTSPRangeType", + File: filepath.Join("internal", "conf", "rtsp_range_type.go"), + }, + { + externalName: "RTSPSessionState", + internalName: "APIRTSPSessionState", + File: filepath.Join("internal", "defs", "api_rtsp.go"), + }, + { + externalName: "RecordFormat", + internalName: "RecordFormat", + File: filepath.Join("internal", "conf", "record_format.go"), + }, + { + externalName: "SRTConnState", + internalName: "APISRTConnState", + File: filepath.Join("internal", "defs", "api_srt.go"), + }, + { + externalName: "WebRTCSessionState", + internalName: "APIWebRTCSessionState", + File: filepath.Join("internal", "defs", "api_webrtc.go"), + }, +} + +func extractEnumValues(name, file string) ([]string, error) { + fset := gotoken.NewFileSet() + + f, err := goparser.ParseFile(fset, file, nil, 0) + if err != nil { + return nil, err + } + + var values []string + + for _, decl := range f.Decls { + genDecl, ok := decl.(*goast.GenDecl) + if !ok || genDecl.Tok != gotoken.CONST { + continue + } + + for _, spec := range genDecl.Specs { + var valSpec *goast.ValueSpec + valSpec, ok = spec.(*goast.ValueSpec) + if !ok || valSpec.Type == nil { + continue + } + + var ident *goast.Ident + ident, ok = valSpec.Type.(*goast.Ident) + if !ok || ident.Name != name { + continue + } + + for _, val := range valSpec.Values { + var lit *goast.BasicLit + lit, ok = val.(*goast.BasicLit) + if !ok { + continue + } + values = append(values, strings.Trim(lit.Value, `"`)) + } + } + } + + return values, nil +} + +func addEnums(astFile *ast.File) error { + for _, e := range enums { + values, err := extractEnumValues(e.internalName, e.File) + if err != nil { + return err + } + + schema := &openAPISchema{ + Type: "string", + Enum: values, + } + + schemaNode, err := yaml.ValueToNode(map[string]*openAPISchema{e.externalName: schema}) + if err != nil { + return err + } + + indentBlockSequences(schemaNode) + + addBlankLineBeforeNewSchemaEntry(schemaNode) + + schemasPath, err := yaml.PathString("$.components.schemas") + if err != nil { + return err + } + + err = schemasPath.MergeFromNode(astFile, schemaNode) + if err != nil { + return err + } + } + + return nil +} diff --git a/internal/apidocsgen/main.go b/internal/apidocsgen/main.go new file mode 100644 index 00000000..9ae84c2b --- /dev/null +++ b/internal/apidocsgen/main.go @@ -0,0 +1,129 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "log" + "os" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/parser" + "github.com/goccy/go-yaml/token" +) + +const ( + templateFile = "internal/apidocsgen/openapi.template.yaml" + outputFile = "api/openapi.yaml" +) + +type openAPISchema struct { + Type string `yaml:"type,omitempty"` + Enum []string `yaml:"enum,omitempty"` + OneOf []openAPIProperty `yaml:"oneOf,omitempty"` + Properties map[string]openAPIProperty `yaml:"properties,omitempty"` +} + +type openAPIProperty struct { + Ref string `yaml:"$ref,omitempty"` + Type string `yaml:"type,omitempty"` + Format string `yaml:"format,omitempty"` + Description string `yaml:"description,omitempty"` + AllOf []openAPIProperty `yaml:"allOf,omitempty"` + Nullable bool `yaml:"nullable,omitempty"` + Deprecated bool `yaml:"deprecated,omitempty"` + Enum []string `yaml:"enum,omitempty"` + Items *openAPIProperty `yaml:"items,omitempty"` +} + +func indentBlockSequences(node ast.Node) { + switch n := node.(type) { + case *ast.MappingNode: + for _, value := range n.Values { + indentBlockSequences(value) + } + + case *ast.MappingValueNode: + if seq, ok := n.Value.(*ast.SequenceNode); ok && !seq.IsFlowStyle && len(seq.Values) > 0 { + seq.AddColumn(2) + } + indentBlockSequences(n.Value) + + case *ast.SequenceNode: + for _, value := range n.Values { + indentBlockSequences(value) + } + } +} + +func addBlankLineBeforeNewSchemaEntry(schemaNode ast.Node) { + if schemasMapping, ok := schemaNode.(*ast.MappingNode); ok && len(schemasMapping.Values) > 0 { + keyTk := schemasMapping.Values[0].Key.GetToken() + keyTk.Position.Line = 3 + keyTk.Prev = &token.Token{ + Type: token.StringType, + Position: &token.Position{Line: 1}, + } + } +} + +func parseTemplate() (*ast.File, error) { + data, err := os.ReadFile(templateFile) + if err != nil { + return nil, err + } + + return parser.ParseBytes(data, parser.ParseComments) +} + +func generate() ([]byte, error) { + astFile, err := parseTemplate() + if err != nil { + return nil, err + } + + err = addEnums(astFile) + if err != nil { + return nil, err + } + + err = addStructs(astFile) + if err != nil { + return nil, err + } + + return []byte(astFile.String()), nil +} + +func main() { + check := flag.Bool("check", false, "check whether the generated OpenAPI matches the file on disk") + flag.Parse() + + generated, err := generate() + if err != nil { + log.Printf("error: %v\n", err) + os.Exit(1) + } + + if *check { + var existing []byte + existing, err = os.ReadFile(outputFile) + if err != nil { + log.Printf("error: %v\n", err) + os.Exit(1) + } + + if !bytes.Equal(existing, generated) { + log.Printf("error: %v\n", fmt.Errorf("%s is outdated, run `go run ./internal/apidocsgen`", outputFile)) + os.Exit(1) + } + + return + } + + err = os.WriteFile(outputFile, generated, 0o644) + if err != nil { + log.Printf("error: %v\n", err) + os.Exit(1) + } +} diff --git a/internal/apidocsgen/openapi.template.yaml b/internal/apidocsgen/openapi.template.yaml new file mode 100644 index 00000000..f8920500 --- /dev/null +++ b/internal/apidocsgen/openapi.template.yaml @@ -0,0 +1,1820 @@ +openapi: 3.0.0 + +info: + version: 1.0.0 + title: MediaMTX API + description: API of MediaMTX, a server and proxy that supports various protocols. + license: + name: MIT + url: https://opensource.org/licenses/MIT + +servers: + - url: http://localhost:9997 + +security: [] + +components: + schemas: + HLSVariant: + type: string + enum: [mpegts, fmp4, lowLatency] + + LogDestination: + type: string + enum: [stdout, file, syslog] + + LogLevel: + type: string + enum: [error, warn, info, debug] + + RTSPAuthMethod: + type: string + enum: [basic, digest] + + RTSPTransport: + type: string + enum: [udp, multicast, tcp, automatic] + + PathTrackCodecProps: + oneOf: + - $ref: "#/components/schemas/PathTrackCodecPropsAV1" + - $ref: "#/components/schemas/PathTrackCodecPropsVP9" + - $ref: "#/components/schemas/PathTrackCodecPropsH265" + - $ref: "#/components/schemas/PathTrackCodecPropsH264" + - $ref: "#/components/schemas/PathTrackCodecPropsOpus" + - $ref: "#/components/schemas/PathTrackCodecPropsMPEG4Audio" + - $ref: "#/components/schemas/PathTrackCodecPropsAC3" + - $ref: "#/components/schemas/PathTrackCodecPropsG711" + - $ref: "#/components/schemas/PathTrackCodecPropsLPCM" + +paths: + /v3/info: + get: + operationId: info + tags: [General] + summary: returns informations about the instance. + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/Info" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/auth/jwks/refresh: + post: + operationId: authJwksRefresh + tags: [Authentication] + summary: Manually refreshes the JWT JWKS. + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/config/global/get: + get: + operationId: configGlobalGet + tags: [Configuration] + summary: returns the global configuration. + description: "" + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/GlobalConf" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/config/global/patch: + patch: + operationId: configGlobalSet + tags: [Configuration] + summary: patches the global configuration. + description: all fields are optional. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/GlobalConf" + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/config/pathdefaults/get: + get: + operationId: configPathDefaultsGet + tags: [Configuration] + summary: returns the default path configuration. + description: "" + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/PathConf" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/config/pathdefaults/patch: + patch: + operationId: configPathDefaultsPatch + tags: [Configuration] + summary: patches the default path configuration. + description: all fields are optional. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/PathConf" + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/config/paths/list: + get: + operationId: configPathsList + tags: [Configuration] + summary: returns all path configurations. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/PathConfList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/config/paths/get/{name}: + get: + operationId: configPathsGet + tags: [Configuration] + summary: returns a path configuration. + description: "" + parameters: + - name: name + in: path + required: true + description: the name of the path. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/PathConf" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: path not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/config/paths/add/{name}: + post: + operationId: configPathsAdd + tags: [Configuration] + summary: adds a path configuration. + description: all fields are optional. + parameters: + - name: name + in: path + required: true + description: the name of the path. + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/PathConf" + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/config/paths/patch/{name}: + patch: + operationId: configPathsPatch + tags: [Configuration] + summary: patches a path configuration. + description: all fields are optional. + parameters: + - name: name + in: path + required: true + description: the name of the path. + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/PathConf" + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: path not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/config/paths/replace/{name}: + post: + operationId: configPathsReplace + tags: [Configuration] + summary: replaces all values of a path configuration. + description: all fields are optional. + parameters: + - name: name + in: path + required: true + description: the name of the path. + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/PathConf" + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: path not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/config/paths/delete/{name}: + delete: + operationId: configPathsDelete + tags: [Configuration] + summary: removes a path configuration. + description: "" + parameters: + - name: name + in: path + required: true + description: the name of the path. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: path not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/hlsmuxers/list: + get: + operationId: hlsMuxersList + tags: [HLS] + summary: returns all HLS muxers. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/HLSMuxerList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/hlsmuxers/get/{name}: + get: + operationId: hlsMuxersGet + tags: [HLS] + summary: returns a HLS muxer. + description: "" + parameters: + - name: name + in: path + required: true + description: name of the muxer. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/HLSMuxer" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: muxer not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/hlssessions/list: + get: + operationId: hlssessionsList + tags: [HLS] + summary: returns all HLS sessions. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/HLSSessionList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/hlssessions/get/{id}: + get: + operationId: hlssessionsGet + tags: [HLS] + summary: returns a HLS session. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the session. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/HLSSession" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: session not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/hlssessions/kick/{id}: + post: + operationId: hlssessionsKick + tags: [HLS] + summary: kicks out a HLS session from the server. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the session. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: session not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/paths/list: + get: + operationId: pathsList + tags: [Paths] + summary: returns all paths. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/PathList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/paths/get/{name}: + get: + operationId: pathsGet + tags: [Paths] + summary: returns a path. + description: "" + parameters: + - name: name + in: path + required: true + description: name of the path. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/Path" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: path not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtspconns/list: + get: + operationId: rtspConnsList + tags: [RTSP] + summary: returns all RTSP connections. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTSPConnList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtspconns/get/{id}: + get: + operationId: rtspConnsGet + tags: [RTSP] + summary: returns a RTSP connection. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the connection. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTSPConn" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: connection not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtspsessions/list: + get: + operationId: rtspSessionsList + tags: [RTSP] + summary: returns all RTSP sessions. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTSPSessionList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtspsessions/get/{id}: + get: + operationId: rtspSessionsGet + tags: [RTSP] + summary: returns a RTSP session. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the connection. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTSPSession" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: session not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtspsessions/kick/{id}: + post: + operationId: rtspSessionsKick + tags: [RTSP] + summary: kicks out a RTSP session from the server. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the session. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: session not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtspsconns/list: + get: + operationId: rtspsConnsList + tags: [RTSP] + summary: returns all RTSPS connections. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTSPConnList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtspsconns/get/{id}: + get: + operationId: rtspsConnsGet + tags: [RTSP] + summary: returns a RTSPS connection. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the connection. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTSPConn" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: connection not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtspssessions/list: + get: + operationId: rtspsSessionsList + tags: [RTSP] + summary: returns all RTSPS sessions. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTSPSessionList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtspssessions/get/{id}: + get: + operationId: rtspsSessionsGet + tags: [RTSP] + summary: returns a RTSPS session. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the connection. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTSPSession" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: session not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtspssessions/kick/{id}: + post: + operationId: rtspsSessionsKick + tags: [RTSP] + summary: kicks out a RTSPS session from the server. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the session. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: session not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtmpconns/list: + get: + operationId: rtmpConnsList + tags: [RTMP] + summary: returns all RTMP connections. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTMPConnList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtmpconns/get/{id}: + get: + operationId: rtmpConnectionsGet + tags: [RTMP] + summary: returns a RTMP connection. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the connection. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTMPConn" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: connection not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtmpconns/kick/{id}: + post: + operationId: rtmpConnsKick + tags: [RTMP] + summary: kicks out a RTMP connection from the server. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the connection. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: connection not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtmpsconns/list: + get: + operationId: rtmpsConnsList + tags: [RTMP] + summary: returns all RTMPS connections. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTMPConnList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtmpsconns/get/{id}: + get: + operationId: rtmpsConnectionsGet + tags: [RTMP] + summary: returns a RTMPS connection. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the connection. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RTMPConn" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: connection not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/rtmpsconns/kick/{id}: + post: + operationId: rtmpsConnsKick + tags: [RTMP] + summary: kicks out a RTMPS connection from the server. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the connection. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: connection not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/srtconns/list: + get: + operationId: srtConnsList + tags: [SRT] + summary: returns all SRT connections. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/SRTConnList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/srtconns/get/{id}: + get: + operationId: srtConnsGet + tags: [SRT] + summary: returns a SRT connection. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the connection. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/SRTConn" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: connection not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/srtconns/kick/{id}: + post: + operationId: srtConnsKick + tags: [SRT] + summary: kicks out a SRT connection from the server. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the connection. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: connection not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/webrtcsessions/list: + get: + operationId: webrtcSessionsList + tags: [WebRTC] + summary: returns all WebRTC sessions. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/WebRTCSessionList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/webrtcsessions/get/{id}: + get: + operationId: webrtcSessionsGet + tags: [WebRTC] + summary: returns a WebRTC session. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the session. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/WebRTCSession" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: session not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/webrtcsessions/kick/{id}: + post: + operationId: webrtcSessionsKick + tags: [WebRTC] + summary: kicks out a WebRTC session from the server. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the session. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: session not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/moqsessions/list: + get: + operationId: moqSessionsList + tags: [Media-over-QUIC] + summary: returns all MoQ sessions. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/MoQSessionList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/moqsessions/get/{id}: + get: + operationId: moqSessionsGet + tags: [Media-over-QUIC] + summary: returns a MoQ session. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the session. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/MoQSession" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: session not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/moqsessions/kick/{id}: + post: + operationId: moqSessionsKick + tags: [Media-over-QUIC] + summary: kicks out a MoQ session from the server. + description: "" + parameters: + - name: id + in: path + required: true + description: ID of the session. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: session not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/recordings/list: + get: + operationId: recordingsList + tags: [Recordings] + summary: returns all recordings, splitted by path. + description: "" + parameters: + - name: page + in: query + description: page number. + schema: + type: integer + default: 0 + - name: itemsPerPage + in: query + description: items per page. + schema: + type: integer + default: 100 + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/RecordingList" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/recordings/get/{name}: + get: + operationId: recordingsGet + tags: [Recordings] + summary: returns recordings of a path. + description: "" + parameters: + - name: name + in: path + required: true + description: name of the path. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/Recording" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: path not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v3/recordings/deletesegment: + delete: + operationId: recordingsDeleteSegment + tags: [Recordings] + summary: deletes a recording segment. + description: "" + parameters: + - name: path + in: query + required: true + description: path. + schema: + type: string + - name: start + in: query + required: true + description: starting date of the segment. + schema: + type: string + responses: + "200": + description: the request was successful. + content: + application/json: + schema: + $ref: "#/components/schemas/OK" + "400": + description: invalid request. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: connection not found. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + description: server error. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" diff --git a/internal/apidocsgen/structs.go b/internal/apidocsgen/structs.go new file mode 100644 index 00000000..d18e519e --- /dev/null +++ b/internal/apidocsgen/structs.go @@ -0,0 +1,525 @@ +package main + +import ( + "fmt" + goast "go/ast" + goparser "go/parser" + gotoken "go/token" + "os" + "reflect" + "strings" + "time" + + "github.com/bluenviron/mediamtx/internal/conf" + "github.com/bluenviron/mediamtx/internal/defs" + "github.com/goccy/go-yaml" + "github.com/goccy/go-yaml/ast" + "github.com/google/uuid" +) + +var structs = []struct { + externalName string + typ reflect.Type +}{ + { + externalName: "AlwaysAvailableTrack", + typ: reflect.TypeOf(conf.AlwaysAvailableTrack{}), + }, + { + externalName: "AuthInternalUser", + typ: reflect.TypeOf(conf.AuthInternalUser{}), + }, + { + externalName: "AuthInternalUserPermission", + typ: reflect.TypeOf(conf.AuthInternalUserPermission{}), + }, + { + externalName: "Error", + typ: reflect.TypeOf(defs.APIError{}), + }, + { + externalName: "GlobalConf", + typ: reflect.TypeOf(conf.Conf{}), + }, + { + externalName: "HLSMuxer", + typ: reflect.TypeOf(defs.APIHLSMuxer{}), + }, + { + externalName: "HLSMuxerList", + typ: reflect.TypeOf(defs.APIHLSMuxerList{}), + }, + { + externalName: "HLSSession", + typ: reflect.TypeOf(defs.APIHLSSession{}), + }, + { + externalName: "HLSSessionList", + typ: reflect.TypeOf(defs.APIHLSSessionList{}), + }, + { + externalName: "Info", + typ: reflect.TypeOf(defs.APIInfo{}), + }, + { + externalName: "MoQSession", + typ: reflect.TypeOf(defs.APIMoQSession{}), + }, + { + externalName: "MoQSessionList", + typ: reflect.TypeOf(defs.APIMoQSessionList{}), + }, + { + externalName: "OK", + typ: reflect.TypeOf(defs.APIOK{}), + }, + { + externalName: "Path", + typ: reflect.TypeOf(defs.APIPath{}), + }, + { + externalName: "PathConf", + typ: reflect.TypeOf(conf.Path{}), + }, + { + externalName: "PathConfList", + typ: reflect.TypeOf(defs.APIPathConfList{}), + }, + { + externalName: "PathList", + typ: reflect.TypeOf(defs.APIPathList{}), + }, + { + externalName: "PathReader", + typ: reflect.TypeOf(defs.APIPathReader{}), + }, + { + externalName: "PathSource", + typ: reflect.TypeOf(defs.APIPathSource{}), + }, + { + externalName: "PathTrack", + typ: reflect.TypeOf(defs.APIPathTrack{}), + }, + { + externalName: "PathTrackCodecPropsAC3", + typ: reflect.TypeOf(defs.APIPathTrackCodecPropsAC3{}), + }, + { + externalName: "PathTrackCodecPropsAV1", + typ: reflect.TypeOf(defs.APIPathTrackCodecPropsAV1{}), + }, + { + externalName: "PathTrackCodecPropsG711", + typ: reflect.TypeOf(defs.APIPathTrackCodecPropsG711{}), + }, + { + externalName: "PathTrackCodecPropsH264", + typ: reflect.TypeOf(defs.APIPathTrackCodecPropsH264{}), + }, + { + externalName: "PathTrackCodecPropsH265", + typ: reflect.TypeOf(defs.APIPathTrackCodecPropsH265{}), + }, + { + externalName: "PathTrackCodecPropsLPCM", + typ: reflect.TypeOf(defs.APIPathTrackCodecPropsLPCM{}), + }, + { + externalName: "PathTrackCodecPropsMPEG4Audio", + typ: reflect.TypeOf(defs.APIPathTrackCodecPropsMPEG4Audio{}), + }, + { + externalName: "PathTrackCodecPropsOpus", + typ: reflect.TypeOf(defs.APIPathTrackCodecPropsOpus{}), + }, + { + externalName: "PathTrackCodecPropsVP9", + typ: reflect.TypeOf(defs.APIPathTrackCodecPropsVP9{}), + }, + { + externalName: "Recording", + typ: reflect.TypeOf(defs.APIRecording{}), + }, + { + externalName: "RecordingList", + typ: reflect.TypeOf(defs.APIRecordingList{}), + }, + { + externalName: "RecordingSegment", + typ: reflect.TypeOf(defs.APIRecordingSegment{}), + }, + { + externalName: "RTMPConn", + typ: reflect.TypeOf(defs.APIRTMPConn{}), + }, + { + externalName: "RTMPConnList", + typ: reflect.TypeOf(defs.APIRTMPConnList{}), + }, + { + externalName: "RTSPConn", + typ: reflect.TypeOf(defs.APIRTSPConn{}), + }, + { + externalName: "RTSPConnList", + typ: reflect.TypeOf(defs.APIRTSPConnsList{}), + }, + { + externalName: "RTSPSession", + typ: reflect.TypeOf(defs.APIRTSPSession{}), + }, + { + externalName: "RTSPSessionList", + typ: reflect.TypeOf(defs.APIRTSPSessionList{}), + }, + { + externalName: "SRTConn", + typ: reflect.TypeOf(defs.APISRTConn{}), + }, + { + externalName: "SRTConnList", + typ: reflect.TypeOf(defs.APISRTConnList{}), + }, + { + externalName: "WebRTCICEServer", + typ: reflect.TypeOf(conf.WebRTCICEServer{}), + }, + { + externalName: "WebRTCSession", + typ: reflect.TypeOf(defs.APIWebRTCSession{}), + }, + { + externalName: "WebRTCSessionList", + typ: reflect.TypeOf(defs.APIWebRTCSessionList{}), + }, +} + +const modulePathPrefix = "github.com/bluenviron/mediamtx/" + +func wrapRef(rt reflect.Type, p openAPIProperty) openAPIProperty { + if p.Ref == "" { + return p + } + + if isStructEnum(rt) { + p.Type = "string" + } else if rt.Kind() == reflect.Struct { + p.Type = "object" + } + + p.AllOf = []openAPIProperty{{Ref: p.Ref}} + p.Ref = "" + return p +} + +func goTypeToOpenAPI(rt reflect.Type) (openAPIProperty, error) { + if rt.Kind() == reflect.Pointer { + prop, err := goTypeToOpenAPI(rt.Elem()) + if err != nil { + return openAPIProperty{}, err + } + + prop = wrapRef(rt.Elem(), prop) + prop.Nullable = true + return prop, nil + } + + if isStructEnum(rt) { + return openAPIProperty{Ref: "#/components/schemas/" + schemaName(rt)}, nil + } + + if rt == reflect.TypeOf((*defs.APIPathTrackCodecProps)(nil)).Elem() { + return openAPIProperty{ + Type: "object", + AllOf: []openAPIProperty{{Ref: "#/components/schemas/" + schemaName(rt)}}, + Nullable: true, + }, nil + } + + switch { + case rt == reflect.TypeOf(uuid.UUID{}): + return openAPIProperty{Type: "string", Format: "uuid"}, nil + + case rt == reflect.TypeOf(time.Time{}): + return openAPIProperty{Type: "string"}, nil + + case rt == reflect.TypeOf(conf.Duration(0)): + return openAPIProperty{Type: "string"}, nil + + case rt == reflect.TypeOf(conf.IPNetwork{}): + return openAPIProperty{Type: "string"}, nil + + case rt == reflect.TypeOf(conf.StringSize(0)): + return openAPIProperty{Type: "string"}, nil + + case rt == reflect.TypeOf(conf.RTSPTransports{}): + items := openAPIProperty{Type: "string", Enum: []string{"udp", "multicast", "tcp"}} + return openAPIProperty{Type: "array", Items: &items}, nil + + case rt.Kind() == reflect.String: + return openAPIProperty{Type: "string"}, nil + + case rt.Kind() >= reflect.Int && rt.Kind() <= reflect.Int64: + return openAPIProperty{Type: "integer", Format: "int64"}, nil + + case rt.Kind() >= reflect.Uint && rt.Kind() <= reflect.Uint64: + return openAPIProperty{Type: "integer", Format: "uint64"}, nil + + case rt.Kind() == reflect.Float32 || rt.Kind() == reflect.Float64: + return openAPIProperty{Type: "number", Format: "double"}, nil + + case rt.Kind() == reflect.Bool: + return openAPIProperty{Type: "boolean"}, nil + + case rt.Kind() == reflect.Struct: + return openAPIProperty{Ref: "#/components/schemas/" + schemaName(rt)}, nil + + case rt.Kind() == reflect.Slice: + items, err := goTypeToOpenAPI(rt.Elem()) + if err != nil { + return openAPIProperty{}, err + } + + return openAPIProperty{Type: "array", Items: &items}, nil + + default: + return openAPIProperty{}, fmt.Errorf("unhandled type: %s", rt.String()) + } +} + +func schemaName(rt reflect.Type) string { + if rt == reflect.TypeOf(conf.Path{}) { + return "PathConf" + } + + if rt == reflect.TypeOf(defs.APIPathTrackCodec("")) { + return "PathTrackCodec" + } + + if rt == reflect.TypeOf((*defs.APIPathTrackCodecProps)(nil)).Elem() { + return "PathTrackCodecProps" + } + + return strings.TrimPrefix(rt.Name(), "API") +} + +func isStructEnum(rt reflect.Type) bool { + switch rt { + case reflect.TypeOf(defs.APIOKStatus("")): + return true + + case reflect.TypeOf(defs.APIErrorStatus("")): + return true + + case reflect.TypeOf(conf.AuthAction("")): + return true + + case reflect.TypeOf(conf.AlwaysAvailableTrackCodec("")): + return true + + case reflect.TypeOf(conf.AuthMethod("")): + return true + + case reflect.TypeOf(conf.Encryption("")): + return true + + case reflect.TypeOf(conf.HLSVariant(0)): + return true + + case reflect.TypeOf(conf.LogDestination(0)): + return true + + case reflect.TypeOf(conf.LogLevel(0)): + return true + + case reflect.TypeOf(conf.RecordFormat("")): + return true + + case reflect.TypeOf(conf.RTSPAuthMethod(0)): + return true + + case reflect.TypeOf(conf.RTSPRangeType("")): + return true + + case reflect.TypeOf(conf.RTSPTransport{}): + return true + + case reflect.TypeOf(defs.APIPathSourceType("")): + return true + + case reflect.TypeOf(defs.APIPathReaderType("")): + return true + + case reflect.TypeOf(defs.APIPathTrackCodec("")): + return true + + case reflect.TypeOf(defs.APIRTMPConnState("")): + return true + + case reflect.TypeOf(defs.APIRTSPSessionState("")): + return true + + case reflect.TypeOf(defs.APIWebRTCSessionState("")): + return true + + case reflect.TypeOf(defs.APIMoQSessionState("")): + return true + + case reflect.TypeOf(defs.APISRTConnState("")): + return true + } + + return false +} + +func extractStructDescriptions(rt reflect.Type) (map[string]string, error) { + pkgPath := rt.PkgPath() + dirPath, hasModulePrefix := strings.CutPrefix(pkgPath, modulePathPrefix) + if !hasModulePrefix { + return map[string]string{}, nil + } + + fset := gotoken.NewFileSet() + + entries, err := os.ReadDir(dirPath) + if err != nil { + return nil, err + } + + for _, entry := range entries { + if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { + continue + } + + filePath := dirPath + "/" + entry.Name() + file, parseErr := goparser.ParseFile(fset, filePath, nil, goparser.ParseComments) + if parseErr != nil { + return nil, parseErr + } + + for _, decl := range file.Decls { + genDecl, genOK := decl.(*goast.GenDecl) + if !genOK || genDecl.Tok != gotoken.TYPE { + continue + } + + for _, spec := range genDecl.Specs { + typeSpec, typeOK := spec.(*goast.TypeSpec) + if !typeOK || typeSpec.Name.Name != rt.Name() { + continue + } + + structType, structOK := typeSpec.Type.(*goast.StructType) + if !structOK { + continue + } + + descriptions := make(map[string]string) + for _, field := range structType.Fields.List { + if field.Tag == nil { + continue + } + + jsonTag := extractStructTag(field.Tag.Value, "json") + jsonName, _, _ := strings.Cut(jsonTag, ",") + if jsonName == "" || jsonName == "-" { + continue + } + + description := "" + if field.Doc != nil { + description = normalizeDescription(field.Doc.Text()) + } else if field.Comment != nil { + description = normalizeDescription(field.Comment.Text()) + } + + if description != "" { + descriptions[jsonName] = description + } + } + + return descriptions, nil + } + } + } + + return map[string]string{}, nil +} + +func extractStructTag(tagValue, key string) string { + tagValue = strings.Trim(tagValue, "`") + return reflect.StructTag(tagValue).Get(key) +} + +func normalizeDescription(description string) string { + return strings.Join(strings.Fields(description), " ") +} + +func generateStructSchema(rt reflect.Type, descriptions map[string]string) (openAPISchema, error) { + schema := openAPISchema{ + Type: "object", + Properties: make(map[string]openAPIProperty), + } + + for field := range rt.Fields() { + jsonTag := field.Tag.Get("json") + name, _, _ := strings.Cut(jsonTag, ",") + deprecated := field.Tag.Get("deprecated") == "true" + + if name == "" || name == "-" || name == "pathDefaults" || name == "paths" || + (strings.Contains(jsonTag, ",omitempty") && !deprecated) { + continue + } + + prop, err := goTypeToOpenAPI(field.Type) + if err != nil { + return openAPISchema{}, err + } + + prop.Deprecated = deprecated + if deprecated { + prop = wrapRef(field.Type, prop) + } + prop.Description = descriptions[name] + + schema.Properties[name] = prop + } + + return schema, nil +} + +func addStructs(astFile *ast.File) error { + for _, s := range structs { + descriptions, err := extractStructDescriptions(s.typ) + if err != nil { + return err + } + + schema, err := generateStructSchema(s.typ, descriptions) + if err != nil { + return err + } + + schemaNode, err := yaml.ValueToNode(map[string]openAPISchema{s.externalName: schema}) + if err != nil { + return err + } + + indentBlockSequences(schemaNode) + + addBlankLineBeforeNewSchemaEntry(schemaNode) + + schemasPath, err := yaml.PathString("$.components.schemas") + if err != nil { + return err + } + + err = schemasPath.MergeFromNode(astFile, schemaNode) + if err != nil { + return err + } + } + + return nil +} diff --git a/internal/defs/api_srt.go b/internal/defs/api_srt.go index 8531eaf6..1e3d8718 100644 --- a/internal/defs/api_srt.go +++ b/internal/defs/api_srt.go @@ -69,7 +69,7 @@ type APISRTConn struct { // The total accumulated time in microseconds, during which the SRT sender has some data to transmit, // including packets that have been sent, but not yet acknowledged UsSndDuration uint64 `json:"usSndDuration"` - // ?? + PacketsReceivedBelated uint64 `json:"packetsReceivedBelated"` // The total number of dropped by the SRT sender DATA packets that have no chance to be delivered in time PacketsSendDrop uint64 `json:"packetsSendDrop"` diff --git a/internal/linters/go2api/go2api_test.go b/internal/linters/go2api/go2api_test.go deleted file mode 100644 index e6b161ec..00000000 --- a/internal/linters/go2api/go2api_test.go +++ /dev/null @@ -1,560 +0,0 @@ -//go:build enable_linters - -package main - -import ( - "os" - "reflect" - "strings" - "testing" - "time" - - "github.com/bluenviron/mediamtx/internal/conf" - "github.com/bluenviron/mediamtx/internal/defs" - "github.com/goccy/go-yaml" - "github.com/google/uuid" - "github.com/stretchr/testify/require" -) - -type openAPIProperty struct { - Ref string `yaml:"$ref"` - Type string `yaml:"type"` - Format string `yaml:"format"` - AllOf []openAPIProperty `yaml:"allOf"` - Nullable bool `yaml:"nullable"` - Deprecated bool `yaml:"deprecated"` - Enum []string `yaml:"enum"` - Items *openAPIProperty `yaml:"items"` -} - -func wrapRef(rt reflect.Type, p openAPIProperty) openAPIProperty { - if p.Ref == "" { - return p - } - - if _, ok := goEnumToApi(rt); ok { - p.Type = "string" - } else if rt.Kind() == reflect.Struct { - p.Type = "object" - } - - p.AllOf = []openAPIProperty{{Ref: p.Ref}} - p.Ref = "" - return p -} - -type openAPISchema struct { - Type string `yaml:"type"` - Enum []string `yaml:"enum"` - OneOf []openAPIProperty `yaml:"oneOf"` - Properties map[string]openAPIProperty `yaml:"properties"` -} - -type openAPI struct { - Components struct { - Schemas map[string]openAPISchema `yaml:"schemas"` - } `yaml:"components"` -} - -func schemaName(rt reflect.Type) string { - name := strings.TrimPrefix(rt.Name(), "API") - - if rt.PkgPath() == "github.com/bluenviron/mediamtx/internal/conf" && name == "Path" { - return "PathConf" - } - - if rt == reflect.TypeOf(defs.APIPathTrackCodec("")) { - return "PathTrackCodec" - } - - return name -} - -func goStructToApi(t *testing.T, rt reflect.Type) openAPIProperty { - if rt.Kind() == reflect.Pointer { - prop := goStructToApi(t, rt.Elem()) - prop = wrapRef(rt.Elem(), prop) - prop.Nullable = true - return prop - } - - if _, ok := goEnumToApi(rt); ok { - return openAPIProperty{Ref: "#/components/schemas/" + schemaName(rt)} - } - - if rt == reflect.TypeOf((*defs.APIPathTrackCodecProps)(nil)).Elem() { - return openAPIProperty{ - Type: "object", - AllOf: []openAPIProperty{{Ref: "#/components/schemas/" + schemaName(rt)}}, - Nullable: true, - } - } - - switch { - case rt == reflect.TypeOf(""): - return openAPIProperty{Type: "string"} - - case rt == reflect.TypeOf(int(0)): - return openAPIProperty{Type: "integer", Format: "int64"} - - case rt == reflect.TypeOf(uint(0)): - return openAPIProperty{Type: "integer", Format: "uint64"} - - case rt == reflect.TypeOf(uint64(0)): - return openAPIProperty{Type: "integer", Format: "uint64"} - - case rt == reflect.TypeOf(float64(0)): - return openAPIProperty{Type: "number", Format: "double"} - - case rt == reflect.TypeOf(false): - return openAPIProperty{Type: "boolean"} - - case rt == reflect.TypeOf(uuid.UUID{}): - return openAPIProperty{Type: "string", Format: "uuid"} - - case rt == reflect.TypeOf(time.Time{}) || - rt == reflect.TypeOf(conf.Duration(0)) || - rt == reflect.TypeOf(conf.IPNetwork{}) || - rt == reflect.TypeOf(conf.Credential("")) || - rt == reflect.TypeOf(conf.StringSize(0)): - return openAPIProperty{Type: "string"} - - case rt == reflect.TypeOf(conf.RTSPTransports{}): - return openAPIProperty{ - Type: "array", - Items: &openAPIProperty{ - Type: "string", - Enum: []string{"udp", "multicast", "tcp"}, - }, - } - - case rt.Kind() == reflect.Struct: - return openAPIProperty{ - Ref: "#/components/schemas/" + schemaName(rt), - } - - case rt.Kind() == reflect.Slice: - items := goStructToApi(t, rt.Elem()) - return openAPIProperty{ - Type: "array", - Items: &items, - } - - default: - t.Errorf("unhandled type: %v", rt) - return openAPIProperty{} - } -} - -func goEnumToApi(rt reflect.Type) (openAPISchema, bool) { - switch rt { - case reflect.TypeOf(defs.APIOKStatus("")): - return openAPISchema{Type: "string", Enum: []string{"ok"}}, true - - case reflect.TypeOf(defs.APIErrorStatus("")): - return openAPISchema{Type: "string", Enum: []string{"error"}}, true - - case reflect.TypeOf(defs.APIPathSourceType("")): - return openAPISchema{Type: "string", Enum: []string{ - "hlsSource", - "redirect", - "rpiCameraSource", - "rtmpConn", - "rtmpsConn", - "rtmpSource", - "rtspSession", - "rtspSource", - "rtspsSession", - "srtConn", - "srtSource", - "mpegtsSource", - "rtpSource", - "webRTCSession", - "webRTCSource", - }}, true - - case reflect.TypeOf(defs.APIPathReaderType("")): - return openAPISchema{Type: "string", Enum: []string{ - "hlsSession", - "rtmpConn", - "rtmpsConn", - "rtspConn", - "rtspSession", - "rtspsConn", - "rtspsSession", - "srtConn", - "webRTCSession", - }}, true - - case reflect.TypeOf(defs.APIPathTrackCodec("")): - return openAPISchema{Type: "string", Enum: []string{ - "AV1", - "VP9", - "VP8", - "H265", - "H264", - "MPEG-4 Video", - "MPEG-1/2 Video", - "M-JPEG", - "Opus", - "Vorbis", - "MPEG-4 Audio", - "MPEG-4 Audio LATM", - "MPEG-1/2 Audio", - "AC3", - "Speex", - "G726", - "G722", - "G711", - "LPCM", - "MPEG-TS", - "KLV", - "Generic", - }}, true - - case reflect.TypeOf(conf.AlwaysAvailableTrackCodec("")): - return openAPISchema{Type: "string", Enum: []string{ - "AV1", - "VP9", - "H265", - "H264", - "MPEG4Audio", - "Opus", - "G711", - "LPCM", - }}, true - - case reflect.TypeOf(conf.AuthAction("")): - return openAPISchema{Type: "string", Enum: []string{ - "publish", - "read", - "playback", - "api", - "metrics", - "pprof", - }}, true - - case reflect.TypeOf(conf.AuthMethod("")): - return openAPISchema{Type: "string", Enum: []string{ - "internal", - "http", - "jwt", - }}, true - - case reflect.TypeOf(conf.Encryption("")): - return openAPISchema{Type: "string", Enum: []string{ - "no", - "optional", - "strict", - }}, true - - case reflect.TypeOf(conf.HLSVariant(0)): - return openAPISchema{Type: "string", Enum: []string{ - "mpegts", - "fmp4", - "lowLatency", - }}, true - - case reflect.TypeOf(conf.LogDestination(0)): - return openAPISchema{Type: "string", Enum: []string{ - "stdout", - "file", - "syslog", - }}, true - - case reflect.TypeOf(conf.LogLevel(0)): - return openAPISchema{Type: "string", Enum: []string{ - "error", - "warn", - "info", - "debug", - }}, true - - case reflect.TypeOf(conf.RecordFormat("")): - return openAPISchema{Type: "string", Enum: []string{ - "fmp4", - "mpegts", - }}, true - - case reflect.TypeOf(conf.RTSPAuthMethod(0)): - return openAPISchema{Type: "string", Enum: []string{ - "basic", - "digest", - }}, true - - case reflect.TypeOf(conf.RTSPRangeType("")): - return openAPISchema{Type: "string", Enum: []string{ - "", - "clock", - "npt", - "smpte", - }}, true - - case reflect.TypeOf(conf.RTSPTransport{}): - return openAPISchema{Type: "string", Enum: []string{ - "udp", - "multicast", - "tcp", - "automatic", - }}, true - - case reflect.TypeOf(defs.APIRTMPConnState("")): - return openAPISchema{Type: "string", Enum: []string{"idle", "read", "publish"}}, true - - case reflect.TypeOf(defs.APIWebRTCSessionState("")): - return openAPISchema{Type: "string", Enum: []string{"read", "publish"}}, true - - case reflect.TypeOf(defs.APISRTConnState("")): - return openAPISchema{Type: "string", Enum: []string{"idle", "read", "publish"}}, true - - case reflect.TypeOf(defs.APIRTSPSessionState("")): - return openAPISchema{Type: "string", Enum: []string{"idle", "read", "publish"}}, true - } - - return openAPISchema{}, false -} - -func TestGo2API(t *testing.T) { - byts, err := os.ReadFile("../../../api/openapi.yaml") - require.NoError(t, err) - - var doc openAPI - err = yaml.Unmarshal(byts, &doc) - require.NoError(t, err) - - t.Run("structs", func(t *testing.T) { - for _, ca := range []struct { - openAPIKey string - goStruct any - }{ - { - "AlwaysAvailableTrack", - conf.AlwaysAvailableTrack{}, - }, - { - "AuthInternalUser", - conf.AuthInternalUser{}, - }, - { - "AuthInternalUserPermission", - conf.AuthInternalUserPermission{}, - }, - { - "GlobalConf", - conf.Conf{}, - }, - { - "HLSMuxer", - defs.APIHLSMuxer{}, - }, - { - "HLSMuxerList", - defs.APIHLSMuxerList{}, - }, - { - "HLSSession", - defs.APIHLSSession{}, - }, - { - "HLSSessionList", - defs.APIHLSSessionList{}, - }, - { - "Info", - defs.APIInfo{}, - }, - { - "Path", - defs.APIPath{}, - }, - { - "PathConf", - conf.Path{}, - }, - { - "PathConfList", - defs.APIPathConfList{}, - }, - { - "PathList", - defs.APIPathList{}, - }, - { - "PathReader", - defs.APIPathReader{}, - }, - { - "PathSource", - defs.APIPathSource{}, - }, - { - "PathTrack", - defs.APIPathTrack{}, - }, - { - "PathTrackCodecPropsAV1", - defs.APIPathTrackCodecPropsAV1{}, - }, - { - "PathTrackCodecPropsVP9", - defs.APIPathTrackCodecPropsVP9{}, - }, - { - "PathTrackCodecPropsH265", - defs.APIPathTrackCodecPropsH265{}, - }, - { - "PathTrackCodecPropsH264", - defs.APIPathTrackCodecPropsH264{}, - }, - { - "PathTrackCodecPropsOpus", - defs.APIPathTrackCodecPropsOpus{}, - }, - { - "PathTrackCodecPropsMPEG4Audio", - defs.APIPathTrackCodecPropsMPEG4Audio{}, - }, - { - "PathTrackCodecPropsAC3", - defs.APIPathTrackCodecPropsAC3{}, - }, - { - "PathTrackCodecPropsG711", - defs.APIPathTrackCodecPropsG711{}, - }, - { - "PathTrackCodecPropsLPCM", - defs.APIPathTrackCodecPropsLPCM{}, - }, - { - "Recording", - defs.APIRecording{}, - }, - { - "RecordingList", - defs.APIRecordingList{}, - }, - { - "RecordingSegment", - defs.APIRecordingSegment{}, - }, - { - "RTMPConn", - defs.APIRTMPConn{}, - }, - { - "RTMPConnList", - defs.APIRTMPConnList{}, - }, - { - "RTSPConn", - defs.APIRTSPConn{}, - }, - { - "RTSPConnList", - defs.APIRTSPConnsList{}, - }, - { - "RTSPSession", - defs.APIRTSPSession{}, - }, - { - "RTSPSessionList", - defs.APIRTSPSessionList{}, - }, - { - "SRTConn", - defs.APISRTConn{}, - }, - { - "SRTConnList", - defs.APISRTConnList{}, - }, - { - "WebRTCSession", - defs.APIWebRTCSession{}, - }, - { - "WebRTCSessionList", - defs.APIWebRTCSessionList{}, - }, - } { - t.Run(ca.openAPIKey, func(t *testing.T) { - content1 := doc.Components.Schemas[ca.openAPIKey] - - content2 := openAPISchema{ - Type: "object", - Properties: make(map[string]openAPIProperty), - } - - ty := reflect.TypeOf(ca.goStruct) - - for i := range ty.NumField() { - sf := ty.Field(i) - js := sf.Tag.Get("json") - name, _, _ := strings.Cut(js, ",") - deprecated := sf.Tag.Get("deprecated") == "true" - - if name != "" && name != "-" && name != "paths" && name != "pathDefaults" && - (!strings.Contains(js, ",omitempty") || deprecated) { - prop := goStructToApi(t, sf.Type) - prop.Deprecated = deprecated - if deprecated { - prop = wrapRef(sf.Type, prop) - } - content2.Properties[name] = prop - } - } - - require.Equal(t, content2, content1) - }) - } - }) - - t.Run("oneOfs", func(t *testing.T) { - require.Equal(t, openAPISchema{OneOf: []openAPIProperty{ - {Ref: "#/components/schemas/PathTrackCodecPropsAV1"}, - {Ref: "#/components/schemas/PathTrackCodecPropsVP9"}, - {Ref: "#/components/schemas/PathTrackCodecPropsH265"}, - {Ref: "#/components/schemas/PathTrackCodecPropsH264"}, - {Ref: "#/components/schemas/PathTrackCodecPropsOpus"}, - {Ref: "#/components/schemas/PathTrackCodecPropsMPEG4Audio"}, - {Ref: "#/components/schemas/PathTrackCodecPropsAC3"}, - {Ref: "#/components/schemas/PathTrackCodecPropsG711"}, - {Ref: "#/components/schemas/PathTrackCodecPropsLPCM"}, - }}, doc.Components.Schemas["PathTrackCodecProps"]) - }) - - t.Run("enums", func(t *testing.T) { - for _, rt := range []reflect.Type{ - reflect.TypeOf(defs.APIOKStatus("")), - reflect.TypeOf(defs.APIErrorStatus("")), - reflect.TypeOf(defs.APIPathSourceType("")), - reflect.TypeOf(defs.APIPathReaderType("")), - reflect.TypeOf(defs.APIPathTrackCodec("")), - reflect.TypeOf(conf.AlwaysAvailableTrackCodec("")), - reflect.TypeOf(conf.AuthAction("")), - reflect.TypeOf(conf.AuthMethod("")), - reflect.TypeOf(conf.Encryption("")), - reflect.TypeOf(conf.HLSVariant(0)), - reflect.TypeOf(conf.LogDestination(0)), - reflect.TypeOf(conf.LogLevel(0)), - reflect.TypeOf(conf.RecordFormat("")), - reflect.TypeOf(conf.RTSPAuthMethod(0)), - reflect.TypeOf(conf.RTSPRangeType("")), - reflect.TypeOf(conf.RTSPTransport{}), - reflect.TypeOf(defs.APIRTMPConnState("")), - reflect.TypeOf(defs.APIRTSPSessionState("")), - reflect.TypeOf(defs.APISRTConnState("")), - reflect.TypeOf(defs.APIWebRTCSessionState("")), - } { - t.Run(rt.Name(), func(t *testing.T) { - content1 := doc.Components.Schemas[schemaName(rt)] - content2, ok := goEnumToApi(rt) - require.True(t, ok) - require.Equal(t, content2, content1) - }) - } - }) -} diff --git a/scripts/apidocs.mk b/scripts/apidocs.mk new file mode 100644 index 00000000..2c2604b5 --- /dev/null +++ b/scripts/apidocs.mk @@ -0,0 +1,2 @@ +apidocs: + go run ./internal/apidocsgen diff --git a/scripts/lint.mk b/scripts/lint.mk index 0212469b..b548b343 100644 --- a/scripts/lint.mk +++ b/scripts/lint.mk @@ -1,8 +1,8 @@ -define DOCKERFILE_API_DOCS_LINT +define DOCKERFILE_APIDOCS_LINT FROM $(NODE_IMAGE) RUN yarn global add @redocly/cli@1.0.0-beta.123 endef -export DOCKERFILE_API_DOCS_LINT +export DOCKERFILE_APIDOCS_LINT lint-go: docker run --rm -v "$(shell pwd):/app" -w /app \ @@ -15,17 +15,15 @@ lint-go-mod: lint-conf: go test -v -tags enable_linters ./internal/linters/conf -lint-go2api: - go test -v -tags enable_linters ./internal/linters/go2api - lint-docslinks: go test -v -tags enable_linters ./internal/linters/docslinks lint-docsorder: go test -v -tags enable_linters ./internal/linters/docsorder -lint-api-docs: - echo "$$DOCKERFILE_API_DOCS_LINT" | docker build . -f - -t temp +lint-apidocs: + go run ./internal/apidocsgen --check + echo "$$DOCKERFILE_APIDOCS_LINT" | docker build . -f - -t temp docker run --rm -v "$(shell pwd)/api:/s" -w /s temp \ sh -c "openapi lint openapi.yaml" @@ -34,4 +32,4 @@ lint-other: docker run --rm -v "$(shell pwd)/:/s" -w /s temp \ sh -c "prettier --check ." -lint: lint-go lint-go-mod lint-conf lint-go2api lint-docslinks lint-docsorder lint-api-docs lint-other +lint: lint-go lint-go-mod lint-conf lint-docslinks lint-docsorder lint-apidocs lint-other