Browse Source

Enable go-critic linter and fix various issues detected

Frédéric Guillot 2 years ago
parent
commit
b1e73fafdf
34 changed files with 126 additions and 109 deletions
  1. 2 2
      .github/workflows/linters.yml
  2. 4 4
      client/client.go
  3. 19 18
      internal/googlereader/handler.go
  4. 1 1
      internal/locale/translations/de_DE.json
  5. 1 1
      internal/locale/translations/el_EL.json
  6. 1 1
      internal/locale/translations/en_US.json
  7. 1 1
      internal/locale/translations/es_ES.json
  8. 1 1
      internal/locale/translations/fi_FI.json
  9. 1 1
      internal/locale/translations/fr_FR.json
  10. 1 1
      internal/locale/translations/hi_IN.json
  11. 1 1
      internal/locale/translations/id_ID.json
  12. 1 1
      internal/locale/translations/it_IT.json
  13. 1 1
      internal/locale/translations/ja_JP.json
  14. 1 1
      internal/locale/translations/nl_NL.json
  15. 1 1
      internal/locale/translations/pl_PL.json
  16. 1 1
      internal/locale/translations/pt_BR.json
  17. 1 1
      internal/locale/translations/ru_RU.json
  18. 1 1
      internal/locale/translations/tr_TR.json
  19. 1 1
      internal/locale/translations/uk_UA.json
  20. 1 1
      internal/locale/translations/zh_CN.json
  21. 1 1
      internal/locale/translations/zh_TW.json
  22. 2 10
      internal/model/enclosure.go
  23. 4 3
      internal/reader/atom/atom_10.go
  24. 39 13
      internal/reader/fetcher/response_handler.go
  25. 8 11
      internal/reader/json/adapter.go
  26. 0 2
      internal/reader/media/media.go
  27. 3 3
      internal/reader/processor/processor.go
  28. 5 4
      internal/reader/readability/readability.go
  29. 4 3
      internal/reader/rewrite/rewrite_functions.go
  30. 5 4
      internal/reader/sanitizer/sanitizer.go
  31. 1 2
      internal/reader/sanitizer/strip_tags.go
  32. 1 1
      internal/reader/xml/decoder.go
  33. 10 10
      internal/storage/category.go
  34. 1 1
      internal/ui/static_javascript.go

+ 2 - 2
.github/workflows/linters.yml

@@ -22,7 +22,7 @@ jobs:
       run: eslint internal/ui/static/js/*.js
 
   golangci:
-    name: Golang Linter
+    name: Golang Linters
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
@@ -32,7 +32,7 @@ jobs:
       - run: "go vet ./..."
       - uses: golangci/golangci-lint-action@v4
         with:
-          args: --timeout 10m --skip-dirs tests --disable errcheck --enable sqlclosecheck --enable misspell --enable gofmt --enable goimports --enable whitespace
+          args: --timeout 10m --skip-dirs tests --disable errcheck --enable sqlclosecheck --enable misspell --enable gofmt --enable goimports --enable whitespace --enable gocritic
       - uses: dominikh/staticcheck-action@v1.3.0
         with:
           version: "2023.1.7"

+ 4 - 4
client/client.go

@@ -28,12 +28,12 @@ func NewClient(endpoint string, credentials ...string) *Client {
 	// Trim trailing slashes and /v1 from the endpoint.
 	endpoint = strings.TrimSuffix(endpoint, "/")
 	endpoint = strings.TrimSuffix(endpoint, "/v1")
-
-	if len(credentials) == 2 {
+	switch len(credentials) {
+	case 2:
 		return &Client{request: &request{endpoint: endpoint, username: credentials[0], password: credentials[1]}}
-	} else if len(credentials) == 1 {
+	case 1:
 		return &Client{request: &request{endpoint: endpoint, apiKey: credentials[0]}}
-	} else {
+	default:
 		return &Client{request: &request{endpoint: endpoint}}
 	}
 }

+ 19 - 18
internal/googlereader/handler.go

@@ -265,9 +265,10 @@ func getStreamFilterModifiers(r *http.Request) (RequestModifiers, error) {
 }
 
 func getStream(streamID string, userID int64) (Stream, error) {
-	if strings.HasPrefix(streamID, FeedPrefix) {
+	switch {
+	case strings.HasPrefix(streamID, FeedPrefix):
 		return Stream{Type: FeedStream, ID: strings.TrimPrefix(streamID, FeedPrefix)}, nil
-	} else if strings.HasPrefix(streamID, fmt.Sprintf(UserStreamPrefix, userID)) || strings.HasPrefix(streamID, StreamPrefix) {
+	case strings.HasPrefix(streamID, fmt.Sprintf(UserStreamPrefix, userID)) || strings.HasPrefix(streamID, StreamPrefix):
 		id := strings.TrimPrefix(streamID, fmt.Sprintf(UserStreamPrefix, userID))
 		id = strings.TrimPrefix(id, StreamPrefix)
 		switch id {
@@ -288,15 +289,15 @@ func getStream(streamID string, userID int64) (Stream, error) {
 		default:
 			return Stream{NoStream, ""}, fmt.Errorf("googlereader: unknown stream with id: %s", id)
 		}
-	} else if strings.HasPrefix(streamID, fmt.Sprintf(UserLabelPrefix, userID)) || strings.HasPrefix(streamID, LabelPrefix) {
+	case strings.HasPrefix(streamID, fmt.Sprintf(UserLabelPrefix, userID)) || strings.HasPrefix(streamID, LabelPrefix):
 		id := strings.TrimPrefix(streamID, fmt.Sprintf(UserLabelPrefix, userID))
 		id = strings.TrimPrefix(id, LabelPrefix)
 		return Stream{LabelStream, id}, nil
-	} else if streamID == "" {
+	case streamID == "":
 		return Stream{NoStream, ""}, nil
+	default:
+		return Stream{NoStream, ""}, fmt.Errorf("googlereader: unknown stream type: %s", streamID)
 	}
-
-	return Stream{NoStream, ""}, fmt.Errorf("googlereader: unknown stream type: %s", streamID)
 }
 
 func getStreams(streamIDs []string, userID int64) ([]Stream, error) {
@@ -382,7 +383,7 @@ func getItemIDs(r *http.Request) ([]int64, error) {
 	return itemIDs, nil
 }
 
-func checkOutputFormat(w http.ResponseWriter, r *http.Request) error {
+func checkOutputFormat(r *http.Request) error {
 	var output string
 	if r.Method == http.MethodPost {
 		err := r.ParseForm()
@@ -736,11 +737,12 @@ func getFeed(stream Stream, store *storage.Storage, userID int64) (*model.Feed,
 }
 
 func getOrCreateCategory(category Stream, store *storage.Storage, userID int64) (*model.Category, error) {
-	if category.ID == "" {
+	switch {
+	case category.ID == "":
 		return store.FirstCategory(userID)
-	} else if store.CategoryTitleExists(userID, category.ID) {
+	case store.CategoryTitleExists(userID, category.ID):
 		return store.CategoryByTitle(userID, category.ID)
-	} else {
+	default:
 		catRequest := model.CategoryRequest{
 			Title: category.ID,
 		}
@@ -908,7 +910,7 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
 		slog.Int64("user_id", userID),
 	)
 
-	if err := checkOutputFormat(w, r); err != nil {
+	if err := checkOutputFormat(r); err != nil {
 		json.BadRequest(w, r, err)
 		return
 	}
@@ -1170,7 +1172,7 @@ func (h *handler) tagListHandler(w http.ResponseWriter, r *http.Request) {
 		slog.String("user_agent", r.UserAgent()),
 	)
 
-	if err := checkOutputFormat(w, r); err != nil {
+	if err := checkOutputFormat(r); err != nil {
 		json.BadRequest(w, r, err)
 		return
 	}
@@ -1205,7 +1207,7 @@ func (h *handler) subscriptionListHandler(w http.ResponseWriter, r *http.Request
 		slog.String("user_agent", r.UserAgent()),
 	)
 
-	if err := checkOutputFormat(w, r); err != nil {
+	if err := checkOutputFormat(r); err != nil {
 		json.BadRequest(w, r, err)
 		return
 	}
@@ -1224,7 +1226,7 @@ func (h *handler) subscriptionListHandler(w http.ResponseWriter, r *http.Request
 			URL:        feed.FeedURL,
 			Categories: []subscriptionCategory{{fmt.Sprintf(UserLabelPrefix, userID) + feed.Category.Title, feed.Category.Title, "folder"}},
 			HTMLURL:    feed.SiteURL,
-			IconURL:    "", //TODO Icons are only base64 encode in DB yet
+			IconURL:    "", // TODO: Icons are base64 encoded in the DB.
 		})
 	}
 	json.OK(w, r, result)
@@ -1251,7 +1253,7 @@ func (h *handler) userInfoHandler(w http.ResponseWriter, r *http.Request) {
 		slog.String("user_agent", r.UserAgent()),
 	)
 
-	if err := checkOutputFormat(w, r); err != nil {
+	if err := checkOutputFormat(r); err != nil {
 		json.BadRequest(w, r, err)
 		return
 	}
@@ -1276,7 +1278,7 @@ func (h *handler) streamItemIDsHandler(w http.ResponseWriter, r *http.Request) {
 		slog.Int64("user_id", userID),
 	)
 
-	if err := checkOutputFormat(w, r); err != nil {
+	if err := checkOutputFormat(r); err != nil {
 		json.BadRequest(w, r, err)
 		return
 	}
@@ -1477,8 +1479,7 @@ func (h *handler) handleFeedStreamHandler(w http.ResponseWriter, r *http.Request
 
 	if len(rm.ExcludeTargets) > 0 {
 		for _, s := range rm.ExcludeTargets {
-			switch s.Type {
-			case ReadStream:
+			if s.Type == ReadStream {
 				builder.WithoutStatus(model.EntryStatusRead)
 			}
 		}

+ 1 - 1
internal/locale/translations/de_DE.json

@@ -505,7 +505,7 @@
     "error.http_body_read": "Der HTTP-Inhalt kann nicht gelesen werden: %v",
     "error.http_empty_response_body": "Der Inhalt der HTTP-Antwort ist leer.",
     "error.http_empty_response": "Die HTTP-Antwort ist leer. Vielleicht versucht die Webseite, sich vor Bots zu schützen?",
-    "error.tls_error": "TLS-Fehler: %v. Wenn Sie mögen, können Sie versuchen die TLS-Verifizierung in den Einstellungen des Abonnements zu deaktivieren.",
+    "error.tls_error": "TLS-Fehler: %q. Wenn Sie mögen, können Sie versuchen die TLS-Verifizierung in den Einstellungen des Abonnements zu deaktivieren.",
     "error.network_operation": "Miniflux kann die Webseite aufgrund eines Netzwerk-Fehlers nicht erreichen: %v",
     "error.network_timeout": "Die Webseite ist zu langsam und die Anfrage ist abgelaufen: %v.",
     "error.http_client_error": "HTTP-Client-Fehler: %v.",

+ 1 - 1
internal/locale/translations/el_EL.json

@@ -505,7 +505,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/en_US.json

@@ -505,7 +505,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/es_ES.json

@@ -505,7 +505,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/fi_FI.json

@@ -505,7 +505,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/fr_FR.json

@@ -505,7 +505,7 @@
     "error.http_body_read": "Impossible de lire le corps de la réponse HTTP : %v.",
     "error.http_empty_response_body": "Le corps de la réponse HTTP est vide.",
     "error.http_empty_response": "La réponse HTTP est vide. Peut-être que ce site web bloque Miniflux avec une protection anti-bot ?",
-    "error.tls_error": "Erreur TLS : %v. Vous pouvez désactiver la vérification TLS dans les paramètres de l'abonnement.",
+    "error.tls_error": "Erreur TLS : %q. Vous pouvez désactiver la vérification TLS dans les paramètres de l'abonnement.",
     "error.network_operation": "Miniflux n'est pas en mesure de se connecter à ce site web à cause d'un problème réseau : %v.",
     "error.network_timeout": "Ce site web est trop lent à répondre : %v.",
     "error.http_client_error": "Erreur du client HTTP : %v.",

+ 1 - 1
internal/locale/translations/hi_IN.json

@@ -505,7 +505,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/id_ID.json

@@ -488,7 +488,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/it_IT.json

@@ -505,7 +505,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/ja_JP.json

@@ -488,7 +488,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/nl_NL.json

@@ -505,7 +505,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/pl_PL.json

@@ -522,7 +522,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/pt_BR.json

@@ -505,7 +505,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/ru_RU.json

@@ -522,7 +522,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/tr_TR.json

@@ -505,7 +505,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/uk_UA.json

@@ -522,7 +522,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/zh_CN.json

@@ -488,7 +488,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 1 - 1
internal/locale/translations/zh_TW.json

@@ -488,7 +488,7 @@
     "error.http_body_read": "Unable to read the HTTP body: %v.",
     "error.http_empty_response_body": "The HTTP response body is empty.",
     "error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
-    "error.tls_error": "TLS error: %v. You could disable TLS verification in the feed settings if you would like.",
+    "error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",
     "error.http_client_error": "HTTP client error: %v.",

+ 2 - 10
internal/model/enclosure.go

@@ -2,7 +2,6 @@
 // SPDX-License-Identifier: Apache-2.0
 
 package model // import "miniflux.app/v2/internal/model"
-import "strings"
 
 // Enclosure represents an attachment.
 type Enclosure struct {
@@ -17,15 +16,8 @@ type Enclosure struct {
 
 // Html5MimeType will modify the actual MimeType to allow direct playback from HTML5 player for some kind of MimeType
 func (e Enclosure) Html5MimeType() string {
-	if strings.HasPrefix(e.MimeType, "video") {
-		switch e.MimeType {
-		// Solution from this stackoverflow discussion:
-		// https://stackoverflow.com/questions/15277147/m4v-mimetype-video-mp4-or-video-m4v/66945470#66945470
-		// tested at the time of this commit (06/2023) on latest Firefox & Vivaldi on this feed
-		// https://www.florenceporcel.com/podcast/lfhdu.xml
-		case "video/m4v":
-			return "video/x-m4v"
-		}
+	if e.MimeType == "video/m4v" {
+		return "video/x-m4v"
 	}
 	return e.MimeType
 }

+ 4 - 3
internal/reader/atom/atom_10.go

@@ -179,11 +179,12 @@ func (a *Atom10Text) Body() string {
 func (a *Atom10Text) Title() string {
 	var content string
 
-	if strings.EqualFold(a.Type, "xhtml") {
+	switch {
+	case strings.EqualFold(a.Type, "xhtml"):
 		content = a.xhtmlContent()
-	} else if strings.Contains(a.InnerXML, "<![CDATA[") {
+	case strings.Contains(a.InnerXML, "<![CDATA["):
 		content = html.UnescapeString(a.CharData)
-	} else {
+	default:
 		content = a.CharData
 	}
 

+ 39 - 13
internal/reader/fetcher/response_handler.go

@@ -10,6 +10,8 @@ import (
 	"io"
 	"net"
 	"net/http"
+	"net/url"
+	"os"
 
 	"miniflux.app/v2/internal/locale"
 )
@@ -94,23 +96,18 @@ func (r *ResponseHandler) ReadBody(maxBodySize int64) ([]byte, *locale.Localized
 
 func (r *ResponseHandler) LocalizedError() *locale.LocalizedErrorWrapper {
 	if r.clientErr != nil {
-		switch r.clientErr.(type) {
-		case x509.CertificateInvalidError, x509.HostnameError:
+		switch {
+		case isSSLError(r.clientErr):
 			return locale.NewLocalizedErrorWrapper(fmt.Errorf("fetcher: %w", r.clientErr), "error.tls_error", r.clientErr)
-		case *net.OpError:
+		case isNetworkError(r.clientErr):
 			return locale.NewLocalizedErrorWrapper(fmt.Errorf("fetcher: %w", r.clientErr), "error.network_operation", r.clientErr)
-		case net.Error:
-			networkErr := r.clientErr.(net.Error)
-			if networkErr.Timeout() {
-				return locale.NewLocalizedErrorWrapper(fmt.Errorf("fetcher: %w", r.clientErr), "error.network_timeout", r.clientErr)
-			}
-		}
-
-		if errors.Is(r.clientErr, io.EOF) {
+		case os.IsTimeout(r.clientErr):
+			return locale.NewLocalizedErrorWrapper(fmt.Errorf("fetcher: %w", r.clientErr), "error.network_timeout", r.clientErr)
+		case errors.Is(r.clientErr, io.EOF):
 			return locale.NewLocalizedErrorWrapper(fmt.Errorf("fetcher: %w", r.clientErr), "error.http_empty_response")
+		default:
+			return locale.NewLocalizedErrorWrapper(fmt.Errorf("fetcher: %w", r.clientErr), "error.http_client_error", r.clientErr)
 		}
-
-		return locale.NewLocalizedErrorWrapper(fmt.Errorf("fetcher: %w", r.clientErr), "error.http_client_error", r.clientErr)
 	}
 
 	switch r.httpResponse.StatusCode {
@@ -145,3 +142,32 @@ func (r *ResponseHandler) LocalizedError() *locale.LocalizedErrorWrapper {
 
 	return nil
 }
+
+func isNetworkError(err error) bool {
+	if _, ok := err.(*url.Error); ok {
+		return true
+	}
+	if err == io.EOF {
+		return true
+	}
+	var opErr *net.OpError
+	if ok := errors.As(err, &opErr); ok {
+		return true
+	}
+	return false
+}
+
+func isSSLError(err error) bool {
+	var certErr x509.UnknownAuthorityError
+	if errors.As(err, &certErr) {
+		return true
+	}
+
+	var hostErr x509.HostnameError
+	if errors.As(err, &hostErr) {
+		return true
+	}
+
+	var algErr x509.InsecureAlgorithmError
+	return errors.As(err, &algErr)
+}

+ 8 - 11
internal/reader/json/adapter.go

@@ -5,7 +5,7 @@ package json // import "miniflux.app/v2/internal/reader/json"
 
 import (
 	"log/slog"
-	"sort"
+	"slices"
 	"strings"
 	"time"
 
@@ -118,24 +118,21 @@ func (j *JSONAdapter) BuildFeed(feedURL string) *model.Feed {
 		}
 
 		// Populate the entry author.
-		itemAuthors := append(item.Authors, j.jsonFeed.Authors...)
+		itemAuthors := j.jsonFeed.Authors
+		itemAuthors = append(itemAuthors, item.Authors...)
 		itemAuthors = append(itemAuthors, item.Author, j.jsonFeed.Author)
 
-		authorNamesMap := make(map[string]bool)
+		var authorNames []string
 		for _, author := range itemAuthors {
 			authorName := strings.TrimSpace(author.Name)
 			if authorName != "" {
-				authorNamesMap[authorName] = true
+				authorNames = append(authorNames, authorName)
 			}
 		}
 
-		var authors []string
-		for authorName := range authorNamesMap {
-			authors = append(authors, authorName)
-		}
-
-		sort.Strings(authors)
-		entry.Author = strings.Join(authors, ", ")
+		slices.Sort(authorNames)
+		authorNames = slices.Compact(authorNames)
+		entry.Author = strings.Join(authorNames, ", ")
 
 		// Populate the entry enclosures.
 		for _, attachment := range item.Attachments {

+ 0 - 2
internal/reader/media/media.go

@@ -93,8 +93,6 @@ func (mc *Content) MimeType() string {
 		return "video/*"
 	case mc.Type == "" && mc.Medium == "audio":
 		return "audio/*"
-	case mc.Type == "" && mc.Medium == "video":
-		return "video/*"
 	case mc.Type != "":
 		return mc.Type
 	default:

+ 3 - 3
internal/reader/processor/processor.go

@@ -401,11 +401,11 @@ func parseISO8601(from string) (time.Duration, error) {
 
 		switch name {
 		case "hour":
-			d = d + (time.Duration(val) * time.Hour)
+			d += (time.Duration(val) * time.Hour)
 		case "minute":
-			d = d + (time.Duration(val) * time.Minute)
+			d += (time.Duration(val) * time.Minute)
 		case "second":
-			d = d + (time.Duration(val) * time.Second)
+			d += (time.Duration(val) * time.Second)
 		default:
 			return 0, fmt.Errorf("unknown field %s", name)
 		}

+ 5 - 4
internal/reader/readability/readability.go

@@ -45,11 +45,12 @@ func (c *candidate) String() string {
 	id, _ := c.selection.Attr("id")
 	class, _ := c.selection.Attr("class")
 
-	if id != "" && class != "" {
+	switch {
+	case id != "" && class != "":
 		return fmt.Sprintf("%s#%s.%s => %f", c.Node().DataAtom, id, class, c.score)
-	} else if id != "" {
+	case id != "":
 		return fmt.Sprintf("%s#%s => %f", c.Node().DataAtom, id, c.score)
-	} else if class != "" {
+	case class != "":
 		return fmt.Sprintf("%s.%s => %f", c.Node().DataAtom, class, c.score)
 	}
 
@@ -222,7 +223,7 @@ func getCandidates(document *goquery.Document) candidateList {
 	// should have a relatively small link density (5% or less) and be mostly
 	// unaffected by this operation
 	for _, candidate := range candidates {
-		candidate.score = candidate.score * (1 - getLinkDensity(candidate.selection))
+		candidate.score *= (1 - getLinkDensity(candidate.selection))
 	}
 
 	return candidates

+ 4 - 3
internal/reader/rewrite/rewrite_functions.go

@@ -376,7 +376,8 @@ func addHackerNewsLinksUsing(entryContent, app string) string {
 				return
 			}
 
-			if app == "opener" {
+			switch app {
+			case "opener":
 				params := url.Values{}
 				params.Add("url", hn_uri.String())
 
@@ -389,12 +390,12 @@ func addHackerNewsLinksUsing(entryContent, app string) string {
 
 				open_with_opener := `<a href="` + url.String() + `">Open with Opener</a>`
 				a.Parent().AppendHtml(" " + open_with_opener)
-			} else if app == "hack" {
+			case "hack":
 				url := strings.Replace(hn_uri.String(), hn_prefix, "hack://", 1)
 
 				open_with_hack := `<a href="` + url + `">Open with HACK</a>`
 				a.Parent().AppendHtml(" " + open_with_hack)
-			} else {
+			default:
 				slog.Warn("Unknown app provided for openHackerNewsLinksWith rewrite rule",
 					slog.String("app", app),
 				)

+ 5 - 4
internal/reader/sanitizer/sanitizer.go

@@ -190,17 +190,18 @@ func sanitizeAttributes(baseURL, tagName string, attributes []html.Attribute) ([
 		}
 
 		if isExternalResourceAttribute(attribute.Key) {
-			if tagName == "iframe" {
+			switch {
+			case tagName == "iframe":
 				if !isValidIframeSource(baseURL, attribute.Val) {
 					continue
 				}
 				value = rewriteIframeURL(attribute.Val)
-			} else if tagName == "img" && attribute.Key == "src" && isValidDataAttribute(attribute.Val) {
+			case tagName == "img" && attribute.Key == "src" && isValidDataAttribute(attribute.Val):
 				value = attribute.Val
-			} else if isAnchor("a", attribute) {
+			case isAnchor("a", attribute):
 				value = attribute.Val
 				isAnchorLink = true
-			} else {
+			default:
 				value, err = urllib.AbsoluteURL(baseURL, value)
 				if err != nil {
 					continue

+ 1 - 2
internal/reader/sanitizer/strip_tags.go

@@ -27,8 +27,7 @@ func StripTags(input string) string {
 		}
 
 		token := tokenizer.Token()
-		switch token.Type {
-		case html.TextToken:
+		if token.Type == html.TextToken {
 			buffer.WriteString(token.Data)
 		}
 	}

+ 1 - 1
internal/reader/xml/decoder.go

@@ -66,7 +66,7 @@ func filterValidXMLChar(r rune) rune {
 func procInst(param, s string) string {
 	// TODO: this parsing is somewhat lame and not exact.
 	// It works for all actual cases, though.
-	param = param + "="
+	param += "="
 	idx := strings.Index(s, param)
 	if idx == -1 {
 		return ""

+ 10 - 10
internal/storage/category.go

@@ -133,12 +133,12 @@ func (s *Storage) CategoriesWithFeedCount(userID int64) (model.Categories, error
 	`
 
 	if user.CategoriesSortingOrder == "alphabetical" {
-		query = query + `
+		query += `
 			ORDER BY
 				c.title ASC
 		`
 	} else {
-		query = query + `
+		query += `
 			ORDER BY
 				count_unread DESC,
 				c.title ASC
@@ -255,14 +255,14 @@ func (s *Storage) RemoveAndReplaceCategoriesByName(userid int64, titles []string
 	}
 
 	query = `
-		WITH d_cats AS (SELECT id FROM categories WHERE user_id = $1 AND title = ANY($2)) 
-		UPDATE feeds 
-		 SET category_id = 
-		  (SELECT id 
-			FROM categories 
-			WHERE user_id = $1 AND id NOT IN (SELECT id FROM d_cats) 
-			ORDER BY title ASC 
-			LIMIT 1) 
+		WITH d_cats AS (SELECT id FROM categories WHERE user_id = $1 AND title = ANY($2))
+		UPDATE feeds
+		 SET category_id =
+		  (SELECT id
+			FROM categories
+			WHERE user_id = $1 AND id NOT IN (SELECT id FROM d_cats)
+			ORDER BY title ASC
+			LIMIT 1)
 		WHERE user_id = $1 AND category_id IN (SELECT id FROM d_cats)
 	`
 	_, err = tx.Exec(query, userid, titleParam)

+ 1 - 1
internal/ui/static_javascript.go

@@ -28,7 +28,7 @@ func (h *handler) showJavascript(w http.ResponseWriter, r *http.Request) {
 
 		if filename == "service-worker" {
 			variables := fmt.Sprintf(`const OFFLINE_URL=%q;`, route.Path(h.router, "offline"))
-			contents = append([]byte(variables)[:], contents[:]...)
+			contents = append([]byte(variables), contents...)
 		}
 
 		b.WithHeader("Content-Type", "text/javascript; charset=utf-8")