Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/BuildAndTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
jobs:
build:

runs-on: macos-13
runs-on: macos-15

steps:

Expand All @@ -32,7 +32,7 @@ jobs:
run: swift package clean

- name: Build project
run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild build-for-testing -destination 'name=iPhone 14 Pro' -scheme 'Bunny-Package' -skipPackagePluginValidation | xcpretty
run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild build-for-testing -destination 'name=iPhone 16 Pro' -scheme 'Bunny-Package' -skipPackagePluginValidation | xcpretty

- name: Run tests
run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild test-without-building -destination 'name=iPhone 14 Pro' -scheme 'Bunny-Package' -skipPackagePluginValidation | xcpretty
run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild test-without-building -destination 'name=iPhone 16 Pro' -scheme 'Bunny-Package' -skipPackagePluginValidation | xcpretty
16 changes: 14 additions & 2 deletions Sources/BunnyStreamPlayer/Player/BunnyStreamPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ public struct BunnyStreamPlayer: View {
let videoId: String
/// The ID of the video library.
let libraryId: Int
/// The embed view token. Required when token authentication is enabled for the video library.
let token: String?
/// The expiration timestamp for the embed view token.
let expires: Int64?

/// The loading state of the video player.
@State private var loadingState: VideoLoadingState = .loading
Expand Down Expand Up @@ -53,6 +57,8 @@ public struct BunnyStreamPlayer: View {
/// - accessKey: The access key for authentication. Can be `nil` for public videos.
/// - videoId: The unique ID of the video to be played.
/// - libraryId: The ID of the video library.
/// - token: The embed view token. Required when token authentication is enabled for the video library.
/// - expires: The expiration timestamp for the embed view token.
/// - playerIcons: Optional custom icons for the video player.
///
/// ### Usage Example:
Expand All @@ -61,7 +67,9 @@ public struct BunnyStreamPlayer: View {
/// var body: some View {
/// BunnyStreamPlayer(accessKey: "your_access_key",
/// videoId: "your_video_id",
/// libraryId: 123)
/// libraryId: 123,
/// token: "cdn_token",
/// expires: 1234567890)
/// .navigationBarTitle(Text("Video Player"), displayMode: .inline)
/// }
/// }
Expand All @@ -70,11 +78,15 @@ public struct BunnyStreamPlayer: View {
accessKey: String?,
videoId: String,
libraryId: Int,
token: String? = nil,
expires: Int64? = nil,
playerIcons: PlayerIcons? = nil
) {
self.accessKey = accessKey
self.videoId = videoId
self.libraryId = libraryId
self.token = token
self.expires = expires
if let accessKey {
self.heatmapLoader = HeatmapLoader(bunnyStreamAPI: .init(accessKey: accessKey))
}
Expand Down Expand Up @@ -119,7 +131,7 @@ public struct BunnyStreamPlayer: View {
func loadVideo() async {
loadingState = .loading
do {
let videoConfigResponse = try await videoPlayerConfigLoader.load(libraryId: libraryId, videoId: videoId)
let videoConfigResponse = try await videoPlayerConfigLoader.load(libraryId: libraryId, videoId: videoId, token: token, expires: expires)
var video = Video(response: videoConfigResponse)
// If Public Video (no access key), heatmap is not loaded - heatmapLoader is nil
let heatmap = try? await heatmapLoader?.loadHeatmap(videoId: videoId, libraryId: libraryId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,26 @@ import SwiftUI
public struct VideoPlayerConfigLoader {
public init() {}

func load(libraryId: Int, videoId: String) async throws -> VideoConfigResponse {
guard let url = URL(string: "https://video.bunnycdn.com/library/\(libraryId)/videos/\(videoId)/play") else {
func load(libraryId: Int, videoId: String, token: String? = nil, expires: Int64? = nil) async throws -> VideoConfigResponse {
guard var components = URLComponents(string: "https://video.bunnycdn.com/library/\(libraryId)/videos/\(videoId)/play") else {
throw VideoPlayerError.unknownError
}


var queryItems: [URLQueryItem] = []
if let token {
queryItems.append(URLQueryItem(name: "token", value: token))
}
if let expires {
queryItems.append(URLQueryItem(name: "expires", value: String(expires)))
}
if !queryItems.isEmpty {
components.queryItems = queryItems
}

guard let url = components.url else {
throw VideoPlayerError.unknownError
}

var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Accept")
Expand Down Expand Up @@ -41,8 +56,8 @@ public struct VideoPlayerConfigLoader {
}
}

public func loadVideoThumbnail(libraryId: Int, videoId: String) async throws -> String {
try await load(libraryId: libraryId, videoId: videoId).thumbnailUrl
public func loadVideoThumbnail(libraryId: Int, videoId: String, token: String? = nil, expires: Int64? = nil) async throws -> String {
try await load(libraryId: libraryId, videoId: videoId, token: token, expires: expires).thumbnailUrl
}
}

Expand Down