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

Video uploading endpoint #9

Closed
jonkri opened this issue Jun 29, 2015 · 18 comments
Closed

Video uploading endpoint #9

jonkri opened this issue Jun 29, 2015 · 18 comments
Labels
Milestone

Comments

@jonkri
Copy link
Collaborator

jonkri commented Jun 29, 2015

Uploading a video file would work something like this:

  1. A file would be uploaded in a standard HTTP way (using POST and the multipart/form-data encoding to something like /upload-video)
  2. The server, upon receiving the file, would return 202 Accepted, and in the body include the identifier of the asynchronous video conversion job that has now started
  3. The client polls the server using GET at something like /query-video/<ID>; while the server is processing the video, it will respond with 200 OK and something like Processing in the body; when the server is done processing the video, it will respond with either 303 See Other with the identifier of the new processed video in the body, or 200 OK and something like Failure in the body
  4. Upon receiving the 303 See Other code, the client can now fetch the video at an endpoint such as /video/<id> using GET

I will create a mock endpoint for this soon providing the same video back as uploaded after a certain amount of time, after which I'm planning to taking a look at converting the video to a proper/compressed format using something like ffmpeg.

@jonkri jonkri self-assigned this Jun 29, 2015
jonkri pushed a commit that referenced this issue Jul 5, 2015
Includes an endpoint for uploading a video, and one for checking the
status of the upload.

Includes a background thread that simulates the processing of videos
(copies files from one directory to another and updates the database
every 10 seconds).

Please refer to #9 for more information.
@jonkri
Copy link
Collaborator Author

jonkri commented Jul 5, 2015

Here's an update for this issue.

The commit above includes an endpoint for uploading a video, and one for checking the status of the upload. It also includes a background thread that simulates the processing of videos (copies files from one directory to another and updates the database every 10 seconds). I'm showing what it looks like in action below.

Request:

$ curl --header "Content-Type: application/octet-stream" --form upload=@big-buck-bunny_trailer.webm --request POST http://localhost:3000/v0.0.0/clubs/ccd11959-4497-4d27-99b6-7467b2445809/upload

Output:

87cf9e45-3c8f-4a1f-a372-fb44c523eab5

Request:

$ curl --data '{}' --request GET localhost:3000/0.0.0/clubs/ccd11959-4497-4d27-99b6-7467b2445809/upload/87cf9e45-3c8f-4a1f-a372-fb44c523eab5

Output:

Processing

Request:

$ curl --data '{}' --header "Content-Type: applicatiorequest GET localhost:3000/0.0.0/clubs/ccd11959-4497-4d27-99b6-7467b2445809/upload/87cf9e45-3c8f-4a1f-a372-fb44c523eab5

Output:

Success

Once the status is "Success", a (defect) version of the file can be downloaded from /0.0.0/clubs/ccd11959-4497-4d27-99b6-7467b2445809/videos/87cf9e45-3c8f-4a1f-a372-fb44c523eab5.

Instead of returning a 303 See Other status code when the "processing" has finished, we return 200 OK. I couldn't find a way to do this with the current version of the REST library that we're using, but I have created an issue about it at silkapp/rest#136.

The file can be downloaded, but includes some information that is not relevant to the uploaded file. I have created an issue about it at silkapp/rest#137.

@jonkri
Copy link
Collaborator Author

jonkri commented Jul 6, 2015

Ah, when I uploaded the video using Curl's --data-binary option, it worked. This is what I did:

Request:

$ curl --data-binary @big-buck-bunny_trailer.webm --request POST http://localhost:3000/v0.0.0/clubs/ccd11959-4497-4d27-99b6-7467b2445809/upload

Output:

f1c1eaf0-1770-4a73-b2bf-3f665caa92e9

Request:

$ wget -O test.webm http://localhost:3000/v0.0.0/clubs/ccd11959-4497-4d27-99b6-7467b2445809/videos/f1c1eaf0-1770-4a73-b2bf-3f665caa92e9

Command:

$ totem test.webm

And voilà, it plays. :)

@hesa
Copy link
Owner

hesa commented Jul 6, 2015

Nice :)

Can I get a list of videos?

Can I list per club/team/training-phase/..?

jonkri pushed a commit that referenced this issue Jul 6, 2015
@jonkri
Copy link
Collaborator Author

jonkri commented Jul 6, 2015

(Sorry, I posted a copy of this message before it was finished, so I deleted that and wrote another one.)

There are currently no relations between anything other than the Video and Club entities.

I have made endpoints like /v0.0.0/clubs/ccd11959-4497-4d27-99b6-7467b2445809/videos return the list of video UUIDs that has been successfully uploaded. (They are sorted by their created date (a field I added last night) in a descending order.)

For the second question I suppose that each video should be able to reference a team, a member, and a training phase. While the possibility of referencing at most one of each of these do for now?

@jonkri
Copy link
Collaborator Author

jonkri commented Jul 23, 2015

(About using lazy bytestrings, as in our current solutions: Some streaming interface would be better, since lazy IO is a bit finicky. The problem with lazy IO is that it doesn't give you control over when stuff happens. I.e. it will read from the file handler when you "look at" the bytestring. You can't control when the file handle is closed.)

@jonkri
Copy link
Collaborator Author

jonkri commented Aug 10, 2015

In addition to compressing the video, we probably want to generate a thumbnail (or several) for any given video as well.

jonkri pushed a commit that referenced this issue Sep 3, 2015
Better video support (#9):

Added a video metadata step necessarily preceeding the video upload.
By including an optional member identifier, we could realize the
capability of instructional videos alongside individual performance
videos (no member identifier means that it's an instructional video).
Additionally, the video metadata includes required club and training
phase identifiers (one of each).

We can now get a list of videos per member, training phase and team
(team is inferred by the member, if this value is set) in addition to
per club.

JSON relation clarifications:

The JSON should now contain identifiers to explicitly express the
entity relationships. Will solve issues analogous to #8.

Web improvements:

Added simple video viewing (assuming WebM). Fixed a problem related
to the teams select box being visible even though no teams exist.
@jonkri
Copy link
Collaborator Author

jonkri commented Sep 3, 2015

The video functionality has received significant changes in ccd9ffc.

Like before, videos belong to a given club. Videos are created by first setting the metadata, and then by uploading the actual video data. The first of these two steps is carried out like so:

POST http://localhost:3000/v0.0.0/clubs/<ID>/videos

The payload of the above request must be application/json and contain one or two UUID properties, the metadata of the video: trainingPhaseUuid (required) and memberUuid (optional). The request returns a UUID string with the identifier of the video element. The video element can be queried in the following way:

GET http://localhost:3000/v0.0.0/clubs/<ID>/videos/uuid/<ID>

The output will look something like this:

{
    "status": "empty",
    "uuid": "b1b62f24-32f0-4ba3-a025-c4933cf89c30",
    "created": "2015-09-02T16:17:23.554190000000Z",
    "trainingPhaseUuid": "bd59ad1d-6c64-4d2b-a5f2-108c8ec5b931",
    "clubUuid": "4bdafb08-d8a5-40e2-83c6-f78f206f9e0f",
    "memberUuid": "92083708-5c47-4c2e-920d-193b5ffd8862"
}

The second identifier in the GET request is the identifier of the video as returned by the first request. The information returned has the following properties: uuid, trainingPhase, member (possibly null), team (null if and only if the member property is null or the given member is not assigned to a team), and status (a) "empty", b) "processing", c) "complete" or d) "failure", depending on whether a) the video data has not been successfully uploaded, b) the video data has been uploaded but has not yet finished processing, c) the video data (has been uploaded and) has been successfully processed, or d) the video data (has been uploaded and) has failed to process).

In order to make the video "complete", and visible, throughout the API, upload the video data:

POST http://localhost:3000/v0.0.0/clubs/<ID>/videos/uuid/<ID>/upload

A command-line example for uploading a file would be:

curl --insecure --data-binary @big-buck-bunny_trailer.webm --request POST https://localhost/api/0.0.0/clubs/<ID>/videos/uuid/<ID>/upload

Upon successful uploading of the video data you can monitor the information returned by the GET request above. Once the video has been successfully processed, you can access it using the following request:

GET http://localhost:3000/v0.0.0/clubs/<ID>/videos/uuid/<ID>/download

You can at any time get all videos for a given club, all videos related to a given member, all videos related to a given training phase, all videos related to a given team (derived from the member that might be set for a video), or all instructional videos (i.e. all videos without any member property set), using the following requests:

GET http://localhost:3000/v0.0.0/clubs/<ID>/videos
GET http://localhost:3000/v0.0.0/clubs/<ID>/videos/member/<ID>
GET http://localhost:3000/v0.0.0/clubs/<ID>/videos/team/<ID>
GET http://localhost:3000/v0.0.0/clubs/<ID>/videos/training-phase/<ID>
GET http://localhost:3000/v0.0.0/clubs/<ID>/videos/instructional

The assumptions are that we don't need to reference a team directly and that we don't need names for videos. We still might want to pursuit non-lazy IO for uploading, as well as thumbnail images. Also, we are still not compressing the video, or changing it in any other way. We should probably investigate using ffmpeg for this.

@jonkri
Copy link
Collaborator Author

jonkri commented Dec 30, 2015

Great news: Video files can now be converted to WebM files!

What happens under the hood is a simple invocation of the avconv (ffmpeg) binary). We are using the default libav-tools as shipped by Ubuntu (the system upon which the Haskell Docker image is built), as can be seen here. So the functionality is not fancy or anything, but so far, it at least seems to fulfill our basic video compression needs.

I thought it might be useful for me to show some step-by-step instructions on how to upload (and download) a video using Curl.

First, we need a club.

$ curl --data '{ "name": "Bergsjö IF" }' --header "Content-Type: application/json" --request POST localhost:3000/0.0.0/clubs
{"uuid":"e8866c4f-9ffd-43d3-99b6-ff9f7dcc4224","created":"2015-12-30T02:47:01.146718Z","teamUuids":[],"memberUuids":[],"trainingPhaseUuids":[],"videoUuids":[],"name":"Bergsjö IF"}

I'll use an environment variable for the club UUID, so that the instructions below are cleaner and easier to copy and paste.

$ export CLUB_UUID=e8866c4f-9ffd-43d3-99b6-ff9f7dcc4224

Since every video has to be associated with a training phase, let's create one.

$ curl --data '{ "name": "Slå Henrik i armhävningar" }' --header "Content-Type: application/json" --request POST localhost:3000/0.0.0/clubs/$CLUB_UUID/training-phases
{"uuid":"24ae7501-c876-400e-a0bf-cd2ea02bf2a9","created":"2015-12-30T02:47:19.74457Z","videoUuids":[],"name":"Slå Henrik i armhävningar","clubUuid":"e8866c4f-9ffd-43d3-99b6-ff9f7dcc4224"}

Analogous to above, I'll store away the training phase UUID.

$ export TRAINING_PHASE_UUID=24ae7501-c876-400e-a0bf-cd2ea02bf2a9

Next, we'll create the video (metadata).

$ curl --data "{ \"trainingPhaseUuid\": \"$TRAINING_PHASE_UUID\" }" --header "Content-Type: application/json" --request POST localhost:3000/0.0.0/clubs/$CLUB_UUID/videos
{"status":"empty","uuid":"eb0a50fe-a585-4d88-a37a-268a01d7a528","created":"2015-12-30T02:47:38.058862Z","trainingPhaseUuid":"24ae7501-c876-400e-a0bf-cd2ea02bf2a9","clubUuid":"e8866c4f-9ffd-43d3-99b6-ff9f7dcc4224"}

We'll save the video UUID in an environment variable as well.

$ export VIDEO_UUID=eb0a50fe-a585-4d88-a37a-268a01d7a528

At this point, the video exists, but since it doesn't have any video data associated with it, it won't be visible on the site. However, since we know the video identifier, we can query the video metadata like so:

$ curl --header "Content-Type: application/json" localhost:3000/0.0.0/clubs/$CLUB_UUID/videos/uuid/$VIDEO_UUID
{"status":"empty","uuid":"eb0a50fe-a585-4d88-a37a-268a01d7a528","created":"2015-12-30T02:47:38.058862Z","trainingPhaseUuid":"24ae7501-c876-400e-a0bf-cd2ea02bf2a9","clubUuid":"e8866c4f-9ffd-43d3-99b6-ff9f7dcc4224"}

Now, let's upload some video data:

$ curl --data-binary @sample.3gp --insecure --request POST https://localhost/api/0.0.0/clubs/$CLUB_UUID/videos/uuid/$VIDEO_UUID/upload

The API will now begin processing the video data into our desired format (WebM). If we repeat the GET request performed above, we can see that the status of the video now is "processing". This can be the basis to, for instance, show a loading animation in the client.

$ curl --header "Content-Type: application/json" localhost:3000/0.0.0/clubs/$CLUB_UUID/videos/uuid/$VIDEO_UUID
{"status":"processing","uuid":"eb0a50fe-a585-4d88-a37a-268a01d7a528","created":"2015-12-30T02:47:38.058862Z","trainingPhaseUuid":"24ae7501-c876-400e-a0bf-cd2ea02bf2a9","clubUuid":"e8866c4f-9ffd-43d3-99b6-ff9f7dcc4224"}

The video file, around 30 MB, took between two and three minutes to process on my machine. However, since this happens in a separate thread, the API was still responsive during that time.

Once the video has finished processing, the status will changed to "complete" (unless Ffmpeg finishes with a non-zero exit code, in which case it will change to "failure"):

$ curl --header "Content-Type: application/json" localhost:3000/0.0.0/clubs/$CLUB_UUID/videos/uuid/$VIDEO_UUID
{"status":"complete","uuid":"eb0a50fe-a585-4d88-a37a-268a01d7a528","created":"2015-12-30T02:47:38.058862Z","published":"2015-12-30T02:51:03.262879Z","trainingPhaseUuid":"24ae7501-c876-400e-a0bf-cd2ea02bf2a9","clubUuid":"e8866c4f-9ffd-43d3-99b6-ff9f7dcc4224"}

We can now download the video with a simple GET request.

$ curl --insecure -o sample.webm https://localhost/api/0.0.0/clubs/$CLUB_UUID/videos/uuid/$VIDEO_UUID/download

I have tried to upload both 3GP and MP4 files, and both worked. I also accessed the video files using the web client, and both could be played.

Additionally, the endpoints mentioned in my last comment (fetching videos by member, team, training phase and instructional) still works, and so does the functionality of specifying the optional member UUID when creating the video.

What do you think? Do you think you could put together some Android client-side magic using this? :)

@jonkri
Copy link
Collaborator Author

jonkri commented Dec 30, 2015

Hi, again!

I have now added the possibility of downloading a picture snapshot from the video. This picture can be used as a thumbnail picture or whatever. The snapshot will be in the JPEG format, and is of relatively high quality, and is currently of the same resolution as the video.

This "poster" picture can be downloaded like so:

$ curl --insecure -o sample.jpeg https://localhost/api/0.0.0/clubs/$CLUB_UUID/videos/uuid/$VIDEO_UUID/download

One current limitation is that the picture is taken in the first frame. See #19 for more information about this.

jonkri pushed a commit that referenced this issue Jan 19, 2016
@hesa
Copy link
Owner

hesa commented Feb 18, 2016

Just to make sure. When downloading a file we get it in webm format?

@jonkri
Copy link
Collaborator Author

jonkri commented Feb 20, 2016

That's correct. :)

@jonkri
Copy link
Collaborator Author

jonkri commented Feb 20, 2016

@hesa
Copy link
Owner

hesa commented Feb 20, 2016

Just got download to work. Request code OK and all.

Will implement replacement of the "local" later

Now food .. and more beer

@jonkri
Copy link
Collaborator Author

jonkri commented Feb 20, 2016

Lovely! :) Nice work!

@hesa
Copy link
Owner

hesa commented Feb 23, 2016

Later is now.
All done now :)

@jonkri
Copy link
Collaborator Author

jonkri commented Feb 23, 2016

Awesome!

@jonkri
Copy link
Collaborator Author

jonkri commented Mar 22, 2016

I should look through this issue, and probably close it.

@jonkri jonkri mentioned this issue Apr 7, 2016
@jonkri
Copy link
Collaborator Author

jonkri commented Apr 7, 2016

This issue can be closed.

@jonkri jonkri closed this as completed Apr 7, 2016
@jonkri jonkri removed their assignment Apr 7, 2016
jonkri pushed a commit that referenced this issue Apr 30, 2016
- Add new video filter endpoints (#9)
- Add new "videos" tab in the web application (#41)
- Add new "videos" page in the web application (#41)
- Fix a problem resulting in large files not being uploaded
- Give PostgreSQL a few more seconds to start up
- Make some JavaScript refactoring related to resolves
@jonkri jonkri removed the 1.0 label May 27, 2016
@jonkri jonkri added this to the 0.1 milestone May 27, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants