Skip to content

Commit c211f79

Browse files
authored
Add GetInvitedFriends, RemoveInvitedFriend client funcs (#47)
closes #46
1 parent bef53df commit c211f79

File tree

4 files changed

+140
-10
lines changed

4 files changed

+140
-10
lines changed

models.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,31 @@ type inviteFriendSettings struct {
400400
FilterTelevision string `json:"filterTelevision"`
401401
}
402402

403+
type invitedFriendsResponse struct {
404+
XMLName xml.Name `xml:"MediaContainer"`
405+
FriendlyName string `xml:"friendlyName,attr"`
406+
Identifier string `xml:"identifier,attr"`
407+
MachineIdentifier string `xml:"machineIdentifier,attr"`
408+
Size int `xml:"size,attr"`
409+
InvitedFriends []InvitedFriend `xml:"Invite"`
410+
}
411+
412+
type InvitedFriend struct {
413+
ID string `xml:"id,attr"`
414+
CreatedAt string `xml:"createdAt,attr"`
415+
IsFriend bool `xml:"friend,attr"`
416+
IsHome bool `xml:"home,attr"`
417+
IsServer bool `xml:"server,attr"`
418+
Username string `xml:"username,attr"`
419+
Email string `xml:"email,attr"`
420+
Thumb string `xml:"thumb,attr"`
421+
FriendlyName string `xml:"friendlyName,attr"`
422+
Server struct {
423+
Name string `xml:"name,attr"`
424+
NumLibraries string `xml:"numLibraries,attr"`
425+
} `xml:"Server"`
426+
}
427+
403428
type resourcesResponse struct {
404429
XMLName xml.Name `xml:"MediaContainer"`
405430
Size int `xml:"size,attr"`

plex.go

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ import (
2020
"github.com/google/uuid"
2121
)
2222

23-
const plexURL = "https://plex.tv"
23+
const (
24+
plexURL = "https://plex.tv"
25+
applicationXml = "application/xml"
26+
applicationJson = "application/json"
27+
)
2428

2529
func defaultHeaders() headers {
2630
version := "0.0.1"
@@ -34,8 +38,8 @@ func defaultHeaders() headers {
3438
ClientIdentifier: "go-plex-client-v" + version,
3539
ContainerSize: "Plex-Container-Size=50",
3640
ContainerStart: "X-Plex-Container-Start=0",
37-
Accept: "application/json",
38-
ContentType: "application/json",
41+
Accept: applicationJson,
42+
ContentType: applicationJson,
3943
}
4044
}
4145

@@ -118,7 +122,7 @@ func SignIn(username, password string) (*Plex, error) {
118122
newHeaders := p.Headers
119123
// Doesn't like having a content type, even form-data
120124
newHeaders.ContentType = "application/x-www-form-urlencoded"
121-
newHeaders.Accept = "application/json"
125+
newHeaders.Accept = applicationJson
122126
resp, err := p.post(query, []byte(body.Encode()), newHeaders)
123127

124128
if err != nil {
@@ -548,7 +552,7 @@ func (p *Plex) GetFriends() ([]Friends, error) {
548552

549553
newHeaders := p.Headers
550554

551-
newHeaders.Accept = "application/xml"
555+
newHeaders.Accept = applicationXml
552556

553557
resp, err := p.get(query, newHeaders)
554558

@@ -733,6 +737,70 @@ func (p *Plex) RemoveFriendAccessToLibrary(userID, machineID, serverID string) (
733737
return true, nil
734738
}
735739

740+
// GetInvitedFriends get all invited friends with request still pending
741+
func (p *Plex) GetInvitedFriends() ([]InvitedFriend, error) {
742+
743+
query := plexURL + "/api/invites/requested"
744+
newHeaders := p.Headers
745+
newHeaders.Accept = applicationXml
746+
747+
resp, err := p.get(query, newHeaders)
748+
if err != nil {
749+
return []InvitedFriend{}, err
750+
}
751+
752+
if resp.StatusCode == http.StatusUnauthorized {
753+
return []InvitedFriend{}, errors.New(ErrorNotAuthorized)
754+
} else if resp.StatusCode != http.StatusOK {
755+
return []InvitedFriend{}, fmt.Errorf(ErrorServerReplied, resp.StatusCode)
756+
}
757+
758+
var invitedFriendsResp invitedFriendsResponse
759+
defer resp.Body.Close()
760+
err = xml.NewDecoder(resp.Body).Decode(&invitedFriendsResp)
761+
if err != nil {
762+
return []InvitedFriend{}, err
763+
}
764+
765+
return invitedFriendsResp.InvitedFriends, nil
766+
}
767+
768+
// RemoveInvitedFriend cancel pending friend invite
769+
func (p *Plex) RemoveInvitedFriend(inviteID string, isFriend, isServer, isHome bool) (bool, error) {
770+
query := plexURL + "/api/invites/requested/" + url.QueryEscape(inviteID)
771+
772+
parsedQuery, parseErr := url.Parse(query)
773+
if parseErr != nil {
774+
return false, parseErr
775+
}
776+
777+
vals := parsedQuery.Query()
778+
vals.Add("friend", boolToOneOrZero(isFriend))
779+
vals.Add("server", boolToOneOrZero(isServer))
780+
vals.Add("home", boolToOneOrZero(isHome))
781+
782+
parsedQuery.RawQuery = vals.Encode()
783+
784+
query = parsedQuery.String()
785+
786+
resp, err := p.delete(query, p.Headers)
787+
if err != nil {
788+
return false, err
789+
}
790+
791+
defer resp.Body.Close()
792+
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusBadRequest {
793+
return false, errors.New(resp.Status)
794+
}
795+
796+
result := new(resultResponse)
797+
if err := xml.NewDecoder(resp.Body).Decode(result); err != nil {
798+
return false, err
799+
}
800+
801+
return result.Response.Code == 0, nil
802+
}
803+
736804
// CheckUsernameOrEmail will check if the username is a Plex user or will verify an email is valid
737805
func (p *Plex) CheckUsernameOrEmail(usernameOrEmail string) (bool, error) {
738806

@@ -767,7 +835,7 @@ func (p *Plex) StopPlayback(machineID string) error {
767835

768836
newHeaders := p.Headers
769837

770-
newHeaders.Accept = "application/xml"
838+
newHeaders.Accept = applicationXml
771839
newHeaders.TargetClientIdentifier = machineID
772840

773841
resp, err := p.get(query, newHeaders)
@@ -898,7 +966,7 @@ func (p *Plex) GetSections(machineID string) ([]ServerSections, error) {
898966

899967
newHeaders := p.Headers
900968

901-
newHeaders.Accept = "application/xml"
969+
newHeaders.Accept = applicationXml
902970

903971
resp, err := p.get(query, newHeaders)
904972

@@ -1230,7 +1298,7 @@ func (p *Plex) TerminateSession(sessionID string, reason string) error {
12301298
query := fmt.Sprintf("%s/status/sessions/terminate?sessionId=%s&reason=%s", p.URL, sessionID, reason)
12311299

12321300
newHeaders := p.Headers
1233-
newHeaders.Accept = "application/xml"
1301+
newHeaders.Accept = applicationXml
12341302

12351303
resp, err := p.get(query, newHeaders)
12361304

plex_test.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func init() {
3333
func newTestServer(code int, body string) (*httptest.Server, *Plex) {
3434
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3535
w.WriteHeader(code)
36-
w.Header().Set("Content-Type", "application/xml")
36+
w.Header().Set("Content-Type", applicationXml)
3737
fmt.Fprintln(w, body)
3838
}))
3939

@@ -191,3 +191,32 @@ func TestInviteFriendResponse(t *testing.T) {
191191
t.Error(err.Error())
192192
}
193193
}
194+
195+
func TestPlex_GetInvitedFriends_Response(t *testing.T) {
196+
testData := []byte(`
197+
<?xml version="1.0" encoding="UTF-8"?>
198+
<MediaContainer friendlyName="myPlex" identifier="com.plexapp.plugins.myplex" machineIdentifier="abc123abc123abc123abc123abc123abc123" size="3">
199+
<Invite id="email1@gmail.com" createdAt="1639964970" friend="0" home="0" server="1" username="" email="email1@gmail.com" thumb="" friendlyName="email1@gmail.com">
200+
<Server name="Server123" numLibraries="3"/>
201+
</Invite>
202+
<Invite id="19661994" createdAt="1643379560" friend="0" home="1" server="0" username="home-user" email="home-user@gmail.com" thumb="https://plex.tv/users/abc/avatar?c=123" friendlyName="home-user"/>
203+
<Invite id="22522496" createdAt="1643574613" friend="1" home="0" server="1" username="existing-user" email="existing-user@umn.edu" thumb="https://plex.tv/users/xyz/avatar?c=456" friendlyName="existing-user">
204+
<Server name="Server123" numLibraries="3"/>
205+
</Invite>
206+
</MediaContainer>
207+
`)
208+
209+
result := new(invitedFriendsResponse)
210+
211+
if err := xml.Unmarshal(testData, result); err != nil {
212+
t.Error(err.Error())
213+
}
214+
}
215+
216+
func TestPlex_RemoveInvitedFriend(t *testing.T) {
217+
success, err := plexConn.RemoveInvitedFriend("email-id-dne@gmail.com", false, true, false)
218+
if err.Error() != "404 Not Found" {
219+
// expect a 404
220+
t.Errorf("success: %v, error: %v", success, err)
221+
}
222+
}

utils.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ func (p *Plex) post(query string, body []byte, h headers) (*http.Response, error
172172
return &http.Response{}, err
173173
}
174174

175-
// req.Header.Set("Content-Type", "application/json")
175+
// req.Header.Set("Content-Type", applicationJson)
176176
req.Header.Add("Accept", h.Accept)
177177
req.Header.Add("X-Plex-Platform", h.Platform)
178178
req.Header.Add("X-Plex-Platform-Version", h.PlatformVersion)
@@ -271,3 +271,11 @@ func (p *Plex) put(query string, body []byte, h headers) (*http.Response, error)
271271

272272
return resp, nil
273273
}
274+
275+
func boolToOneOrZero(input bool) string {
276+
if input {
277+
return "1"
278+
} else {
279+
return "0"
280+
}
281+
}

0 commit comments

Comments
 (0)