Back to Blog
Apr 21, 2024

Create a website with multiple concurrent live streams

Written by Zack Schwartz


Using a combination of Raytha, Mux.com, and OBS or Zoom, you can create a website that has multiple and concurrent live streams going on simultaneously. Imagine you are running a live virtual event such as an annual conference, gaming event, or music festival, visitors to your website can hop in and out of different live streams.

Pre-requisites:
  • An API key from mux.com for live streaming capability
  • A Raytha website for website portion to stream live events
  • Either OBS, Zoom, or another platform that can stream to an RTMP endpoint with a stream key.

Update your data model in Raytha
First, ensure you have a Content Type in Raytha that you will use for live streaming. In my example I create a Content Type called a Session and ensure it has 2 extra fields for Playback ID and Stream Key.


Create a Raytha function
We need to connect Raytha to mux and we can do this with Raytha's Functions feature.


Below is the code example for my prototype. Be sure to replace "Basic ...' with your own base64 encoded id:secret from mux.com's access tokens.

    /** The following classes are available:
     * API_V1
     * CurrentOrganization
     * CurrentUser
     * Emailer
     * HttpClient
    */
    /** 
     * Receives a get request at /raytha/functions/execute/{developerName}
     * @param {IQueryCollection} query Passed in from .NET's Request.Query
     * @returns {object} of type JsonResult, HtmlResult, RedirectResult, or StatusCodeResult
     */
    function get(query) {
        let session_id = query.find(item => item.Key === "sessionId").Value[0];
        if (CurrentUser.IsAdmin) {
            try {
                let current_session = API_V1.GetContentItemById(session_id);
                let current_session_playback_id = host.newVar(System.Object);
                current_session.Result.PublishedContent.TryGetValue("playback_id", current_session_playback_id.out);
                if (current_session_playback_id.value.HasValue) {
                    return new StatusCodeResult(400, "Error: Live Stream Already Started");
                }
                let headers = {
                    'Authorization': 'Basic <replace me>'
                }
                let mux_payload = {
                    "playback_policy": ["public"],
                    "new_asset_settings": {
                        "playback_policy": ["public"]
                    }
                };
                let response = HttpClient.Post("https://api.mux.com/video/v1/live-streams", headers=headers, body=mux_payload);
                let result = JSON.parse(response);
                let playback_id = result.data.playback_ids[0].id;
                let stream_key = result.data.stream_key;
                let current_session_title = host.newVar(System.Object);
                current_session.Result.PublishedContent.TryGetValue("title", current_session_title.out);
                let current_session_content = host.newVar(System.Object);
                current_session.Result.PublishedContent.TryGetValue("content", current_session_content.out);
                
                let updated_session = {
                    'title': current_session_title.value.Value,
                    'content': current_session_content.value.Value,
                    'playback_id': playback_id,
                    'stream_key': stream_key
                };
                API_V1.EditContentItem(session_id, false, updated_session);
                return new RedirectResult(CurrentOrganization.WebsiteUrl + "/" + current_session.Result.RoutePath + "?stream_created=true");
            } catch (error) {
                return new StatusCodeResult(500, error.message);
            }
        }
        return new StatusCodeResult(401, "You must be an admin to start a live stream");
    }
    /** 
     * Receives a post request at /raytha/functions/execute/{developerName}
     * @param {IFormCollection} payload Passed in from .NET's Request.Form
     * @param {IQueryCollection} query Passed in from .NET's Request.Query
     * @returns {object} of type JsonResult, HtmlResult, RedirectResult, or StatusCodeResult
     */
    function post(payload, query) {
        return new JsonResult({ success: true });
        //example 1: return new HtmlResult("<p>Hello World</p>");
        //example 2: return new RedirectResult("https://raytha.com");
        //example 3: return new StatusCodeResult(404, "Not Found");
    }

If you have other field properties, you should update the code to reflect that as well.

Update the template
The final piece is to update the template code so that an admin can start a live stream and then visitors can watch it. This is my content_item_detail_view.

<div class="container">
  <div class="d-flex justify-content-center">
    <div class="col-lg-8">{{ QueryParams }}

      {% if QueryParams['stream_created'] %}
        <div class="alert alert-success" role="alert">
          Stream key created: {{ Target.PublishedContent.stream_key }}
        </div>
      {% endif %}
      <h3>{{ Target.PrimaryField }}</h3>
      {{ Target.PublishedContent.content.Text }}

      {% if Target.PublishedContent.stream_key.HasValue %}
        <div class="row">
          <mux-player
            playback-id="{{ Target.PublishedContent.playback_id }}"
            metadata-video-title="{{ Target.PrimaryField }}"
          ></mux-player>
        </div>

        {% if CurrentUser.IsAdmin %}
          <div class="row">
            <div class="col-12">
              <p>RTMP Server Url: rtmps://global-live.mux.com:443/app</p>
              <p>RTMP Stream Key: {{ Target.PublishedContent.stream_key }}
            </div>
          </div>
        {% endif %}
      {% else %}
        {% if CurrentUser.IsAdmin %}
          <div class="row">
            <a class="btn btn-primary" href="/raytha/functions/execute/create_live_stream?sessionId={{ Target.Id }}">Create Live Stream</a>
          </div>
        {% endif %}
      {% endif %}
    </div>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/@mux/mux-player"></script>

If you are logged in as an administrator, you would see this:



You would then be able to copy the url and stream key into OBS or Zoom.

Start streaming!

picture of the author
Zack Schwartz @apexdodge

ENTREPRENEUR & SOFTWARE ENGINEER, AUSTIN, TX
I enjoy tackling a wide array of business challenges ranging from front line product support and operations to sales and marketing efforts. My core expertise is in software development building enterprise level web applications.


Subscribe to the Raytha newsletter