Skip to content

Commit

Permalink
feat: Add API route /v1/enclosures/{enclosureId}
Browse files Browse the repository at this point in the history
The GET method exposes individual enclosures by their ID.
The PUT method allows external clients to update media_progression, similiar to the internal API used by the UI.

The media proxy code was refactored to a method on the Enclosure and EnclosureList structures
to avoid duplicate code and risking diverging behavior between googlereader and the API.
  • Loading branch information
wolfhechel committed Aug 6, 2024
1 parent bcbf9f4 commit 5c4df28
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 29 deletions.
2 changes: 2 additions & 0 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool) {
sr.HandleFunc("/entries/{entryID}/fetch-content", handler.fetchContent).Methods(http.MethodGet)
sr.HandleFunc("/flush-history", handler.flushHistory).Methods(http.MethodPut, http.MethodDelete)
sr.HandleFunc("/icons/{iconID}", handler.getIconByIconID).Methods(http.MethodGet)
sr.HandleFunc("/enclosures/{enclosureID}", handler.getEnclosureById).Methods(http.MethodGet)
sr.HandleFunc("/enclosures/{enclosureID}", handler.updateEnclosureById).Methods(http.MethodPut)
sr.HandleFunc("/version", handler.versionHandler).Methods(http.MethodGet)
}

Expand Down
85 changes: 85 additions & 0 deletions internal/api/enclosure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package api // import "miniflux.app/v2/internal/api"

import (
json_parser "encoding/json"
"net/http"

"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/json"
"miniflux.app/v2/internal/model"
"miniflux.app/v2/internal/validator"
)

func (h *handler) getEnclosureById(w http.ResponseWriter, r *http.Request) {
enclosureID := request.RouteInt64Param(r, "enclosureID")

enclosure, err := h.store.GetEnclosure(enclosureID)

if err != nil {
json.ServerError(w, r, err)
return
}

if enclosure == nil {
json.NotFound(w, r)
return
}

userID := request.UserID(r)

if enclosure.UserID != userID {
json.NotFound(w, r)
return
}

enclosure.ProxifyEnclosureURL(h.router)

json.OK(w, r, enclosure)
}

func (h *handler) updateEnclosureById(w http.ResponseWriter, r *http.Request) {
enclosureID := request.RouteInt64Param(r, "enclosureID")

var enclosureUpdateRequest model.EnclosureUpdateRequest

if err := json_parser.NewDecoder(r.Body).Decode(&enclosureUpdateRequest); err != nil {
json.BadRequest(w, r, err)
return
}

if err := validator.ValidateEnclosureUpdateRequest(&enclosureUpdateRequest); err != nil {
json.BadRequest(w, r, err)
return
}

enclosure, err := h.store.GetEnclosure(enclosureID)

if err != nil {
json.BadRequest(w, r, err)
return
}

if enclosure == nil {
json.NotFound(w, r)
return
}

userID := request.UserID(r)

if enclosure.UserID != userID {
json.NotFound(w, r)
return
}

enclosure.MediaProgression = enclosureUpdateRequest.MediaProgression

if err := h.store.UpdateEnclosure(enclosure); err != nil {
json.ServerError(w, r, err)
return
}

json.NoContent(w, r)
}
17 changes: 2 additions & 15 deletions internal/api/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ import (
"errors"
"net/http"
"strconv"
"strings"
"time"

"miniflux.app/v2/internal/config"
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/json"
"miniflux.app/v2/internal/integration"
Expand All @@ -20,7 +18,6 @@ import (
"miniflux.app/v2/internal/reader/processor"
"miniflux.app/v2/internal/reader/readingtime"
"miniflux.app/v2/internal/storage"
"miniflux.app/v2/internal/urllib"
"miniflux.app/v2/internal/validator"
)

Expand All @@ -37,18 +34,8 @@ func (h *handler) getEntryFromBuilder(w http.ResponseWriter, r *http.Request, b
}

entry.Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, entry.Content)
proxyOption := config.Opts.MediaProxyMode()

for i := range entry.Enclosures {
if proxyOption == "all" || proxyOption != "none" && !urllib.IsHTTPS(entry.Enclosures[i].URL) {
for _, mediaType := range config.Opts.MediaProxyResourceTypes() {
if strings.HasPrefix(entry.Enclosures[i].MimeType, mediaType+"/") {
entry.Enclosures[i].URL = mediaproxy.ProxifyAbsoluteURL(h.router, entry.Enclosures[i].URL)
break
}
}
}
}

entry.Enclosures.ProxifyEnclosureURL(h.router)

json.OK(w, r, entry)
}
Expand Down
15 changes: 2 additions & 13 deletions internal/googlereader/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
mff "miniflux.app/v2/internal/reader/handler"
mfs "miniflux.app/v2/internal/reader/subscription"
"miniflux.app/v2/internal/storage"
"miniflux.app/v2/internal/urllib"
"miniflux.app/v2/internal/validator"

"github.com/gorilla/mux"
Expand Down Expand Up @@ -1004,18 +1003,8 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
}

entry.Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, entry.Content)
proxyOption := config.Opts.MediaProxyMode()

for i := range entry.Enclosures {
if proxyOption == "all" || proxyOption != "none" && !urllib.IsHTTPS(entry.Enclosures[i].URL) {
for _, mediaType := range config.Opts.MediaProxyResourceTypes() {
if strings.HasPrefix(entry.Enclosures[i].MimeType, mediaType+"/") {
entry.Enclosures[i].URL = mediaproxy.ProxifyAbsoluteURL(h.router, entry.Enclosures[i].URL)
break
}
}
}
}

entry.Enclosures.ProxifyEnclosureURL(h.router)

contentItems[i] = contentItem{
ID: fmt.Sprintf(EntryIDLong, entry.ID),
Expand Down
43 changes: 42 additions & 1 deletion internal/model/enclosure.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
// SPDX-License-Identifier: Apache-2.0

package model // import "miniflux.app/v2/internal/model"
import "strings"
import (
"strings"

"github.com/gorilla/mux"
"miniflux.app/v2/internal/config"
"miniflux.app/v2/internal/mediaproxy"
"miniflux.app/v2/internal/urllib"
)

// Enclosure represents an attachment.
type Enclosure struct {
Expand All @@ -15,6 +22,10 @@ type Enclosure struct {
MediaProgression int64 `json:"media_progression"`
}

type EnclosureUpdateRequest struct {
MediaProgression int64 `json:"media_progression"`
}

// Html5MimeType will modify the actual MimeType to allow direct playback from HTML5 player for some kind of MimeType
func (e Enclosure) Html5MimeType() string {
if e.MimeType == "video/m4v" {
Expand All @@ -34,3 +45,33 @@ func (el EnclosureList) ContainsAudioOrVideo() bool {
}
return false
}

func (el EnclosureList) ProxifyEnclosureURL(router *mux.Router) {
proxyOption := config.Opts.MediaProxyMode()

if proxyOption == "all" || proxyOption != "none" {
for i := range el {
if urllib.IsHTTPS(el[i].URL) {
for _, mediaType := range config.Opts.MediaProxyResourceTypes() {
if strings.HasPrefix(el[i].MimeType, mediaType+"/") {
el[i].URL = mediaproxy.ProxifyAbsoluteURL(router, el[i].URL)
break
}
}
}
}
}
}

func (e *Enclosure) ProxifyEnclosureURL(router *mux.Router) {
proxyOption := config.Opts.MediaProxyMode()

if proxyOption == "all" || proxyOption != "none" && !urllib.IsHTTPS(e.URL) {
for _, mediaType := range config.Opts.MediaProxyResourceTypes() {
if strings.HasPrefix(e.MimeType, mediaType+"/") {
e.URL = mediaproxy.ProxifyAbsoluteURL(router, e.URL)
break
}
}
}
}
18 changes: 18 additions & 0 deletions internal/validator/enclosure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package validator

import (
"fmt"

"miniflux.app/v2/internal/model"
)

func ValidateEnclosureUpdateRequest(request *model.EnclosureUpdateRequest) error {
if request.MediaProgression < 0 {
return fmt.Errorf(`media progression must an positive integer`)
}

return nil
}

0 comments on commit 5c4df28

Please sign in to comment.