diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 2049e27e..6f637d19 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -145,7 +145,16 @@ const QHash WSRequestHandler::messageMap { { "StartOutput", &WSRequestHandler::StartOutput }, { "StopOutput", &WSRequestHandler::StopOutput }, - { "PlayPauseMedia", &WSRequestHandler::PlayPauseMedia } + { "PlayPauseMedia", &WSRequestHandler::PlayPauseMedia }, + { "RestartMedia", &WSRequestHandler::RestartMedia }, + { "StopMedia", &WSRequestHandler::StopMedia }, + { "NextMedia", &WSRequestHandler::NextMedia }, + { "PreviousMedia", &WSRequestHandler::PreviousMedia }, + { "GetMediaDuration", &WSRequestHandler::GetMediaDuration }, + { "GetMediaTime", &WSRequestHandler::GetMediaTime }, + { "SetMediaTime", &WSRequestHandler::SetMediaTime }, + { "ScrubMedia", &WSRequestHandler::ScrubMedia }, + { "GetMediaState", &WSRequestHandler::GetMediaState } }; const QSet WSRequestHandler::authNotRequired { diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index cf79f214..6b82eb0d 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -164,4 +164,13 @@ class WSRequestHandler { RpcResponse StopOutput(const RpcRequest&); RpcResponse PlayPauseMedia(const RpcRequest&); + RpcResponse RestartMedia(const RpcRequest&); + RpcResponse StopMedia(const RpcRequest&); + RpcResponse NextMedia(const RpcRequest&); + RpcResponse PreviousMedia(const RpcRequest&); + RpcResponse GetMediaDuration(const RpcRequest&); + RpcResponse GetMediaTime(const RpcRequest&); + RpcResponse SetMediaTime(const RpcRequest&); + RpcResponse ScrubMedia(const RpcRequest&); + RpcResponse GetMediaState(const RpcRequest&); }; diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp index 019e5f7e..0298e533 100644 --- a/src/WSRequestHandler_MediaControl.cpp +++ b/src/WSRequestHandler_MediaControl.cpp @@ -33,3 +33,315 @@ RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) { return request.success(); } +/** +* Restart a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @api requests +* @name RestartMedia +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::RestartMedia(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + obs_source_media_restart(source); + return request.success(); +} + +/** +* Stop a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @api requests +* @name StopMedia +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::StopMedia(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + obs_source_media_stop(source); + return request.success(); +} + +/** +* Skip to the next media item in the playlist. Supports only vlc media source (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @api requests +* @name NextMedia +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::NextMedia(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + obs_source_media_next(source); + return request.success(); +} + +/** +* Go to the previous media item in the playlist. Supports only vlc media source (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @api requests +* @name PreviousMedia +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::PreviousMedia(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + obs_source_media_previous(source); + return request.success(); +} + +/** +* Get the length of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* Note: For some reason, for the first 5 or so seconds that the media is playing, the total duration can be off by upwards of 50ms. +* +* @param {String} `sourceName` Source name. +* +* @return {int} `mediaDuration` The total length of media in milliseconds.. +* +* @api requests +* @name GetMediaDuration +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_int(response, "timeStamp", obs_source_media_get_duration(source)); + return request.success(response); +} + +/** +* Get the current timestamp of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @return {int} `timeStamp` The time in milliseconds since the start of the media. +* +* @api requests +* @name GetMediaTime +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_int(response, "timeStamp", obs_source_media_get_time(source)); + return request.success(response); +} + +/** +* Set the timestamp of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* @param {int} `timeStamp` Milliseconds to set the timestamp to. +* +* @api requests +* @name SetMediaTime +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) { + if (!request.hasField("sourceName") || !request.hasField("timeStamp")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + int64_t timeStamp = (int64_t)obs_data_get_int(request.parameters(), "timeStamp"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + obs_source_media_set_time(source, timeStamp); + return request.success(); +} + +/** +* Scrub media using a supplied offset. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* Note: Due to processing/network delays, this request is not perfect. The processing rate of this request has also not been tested. +* +* @param {String} `sourceName` Source name. +* @param {int} `timeOffset` Millisecond offset (positive or negative) to offset the current media position. +* +* @api requests +* @name ScrubMedia +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) { + if (!request.hasField("sourceName") || !request.hasField("timeOffset")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + int64_t timeOffset = (int64_t)obs_data_get_int(request.parameters(), "timeOffset"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + int64_t newTime = obs_source_media_get_time(source) + timeOffset; + if (newTime < 0) { + newTime = 0; + } + + obs_source_media_set_time(source, newTime); + return request.success(); +} + +/** +* Get the current playing state of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @return {String} `mediaState` The media state of the provided source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error` +* +* @api requests +* @name GetMediaState +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + QString mediaState; + enum obs_media_state mstate = obs_source_media_get_state(source); + switch (mstate) { + case OBS_MEDIA_STATE_NONE: + mediaState = "none"; + break; + case OBS_MEDIA_STATE_PLAYING: + mediaState = "playing"; + break; + case OBS_MEDIA_STATE_OPENING: + mediaState = "opening"; + break; + case OBS_MEDIA_STATE_BUFFERING: + mediaState = "buffering"; + break; + case OBS_MEDIA_STATE_PAUSED: + mediaState = "paused"; + break; + case OBS_MEDIA_STATE_STOPPED: + mediaState = "stopped"; + break; + case OBS_MEDIA_STATE_ENDED: + mediaState = "ended"; + break; + case OBS_MEDIA_STATE_ERROR: + mediaState = "error"; + break; + default: + mediaState = "unknown"; + } + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_string(response, "mediaState", mediaState.toUtf8()); + + return request.success(response); +}