Skip to content

Commit

Permalink
Merge pull request #92 from apivideo/feature/java_rate_limit
Browse files Browse the repository at this point in the history
Feature/java rate limit
  • Loading branch information
bot-api-video authored Apr 24, 2024
2 parents d850558 + 331c1de commit 99e04dd
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 34 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,24 @@ Method | HTTP request | Description
- [VideoSourceLiveStreamLink](https://github.com/apivideo/api.video-android-uploader/blob/main/docs/VideoSourceLiveStreamLink.md)


### Documentation for Authorization
### Rate Limiting

api.video implements rate limiting to ensure fair usage and stability of the service. The API provides the rate limit values in the response headers for any API requests you make. The /auth endpoint is the only route without rate limitation.

In this client, you can access these headers by using the `*WithHttpInfo()` or `*Async` versions of the methods. These methods return the `ApiResponse` that contains the response body and the headers, allowing you to check the `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Retry-After` headers to understand your current rate limit status.
Read more about these response headers in the [API reference](https://docs.api.video/reference#limitation).

Here is an example of how to use these methods:

When listening to the `WorkInfo` with the `WorkManager`, you can access the headers in the `OutputData` of the `WorkInfo`:
```kotlin
val headers = workInfo.outputData.toHeaders()
Log.i(TAG, "X-RateLimit-Limit: ${headers["x-ratelimit-limit"]!![0]}")
Log.i(TAG, "X-RateLimit-Remaining: ${headers["x-ratelimit-remaining"]!![0]}")
Log.i(TAG, "X-RateLimit-Retry-After: ${headers["x-ratelimit-retry-after"]!![0]}")
```

### Authorization

#### API key

Expand Down
4 changes: 4 additions & 0 deletions docs/AdvancedAuthenticationApi.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Method | HTTP request | Description
<a name="authenticate"></a>
# **authenticate**
> AccessToken authenticate(authenticatePayload)
> okhttp3.Call authenticateAsync(authenticatePayload, callback)
> ApiResponse<AccessToken> authenticateWithHttpInfo(authenticatePayload)
Get Bearer Token

Expand Down Expand Up @@ -77,6 +79,8 @@ No authorization required
<a name="refresh"></a>
# **refresh**
> AccessToken refresh(refreshTokenPayload)
> okhttp3.Call refreshAsync(refreshTokenPayload, callback)
> ApiResponse<AccessToken> refreshWithHttpInfo(refreshTokenPayload)
Refresh Bearer Token

Expand Down
2 changes: 2 additions & 0 deletions docs/VideosApi.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Method | HTTP request | Description
<a name="upload"></a>
# **upload**
> Video upload(videoId, file)
> ApiResponse<Video> uploadWithHttpInfo(videoId, file)
Upload a video

Expand Down Expand Up @@ -124,6 +125,7 @@ Video result = session.uploadLastPart(new File("sample.mp4.partn"));
<a name="uploadWithUploadToken"></a>
# **uploadWithUploadToken**
> Video uploadWithUploadToken(token, file)
> ApiResponse<Video> uploadWithUploadTokenWithHttpInfo(token, file)
Upload with an delegated upload token

Expand Down
40 changes: 33 additions & 7 deletions src/main/java/video/api/uploader/VideosApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ public ApiResponse<Video> uploadWithHttpInfo(String videoId, File file) throws A
return uploadWithHttpInfo(videoId, file, null);
}

public ApiResponse<Video> uploadPartWithHttpInfo(String videoId, File file, Integer part, boolean isLast,
private ApiResponse<Video> uploadPartWithHttpInfoInternal(String videoId, File file, Integer part, boolean isLast,
UploadPartProgressListener uploadProgressListener) throws ApiException {
long fileSize = file.length();
okhttp3.Call localVarCall = uploadChunkValidateBeforeCall(videoId, file, 0, fileSize, fileSize,
Expand Down Expand Up @@ -619,10 +619,23 @@ public Video uploadLastPart(File part, Integer partId, UploadPartProgressListene
@Override
public Video uploadPart(File part, Integer partId, boolean isLastPart,
UploadPartProgressListener uploadProgressListener) throws ApiException {
ApiResponse<Video> localVarResp = uploadPartWithHttpInfo(this.videoId, part, partId, isLastPart,
ApiResponse<Video> localVarResp = uploadPartWithHttpInfo(part, partId, isLastPart, uploadProgressListener);
return localVarResp.getData();
}

@Override
public ApiResponse<Video> uploadPartWithHttpInfo(File part, boolean isLastPart,
UploadPartProgressListener uploadProgressListener) throws ApiException {
return uploadPartWithHttpInfo(part, this.partId++, isLastPart, uploadProgressListener);
}

@Override
public ApiResponse<Video> uploadPartWithHttpInfo(File part, Integer partId, boolean isLastPart,
UploadPartProgressListener uploadProgressListener) throws ApiException {
ApiResponse<Video> localVarResp = uploadPartWithHttpInfoInternal(this.videoId, part, partId, isLastPart,
uploadProgressListener);

return localVarResp.getData();
return localVarResp;
}
}

Expand Down Expand Up @@ -1202,7 +1215,7 @@ public ApiResponse<Video> uploadWithUploadTokenWithHttpInfo(String token, File f
return uploadWithUploadTokenWithHttpInfo(token, file, null, null);
}

public ApiResponse<Video> uploadWithUploadTokenPartWithHttpInfo(String token, File file, String videoId,
private ApiResponse<Video> uploadWithUploadTokenPartWithHttpInfoInternal(String token, File file, String videoId,
Integer part, boolean isLast, UploadPartProgressListener uploadProgressListener) throws ApiException {
long fileSize = file.length();
okhttp3.Call localVarCall = uploadWithUploadTokenChunkValidateBeforeCall(token, file, videoId, 0, fileSize,
Expand Down Expand Up @@ -1278,12 +1291,25 @@ public Video uploadLastPart(File part, Integer partId, UploadPartProgressListene
@Override
public Video uploadPart(File part, Integer partId, boolean isLastPart,
UploadPartProgressListener uploadProgressListener) throws ApiException {
ApiResponse<Video> localVarResp = uploadWithUploadTokenPartWithHttpInfo(this.token, part, this.videoId,
partId, isLastPart, uploadProgressListener);
ApiResponse<Video> localVarResp = uploadPartWithHttpInfo(part, partId, isLastPart, uploadProgressListener);
return localVarResp.getData();
}

@Override
public ApiResponse<Video> uploadPartWithHttpInfo(File part, boolean isLastPart,
UploadPartProgressListener uploadProgressListener) throws ApiException {
return uploadPartWithHttpInfo(part, this.partId++, isLastPart, uploadProgressListener);
}

@Override
public ApiResponse<Video> uploadPartWithHttpInfo(File part, Integer partId, boolean isLastPart,
UploadPartProgressListener uploadProgressListener) throws ApiException {
ApiResponse<Video> localVarResp = uploadWithUploadTokenPartWithHttpInfoInternal(this.token, part,
this.videoId, partId, isLastPart, uploadProgressListener);
if (this.videoId == null) {
this.videoId = localVarResp.getData().getVideoId();
}
return localVarResp.getData();
return localVarResp;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
package video.api.uploader.api.upload;

import video.api.uploader.api.ApiException;
import video.api.uploader.api.ApiResponse;
import video.api.uploader.api.models.Video;

import java.io.File;

public interface IProgressiveUploadSession {
public String getVideoId();
String getVideoId();

public String getToken();
String getToken();

public Video uploadPart(File part) throws ApiException;
Video uploadPart(File part) throws ApiException;

public Video uploadLastPart(File part) throws ApiException;
Video uploadLastPart(File part) throws ApiException;

public Video uploadPart(File part, UploadPartProgressListener uploadProgressListener) throws ApiException;
Video uploadPart(File part, UploadPartProgressListener uploadProgressListener) throws ApiException;

public Video uploadLastPart(File part, UploadPartProgressListener uploadProgressListener) throws ApiException;
Video uploadLastPart(File part, UploadPartProgressListener uploadProgressListener) throws ApiException;

public Video uploadPart(File part, boolean isLastPart, UploadPartProgressListener uploadProgressListener)
Video uploadPart(File part, boolean isLastPart, UploadPartProgressListener uploadProgressListener)
throws ApiException;

public Video uploadPart(File part, Integer partId, UploadPartProgressListener uploadProgressListener)
Video uploadPart(File part, Integer partId, UploadPartProgressListener uploadProgressListener) throws ApiException;

Video uploadLastPart(File part, Integer partId, UploadPartProgressListener uploadProgressListener)
throws ApiException;

public Video uploadLastPart(File part, Integer partId, UploadPartProgressListener uploadProgressListener)
Video uploadPart(File part, Integer partId, boolean isLastPart, UploadPartProgressListener uploadProgressListener)
throws ApiException;

public Video uploadPart(File part, Integer partId, boolean isLastPart,
ApiResponse<Video> uploadPartWithHttpInfo(File part, boolean isLastPart,
UploadPartProgressListener uploadProgressListener) throws ApiException;

ApiResponse<Video> uploadPartWithHttpInfo(File part, Integer partId, boolean isLastPart,
UploadPartProgressListener uploadProgressListener) throws ApiException;
}
13 changes: 12 additions & 1 deletion src/main/java/video/api/uploader/api/work/DataExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,15 @@ fun Data.toFile(): File {
*
* @return The progress
*/
fun Data.toProgress(): Int = this.getInt(AbstractUploadWorker.PROGRESS_KEY, 0)
fun Data.toProgress(): Int = this.getInt(AbstractUploadWorker.PROGRESS_KEY, 0)

/**
* Extension functions for [Data] to deserialize the response headers.
*
* @return A map of headers
*/
fun Data.toHeaders() = JSON().deserialize(
this.getString(
AbstractUploadWorker.HEADERS_KEY
), Map::class.java
) as Map<String, List<String>>
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package video.api.uploader.api.work

import androidx.work.Data
import androidx.work.WorkManager
import video.api.uploader.api.upload.IProgressiveUploadSession
import video.api.uploader.api.work.stores.ProgressiveUploadSessionStore
import video.api.uploader.api.work.workers.AbstractUploadWorker
import video.api.uploader.api.work.workers.ProgressiveUploadWorker
import video.api.uploader.api.work.workers.UploadWorker
import java.io.File
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ abstract class AbstractUploadWorker(
companion object {
const val PROGRESS_KEY = "progress"
const val VIDEO_KEY = "video"
const val HEADERS_KEY = "headers"
const val ERROR_KEY = "error"
const val FILE_PATH_KEY = "filePath"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,17 @@ class ProgressiveUploadWorker(
* Use an executor to make the coroutine cancellable.
*/
return try {
val video = suspendCancellableCoroutine { continuation ->
val response = suspendCancellableCoroutine { continuation ->
val future = uploaderExecutor.submit {
try {
val video = if (partId != DEFAULT_PART_ID) {
val response = if (partId != DEFAULT_PART_ID) {
ProgressiveUploadSessionStore.get(sessionIndex)!!
.uploadPart(file, partId, isLastPart, this@ProgressiveUploadWorker)
.uploadPartWithHttpInfo(file, partId, isLastPart, this@ProgressiveUploadWorker)
} else {
ProgressiveUploadSessionStore.get(sessionIndex)!!
.uploadPart(file, isLastPart, this@ProgressiveUploadWorker)
.uploadPartWithHttpInfo(file, isLastPart, this@ProgressiveUploadWorker)
}
continuation.resume(video, null)
continuation.resume(response, null)
} catch (e: Exception) {
continuation.resumeWithException(e)
}
Expand All @@ -90,7 +90,8 @@ class ProgressiveUploadWorker(
Result.success(
workDataOf(
FILE_PATH_KEY to filePath,
VIDEO_KEY to JSON().serialize(video)
VIDEO_KEY to JSON().serialize(response.data),
HEADERS_KEY to JSON().serialize(response.headers)
)
)
} catch (e: CancellationException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,22 @@ class UploadWorker(
* Use an executor to make the coroutine cancellable.
*/
return try {
val video = suspendCancellableCoroutine { continuation ->
val response = suspendCancellableCoroutine { continuation ->
val future = uploaderExecutor.submit {
try {
val video = if (token == null) {
videosApi.upload(
val response = if (token == null) {
videosApi.uploadWithHttpInfo(
videoId, file, this@UploadWorker
)
} else {
videosApi.uploadWithUploadToken(
videosApi.uploadWithUploadTokenWithHttpInfo(
token,
file,
videoId,
this@UploadWorker
)
}
continuation.resume(video, null)
continuation.resume(response, null)
} catch (e: Exception) {
continuation.resumeWithException(e)
}
Expand All @@ -97,7 +97,8 @@ class UploadWorker(
Result.success(
workDataOf(
FILE_PATH_KEY to filePath,
VIDEO_KEY to JSON().serialize(video)
VIDEO_KEY to JSON().serialize(response.data),
HEADERS_KEY to JSON().serialize(response.headers)
)
)
} catch (e: CancellationException) {
Expand Down

0 comments on commit 99e04dd

Please sign in to comment.