Closed Jman012 closed 2 years ago
This also affected livestreams.
1.2.3-1 is being pushed to Test Flight soon.
Apologies for the surprise change there!
As you've figured, the qualityLevelParams
dictionary is meant to hold arbitrary keys which resource URI templates may then reference.
We found requiring clients to perform the necessary parsing and replacement for delivery resource URIs to be a source of unwanted complexity. As such, in an effort to simplify client implementations (along with certain backend concerns), we have revamped the way delivery is managed.
Because of this, the concept of qualityLevelParams
no longer exists internally, outside of a single unit which is responsible for downgrading new v3 resources to v2. This involves parsing arbitrary sets of v3 URIs on-the-fly and attempting to produce a valid v2 template given said input.
Any and every v3 resource must be automatically downgrade-able to v2. Unfortunately, it is impossible to guarantee this while constrained to only {token}
and {qualityLevels}
which it appears a number of 3rd party apps have assumed as the only possible keys during template replacement.
I'd highly recommend switching over to delivery-info v3 if possible. I'm not able to provide in-depth details at the moment (it's quite a lot to prepare public docs for), though I can nudge you towards the right path a bit:
outputKind: ('hls.mpegts' | 'hls.fmp4' | 'dash.mpegts' | 'dash.m4s' | 'flat')[]
to ensure the right vehicle is used for your client, e.g. outputKind=hls.fmp4 is optimal for tvOS 10+.entityId
is a Livestream#id
(no longer is the creator ID used).You may safely assume:
groups
array may consist of zero or more elements.group#origins
array may consist of one or more elements. It will never be empty if present.
variant#origins
set).url
field. This URL is absolute and may contain a trailing slash.queryUrl
which clients can use to test connection speeds against. This URL is absolute and may contain a trailing slash.group#variants
array may consist of zero or more elements.
name
and label
fields.url
field. This URL may be absolute or relative and may contain a trailing slash.
variant#origins
group#origins
new URL(
variant.url,
pickBestOrigin(variant.origins || group.origins || [])?.url || 'https://www.floatplane.com'
);
origins
field which substitutes any group#origins
. It will never be empty if present.order
field. Orders may not be consecutive (expect holes, values up to 253 – 1).enabled
, hidden
, meta
and/or mimeType
fields.
hidden
is truthy as these may be unstable / used internally for various purposes.meta.common.access.deniedReason: 'isMissingPermission' | 'isProcessing' | 'isBroken'
and meta.common.access.deniedMessage: string
.url
will point to an appropriate page where the user may, e.g., subscribe or view information about why a video might have broken / failed to process.enabled
field will always be truthy if the user is entitled to consume the relevant content. Otherwise, assume the content is inaccessible.Given the constraints of v3, any deviation from the above will be considered a bug, so please do let us know if you encounter anything bizarre.
We're aware and would like to make 3rd party interactions with the API more pleasant. This is however pending other tasks in the pipeline. Many thanks for your patience!
I appreciate this very, very much Rua-Yuki! This is quite detailed and will do us well when implementing it. Such details about optional fields can be quite important for consumption. I don't happen to see a common
field under meta
in any examples currently, but it's nice to know that may exist. I'll be sure to include those details.
I will formalize all of this into https://github.com/Jman012/FloatplaneAPI/ in the coming days.
We all appreciate this a lot!
Cc @bmlzootown, @Inrixia.
I don't happen to see a
common
field undermeta
in any examples currently, but it's nice to know that may exist. I'll be sure to include those details.
Sorry, the example links posted above only go as far as 1080p. The relevant fields should appear if you try, e.g., a 4K video given a 1080p-subbed user.
Consider meta
to look something like so:
// Related types referenced by metadata.
// ═══════════════════════════════════════════════════════
enum DeliveryResourceAccessDenialReason {
/**
* Indicates that the requester is lacking a required plan or other form of permission
* entitling on to access the corresponding resource.
*/
IsMissingPermission = 'isMissingPermission',
/**
* Indicates that the corresponding resource is processing. Clients may choose to
* periodically refetch an asset's info when it has reported this state.
*/
IsProcessing = 'isProcessing',
/**
* Indicates that the corresponding resource is defective in some manner which has
* rendered it currently inaccessible. It is possible that the asset will be repaired
* at some later point in time. Clients may choose to periodically refetch an asset's
* info when it has reported this state.
*/
IsBroken = 'isBroken',
}
enum DeliveryResourceLowLatencyLiveExtension {
/**
* 🍎-backed low-latency HLS extension.
*/
LLHls = 'llhls',
/**
* Community-backed low-latency HLS extension.
*/
CLHls = 'clhls',
/**
* IVS custom low-latency HLS extension.
*/
IvsHls = 'ivshls',
/**
* DASH-IF-backed low-Latency DASH extension.
*/
LLDash = 'lldash',
}
// Common characteristics shared by metadata components.
// ═══════════════════════════════════════════════════════
interface MediaBitrateInfo {
/**
* Maximum bitrate observed for the data stream.
*/
maximum: number;
/**
* Average bitrate observed for the data stream.
*/
average: number;
}
interface MediaIdentityCharacteristics {
/**
* RFC 6381 codec string indicating stream data chunk format.
*/
codec?: string;
/**
* RFC 6381 codec string indicating stream format on the most basic level, without the
* addition of profile/level/etc. information.
*/
codecSimple?: string;
/**
* MIME-type for individual stream data chunks (as opposed to a containing playlist).
*/
mimeType?: string;
}
interface ImagePresentationCharacteristics {
/**
* Count of horizontal pixels presented.
*/
width?: number;
/**
* Count of vertical pixels presented.
*/
height?: number;
/**
* Whether or not this data stream carries HDR content.
*/
isHdr?: boolean;
}
// Components of metadata.
// ═══════════════════════════════════════════════════════
/**
* Basic information which generally applies to most resource types.
*/
interface MetadataCommonInfo {
/**
* Size of the corresponding media file, measured in bytes.
*/
size?: number;
/**
* Contains clarifying information regarding resource access rules.
*
* This is often (but not always) set when a variant is not enabled.
*/
access?: {
/**
* Indicator describing the reason access has been witheld for a resource.
*/
deniedReason?: DeliveryResourceAccessDenialReason;
/**
* Message describing in human-readable terms why access has been witheld for a
* resource.
*/
deniedMessage?: string;
}
}
/**
* Metadata specific to still-image streams carried by resources. This generally applies
* only to pure picture content but may see use for embedded thumbnails/etc. in video/audio
* files.
*/
type MetadataImageInfo = MediaIdentityCharacteristics & ImagePresentationCharacteristics;
/**
* Metadata specific to audio streams carried by resources.
*/
interface MetadataAudioInfo extends MediaIdentityCharacteristics {
/**
* Count of channels carried by the audio stream.
*/
channelCount?: number;
/**
* Count of samples recorded per second.
*/
samplerate?: number;
/**
* Bitrate information for the carried media data stream.
* @todo (this is partial only temporarily; average bitrate isn't held for all media)
*/
bitrate?: Partial<MediaBitrateInfo>;
}
/**
* Metadata specific to video streams carried by resources.
*/
interface MetadataVideoInfo extends MediaIdentityCharacteristics, ImagePresentationCharacteristics {
/**
* Maximum count of frames presented per second for the video.
*/
fps?: number;
/**
* Bitrate information for the carried media data stream.
* @todo (this is partial only temporarily; average bitrate isn't held for all media)
*/
bitrate?: Partial<MediaBitrateInfo>;
}
/**
* Metadata specific to livestream resources.
*/
interface MetadataLiveInfo {
/**
* Indicates the low-latency extension, if any, which the resource leverages.
*/
lowLatencyExtension?: DeliveryResourceLowLatencyLiveExtension;
}
// Combined resource metadata as served.
// ═══════════════════════════════════════════════════════
interface DeliveryResourceMetadata {
common?: MetadataCommonInfo;
video?: MetadataVideoInfo;
audio?: MetadataAudioInfo;
image?: MetadataImageInfo;
live?: MetadataLiveInfo;
}
Thank you so much for the TS types! That helped create a full meta
object. I've implemented this in the unofficial API spec at https://github.com/Jman012/FloatplaneAPI/commit/f8aba6842526e7aee6ec986da1769975b2c24bac. I'll integrate this into Wasserflug soon to make sure it works well.
Using the new Delivery API will be included in 1.3.0-1.
CDN API endpoint changes.
Summary of changes: