Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/java rate limit #92

Merged
merged 1 commit into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading