> ## Documentation Index
> Fetch the complete documentation index at: https://gcore-doc-2189.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# HLS, MPEG-DASH, and MP4

export const MethodSection = ({children}) => children ?? null;

export const MethodSwitch = ({children}) => {
  const tabs = React.Children.toArray(children).map(c => {
    if (!c || !c.props) return null;
    if (c.props.id) return c;
    const inner = c.props.children;
    if (inner && inner.props && inner.props.id) return inner;
    return null;
  }).filter(Boolean);
  const firstId = tabs.length > 0 ? tabs[0].props.id : "";
  const [active, setActive] = React.useState(firstId);
  React.useEffect(() => {
    try {
      const saved = localStorage.getItem("gcore_docs_method");
      if (saved && tabs.find(t => t.props.id === saved)) {
        setActive(saved);
      }
    } catch (_) {}
  }, []);
  React.useEffect(() => {
    try {
      document.querySelectorAll("h2[id], h3[id]").forEach(heading => {
        const visible = heading.offsetParent !== null;
        document.querySelectorAll(`a[href="#${heading.id}"]`).forEach(link => {
          if (link.closest("h1,h2,h3,h4,h5,h6")) return;
          const li = link.closest("li");
          if (li) li.style.display = visible ? "" : "none";
        });
      });
    } catch (_) {}
    window.dispatchEvent(new Event("scroll"));
  }, [active]);
  const handleClick = id => {
    setActive(id);
    try {
      localStorage.setItem("gcore_docs_method", id);
    } catch (_) {}
  };
  return <div>
      <div className="not-prose flex gap-0 border-b border-zinc-200 dark:border-zinc-800 mb-8 mt-2" role="tablist">
        {tabs.map(tab => {
    const isActive = active === tab.props.id;
    return <button key={tab.props.id} role="tab" aria-selected={isActive} onClick={() => handleClick(tab.props.id)} className={["px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors cursor-pointer", isActive ? "border-primary text-primary" : "border-transparent text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"].join(" ")}>
              {tab.props.label}
            </button>;
  })}
      </div>

      {tabs.map(tab => <div key={tab.props.id} style={{
    display: active === tab.props.id ? "" : "none"
  }}>
          {tab.props.children}
        </div>)}
    </div>;
};

<MethodSwitch>
  <MethodSection id="portal" label="Customer Portal">
    <p>Gcore Video Streaming allows you to deliver video-on-demand (VOD) content optimized for different devices and platforms. We offer multiple video formats as part of the service, so you're not limited by the technical constraints of a single playback option and can choose a format that best fits your use case and functional requirements. </p>

    <p>We offer the following formats for playback: </p>

    * **HLS MPEG-TS** : A default VOD delivery to all devices. Has codecs limitations. For instance, it supports H264 video codec up to High Profile [level 4.1](https://en.wikipedia.org/wiki/Advanced_Video_Coding#Levels) but using newer codecs like H265 and AV1 isn't possible.

    * **HLS CMAF** : A common format supported by all modern devices (e.g., iOS 10 and later). Supports a fragmented MP4 (fMP4) format of video chunks that allows using a wide range of modern technologies such as new H265 and AV1 codecs.

    * **MPEG-DASH** : A popular video delivery format with the advantages of a universal single manifest. It provides buffer-based (BOLA) and hybrid (dynamic) bitrate adaptation algorithms and natively supports all modern codecs. Most preferred on Android and Windows platforms.

    * **MP4** : A format for downloading videos as a single file in an MP4 container. Used when a user doesn't just want to watch a video but download it locally, for example, for editing.

    <p>You can also find a description of supported video formats and relevant use cases in our [API documentation](/api-reference/streaming/videos/get-video). </p>

    ## HLS TS and CMAF formats

    <p>The HTTP Live Streaming (HLS) format is the most common format for delivering videos online. This format allows you to:</p>

    * **Use adaptive bitrate**. This provides viewers with video content at a quality level determined by the speed of their internet connection.
    * **Slice into chunks**. Video content is chunked into segments from 2 to 6 seconds and cached for fast delivery worldwide using CDNs.
    * **Protect content from unauthorized use**. HLS supports AES-128 encryption and is compatible with major DRM (Digital Rights Management) systems. This ensures secure delivery and controlled access to video content.

    <p>The `hls_url` contains a URL to the manifest (.m3u8) that tells players the order in which they should play the chunks into which the video is divided. </p>

    <p>The `hls_cmaf_url` contains a URL to the master playlist HLS (master-cmaf.m3u8) with CMAF-based chunks. The chunks are in the fMP4 container, which is codec-agnostic and allows the use of anything like H264, H265, or AV1. </p>

    <p>Considering the existing limitations of the old HLS, it's important to understand how the manifest and chunks will be formed. Chunk type is selected automatically according to the used type of video codec: </p>

    * master.m3u8:

      * **MPEG-TS** if the video was encoded in H264 only.
      * **CMAF** if the video was encoded in additional HEVC and AV1. CMAF is used because Apple devices don't support HEVC/AV1 over MPEG TS and aren't standardized in TS-container.
    * master-cmaf.m3u8 will always contain CMAF chunks for any video codec.

    <p>Here is the full possible format of the link to get the master manifest:</p>

    ```sh theme={null}
    https://domain.com/videos/{client_id}_{video_slug}/master[-cmaf][-min-N][-max-N][-img][-(h264|hevc|av1)].m3u8
    ```

    <p>Where:</p>

    * <code>{"{client_id}"}</code>: Your account ID.
    * <code>{"{video_slug}"}</code>: Slug identifier of the video.
    * `[-cmaf]`: The HLS CMAF version of the manifest. For details, check the hls\_cmaf\_url field description in our [API docs](/api-reference/streaming/videos/get-list-of-videos).
    * `[-min-N]`: Special suffix that specifies the lowest video quality limit available in an Adaptive Bitrate (ABR) streaming. Learn more about the setting in the [Limit quality](/streaming/video-hosting/hls-and-mp4#limit-the-lowest-and-highest-quality-in-hls-adaptive-bitrate) section.
    * `[-max-N]`: Special suffix that specifies the highest video quality limit available in the ABR streaming. Learn more about the setting in the [Limit quality](/streaming/video-hosting/hls-and-mp4#limit-the-lowest-and-highest-quality-in-hls-adaptive-bitrate) section.
    * `[-img]`: The trick play feature for Roku devices allows thumbnail images to be embedded directly into the manifest. Read more in the article [Timeline hover previews: use with players and Roku devices](/streaming/video-hosting/timeline-hover-previews-use-in-players-and-roku-devices#roku-trick-play).
    * `[-(h264|hevc|av1)]`: Video codec soft limitation. Applicable if the video was simultaneously transcoded into multiple codecs (264, H265, and AV1) and you want to return just one video codec in a manifest.

    <p>For example, check out the following manifest links for a master playlist: </p>

    * The link to the master playlist: [https://demo-public.gvideo.io/videos/2675\_FnlHXwA16ZMxmUr/master.m3u8](https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/master.m3u8)
    * The link to the master playlist with CMAF-based chunks: [https://demo-public.gvideo.io/videos/2675\_FnlHXwA16ZMxmUr/master-cmaf.m3u8](https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/master-cmaf.m3u8)

    ## DASH format

    <p>Dynamic Adaptive Streaming over HTTP (DASH) is the industry standard video format that's widely adopted by streaming services and supported on most non-Apple devices and browsers. </p>

    <p>This format allows you to: </p>

    * **Use adaptive bitrate**. You can stream videos of different quality and switch in the middle of a video from one quality level to another.

    * **Support for multiple DRM systems**. DASH is compatible with various Digital Rights Management (DRM) systems, allowing for the secure distribution of media across different platforms.

    * **Deliver content encoded in different formats**. DASH is designed to work with a wide range of audio and video codecs, which ensures seamless playback across different devices and platforms.

    <p>Video content delivered in DASH format contains a manifest file (.mpd). This file informs players about the video structure (chunks and segments), available quality levels and bitrates, and the order and conditions under which each video chunk should be played. </p>

    <p>For DASH, chunks will also differ depending on the type of video codec that was used to encode the quality: </p>

    * CMAF chunks for H264 and H265 video codecs
    * WebM chunks for AV1 video

    <p>Example of the WebM output: </p>

    ```xml theme={null}
    ... 
        <AdaptationSet 
            id="1" 
            segmentAlignment="true" 
            maxWidth="3840" 
            maxHeight="2160" 
            maxFrameRate="45000/751"> 
            <SegmentTemplate 
                timescale="1000" 
                media="dash-segment-$Number$-$RepresentationID$.webm" 
                initialization="dash-init-$RepresentationID$.webm" 
                startNumber="1"> 
    ... 
            </SegmentTemplate> 
          <Representation 
              id="f1-v1-x3" 
              mimeType="video/webm" 
              codecs="av01.0.13M.08" 
              width="3840" 
              height="2160" 
              frameRate="45000/751" 
              sar="1:1" 
              startWithSAP="1"  
    ```

    <p>Here is the full possible format of the link to get the master manifest:</p>

    ```sh theme={null}
    https://domain.com/videos/{client_id}_{video_slug}/master[-(h264|hevc|av1)].mpd
    ```

    <p>Where:</p>

    * <code>{"{client_id}"}</code>: Your account ID.
    * <code>{"{video_slug}"}</code>: Slug identifier of the video.
    * `[-min-N]`: Special suffix that specifies the lowest video quality limit available in an Adaptive Bitrate (ABR) streaming (same behavior as for HLS). Learn more about the setting in the Limit quality section.
    * `[-max-N]`: Special suffix that specifies the highest video quality limit available in the ABR streaming (same behavior as for HLS). Learn more about the setting in the Limit quality section.
    * `[-(h264|hevc|av1)]`: Video codec soft limitation. Applicable if the video was simultaneously transcoded into multiple codecs (264, H265, and AV1) and you want to return just one video codec in a manifest.

    <p>Example of the manifest link:  [https://demo-public.gvideo.io/videos/2675\_FnlHXwA16ZMxmUr/master.mpd](https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/master.mpd)</p>

    ## MP4 format

    ### MP4 as a source

    <p>Video can be streamed and downloaded using the .mp4 file format when you want to handle the whole file at once instead of in chunks. MP4 format allows users to:</p>

    * Watch videos offline on mobile devices
    * Download video files for local modification
    * Play videos even on very outdated devices
    * Share videos on social networks and other streaming services
    * Content can be protected with [Secure Token](/streaming/extra-features/protect-your-content-with-temporary-links-and-secure-tokens#how-to-enable-the-secure-token-feature). DRM protection isn't supported.

    <p>When using the MP4 format, users can work with each available video resolution separately. For example, select the video quality version (720p) and download only that version: [https://demo-public.gvideo.io/videos/2675\_FnlHXwA16ZMxmUr/qid3570v3\_h264\_1566\_720.mp4](https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/qid3570v3_h264_1566_720.mp4)</p>

    <p>Example usage:</p>

    ```html theme={null}
    <video width="100%" height="auto" autoplay="autoplay" loop="true" playsinline="" oncanplay="this.play()">
         <source type="video/mp4" src="https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/qid3570v3_h264_1566_720.mp4"/>
    </video>
    ```

    <p>Link to the MP4 file contains information about the encoding method using format: </p>

    ```
    <quality_version>_<codec>_<bitrate>_<height>.mp4
    ```

    <p>Where: </p>

    * `<quality_version>` – Internal quality identifier and file version
    * `<codec>` – Codec name that was used to encode the video, or audio codec if it is an audio-only file
    * `<bitrate>` – Encoding bitrate in Kbps
    * `<height>` – Video height, or word "audio" if it is an audio-only file

    <p>Example: <code>/videos/{"{client_id}"}\_{"{slug}"}/qid3567v1\_h264\_4050\_1080.mp4</code></p>

    <Info>
      Avoid storing or relying on direct MP4 file links. These URLs are internal and dynamically generated, and their structure may change at any time without prior notice.
    </Info>

    ### MP4 custom download

    <p>Gcore supports regular download requests for entire MP4 file downloads. For each converted video, additional download endpoints are available from API. </p>
    <p>An MP4 download enpoints look like:</p>

    1. <code>/videos/{"{client_id}"}\_{"{slug}"}/{"{filename}"}.mp4</code>
    2. <code>/videos/{"{client_id}"}\_{"{slug}"}/{"{filename}"}.mp4/download</code>
    3. <code>/videos/{"{client_id}"}\_{"{slug}"}/{"{filename}"}.mp4/download={"{custom_filename}"}</code>

    <p>The first option returns the file as is.</p>
    <p>Response will be:</p>

    ```sh theme={null}
    GET /qid3576v3_h264_450_360.mp4
    ...
    content-type: video/mp4
    ```

    <p>The second option with `/download` will respond with HTTP response header that directly tells browsers to download the file instead of playing it in the browser:</p>

    ```
    GET /qid3576v3_h264_450_360.mp4/download
    ...
    content-type: video/mp4
    content-disposition: attachment
    access-control-expose-headers: Content-Disposition
    ```

    <p>The third option with <code>/download={"{custom_filename}"}</code> allows you to set a custom name for the file being downloaded. </p>
    <p>Filename constraints:</p>

    * Length: 1-255 characters
    * Must NOT include the .mp4 extension (it is added automatically)
    * Allowed characters: a-z, A-Z, 0-9, \_(underscore), -(dash), .(dot)
    * First character cannot be .(dot)
    * Example valid filenames: `holiday2025`, `_backup.final`, `clip-v1.2`

    ```
    GET /qid3576v3_h264_450_360.mp4/download={custom_filename}
    ...
    content-type: video/mp4
    content-disposition: attachment; filename="{custom_filename}.mp4"
    access-control-expose-headers: Content-Disposition                    
    ```

    <p>Example:</p>

    * MP4 as is: `https://demo-public.gvideo.io/videos/2675_1OFgHZ1FWZNNvx1A/qid3567v1_h264_4050_1080.mp4/download`
    * MP4 with custom download filename "`highlights_v1.1_2025-05-30`.mp4": `https://demo-public.gvideo.io/videos/2675_1OFgHZ1FWZNNvx1A/qid3567v1_h264_4050_1080.mp4/download=highlights_v1.1_2025-05-30`

    ### MP4 dynamic speed limiting

    <p>This mode sets download limits for requests. To activate limits use special attributes in in a query string. </p>
    <p>The `speed` sets the maximum download speed, while the buffer sets how much data will be transferred without the speed limit. The buffer argument is optional.</p>

    <p>Example with limit download speed to 500KB/s after downloading 10MB.</p>

    ```
    curl -L -o /dev/null -w "%{speed_download}\n" \
    "https://demo-public.gvideo.io/videos/2675_1OFgHZ1FWZNNvx1A/qid3576v1_h264_450_360.mp4?speed=500K&buffer=10M" \           
    | awk '{printf "Download speed: %.2f KB/s (%.2f MB/s)\n", $1/1024, $1/1048576}'

      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100 14.4M  100 14.4M    0     0   501k      0  0:00:29  0:00:29 --:--:--  467k

    Download speed: 501.86 KB/s (0.49 MB/s)
    ```

    ### MP4 advanced secure token protection

    <p>A regular secure token protects the entire video entity, so it allows to play HLS/DASH ABR securely.</p>

    <p>When you need to distribute MP4 files separatelly from ABR use an advanced secure token provided as an additional query parameter. This ensures that only authorized requests can access a given MP4 version and prevents unauthorized distribution across qualities.</p>
    <p>MP4 advanced secure token can be combined with MP4 speed limit.</p>

    <p>Example:</p>

    ```
    ???
    ```

    <p>Read more on the page [Protected temporary link](/streaming/extra-features/protect-your-content-with-temporary-links-and-secure-tokens#advanced-secure-token-for-mp4).</p>

    ### MP4 range request

    <p>We also support range requests specifying the HTTP "Range" header.</p>

    ```sh theme={null}
    curl -I 'https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/qid3570v3_h264_1566_720.mp4' -H 'range: bytes=4000000-' 

    HTTP/2 206 
    content-type: video/mp4
    content-length: 38878900
    content-range: bytes 4000000-42878899/42878900
    ...
    ```

    <Tip>
      **Tip**

      You can protect MP4 files with the [Video protection via the Security Tokens feature](/streaming/extra-features/protect-your-content-with-temporary-links-and-secure-tokens).
    </Tip>

    ## Get HLS, DASH, and MP4 links

    <p>1. In the [Gcore Customer Portal](https://portal.gcore.com/streaming/video/list), navigate to **Streaming** > **Video Hosting**.</p>

    <p>2. Open the video for which you want to export the link.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore-doc-2189/duxwAFZJ2KxkHgb4/images/docs/streaming-platform/video-hosting/hls-and-mp4/export-video-links.png?fit=max&auto=format&n=duxwAFZJ2KxkHgb4&q=85&s=3df74bb7a00a073bb9a7200725dadc01" alt="Gcore Customer Portal" width="7304" height="4064" data-path="images/docs/streaming-platform/video-hosting/hls-and-mp4/export-video-links.png" />
    </Frame>

    <p>3. In the **Links for the export** section, copy the relevant link:</p>

    * **Video page URL**: A Web player URL to access and play the video directly from a website.
    * **HLS**: A URL to a master playlist HLS (master.m3u8) with MPEGTS container.
    * **iFrame embed code**: A URL to the HTML video player with the video inside. It can be inserted into an iframe on your website and the video will automatically play in all browsers.

    ## Limit the lowest and highest quality in HLS adaptive bitrate

    <p>The manifest file (.m3u8) for HLS and HLS CMAF, and manifest (.mpd) for MPEG-DASH streaming contains all possible video qualities by default. If you want to limit the quality for old devices, non-paying users, or a quick view, you can specify a suffix indicating the minimum and/or maximum allowed resolution:</p>

    * `-min-N`: minimum resolution not less than specified
    * `-max-N`: maximum resolution not exceeding than specified

    <p>`N` is a resolution (height or width) from 240 to 9999. If the video is horizontally oriented, the resolution is its height. If it's vertically oriented, it should be width. </p>

    <p>Here's an example of how to get two qualities only—360 and 480—instead of the full set for HLS (the same can be done for MPEG-DASH manifest .mpd): </p>

    ```sh theme={null}
    curl https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/master-min-360-max-480-img.m3u8 
    ```

    <p>The response is: </p>

    ```
    #EXTM3U
    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=928000,RESOLUTION=1148x480,FRAME-RATE=24.000,CODECS="avc1.4d401f,mp4a.40.2",VIDEO-RANGE=SDR
    index-svod480n-v1-a1.m3u8
    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=514000,RESOLUTION=860x360,FRAME-RATE=24.000,CODECS="avc1.42c01e,mp4a.40.2",VIDEO-RANGE=SDR
    index-svod360n-v1-a1.m3u8

    #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=71839,RESOLUTION=1148x480,CODECS="avc1.4d401f",URI="iframes-svod480n-v1-a1.m3u8",VIDEO-RANGE=SDR
    #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=35296,RESOLUTION=860x360,CODECS="avc1.42c01e",URI="iframes-svod360n-v1-a1.m3u8",VIDEO-RANGE=SDR
    ```
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>Retrieve all playback URLs for a video with a single API call. The response includes <code>hls\_url</code>, <code>hls\_cmaf\_url</code>, <code>dash\_url</code>, and per-quality <code>mp4\_url</code> values in the <code>converted\_videos</code> array.</p>

    <Info>
      An [API token](/account-settings/api-tokens) is required.
    </Info>

    <p>Open a terminal and export the required variables:</p>

    ```bash theme={null}
    export GCORE_API_KEY="{YOUR_API_KEY}"
    export VIDEO_ID="{YOUR_VIDEO_ID}"
    ```

    ## Get playback URLs

    <p>Retrieve the video object to access all playback URLs — HLS, HLS CMAF, MPEG-DASH, and per-quality MP4.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        from gcore import Gcore

        client = Gcore()

        video_id = int(os.environ["VIDEO_ID"])
        video = client.streaming.videos.get(video_id)

        print(video.hls_url)       # HLS MPEG-TS master playlist
        print(video.hls_cmaf_url)  # HLS CMAF master playlist
        print(video.dash_url)      # MPEG-DASH manifest

        for quality in video.converted_videos:
            print(quality.name, quality.mp4_url)
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"
            "strconv"

            gcore "github.com/G-Core/gcore-go"
        )

        func main() {
            client := gcore.NewClient()
            ctx := context.Background()

            videoID, _ := strconv.ParseInt(os.Getenv("VIDEO_ID"), 10, 64)
            video, err := client.Streaming.Videos.Get(ctx, videoID)
            if err != nil {
                panic(err)
            }

            fmt.Println(video.HlsURL)      // HLS MPEG-TS master playlist
            fmt.Println(video.HlsCmafURL)  // HLS CMAF master playlist
            fmt.Println(video.DashURL)     // MPEG-DASH manifest

            for _, quality := range video.ConvertedVideos {
                fmt.Println(quality.Name, quality.MP4URL)
            }
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl "https://api.gcore.com/streaming/videos/$VIDEO_ID" \
          -H "Authorization: APIKey $GCORE_API_KEY"
        ```

        <p>The API returns:</p>

        ```json theme={null}
        {
          "id": 123456,
          "name": "My video",
          "hls_url": "https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/master.m3u8",
          "hls_cmaf_url": "https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/master-cmaf.m3u8",
          "dash_url": "https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/master.mpd",
          "iframe_url": "https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr",
          "converted_videos": [
            {
              "name": "vod720n",
              "width": 1720,
              "height": 720,
              "mp4_url": "https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/qid3570v3_h264_1566_720.mp4"
            },
            {
              "name": "vod480n",
              "width": 1146,
              "height": 480,
              "mp4_url": "https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/qid3573v3_h264_800_468.mp4"
            }
          ]
        }
        ```
      </Tab>
    </Tabs>

    ## Limit HLS and DASH quality range

    <p>Append `-min-N` and/or `-max-N` suffixes to the `hls_url` or `dash_url` to restrict the quality levels served in the manifest. `N` is a resolution in pixels (height for landscape video, width for portrait), from 240 to 9999.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        from gcore import Gcore

        client = Gcore()

        video_id = int(os.environ["VIDEO_ID"])
        video = client.streaming.videos.get(video_id)

        # Serve only 360p and 480p — insert suffixes before the file extension
        base = video.hls_url.replace("master.m3u8", "master-min-360-max-480.m3u8")
        print(base)
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"
            "strconv"
            "strings"

            gcore "github.com/G-Core/gcore-go"
        )

        func main() {
            client := gcore.NewClient()
            ctx := context.Background()

            videoID, _ := strconv.ParseInt(os.Getenv("VIDEO_ID"), 10, 64)
            video, err := client.Streaming.Videos.Get(ctx, videoID)
            if err != nil {
                panic(err)
            }

            // Serve only 360p and 480p
            base := strings.Replace(video.HlsURL, "master.m3u8", "master-min-360-max-480.m3u8", 1)
            fmt.Println(base)
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        # Replace master.m3u8 with master-min-360-max-480.m3u8 in the hls_url value
        curl "https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/master-min-360-max-480.m3u8"
        ```

        <p>The manifest contains only the 360p and 480p quality levels:</p>

        ```
        #EXTM3U
        #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=928000,RESOLUTION=1148x480,...
        index-svod480n-v1-a1.m3u8
        #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=514000,RESOLUTION=860x360,...
        index-svod360n-v1-a1.m3u8
        ```
      </Tab>
    </Tabs>

    ## Download an MP4 file

    <p>Each quality in `converted_videos` has an `mp4_url`. Append `/download` to trigger a browser download instead of inline playback, or append `/download={"{filename}"}` to set a custom filename.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        from gcore import Gcore

        client = Gcore()

        video_id = int(os.environ["VIDEO_ID"])
        video = client.streaming.videos.get(video_id)

        # Get the 720p quality and build the download URL
        quality = next((q for q in video.converted_videos if "720" in q.name), None)
        if quality:
            download_url = quality.mp4_url + "/download=my-video-720p"
            print(download_url)
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"
            "strconv"
            "strings"

            gcore "github.com/G-Core/gcore-go"
        )

        func main() {
            client := gcore.NewClient()
            ctx := context.Background()

            videoID, _ := strconv.ParseInt(os.Getenv("VIDEO_ID"), 10, 64)
            video, err := client.Streaming.Videos.Get(ctx, videoID)
            if err != nil {
                panic(err)
            }

            for _, quality := range video.ConvertedVideos {
                if strings.Contains(quality.Name, "720") {
                    fmt.Println(quality.MP4URL + "/download=my-video-720p")
                    break
                }
            }
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        # Download the file with a custom filename
        curl -L -o my-video-720p.mp4 \
          "https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/qid3570v3_h264_1566_720.mp4/download=my-video-720p"
        ```

        <p>The response includes `Content-Disposition: attachment; filename="my-video-720p.mp4"` which prompts browsers to save the file.</p>
      </Tab>
    </Tabs>

    ## Limit MP4 download speed

    <p>Append `?speed=N` to an `mp4_url` to cap the transfer rate. Add `&buffer=M` to allow a burst of `M` bytes before the limit applies. Use `K` for kilobytes and `M` for megabytes.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        from gcore import Gcore

        client = Gcore()

        video_id = int(os.environ["VIDEO_ID"])
        video = client.streaming.videos.get(video_id)

        # 500 KB/s after an initial 10 MB burst
        quality = video.converted_videos[0] if video.converted_videos else None
        if quality:
            print(quality.mp4_url + "?speed=500K&buffer=10M")
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"
            "strconv"

            gcore "github.com/G-Core/gcore-go"
        )

        func main() {
            client := gcore.NewClient()
            ctx := context.Background()

            videoID, _ := strconv.ParseInt(os.Getenv("VIDEO_ID"), 10, 64)
            video, err := client.Streaming.Videos.Get(ctx, videoID)
            if err != nil {
                panic(err)
            }

            if len(video.ConvertedVideos) > 0 {
                fmt.Println(video.ConvertedVideos[0].MP4URL + "?speed=500K&buffer=10M")
            }
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        # Download capped at 500 KB/s with a 10 MB initial burst
        curl -L -o /dev/null -w "%{speed_download}\n" \
          "https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/qid3570v3_h264_1566_720.mp4?speed=500K&buffer=10M"
        ```
      </Tab>
    </Tabs>

    ## Request an MP4 byte range

    <p>Use the HTTP `Range` header to fetch a specific byte range of an MP4 file — useful for resumable downloads or seeking.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        import urllib.request
        from gcore import Gcore

        client = Gcore()

        video_id = int(os.environ["VIDEO_ID"])
        video = client.streaming.videos.get(video_id)

        quality = video.converted_videos[0] if video.converted_videos else None
        if quality:
            req = urllib.request.Request(quality.mp4_url)
            req.add_header("Range", "bytes=0-1023")
            with urllib.request.urlopen(req) as resp:
                print(resp.status, resp.headers["Content-Range"])
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "net/http"
            "os"
            "strconv"

            gcore "github.com/G-Core/gcore-go"
        )

        func main() {
            client := gcore.NewClient()
            ctx := context.Background()

            videoID, _ := strconv.ParseInt(os.Getenv("VIDEO_ID"), 10, 64)
            video, err := client.Streaming.Videos.Get(ctx, videoID)
            if err != nil {
                panic(err)
            }

            if len(video.ConvertedVideos) == 0 {
                return
            }
            req, _ := http.NewRequest("GET", video.ConvertedVideos[0].MP4URL, nil)
            req.Header.Set("Range", "bytes=0-1023")
            resp, err := http.DefaultClient.Do(req)
            if err != nil {
                panic(err)
            }
            defer resp.Body.Close()
            fmt.Println(resp.Status, resp.Header.Get("Content-Range"))
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -I \
          -H "Range: bytes=4000000-" \
          "https://demo-public.gvideo.io/videos/2675_FnlHXwA16ZMxmUr/qid3570v3_h264_1566_720.mp4"
        ```

        <p>The server responds with HTTP 206 Partial Content:</p>

        ```
        HTTP/2 206
        content-type: video/mp4
        content-length: 38878900
        content-range: bytes 4000000-42878899/42878900
        ```
      </Tab>
    </Tabs>
  </MethodSection>
</MethodSwitch>
