From 009292eebf9e12f3bc9e75aaefeade1973e22877 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Tue, 25 Aug 2026 19:12:11 +0530 Subject: [PATCH 1/2] Add examples and Rails project --- examples/.env.example | 14 ++ examples/.gitignore | 4 + examples/README.md | 87 ++++++++++ examples/ai_features.rb | 42 +++++ examples/analytics.rb | 43 +++++ examples/basic_usage.rb | 36 ++++ examples/configuration.rb | 36 ++++ examples/create_upload.rb | 36 ++++ examples/drm_configuration.rb | 41 +++++ examples/error_handling.rb | 28 +++ examples/live_streaming.rb | 57 ++++++ examples/media_upload.rb | 47 +++++ examples/playlist_management.rb | 57 ++++++ examples/rails-example/.env.example | 10 ++ examples/rails-example/Gemfile | 7 + examples/rails-example/README.md | 46 +++++ examples/rails-example/app.rb | 100 +++++++++++ examples/rails-example/config.ru | 4 + examples/signing_keys.rb | 42 +++++ examples/test_examples.rb | 68 ++++++++ examples/verify_webhook.rb | 43 +++++ samples/README.md | 96 ----------- samples/ai_features.rb | 240 -------------------------- samples/analytics.rb | 211 ----------------------- samples/basic_usage.rb | 88 ---------- samples/configuration.rb | 199 --------------------- samples/drm_configuration.rb | 226 ------------------------ samples/error_handling.rb | 240 -------------------------- samples/live_streaming.rb | 204 ---------------------- samples/media_upload.rb | 149 ---------------- samples/playlist_management.rb | 257 ---------------------------- samples/run_all_samples.rb | 225 ------------------------ samples/signing_keys.rb | 215 ----------------------- 33 files changed, 848 insertions(+), 2350 deletions(-) create mode 100644 examples/.env.example create mode 100644 examples/.gitignore create mode 100644 examples/README.md create mode 100644 examples/ai_features.rb create mode 100644 examples/analytics.rb create mode 100644 examples/basic_usage.rb create mode 100644 examples/configuration.rb create mode 100644 examples/create_upload.rb create mode 100644 examples/drm_configuration.rb create mode 100644 examples/error_handling.rb create mode 100644 examples/live_streaming.rb create mode 100644 examples/media_upload.rb create mode 100644 examples/playlist_management.rb create mode 100644 examples/rails-example/.env.example create mode 100644 examples/rails-example/Gemfile create mode 100644 examples/rails-example/README.md create mode 100644 examples/rails-example/app.rb create mode 100644 examples/rails-example/config.ru create mode 100644 examples/signing_keys.rb create mode 100644 examples/test_examples.rb create mode 100644 examples/verify_webhook.rb delete mode 100644 samples/README.md delete mode 100644 samples/ai_features.rb delete mode 100644 samples/analytics.rb delete mode 100644 samples/basic_usage.rb delete mode 100644 samples/configuration.rb delete mode 100644 samples/drm_configuration.rb delete mode 100644 samples/error_handling.rb delete mode 100644 samples/live_streaming.rb delete mode 100644 samples/media_upload.rb delete mode 100644 samples/playlist_management.rb delete mode 100644 samples/run_all_samples.rb delete mode 100644 samples/signing_keys.rb diff --git a/examples/.env.example b/examples/.env.example new file mode 100644 index 0000000..fbe7b64 --- /dev/null +++ b/examples/.env.example @@ -0,0 +1,14 @@ +# Copy this file to ".env" and fill in your credentials, then load it in your +# shell before running an example (e.g. `export $(grep -v '^#' .env | xargs)`). +# +# From your FastPix Dashboard (https://dashboard.fastpix.com): +# Access Token -> FASTPIX_USERNAME +# Secret Key -> FASTPIX_PASSWORD +FASTPIX_USERNAME= +FASTPIX_PASSWORD= + +# From Dashboard > Webhooks (needed to verify webhook signatures). +FASTPIX_WEBHOOK_SECRET= + +# Optional: an existing "Ready" media id, used by ai_features.rb. +# FASTPIX_MEDIA_ID= diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..7b6b886 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,4 @@ +.env +rails-example/tmp/ +rails-example/log/ +rails-example/.bundle/ diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..a647d55 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,87 @@ +# FastPix Ruby SDK examples + +Small, runnable scripts that show how to use the SDK against the FastPix API, +plus a minimal Rails app that wires uploads and webhooks into HTTP endpoints. + +## Setup + +You'll need Ruby 3.2+ and a FastPix account. + +1. Install the SDK (from RubyGems, or point Bundler at this repo while developing): + + ```bash + gem install fastpixapi + ``` + +2. Set your credentials. Copy `.env.example` to `.env`, fill it in, and load it: + + ```bash + cp .env.example .env + # edit .env, then: + export $(grep -v '^#' .env | xargs) + ``` + + `FASTPIX_USERNAME` is your Access Token and `FASTPIX_PASSWORD` is your Secret + Key, both from the [Dashboard](https://dashboard.fastpix.com). The examples + read them from the environment — nothing is hardcoded. + +3. Run any example: + + ```bash + ruby examples/basic_usage.rb + ``` + +## The examples + +| File | What it does | +| --- | --- | +| `basic_usage.rb` | Initialise the SDK and read from a few endpoints. | +| `create_upload.rb` | Mint a signed direct-upload URL for a device upload. | +| `verify_webhook.rb` | Verify a webhook signature. Runs offline, no credentials. | +| `media_upload.rb` | Create a media from a URL, read it, delete it. | +| `live_streaming.rb` | Create a live stream, toggle it, delete it. | +| `playlist_management.rb` | Create a playlist, add a media, delete it. | +| `signing_keys.rb` | Create, read and delete a signing key. | +| `analytics.rb` | Read views, dimensions, metrics and errors. | +| `drm_configuration.rb` | List DRM configurations. | +| `ai_features.rb` | Enable summary, chapters and named entities on a media. | +| `configuration.rb` | SDK options: timeout, retries, custom server URL. | +| `rails-example/` | A Rails app exposing `/uploads` and `/webhooks`. | + +A few examples need account features to be enabled (live streaming, DRM, +admin-level signing keys) or an existing ready media (`ai_features.rb`, via +`FASTPIX_MEDIA_ID`). When something isn't available they print why and exit +cleanly rather than failing hard. + +## Uploading a file after you have a signed URL + +`create_upload.rb` (and the Rails `/uploads` endpoint) hand you a signed URL. +The client uploads the file straight to that URL, so the bytes never touch your +server. Once it finishes, FastPix processes the video and sends the +`video.media.ready` webhook. + +We keep this simple and PUT the whole file in one request — good enough for +small files. For larger ones you'll usually want a resumable upload (chunked, +with retries and progress); the same signed URL supports that too. + +```bash +# Upload the file straight to the signed URL from create_upload.rb +curl -X PUT --upload-file video.mp4 \ + -H "Content-Type: video/mp4" \ + "$UPLOAD_URL" +``` + +Or from the browser, straight off a file input: + +```js +const { url } = await (await fetch("/uploads", { method: "POST" })).json(); +await fetch(url, { + method: "PUT", + headers: { "Content-Type": file.type || "application/octet-stream" }, + body: file, +}); +``` + +The examples mint uploads with `cors_origin: "*"` so the browser can PUT from +anywhere — lock that down before you ship. The docs go deeper, resumable +included: https://fastpix.com/docs/upload-videos/upload-videos-from-device diff --git a/examples/ai_features.rb b/examples/ai_features.rb new file mode 100644 index 0000000..fb71e53 --- /dev/null +++ b/examples/ai_features.rb @@ -0,0 +1,42 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Turn on in-video AI features (summary, chapters, named entities) for a media. +# These need a media that is already "Ready", so set FASTPIX_MEDIA_ID to an +# existing ready media id before running. + +require 'fastpixapi' +require 'json' + +Models = ::FastpixClient::Models +Ops = Models::Operations + +media_id = ENV['FASTPIX_MEDIA_ID'] +if media_id.nil? || media_id.empty? + abort 'Set FASTPIX_MEDIA_ID to a ready media id first (see the comment at the top).' +end + +sdk = ::FastpixClient::Fastpixapi.new( + security: Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') + ) +) + +summary = sdk.in_video_ai_features.update_media_summary( + media_id: media_id, + body: Ops::UpdateMediaSummaryRequestBody.new(generate: true) +) +puts "update_media_summary -> HTTP #{summary.status_code}" + +chapters = sdk.in_video_ai_features.update_media_chapters( + media_id: media_id, + body: Ops::UpdateMediaChaptersRequestBody.new(chapters: true) +) +puts "update_media_chapters -> HTTP #{chapters.status_code}" + +entities = sdk.in_video_ai_features.update_media_named_entities( + media_id: media_id, + body: Ops::UpdateMediaNamedEntitiesRequestBody.new(named_entities: true) +) +puts "update_media_named_entities -> HTTP #{entities.status_code}" diff --git a/examples/analytics.rb b/examples/analytics.rb new file mode 100644 index 0000000..881b7db --- /dev/null +++ b/examples/analytics.rb @@ -0,0 +1,43 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Read-only tour of the analytics endpoints: views, dimensions, metrics, errors. +# All are scoped to a time window; here we use the last 24 hours. + +require 'fastpixapi' +require 'json' + +Models = ::FastpixClient::Models +Ops = Models::Operations + +sdk = ::FastpixClient::Fastpixapi.new( + security: Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') + ) +) + +def rows(res) + data = JSON.parse(res.raw_response.body)['data'] + data.is_a?(Array) ? data.length : 'n/a' +rescue StandardError + 'n/a' +end + +views = sdk.views.list_video_views( + request: Ops::ListVideoViewsRequest.new(timespan: Ops::ListVideoViewsTimespan::TWENTY_FOURHOURS) +) +puts "list_video_views -> HTTP #{views.status_code}, #{rows(views)} row(s)" + +dims = sdk.dimensions.list_dimensions +puts "list_dimensions -> HTTP #{dims.status_code}, #{rows(dims)} row(s)" + +overall = sdk.metrics.list_overall_values( + metric_id: Ops::ListOverallValuesMetricId::QUALITY_OF_EXPERIENCE_SCORE, + measurement: 'avg', + timespan: Ops::ListOverallValuesTimespan::TWENTY_FOURHOURS +) +puts "list_overall_values -> HTTP #{overall.status_code}" + +errors = sdk.errors.list_errors(timespan: Ops::ListErrorsTimespan::TWENTY_FOURHOURS) +puts "list_errors -> HTTP #{errors.status_code}, #{rows(errors)} row(s)" diff --git a/examples/basic_usage.rb b/examples/basic_usage.rb new file mode 100644 index 0000000..dc409ab --- /dev/null +++ b/examples/basic_usage.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Connectivity check: initialise the SDK and read from a few endpoints. + +require 'fastpixapi' +require 'json' + +Models = ::FastpixClient::Models + +sdk = ::FastpixClient::Fastpixapi.new( + security: Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') + ) +) + +def count(res) + data = JSON.parse(res.raw_response.body)['data'] + data.is_a?(Array) ? data.length : 'n/a' +rescue StandardError + 'n/a' +end + +media = sdk.manage_videos.list_media(limit: 5) +puts "list_media -> HTTP #{media.status_code}, #{count(media)} item(s)" + +streams = sdk.manage_live_stream.get_all_streams(limit: 5) +puts "get_all_streams -> HTTP #{streams.status_code}, #{count(streams)} item(s)" + +views = sdk.views.list_video_views( + request: Models::Operations::ListVideoViewsRequest.new( + timespan: Models::Operations::ListVideoViewsTimespan::TWENTY_FOURHOURS + ) +) +puts "list_video_views -> HTTP #{views.status_code}, #{count(views)} item(s)" diff --git a/examples/configuration.rb b/examples/configuration.rb new file mode 100644 index 0000000..0ce7117 --- /dev/null +++ b/examples/configuration.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Show the SDK constructor options: request timeout, retries, and a custom +# server URL. Each is optional; the defaults are fine for most apps. + +require 'fastpixapi' + +Models = ::FastpixClient::Models +Utils = ::FastpixClient::Utils + +security = Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') +) + +# Retries with exponential backoff (all fields optional). +retry_config = Utils::RetryConfig.new( + backoff: Utils::BackoffStrategy.new( + initial_interval: 500, # ms + max_interval: 10_000, # ms + exponent: 1.5, + max_elapsed_time: 30_000 # ms + ), + retry_connection_errors: true +) + +sdk = ::FastpixClient::Fastpixapi.new( + security: security, + timeout_ms: 20_000, # per-request timeout + retry_config: retry_config + # server_url: 'https://api.fastpix.com/v1/' # override the API base URL if needed +) + +res = sdk.manage_videos.list_media(limit: 1) +puts "Configured client works -> HTTP #{res.status_code}" diff --git a/examples/create_upload.rb b/examples/create_upload.rb new file mode 100644 index 0000000..7770ba7 --- /dev/null +++ b/examples/create_upload.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Mint a signed direct-upload URL. The client then PUTs the file straight to +# that URL, so the bytes never touch your server. See examples/README.md for +# setup and how to upload the file once you have the URL. + +require 'fastpixapi' + +Models = ::FastpixClient::Models + +sdk = ::FastpixClient::Fastpixapi.new( + security: Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') + ) +) + +req = Models::Operations::DirectUploadVideoMediaRequest.new( + cors_origin: '*', # tighten this to your own origin before you ship + push_media_settings: Models::Operations::PushMediaSettings.new( + metadata: { 'source' => 'create_upload_example' } + ) +) + +res = sdk.input_video.direct_upload_video_media(request: req) +data = res.object&.data + +puts "Upload ID: #{data&.upload_id}" +puts "Timeout: #{data&.timeout}s" +puts +puts 'PUT your file to this signed URL to upload it:' +puts data&.url +puts +puts 'e.g. curl -X PUT --upload-file video.mp4 \\' +puts ' -H "Content-Type: video/mp4" ""' diff --git a/examples/drm_configuration.rb b/examples/drm_configuration.rb new file mode 100644 index 0000000..7ff42c1 --- /dev/null +++ b/examples/drm_configuration.rb @@ -0,0 +1,41 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Read-only: list your DRM configurations and fetch one by id. +# DRM configs are created in the Dashboard; the SDK reads them. + +require 'fastpixapi' +require 'json' + +Models = ::FastpixClient::Models + +sdk = ::FastpixClient::Fastpixapi.new( + security: Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') + ) +) + +def body(res) + JSON.parse(res.raw_response.body) +rescue StandardError + {} +end + +# The list endpoint returns 400 when the workspace has no DRM configuration yet. +begin + list = sdk.drm_configurations.get_drm_configuration(limit: 10) +rescue FastpixClient::Models::Errors::APIError => e + warn "get_drm_configuration: #{JSON.parse(e.body).dig('error', 'message') rescue e.body}" + warn 'Create a DRM configuration in the Dashboard, then rerun.' + exit 0 +end +configs = body(list)['data'] || [] +puts "get_drm_configuration -> HTTP #{list.status_code}, #{configs.length} config(s)" + +if (drm_id = configs.dig(0, 'id')) + one = sdk.drm_configurations.get_drm_configuration_by_id(drm_configuration_id: drm_id) + puts "get_drm_configuration_by_id -> HTTP #{one.status_code}, id #{drm_id}" +else + puts 'get_drm_configuration_by_id -> skipped (no DRM configs; create one in the Dashboard)' +end diff --git a/examples/error_handling.rb b/examples/error_handling.rb new file mode 100644 index 0000000..01c6e3a --- /dev/null +++ b/examples/error_handling.rb @@ -0,0 +1,28 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# How to handle API errors. Every failed request raises +# FastpixClient::Models::Errors::APIError, which carries the HTTP status and +# the raw response body so you can read the server's error details. + +require 'fastpixapi' +require 'json' +require 'securerandom' + +Models = ::FastpixClient::Models + +sdk = ::FastpixClient::Fastpixapi.new( + security: Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') + ) +) + +begin + # A media id that doesn't exist -> the API returns an error. + sdk.manage_videos.get_media(media_id: SecureRandom.uuid) +rescue FastpixClient::Models::Errors::APIError => e + puts "Caught APIError: HTTP #{e.status_code}" + error = (JSON.parse(e.body)['error'] rescue nil) + puts " message: #{error&.fetch('message', nil) || e.body}" +end diff --git a/examples/live_streaming.rb b/examples/live_streaming.rb new file mode 100644 index 0000000..7a16d9f --- /dev/null +++ b/examples/live_streaming.rb @@ -0,0 +1,57 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Flow: create a live stream, read it, toggle it, then delete it. +# New streams start enabled, so the demo order is disable -> enable. + +require 'fastpixapi' +require 'json' + +Models = ::FastpixClient::Models + +sdk = ::FastpixClient::Fastpixapi.new( + security: Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') + ) +) + +def body(res) + JSON.parse(res.raw_response.body) +rescue StandardError + {} +end + +# 1. Create a stream. You'll stream to the returned stream key over RTMP. +# (Live streaming has to be enabled on your account for this to succeed.) +begin + create = sdk.start_live_stream.create_new_stream( + request: Models::Components::CreateLiveStreamRequest.new( + playback_settings: Models::Components::PlaybackSettings.new, + input_media_settings: Models::Components::InputMediaSettings.new( + metadata: { 'source' => 'live_streaming_example' } + ) + ) + ) +rescue FastpixClient::Models::Errors::APIError => e + warn "create_new_stream failed: #{JSON.parse(e.body)['error']&.fetch('message', e.body) rescue e.body}" + exit 1 +end +data = body(create)['data'] || {} +stream_id = data['streamId'] +puts "create_new_stream -> HTTP #{create.status_code}, stream id #{stream_id}" +puts " stream key: #{data['streamKey']}" + +# 2. Read it back. +get = sdk.manage_live_stream.get_live_stream_by_id(stream_id: stream_id) +puts "get_by_id -> HTTP #{get.status_code}" + +# 3. Toggle: a fresh stream is already enabled, so disable first. +disable = sdk.manage_live_stream.disable_live_stream(stream_id: stream_id) +puts "disable -> HTTP #{disable.status_code}" +enable = sdk.manage_live_stream.enable_live_stream(stream_id: stream_id) +puts "enable -> HTTP #{enable.status_code}" + +# 4. Clean up. +del = sdk.manage_live_stream.delete_live_stream(stream_id: stream_id) +puts "delete -> HTTP #{del.status_code}" diff --git a/examples/media_upload.rb b/examples/media_upload.rb new file mode 100644 index 0000000..df1c6e6 --- /dev/null +++ b/examples/media_upload.rb @@ -0,0 +1,47 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Flow: create a media from a public URL, read it back, then delete it. +# (For device uploads that mint a signed URL, see create_upload.rb.) + +require 'fastpixapi' +require 'json' + +Models = ::FastpixClient::Models + +sdk = ::FastpixClient::Fastpixapi.new( + security: Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') + ) +) + +def body(res) + JSON.parse(res.raw_response.body) +rescue StandardError + {} +end + +# 1. Create a media from a hosted file. +create = sdk.input_video.create_media( + request: Models::Components::CreateMediaRequest.new( + inputs: [ + Models::Components::PullVideoInput.new( + type: 'video', + url: 'https://static.fastpix.com/fp-sample-video.mp4' + ) + ], + metadata: { 'source' => 'media_upload_example' } + ) +) +media = body(create)['data'] +media_id = media.is_a?(Array) ? media.first['id'] : media['id'] +puts "create_media -> HTTP #{create.status_code}, media id #{media_id}" + +# 2. Read it back. A freshly created media is still processing (status "Created"). +get = sdk.manage_videos.get_media(media_id: media_id) +puts "get_media -> HTTP #{get.status_code}, status #{body(get).dig('data', 'status')}" + +# 3. Clean up so reruns stay tidy. +del = sdk.manage_videos.delete_media(media_id: media_id) +puts "delete_media -> HTTP #{del.status_code}" diff --git a/examples/playlist_management.rb b/examples/playlist_management.rb new file mode 100644 index 0000000..01c7b37 --- /dev/null +++ b/examples/playlist_management.rb @@ -0,0 +1,57 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Flow: create a manual playlist, add a media to it, read it, then delete it. + +require 'fastpixapi' +require 'json' + +Models = ::FastpixClient::Models + +sdk = ::FastpixClient::Fastpixapi.new( + security: Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') + ) +) + +def body(res) + JSON.parse(res.raw_response.body) +rescue StandardError + {} +end + +# 1. Create a manual playlist. reference_id must be alphanumeric and unique +# per workspace, so derive a fresh one each run. +create = sdk.playlist.create_a_playlist( + request: Models::Components::CreatePlaylistRequestManual.new( + name: 'Example playlist', + reference_id: "ex#{Time.now.to_i}#{rand(1000)}", + type: Models::Components::CreatePlaylistRequestManualType::MANUAL, + # description allows only alphanumerics, spaces, hyphens and underscores + description: 'Created by the playlist management example' + ) +) +playlist_id = body(create).dig('data', 'id') +puts "create_a_playlist -> HTTP #{create.status_code}, playlist id #{playlist_id}" + +# 2. Add an existing media, if the workspace has one. +media = sdk.manage_videos.list_media(limit: 1) +media_id = (body(media)['data'] || []).dig(0, 'id') +if media_id + add = sdk.playlist.add_media_to_playlist( + playlist_id: playlist_id, + body: Models::Components::MediaIdsRequest.new(media_ids: [media_id]) + ) + puts "add_media -> HTTP #{add.status_code}, media #{media_id}" +else + puts 'add_media -> skipped (no media in workspace)' +end + +# 3. Read it back. +get = sdk.playlist.get_playlist_by_id(playlist_id: playlist_id) +puts "get_playlist -> HTTP #{get.status_code}" + +# 4. Clean up. +del = sdk.playlist.delete_a_playlist(playlist_id: playlist_id) +puts "delete_a_playlist -> HTTP #{del.status_code}" diff --git a/examples/rails-example/.env.example b/examples/rails-example/.env.example new file mode 100644 index 0000000..dc3aecb --- /dev/null +++ b/examples/rails-example/.env.example @@ -0,0 +1,10 @@ +# Copy to ".env" and fill in, then: export $(grep -v '^#' .env | xargs) +# +# From your FastPix Dashboard (https://dashboard.fastpix.com): +# Access Token -> FASTPIX_USERNAME +# Secret Key -> FASTPIX_PASSWORD +FASTPIX_USERNAME= +FASTPIX_PASSWORD= + +# From Dashboard > Webhooks, used to verify webhook signatures. +FASTPIX_WEBHOOK_SECRET= diff --git a/examples/rails-example/Gemfile b/examples/rails-example/Gemfile new file mode 100644 index 0000000..551b2f6 --- /dev/null +++ b/examples/rails-example/Gemfile @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +gem 'fastpixapi' +gem 'puma' +gem 'rails', '~> 8.0' diff --git a/examples/rails-example/README.md b/examples/rails-example/README.md new file mode 100644 index 0000000..782d9be --- /dev/null +++ b/examples/rails-example/README.md @@ -0,0 +1,46 @@ +# FastPix + Rails example + +A minimal Rails app with two endpoints: + +- `POST /uploads` — mints a signed direct-upload URL via the SDK and returns + `{ uploadId, url }`. Your client PUTs the file straight to that URL, so the + video never passes through this server. +- `POST /webhooks` — verifies the `FastPix-Signature` on the raw request body, + then handles the event and acks with `200` quickly. + +It's a single file (`app.rb`) to keep the moving parts visible. + +## Run it + +```bash +cd examples/rails-example +bundle install +cp .env.example .env # fill in your credentials +export $(grep -v '^#' .env | xargs) +bundle exec rackup -p 9292 +``` + +While developing against this repo (before the gem is published), point Ruby at +the local SDK instead: `RUBYLIB=../../lib bundle exec rackup -p 9292`. + +## Try it + +```bash +# 1. Get a signed upload URL +UPLOAD_URL=$(curl -s -X POST localhost:9292/uploads | ruby -rjson -e 'puts JSON.parse(STDIN.read)["url"]') + +# 2. Upload a file straight to it +curl -X PUT --upload-file video.mp4 -H "Content-Type: video/mp4" "$UPLOAD_URL" +``` + +FastPix will POST a `video.media.ready` webhook to `/webhooks` once processing +finishes. To test webhook verification locally, sign a payload with your +`FASTPIX_WEBHOOK_SECRET` and send it with a `FastPix-Signature` header — a valid +signature returns `200`, a bad one returns `401`. + +## Before you ship + +- `/uploads` mints upload URLs with no auth here — add your own before exposing it. +- Uploads are created with `cors_origin: "*"` so any browser can PUT; scope it + to your own origin in production. +- `config.hosts` is cleared for the demo; set your allowed hosts. diff --git a/examples/rails-example/app.rb b/examples/rails-example/app.rb new file mode 100644 index 0000000..258f3f7 --- /dev/null +++ b/examples/rails-example/app.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +# A minimal single-file Rails app with two endpoints: +# +# POST /uploads mint a signed direct-upload URL (client PUTs the file there) +# POST /webhooks verify a FastPix webhook signature, then handle the event +# +# The video file never passes through this server: the client uploads it +# straight to the signed URL. See README.md to run it. + +require 'rails' +require 'action_controller/railtie' +require 'fastpixapi' +require 'openssl' +require 'base64' +require 'json' + +Models = ::FastpixClient::Models + +class App < Rails::Application + config.root = __dir__ + config.eager_load = false + config.consider_all_requests_local = true + config.secret_key_base = 'demo-not-a-real-secret' + config.logger = Logger.new($stdout) + config.hosts.clear # demo only; set allowed hosts in production + + routes.append do + post '/uploads', to: 'app#uploads' + post '/webhooks', to: 'app#webhooks' + end +end + +def fastpix + ::FastpixClient::Fastpixapi.new( + security: Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') + ) + ) +end + +# Constant-time compare (stdlib has no public helper). +def secure_compare(a, b) + return false unless a.bytesize == b.bytesize + + res = 0 + a.bytes.zip(b.bytes) { |x, y| res |= x ^ y } + res.zero? +end + +# Verify FastPix-Signature = Base64(HMAC-SHA256(decoded secret, raw body)). +def valid_signature?(raw_body, signature) + secret = ENV['FASTPIX_WEBHOOK_SECRET'] + return false if secret.to_s.empty? || signature.to_s.empty? + + key = Base64.decode64(secret) # Signing Secret is Base64; use its decoded bytes. + expected = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', key, raw_body)) + secure_compare(expected, signature) +end + +class AppController < ActionController::Base + # Webhooks are server-to-server and HMAC-authed with no cookie, so CSRF + # protection doesn't apply. Add auth to /uploads before you ship it. + skip_forgery_protection + + # POST /uploads -> { uploadId, url } + def uploads + req = Models::Operations::DirectUploadVideoMediaRequest.new( + cors_origin: '*', # tighten to your own origin in production + push_media_settings: Models::Operations::PushMediaSettings.new( + metadata: { 'source' => 'rails_example' } + ) + ) + data = fastpix.input_video.direct_upload_video_media(request: req).object&.data + render json: { uploadId: data&.upload_id, url: data&.url } + end + + # POST /webhooks -> ack fast; verify the raw body before trusting it. + def webhooks + raw = request.body.read + unless valid_signature?(raw, request.headers['FastPix-Signature']) + return head :unauthorized + end + + event = JSON.parse(raw) + case event['type'] + when 'video.media.ready' + Rails.logger.info("media ready: #{event.dig('data', 'id')}") + when 'video.media.failed' + Rails.logger.info("media failed: #{event.dig('data', 'id')}") + else + Rails.logger.info("unhandled event: #{event['type']}") + end + + head :ok # 2xx quickly; FastPix retries on non-2xx. + end +end + +App.initialize! diff --git a/examples/rails-example/config.ru b/examples/rails-example/config.ru new file mode 100644 index 0000000..b611ae0 --- /dev/null +++ b/examples/rails-example/config.ru @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +require_relative 'app' +run App diff --git a/examples/signing_keys.rb b/examples/signing_keys.rb new file mode 100644 index 0000000..8f746bb --- /dev/null +++ b/examples/signing_keys.rb @@ -0,0 +1,42 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Flow: create a signing key, read it back, then delete it. +# Signing keys sign playback tokens for private/DRM playback. + +require 'fastpixapi' +require 'json' + +Models = ::FastpixClient::Models + +sdk = ::FastpixClient::Fastpixapi.new( + security: Models::Components::Security.new( + username: ENV.fetch('FASTPIX_USERNAME'), + password: ENV.fetch('FASTPIX_PASSWORD') + ) +) + +def body(res) + JSON.parse(res.raw_response.body) +rescue StandardError + {} +end + +# 1. Create a key. The private key comes back once, here only. +# Creating signing keys needs an access token with system/admin permission. +begin + create = sdk.signing_keys.create_signing_key +rescue FastpixClient::Models::Errors::APIError => e + warn "create_signing_key failed: #{JSON.parse(e.body).dig('error', 'message') rescue e.body}" + exit 1 +end +key_id = body(create).dig('data', 'id') +puts "create_signing_key -> HTTP #{create.status_code}, key id #{key_id}" + +# 2. Read it back (metadata only, no private key). +get = sdk.signing_keys.get_signing_key_by_id(signing_key_id: key_id) +puts "get_signing_key_by_id -> HTTP #{get.status_code}" + +# 3. Clean up. +del = sdk.signing_keys.delete_signing_key(signing_key_id: key_id) +puts "delete_signing_key -> HTTP #{del.status_code}" diff --git a/examples/test_examples.rb b/examples/test_examples.rb new file mode 100644 index 0000000..f7ec6e8 --- /dev/null +++ b/examples/test_examples.rb @@ -0,0 +1,68 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Offline checks for the examples. No credentials or network needed. +# ruby examples/test_examples.rb +require 'minitest/autorun' +require 'open3' + +HERE = __dir__ +RUBY_EXAMPLES = Dir[File.join(HERE, '*.rb')].reject { |f| File.basename(f) == 'test_examples.rb' } + + Dir[File.join(HERE, 'rails-example', 'app.rb')] + +class ExampleWellFormednessTest < Minitest::Test + # A UUID that isn't all-zeros looks like a real credential. + REAL_UUID = /\b(?!0{8}-0{4}-0{4}-0{4}-0{12})[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/ + + def test_every_example_parses + RUBY_EXAMPLES.each do |file| + _out, err, status = Open3.capture3('ruby', '-c', file) + assert status.success?, "#{File.basename(file)} failed to parse: #{err}" + end + end + + def test_credentials_come_from_env + RUBY_EXAMPLES.each do |file| + src = File.read(file) + next unless src.include?('FASTPIX_USERNAME') + + assert_match(/ENV\.fetch\(['"]FASTPIX_USERNAME|ENV\[['"]FASTPIX_USERNAME/, src, + "#{File.basename(file)} should read FASTPIX_USERNAME from the environment") + end + end + + def test_no_hardcoded_credentials + RUBY_EXAMPLES.each do |file| + src = File.read(file) + refute_match REAL_UUID, src, "#{File.basename(file)} appears to contain a hardcoded credential" + end + end +end + +class WebhookVerifierTest < Minitest::Test + def setup + # Load verify_webhook.rb without running its demo main. + require File.join(HERE, 'verify_webhook.rb') + @secret = Base64.strict_encode64('signing-secret') + @body = '{"type":"video.media.ready","data":{"id":"abc-123"}}' + key = Base64.decode64(@secret) + @sig = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', key, @body)) + end + + def test_valid_signature_passes + assert valid_signature?(@body, @sig, @secret) + end + + def test_wrong_signature_fails + refute valid_signature?(@body, "#{@sig}x", @secret) + end + + def test_tampered_body_fails + refute valid_signature?("#{@body} ", @sig, @secret) + end + + def test_missing_inputs_fail + refute valid_signature?(@body, '', @secret) + refute valid_signature?(@body, @sig, '') + end +end diff --git a/examples/verify_webhook.rb b/examples/verify_webhook.rb new file mode 100644 index 0000000..9defe3d --- /dev/null +++ b/examples/verify_webhook.rb @@ -0,0 +1,43 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Verify a FastPix webhook signature before trusting the payload. +# +# FastPix signs the raw request body with your webhook Signing Secret +# (Dashboard > Webhooks) and sends it as a Base64 HMAC-SHA256 in the +# "FastPix-Signature" header. The Signing Secret is itself Base64-encoded, so +# sign with its decoded bytes as the key. Verify the body exactly as received: +# parsing and re-serializing changes the bytes and the signature won't match. +# +# This runs offline: it self-signs a demo payload and checks it. + +require 'openssl' +require 'base64' + +# Returns true if signature is a valid FastPix-Signature for raw_body. +def valid_signature?(raw_body, signature, secret) + return false if secret.to_s.empty? || signature.to_s.empty? + + key = Base64.decode64(secret) # Signing Secret is Base64; use its decoded bytes. + expected = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', key, raw_body)) + secure_compare(expected, signature) +end + +# Constant-time string comparison (stdlib has no public helper). +def secure_compare(a, b) + return false unless a.bytesize == b.bytesize + + res = 0 + a.bytes.zip(b.bytes) { |x, y| res |= x ^ y } + res.zero? +end + +if __FILE__ == $PROGRAM_NAME + secret = ENV['FASTPIX_WEBHOOK_SECRET'] || Base64.strict_encode64('demo-secret') + raw_body = '{"type":"video.media.ready","data":{"id":"abc-123"}}' + key = Base64.decode64(secret) + signature = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', key, raw_body)) + + puts valid_signature?(raw_body, signature, secret) ? 'verified' : 'rejected' + puts valid_signature?(raw_body, signature + 'x', secret) ? 'verified' : 'rejected (tampered)' +end diff --git a/samples/README.md b/samples/README.md deleted file mode 100644 index e28768e..0000000 --- a/samples/README.md +++ /dev/null @@ -1,96 +0,0 @@ -# FastPix Ruby SDK Samples - -This directory contains comprehensive examples demonstrating how to use the FastPix Ruby SDK for various API operations. - -## Prerequisites - -Before running these samples, ensure you have: - -- Ruby 3.2+ installed -- FastPix API credentials (username and password) -- Internet connection - -## Setup - -1. Install the FastPix Ruby SDK: - ```bash - gem install fastpixapi - ``` - -2. Set your credentials as environment variables: - ```bash - export FASTPIX_USERNAME="your_username_here" - export FASTPIX_PASSWORD="your_password_here" - ``` - -3. Run any sample: - ```bash - ruby samples/basic_usage.rb - ``` - -## Sample Files - -### Core Examples -- **`basic_usage.rb`** - Basic SDK setup and authentication -- **`error_handling.rb`** - Comprehensive error handling examples -- **`configuration.rb`** - SDK configuration options - -### Media Management -- **`media_upload.rb`** - Upload media from URL and direct upload -- **`media_management.rb`** - List, get, update, and delete media -- **`media_tracks.rb`** - Add and manage media tracks (audio/subtitles) - -### Live Streaming -- **`live_streaming.rb`** - Create and manage live streams -- **`live_playback.rb`** - Manage live stream playback IDs -- **`simulcast.rb`** - Simulcast streams to external platforms - -### Playback Management -- **`playback_ids.rb`** - Create and manage playback IDs for media -- **`playlist_management.rb`** - Create and manage playlists - -### Analytics & Data -- **`analytics.rb`** - Video analytics and performance tracking -- **`metrics.rb`** - Metrics and data insights - -### Security & Authentication -- **`signing_keys.rb`** - Manage cryptographic signing keys -- **`drm_configuration.rb`** - DRM configuration management - -### AI Features -- **`ai_features.rb`** - In-video AI processing features - -## Running Samples - -Each sample is self-contained and can be run independently: - -```bash -# Run a specific sample -ruby samples/media_upload.rb - -# Run all samples (if you have a runner script) -ruby samples/run_all_samples.rb -``` - -## Important Notes - -- Replace placeholder credentials with your actual FastPix credentials -- Some operations may require specific data to be available in your FastPix account -- Error handling is included in all samples to demonstrate proper SDK usage -- Samples include both success and error scenarios - -## Troubleshooting - -If you encounter issues: - -1. Verify your credentials are correct -2. Check your internet connection -3. Ensure you're using Ruby 3.2+ -4. Check the FastPix API documentation for any service-specific requirements - -## Support - -For additional help: -- Check the main SDK documentation -- Visit the FastPix API documentation -- Review the error messages for specific guidance diff --git a/samples/ai_features.rb b/samples/ai_features.rb deleted file mode 100644 index 465d227..0000000 --- a/samples/ai_features.rb +++ /dev/null @@ -1,240 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# FastPix Ruby SDK - AI Features Examples -# This example demonstrates in-video AI processing features - -require 'fastpixapi' - -# Configuration -USERNAME = ENV['FASTPIX_USERNAME'] || 'your_username_here' -PASSWORD = ENV['FASTPIX_PASSWORD'] || 'your_password_here' - -puts "🤖 FastPix Ruby SDK - AI Features Examples" -puts "=" * 50 - -begin - # Initialize the SDK - sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - puts "✅ SDK initialized successfully" - - # Example 1: List AI features - puts "\n1. Listing available AI features..." - begin - response = sdk.in_video_ai_features.list_ai_features(limit: 10) - - if response.status_code == 200 - puts "✅ AI features retrieved successfully" - puts " Total features: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Available AI features:" - response.object.data.first(5).each_with_index do |feature, index| - puts " #{index + 1}. ID: #{feature.id}" - puts " Name: #{feature.name}" - puts " Type: #{feature.type}" - puts " Status: #{feature.status}" - puts " Created: #{feature.created_at || 'Unknown'}" - end - else - puts " ℹ️ No AI features found" - puts " This is normal for new accounts or test environments" - end - else - puts "⚠️ AI features listing failed with status: #{response.status_code}" - end - rescue => e - puts "❌ AI features listing failed: #{e.message}" - end - - # Example 2: Get AI feature details (if we have one) - puts "\n2. Getting AI feature details..." - begin - # First, try to get an AI feature ID from the list - list_response = sdk.in_video_ai_features.list_ai_features(limit: 1) - - if list_response.status_code == 200 && - list_response.object&.data&.any? && - list_response.object.data.first&.id - - feature_id = list_response.object.data.first.id - puts " Using AI feature ID: #{feature_id}" - - response = sdk.in_video_ai_features.get_ai_feature_by_id( - ai_feature_id: feature_id - ) - - if response.status_code == 200 - puts "✅ AI feature details retrieved successfully" - feature = response.object&.data - puts " ID: #{feature&.id}" - puts " Name: #{feature&.name}" - puts " Type: #{feature&.type}" - puts " Status: #{feature&.status}" - puts " Description: #{feature&.description}" - puts " Created: #{feature&.created_at}" - else - puts "⚠️ AI feature details retrieval failed with status: #{response.status_code}" - end - else - puts "ℹ️ No AI features available to retrieve details for" - end - rescue => e - puts "❌ AI feature details retrieval failed: #{e.message}" - end - - # Example 3: List AI features with pagination - puts "\n3. Listing AI features with pagination..." - begin - response = sdk.in_video_ai_features.list_ai_features( - limit: 5, - offset: 0 - ) - - if response.status_code == 200 - puts "✅ Paginated AI features retrieved successfully" - puts " Features count: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " AI features:" - response.object.data.each_with_index do |feature, index| - puts " #{index + 1}. ID: #{feature.id}" - puts " Name: #{feature.name}" - puts " Type: #{feature.type}" - puts " Status: #{feature.status}" - end - end - else - puts "⚠️ Paginated AI features retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Paginated AI features retrieval failed: #{e.message}" - end - - # Example 4: List AI features with filters - puts "\n4. Listing AI features with filters..." - begin - # Try to filter by type (if supported) - response = sdk.in_video_ai_features.list_ai_features( - limit: 10, - type: 'object_detection' # Example filter - ) - - if response.status_code == 200 - puts "✅ Filtered AI features retrieved successfully" - puts " Features count: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Filtered AI features:" - response.object.data.first(3).each_with_index do |feature, index| - puts " #{index + 1}. ID: #{feature.id}" - puts " Name: #{feature.name}" - puts " Type: #{feature.type}" - end - end - else - puts "⚠️ Filtered AI features retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Filtered AI features retrieval failed: #{e.message}" - end - - # Example 5: AI feature search - puts "\n5. Searching AI features..." - begin - # Try to search by name or other criteria - response = sdk.in_video_ai_features.list_ai_features( - limit: 10, - search: 'detection' # Example search term - ) - - if response.status_code == 200 - puts "✅ AI feature search completed successfully" - puts " Search results count: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Search results:" - response.object.data.first(3).each_with_index do |feature, index| - puts " #{index + 1}. ID: #{feature.id}" - puts " Name: #{feature.name}" - puts " Type: #{feature.type}" - end - end - else - puts "⚠️ AI feature search failed with status: #{response.status_code}" - end - rescue => e - puts "❌ AI feature search failed: #{e.message}" - end - - # Example 6: AI feature statistics - puts "\n6. Getting AI feature statistics..." - begin - # Get all features to calculate statistics - response = sdk.in_video_ai_features.list_ai_features(limit: 100) - - if response.status_code == 200 - puts "✅ AI feature statistics retrieved successfully" - features = response.object&.data || [] - - puts " Statistics:" - puts " - Total features: #{features.length}" - - # Group by type - type_counts = features.group_by(&:type).transform_values(&:length) - if type_counts.any? - puts " - By type:" - type_counts.each do |type, count| - puts " * #{type}: #{count}" - end - end - - # Group by status - status_counts = features.group_by(&:status).transform_values(&:length) - if status_counts.any? - puts " - By status:" - status_counts.each do |status, count| - puts " * #{status}: #{count}" - end - end - else - puts "⚠️ AI feature statistics retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ AI feature statistics retrieval failed: #{e.message}" - end - - # Example 7: Common AI feature types - puts "\n7. Common AI feature types..." - puts " Typical AI features available:" - puts " - Object Detection: Identify and locate objects in video frames" - puts " - Face Recognition: Detect and recognize faces in video content" - puts " - Scene Classification: Categorize video scenes and content" - puts " - Text Recognition: Extract text from video frames (OCR)" - puts " - Motion Analysis: Track movement and motion patterns" - puts " - Content Moderation: Detect inappropriate or sensitive content" - puts " - Sentiment Analysis: Analyze emotional tone of video content" - puts " - Quality Assessment: Evaluate video quality and technical metrics" - - puts "\n🎉 AI features examples completed!" - puts "\nKey concepts:" - puts "- AI features provide intelligent analysis of video content" - puts "- Different features serve different use cases (detection, analysis, etc.)" - puts "- AI features can be applied to both live and on-demand content" - puts "- Features may require specific video formats or quality levels" - puts "- AI processing may take time depending on video length and complexity" - -rescue => e - puts "❌ Example failed: #{e.message}" - puts "\nTroubleshooting:" - puts "1. Verify your credentials are correct" - puts "2. Check your internet connection" - puts "3. Ensure you have proper permissions for AI features" - puts "4. Some AI features may require specific account permissions" - puts "5. AI features may not be available in all environments" -end diff --git a/samples/analytics.rb b/samples/analytics.rb deleted file mode 100644 index 2da8803..0000000 --- a/samples/analytics.rb +++ /dev/null @@ -1,211 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# FastPix Ruby SDK - Analytics Examples -# This example demonstrates analytics and metrics operations - -require 'fastpixapi' - -# Configuration -USERNAME = ENV['FASTPIX_USERNAME'] || 'your_username_here' -PASSWORD = ENV['FASTPIX_PASSWORD'] || 'your_password_here' - -puts "📊 FastPix Ruby SDK - Analytics Examples" -puts "=" * 50 - -begin - # Initialize the SDK - sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - puts "✅ SDK initialized successfully" - - # Example 1: List video views - puts "\n1. Listing video views..." - begin - response = sdk.views.list_video_views(limit: 10) - - if response.status_code == 200 - puts "✅ Video views retrieved successfully" - puts " Total views: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Recent views:" - response.object.data.first(3).each_with_index do |view, index| - puts " #{index + 1}. View ID: #{view.id}" - puts " Media ID: #{view.media_id}" - puts " Playback ID: #{view.playback_id}" - puts " Created: #{view.created_at || 'Unknown'}" - end - end - else - puts "⚠️ Video views listing failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Video views listing failed: #{e.message}" - end - - # Example 2: Get timeseries data - puts "\n2. Getting timeseries data..." - begin - request = FastpixApiSDK::Models::Operations::GetTimeseriesDataRequest.new( - metric_id: FastpixApiSDK::Models::Operations::GetTimeseriesDataMetricId::VIEWS, - timespan: FastpixApiSDK::Models::Operations::GetTimeseriesDataTimespan::SEVENDAYS - ) - - response = sdk.metrics.get_timeseries_data(request: request) - - if response.status_code == 200 - puts "✅ Timeseries data retrieved successfully" - data = response.object&.data - puts " Metric: #{data&.metric_id}" - puts " Timespan: #{data&.timespan}" - puts " Data points: #{data&.data&.length || 0}" - - if data&.data&.any? - puts " Sample data points:" - data.data.first(3).each_with_index do |point, index| - puts " #{index + 1}. Time: #{point.time}" - puts " Value: #{point.value}" - end - end - else - puts "⚠️ Timeseries data retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Timeseries data retrieval failed: #{e.message}" - end - - # Example 3: List breakdown values for dimensions - puts "\n3. Getting breakdown values for dimensions..." - begin - # First, get available dimensions - dimensions_response = sdk.dimensions.list_dimensions() - - if dimensions_response.status_code == 200 && dimensions_response.object&.data&.any? - dimension = dimensions_response.object.data.first - puts " Using dimension: #{dimension}" - - # Get breakdown values for the first dimension - breakdown_response = sdk.dimensions.list_breakdown_values_for_dimension( - dimension_id: dimension, - limit: 10 - ) - - if breakdown_response.status_code == 200 - puts "✅ Breakdown values retrieved successfully" - puts " Dimension: #{dimension}" - puts " Values count: #{breakdown_response.object&.data&.length || 0}" - - if breakdown_response.object&.data&.any? - puts " Sample values:" - breakdown_response.object.data.first(5).each_with_index do |value, index| - puts " #{index + 1}. #{value}" - end - end - else - puts "⚠️ Breakdown values retrieval failed with status: #{breakdown_response.status_code}" - end - else - puts "ℹ️ No dimensions available for breakdown analysis" - end - rescue => e - puts "❌ Breakdown values retrieval failed: #{e.message}" - end - - # Example 4: List comparison values - puts "\n4. Getting comparison values..." - begin - response = sdk.dimensions.list_comparison_values(limit: 10) - - if response.status_code == 200 - puts "✅ Comparison values retrieved successfully" - puts " Values count: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Sample comparison values:" - response.object.data.first(5).each_with_index do |value, index| - puts " #{index + 1}. #{value}" - end - end - else - puts "⚠️ Comparison values retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Comparison values retrieval failed: #{e.message}" - end - - # Example 5: Get specific video view details (if we have views) - puts "\n5. Getting specific video view details..." - begin - # First, try to get a view ID from the list - views_response = sdk.views.list_video_views(limit: 1) - - if views_response.status_code == 200 && - views_response.object&.data&.any? && - views_response.object.data.first&.id - - view_id = views_response.object.data.first.id - puts " Using view ID: #{view_id}" - - response = sdk.views.get_video_view_details(view_id: view_id) - - if response.status_code == 200 - puts "✅ Video view details retrieved successfully" - view = response.object&.data - puts " View ID: #{view&.id}" - puts " Media ID: #{view&.media_id}" - puts " Playback ID: #{view&.playback_id}" - puts " Duration: #{view&.duration} seconds" if view&.duration - puts " Created: #{view&.created_at}" - else - puts "⚠️ Video view details retrieval failed with status: #{response.status_code}" - end - else - puts "ℹ️ No video views available to retrieve details for" - end - rescue => e - puts "❌ Video view details retrieval failed: #{e.message}" - end - - # Example 6: List signing keys (for analytics access) - puts "\n6. Listing signing keys..." - begin - response = sdk.signing_keys.list_signing_keys(limit: 10) - - if response.status_code == 200 - puts "✅ Signing keys retrieved successfully" - puts " Keys count: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Recent signing keys:" - response.object.data.first(3).each_with_index do |key, index| - puts " #{index + 1}. ID: #{key.id}" - puts " Name: #{key.name}" - puts " Created: #{key.created_at || 'Unknown'}" - end - end - else - puts "⚠️ Signing keys listing failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Signing keys listing failed: #{e.message}" - end - - puts "\n🎉 Analytics examples completed!" - puts "\nNext steps:" - puts "- Check out metrics.rb for more detailed metrics operations" - puts "- Use the analytics data to build dashboards and reports" - puts "- Integrate with your application's analytics pipeline" - -rescue => e - puts "❌ Example failed: #{e.message}" - puts "\nTroubleshooting:" - puts "1. Verify your credentials are correct" - puts "2. Check your internet connection" - puts "3. Ensure you have proper permissions for analytics operations" - puts "4. Some analytics data may require time to accumulate" -end diff --git a/samples/basic_usage.rb b/samples/basic_usage.rb deleted file mode 100644 index 4a5fa4d..0000000 --- a/samples/basic_usage.rb +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# Basic FastPix Ruby SDK Usage Example -# This example demonstrates basic SDK setup, authentication, and simple operations - -require 'fastpixapi' - -# Configuration -USERNAME = ENV['FASTPIX_USERNAME'] || 'your_username_here' -PASSWORD = ENV['FASTPIX_PASSWORD'] || 'your_password_here' - -puts "🚀 FastPix Ruby SDK - Basic Usage Example" -puts "=" * 50 - -begin - # Initialize the SDK - puts "\n1. Initializing FastPix SDK..." - sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - puts "✅ SDK initialized successfully" - - # Test basic connectivity by listing media - puts "\n2. Testing API connectivity..." - begin - media_response = sdk.manage_videos.list_media(limit: 5) - - if media_response.status_code == 200 - puts "✅ API connection successful" - puts " Status: #{media_response.status_code}" - puts " Media count: #{media_response.object&.data&.length || 0}" - else - puts "⚠️ API responded with status: #{media_response.status_code}" - end - rescue => e - puts "❌ API connection failed: #{e.message}" - end - - # Test live stream listing - puts "\n3. Testing live stream operations..." - begin - streams_response = sdk.manage_live_stream.get_all_streams(limit: 5) - - if streams_response.status_code == 200 - puts "✅ Live streams API accessible" - puts " Status: #{streams_response.status_code}" - puts " Streams count: #{streams_response.object&.data&.length || 0}" - else - puts "⚠️ Live streams API responded with status: #{streams_response.status_code}" - end - rescue => e - puts "❌ Live streams API failed: #{e.message}" - end - - # Test analytics - puts "\n4. Testing analytics operations..." - begin - views_response = sdk.views.list_video_views(request: FastpixApiSDK::Models::Operations::ListVideoViewsRequest.new(limit: 5, timespan: FastpixApiSDK::Models::Operations::ListVideoViewsRequestTimespan::SEVENDAYS)) - - if views_response.status_code == 200 - puts "✅ Analytics API accessible" - puts " Status: #{views_response.status_code}" - puts " Views count: #{views_response.object&.data&.length || 0}" - else - puts "⚠️ Analytics API responded with status: #{views_response.status_code}" - end - rescue => e - puts "❌ Analytics API failed: #{e.message}" - end - - puts "\n🎉 Basic usage example completed!" - puts "\nNext steps:" - puts "- Check out media_upload.rb for media operations" - puts "- Check out live_streaming.rb for live stream operations" - puts "- Check out analytics.rb for data insights" - -rescue => e - puts "❌ SDK initialization failed: #{e.message}" - puts "\nTroubleshooting:" - puts "1. Verify your credentials are correct" - puts "2. Check your internet connection" - puts "3. Ensure you're using Ruby 3.2+" - puts "4. Make sure the fastpixapi gem is installed" -end diff --git a/samples/configuration.rb b/samples/configuration.rb deleted file mode 100644 index 0249a83..0000000 --- a/samples/configuration.rb +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# FastPix Ruby SDK - Configuration Examples -# This example demonstrates various SDK configuration options - -require 'fastpixapi' - -# Configuration -USERNAME = ENV['FASTPIX_USERNAME'] || 'your_username_here' -PASSWORD = ENV['FASTPIX_PASSWORD'] || 'your_password_here' - -puts "⚙️ FastPix Ruby SDK - Configuration Examples" -puts "=" * 50 - -begin - # Example 1: Basic SDK initialization - puts "\n1. Basic SDK initialization..." - sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - puts "✅ Basic SDK initialized successfully" - - # Example 2: SDK with custom configuration - puts "\n2. SDK with custom configuration..." - custom_sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - puts "✅ Custom SDK initialized successfully" - - # Example 3: SDK configuration details - puts "\n3. SDK configuration details..." - config = custom_sdk.sdk_configuration - puts " Server URL: #{config.server_url}" - puts " SDK Version: #{config.sdk_version}" - puts " OpenAPI Doc Version: #{config.openapi_doc_version}" - puts " Language: #{config.language}" - puts " User Agent: #{config.user_agent}" - puts " Timeout: #{config.timeout} seconds" - - # Example 4: Test different server configurations - puts "\n4. Testing different server configurations..." - - # Test with default server - begin - response = sdk.manage_videos.list_media(limit: 1) - puts " ✅ Default server connection: #{response.status_code}" - rescue => e - puts " ❌ Default server connection failed: #{e.message}" - end - - # Test with custom server - begin - response = custom_sdk.manage_videos.list_media(limit: 1) - puts " ✅ Custom server connection: #{response.status_code}" - rescue => e - puts " ❌ Custom server connection failed: #{e.message}" - end - - # Example 5: Retry configuration testing - puts "\n5. Testing retry configuration..." - - # Create SDK with aggressive retry settings for testing - retry_sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - - begin - response = retry_sdk.manage_videos.list_media(limit: 1) - puts " ✅ Retry configuration test: #{response.status_code}" - rescue => e - puts " ❌ Retry configuration test failed: #{e.message}" - end - - # Example 6: Timeout configuration testing - puts "\n6. Testing timeout configuration..." - - # Create SDK with short timeout for testing - timeout_sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - - begin - response = timeout_sdk.manage_videos.list_media(limit: 1) - puts " ✅ Timeout configuration test: #{response.status_code}" - rescue => e - puts " ❌ Timeout configuration test failed: #{e.message}" - end - - # Example 7: Environment-based configuration - puts "\n7. Environment-based configuration..." - - # Check environment variables - puts " Environment variables:" - puts " - FASTPIX_USERNAME: #{ENV['FASTPIX_USERNAME'] ? 'Set' : 'Not set'}" - puts " - FASTPIX_PASSWORD: #{ENV['FASTPIX_PASSWORD'] ? 'Set' : 'Not set'}" - puts " - FASTPIX_SERVER_URL: #{ENV['FASTPIX_SERVER_URL'] || 'Not set'}" - puts " - FASTPIX_TIMEOUT: #{ENV['FASTPIX_TIMEOUT'] || 'Not set'}" - - # Create SDK using environment variables - env_sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: ENV['FASTPIX_USERNAME'] || USERNAME, - password: ENV['FASTPIX_PASSWORD'] || PASSWORD - ), - server_url: ENV['FASTPIX_SERVER_URL'], - timeout: ENV['FASTPIX_TIMEOUT']&.to_i - ) - - begin - response = env_sdk.manage_videos.list_media(limit: 1) - puts " ✅ Environment-based configuration test: #{response.status_code}" - rescue => e - puts " ❌ Environment-based configuration test failed: #{e.message}" - end - - # Example 8: Configuration validation - puts "\n8. Configuration validation..." - - def validate_sdk_config(sdk, name) - puts " Validating #{name} configuration..." - - config = sdk.sdk_configuration - - # Check required fields - required_fields = [:server_url, :sdk_version, :timeout] - missing_fields = required_fields.select { |field| config.send(field).nil? } - - if missing_fields.empty? - puts " ✅ All required fields present" - else - puts " ❌ Missing required fields: #{missing_fields.join(', ')}" - end - - # Check timeout value - if config.timeout && config.timeout > 0 - puts " ✅ Timeout value is valid: #{config.timeout} seconds" - else - puts " ⚠️ Timeout value may be invalid: #{config.timeout}" - end - - # Check server URL format - if config.server_url && config.server_url.match?(/\Ahttps?:\/\//) - puts " ✅ Server URL format is valid: #{config.server_url}" - else - puts " ⚠️ Server URL format may be invalid: #{config.server_url}" - end - end - - validate_sdk_config(sdk, "Basic SDK") - validate_sdk_config(custom_sdk, "Custom SDK") - - # Example 9: Configuration comparison - puts "\n9. Configuration comparison..." - - basic_config = sdk.sdk_configuration - custom_config = custom_sdk.sdk_configuration - - puts " Configuration differences:" - puts " - Server URL: #{basic_config.server_url} vs #{custom_config.server_url}" - puts " - Timeout: #{basic_config.timeout} vs #{custom_config.timeout}" - puts " - SDK Version: #{basic_config.sdk_version} vs #{custom_config.sdk_version}" - - # Example 10: Best practices - puts "\n10. Configuration best practices..." - puts " ✅ Use environment variables for sensitive data (credentials)" - puts " ✅ Set appropriate timeouts based on your use case" - puts " ✅ Configure retry logic for production environments" - puts " ✅ Use HTTPS URLs for production" - puts " ✅ Validate configuration before making API calls" - puts " ✅ Log configuration details for debugging (without sensitive data)" - - puts "\n🎉 Configuration examples completed!" - puts "\nNext steps:" - puts "- Use environment variables for production deployments" - puts "- Configure appropriate timeouts and retry logic" - puts "- Test your configuration in different environments" - puts "- Monitor API performance and adjust configuration as needed" - -rescue => e - puts "❌ Example failed: #{e.message}" - puts "\nTroubleshooting:" - puts "1. Verify your credentials are correct" - puts "2. Check your internet connection" - puts "3. Ensure configuration values are valid" - puts "4. Review the configuration options in the SDK documentation" -end diff --git a/samples/drm_configuration.rb b/samples/drm_configuration.rb deleted file mode 100644 index 88a85e8..0000000 --- a/samples/drm_configuration.rb +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# FastPix Ruby SDK - DRM Configuration Examples -# This example demonstrates DRM (Digital Rights Management) configuration - -require 'fastpixapi' - -# Configuration -USERNAME = ENV['FASTPIX_USERNAME'] || 'your_username_here' -PASSWORD = ENV['FASTPIX_PASSWORD'] || 'your_password_here' - -puts "🔒 FastPix Ruby SDK - DRM Configuration Examples" -puts "=" * 50 - -begin - # Initialize the SDK - sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - puts "✅ SDK initialized successfully" - - # Example 1: List DRM configurations - puts "\n1. Listing DRM configurations..." - begin - response = sdk.drm_configurations.get_drm_configuration(limit: 10) - - if response.status_code == 200 - puts "✅ DRM configurations retrieved successfully" - puts " Total configurations: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Recent DRM configurations:" - response.object.data.first(3).each_with_index do |config, index| - puts " #{index + 1}. ID: #{config.id}" - puts " Name: #{config.name}" - puts " Type: #{config.type}" - puts " Created: #{config.created_at || 'Unknown'}" - end - else - puts " ℹ️ No DRM configurations found" - puts " This is normal for new accounts or test environments" - end - else - puts "⚠️ DRM configurations listing failed with status: #{response.status_code}" - end - rescue => e - puts "❌ DRM configurations listing failed: #{e.message}" - end - - # Example 2: Get DRM configuration by ID (if we have one) - puts "\n2. Getting DRM configuration by ID..." - begin - # First, try to get a DRM configuration ID from the list - list_response = sdk.drm_configurations.get_drm_configuration(limit: 1) - - if list_response.status_code == 200 && - list_response.object&.data&.any? && - list_response.object.data.first&.id - - config_id = list_response.object.data.first.id - puts " Using DRM configuration ID: #{config_id}" - - response = sdk.drm_configurations.get_drm_configuration_by_id( - drm_configuration_id: config_id - ) - - if response.status_code == 200 - puts "✅ DRM configuration details retrieved successfully" - config = response.object&.data - puts " ID: #{config&.id}" - puts " Name: #{config&.name}" - puts " Type: #{config&.type}" - puts " Status: #{config&.status}" - puts " Created: #{config&.created_at}" - else - puts "⚠️ DRM configuration details retrieval failed with status: #{response.status_code}" - end - else - puts "ℹ️ No DRM configurations available to retrieve details for" - end - rescue => e - puts "❌ DRM configuration details retrieval failed: #{e.message}" - end - - # Example 3: List DRM configurations with pagination - puts "\n3. Listing DRM configurations with pagination..." - begin - response = sdk.drm_configurations.get_drm_configuration( - limit: 5, - offset: 0 - ) - - if response.status_code == 200 - puts "✅ Paginated DRM configurations retrieved successfully" - puts " Configurations count: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " DRM configurations:" - response.object.data.each_with_index do |config, index| - puts " #{index + 1}. ID: #{config.id}" - puts " Name: #{config.name}" - puts " Type: #{config.type}" - puts " Created: #{config.created_at || 'Unknown'}" - end - end - else - puts "⚠️ Paginated DRM configurations retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Paginated DRM configurations retrieval failed: #{e.message}" - end - - # Example 4: List DRM configurations with filters - puts "\n4. Listing DRM configurations with filters..." - begin - # Try to filter by type (if supported) - response = sdk.drm_configurations.get_drm_configuration( - limit: 10, - type: 'widevine' # Example filter - ) - - if response.status_code == 200 - puts "✅ Filtered DRM configurations retrieved successfully" - puts " Configurations count: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Filtered DRM configurations:" - response.object.data.first(3).each_with_index do |config, index| - puts " #{index + 1}. ID: #{config.id}" - puts " Name: #{config.name}" - puts " Type: #{config.type}" - end - end - else - puts "⚠️ Filtered DRM configurations retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Filtered DRM configurations retrieval failed: #{e.message}" - end - - # Example 5: DRM configuration search - puts "\n5. Searching DRM configurations..." - begin - # Try to search by name or other criteria - response = sdk.drm_configurations.get_drm_configuration( - limit: 10, - search: 'sample' # Example search term - ) - - if response.status_code == 200 - puts "✅ DRM configuration search completed successfully" - puts " Search results count: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Search results:" - response.object.data.first(3).each_with_index do |config, index| - puts " #{index + 1}. ID: #{config.id}" - puts " Name: #{config.name}" - puts " Type: #{config.type}" - end - end - else - puts "⚠️ DRM configuration search failed with status: #{response.status_code}" - end - rescue => e - puts "❌ DRM configuration search failed: #{e.message}" - end - - # Example 6: DRM configuration statistics - puts "\n6. Getting DRM configuration statistics..." - begin - # Get all configurations to calculate statistics - response = sdk.drm_configurations.get_drm_configuration(limit: 100) - - if response.status_code == 200 - puts "✅ DRM configuration statistics retrieved successfully" - configs = response.object&.data || [] - - puts " Statistics:" - puts " - Total configurations: #{configs.length}" - - # Group by type - type_counts = configs.group_by(&:type).transform_values(&:length) - if type_counts.any? - puts " - By type:" - type_counts.each do |type, count| - puts " * #{type}: #{count}" - end - end - - # Group by status - status_counts = configs.group_by(&:status).transform_values(&:length) - if status_counts.any? - puts " - By status:" - status_counts.each do |status, count| - puts " * #{status}: #{count}" - end - end - else - puts "⚠️ DRM configuration statistics retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ DRM configuration statistics retrieval failed: #{e.message}" - end - - puts "\n🎉 DRM configuration examples completed!" - puts "\nKey concepts:" - puts "- DRM configurations control how content is protected and accessed" - puts "- Different DRM types (Widevine, PlayReady, FairPlay) support different platforms" - puts "- DRM configurations determine encryption and licensing policies" - puts "- Use appropriate DRM types for your target platforms and devices" - puts "- DRM configurations are typically managed by content administrators" - -rescue => e - puts "❌ Example failed: #{e.message}" - puts "\nTroubleshooting:" - puts "1. Verify your credentials are correct" - puts "2. Check your internet connection" - puts "3. Ensure you have proper permissions for DRM operations" - puts "4. Some DRM operations may require specific account permissions" - puts "5. DRM configurations may not be available in all environments" -end diff --git a/samples/error_handling.rb b/samples/error_handling.rb deleted file mode 100644 index f902777..0000000 --- a/samples/error_handling.rb +++ /dev/null @@ -1,240 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# FastPix Ruby SDK - Error Handling Examples -# This example demonstrates comprehensive error handling patterns - -require 'fastpixapi' - -# Configuration -USERNAME = ENV['FASTPIX_USERNAME'] || 'your_username_here' -PASSWORD = ENV['FASTPIX_PASSWORD'] || 'your_password_here' - -puts "⚠️ FastPix Ruby SDK - Error Handling Examples" -puts "=" * 50 - -begin - # Initialize the SDK - sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - puts "✅ SDK initialized successfully" - - # Example 1: Handle authentication errors - puts "\n1. Testing authentication error handling..." - begin - # Try with invalid credentials - invalid_sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: 'invalid_username', - password: 'invalid_password' - ) - ) - - response = invalid_sdk.manage_videos.list_media(limit: 1) - puts "⚠️ Expected authentication error but got status: #{response.status_code}" - rescue FastpixApiSDK::Models::Errors::UnauthorizedError => e - puts "✅ Caught UnauthorizedError as expected" - puts " Error: #{e.message}" - rescue FastpixApiSDK::Models::Errors::UnauthorizedResponseError => e - puts "✅ Caught UnauthorizedResponseError as expected" - puts " Error: #{e.message}" - rescue => e - puts "ℹ️ Caught other error: #{e.class.name} - #{e.message}" - end - - # Example 2: Handle validation errors - puts "\n2. Testing validation error handling..." - begin - # Try to create media with invalid data - invalid_request = FastpixApiSDK::Models::Components::CreateMediaRequest.new( - inputs: [], # Empty inputs should cause validation error - access_policy: FastpixApiSDK::Models::Components::CreateMediaRequestAccessPolicy::PUBLIC - ) - - response = sdk.input_video.create_media(request: invalid_request) - - if response.status_code == 400 - puts "✅ Validation error caught as expected" - puts " Status: #{response.status_code}" - else - puts "⚠️ Expected validation error but got status: #{response.status_code}" - end - rescue FastpixApiSDK::Models::Errors::ValidationErrorResponse => e - puts "✅ Caught ValidationErrorResponse as expected" - puts " Error: #{e.message}" - rescue => e - puts "ℹ️ Caught other error: #{e.class.name} - #{e.message}" - end - - # Example 3: Handle not found errors - puts "\n3. Testing not found error handling..." - begin - # Try to get a non-existent media - response = sdk.manage_videos.get_media(media_id: 'non-existent-id') - - if response.status_code == 404 - puts "✅ Not found error caught as expected" - puts " Status: #{response.status_code}" - else - puts "⚠️ Expected not found error but got status: #{response.status_code}" - end - rescue FastpixApiSDK::Models::Errors::NotFoundError => e - puts "✅ Caught NotFoundError as expected" - puts " Error: #{e.message}" - rescue FastpixApiSDK::Models::Errors::MediaNotFoundError => e - puts "✅ Caught MediaNotFoundError as expected" - puts " Error: #{e.message}" - rescue => e - puts "ℹ️ Caught other error: #{e.class.name} - #{e.message}" - end - - # Example 4: Handle bad request errors - puts "\n4. Testing bad request error handling..." - begin - # Try to get DRM configuration with invalid parameters - response = sdk.drm_configurations.get_drm_configuration(limit: -1) # Invalid limit - - if response.status_code == 400 - puts "✅ Bad request error caught as expected" - puts " Status: #{response.status_code}" - else - puts "⚠️ Expected bad request error but got status: #{response.status_code}" - end - rescue FastpixApiSDK::Models::Errors::BadRequestError => e - puts "✅ Caught BadRequestError as expected" - puts " Error: #{e.message}" - rescue => e - puts "ℹ️ Caught other error: #{e.class.name} - #{e.message}" - end - - # Example 5: Handle forbidden errors - puts "\n5. Testing forbidden error handling..." - begin - # Try to access a resource that might be forbidden - response = sdk.signing_keys.get_signing_key_by_id(signing_key_id: 'forbidden-key-id') - - if response.status_code == 403 - puts "✅ Forbidden error caught as expected" - puts " Status: #{response.status_code}" - else - puts "⚠️ Expected forbidden error but got status: #{response.status_code}" - end - rescue FastpixApiSDK::Models::Errors::ForbiddenError => e - puts "✅ Caught ForbiddenError as expected" - puts " Error: #{e.message}" - rescue => e - puts "ℹ️ Caught other error: #{e.class.name} - #{e.message}" - end - - # Example 6: Comprehensive error handling wrapper - puts "\n6. Testing comprehensive error handling wrapper..." - - def safe_api_call(description, &_block) - puts " #{description}..." - begin - result = yield - if result.respond_to?(:status_code) && result.status_code >= 200 && result.status_code < 300 - puts " ✅ Success: #{result.status_code}" - return result - else - puts " ⚠️ API returned status: #{result.status_code}" - return result - end - rescue FastpixApiSDK::Models::Errors::ValidationErrorResponse => e - puts " ❌ Validation Error: #{e.message}" - return nil - rescue FastpixApiSDK::Models::Errors::BadRequestError => e - puts " ❌ Bad Request Error: #{e.message}" - return nil - rescue FastpixApiSDK::Models::Errors::NotFoundError => e - puts " ❌ Not Found Error: #{e.message}" - return nil - rescue FastpixApiSDK::Models::Errors::UnauthorizedError => e - puts " ❌ Unauthorized Error: #{e.message}" - return nil - rescue FastpixApiSDK::Models::Errors::ForbiddenError => e - puts " ❌ Forbidden Error: #{e.message}" - return nil - rescue => e - puts " ❌ Unexpected Error: #{e.class.name} - #{e.message}" - return nil - end - end - - # Test the wrapper with various operations - safe_api_call("Listing media") do - sdk.manage_videos.list_media(limit: 5) - end - - safe_api_call("Getting non-existent media") do - sdk.manage_videos.get_media(media_id: 'definitely-does-not-exist') - end - - safe_api_call("Creating invalid media request") do - invalid_request = FastpixApiSDK::Models::Components::CreateMediaRequest.new( - inputs: [], - access_policy: FastpixApiSDK::Models::Components::CreateMediaRequestAccessPolicy::PUBLIC - ) - sdk.input_video.create_media(request: invalid_request) - end - - # Example 7: Retry logic for transient errors - puts "\n7. Testing retry logic for transient errors..." - - def api_call_with_retry(description, max_retries: 3, &_block) - puts " #{description} (with retry logic)..." - - retries = 0 - loop do - begin - result = yield - if result.respond_to?(:status_code) && result.status_code >= 200 && result.status_code < 300 - puts " ✅ Success on attempt #{retries + 1}: #{result.status_code}" - return result - elsif result.respond_to?(:status_code) && result.status_code >= 500 && retries < max_retries - retries += 1 - puts " ⚠️ Server error on attempt #{retries}, retrying in #{retries * 2} seconds..." - sleep(retries * 2) - next - else - puts " ❌ Failed after #{retries + 1} attempts: #{result.status_code}" - return result - end - rescue => e - if retries < max_retries - retries += 1 - puts " ⚠️ Exception on attempt #{retries}, retrying in #{retries * 2} seconds: #{e.message}" - sleep(retries * 2) - next - else - puts " ❌ Failed after #{retries + 1} attempts: #{e.class.name} - #{e.message}" - return nil - end - end - end - end - - # Test retry logic - api_call_with_retry("Listing live streams") do - sdk.manage_live_stream.get_all_streams(limit: 5) - end - - puts "\n🎉 Error handling examples completed!" - puts "\nKey takeaways:" - puts "- Always handle specific error types for better user experience" - puts "- Implement retry logic for transient errors" - puts "- Use comprehensive error handling wrappers" - puts "- Log errors appropriately for debugging" - puts "- Provide meaningful error messages to users" - -rescue => e - puts "❌ Example failed: #{e.message}" - puts "\nTroubleshooting:" - puts "1. Verify your credentials are correct" - puts "2. Check your internet connection" - puts "3. Review the error handling patterns in this example" -end diff --git a/samples/live_streaming.rb b/samples/live_streaming.rb deleted file mode 100644 index a09f14c..0000000 --- a/samples/live_streaming.rb +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# FastPix Ruby SDK - Live Streaming Examples -# This example demonstrates live stream creation and management - -require 'fastpixapi' - -# Configuration -USERNAME = ENV['FASTPIX_USERNAME'] || 'your_username_here' -PASSWORD = ENV['FASTPIX_PASSWORD'] || 'your_password_here' - -puts "📺 FastPix Ruby SDK - Live Streaming Examples" -puts "=" * 50 - -begin - # Initialize the SDK - sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - puts "✅ SDK initialized successfully" - - # Example 1: List existing live streams - puts "\n1. Listing existing live streams..." - begin - response = sdk.manage_live_stream.get_all_streams(limit: 10) - - if response.status_code == 200 - puts "✅ Live streams retrieved successfully" - puts " Total streams: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Recent streams:" - response.object.data.first(3).each_with_index do |stream, index| - puts " #{index + 1}. ID: #{stream.id}" - puts " Status: #{stream.status || 'Unknown'}" - puts " Created: #{stream.created_at || 'Unknown'}" - end - end - else - puts "⚠️ Live streams listing failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Live streams listing failed: #{e.message}" - end - - # Example 2: Create a new live stream - puts "\n2. Creating a new live stream..." - begin - create_request = FastpixApiSDK::Models::Components::CreateLiveStreamRequest.new( - name: "Sample Live Stream #{Time.now.strftime('%Y%m%d_%H%M%S')}", - description: "Sample live stream created via Ruby SDK", - access_policy: FastpixApiSDK::Models::Components::CreateLiveStreamRequestAccessPolicy::PUBLIC, - metadata: { - 'source' => 'ruby_sdk_sample', - 'created_at' => Time.now.iso8601 - } - ) - - response = sdk.start_live_stream.create_new_stream(request: create_request) - - if response.status_code == 201 - puts "✅ Live stream created successfully" - stream = response.object&.data - puts " Stream ID: #{stream&.id}" - puts " Stream Key: #{stream&.stream_key}" - puts " RTMP URL: #{stream&.rtmp_url}" - puts " Status: #{stream&.status}" - - # Store stream ID for later operations - @stream_id = stream&.id - else - puts "⚠️ Live stream creation failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Live stream creation failed: #{e.message}" - end - - # Example 3: Get live stream details (if we created one) - if @stream_id - puts "\n3. Getting live stream details..." - begin - response = sdk.manage_live_stream.get_live_stream_by_id(stream_id: @stream_id) - - if response.status_code == 200 - puts "✅ Live stream details retrieved successfully" - stream = response.object&.data - puts " ID: #{stream&.id}" - puts " Name: #{stream&.name}" - puts " Status: #{stream&.status}" - puts " Stream Key: #{stream&.stream_key}" - puts " RTMP URL: #{stream&.rtmp_url}" - puts " Created: #{stream&.created_at}" - else - puts "⚠️ Live stream details retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Live stream details retrieval failed: #{e.message}" - end - end - - # Example 4: Create playback ID for live stream - if @stream_id - puts "\n4. Creating playback ID for live stream..." - begin - request_body = FastpixApiSDK::Models::Operations::CreatePlaybackIdOfStreamRequestBody.new( - access_policy: FastpixApiSDK::Models::Components::PlaybackIdRequestAccessPolicy::PUBLIC - ) - - response = sdk.live_playback.create_playback_id_of_stream( - stream_id: @stream_id, - request_body: request_body - ) - - if response.status_code == 201 - puts "✅ Playback ID created successfully" - playback = response.object&.data - puts " Playback ID: #{playback&.playback_id}" - puts " Access Policy: #{playback&.access_policy}" - puts " Created: #{playback&.created_at}" - - # Store playback ID for later operations - @playback_id = playback&.playback_id - else - puts "⚠️ Playback ID creation failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Playback ID creation failed: #{e.message}" - end - end - - # Example 5: List live stream clips - if @stream_id - puts "\n5. Listing live stream clips..." - begin - response = sdk.live_playback.list_live_clips(stream_id: @stream_id, limit: 10) - - if response.status_code == 200 - puts "✅ Live stream clips retrieved successfully" - puts " Total clips: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Recent clips:" - response.object.data.first(3).each_with_index do |clip, index| - puts " #{index + 1}. ID: #{clip.id}" - puts " Duration: #{clip.duration} seconds" if clip.duration - puts " Created: #{clip.created_at || 'Unknown'}" - end - end - else - puts "⚠️ Live stream clips listing failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Live stream clips listing failed: #{e.message}" - end - end - - # Example 6: Update live stream (if we created one) - if @stream_id - puts "\n6. Updating live stream..." - begin - update_request = FastpixApiSDK::Models::Components::PatchLiveStreamRequest.new( - name: "Updated Live Stream #{Time.now.strftime('%Y%m%d_%H%M%S')}", - description: "Updated live stream description", - metadata: { - 'updated_at' => Time.now.iso8601, - 'updated_by' => 'ruby_sdk' - } - ) - - response = sdk.manage_live_stream.update_live_stream( - stream_id: @stream_id, - request: update_request - ) - - if response.status_code == 200 - puts "✅ Live stream updated successfully" - stream = response.object&.data - puts " Updated Name: #{stream&.name}" - puts " Updated Description: #{stream&.description}" - else - puts "⚠️ Live stream update failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Live stream update failed: #{e.message}" - end - end - - puts "\n🎉 Live streaming examples completed!" - puts "\nNext steps:" - puts "- Check out live_playback.rb for more playback operations" - puts "- Check out simulcast.rb for simulcast streaming" - puts "- Use the stream key and RTMP URL to start streaming from your encoder" - -rescue => e - puts "❌ Example failed: #{e.message}" - puts "\nTroubleshooting:" - puts "1. Verify your credentials are correct" - puts "2. Check your internet connection" - puts "3. Ensure you have proper permissions for live streaming operations" -end diff --git a/samples/media_upload.rb b/samples/media_upload.rb deleted file mode 100644 index 0bbc148..0000000 --- a/samples/media_upload.rb +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# FastPix Ruby SDK - Media Upload Examples -# This example demonstrates various media upload methods - -require 'fastpixapi' - -# Configuration -USERNAME = ENV['FASTPIX_USERNAME'] || 'your_username_here' -PASSWORD = ENV['FASTPIX_PASSWORD'] || 'your_password_here' - -puts "📹 FastPix Ruby SDK - Media Upload Examples" -puts "=" * 50 - -begin - # Initialize the SDK - sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - puts "✅ SDK initialized successfully" - - # Example 1: Upload media from URL - puts "\n1. Uploading media from URL..." - begin - create_request = FastpixApiSDK::Models::Components::CreateMediaRequest.new( - inputs: [ - FastpixApiSDK::Models::Components::VideoInput.new( - type: 'video', - url: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' - ) - ], - access_policy: FastpixApiSDK::Models::Components::CreateMediaRequestAccessPolicy::PUBLIC, - metadata: { - 'source' => 'sample_upload', - 'description' => 'Sample video upload from URL' - } - ) - - response = sdk.input_video.create_media(request: create_request) - - if response.status_code == 201 - puts "✅ Media uploaded successfully from URL" - puts " Media ID: #{response.object&.data&.first&.id}" - puts " Status: #{response.status_code}" - else - puts "⚠️ Upload failed with status: #{response.status_code}" - end - rescue => e - puts "❌ URL upload failed: #{e.message}" - end - - # Example 2: Direct upload (get upload URL) - puts "\n2. Creating direct upload URL..." - begin - response = sdk.input_video.direct_upload_video_media( - cors_origin: 'https://example.com', - push_media_settings: FastpixApiSDK::Models::Operations::PushMediaSettings.new( - access_policy: FastpixApiSDK::Models::Components::BasicAccessPolicy::PUBLIC, - metadata: { - 'upload_type' => 'direct_upload', - 'description' => 'Direct upload example' - } - ) - ) - - if response.status_code == 201 - puts "✅ Direct upload URL created successfully" - puts " Upload ID: #{response.object&.data&.id}" - puts " Upload URL: #{response.object&.data&.url[0..100]}..." - puts " Timeout: #{response.object&.data&.timeout} seconds" - else - puts "⚠️ Direct upload creation failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Direct upload creation failed: #{e.message}" - end - - # Example 3: List existing media - puts "\n3. Listing existing media..." - begin - response = sdk.manage_videos.list_media(limit: 10) - - if response.status_code == 200 - puts "✅ Media list retrieved successfully" - puts " Total media: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Recent media:" - response.object.data.first(3).each_with_index do |media, index| - puts " #{index + 1}. ID: #{media.id}" - puts " Status: #{media.status || 'Unknown'}" - puts " Created: #{media.created_at || 'Unknown'}" - end - end - else - puts "⚠️ Media listing failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Media listing failed: #{e.message}" - end - - # Example 4: Get specific media by ID (if we have one) - puts "\n4. Getting media details..." - begin - # First, try to get a media ID from the list - list_response = sdk.manage_videos.list_media(limit: 1) - - if list_response.status_code == 200 && - list_response.object&.data&.any? && - list_response.object.data.first&.id - - media_id = list_response.object.data.first.id - puts " Using media ID: #{media_id}" - - response = sdk.manage_videos.get_media(media_id: media_id) - - if response.status_code == 200 - puts "✅ Media details retrieved successfully" - media = response.object&.data - puts " ID: #{media&.id}" - puts " Status: #{media&.status}" - puts " Resolution: #{media&.max_resolution}" - puts " Duration: #{media&.duration} seconds" if media&.duration - else - puts "⚠️ Media details retrieval failed with status: #{response.status_code}" - end - else - puts "ℹ️ No media available to retrieve details for" - end - rescue => e - puts "❌ Media details retrieval failed: #{e.message}" - end - - puts "\n🎉 Media upload examples completed!" - puts "\nNext steps:" - puts "- Check out media_management.rb for more media operations" - puts "- Check out media_tracks.rb for adding audio/subtitle tracks" - -rescue => e - puts "❌ Example failed: #{e.message}" - puts "\nTroubleshooting:" - puts "1. Verify your credentials are correct" - puts "2. Check your internet connection" - puts "3. Ensure you have proper permissions for media operations" -end diff --git a/samples/playlist_management.rb b/samples/playlist_management.rb deleted file mode 100644 index c6ca224..0000000 --- a/samples/playlist_management.rb +++ /dev/null @@ -1,257 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# FastPix Ruby SDK - Playlist Management Examples -# This example demonstrates playlist creation and management - -require 'fastpixapi' - -# Configuration -USERNAME = ENV['FASTPIX_USERNAME'] || 'your_username_here' -PASSWORD = ENV['FASTPIX_PASSWORD'] || 'your_password_here' - -puts "📋 FastPix Ruby SDK - Playlist Management Examples" -puts "=" * 50 - -begin - # Initialize the SDK - sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - puts "✅ SDK initialized successfully" - - # Example 1: List existing playlists - puts "\n1. Listing existing playlists..." - begin - response = sdk.playlist.get_all_playlists(limit: 10) - - if response.status_code == 200 - puts "✅ Playlists retrieved successfully" - puts " Total playlists: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Recent playlists:" - response.object.data.first(3).each_with_index do |playlist, index| - puts " #{index + 1}. ID: #{playlist.id}" - puts " Name: #{playlist.name}" - puts " Type: #{playlist.type}" - puts " Created: #{playlist.created_at || 'Unknown'}" - end - end - else - puts "⚠️ Playlists listing failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Playlists listing failed: #{e.message}" - end - - # Example 2: Create a new playlist - puts "\n2. Creating a new playlist..." - begin - create_request = FastpixApiSDK::Models::Components::CreatePlaylistRequest.new( - name: "Sample Playlist #{Time.now.strftime('%Y%m%d_%H%M%S')}", - reference_id: "sample-playlist-#{Time.now.to_i}", - type: FastpixApiSDK::Models::Components::CreatePlaylistRequestType::MANUAL, - description: "Sample playlist created via Ruby SDK", - metadata: { - 'source' => 'ruby_sdk_sample', - 'created_at' => Time.now.iso8601 - } - ) - - response = sdk.playlist.create_a_playlist(request: create_request) - - if response.status_code == 201 - puts "✅ Playlist created successfully" - playlist = response.object&.data - puts " Playlist ID: #{playlist&.id}" - puts " Name: #{playlist&.name}" - puts " Type: #{playlist&.type}" - puts " Reference ID: #{playlist&.reference_id}" - - # Store playlist ID for later operations - @playlist_id = playlist&.id - else - puts "⚠️ Playlist creation failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Playlist creation failed: #{e.message}" - end - - # Example 3: Get playlist details (if we created one) - if @playlist_id - puts "\n3. Getting playlist details..." - begin - response = sdk.playlist.get_playlist_by_id(playlist_id: @playlist_id) - - if response.status_code == 200 - puts "✅ Playlist details retrieved successfully" - playlist = response.object&.data - puts " ID: #{playlist&.id}" - puts " Name: #{playlist&.name}" - puts " Type: #{playlist&.type}" - puts " Description: #{playlist&.description}" - puts " Media count: #{playlist&.media_list&.length || 0}" - puts " Created: #{playlist&.created_at}" - else - puts "⚠️ Playlist details retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Playlist details retrieval failed: #{e.message}" - end - end - - # Example 4: Add media to playlist (if we have a playlist and media) - if @playlist_id - puts "\n4. Adding media to playlist..." - begin - # First, try to get a media ID from the list - media_response = sdk.manage_videos.list_media(limit: 1) - - if media_response.status_code == 200 && - media_response.object&.data&.any? && - media_response.object.data.first&.id - - media_id = media_response.object.data.first.id - puts " Using media ID: #{media_id}" - - request_body = FastpixApiSDK::Models::Operations::AddMediaToPlaylistRequestBody.new( - media_id: media_id - ) - - response = sdk.playlist.add_media_to_playlist( - playlist_id: @playlist_id, - request_body: request_body - ) - - if response.status_code == 201 - puts "✅ Media added to playlist successfully" - puts " Media ID: #{media_id}" - puts " Playlist ID: #{@playlist_id}" - else - puts "⚠️ Media addition to playlist failed with status: #{response.status_code}" - end - else - puts "ℹ️ No media available to add to playlist" - end - rescue => e - puts "❌ Media addition to playlist failed: #{e.message}" - end - end - - # Example 5: Update playlist - if @playlist_id - puts "\n5. Updating playlist..." - begin - update_request = FastpixApiSDK::Models::Components::UpdatePlaylistRequest.new( - name: "Updated Playlist #{Time.now.strftime('%Y%m%d_%H%M%S')}", - description: "Updated playlist description", - metadata: { - 'updated_at' => Time.now.iso8601, - 'updated_by' => 'ruby_sdk' - } - ) - - response = sdk.playlist.update_a_playlist( - playlist_id: @playlist_id, - request: update_request - ) - - if response.status_code == 200 - puts "✅ Playlist updated successfully" - playlist = response.object&.data - puts " Updated Name: #{playlist&.name}" - puts " Updated Description: #{playlist&.description}" - else - puts "⚠️ Playlist update failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Playlist update failed: #{e.message}" - end - end - - # Example 6: List playlist media - if @playlist_id - puts "\n6. Listing playlist media..." - begin - response = sdk.playlist.get_playlist_by_id(playlist_id: @playlist_id) - - if response.status_code == 200 - puts "✅ Playlist media retrieved successfully" - playlist = response.object&.data - media_list = playlist&.media_list || [] - puts " Media count: #{media_list.length}" - - if media_list.any? - puts " Media in playlist:" - media_list.first(5).each_with_index do |media, index| - puts " #{index + 1}. Media ID: #{media.id}" - puts " Title: #{media.title || 'Unknown'}" - puts " Duration: #{media.duration} seconds" if media.duration - end - end - else - puts "⚠️ Playlist media retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Playlist media retrieval failed: #{e.message}" - end - end - - # Example 7: Change media order in playlist - if @playlist_id - puts "\n7. Changing media order in playlist..." - begin - # First, get the current media list - playlist_response = sdk.playlist.get_playlist_by_id(playlist_id: @playlist_id) - - if playlist_response.status_code == 200 && - playlist_response.object&.data&.media_list&.length && - playlist_response.object.data.media_list.length > 1 - - media_list = playlist_response.object.data.media_list - puts " Current media order: #{media_list.map(&:id).join(', ')}" - - # Reverse the order as an example - reversed_order = media_list.reverse.map(&:id) - puts " New media order: #{reversed_order.join(', ')}" - - request_body = FastpixApiSDK::Models::Operations::ChangeMediaOrderInPlaylistRequestBody.new( - media_ids: reversed_order - ) - - response = sdk.playlist.change_media_order_in_playlist( - playlist_id: @playlist_id, - request_body: request_body - ) - - if response.status_code == 200 - puts "✅ Media order changed successfully" - else - puts "⚠️ Media order change failed with status: #{response.status_code}" - end - else - puts "ℹ️ Not enough media in playlist to change order" - end - rescue => e - puts "❌ Media order change failed: #{e.message}" - end - end - - puts "\n🎉 Playlist management examples completed!" - puts "\nNext steps:" - puts "- Check out media_management.rb for more media operations" - puts "- Use playlists to organize and manage your video content" - puts "- Integrate playlist functionality into your application" - -rescue => e - puts "❌ Example failed: #{e.message}" - puts "\nTroubleshooting:" - puts "1. Verify your credentials are correct" - puts "2. Check your internet connection" - puts "3. Ensure you have proper permissions for playlist operations" - puts "4. Make sure you have media available to add to playlists" -end diff --git a/samples/run_all_samples.rb b/samples/run_all_samples.rb deleted file mode 100644 index 46e9440..0000000 --- a/samples/run_all_samples.rb +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# FastPix Ruby SDK - Sample Runner -# This script runs all available samples in the correct order - -require 'time' -require 'fastpixapi' - -# Configuration -USERNAME = ENV['FASTPIX_USERNAME'] || 'your_username_here' -PASSWORD = ENV['FASTPIX_PASSWORD'] || 'your_password_here' - -puts "🚀 FastPix Ruby SDK - Sample Runner" -puts "=" * 50 -puts "Starting at: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" -puts "" - -# Check prerequisites -def check_prerequisites - puts "🔍 Checking prerequisites..." - - # Check Ruby version - ruby_version = RUBY_VERSION - if Gem::Version.new(ruby_version) >= Gem::Version.new('3.2') - puts "✅ Ruby version: #{ruby_version} (compatible)" - else - puts "❌ Ruby version: #{ruby_version} (requires 3.2+)" - return false - end - - # Check credentials - if USERNAME == 'your_username_here' || PASSWORD == 'your_password_here' - puts "⚠️ Using placeholder credentials - set FASTPIX_USERNAME and FASTPIX_PASSWORD" - else - puts "✅ Credentials configured" - end - - # Check if fastpixapi gem is available (required at the top of the file) - if defined?(FastpixClient) - puts "✅ FastPix API SDK gem loaded" - else - puts "❌ FastPix API SDK gem not found" - puts " Install with: gem install fastpixapi" - return false - end - - puts "" - true -end - -# Run a sample with error handling -def run_sample(sample_name, sample_file) - puts "📁 Running #{sample_name}..." - puts "-" * 30 - - start_time = Time.now - - begin - # Load and run the sample - load sample_file - end_time = Time.now - duration = (end_time - start_time).round(2) - - puts "" - puts "✅ #{sample_name} completed in #{duration}s" - puts "" - - true - rescue => e - end_time = Time.now - duration = (end_time - start_time).round(2) - - puts "" - puts "❌ #{sample_name} failed after #{duration}s: #{e.message}" - puts "" - - false - end -end - -# Run a single sample, updating results. Returns :stop to halt the run, else :continue. -def run_one_sample(sample, index, total, results) - puts "🔄 Sample #{index + 1}/#{total}: #{sample[:name]}" - - unless File.exist?(sample[:file]) - puts "❌ Sample file not found: #{sample[:file]}" - results[:skipped] += 1 - return :continue - end - - if run_sample(sample[:name], sample[:file]) - results[:passed] += 1 - return :continue - end - - results[:failed] += 1 - # If it's a required sample, ask if we should continue - if sample[:required] - print " This is a required sample. Continue with remaining samples? (y/n): " - response = gets.chomp.downcase - unless response == 'y' || response == 'yes' - puts " Stopping execution due to required sample failure." - return :stop - end - end - - :continue -end - -# Main execution -def main - # Check prerequisites - unless check_prerequisites - puts "❌ Prerequisites check failed. Please fix the issues above and try again." - exit 1 - end - - # Define samples to run in order - samples = [ - { - name: "Basic Usage", - file: "samples/basic_usage.rb", - required: true - }, - { - name: "Configuration", - file: "samples/configuration.rb", - required: false - }, - { - name: "Error Handling", - file: "samples/error_handling.rb", - required: false - }, - { - name: "Media Upload", - file: "samples/media_upload.rb", - required: true - }, - { - name: "Live Streaming", - file: "samples/live_streaming.rb", - required: false - }, - { - name: "Playlist Management", - file: "samples/playlist_management.rb", - required: false - }, - { - name: "Analytics", - file: "samples/analytics.rb", - required: false - }, - { - name: "AI Features", - file: "samples/ai_features.rb", - required: false - }, - { - name: "Signing Keys", - file: "samples/signing_keys.rb", - required: false - }, - { - name: "DRM Configuration", - file: "samples/drm_configuration.rb", - required: false - } - ] - - # Track results - results = { - passed: 0, - failed: 0, - skipped: 0 - } - - start_time = Time.now - - # Run each sample - samples.each_with_index do |sample, index| - break if run_one_sample(sample, index, samples.length, results) == :stop - - # Add a small delay between samples - sleep(1) unless index == samples.length - 1 - end - - end_time = Time.now - total_duration = (end_time - start_time).round(2) - - # Print summary - puts "📊 Sample Execution Summary" - puts "=" * 30 - puts "Total time: #{total_duration}s" - puts "Passed: #{results[:passed]}" - puts "Failed: #{results[:failed]}" - puts "Skipped: #{results[:skipped]}" - puts "" - - if results[:failed] == 0 - puts "🎉 All samples completed successfully!" - puts "" - puts "Next steps:" - puts "- Review the sample code to understand SDK usage" - puts "- Modify samples to fit your specific use case" - puts "- Integrate SDK functionality into your application" - puts "- Check out the main SDK documentation for advanced features" - else - puts "⚠️ Some samples failed. Please review the errors above." - puts "" - puts "Troubleshooting tips:" - puts "- Verify your FastPix credentials are correct" - puts "- Check your internet connection" - puts "- Ensure you have proper permissions for the operations" - puts "- Review the error messages for specific guidance" - end - - puts "" - puts "Finished at: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" -end - -# Run the main function -main diff --git a/samples/signing_keys.rb b/samples/signing_keys.rb deleted file mode 100644 index d064770..0000000 --- a/samples/signing_keys.rb +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# FastPix Ruby SDK - Signing Keys Examples -# This example demonstrates signing key management for secure access - -require 'fastpixapi' - -# Configuration -USERNAME = ENV['FASTPIX_USERNAME'] || 'your_username_here' -PASSWORD = ENV['FASTPIX_PASSWORD'] || 'your_password_here' - -puts "🔐 FastPix Ruby SDK - Signing Keys Examples" -puts "=" * 50 - -begin - # Initialize the SDK - sdk = FastpixApiSDK::Fastpix.new( - security: FastpixApiSDK::Models::Components::Security.new( - username: USERNAME, - password: PASSWORD - ) - ) - puts "✅ SDK initialized successfully" - - # Example 1: List existing signing keys - puts "\n1. Listing existing signing keys..." - begin - response = sdk.signing_keys.list_signing_keys(limit: 10) - - if response.status_code == 200 - puts "✅ Signing keys retrieved successfully" - puts " Total keys: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Recent signing keys:" - response.object.data.first(3).each_with_index do |key, index| - puts " #{index + 1}. ID: #{key.id}" - puts " Name: #{key.name}" - puts " Created: #{key.created_at || 'Unknown'}" - end - end - else - puts "⚠️ Signing keys listing failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Signing keys listing failed: #{e.message}" - end - - # Example 2: Create a new signing key - puts "\n2. Creating a new signing key..." - begin - create_request = FastpixApiSDK::Models::Components::CreateSigningKeyRequest.new( - name: "Sample Signing Key #{Time.now.strftime('%Y%m%d_%H%M%S')}", - description: "Sample signing key created via Ruby SDK", - metadata: { - 'source' => 'ruby_sdk_sample', - 'created_at' => Time.now.iso8601 - } - ) - - response = sdk.signing_keys.create_signing_key(request: create_request) - - if response.status_code == 201 - puts "✅ Signing key created successfully" - key = response.object&.data - puts " Key ID: #{key&.id}" - puts " Name: #{key&.name}" - puts " Description: #{key&.description}" - puts " Created: #{key&.created_at}" - - # Store key ID for later operations - @signing_key_id = key&.id - else - puts "⚠️ Signing key creation failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Signing key creation failed: #{e.message}" - end - - # Example 3: Get signing key details (if we created one) - if @signing_key_id - puts "\n3. Getting signing key details..." - begin - response = sdk.signing_keys.get_signing_key_by_id(signing_key_id: @signing_key_id) - - if response.status_code == 200 - puts "✅ Signing key details retrieved successfully" - key = response.object&.data - puts " ID: #{key&.id}" - puts " Name: #{key&.name}" - puts " Description: #{key&.description}" - puts " Created: #{key&.created_at}" - puts " Updated: #{key&.updated_at}" - else - puts "⚠️ Signing key details retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Signing key details retrieval failed: #{e.message}" - end - end - - # Example 4: Get public PEM using signing key ID - if @signing_key_id - puts "\n4. Getting public PEM for signing key..." - begin - response = sdk.signing_keys.get_public_pem_using_signing_key_id( - signing_key_id: @signing_key_id - ) - - if response.status_code == 200 - puts "✅ Public PEM retrieved successfully" - pem_data = response.object&.data - puts " Key ID: #{pem_data&.id}" - puts " Public PEM length: #{pem_data&.public_pem&.length || 0} characters" - puts " Public PEM preview: #{pem_data&.public_pem&.[](0..50)}..." - else - puts "⚠️ Public PEM retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Public PEM retrieval failed: #{e.message}" - end - end - - # Example 5: Update signing key - if @signing_key_id - puts "\n5. Updating signing key..." - begin - update_request = FastpixApiSDK::Models::Components::UpdateSigningKeyRequest.new( - name: "Updated Signing Key #{Time.now.strftime('%Y%m%d_%H%M%S')}", - description: "Updated signing key description", - metadata: { - 'updated_at' => Time.now.iso8601, - 'updated_by' => 'ruby_sdk' - } - ) - - response = sdk.signing_keys.update_signing_key( - signing_key_id: @signing_key_id, - request: update_request - ) - - if response.status_code == 200 - puts "✅ Signing key updated successfully" - key = response.object&.data - puts " Updated Name: #{key&.name}" - puts " Updated Description: #{key&.description}" - else - puts "⚠️ Signing key update failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Signing key update failed: #{e.message}" - end - end - - # Example 6: List signing keys with pagination - puts "\n6. Listing signing keys with pagination..." - begin - response = sdk.signing_keys.list_signing_keys( - limit: 5, - offset: 0 - ) - - if response.status_code == 200 - puts "✅ Paginated signing keys retrieved successfully" - puts " Keys count: #{response.object&.data&.length || 0}" - - if response.object&.data&.any? - puts " Signing keys:" - response.object.data.each_with_index do |key, index| - puts " #{index + 1}. ID: #{key.id}" - puts " Name: #{key.name}" - puts " Created: #{key.created_at || 'Unknown'}" - end - end - else - puts "⚠️ Paginated signing keys retrieval failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Paginated signing keys retrieval failed: #{e.message}" - end - - # Example 7: Delete signing key (if we created one) - if @signing_key_id - puts "\n7. Deleting signing key..." - begin - response = sdk.signing_keys.delete_signing_key(signing_key_id: @signing_key_id) - - if response.status_code == 200 - puts "✅ Signing key deleted successfully" - puts " Deleted Key ID: #{@signing_key_id}" - else - puts "⚠️ Signing key deletion failed with status: #{response.status_code}" - end - rescue => e - puts "❌ Signing key deletion failed: #{e.message}" - end - end - - puts "\n🎉 Signing keys examples completed!" - puts "\nKey concepts:" - puts "- Signing keys are used for secure access and token management" - puts "- Each key has a public/private key pair for cryptographic operations" - puts "- Keys can be used to sign requests and verify authenticity" - puts "- Store private keys securely and never expose them" - puts "- Use public keys for verification and sharing" - -rescue => e - puts "❌ Example failed: #{e.message}" - puts "\nTroubleshooting:" - puts "1. Verify your credentials are correct" - puts "2. Check your internet connection" - puts "3. Ensure you have proper permissions for signing key operations" - puts "4. Some operations may require specific account permissions" -end From 258c3196a90b94dd8db5b4e96ea6e471b9ea92b4 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 26 Aug 2026 17:29:08 +0530 Subject: [PATCH 2/2] Use ruby -S bundle in Rails example commands --- examples/.gitignore | 2 ++ examples/rails-example/README.md | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/examples/.gitignore b/examples/.gitignore index 7b6b886..a2112ef 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -2,3 +2,5 @@ rails-example/tmp/ rails-example/log/ rails-example/.bundle/ +rails-example/vendor/ +rails-example/Gemfile.lock diff --git a/examples/rails-example/README.md b/examples/rails-example/README.md index 782d9be..df991f7 100644 --- a/examples/rails-example/README.md +++ b/examples/rails-example/README.md @@ -12,16 +12,25 @@ It's a single file (`app.rb`) to keep the moving parts visible. ## Run it +Rails 8 needs Ruby 3.2+. The commands use `ruby -S bundle` so bundler always +runs under your current `ruby` — handy on machines (like macOS) where a stray +`bundle` points at an older system Ruby. If your `bundle` already matches your +`ruby`, plain `bundle ...` works just as well. + ```bash cd examples/rails-example -bundle install +ruby -S bundle install cp .env.example .env # fill in your credentials export $(grep -v '^#' .env | xargs) -bundle exec rackup -p 9292 +ruby -S bundle exec rackup -p 9292 ``` While developing against this repo (before the gem is published), point Ruby at -the local SDK instead: `RUBYLIB=../../lib bundle exec rackup -p 9292`. +the local SDK: `RUBYLIB=../../lib ruby -S bundle exec rackup -p 9292`. + +If `bundle install` hits a permission error writing to the gem directory, +install the gems into the project instead: `ruby -S bundle config set --local +path vendor/bundle`, then `ruby -S bundle install`. ## Try it