Repository: miniflux/v2

Issue:
Maybe we should add the feed's subtitle to the feed information
Typically this section holds the introductory information about the site, which gives you an idea of what the general content of the site is.

Discussion:
@xfzv: What do you mean by "feed's subtitle?"

Feed title + site URL + category name already give enough information in my opinion.@rebron1900: I mean the rss profile field, for example in my site the subtitile is the introductory info for my subscription

```xml
<feed xmlns="http://www.w3.org/2005/Atom">
<title>只是玩玩</title>
<subtitle>All work and no play makes Jack a dull boy</subtitle>
<link href="https://1900.live" rel="self"/>
<link href="https://1900.live"/>
<updated>2024-04-09T00:46:07.000+08:00</updated>
<id>https://1900.live/</id>
</feed>
```@xfzv: I see. I'm no expert but it seems that not all feeds provide a `<subtitle>` tag though. GitHub / Invidious feeds don't have one for instance.@rebron1900: > I see. I'm no expert but it seems that not all feeds provide a `<subtitle>` tag though. GitHub / Invidious feeds don't have one for instance.

Thanks for the heads up, I went and checked my rss subscription list and it's true that I don't use `subtitle` as an intro field, almost all my subscriptions use the `description` field.

But if it's a description field, it would also like to be displayed in miniflux

Solution patch:
diff --git a/internal/database/migrations.go b/internal/database/migrations.go
index df2a5dc7..e48217ad 100644
--- a/internal/database/migrations.go
+++ b/internal/database/migrations.go
@@ -898,4 +898,9 @@ var migrations = []func(tx *sql.Tx) error{
 		_, err = tx.Exec(sql)
 		return err
 	},
+	func(tx *sql.Tx) (err error) {
+		sql := `ALTER TABLE feeds ADD COLUMN description text default ''`
+		_, err = tx.Exec(sql)
+		return err
+	},
 }
diff --git a/internal/locale/translations/de_DE.json b/internal/locale/translations/de_DE.json
index 0f43cfb3..fa3d3a68 100644
--- a/internal/locale/translations/de_DE.json
+++ b/internal/locale/translations/de_DE.json
@@ -317,6 +317,7 @@
     "form.feed.label.title": "Titel",
     "form.feed.label.site_url": "URL der Webseite",
     "form.feed.label.feed_url": "URL des Abonnements",
+    "form.feed.label.description": "Beschreibung",
     "form.feed.label.category": "Kategorie",
     "form.feed.label.crawler": "Originalinhalt herunterladen",
     "form.feed.label.feed_username": "Benutzername des Abonnements",
diff --git a/internal/locale/translations/el_EL.json b/internal/locale/translations/el_EL.json
index 34e80d0b..28169261 100644
--- a/internal/locale/translations/el_EL.json
+++ b/internal/locale/translations/el_EL.json
@@ -319,6 +319,7 @@
     "form.feed.label.title": "Τίτλος",
     "form.feed.label.site_url": "Διεύθυνση URL ιστότοπου",
     "form.feed.label.feed_url": "Διεύθυνση URL ροής",
+    "form.feed.label.description": "Περιγραφή",
     "form.feed.label.category": "Κατηγορία",
     "form.feed.label.crawler": "Λήψη αρχικού περιεχομένου",
     "form.feed.label.feed_username": "Όνομα Χρήστη ροής",
diff --git a/internal/locale/translations/en_US.json b/internal/locale/translations/en_US.json
index ab47eb75..71867ffe 100644
--- a/internal/locale/translations/en_US.json
+++ b/internal/locale/translations/en_US.json
@@ -317,6 +317,7 @@
     "form.feed.label.title": "Title",
     "form.feed.label.site_url": "Site URL",
     "form.feed.label.feed_url": "Feed URL",
+    "form.feed.label.description": "Description",
     "form.feed.label.category": "Category",
     "form.feed.label.crawler": "Fetch original content",
     "form.feed.label.feed_username": "Feed Username",
diff --git a/internal/locale/translations/es_ES.json b/internal/locale/translations/es_ES.json
index 499e3eb3..0c7766e8 100644
--- a/internal/locale/translations/es_ES.json
+++ b/internal/locale/translations/es_ES.json
@@ -317,6 +317,7 @@
     "form.feed.label.title": "Título",
     "form.feed.label.site_url": "URL del sitio",
     "form.feed.label.feed_url": "URL de la fuente",
+    "form.feed.label.description": "Descripción",
     "form.feed.label.category": "Categoría",
     "form.feed.label.crawler": "Obtener rastreador original",
     "form.feed.label.feed_username": "Nombre de usuario de la fuente",
diff --git a/internal/locale/translations/fi_FI.json b/internal/locale/translations/fi_FI.json
index 7028ad1c..7b6565fc 100644
--- a/internal/locale/translations/fi_FI.json
+++ b/internal/locale/translations/fi_FI.json
@@ -319,6 +319,7 @@
     "form.feed.label.title": "Otsikko",
     "form.feed.label.site_url": "Sivuston URL-osoite",
     "form.feed.label.feed_url": "Syötteen URL-osoite",
+    "form.feed.label.description": "Kuvaus",
     "form.feed.label.category": "Kategoria",
     "form.feed.label.crawler": "Nouda alkuperäinen sisältö",
     "form.feed.label.feed_username": "Syötteen käyttäjätunnus",
diff --git a/internal/locale/translations/fr_FR.json b/internal/locale/translations/fr_FR.json
index b73a8d95..b25033b4 100644
--- a/internal/locale/translations/fr_FR.json
+++ b/internal/locale/translations/fr_FR.json
@@ -317,6 +317,7 @@
     "form.feed.label.title": "Titre",
     "form.feed.label.site_url": "URL du site web",
     "form.feed.label.feed_url": "URL du flux",
+    "form.feed.label.description": "Description",
     "form.feed.label.category": "Catégorie",
     "form.feed.label.crawler": "Récupérer le contenu original",
     "form.feed.label.feed_username": "Nom d'utilisateur du flux",
diff --git a/internal/locale/translations/hi_IN.json b/internal/locale/translations/hi_IN.json
index 92922079..2ebe379a 100644
--- a/internal/locale/translations/hi_IN.json
+++ b/internal/locale/translations/hi_IN.json
@@ -317,6 +317,7 @@
     "form.feed.label.title": "शीर्षक",
     "form.feed.label.site_url": "साइट यूआरएल",
     "form.feed.label.feed_url": "फ़ीड यूआरएल",
+    "form.feed.label.description": "विवरण",
     "form.feed.label.category": "श्रेणी",
     "form.feed.label.crawler": "मूल सामग्री प्राप्त करें",
     "form.feed.label.feed_username": "फ़ीड उपयोगकर्ता नाम",
diff --git a/internal/locale/translations/id_ID.json b/internal/locale/translations/id_ID.json
index 16e66ff1..06ecd7db 100644
--- a/internal/locale/translations/id_ID.json
+++ b/internal/locale/translations/id_ID.json
@@ -307,6 +307,7 @@
     "form.feed.label.title": "Judul",
     "form.feed.label.site_url": "URL Situs",
     "form.feed.label.feed_url": "URL Umpan",
+    "form.feed.label.description": "Deskripsi",
     "form.feed.label.category": "Kategori",
     "form.feed.label.crawler": "Ambil konten asli",
     "form.feed.label.feed_username": "Nama Pengguna Umpan",
diff --git a/internal/locale/translations/it_IT.json b/internal/locale/translations/it_IT.json
index c9edd6f2..fcb5daae 100644
--- a/internal/locale/translations/it_IT.json
+++ b/internal/locale/translations/it_IT.json
@@ -317,6 +317,7 @@
     "form.feed.label.title": "Titolo",
     "form.feed.label.site_url": "URL del sito",
     "form.feed.label.feed_url": "URL del feed",
+    "form.feed.label.description": "Descrizione",
     "form.feed.label.category": "Categoria",
     "form.feed.label.crawler": "Scarica il contenuto integrale",
     "form.feed.label.feed_username": "Nome utente del feed",
diff --git a/internal/locale/translations/ja_JP.json b/internal/locale/translations/ja_JP.json
index 0beb0759..f6f3d72c 100644
--- a/internal/locale/translations/ja_JP.json
+++ b/internal/locale/translations/ja_JP.json
@@ -307,6 +307,7 @@
     "form.feed.label.title": "タイトル",
     "form.feed.label.site_url": "サイト URL",
     "form.feed.label.feed_url": "フィード URL",
+    "form.feed.label.description": "説明",
     "form.feed.label.category": "カテゴリ",
     "form.feed.label.crawler": "オリジナルの内容を取得",
     "form.feed.label.feed_username": "フィードのユーザー名",
diff --git a/internal/locale/translations/nl_NL.json b/internal/locale/translations/nl_NL.json
index 3f17f631..6b73abbc 100644
--- a/internal/locale/translations/nl_NL.json
+++ b/internal/locale/translations/nl_NL.json
@@ -317,6 +317,7 @@
     "form.feed.label.title": "Naam",
     "form.feed.label.site_url": "Website URL",
     "form.feed.label.feed_url": "Feed URL",
+    "form.feed.label.description": "Beschrijving",
     "form.feed.label.category": "Categorie",
     "form.feed.label.crawler": "Download originele content",
     "form.feed.label.feed_username": "Feed-gebruikersnaam",
diff --git a/internal/locale/translations/pl_PL.json b/internal/locale/translations/pl_PL.json
index ddaf0d8b..0044d021 100644
--- a/internal/locale/translations/pl_PL.json
+++ b/internal/locale/translations/pl_PL.json
@@ -327,6 +327,7 @@
     "form.feed.label.title": "Tytuł",
     "form.feed.label.site_url": "URL strony",
     "form.feed.label.feed_url": "URL kanału",
+    "form.feed.label.description": "Opis",
     "form.feed.label.category": "Kategoria",
     "form.feed.label.crawler": "Pobierz oryginalną treść",
     "form.feed.label.feed_username": "Subskrypcję nazwa użytkownika",
diff --git a/internal/locale/translations/pt_BR.json b/internal/locale/translations/pt_BR.json
index 8502b778..1dd3f195 100644
--- a/internal/locale/translations/pt_BR.json
+++ b/internal/locale/translations/pt_BR.json
@@ -317,6 +317,7 @@
     "form.feed.label.title": "Título",
     "form.feed.label.site_url": "URL do site",
     "form.feed.label.feed_url": "URL da fonte",
+    "form.feed.label.description": "Descrição",
     "form.feed.label.category": "Categoria",
     "form.feed.label.crawler": "Obter conteúdo original",
     "form.feed.label.feed_username": "Nome de usuário da fonte",
diff --git a/internal/locale/translations/ru_RU.json b/internal/locale/translations/ru_RU.json
index 2d17fae9..d759621b 100644
--- a/internal/locale/translations/ru_RU.json
+++ b/internal/locale/translations/ru_RU.json
@@ -327,6 +327,7 @@
     "form.feed.label.title": "Название",
     "form.feed.label.site_url": "Адрес сайта",
     "form.feed.label.feed_url": "Адрес подписки",
+    "form.feed.label.description": "Описание",
     "form.feed.label.category": "Категория",
     "form.feed.label.crawler": "Извлечь оригинальное содержимое",
     "form.feed.label.feed_username": "Имя пользователя подписки",
diff --git a/internal/locale/translations/tr_TR.json b/internal/locale/translations/tr_TR.json
index 3edd074b..fbaa3efb 100644
--- a/internal/locale/translations/tr_TR.json
+++ b/internal/locale/translations/tr_TR.json
@@ -154,6 +154,7 @@
   "form.feed.label.disabled": "Bu beslemeyi yenileme",
   "form.feed.label.feed_password": "Besleme Parolası",
   "form.feed.label.feed_url": "Besleme URL'si",
+  "form.feed.label.description": "Açıklama",
   "form.feed.label.feed_username": "Besleme Kullanıcı Adı",
   "form.feed.label.fetch_via_proxy": "Proxy ile çek",
   "form.feed.label.hide_globally": "Genel okunmamış listesindeki girişleri gizle",
diff --git a/internal/locale/translations/uk_UA.json b/internal/locale/translations/uk_UA.json
index a7563fe6..84ed3278 100644
--- a/internal/locale/translations/uk_UA.json
+++ b/internal/locale/translations/uk_UA.json
@@ -327,6 +327,7 @@
     "form.feed.label.title": "Назва",
     "form.feed.label.site_url": "URL-адреса сайту",
     "form.feed.label.feed_url": "URL-адреса стрічки",
+    "form.feed.label.description": "Опис",
     "form.feed.label.category": "Категорія",
     "form.feed.label.crawler": "Завантажувати оригінальний вміст",
     "form.feed.label.feed_username": "Ім’я користувача для завантаження",
diff --git a/internal/locale/translations/zh_CN.json b/internal/locale/translations/zh_CN.json
index ba0d1f57..7ab55344 100644
--- a/internal/locale/translations/zh_CN.json
+++ b/internal/locale/translations/zh_CN.json
@@ -307,6 +307,7 @@
     "form.feed.label.title": "标题",
     "form.feed.label.site_url": "源网站 URL",
     "form.feed.label.feed_url": "订阅源 URL",
+    "form.feed.label.description": "描述",
     "form.feed.label.category": "类别",
     "form.feed.label.crawler": "抓取全文内容",
     "form.feed.label.feed_username": "源用户名",
diff --git a/internal/locale/translations/zh_TW.json b/internal/locale/translations/zh_TW.json
index 57f50b08..12ea4c7a 100644
--- a/internal/locale/translations/zh_TW.json
+++ b/internal/locale/translations/zh_TW.json
@@ -307,6 +307,7 @@
     "form.feed.label.title": "標題",
     "form.feed.label.site_url": "網站 URL",
     "form.feed.label.feed_url": "訂閱 Feed URL",
+    "form.feed.label.description": "描述",
     "form.feed.label.category": "類別",
     "form.feed.label.crawler": "下載原文內容",
     "form.feed.label.feed_username": "Feed 使用者名稱",
diff --git a/internal/model/feed.go b/internal/model/feed.go
index 5273eea2..7b89dca0 100644
--- a/internal/model/feed.go
+++ b/internal/model/feed.go
@@ -28,6 +28,7 @@ type Feed struct {
 	FeedURL                     string    `json:"feed_url"`
 	SiteURL                     string    `json:"site_url"`
 	Title                       string    `json:"title"`
+	Description                 string    `json:"description"`
 	CheckedAt                   time.Time `json:"checked_at"`
 	NextCheckAt                 time.Time `json:"next_check_at"`
 	EtagHeader                  string    `json:"etag_header"`
@@ -167,6 +168,7 @@ type FeedModificationRequest struct {
 	FeedURL                     *string `json:"feed_url"`
 	SiteURL                     *string `json:"site_url"`
 	Title                       *string `json:"title"`
+	Description                 *string `json:"description"`
 	ScraperRules                *string `json:"scraper_rules"`
 	RewriteRules                *string `json:"rewrite_rules"`
 	BlocklistRules              *string `json:"blocklist_rules"`
@@ -201,6 +203,10 @@ func (f *FeedModificationRequest) Patch(feed *Feed) {
 		feed.Title = *f.Title
 	}
 
+	if f.Description != nil && *f.Description != "" {
+		feed.Description = *f.Description
+	}
+
 	if f.ScraperRules != nil {
 		feed.ScraperRules = *f.ScraperRules
 	}
diff --git a/internal/reader/opml/handler.go b/internal/reader/opml/handler.go
index 9257d354..2e7bceb9 100644
--- a/internal/reader/opml/handler.go
+++ b/internal/reader/opml/handler.go
@@ -29,6 +29,7 @@ func (h *Handler) Export(userID int64) (string, error) {
 			Title:        feed.Title,
 			FeedURL:      feed.FeedURL,
 			SiteURL:      feed.SiteURL,
+			Description:  feed.Description,
 			CategoryName: feed.Category.Title,
 		})
 	}
@@ -68,11 +69,12 @@ func (h *Handler) Import(userID int64, data io.Reader) error {
 			}
 
 			feed := &model.Feed{
-				UserID:   userID,
-				Title:    subscription.Title,
-				FeedURL:  subscription.FeedURL,
-				SiteURL:  subscription.SiteURL,
-				Category: category,
+				UserID:      userID,
+				Title:       subscription.Title,
+				FeedURL:     subscription.FeedURL,
+				SiteURL:     subscription.SiteURL,
+				Description: subscription.Description,
+				Category:    category,
 			}
 
 			h.store.CreateFeed(feed)
diff --git a/internal/reader/opml/opml.go b/internal/reader/opml/opml.go
index 8fe3af09..c6caa9c9 100644
--- a/internal/reader/opml/opml.go
+++ b/internal/reader/opml/opml.go
@@ -27,11 +27,12 @@ type opmlHeader struct {
 }
 
 type opmlOutline struct {
-	Title    string                `xml:"title,attr,omitempty"`
-	Text     string                `xml:"text,attr"`
-	FeedURL  string                `xml:"xmlUrl,attr,omitempty"`
-	SiteURL  string                `xml:"htmlUrl,attr,omitempty"`
-	Outlines opmlOutlineCollection `xml:"outline,omitempty"`
+	Title       string                `xml:"title,attr,omitempty"`
+	Text        string                `xml:"text,attr"`
+	FeedURL     string                `xml:"xmlUrl,attr,omitempty"`
+	SiteURL     string                `xml:"htmlUrl,attr,omitempty"`
+	Description string                `xml:"description,attr,omitempty"`
+	Outlines    opmlOutlineCollection `xml:"outline,omitempty"`
 }
 
 func (outline opmlOutline) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
diff --git a/internal/reader/opml/parser.go b/internal/reader/opml/parser.go
index 09455a69..6b972d40 100644
--- a/internal/reader/opml/parser.go
+++ b/internal/reader/opml/parser.go
@@ -34,6 +34,7 @@ func getSubscriptionsFromOutlines(outlines opmlOutlineCollection, category strin
 				Title:        outline.GetTitle(),
 				FeedURL:      outline.FeedURL,
 				SiteURL:      outline.GetSiteURL(),
+				Description:  outline.Description,
 				CategoryName: category,
 			})
 		} else if outline.Outlines.HasChildren() {
diff --git a/internal/reader/opml/serializer.go b/internal/reader/opml/serializer.go
index 2ecccbed..229c5a82 100644
--- a/internal/reader/opml/serializer.go
+++ b/internal/reader/opml/serializer.go
@@ -48,10 +48,11 @@ func convertSubscriptionsToOPML(subscriptions SubcriptionList) *opmlDocument {
 		category := opmlOutline{Text: categoryName, Outlines: make(opmlOutlineCollection, 0, len(groupedSubs[categoryName]))}
 		for _, subscription := range groupedSubs[categoryName] {
 			category.Outlines = append(category.Outlines, opmlOutline{
-				Title:   subscription.Title,
-				Text:    subscription.Title,
-				FeedURL: subscription.FeedURL,
-				SiteURL: subscription.SiteURL,
+				Title:       subscription.Title,
+				Text:        subscription.Title,
+				FeedURL:     subscription.FeedURL,
+				SiteURL:     subscription.SiteURL,
+				Description: subscription.Description,
 			})
 		}
 
diff --git a/internal/reader/opml/subscription.go b/internal/reader/opml/subscription.go
index 6c05a204..f3a5ac34 100644
--- a/internal/reader/opml/subscription.go
+++ b/internal/reader/opml/subscription.go
@@ -9,12 +9,14 @@ type Subcription struct {
 	SiteURL      string
 	FeedURL      string
 	CategoryName string
+	Description  string
 }
 
 // Equals compare two subscriptions.
 func (s Subcription) Equals(subscription *Subcription) bool {
 	return s.Title == subscription.Title && s.SiteURL == subscription.SiteURL &&
-		s.FeedURL == subscription.FeedURL && s.CategoryName == subscription.CategoryName
+		s.FeedURL == subscription.FeedURL && s.CategoryName == subscription.CategoryName &&
+		s.Description == subscription.Description
 }
 
 // SubcriptionList is a list of subscriptions.
diff --git a/internal/storage/entry_query_builder.go b/internal/storage/entry_query_builder.go
index 9ab26738..6bc475fc 100644
--- a/internal/storage/entry_query_builder.go
+++ b/internal/storage/entry_query_builder.go
@@ -281,6 +281,7 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) {
 			f.title as feed_title,
 			f.feed_url,
 			f.site_url,
+			f.description,
 			f.checked_at,
 			f.category_id,
 			c.title as category_title,
@@ -347,6 +348,7 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) {
 			&entry.Feed.Title,
 			&entry.Feed.FeedURL,
 			&entry.Feed.SiteURL,
+			&entry.Feed.Description,
 			&entry.Feed.CheckedAt,
 			&entry.Feed.Category.ID,
 			&entry.Feed.Category.Title,
diff --git a/internal/storage/feed.go b/internal/storage/feed.go
index e7aaf12a..36939d3d 100644
--- a/internal/storage/feed.go
+++ b/internal/storage/feed.go
@@ -238,10 +238,11 @@ func (s *Storage) CreateFeed(feed *model.Feed) error {
 			url_rewrite_rules,
 			no_media_player,
 			apprise_service_urls,
-			disable_http2
+			disable_http2,
+			description
 		)
 		VALUES
-			($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)
+			($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26)
 		RETURNING
 			id
 	`
@@ -272,6 +273,7 @@ func (s *Storage) CreateFeed(feed *model.Feed) error {
 		feed.NoMediaPlayer,
 		feed.AppriseServiceURLs,
 		feed.DisableHTTP2,
+		feed.Description,
 	).Scan(&feed.ID)
 	if err != nil {
 		return fmt.Errorf(`store: unable to create feed %q: %v`, feed.FeedURL, err)
@@ -344,9 +346,10 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) {
 			url_rewrite_rules=$25,
 			no_media_player=$26,
 			apprise_service_urls=$27,
-			disable_http2=$28
+			disable_http2=$28,
+			description=$29
 		WHERE
-			id=$29 AND user_id=$30
+			id=$30 AND user_id=$31
 	`
 	_, err = s.db.Exec(query,
 		feed.FeedURL,
@@ -377,6 +380,7 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) {
 		feed.NoMediaPlayer,
 		feed.AppriseServiceURLs,
 		feed.DisableHTTP2,
+		feed.Description,
 		feed.ID,
 		feed.UserID,
 	)
diff --git a/internal/storage/feed_query_builder.go b/internal/storage/feed_query_builder.go
index e12107ec..79a752d9 100644
--- a/internal/storage/feed_query_builder.go
+++ b/internal/storage/feed_query_builder.go
@@ -135,6 +135,7 @@ func (f *FeedQueryBuilder) GetFeeds() (model.Feeds, error) {
 			f.feed_url,
 			f.site_url,
 			f.title,
+			f.description,
 			f.etag_header,
 			f.last_modified_header,
 			f.user_id,
@@ -202,6 +203,7 @@ func (f *FeedQueryBuilder) GetFeeds() (model.Feeds, error) {
 			&feed.FeedURL,
 			&feed.SiteURL,
 			&feed.Title,
+			&feed.Description,
 			&feed.EtagHeader,
 			&feed.LastModifiedHeader,
 			&feed.UserID,
diff --git a/internal/template/templates/views/edit_feed.html b/internal/template/templates/views/edit_feed.html
index b0b48236..cda7e251 100644
--- a/internal/template/templates/views/edit_feed.html
+++ b/internal/template/templates/views/edit_feed.html
@@ -63,6 +63,9 @@
             <label for="form-feed-url">{{ t "form.feed.label.feed_url" }}</label>
             <input type="url" name="feed_url" id="form-feed-url" placeholder="https://domain.tld/" value="{{ .form.FeedURL }}" spellcheck="false" required>
 
+            <label for="form-description">{{ t "form.feed.label.description" }}</label>
+            <textarea name="description" id="form-description" cols="40" rows="10" >{{ .form.Description }}</textarea>
+
             {{ if not .form.CategoryHidden }}
             <label><input type="checkbox" name="hide_globally" value="1"{{ if .form.HideGlobally }} checked{{ end }}> {{ t "form.feed.label.hide_globally" }}</label>
             {{ end }}
diff --git a/internal/ui/feed_edit.go b/internal/ui/feed_edit.go
index ba8901bb..e91b93c7 100644
--- a/internal/ui/feed_edit.go
+++ b/internal/ui/feed_edit.go
@@ -43,6 +43,7 @@ func (h *handler) showEditFeedPage(w http.ResponseWriter, r *http.Request) {
 		SiteURL:                     feed.SiteURL,
 		FeedURL:                     feed.FeedURL,
 		Title:                       feed.Title,
+		Description:                 feed.Description,
 		ScraperRules:                feed.ScraperRules,
 		RewriteRules:                feed.RewriteRules,
 		BlocklistRules:              feed.BlocklistRules,
diff --git a/internal/ui/feed_update.go b/internal/ui/feed_update.go
index da75d59f..5a6fd625 100644
--- a/internal/ui/feed_update.go
+++ b/internal/ui/feed_update.go
@@ -59,6 +59,7 @@ func (h *handler) updateFeed(w http.ResponseWriter, r *http.Request) {
 		FeedURL:         model.OptionalString(feedForm.FeedURL),
 		SiteURL:         model.OptionalString(feedForm.SiteURL),
 		Title:           model.OptionalString(feedForm.Title),
+		Description:     model.OptionalString(feedForm.Description),
 		CategoryID:      model.OptionalNumber(feedForm.CategoryID),
 		BlocklistRules:  model.OptionalString(feedForm.BlocklistRules),
 		KeeplistRules:   model.OptionalString(feedForm.KeeplistRules),
diff --git a/internal/ui/form/feed.go b/internal/ui/form/feed.go
index f78e1784..c5345e9d 100644
--- a/internal/ui/form/feed.go
+++ b/internal/ui/form/feed.go
@@ -15,6 +15,7 @@ type FeedForm struct {
 	FeedURL                     string
 	SiteURL                     string
 	Title                       string
+	Description                 string
 	ScraperRules                string
 	RewriteRules                string
 	BlocklistRules              string
@@ -43,6 +44,7 @@ func (f FeedForm) Merge(feed *model.Feed) *model.Feed {
 	feed.Title = f.Title
 	feed.SiteURL = f.SiteURL
 	feed.FeedURL = f.FeedURL
+	feed.Description = f.Description
 	feed.ScraperRules = f.ScraperRules
 	feed.RewriteRules = f.RewriteRules
 	feed.BlocklistRules = f.BlocklistRules
@@ -76,6 +78,7 @@ func NewFeedForm(r *http.Request) *FeedForm {
 		FeedURL:                     r.FormValue("feed_url"),
 		SiteURL:                     r.FormValue("site_url"),
 		Title:                       r.FormValue("title"),
+		Description:                 r.FormValue("description"),
 		ScraperRules:                r.FormValue("scraper_rules"),
 		UserAgent:                   r.FormValue("user_agent"),
 		Cookie:                      r.FormValue("cookie"),



Test patch:
diff --git a/internal/reader/opml/parser_test.go b/internal/reader/opml/parser_test.go
index 4dc855e6..9210c178 100644
--- a/internal/reader/opml/parser_test.go
+++ b/internal/reader/opml/parser_test.go
@@ -33,7 +33,7 @@ func TestParseOpmlWithoutCategories(t *testing.T) {
 	`
 
 	var expected SubcriptionList
-	expected = append(expected, &Subcription{Title: "CNET News.com", FeedURL: "http://news.com.com/2547-1_3-0-5.xml", SiteURL: "http://news.com.com/"})
+	expected = append(expected, &Subcription{Title: "CNET News.com", FeedURL: "http://news.com.com/2547-1_3-0-5.xml", SiteURL: "http://news.com.com/", Description: "Tech news and business reports by CNET News.com. Focused on information technology, core topics include computers, hardware, software, networking, and Internet media."})
 
 	subscriptions, err := Parse(bytes.NewBufferString(data))
 	if err != nil {