Skip to content

Instantly share code, notes, and snippets.

@kylehowells
Last active February 6, 2024 10:36
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kylehowells/74f538c766a244a3666319860a937030 to your computer and use it in GitHub Desktop.
Save kylehowells/74f538c766a244a3666319860a937030 to your computer and use it in GitHub Desktop.

Getting a streamable link is done by the client.

You basically need to send the service a detailed list of the type of media you can play, and it will give you a transcode URL to call if it thinks you need transcoding in order to play this file.

If you don't and can play the file directly it won't give you a transcode URL.

POST <server_address>/Items/<item_id>/PlaybackInfo?UserId=<current_user_id>

Request body

{
  "DeviceProfile": {
    "MaxStreamingBitrate": 120000000,
    "MaxStaticBitrate": 100000000,
    "MusicStreamingTranscodingBitrate": 384000,
    "DirectPlayProfiles": [
      {
        "Container": "webm",
        "Type": "Video",
        "VideoCodec": "vp8,vp9",
        "AudioCodec": "vorbis"
      },
      {
        "Container": "mp4,m4v",
        "Type": "Video",
        "VideoCodec": "h264,vp8,vp9",
        "AudioCodec": "aac,mp3,ac3,eac3,flac,alac,vorbis"
      },
      {
        "Container": "mov",
        "Type": "Video",
        "VideoCodec": "h264",
        "AudioCodec": "aac,mp3,ac3,eac3,flac,alac,vorbis"
      },
      {
        "Container": "mp3",
        "Type": "Audio"
      },
      {
        "Container": "aac",
        "Type": "Audio"
      },
      {
        "Container": "m4a",
        "AudioCodec": "aac",
        "Type": "Audio"
      },
      {
        "Container": "m4b",
        "AudioCodec": "aac",
        "Type": "Audio"
      },
      {
        "Container": "flac",
        "Type": "Audio"
      },
      {
        "Container": "alac",
        "Type": "Audio"
      },
      {
        "Container": "m4a",
        "AudioCodec": "alac",
        "Type": "Audio"
      },
      {
        "Container": "m4b",
        "AudioCodec": "alac",
        "Type": "Audio"
      },
      {
        "Container": "webma",
        "Type": "Audio"
      },
      {
        "Container": "webm",
        "AudioCodec": "webma",
        "Type": "Audio"
      },
      {
        "Container": "wav",
        "Type": "Audio"
      }
    ],
    "TranscodingProfiles": [
      {
        "Container": "aac",
        "Type": "Audio",
        "AudioCodec": "aac",
        "Context": "Streaming",
        "Protocol": "hls",
        "MaxAudioChannels": "6",
        "MinSegments": "2",
        "BreakOnNonKeyFrames": true
      },
      {
        "Container": "aac",
        "Type": "Audio",
        "AudioCodec": "aac",
        "Context": "Streaming",
        "Protocol": "http",
        "MaxAudioChannels": "6"
      },
      {
        "Container": "mp3",
        "Type": "Audio",
        "AudioCodec": "mp3",
        "Context": "Streaming",
        "Protocol": "http",
        "MaxAudioChannels": "6"
      },
      {
        "Container": "wav",
        "Type": "Audio",
        "AudioCodec": "wav",
        "Context": "Streaming",
        "Protocol": "http",
        "MaxAudioChannels": "6"
      },
      {
        "Container": "mp3",
        "Type": "Audio",
        "AudioCodec": "mp3",
        "Context": "Static",
        "Protocol": "http",
        "MaxAudioChannels": "6"
      },
      {
        "Container": "aac",
        "Type": "Audio",
        "AudioCodec": "aac",
        "Context": "Static",
        "Protocol": "http",
        "MaxAudioChannels": "6"
      },
      {
        "Container": "wav",
        "Type": "Audio",
        "AudioCodec": "wav",
        "Context": "Static",
        "Protocol": "http",
        "MaxAudioChannels": "6"
      },
      {
        "Container": "ts",
        "Type": "Video",
        "AudioCodec": "aac,mp3,ac3,eac3",
        "VideoCodec": "h264",
        "Context": "Streaming",
        "Protocol": "hls",
        "MaxAudioChannels": "6",
        "MinSegments": "2",
        "BreakOnNonKeyFrames": true
      },
      {
        "Container": "webm",
        "Type": "Video",
        "AudioCodec": "vorbis",
        "VideoCodec": "vpx",
        "Context": "Streaming",
        "Protocol": "http",
        "MaxAudioChannels": "6"
      },
      {
        "Container": "mp4",
        "Type": "Video",
        "AudioCodec": "aac,mp3,ac3,eac3,flac,alac,vorbis",
        "VideoCodec": "h264",
        "Context": "Static",
        "Protocol": "http"
      }
    ],
    "ContainerProfiles": [],
    "CodecProfiles": [
      {
        "Type": "Video",
        "Codec": "h264",
        "Conditions": [
          {
            "Condition": "NotEquals",
            "Property": "IsAnamorphic",
            "Value": "true",
            "IsRequired": false
          },
          {
            "Condition": "EqualsAny",
            "Property": "VideoProfile",
            "Value": "high|main|baseline|constrained baseline",
            "IsRequired": false
          },
          {
            "Condition": "LessThanEqual",
            "Property": "VideoLevel",
            "Value": "51",
            "IsRequired": false
          },
          {
            "Condition": "NotEquals",
            "Property": "IsInterlaced",
            "Value": "true",
            "IsRequired": false
          }
        ]
      },
      {
        "Type": "Video",
        "Codec": "hevc",
        "Conditions": [
          {
            "Condition": "NotEquals",
            "Property": "IsAnamorphic",
            "Value": "true",
            "IsRequired": false
          },
          {
            "Condition": "EqualsAny",
            "Property": "VideoProfile",
            "Value": "main|main 10",
            "IsRequired": false
          },
          {
            "Condition": "LessThanEqual",
            "Property": "VideoLevel",
            "Value": "183",
            "IsRequired": false
          },
          {
            "Condition": "NotEquals",
            "Property": "IsInterlaced",
            "Value": "true",
            "IsRequired": false
          }
        ]
      }
    ],
    "SubtitleProfiles": [
      {
        "Format": "vtt",
        "Method": "External"
      },
      {
        "Format": "ass",
        "Method": "External"
      },
      {
        "Format": "ssa",
        "Method": "External"
      }
    ],
    "ResponseProfiles": [
      {
        "Type": "Video",
        "Container": "m4v",
        "MimeType": "video/mp4"
      }
    ]
  }
}

That's the profile the web UI uses for the Safari (MP4's basically).

In return the server will give you something like this (remember the user might have multiple versions of the Movie (DVD vs Bluray, extended editions, directors cut, etc....), this list will contain info for each file):

{
  "MediaSources": [
    {
      "Protocol": "File",
      "Id": "bf22ec0d983592dda98b24346afa7735",
      "Type": "Default",
      "Container": "webm",
      "Size": 2188750848,
      "Name": "Camp Rock (2008)",
      "IsRemote": false,
      "ETag": "95706793fc3b7f3b84b35d6fbfd071d3",
      "RunTimeTicks": 56743358464,
      "SupportsTranscoding": true,
      "VideoType": "VideoFile",
      "MediaStreams": [
        {
          "Codec": "h264",
          "Height": 576,
          "Width": 720,
          "AverageFrameRate": 25,
          "RealFrameRate": 25,
          "Type": "Video",
          "AspectRatio": "16:9",
        },
        {
          "Codec": "ac3",
          "Language": "eng",
          "DisplayTitle": "En - Dolby Digital - 5.1 - Default",
          "Channels": 6,
          "SampleRate": 48000,
          "Type": "Audio"
        },
        {
          "Codec": "aac",
          "Language": "eng",
          "DisplayTitle": "En - AAC - Stereo",
          "BitRate": 192000,
          "Channels": 2,
          "Type": "Audio",
          "Index": 2
        },
        {
          "Codec": "ac3",
          "Language": "dan",
          "BitRate": 384000,
          "Channels": 6,
          "Type": "Audio"
        },
        {
          "Codec": "srt",
          "IsDefault": false,
          "IsForced": false,
          "Type": "Subtitle",
          "IsExternal": true,
          "DeliveryMethod": "External",
          "DeliveryUrl": "/Videos/<uuid>/Subtitles/5/0/Stream.vtt?api_key=....",
        }
      ],
      "MediaAttachments": [],
      "Formats": [],
      "Bitrate": 4237824,
      "RequiredHttpHeaders": {},
      "TranscodingUrl": "/videos/<item_id>/master.m3u8?<long chain of generated config options>",
      "TranscodingSubProtocol": "hls",
      "TranscodingContainer": "ts",
      "DefaultAudioStreamIndex": 1
    }
  ],
  "PlaySessionId": "fe2f0d6ba2e24b419f61c07683ce915b"
}

If the video is an MP4 you can just stream and play directly you won't get a TranscodingUrl option in the response.

That case you just use this URL:

<server_address>/Videos/\(mediaSource.Id)/stream.mp4
@kylehowells
Copy link
Author

Example request exported from Paw for axios.

axios({
	"method": "POST",
	"url": "http://<server_ip>:8096/Items/<item_id>/PlaybackInfo",
	"params": {
		"UserId": "<login_response[User.Id]>"
	},
	"headers": {
		"x-mediabrowser-token": "<login_response[AccessToken]>",
		"Content-Type": "application/json; charset=utf-8"
	},
	"data": {
		"DeviceProfile": {
			"CodecProfiles": [
				{
					"Codec": "h264",
					"Type": "Video",
					"Conditions": [
						{
							"IsRequired": false,
							"Value": "true",
							"Condition": "NotEquals",
							"Property": "IsAnamorphic"
						},
						{
							"IsRequired": false,
							"Value": "high|main|baseline|constrained baseline",
							"Condition": "EqualsAny",
							"Property": "VideoProfile"
						},
						{
							"IsRequired": false,
							"Value": "51",
							"Condition": "LessThanEqual",
							"Property": "VideoLevel"
						},
						{
							"IsRequired": false,
							"Value": "true",
							"Condition": "NotEquals",
							"Property": "IsInterlaced"
						}
					]
				},
				{
					"Codec": "hevc",
					"Type": "Video",
					"Conditions": [
						{
							"IsRequired": false,
							"Value": "true",
							"Condition": "NotEquals",
							"Property": "IsAnamorphic"
						},
						{
							"IsRequired": false,
							"Value": "main|main 10",
							"Condition": "EqualsAny",
							"Property": "VideoProfile"
						},
						{
							"IsRequired": false,
							"Value": "183",
							"Condition": "LessThanEqual",
							"Property": "VideoLevel"
						},
						{
							"IsRequired": false,
							"Value": "true",
							"Condition": "NotEquals",
							"Property": "IsInterlaced"
						}
					]
				}
			],
			"SubtitleProfiles": [
				{
					"Method": "External",
					"Format": "vtt"
				},
				{
					"Method": "External",
					"Format": "ass"
				},
				{
					"Method": "External",
					"Format": "ssa"
				}
			],
			"MaxStreamingBitrate": 120000000,
			"MusicStreamingTranscodingBitrate": 384000,
			"DirectPlayProfiles": [
				{
					"Container": "webm",
					"Type": "Video",
					"VideoCodec": "vp8,vp9",
					"AudioCodec": "vorbis"
				},
				{
					"Container": "mp4,m4v",
					"Type": "Video",
					"VideoCodec": "h264,vp8,vp9",
					"AudioCodec": "aac,mp3,ac3,eac3,flac,alac,vorbis"
				},
				{
					"Container": "mov",
					"Type": "Video",
					"VideoCodec": "h264",
					"AudioCodec": "aac,mp3,ac3,eac3,flac,alac,vorbis"
				},
				{
					"Container": "mp3",
					"Type": "Audio"
				},
				{
					"Container": "aac",
					"Type": "Audio"
				},
				{
					"AudioCodec": "aac",
					"Container": "m4a",
					"Type": "Audio"
				},
				{
					"AudioCodec": "aac",
					"Container": "m4b",
					"Type": "Audio"
				},
				{
					"Container": "flac",
					"Type": "Audio"
				},
				{
					"Container": "alac",
					"Type": "Audio"
				},
				{
					"AudioCodec": "alac",
					"Container": "m4a",
					"Type": "Audio"
				},
				{
					"AudioCodec": "alac",
					"Container": "m4b",
					"Type": "Audio"
				},
				{
					"Container": "webma",
					"Type": "Audio"
				},
				{
					"AudioCodec": "webma",
					"Container": "webm",
					"Type": "Audio"
				},
				{
					"Container": "wav",
					"Type": "Audio"
				}
			],
			"TranscodingProfiles": [
				{
					"MinSegments": "2",
					"AudioCodec": "aac",
					"BreakOnNonKeyFrames": true,
					"Type": "Audio",
					"Protocol": "hls",
					"MaxAudioChannels": "6",
					"Container": "aac",
					"Context": "Streaming"
				},
				{
					"Container": "aac",
					"Context": "Streaming",
					"Type": "Audio",
					"Protocol": "http",
					"AudioCodec": "aac",
					"MaxAudioChannels": "6"
				},
				{
					"Container": "mp3",
					"Context": "Streaming",
					"Type": "Audio",
					"Protocol": "http",
					"AudioCodec": "mp3",
					"MaxAudioChannels": "6"
				},
				{
					"Container": "wav",
					"Context": "Streaming",
					"Type": "Audio",
					"Protocol": "http",
					"AudioCodec": "wav",
					"MaxAudioChannels": "6"
				},
				{
					"Container": "mp3",
					"Context": "Static",
					"Type": "Audio",
					"Protocol": "http",
					"AudioCodec": "mp3",
					"MaxAudioChannels": "6"
				},
				{
					"Container": "aac",
					"Context": "Static",
					"Type": "Audio",
					"Protocol": "http",
					"AudioCodec": "aac",
					"MaxAudioChannels": "6"
				},
				{
					"Container": "wav",
					"Context": "Static",
					"Type": "Audio",
					"Protocol": "http",
					"AudioCodec": "wav",
					"MaxAudioChannels": "6"
				},
				{
					"MinSegments": "2",
					"AudioCodec": "aac,mp3,ac3,eac3",
					"VideoCodec": "h264",
					"BreakOnNonKeyFrames": true,
					"Type": "Video",
					"Protocol": "hls",
					"MaxAudioChannels": "6",
					"Container": "ts",
					"Context": "Streaming"
				},
				{
					"AudioCodec": "vorbis",
					"VideoCodec": "vpx",
					"Type": "Video",
					"Protocol": "http",
					"MaxAudioChannels": "6",
					"Container": "webm",
					"Context": "Streaming"
				},
				{
					"Container": "mp4",
					"Context": "Static",
					"Type": "Video",
					"VideoCodec": "h264",
					"AudioCodec": "aac,mp3,ac3,eac3,flac,alac,vorbis",
					"Protocol": "http"
				}
			],
			"ContainerProfiles": [],
			"ResponseProfiles": [
				{
					"MimeType": "video/mp4",
					"Type": "Video",
					"Container": "m4v"
				}
			],
			"MaxStaticBitrate": 100000000
		}
	}
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment