Shoplive ショートフォームエディター SDK 連携ガイド
ユーザーがアプリ内でショート動画を作成してアップロードできるようにする SDK です。
動画の選択 → 編集(トリム・クロップ・フィルター・速度・音量)→ カバー選択 → アップロードまで、画面全体を SDK が提供します。
コード例は上部の iOS / Android タブで切り替えられます。
概要 & 要件
ショートフォームエディター SDK は UGC(ユーザー制作ショート)のための SDK です。 トリミング・クロップ・フィルター・エンコード・アップロードなど、自前で作ると時間のかかる部分を SDK がまとめて担当し、 アプリはいつ開くかと終わった後に何をするかだけを決めます。
2 つの使い方
ミッションマップ — 何を実装したいですか?
| 実装したいこと | ミッション |
|---|---|
| 「ショートを投稿」ボタンでエディターが開き、アップロードまで完了する | ミッション 1 · エディターを開く |
| 動画の長さ・比率を自社ポリシーに合わせ、使わない編集ツールを隠す | ミッション 2 · 画面のカスタマイズ |
| ギャラリーは自社のものを使い、編集機能だけ借りる | 付録 · 動画エディターだけを使う |
| サムネイル(カバー)だけ選び直させる | 付録 · カバー選択 |
提供形態(プラットフォーム対応)
| 形態 | iOS | Android |
|---|---|---|
| フルフロー | ShopLiveShortformEditor.shared.start(vc) | ShopLiveShortformEditor(activity).start() |
| エディター単体 | ShopliveVideoEditor.shared.build(data:completion:) | ShopLiveVideoEditor(activity).start(data) |
| カバー選択 | ShopLiveCoverPicker.shared.build(data:completion:) | ShopLiveCoverPicker(activity).start(data) |
| 画像 → 動画 | — | ShopLiveImageEditor(activity).start(data) |
| 結果の受信 | ShopLiveShortformEditorDelegate | ShopLiveShortformEditorHandler |
present する形、
Android は SDK が Activity を開き結果をハンドラーで返す形です。
画像編集は Android のみ単体コンポーネントとして公開されています(カバー選択は両プラットフォームにあります)。
両プラットフォームを同時に導入する場合は、この差をアプリ層で吸収してください。要件
| 項目 | iOS | Android |
|---|---|---|
| 最小バージョン | iOS 13.0 以上 | shoplive-media3(既定エンジン)利用時は minSdk 23 以上 · shoplive-exoplayer 利用時は 19 以上 |
| SDK バージョン | 1.8.12(ShopLiveShortformEditorSDK) | 1.8.14(shoplive-video-editor) |
| 必須の同梱モジュール | ShopliveFilterSDK · ShopliveSDKCommon | shoplive-common · shoplive-network |
| 実行環境 | UIViewController | ComponentActivity(AppCompatActivity を含む) |
| アプリサイズ | FFmpeg ベースのエンコードを含むため、アプリ容量が目に見えて増えます | |
armeabi-v7a・arm64-v8a・x86・x86_64
の 4 つの ABI の .so が入るため、容量が重要なら ABI 分割(App Bundle)を有効にしてください。インストール
// 1) Swift Package Manager
dependencies: [
.package(url: "https://github.com/shoplive/shortform-editor-ios", .upToNextMajor(from: "1.8.12")),
.package(url: "https://github.com/shoplive/common-ios", .upToNextMajor(from: "1.8.12")),
]
// ターゲットに ShopLiveShortformEditorSDK と ShopliveFilterSDK を併せて追加します。
// 2) CocoaPods — Podfile
// pod 'ShopLiveShortformEditorSDK', '1.8.12' # ShopliveFilterSDK を含む
// pod 'ShopliveSDKCommon', '1.8.12'
// 3) import
import ShopliveSDKCommon
import ShopLiveShortformEditorSDK
dependencies {
implementation("cloud.shoplive:shoplive-video-editor:1.8.14") // エディター
implementation("cloud.shoplive:shoplive-common:1.8.14") // 共通(accessKey・認証)
implementation("cloud.shoplive:shoplive-network:1.8.14") // ネットワーク
implementation("cloud.shoplive:shoplive-filter:1.8.14") // フィルター(エディターに必須)
// 再生エンジン — 以下の<b>どちらか一方だけ</b>を宣言してください(エンジンのバージョン体系は SDK と別です)
implementation("cloud.shoplive:shoplive-media3:1.10.1.3") // media3(既定)— minSdk 23
// implementation("cloud.shoplive:shoplive-exoplayer:2.19.1.20") // ExoPlayer(レガシー)— minSdk 19
}
android {
defaultConfig { multiDexEnabled = true }
// ネイティブのエンコードライブラリを含む → 容量が重要なら ABI 分割を推奨
bundle { abi { enableSplit = true } }
}
Info.plist と権限
<!-- Info.plist — 未設定だとアプリがクラッシュします -->
<key>NSPhotoLibraryUsageDescription</key>
<string>ショートにする動画を選ぶために写真へのアクセスが必要です。</string>
<key>NSCameraUsageDescription</key>
<string>ショートを撮影するためにカメラへのアクセスが必要です。</string>
<key>NSMicrophoneUsageDescription</key>
<string>動画の音声を録音するためにマイクへのアクセスが必要です。</string>
// ギャラリー選択は PickVisualMedia(フォトピッカー)を使うため、ストレージ権限は不要です。
// 撮影機能を併せて組み込む場合のみ AndroidManifest.xml に追加:
// <uses-permission android:name="android.permission.CAMERA" />
// <uses-permission android:name="android.permission.RECORD_AUDIO" />
setPermissionHandler に ShopLivePermissionHandler を渡すと、
写真・カメラ・マイクの権限結果をアプリが受け取り、設定アプリへ誘導する案内を自前で表示できます(付録)。クイックスタート (Quick Start)
accessKey の設定とユーザー識別の両方が必要です。
アップロードされたショートは作成者に帰属するため、ログインユーザーでないと自分の投稿を再び見つけられません。// 1) アプリ起動時に 1 回
ShopLiveCommon.setAccessKey(accessKey: "取得した_ACCESS_KEY")
ShopLiveCommon.setAuthToken(authToken: myJwtFromServer)
// 2)「ショートを投稿」ボタンから — 選択・編集・カバー・アップロードまで一気に
ShopLiveShortformEditor.shared
.setDelegate(delegate: self)
.start(self)
// 1) アプリ起動時に 1 回(Application.onCreate)
ShopLiveCommon.setAccessKey("取得した_ACCESS_KEY")
ShopLiveCommon.setAuthToken(myJwtFromServer)
// 2)「ショートを投稿」ボタンから — 選択・編集・カバー・アップロードまで一気に
ShopLiveShortformEditor(this) // this = ComponentActivity
.setHandler(myHandler)
.start()
ComponentActivity でないと動作しません
エディターは activityResultRegistry で画面結果をやり取りします。
Application/Service のコンテキストや旧式の Activity を渡すと
9050 REQUIRED_COMPONENT_ACTIVITY が出るか、何も起きません。次のステップ画面用語ガイド · ミッション 1 から始める →
画面用語ガイド
フルフローは4 つの画面が順に続きます。以降のミッション・オプションの説明でも同じ名前を使います。
画面の流れ
| 段階 | 画面 | ユーザーの操作 | アプリが制御できること |
|---|---|---|---|
| ① | メディア選択 | ギャラリーから動画を選ぶ | 許容する長さの範囲・拒否時の案内文(ミッション 2) |
| ② | 動画編集 | トリム・クロップ・フィルター・速度・音量 | 表示する編集ツール・比率・長さ制限(ミッション 2) |
| ③ | カバー選択 | サムネイルにするフレームを選ぶ | 比率・クロップボタンの表示(iOS・Android) |
| ④ | アップロード | タイトル・説明・ハッシュタグ・商品を入力して登録 | 入力項目の表示・初期値(タグ・商品)・公開状態(ミッション 2) |
編集ツール(② の画面)
| ツール | できること | オフにする方法 |
|---|---|---|
| トリム | 開始・終了位置をつまんで長さを調整 | 常に表示(長さの範囲のみ調整可) |
| クロップ | 画面比率に合わせて切り出す | iOS editOptions · Android isUsedCropButton |
| フィルター | コンソールに登録された色補正プリセットを適用 | iOS editOptions · Android isUsedFilterButton |
| 再生速度 | 倍速を調整 | iOS editOptions · Android isUsedPlaybackSpeedButton |
| 音量 | 元音声の大きさを調整 | iOS editOptions · Android isUsedVolumeButton |
アップロード画面(④ の画面)
| 入力項目 | 既定値 | 制御 |
|---|---|---|
| 説明(本文) | 表示 | iOS isDescriptionVisible |
| ハッシュタグ | 表示 | iOS isTagsVisible · Android ui.hashTag |
| 動画の差し替え | 非表示 | Android ui.videoChange |
| 評価(rating) | 非表示 | Android ui.rating |
| 紐づけ商品 | なし | Android products で初期値を指定 |
| 公開状態 | 公開(OPENED) | iOS shortsStatus · Android shortformStatus |
前へ ← クイックスタート次へ → デモアプリで確認する
デモアプリで確認する
デモアプリにはエディターのオプションポップアップがあり、コードを変えずにミッション 2 のオプションが 画面にどう反映されるかをすぐ確認できます。
- フルフロー — 選択からアップロードまで SDK が提供する既定の経路(ミッション 1)。
- ギャラリー + エディター — アプリのギャラリーで選んだ動画をエディターにだけ渡す経路(付録)。
- エディター単体 — すでに持っているファイルパス/URL でエディターだけを開く経路。
- オプションポップアップで比率・最小/最大の長さ・説明とタグの表示・編集ツールを変えながら確認します。
前へ ← 画面用語ガイド次へ → ミッション 1 · エディターを開く
ミッション 1 · エディターを開く
- 結果を受け取るデリゲート/ハンドラーを用意します。
- ボタンのアクションでエディターを開始します。
- アップロード成功・キャンセル・エラーをそれぞれ処理します。
ステップ 1 · 開始する
@IBAction func createShorts() {
ShopLiveShortformEditor.shared
.setPermissionHandler(self) // 任意 — 付録を参照
.setDelegate(delegate: self)
.start(self) // 現在の ViewController の上に表示
}
// アプリ側から強制的に閉じるとき
ShopLiveShortformEditor.shared.close()
fun createShorts() {
ShopLiveShortformEditor(this) // this = ComponentActivity
.setHandler(myHandler)
.start()
}
ステップ 2 · 結果を受け取る
extension MyViewController: ShopLiveShortformEditorDelegate {
func onShopLiveShortformEditorUploadSuccess(result: ShopliveEditorResultData?) {
// アップロード完了 — result.shortsId で作ったばかりのショートを再生できます
guard let shortsId = result?.shortsId else { return }
myFeed.insertNewShorts(shortsId)
}
func onShopLiveShortformEditorVideoConvertSuccess(videoPath: String) {
// エンコード済みのローカルファイルパス(アップロード前の段階)
}
func onShopLiveShortformEditorCoverImageSuccess(image: UIImage?) {
// ユーザーが選んだカバー画像
}
func onShopLiveShortformEditorClosed() {
// 途中で閉じられた — 必要なら「下書き保存」を案内
}
func onShopLiveShortformEditorError(error: ShopLiveCommonError) {
log("editor error \(error.codes) \(error.message ?? "")")
}
func onShopLiveShortformEditorOnEvent(name: String, payload: [String : Any]?) {
analytics.log(name) // ボタン単位の行動ログ
}
}
val myHandler = object : ShopLiveShortformEditorHandler() {
override fun onSuccess(activity: ComponentActivity) {
// アップロード完了 — 一覧を更新するか「自分のショート」タブへ
activity.finish()
myFeed.refresh()
}
override fun onCancel() {
// 途中で閉じられた
}
override fun onError(context: Context, error: ShopLiveCommonError) {
log("editor error ${error.code} ${error.message}")
}
override fun onEvent(context: Context, command: String, payload: Map<String, Any?>) {
analytics.log(command) // ボタン単位の行動ログ
}
}
ShopliveEditorResultData(shortsId を含む)を返しますが、
Android のフルフローの onSuccess はActivity だけを渡します。
Android で shortsId が必要な場合はエディター単体経路の
ShopLiveEditorResultData.shortsId を使うか、アップロード後に一覧を取得し直してください。ステップ 3 · アップロード画面に初期値を入れる(Android)
商品詳細から「この商品でショートを作る」といった導線なら、商品・タグをあらかじめ埋められます。
ShopLiveShortformEditor(this)
.setShortformUploaderData(
ShopLiveShortformUploaderData(
tags = listOf("春の新作"),
products = listOf(
ShopLiveShortformUploaderProductData(
customerProductId = "P-1001",
sku = "SKU-1001"
)
),
ui = ShopLiveShortformUploaderUiData(
hashTag = true,
videoChange = false,
rating = false
)
)
)
.setHandler(myHandler)
.start()
// iOS はアップローダーを単体コンポーネントとして開き、初期値を注入します。
// (フルフローの ShopLiveShortformEditor には注入ポイントがありません)
ShopLiveShortformUploader.shared
.setDelegate(self)
.setUploaderData(ShopLiveShortformUploaderData(
ui: ShopLiveUgcUploaderUiData(hashTag: true, videoChange: false, rating: false),
tags: ["春の新作"],
products: [ShopLiveConversionProductData(sku: "SKU-1001", customProductId: "P-1001")]
))
.build { uploaderVC in
uploaderVC.modalPresentationStyle = .fullScreen
self.present(uploaderVC, animated: true)
}
let visible = ShopLiveShortFormEditorVisibleContent(
isDescriptionVisible: true,
isTagsVisible: true
)
完了確認
- ボタンをタップするとメディア選択画面が開く
- 動画を選ぶと編集画面に進む
- アップロードを終えると成功コールバックが呼ばれる
- 途中で閉じるとキャンセルコールバックが呼ばれ、アプリ画面が正常に戻る
- アップロードしたショートがショートフォーム SDK の一覧で取得できる
関連: 権限の扱い次へ → ミッション 2 · 画面をカスタマイズする
ミッション 2 · 画面をカスタマイズする
方法 ① 動画の長さ・比率
let config = ShopLiveShortformEditorConfiguration(
videoCropOption: ShopLiveShortFormEditorAspectRatio(width: 9, height: 16, isFixed: true),
visibleContents: nil,
videoOutputOption: ShopLiveShortformEditorVideoOuputOption(
videoOutputQuality: .high,
videoOutputResoltuion: ._720
),
mediaPickerVideoDurationOption: ShopLiveMediaPickerVideoDurationOption(
minVideoDuration: 3, // 秒
maxVideoDuration: 60 * 5,
invalidDurationToastMessage: "3 秒〜5 分の動画のみアップロードできます"
),
videoUploadOption: nil,
minVideoDuration: 3, // 編集結果の長さ(秒)
maxVideoDuration: 60
)
ShopLiveShortformEditor.shared
.setConfiguration(config)
.setDelegate(delegate: self)
.start(self)
ShopLiveShortformEditor(this)
.setVideoEditorData(
ShopLiveVideoEditorData(
minVideoDuration = 3_000, // ミリ秒
maxVideoDuration = 60_000,
aspectRatio = ShopLiveShortformEditorAspectRatio(
width = 9, height = 16, isFixed = true
),
outputVideoQuality = ShopLiveVideoEditorVideoQuality.HIGH,
outputResolution = ShopLiveVideoEditorResolution.RESOLUTION_720
)
)
.setHandler(myHandler)
.start()
60、Android は 60_000 と書きます。
共通の定数を置くなら片方の単位で定義して変換してください。1000 倍ずれると
「動画が必ず短すぎる/長すぎる」という症状として現れます。mediaPickerVideoDurationOption は反映されません
ShopLiveShortformEditorConfiguration の init が引数を受け取るだけで内部に代入しないため、
メディア選択の長さ制限は常に既定値(3 秒〜15 分)で動作します。選択段階を制限する必要があれば
ShopLiveMediaPicker の単体経路を使うか、SDK の修正を依頼してください。以下の説明は意図された設計で、
編集結果の長さ(min/maxVideoDuration)は正しく反映されます。mediaPickerVideoDurationOption はギャラリーで選べる元動画の長さ、
min/maxVideoDuration は編集結果の長さです。
5 分の元動画を選ばせて 60 秒に切らせたい場合は、2 つの値を別々に指定してください。isFixed の意味
true ならユーザーは比率を変更できません(9:16 固定)。フィードが縦型専用なら true を推奨します。
既定値は iOS が true、Android が false とプラットフォームで異なるため、両方で明示してください。方法 ② 編集ツールを隠す
// フィルターとクロップだけ残し、速度と音量は隠す
let visible = ShopLiveShortFormEditorVisibleContent(
isDescriptionVisible: true,
isTagsVisible: true,
editOptions: [.filter, .crop] // 既定値: [.filter, .playBackSpeed, .volume, .crop]
)
ShopLiveVideoEditorData(
visibleActionButton = ShopLiveShortformEditorVisibleActionButton(
isUsedFilterButton = true,
isUsedCropButton = true,
isUsedPlaybackSpeedButton = false,
isUsedVolumeButton = false
)
)
方法 ③ アップロード画面の入力項目
// 説明・ハッシュタグの表示可否 + アップロードポリシー
let visible = ShopLiveShortFormEditorVisibleContent(
isDescriptionVisible: true,
isTagsVisible: false // ハッシュタグ入力を隠す
)
let upload = ShopLiveShortFormEditorVideoUploadOption(
shortsStatus: .CLOSED, // アップロード直後は非公開 → 審査後に公開
isCreatedShortform: true
)
ShopLiveShortformEditor(this)
.setShortformUploaderData(
ShopLiveShortformUploaderData(
ui = ShopLiveShortformUploaderUiData(hashTag = false)
)
)
.setVideoEditorData(
ShopLiveVideoEditorData(
shortformStatus = ShopLiveShortformStatus.CLOSED // 非公開でアップロード
)
)
.setHandler(myHandler)
.start()
ShopLiveShortformEditor(フルフロー)は内部で isCreatedShortform = true と
shortformStatus = CLOSED を上書きします。そのため以下の設定はフルフローでは無視され、常に非公開で登録されます。
公開状態を自分で制御するにはエディター単体の経路を使ってください。CLOSED でアップロードしてください
CLOSED でアップロードすると、コンソールで審査してから公開できます。
ユーザー制作コンテンツを即時に表示するのが難しいサービスでは、こちらを既定にすべきです。方法 ④ 色・アイコンを変える(Android)
Android では編集画面のボタン・スライダー・トリムの色まで差し替えられます。
ShopLiveShortformEditor(this)
.setVideoEditorCustomize(
ShopLiveVideoEditorCustomizeData(
mainData = ShopLiveVideoEditorCustomizeMainData(
titleTextSize = 16f,
backButtonBackgroundRes = R.drawable.ic_my_back,
closeButtonBackgroundRes = R.drawable.ic_my_close,
confirmButtonTextColor = Color.WHITE,
confirmButtonBackgroundRes = R.drawable.bg_my_button,
trimColor = Color.parseColor("#245EFF"),
trimHandleColor = Color.WHITE
)
// volumeData · cropData · filterData · playbackSpeedData も同じ方式
)
)
.setHandler(myHandler)
.start()
// iOS は画面ごとのグローバル config シングルトンでアイコン・色を差し替えます。
// エディターを開く前に一度だけ設定すれば十分です。
ShopLiveShortformEditor.MediaPickerConfig.global.cellCornerRadius = 8
ShopLiveShortformEditor.EditorMainConfig.global.confirmButtonBackgroundColor = .systemPink
ShopLiveShortformEditor.EditorMainConfig.global.confirmButtonTextColor = .white
ShopLiveShortformEditor.EditorCropConfig.global.cropColor = .systemPink
// このほか EditorVolumeConfig · EditorSpeedConfig · EditorFilterConfig
// · EditorCoverPickerConfig も同じように .global にアクセスします。
*Config.global シングルトン、
Android は setVideoEditorCustomize(…) です。ピクセル単位で揃えることを要件にせず、
機能の露出範囲を揃える方向で合意するのが現実的です。完了確認
- ポリシーより短い/長い動画を選ぶと案内文が出る
- 編集画面に有効化したツールだけが表示される
- アップロード画面に隠した入力項目が表示されない
- アップロード結果の公開状態が意図した値(公開/非公開)になっている
前へ ← ミッション 1次へ → 付録 · 動画エディターだけを使う
動画エディターだけを使う
アプリにすでにギャラリーや撮影画面があり、編集機能だけが必要な場合です。 アプリが選んだ動画を渡すとエディターだけが開き、結果をアプリが受け取ります。
// iOS — エディターの「画面」を返すので、アプリが自分で present します
ShopliveVideoEditor.shared
.setDelegate(self)
.setConfiguration(ShopliveVideoEditorConfiguration(
videoCropOption: ShopliveVideoEditorAspectRatio(width: 9, height: 16, isFixed: true),
videoOutputOption: nil,
videoTrimOption: ShopliveVideoEditorTrimOption(minVideoDuration: 3, maxVideoDuration: 60),
visibleContents: nil,
videoUploadOption: nil
))
.build(data: ShopLiveVideoEditorData(videoUrl: pickedFileURL, isCreatedShortform: true)) { editorVC in
editorVC.modalPresentationStyle = .fullScreen
self.present(editorVC, animated: true)
}
// メモリ解放 — エディターを閉じた後に
ShopliveVideoEditor.shared.cleanUpMemory()
// Android — SDK が Activity を開き、結果をハンドラーで返します
ShopLiveVideoEditor(this)
.setData(ShopLiveVideoEditorData(
minVideoDuration = 3_000,
maxVideoDuration = 60_000,
isCreatedShortform = true
))
.setHandler(object : ShopLiveVideoEditorHandler() {
override fun onSuccess(
videoEditorActivity: ComponentActivity,
result: ShopLiveEditorResultData
) {
// result.shortsId · localVideoUri · localCoverImageUri · duration …
ShopLiveVideoEditor.close()
uploadToMyServer(result.localVideoUri)
}
override fun onCancel() { /* ユーザーが閉じた */ }
override fun onError(context: Context, error: ShopLiveCommonError) { log(error) }
})
.start(ShopLiveEditorLocalData(pickedUri)) // カメラ結果なら ShopLiveEditorCameraData
入力ソース
| ソース | iOS | Android |
|---|---|---|
| ローカルファイル | ShopLiveVideoEditorData(videoUrl:) | ShopLiveEditorLocalData(uri) |
| カメラ撮影の結果 | ShopLiveVideoEditorData(videoUrl:) | ShopLiveEditorCameraData(uri) |
| リモート URL | ShopLiveVideoEditorData(videoRemoteUrl:) | — |
| 絶対/相対パスの組 | ShopLiveVideoEditorData(videoAbsoluteUrl:videoRelativeUrl:) | — |
isCreatedShortform の意味を確認してください
true なら編集結果が Shoplive のショートとして登録されます(だから shortsId が返ります)。
編集結果のファイルだけをアプリが受け取り登録はしないなら false にしてください。9501 が返ります
エディターは入力動画のコーデックを先に検査します。アプリのギャラリーから任意のファイルを渡せる構成なら、
UNSUPPORTED_MEDIA を受け取ってユーザーに案内する経路を必ず用意してください。カバー選択・画像編集
サムネイルだけを選び直させたり、静止画を短い動画にしたりする単体コンポーネントです。 カバー選択は iOS・Android 両方、画像編集は Android のみです。
カバー選択
ShopLiveCoverPicker(this)
.setData(ShopLiveCoverPickerData(
shortsId = "SHORTS_ID",
aspectRatio = ShopLiveShortformEditorAspectRatio(9, 16, isFixed = true),
visibleActionButton = ShopLiveCoverPickerVisibleActionButton(isUsedCropButton = true)
))
.setHandler(object : ShopLiveCoverPickerHandler() {
override fun onSuccess(
coverPickerActivity: ComponentActivity,
result: ShopLiveEditorResultData
) {
ShopLiveCoverPicker.close()
applyCover(result.localCoverImageUri)
}
override fun onCancel() { /* … */ }
})
.start(ShopLiveCoverPickerLocalData(localVideoUri)) // または ShopLiveCoverPickerUrlData(remoteUrl)
// iOS でもカバー選択を単体で開けます(画面を返すのでアプリが present します)
ShopLiveCoverPicker.shared
.setDelegate(self)
.setConfiguration(ShopLiveCoverPickerConfiguration(
cropOption: ShopLiveShortFormEditorAspectRatio(width: 9, height: 16, isFixed: true),
visibleActionButton: ShopLiveCoverPickerVisibleActionButton(editOptions: [.crop])
))
.build(data: ShopLiveCoverPickerData(videoUrl: localVideoURL, shortsId: shortsId)) { pickerVC in
self.present(pickerVC, animated: true)
}
// 閉じた後にメモリを解放
ShopLiveCoverPicker.shared.cleanUpMemory()
画像 → 短い動画
ShopLiveImageEditor(this)
.setData(ShopLiveImageEditorData(
isCreatedShortform = true,
resolution = ShopLiveVideoEditorResolution.RESOLUTION_720,
videoDuration = 10_000 // ミリ秒 — 画像を 10 秒の動画に
))
.setHandler(object : ShopLiveImageEditorHandler() {
override fun onSuccess(result: ShopLiveEditorResultData) { /* … */ }
override fun onError(error: ShopLiveCommonError) { log(error) }
})
.start(ShopLiveEditorLocalData(pickedImageUri))
// iOS には画像 → 動画の変換 API が公開されていません。
権限の扱い
写真・カメラ・マイクの権限が拒否されたときに、SDK の既定動作ではなくアプリが自分で案内したい場合に使います。
ShopLiveShortformEditor.shared
.setPermissionHandler(self)
.setDelegate(delegate: self)
.start(self)
extension MyViewController: ShopLivePermissionHandler {
func handlePhotoLibraryUsagePermission(status: PermissionStatus) {
// 拒否状態なら「設定から写真へのアクセスを許可してください」という案内をアプリが表示
}
func handleCameraPermission(status: PermissionStatus) { /* … */ }
func handleMicroPhonePermission(status: PermissionStatus) { /* … */ }
func handleMediaLibraryUsagePermission(status: PermissionStatus) { /* … */ }
}
// Android はフォトピッカー(PickVisualMedia)を使うため、ストレージ権限の処理は不要です。
// 撮影を組み込んだ場合のみ、アプリが CAMERA・RECORD_AUDIO 権限を要求し、
// 許可された後に ShopLiveEditorCameraData でエディターを開始してください。
NSPhotoLibraryUsageDescription などの説明文が実際の用途と異なると、アプリ審査でリジェクトされることがあります。
「ショート制作のために」のように具体的に書いてください(インストール)。データフィールド辞典
Configuration — 何をどこに入れるか
| 決めたいこと | iOS | Android |
|---|---|---|
| 画面比率 | videoCropOption | ShopLiveVideoEditorData.aspectRatio |
| 結果の長さ | minVideoDuration · maxVideoDuration(秒) | minVideoDuration · maxVideoDuration(ms) |
| 選べる元動画の長さ | mediaPickerVideoDurationOption | — |
| 編集ツールの表示 | visibleContents.editOptions | visibleActionButton |
| 説明・タグ入力の表示 | visibleContents.isDescriptionVisible/isTagsVisible | ShopLiveShortformUploaderData.ui |
| 出力品質・解像度 | videoOutputOption | outputVideoQuality · outputResolution |
| アップロード時の公開状態 | videoUploadOption.shortsStatus | shortformStatus |
| ショートとして登録するか | videoUploadOption.isCreatedShortform | isCreatedShortform |
| 色・アイコン | ShopLiveShortformEditor.*Config.global | setVideoEditorCustomize(…) |
| アップロード初期値(商品・タグ) | ShopLiveShortformUploader.setUploaderData(…) | setShortformUploaderData(…) |
比率と長さ
| フィールド | 既定値(iOS) | 既定値(Android) | 説明 |
|---|---|---|---|
width : height | 9 : 16 | 9 : 16 | クロップ比率 |
isFixed | true | false | ユーザーが比率を変更できるか — 既定値がプラットフォームで異なる |
minVideoDuration | 1(秒) | 1000(ms) | 結果の最小の長さ |
maxVideoDuration | 60(秒) | 60000(ms) | 結果の最大の長さ |
| メディア選択の最小 | 3(秒) | — | ギャラリーで選べる元動画の最小の長さ |
| メディア選択の最大 | 900(秒 = 15 分) | — | ギャラリーで選べる元動画の最大の長さ |
maxVideoDuration ≤ minVideoDuration のとき黙って既定値(1〜60 秒)に戻します。
Android は 1 未満の値だけを既定値(1000 ms / 60000 ms)に置き換え、max ≤ min の検証は行いません。
どちらもエラーにならないため設定が無視されたことに気づきにくい点に注意してください。値を計算して入れる場合はアプリ側で順序を検証してください。結果データ
| フィールド | iOS ShopliveEditorResultData | Android ShopLiveEditorResultData |
|---|---|---|
| ショート ID | shortsId | shortsId |
| ローカル動画 | localVideoUrl(String) | localVideoUri(Uri) |
| ローカルカバー | localCoverImage(UIImage) | localCoverImageUri(Uri) |
| アップロード済み動画 | remoteOriginVideoUrl | remoteOriginVideoUrl |
| アップロード済みカバー | remoteCoverImageUrl | remoteCoverImageUrl |
| 解像度 | width · height | width · height |
| 長さ | duration(Double) | duration(Long) |
| 撮影日時 | videoCreatedAt | — |
デリゲート・ハンドラー
| タイミング | iOS(フルフロー) | Android(フルフロー) |
|---|---|---|
| アップロード成功 | onShopLiveShortformEditorUploadSuccess(result:) | onSuccess(activity) |
| エンコード完了 | onShopLiveShortformEditorVideoConvertSuccess(videoPath:) | — |
| カバー確定 | onShopLiveShortformEditorCoverImageSuccess(image:) | — |
| クローズ・キャンセル | onShopLiveShortformEditorClosed() | onCancel() |
| エラー | onShopLiveShortformEditorError(error:) | onError(context, error) |
| 行動イベント | onShopLiveShortformEditorOnEvent(name:payload:) | onEvent(context, command, payload) |
| タイミング | iOS(エディター単体) | Android(エディター単体) |
|---|---|---|
| 成功 | onShopLiveVideoEditorUploadSuccess(editor:result:) | onSuccess(activity, result) |
| エンコード完了 | onShopLiveVideoEditorVideoConvertSuccess(editor:videoPath:) | — |
| キャンセル | onShopLiveVideoEditorCancelled(editor:) | onCancel() |
| エラー | onShopLiveVideoEditorError(editor:error:) | onError(context, error) |
editor(ViewController)が併せて渡されます
エディター単体のデリゲートはどのエディターから来たイベントかを一緒に渡します。
エディターを閉じたり、その上にアラートを出したりするときにこの参照を使ってください。型 & ステータス
編集ツール(iOS SLEditOptions)
| 値 | ツール | Android の対応 |
|---|---|---|
.filter | フィルター | isUsedFilterButton |
.playBackSpeed | 再生速度 | isUsedPlaybackSpeedButton |
.volume | 音量 | isUsedVolumeButton |
.crop | クロップ | isUsedCropButton |
[.filter, .playBackSpeed, .volume, .crop])です。出力品質・解像度
| 区分 | iOS | Android |
|---|---|---|
| 品質 | .normal · .high · .max(既定 .high) | NORMAL · HIGH · MAX(既定 NORMAL) |
| 解像度 | ._360 · ._480 · ._720 · ._1080(既定 ._720) | RESOLUTION_360/480/720/960/1080(既定 720) |
high vs Android NORMAL)。
アップロード容量・時間を揃えるなら両方で明示してください。
RESOLUTION_960 は Android にのみあります。公開状態
| 値 | 意味 |
|---|---|
OPENED | アップロード直後に公開(既定値) |
CLOSED | 非公開でアップロード — コンソールで審査後に公開 |
行動イベント
onEvent に渡される値です。分析ログ用で、一覧は今後増える可能性があるため、知らない値は無視してください。
| コマンド | タイミング |
|---|---|
VIDEO_EDITOR_CLICK_CLOSE / …CONFIRM | エディターを閉じる/確定 |
VIDEO_EDITOR_CLICK_CROP / …CROP_CONFIRM / …CROP_CANCEL | クロップ開始/確定/キャンセル |
VIDEO_EDITOR_CLICK_FILTER / …FILTER_CONFIRM / …FILTER_CANCEL | フィルター開始/確定/キャンセル |
VIDEO_EDITOR_CLICK_VOLUME / …VOLUME_CONFIRM / …VOLUME_CANCEL | 音量開始/確定/キャンセル |
VIDEO_EDITOR_CLICK_PLAYBACK_SPEED / …CONFIRM / …CANCEL | 速度開始/確定/キャンセル |
COVER_PICKER_CLICK_CAMERA_ROLL / …CLOSE / …CONFIRM | カバー選択(Android) |
エラー処理
エラーはデリゲート/ハンドラーの onError で渡されます。ショートフォーム SDK と同じコード体系で、
エディターでは主に 9300 番台(エンコード)と 9500 番台(未対応メディア)が発生します。
| コード | 名前 | 原因・対処 |
|---|---|---|
9000 | NOT_INITIALIZED_ACCESS_KEY | setAccessKey の前にエディターを開始した |
9002 | NOT_INITIALIZED_CONFIG | エディター設定をサーバーから受け取れていない → ネットワークを確認して再試行 |
9050 | REQUIRED_COMPONENT_ACTIVITY | (Android)ComponentActivity 以外のコンテキストを渡した |
9300 | FAILED_ENCODING | エンコード失敗 → 空き容量と元ファイルの状態を確認 |
9301 | FAILED_VIDEO_ENCODING | 動画のエンコード失敗 |
9302 | FAILED_COVER_ENCODING | カバー画像の生成失敗 |
9320 | WITHOUT_THUMBNAIL | サムネイルを生成できなかった → カバーを選び直すよう案内 |
9501 | UNSUPPORTED_MEDIA | 未対応のコーデック → 「この動画は使用できません」と案内 |
9900 | FAILED_NETWORK | アップロード中のネットワーク失敗 → 再試行を提供 |
10000 | UNEXPECTED_ERROR | 分類されていない例外 → ログを添えてお問い合わせください |
onError に必ず実装してください。導入企業が直接設定する項目
トラブルシューティング & FAQ
ボタンを押しても何も起きません(Android)
ComponentActivity 以外のコンテキストを渡した可能性が高いです。
エディターは activityResultRegistry で画面結果をやり取りするため、
applicationContext や旧式の Activity では動作しません(クイックスタート (Quick Start))。
長さ制限を設定したのに反映されません
単位を確認してください — iOS は秒、Android はミリ秒です。
また maxVideoDuration が minVideoDuration 以下だと、SDK は
黙って既定値に戻します(比率と長さ)。
比率の固定が iOS でしか効かないように見えます
isFixed の既定値が iOS は true、Android は false と異なります。
両方で明示的に指定してください(ミッション 2)。
フィルターボタンを有効にしたのに一覧が空です
コンソールにフィルタープリセットが登録されていません(導入企業側の設定)。
アップロードできたのに一覧に出ません
① CLOSED(非公開)でアップロードされた場合 — コンソールで公開処理が必要です。
② isCreatedShortform = false だとショートとして登録されません。
③ 検索条件(タグ・コレクション)がアップロード時の入力値と合っているか確認してください。
アプリの容量が大きく増えました
ネイティブのエンコードライブラリが 4 つの ABI で入ります。Android App Bundle の ABI 分割を有効にすると、端末ごとのダウンロード容量が減ります(インストール)。
両プラットフォームの画面を同じにできますか
色・アイコンのカスタマイズはAndroid 専用のため、完全に同じにはなりません。 機能の露出範囲(編集ツール・入力項目)を揃える方向で合意するのが現実的です(ミッション 2)。
アップロードしたショートをアプリで再生するには
ショートフォーム SDK ガイドを参照してください。結果の shortsId を
そのまま渡せばすぐ再生できます。