SDK Frame.io для Python — руководство по добавлению файлов

В этом руководстве объясняется, как добавлять файлы в Frame.io с помощью SDK Frame.io для Python (frameio). SDK разбивает файлы на фрагменты и добавляет их частями в хранилище S3 через предварительно подписанные URL-адреса. При этом выполняются параллельные рабочие процессы, поддерживаются автоматические повторные попытки и предоставляется возможность отслеживать прогресс. Общие компоненты процесса добавлении файлов через API-интерфейс (URL-адреса для добавления, заголовки, разбиение на фрагменты) — в разделе Как работает локальное и удаленное добавление файлов.


Требования

1

Аутентификация

У вас есть рабочий клиент Frameio. Подробнее о настройке — в руководстве по аутентификации.

2

Установка SDK

$ pip install frameio
3

Целевая папка

Вам требуются идентификаторы account_id и folder_id папки, куда нужно добавить файл. Используйте SDK для их поиска.

1 # List your accounts
2 accounts = client.accounts.index()
3 account_id = accounts.data[0].id
4
5 # List workspaces in the account
6 workspaces = client.workspaces.index(account_id=account_id)
7 workspace_id = workspaces.data[0].id
8
9 # List projects in the workspace
10 projects = client.projects.index(account_id=account_id, workspace_id=workspace_id)
11 project = projects.data[0]
12
13 # The project's root folder is the top-level upload target
14 folder_id = project.root_folder_id
15
16 # Or list subfolders to upload into a specific one
17 folders = client.folders.list(account_id=account_id, folder_id=folder_id)

Быстрый старт

1import os
2from frameio import Frameio
3from frameio.files import FileCreateLocalUploadParamsData
4from frameio.upload import FrameioUploader
5
6client = Frameio(token="YOUR_TOKEN")
7
8file_path = "/path/to/video.mp4"
9file_size = os.path.getsize(file_path)
10
11# 1. Create the file resource and get pre-signed upload URLs
12response = client.files.create_local_upload(
13 account_id="YOUR_ACCOUNT_ID",
14 folder_id="YOUR_FOLDER_ID",
15 data=FileCreateLocalUploadParamsData(
16 name="video.mp4",
17 file_size=file_size,
18 ),
19)
20
21# 2. Upload the file to S3
22with open(file_path, "rb") as f:
23 FrameioUploader(response.data, f).upload()

Готово. SDK разбивает файл на фрагменты на основе URL-адресов, полученных от API-интерфейса, добавляет их в хранилище в параллельном режиме и автоматически выполняет повторные попытки при сбоях.


Как это работает

Локальное добавление — это двухэтапный процесс.

1

Создание ресурса для файла

Вызовите метод client.files.create_local_upload() с указанием имени и размера файла. API-интерфейс создает файл-заполнитель и возвращает предварительно подписанные URL-адреса S3 PUT — по одному на фрагмент. Количество URL-адресов (и, следовательно, фрагментов) зависит от размера файла.

2

Добавление в S3

Компонент FrameioUploader считывает полученные в ответе URL-адреса для добавления, разбивает ваш файл на соответствующие фрагменты и в параллельном режиме отправляет каждый фрагмент по его URL-адресу, используя пул потоков. Каждый такой запрос автоматически включает обязательные заголовки x-amz-acl: private и Content-Type.

Добавление выполняется напрямую из вашего приложения в хранилище S3, минуя серверы API-интерфейса Frame.io. Аналогичную схему используют такие сервисы, как YouTube, Vimeo и Dropbox, для передачи больших файлов.


Использование компонента FrameioUploader

FrameioUploader — это рекомендуемый инструмент для добавления файлов. Он служит оберткой для низкоуровневого механизма добавления файлов и управляет всеми процессами: извлекает URL-адреса из ответа API-интерфейса, добавляет нужные заголовки, разбивает файлы на фрагменты и выполняет их параллельную отправку.

Отслеживание прогресса

Используйте функцию обратного вызова on_progress для отслеживания хода добавления.

1def on_progress(bytes_uploaded: int, total_bytes: int) -> None:
2 pct = bytes_uploaded / total_bytes * 100
3 print(f"\r{pct:.1f}% ({bytes_uploaded:,} / {total_bytes:,} bytes)", end="", flush=True)
4
5with open(file_path, "rb") as f:
6 FrameioUploader(response.data, f, on_progress=on_progress).upload()
7
8print("\nUpload complete!")

Функция обратного вызова срабатывает после завершения отправки каждого фрагмента, отображая общее количество отправленных байтов и общий размер файла.

Индикатор выполнения на базе библиотеки Rich

Для создания эстетичного и информативного интерфейса в терминале используйте библиотеку Rich.

1from rich.progress import Progress, BarColumn, DownloadColumn, TransferSpeedColumn, TimeRemainingColumn
2
3with Progress(
4 "[progress.description]{task.description}",
5 BarColumn(),
6 DownloadColumn(),
7 TransferSpeedColumn(),
8 TimeRemainingColumn(),
9) as progress:
10 task = progress.add_task("Uploading...", total=file_size)
11
12 with open(file_path, "rb") as f:
13 FrameioUploader(
14 response.data, f,
15 on_progress=lambda done, total: progress.update(task, completed=done),
16 ).upload()

Конфигурация

FrameioUploader принимает несколько дополнительных параметров.

ПараметрПо умолчаниюОписание
max_workers5Количество одновременных потоков добавления.
headers{"x-amz-acl": "private"}Заголовки, отправляемые с каждым запросом S3 PUT. Пользовательские заголовки объединяются со значениями по умолчанию.
max_retries3Количество повторных попыток для каждого фрагмента (экспоненциальная задержка: 1 с, 2 с, 4 с, …).
on_progressНетФункция обратного вызова вида (bytes_uploaded, total_bytes), срабатывающая после добавления каждого фрагмента.
1with open(file_path, "rb") as f:
2 FrameioUploader(
3 response.data,
4 f,
5 max_workers=10, # more parallelism for high-bandwidth connections
6 max_retries=5, # more resilient on flaky networks
7 on_progress=on_progress,
8 ).upload()

Полный пример

Полный пример кода с аутентификацией, добавлением файлов и отслеживанием прогресса.

1import os
2from frameio import Frameio
3from frameio.auth import ServerToServerAuth
4from frameio.files import FileCreateLocalUploadParamsData
5from frameio.upload import FrameioUploader
6
7# Authenticate
8auth = ServerToServerAuth(
9 client_id="YOUR_CLIENT_ID",
10 client_secret="YOUR_CLIENT_SECRET",
11)
12client = Frameio(token=auth.get_token)
13
14# Prepare the file
15file_path = "/path/to/video.mp4"
16file_name = os.path.basename(file_path)
17file_size = os.path.getsize(file_path)
18
19# Create the file resource
20response = client.files.create_local_upload(
21 account_id="YOUR_ACCOUNT_ID",
22 folder_id="YOUR_FOLDER_ID",
23 data=FileCreateLocalUploadParamsData(
24 name=file_name,
25 file_size=file_size,
26 ),
27)
28
29print(f"Uploading {file_name} ({file_size:,} bytes) in {len(response.data.upload_urls)} chunks...")
30
31# Upload with progress
32def on_progress(uploaded: int, total: int) -> None:
33 print(f"\r{uploaded / total:.0%}", end="", flush=True)
34
35with open(file_path, "rb") as f:
36 FrameioUploader(response.data, f, on_progress=on_progress).upload()
37
38print(f"\nDone! View at: {response.data.view_url}")

Если вам нужен полный контроль над процессом добавления, например, чтобы вручную разбивать файлы на фрагменты, выполнять интеграцию с асинхронными конвейерами или настраивать логику повторных попыток, обратитесь к разделу Как работает локальное и удаленное добавление файлов, где описан прямой процесс работы с API-интерфейсом и приведен пример автономного сценария на Python.


Удаленное добавление

Если файл доступен по публичному URL-адресу, используйте удаленное добавление. В этом случае разбиение на фрагменты не требуется — Frame.io получает файл напрямую.

1from frameio.files import FileCreateRemoteUploadParamsData
2
3response = client.files.create_remote_upload(
4 account_id="YOUR_ACCOUNT_ID",
5 folder_id="YOUR_FOLDER_ID",
6 data=FileCreateRemoteUploadParamsData(
7 name="video.mp4",
8 source_url="https://example.com/video.mp4",
9 ),
10)
11print(f"File created: {response.data.id}")

В настоящее время для удаленного добавления установлено ограничение на размер файла 50 ГБ. Для файлов размером более 50 ГБ используйте локальное добавление.


Проверка статуса отправки

После завершения отправки можно убедиться, что файл был получен.

1status = client.files.show_file_upload_status(
2 account_id="YOUR_ACCOUNT_ID",
3 file_id=response.data.id,
4)
5print(f"Upload complete: {status.data.upload_complete}")