Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
RoyXiang committed Mar 11, 2022
2 parents cfbb274 + 281ea62 commit 9d132cb
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 360 deletions.
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,13 @@ env CGO_ENABLED=0 go install -trimpath -ldflags="-s -w" github.com/RoyXiang/plex

1. Configure environment variables in your preferred way
- `PLEX_BASEURL` (Required, e.g. `http://127.0.0.1:32400`)
- `PLEX_TOKEN` (Optional, if you need it, see [here](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/))
* It is used to receive notifications from Plex Media Server
* Notifications are used to flush the cache of metadata
- `REDIS_URL` (Optional, e.g. `redis://127.0.0.1:6379`)
* If you need a cache layer, set a value for it
* `PLEX_TOKEN` is required
- `PLAXT_URL` (Optional, e.g. `https://plaxt.astandke.com/api?id=generate-your-own-silly`)
* `PLEX_TOKEN` is required
* Set it if you run an instance of [Plaxt](https://github.com/XanderStrike/goplaxt)
* Or, you can set it to [the official one](https://plaxt.astandke.com/)
- `PLEX_TOKEN` (Optional, if you need it, see [here](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/))
- `REDIRECT_WEB_APP` (Optional, default: `true`)
- `DISABLE_TRANSCODE` (Optional, default: `true`)
- `NO_REQUEST_LOGS` (Optional, default: `false`)
Expand Down
14 changes: 14 additions & 0 deletions common/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type MultipleLock interface {
TryLock(interface{}, time.Duration) bool
Lock(interface{})
Unlock(interface{})
RLock(interface{})
RUnlock(interface{})
}

type lock struct {
Expand Down Expand Up @@ -44,6 +46,18 @@ func (l *lock) Unlock(key interface{}) {
l.putBackInPool(key, m)
}

func (l *lock) RLock(key interface{}) {
m := l.getLocker(key)
atomic.AddInt64(&m.counter, 1)
m.lock.rLock()
}

func (l *lock) RUnlock(key interface{}) {
m := l.getLocker(key)
m.lock.rUnlock()
l.putBackInPool(key, m)
}

func (l *lock) getLocker(key interface{}) *refCounter {
res, _ := l.inUse.LoadOrStore(key, &refCounter{
counter: 0,
Expand Down
34 changes: 29 additions & 5 deletions common/mutex.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,52 @@ import (
)

type timedMutex struct {
c chan struct{}
write chan struct{}
readers chan int
}

func newTimedMutex() interface{} {
return &timedMutex{make(chan struct{}, 1)}
return &timedMutex{
write: make(chan struct{}, 1),
readers: make(chan int, 1),
}
}

func (m *timedMutex) tryLock(timeout time.Duration) bool {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
select {
case m.c <- struct{}{}:
case m.write <- struct{}{}:
return true
case <-ctx.Done():
}
return false
}

func (m *timedMutex) lock() {
m.c <- struct{}{}
m.write <- struct{}{}
}

func (m *timedMutex) unlock() {
<-m.c
<-m.write
}

func (m *timedMutex) rLock() {
var rs int
select {
case m.write <- struct{}{}:
case rs = <-m.readers:
}
rs++
m.readers <- rs
}

func (m *timedMutex) rUnlock() {
rs := <-m.readers
rs--
if rs == 0 {
<-m.write
return
}
m.readers <- rs
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ require (
github.com/go-chi/chi/v5 v5.0.7
github.com/go-redis/redis/v8 v8.11.4
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0
github.com/jrudio/go-plex-client v0.0.0-20220106065909-9e1d590b99aa
github.com/xanderstrike/plexhooks v0.0.0-20200926011736-c63bcd35fe3e
)
Expand All @@ -15,6 +14,7 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
)

replace github.com/jrudio/go-plex-client v0.0.0-20220106065909-9e1d590b99aa => github.com/RoyXiang/go-plex-client v0.0.0-20220310075632-2e318838193f
replace github.com/jrudio/go-plex-client v0.0.0-20220106065909-9e1d590b99aa => github.com/RoyXiang/go-plex-client v0.0.0-20220311080534-974eae4cd6e6
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/RoyXiang/go-plex-client v0.0.0-20220310075632-2e318838193f h1:vUNGSKhuDTgEqQOmx/mpzR00IPXw7pCbtWha3IlsQyA=
github.com/RoyXiang/go-plex-client v0.0.0-20220310075632-2e318838193f/go.mod h1:twidbPLE4eUk3CgDno5uCzpnPRboBTElH+iJrQO7S4w=
github.com/RoyXiang/go-plex-client v0.0.0-20220311080534-974eae4cd6e6 h1:qO7fjWR3T7kmiXMjSX3lKMROzNCEe00d6DeBFwV1rgw=
github.com/RoyXiang/go-plex-client v0.0.0-20220311080534-974eae4cd6e6/go.mod h1:twidbPLE4eUk3CgDno5uCzpnPRboBTElH+iJrQO7S4w=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
Expand Down
15 changes: 7 additions & 8 deletions handler/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,20 @@ const (
headerRealIP = "X-Real-IP"
headerVary = "Vary"

cachePrefixDynamic = "dynamic"
cachePrefixMetadata = "metadata"
cachePrefixStatic = "static"
cachePrefixPlex = "plex"
cachePrefixDynamic = "dynamic"
cachePrefixStatic = "static"
cachePrefixPlex = "plex"

cacheTtlDynamic = time.Second * 5
cacheTtlMetadata = time.Hour * 24
cacheTtlStatic = time.Hour
cacheTtlDynamic = time.Second * 5
cacheTtlStatic = time.Hour

contentTypeAny = "*/*"
contentTypeXml = "xml"

lockKeyFriends = "plex:friends"
lockKeySections = "plex:library:sections"
lockKeySessions = "plex:playback:sessions"
lockKeyToken = "plex:token"
lockKeyUsers = "plex:users"

watchedThreshold = 90

Expand Down
13 changes: 3 additions & 10 deletions handler/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"log"
"net/http"
"os"
"sync"

"github.com/go-chi/chi/v5/middleware"
"github.com/go-redis/redis/v8"
Expand All @@ -16,8 +15,6 @@ var (
redisClient *redis.Client

emptyStruct = struct{}{}

mu sync.RWMutex
)

func init() {
Expand All @@ -34,7 +31,7 @@ func init() {
}

redisUrl := os.Getenv("REDIS_URL")
if redisUrl != "" && plexClient.IsTokenSet() {
if redisUrl != "" {
options, err := redis.ParseURL(redisUrl)
if err == nil {
redisClient = redis.NewClient(options)
Expand All @@ -61,12 +58,8 @@ func NewRouter() http.Handler {
staticRouter.Path("/library/metadata/{key}/art/{id}").Handler(plexClient)
staticRouter.Path("/library/metadata/{key}/thumb/{id}").Handler(plexClient)
staticRouter.Path("/photo/:/transcode").Handler(plexClient)

metadataRouter := r.Methods(http.MethodGet).PathPrefix("/library").Subrouter()
metadataRouter.Use(metadataMiddleware)
metadataRouter.PathPrefix("/collections/").Handler(plexClient)
metadataRouter.PathPrefix("/metadata/").Handler(plexClient)
metadataRouter.PathPrefix("/sections/").Handler(plexClient)
staticRouter.PathPrefix("/web/js/").Handler(plexClient)
staticRouter.PathPrefix("/web/static/").Handler(plexClient)

dynamicRouter := r.Methods(http.MethodGet).Subrouter()
dynamicRouter.Use(dynamicMiddleware)
Expand Down
69 changes: 40 additions & 29 deletions handler/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import (
"net/http"
"net/http/httptest"
"net/url"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/RoyXiang/plexproxy/common"
)

var (
cacheInfoCtxKey = &ctxKeyType{"cacheInfo"}
tokenCtxKey = &ctxKeyType{"token"}
userCtxKey = &ctxKeyType{"user"}
)

Expand Down Expand Up @@ -76,9 +80,13 @@ func normalizeMiddleware(next http.Handler) http.Handler {
func wrapMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if token := r.Header.Get(headerToken); token != "" {
ctx := context.WithValue(r.Context(), tokenCtxKey, token)
if user := plexClient.GetUser(token); user != nil {
r = r.WithContext(context.WithValue(r.Context(), userCtxKey, user))
ctx = context.WithValue(ctx, userCtxKey, user)
} else {
common.GetLogger().Printf("Cannot get user info: %s", token)
}
r = r.WithContext(ctx)
}
next.ServeHTTP(wrapResponseWriter(w, r.ProtoMajor), r)
})
Expand Down Expand Up @@ -114,37 +122,42 @@ func staticMiddleware(next http.Handler) http.Handler {
})
}

func metadataMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), cacheInfoCtxKey, &cacheInfo{
Prefix: cachePrefixMetadata,
Ttl: cacheTtlMetadata,
})
r = r.WithContext(ctx)
cacheMiddleware(next).ServeHTTP(w, r)
})
}

func dynamicMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), cacheInfoCtxKey, &cacheInfo{
Prefix: cachePrefixDynamic,
Ttl: cacheTtlDynamic,
})
r = r.WithContext(ctx)
cacheMiddleware(next).ServeHTTP(w, r)
var ctx context.Context
switch filepath.Ext(r.URL.EscapedPath()) {
case ".css", ".ico", ".jpeg", ".jpg", ".webp":
ctx = context.WithValue(r.Context(), cacheInfoCtxKey, &cacheInfo{
Prefix: cachePrefixStatic,
Ttl: cacheTtlStatic,
})
case ".m3u8", ".ts":
ctx = r.Context()
default:
if rh := r.Header.Get(headerRange); rh != "" {
ctx = r.Context()
break
}
ctx = context.WithValue(r.Context(), cacheInfoCtxKey, &cacheInfo{
Prefix: cachePrefixDynamic,
Ttl: cacheTtlDynamic,
})
}
cacheMiddleware(next).ServeHTTP(w, r.WithContext(ctx))
})
}

func cacheMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctxValue := r.Context().Value(cacheInfoCtxKey)
if ctxValue == nil {
next.ServeHTTP(w, r)
return
}

var cacheKey string
ctx := context.Background()
info := r.Context().Value(cacheInfoCtxKey).(*cacheInfo)
if info.Prefix == cachePrefixMetadata {
mu.RLock()
defer mu.RUnlock()
}
info := ctxValue.(*cacheInfo)

defer func() {
if cacheKey == "" {
Expand Down Expand Up @@ -183,18 +196,16 @@ func cacheMiddleware(next http.Handler) http.Handler {
w.Header()[k] = v
}
}()
if isStreamRequest(r) {
return
}
params := r.URL.Query()
switch info.Prefix {
case cachePrefixStatic:
break
case cachePrefixDynamic, cachePrefixMetadata:
user := r.Context().Value(userCtxKey)
if user != nil {
case cachePrefixDynamic:
if user := r.Context().Value(userCtxKey); user != nil {
params.Set(headerUserId, strconv.Itoa(user.(*plexUser).Id))
params.Set(headerAccept, getAcceptContentType(r))
} else if token := r.Context().Value(tokenCtxKey); token != nil {
params.Set(headerToken, token.(string))
} else {
return
}
Expand Down
Loading

0 comments on commit 9d132cb

Please sign in to comment.