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

Implemented Twilio Video Api #20

Closed
wants to merge 1 commit into from
Closed
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
1 change: 1 addition & 0 deletions AUTHORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Maksym Pavlenko <[email protected]>
Marcus Westin <[email protected]>
Minko Gechev <[email protected]>
Scott Gose <[email protected]>
Tomasz Tomalak <[email protected]>
4 changes: 4 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ go_library(
"prices_voice.go",
"queue.go",
"recording.go",
"room.go",
"transcription.go",
"types.go",
"validation.go",
"video_recording.go",
"wireless.go",
"wireless_commands.go",
],
Expand Down Expand Up @@ -80,9 +82,11 @@ go_test(
"prices_voice_test.go",
"recording_test.go",
"responses_test.go",
"room_test.go",
"transcription_test.go",
"types_test.go",
"validation_test.go",
"video_recording_test.go",
"wireless_commands_test.go",
"wireless_test.go",
],
Expand Down
20 changes: 20 additions & 0 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ const NotifyVersion = "v1"
const LookupBaseURL = "https://lookups.twilio.com"
const LookupVersion = "v1"

// Video service
var VideoBaseUrl = "https://video.twilio.com"

const VideoVersion = "v1"

type Client struct {
*rest.Client
Monitor *Client
Expand All @@ -62,6 +67,7 @@ type Client struct {
Wireless *Client
Notify *Client
Lookup *Client
Video *Client

// FullPath takes a path part (e.g. "Messages") and
// returns the full API path, including the version (e.g.
Expand Down Expand Up @@ -108,6 +114,10 @@ type Client struct {

// NewLookupClient initializes these services
LookupPhoneNumbers *LookupPhoneNumbersService

// NewVideoClient initializes these services
Rooms *RoomService
VideoRecordings *VideoRecordingService
}

const defaultTimeout = 30*time.Second + 500*time.Millisecond
Expand Down Expand Up @@ -245,6 +255,15 @@ func NewLookupClient(accountSid string, authToken string, httpClient *http.Clien
return c
}

// NewVideoClient returns a new Client to use the video API
func NewVideoClient(accountSid string, authToken string, httpClient *http.Client) *Client {
c := newNewClient(accountSid, authToken, VideoBaseUrl, httpClient)
c.APIVersion = VideoVersion
c.Rooms = &RoomService{client: c}
c.VideoRecordings = &VideoRecordingService{client: c}
return c
}

// NewClient creates a Client for interacting with the Twilio API. This is the
// main entrypoint for API interactions; view the methods on the subresources
// for more information.
Expand All @@ -269,6 +288,7 @@ func NewClient(accountSid string, authToken string, httpClient *http.Client) *Cl
c.Wireless = NewWirelessClient(accountSid, authToken, httpClient)
c.Notify = NewNotifyClient(accountSid, authToken, httpClient)
c.Lookup = NewLookupClient(accountSid, authToken, httpClient)
c.Video = NewVideoClient(accountSid, authToken, httpClient)

c.Accounts = &AccountService{client: c}
c.Applications = &ApplicationService{client: c}
Expand Down
47 changes: 47 additions & 0 deletions responses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func getServer(response []byte) (*Client, *Server) {
client.Wireless.Base = s.URL
client.Notify.Base = s.URL
client.Lookup.Base = s.URL
client.Video.Base = s.URL
return client, s
}

Expand All @@ -83,6 +84,7 @@ func getServerCode(response []byte, code int) (*Client, *Server) {
client.Wireless.Base = s.URL
client.Notify.Base = s.URL
client.Lookup.Base = s.URL
client.Video.Base = s.URL
return client, s
}

Expand Down Expand Up @@ -2730,5 +2732,50 @@ var phoneLookupResponse = []byte(`
}
`)

var roomResponse = []byte(`
{
"api_key_sid": "AC58f1e8f2b1c6b88ca90a012a4be0c279",
"date_created": "2015-07-30T20:00:00Z",
"date_updated": "2015-07-30T20:00:00Z",
"status": "in-progress",
"type": "peer-to-peer",
"sid": "RMca86cf94c7d4f89e0bd45bfa7d9b9e7d",
"enable_turn": false,
"unique_name": "DailyStandup",
"max_participants": 10,
"duration": 0,
"status_callback_method": "POST",
"status_callback": "",
"record_participants_on_connect": false,
"end_time": "2015-07-30T20:00:00Z",
"url": "https://video.twilio.com/v1/Rooms/RMca86cf94c7d4f89e0bd45bfa7d9b9e7d",
"links": {
"recordings": "https://video.twilio.com/v1/Rooms/RMca86cf94c7d4f89e0bd45bfa7d9b9e7d/Recordings"
}
}
`)

var videoRecordingResponse = []byte(`
{
"api_key_sid": "AC58f1e8f2b1c6b88ca90a012a4be0c279",
"status": "processing",
"date_created": "2015-07-30T20:00:00Z",
"sid": "RT63868a235fc1cf3987e6a2b67346273f",
"source_sid": "MT58f1e8f2b1c6b88ca90a012a4be0c279",
"size": 0,
"url": "https://video.twilio.com/v1/Recordings/RT58f1e8f2b1c6b88ca90a012a4be0c279",
"type": "audio",
"duration": 20,
"container_format": "mka",
"codec": "OPUS",
"grouping_sids": {
"room_sid" : "RM58f1e8f2b1c6b88ca90a012a4be0c279"
},
"links": {
"media": "https://video.twilio.com/v1/Recordings/RT58f1e8f2b1c6b88ca90a012a4be0c279/Media"
}
}
`)

const from = "+19253920364"
const to = "+19253920364"
92 changes: 92 additions & 0 deletions room.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package twilio

import (
"context"
"net/url"
)

const roomPathPart = "Rooms"

type RoomService struct {
client *Client
}

type Room struct {
Sid string `json:"sid"`
AccountSid string `json:"account_sid"`
Type string `json:"type"`
EnableTurn bool `json:"enable_turn"`
UniqueName string `json:"unique_name"`
StatusCallback string `json:"status_callback"`
StatusCallbackMethod string `json:"status_callback_method"`
MaxParticipants uint `json:"max_participants"`
RecordParticipantsOnConnect bool `json:"record_participants_on_connect"`
Duration uint `json:"duration"`
MediaRegion string `json:"media_region"`
Status Status `json:"status"`
DateCreated TwilioTime `json:"date_created"`
DateUpdated TwilioTime `json:"date_updated"`
EndTime TwilioTime `json:"end_time"`
URL string `json:"url"`
Links map[string]string `json:"links"`
}

type RoomPage struct {
Meta Meta `json:"meta"`
Rooms []*Room `json:"rooms"`
}

type RoomPageIterator struct {
p *PageIterator
}

// Get finds a single Room resource by its sid or unique name, or returns an error.
func (r *RoomService) Get(ctx context.Context, sidOrUniqueName string) (*Room, error) {
room := new(Room)
err := r.client.GetResource(ctx, roomPathPart, sidOrUniqueName, room)
return room, err
}

// Complete an in-progress Room with the given sid. All connected
// Participants will be immediately disconnected from the Room.
func (r *RoomService) Complete(sid string) (*Room, error) {
room := new(Room)
v := url.Values{}
v.Set("Status", string(StatusCompleted))
err := r.client.UpdateResource(context.Background(), roomPathPart, sid, v, room)
return room, err
}

// Create a room with the given url.Values. For more information on valid values,
// see https://www.twilio.com/docs/api/video/rooms-resource#post-parameters or use the
func (r *RoomService) Create(ctx context.Context, data url.Values) (*Room, error) {
room := new(Room)
err := r.client.CreateResource(ctx, roomPathPart, data, room)
return room, err
}

// Returns a list of rooms. For more information on valid values,
// see https://www.twilio.com/docs/api/video/rooms-resource#get-list-resource
func (r *RoomService) GetPage(ctx context.Context, data url.Values) (*RoomPage, error) {
return r.GetPageIterator(data).Next(ctx)
}

// GetPageIterator returns an iterator which can be used to retrieve pages.
func (r *RoomService) GetPageIterator(data url.Values) *RoomPageIterator {
iter := NewPageIterator(r.client, data, roomPathPart)
return &RoomPageIterator{
p: iter,
}
}

// Next returns the next page of resources. If there are no more resources,
// NoMoreResults is returned.
func (r *RoomPageIterator) Next(ctx context.Context) (*RoomPage, error) {
rp := new(RoomPage)
err := r.p.Next(ctx, rp)
if err != nil {
return nil, err
}
r.p.SetNextPageURI(rp.Meta.NextPageURL)
return rp, nil
}
25 changes: 25 additions & 0 deletions room_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package twilio

import (
"context"
"testing"
)

func TestGetRoom(t *testing.T) {
t.Parallel()
client, server := getServer(roomResponse)
defer server.Close()
room, err := client.Video.Rooms.Get(context.Background(), "RMca86cf94c7d4f89e0bd45bfa7d9b9e7d")
if err != nil {
t.Fatal(err)
}
if room.Sid != "RMca86cf94c7d4f89e0bd45bfa7d9b9e7d" {
t.Errorf("room: got sid %q, want %q", room.Sid, "RMca86cf94c7d4f89e0bd45bfa7d9b9e7d")
}
if room.Status != StatusInProgress {
t.Errorf("room: got status %q, want %q", room.Status, StatusInProgress)
}
if room.Type != RoomTypePeerToPeer {
t.Errorf("room: got type %q, want %q", room.Type, RoomTypePeerToPeer)
}
}
4 changes: 4 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,7 @@ func capitalize(s string) string {
utf8.EncodeRune(b, unicode.ToTitle(r))
return strings.Join([]string{string(b), s[l:]}, "")
}

// types of video room
const RoomType = "group"
const RoomTypePeerToPeer = "peer-to-peer"
95 changes: 95 additions & 0 deletions video_recording.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package twilio

import (
"context"
"net/url"
"strings"
)

type VideoRecordingService struct {
client *Client
}

const videoRecordingsPathPart = "Recordings"

func videoMediaPathPart(recordingSid string) string {
return strings.Join([]string{videoRecordingsPathPart, recordingSid, "Media"}, "/")
}

type VideoRecording struct {
Sid string `json:"sid"`
Duration uint `json:"duration"`
Status Status `json:"status"`
DateCreated TwilioTime `json:"date_created"`
SourceSid string `json:"source_sid"`
URI string `json:"uri"`
Size uint `json:"size"`
Type string `json:"type"`
ContainerFormat string `json:"container_format"`
Codec string `json:"codec"`
GroupingSids map[string]string `json:"grouping_sids"`
Links map[string]string `json:"links"`
}

type VideoMedia struct {
Location string `json:"location"`
}

type VideoRecordingPage struct {
Meta Meta `json:"meta"`
Recordings []*VideoRecording `json:"recordings"`
}

type VideoRecordingPageIterator struct {
p *PageIterator
}

// When you make a request to this URL, Twilio will generate a temporary URL for accessing
// this binary data, and issue an HTTP 302 redirect response to your request. The Recording
// will be returned in the format as described in the metadata.
func (vr *VideoRecordingService) Media(ctx context.Context, sid string) (*VideoMedia, error) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to have the API resemble this one for Messages Media? https://github.com/kevinburke/twilio-go/blob/master/media.go#L59

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that but are you sure we need that to download this media? Because when i was looking at this Messages Media it was looking like some complicated stuff (https://www.twilio.com/docs/api/messaging/media#media-list-resource) but in VideoRecordings they returning proper link to download it

{
    "location": "https://com.twilio.dev-us1.video.recording.s3.amazonaws.com/RTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

media := new(VideoMedia)
path := videoMediaPathPart(sid)
err := vr.client.ListResource(ctx, path, nil, media)
return media, err
}

// Returns the VideoRecording with the given sid.
func (vr *VideoRecordingService) Get(ctx context.Context, sid string) (*VideoRecording, error) {
recording := new(VideoRecording)
err := vr.client.GetResource(ctx, videoRecordingsPathPart, sid, recording)
return recording, err
}

// Delete the VideoRecording with the given sid. If the VideoRecording has already been
// deleted, or does not exist, Delete returns nil. If another error or a
// timeout occurs, the error is returned.
func (vr *VideoRecordingService) Delete(ctx context.Context, sid string) error {
return vr.client.DeleteResource(ctx, videoRecordingsPathPart, sid)
}

// Returns a list of recordings. For more information on valid values,
// see https://www.twilio.com/docs/api/video/recordings-resource#recordings-list-resource
func (vr *VideoRecordingService) GetPage(ctx context.Context, data url.Values) (*VideoRecordingPage, error) {
return vr.GetPageIterator(data).Next(ctx)
}

// GetPageIterator returns an iterator which can be used to retrieve pages.
func (vr *VideoRecordingService) GetPageIterator(data url.Values) *VideoRecordingPageIterator {
iter := NewPageIterator(vr.client, data, videoRecordingsPathPart)
return &VideoRecordingPageIterator{
p: iter,
}
}

// Next returns the next page of resources. If there are no more resources,
// NoMoreResults is returned.
func (vr *VideoRecordingPageIterator) Next(ctx context.Context) (*VideoRecordingPage, error) {
vrp := new(VideoRecordingPage)
err := vr.p.Next(ctx, vrp)
if err != nil {
return nil, err
}
vr.p.SetNextPageURI(vrp.Meta.NextPageURL)
return vrp, nil
}
Loading