diff --git a/.vscode/settings.json b/.vscode/settings.json
index 081298b..b6f1097 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -36,6 +36,7 @@
"hotspot",
"huaweimobilewifi",
"isready",
+ "Itune",
"jackc",
"joho",
"ldflags",
diff --git a/add_item.go b/add_item.go
index c1f02b4..be1a649 100644
--- a/add_item.go
+++ b/add_item.go
@@ -32,6 +32,12 @@ func addItemToDB(item *gofeed.Item, ctx context.Context, newFeed db.Feed) {
// Add Dublin Core to the database
createItemDublinCore(ctx, item, newItem)
+ // Add iTunes extensions to the database
+ _, err = createItemItunes(ctx, item, newItem)
+ if err != nil {
+ log.Printf("Error adding iTunes extensions to database: %s", err)
+ }
+
log.Printf("Item added to database")
}
diff --git a/db/feeds.sql.go b/db/feeds.sql.go
index ac771b3..1888b7d 100644
--- a/db/feeds.sql.go
+++ b/db/feeds.sql.go
@@ -436,6 +436,194 @@ func (q *Queries) CreateFeedImage(ctx context.Context, arg CreateFeedImageParams
return i, err
}
+const createFeedItunes = `-- name: CreateFeedItunes :one
+INSERT INTO
+ feed_itunes (
+ created_at,
+ updated_at,
+ deleted_at,
+ author,
+ "block",
+ "explicit",
+ keywords,
+ subtitle,
+ summary,
+ "image",
+ complete,
+ new_feed_url,
+ "type",
+ feed_id
+ )
+VALUES
+ (
+ $1,
+ $2,
+ $3,
+ $4,
+ $5,
+ $6,
+ $7,
+ $8,
+ $9,
+ $10,
+ $11,
+ $12,
+ $13,
+ $14
+ )
+RETURNING
+ id, created_at, updated_at, deleted_at, author, block, explicit, keywords, subtitle, summary, image, complete, new_feed_url, type, feed_id
+`
+
+type CreateFeedItunesParams struct {
+ CreatedAt pgtype.Timestamptz `json:"created_at"`
+ UpdatedAt pgtype.Timestamptz `json:"updated_at"`
+ DeletedAt pgtype.Timestamptz `json:"deleted_at"`
+ Author pgtype.Text `json:"author"`
+ Block pgtype.Text `json:"block"`
+ Explicit pgtype.Text `json:"explicit"`
+ Keywords pgtype.Text `json:"keywords"`
+ Subtitle pgtype.Text `json:"subtitle"`
+ Summary pgtype.Text `json:"summary"`
+ Image pgtype.Text `json:"image"`
+ Complete pgtype.Text `json:"complete"`
+ NewFeedUrl pgtype.Text `json:"new_feed_url"`
+ Type pgtype.Text `json:"type"`
+ FeedID int64 `json:"feed_id"`
+}
+
+func (q *Queries) CreateFeedItunes(ctx context.Context, arg CreateFeedItunesParams) (FeedItune, error) {
+ row := q.db.QueryRow(ctx, createFeedItunes,
+ arg.CreatedAt,
+ arg.UpdatedAt,
+ arg.DeletedAt,
+ arg.Author,
+ arg.Block,
+ arg.Explicit,
+ arg.Keywords,
+ arg.Subtitle,
+ arg.Summary,
+ arg.Image,
+ arg.Complete,
+ arg.NewFeedUrl,
+ arg.Type,
+ arg.FeedID,
+ )
+ var i FeedItune
+ err := row.Scan(
+ &i.ID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.DeletedAt,
+ &i.Author,
+ &i.Block,
+ &i.Explicit,
+ &i.Keywords,
+ &i.Subtitle,
+ &i.Summary,
+ &i.Image,
+ &i.Complete,
+ &i.NewFeedUrl,
+ &i.Type,
+ &i.FeedID,
+ )
+ return i, err
+}
+
+const createFeedItunesCategory = `-- name: CreateFeedItunesCategory :one
+INSERT INTO
+ feed_itunes_categories (
+ created_at,
+ updated_at,
+ deleted_at,
+ "text",
+ subcategory,
+ itunes_id
+ )
+VALUES
+ ($1, $2, $3, $4, $5, $6)
+RETURNING
+ id, created_at, updated_at, deleted_at, text, subcategory, itunes_id
+`
+
+type CreateFeedItunesCategoryParams struct {
+ CreatedAt pgtype.Timestamptz `json:"created_at"`
+ UpdatedAt pgtype.Timestamptz `json:"updated_at"`
+ DeletedAt pgtype.Timestamptz `json:"deleted_at"`
+ Text pgtype.Text `json:"text"`
+ Subcategory pgtype.Int8 `json:"subcategory"`
+ ItunesID int64 `json:"itunes_id"`
+}
+
+func (q *Queries) CreateFeedItunesCategory(ctx context.Context, arg CreateFeedItunesCategoryParams) (FeedItunesCategory, error) {
+ row := q.db.QueryRow(ctx, createFeedItunesCategory,
+ arg.CreatedAt,
+ arg.UpdatedAt,
+ arg.DeletedAt,
+ arg.Text,
+ arg.Subcategory,
+ arg.ItunesID,
+ )
+ var i FeedItunesCategory
+ err := row.Scan(
+ &i.ID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.DeletedAt,
+ &i.Text,
+ &i.Subcategory,
+ &i.ItunesID,
+ )
+ return i, err
+}
+
+const createFeedItunesOwner = `-- name: CreateFeedItunesOwner :one
+INSERT INTO
+ feed_itunes_owners (
+ created_at,
+ updated_at,
+ deleted_at,
+ email,
+ "name",
+ itunes_id
+ )
+VALUES
+ ($1, $2, $3, $4, $5, $6)
+RETURNING
+ id, created_at, updated_at, deleted_at, email, name, itunes_id
+`
+
+type CreateFeedItunesOwnerParams struct {
+ CreatedAt pgtype.Timestamptz `json:"created_at"`
+ UpdatedAt pgtype.Timestamptz `json:"updated_at"`
+ DeletedAt pgtype.Timestamptz `json:"deleted_at"`
+ Email pgtype.Text `json:"email"`
+ Name pgtype.Text `json:"name"`
+ ItunesID int64 `json:"itunes_id"`
+}
+
+func (q *Queries) CreateFeedItunesOwner(ctx context.Context, arg CreateFeedItunesOwnerParams) (FeedItunesOwner, error) {
+ row := q.db.QueryRow(ctx, createFeedItunesOwner,
+ arg.CreatedAt,
+ arg.UpdatedAt,
+ arg.DeletedAt,
+ arg.Email,
+ arg.Name,
+ arg.ItunesID,
+ )
+ var i FeedItunesOwner
+ err := row.Scan(
+ &i.ID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.DeletedAt,
+ &i.Email,
+ &i.Name,
+ &i.ItunesID,
+ )
+ return i, err
+}
+
const createItem = `-- name: CreateItem :one
INSERT INTO
items (
@@ -813,6 +1001,111 @@ func (q *Queries) CreateItemImage(ctx context.Context, arg CreateItemImageParams
return i, err
}
+const createItemItunes = `-- name: CreateItemItunes :one
+INSERT INTO
+ item_itunes (
+ created_at,
+ updated_at,
+ deleted_at,
+ author,
+ "block",
+ "explicit",
+ keywords,
+ subtitle,
+ summary,
+ "image",
+ is_closed_captioned,
+ episode,
+ season,
+ "order",
+ episode_type,
+ item_id
+ )
+VALUES
+ (
+ $1,
+ $2,
+ $3,
+ $4,
+ $5,
+ $6,
+ $7,
+ $8,
+ $9,
+ $10,
+ $11,
+ $12,
+ $13,
+ $14,
+ $15,
+ $16
+ )
+RETURNING
+ id, created_at, updated_at, deleted_at, author, block, duration, explicit, keywords, subtitle, summary, image, is_closed_captioned, episode, season, "order", episode_type, item_id
+`
+
+type CreateItemItunesParams struct {
+ CreatedAt pgtype.Timestamptz `json:"created_at"`
+ UpdatedAt pgtype.Timestamptz `json:"updated_at"`
+ DeletedAt pgtype.Timestamptz `json:"deleted_at"`
+ Author pgtype.Text `json:"author"`
+ Block pgtype.Text `json:"block"`
+ Explicit pgtype.Text `json:"explicit"`
+ Keywords pgtype.Text `json:"keywords"`
+ Subtitle pgtype.Text `json:"subtitle"`
+ Summary pgtype.Text `json:"summary"`
+ Image pgtype.Text `json:"image"`
+ IsClosedCaptioned pgtype.Text `json:"is_closed_captioned"`
+ Episode pgtype.Text `json:"episode"`
+ Season pgtype.Text `json:"season"`
+ Order pgtype.Text `json:"order"`
+ EpisodeType pgtype.Text `json:"episode_type"`
+ ItemID int64 `json:"item_id"`
+}
+
+func (q *Queries) CreateItemItunes(ctx context.Context, arg CreateItemItunesParams) (ItemItune, error) {
+ row := q.db.QueryRow(ctx, createItemItunes,
+ arg.CreatedAt,
+ arg.UpdatedAt,
+ arg.DeletedAt,
+ arg.Author,
+ arg.Block,
+ arg.Explicit,
+ arg.Keywords,
+ arg.Subtitle,
+ arg.Summary,
+ arg.Image,
+ arg.IsClosedCaptioned,
+ arg.Episode,
+ arg.Season,
+ arg.Order,
+ arg.EpisodeType,
+ arg.ItemID,
+ )
+ var i ItemItune
+ err := row.Scan(
+ &i.ID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.DeletedAt,
+ &i.Author,
+ &i.Block,
+ &i.Duration,
+ &i.Explicit,
+ &i.Keywords,
+ &i.Subtitle,
+ &i.Summary,
+ &i.Image,
+ &i.IsClosedCaptioned,
+ &i.Episode,
+ &i.Season,
+ &i.Order,
+ &i.EpisodeType,
+ &i.ItemID,
+ )
+ return i, err
+}
+
const getFeed = `-- name: GetFeed :one
SELECT
id, url, created_at, updated_at, deleted_at, title, description, link, feed_link, links, updated, updated_parsed, published, published_parsed, language, copyright, generator, categories, custom, feed_type, feed_version
@@ -1063,6 +1356,136 @@ func (q *Queries) GetFeedImages(ctx context.Context, arg GetFeedImagesParams) ([
return items, nil
}
+const getFeedItunes = `-- name: GetFeedItunes :one
+SELECT
+ id, created_at, updated_at, deleted_at, author, block, explicit, keywords, subtitle, summary, image, complete, new_feed_url, type, feed_id
+FROM
+ feed_itunes
+WHERE
+ feed_id = $1
+`
+
+func (q *Queries) GetFeedItunes(ctx context.Context, feedID int64) (FeedItune, error) {
+ row := q.db.QueryRow(ctx, getFeedItunes, feedID)
+ var i FeedItune
+ err := row.Scan(
+ &i.ID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.DeletedAt,
+ &i.Author,
+ &i.Block,
+ &i.Explicit,
+ &i.Keywords,
+ &i.Subtitle,
+ &i.Summary,
+ &i.Image,
+ &i.Complete,
+ &i.NewFeedUrl,
+ &i.Type,
+ &i.FeedID,
+ )
+ return i, err
+}
+
+const getFeedItunesCategories = `-- name: GetFeedItunesCategories :many
+SELECT
+ id, created_at, updated_at, deleted_at, text, subcategory, itunes_id
+FROM
+ feed_itunes_categories
+WHERE
+ itunes_id = $1
+ORDER BY
+ created_at DESC
+LIMIT
+ $2
+OFFSET
+ $3
+`
+
+type GetFeedItunesCategoriesParams struct {
+ ItunesID int64 `json:"itunes_id"`
+ Limit int32 `json:"limit"`
+ Offset int32 `json:"offset"`
+}
+
+func (q *Queries) GetFeedItunesCategories(ctx context.Context, arg GetFeedItunesCategoriesParams) ([]FeedItunesCategory, error) {
+ rows, err := q.db.Query(ctx, getFeedItunesCategories, arg.ItunesID, arg.Limit, arg.Offset)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ items := []FeedItunesCategory{}
+ for rows.Next() {
+ var i FeedItunesCategory
+ if err := rows.Scan(
+ &i.ID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.DeletedAt,
+ &i.Text,
+ &i.Subcategory,
+ &i.ItunesID,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const getFeedItunesOwners = `-- name: GetFeedItunesOwners :many
+SELECT
+ id, created_at, updated_at, deleted_at, email, name, itunes_id
+FROM
+ feed_itunes_owners
+WHERE
+ itunes_id = $1
+ORDER BY
+ created_at DESC
+LIMIT
+ $2
+OFFSET
+ $3
+`
+
+type GetFeedItunesOwnersParams struct {
+ ItunesID int64 `json:"itunes_id"`
+ Limit int32 `json:"limit"`
+ Offset int32 `json:"offset"`
+}
+
+func (q *Queries) GetFeedItunesOwners(ctx context.Context, arg GetFeedItunesOwnersParams) ([]FeedItunesOwner, error) {
+ rows, err := q.db.Query(ctx, getFeedItunesOwners, arg.ItunesID, arg.Limit, arg.Offset)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ items := []FeedItunesOwner{}
+ for rows.Next() {
+ var i FeedItunesOwner
+ if err := rows.Scan(
+ &i.ID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.DeletedAt,
+ &i.Email,
+ &i.Name,
+ &i.ItunesID,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
const getFeeds = `-- name: GetFeeds :many
SELECT
id, url, created_at, updated_at, deleted_at, title, description, link, feed_link, links, updated, updated_parsed, published, published_parsed, language, copyright, generator, categories, custom, feed_type, feed_version
@@ -1369,6 +1792,41 @@ func (q *Queries) GetItemImages(ctx context.Context, arg GetItemImagesParams) ([
return items, nil
}
+const getItemItunes = `-- name: GetItemItunes :one
+SELECT
+ id, created_at, updated_at, deleted_at, author, block, duration, explicit, keywords, subtitle, summary, image, is_closed_captioned, episode, season, "order", episode_type, item_id
+FROM
+ item_itunes
+WHERE
+ item_id = $1
+`
+
+func (q *Queries) GetItemItunes(ctx context.Context, itemID int64) (ItemItune, error) {
+ row := q.db.QueryRow(ctx, getItemItunes, itemID)
+ var i ItemItune
+ err := row.Scan(
+ &i.ID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.DeletedAt,
+ &i.Author,
+ &i.Block,
+ &i.Duration,
+ &i.Explicit,
+ &i.Keywords,
+ &i.Subtitle,
+ &i.Summary,
+ &i.Image,
+ &i.IsClosedCaptioned,
+ &i.Episode,
+ &i.Season,
+ &i.Order,
+ &i.EpisodeType,
+ &i.ItemID,
+ )
+ return i, err
+}
+
const getItems = `-- name: GetItems :many
SELECT
id, created_at, updated_at, deleted_at, title, description, content, link, links, updated, updated_parsed, published, published_parsed, guid, categories, custom, feed_id
diff --git a/db/models.go b/db/models.go
index b095294..553c0af 100644
--- a/db/models.go
+++ b/db/models.go
@@ -117,6 +117,26 @@ type FeedItune struct {
FeedID int64 `json:"feed_id"`
}
+type FeedItunesCategory struct {
+ ID int64 `json:"id"`
+ CreatedAt pgtype.Timestamptz `json:"created_at"`
+ UpdatedAt pgtype.Timestamptz `json:"updated_at"`
+ DeletedAt pgtype.Timestamptz `json:"deleted_at"`
+ Text pgtype.Text `json:"text"`
+ Subcategory pgtype.Int8 `json:"subcategory"`
+ ItunesID int64 `json:"itunes_id"`
+}
+
+type FeedItunesOwner struct {
+ ID int64 `json:"id"`
+ CreatedAt pgtype.Timestamptz `json:"created_at"`
+ UpdatedAt pgtype.Timestamptz `json:"updated_at"`
+ DeletedAt pgtype.Timestamptz `json:"deleted_at"`
+ Email pgtype.Text `json:"email"`
+ Name pgtype.Text `json:"name"`
+ ItunesID int64 `json:"itunes_id"`
+}
+
type Item struct {
ID int64 `json:"id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
@@ -213,23 +233,3 @@ type ItemItune struct {
EpisodeType pgtype.Text `json:"episode_type"`
ItemID int64 `json:"item_id"`
}
-
-type ItunesCategory struct {
- ID int64 `json:"id"`
- CreatedAt pgtype.Timestamptz `json:"created_at"`
- UpdatedAt pgtype.Timestamptz `json:"updated_at"`
- DeletedAt pgtype.Timestamptz `json:"deleted_at"`
- Text pgtype.Text `json:"text"`
- Subcategory pgtype.Text `json:"subcategory"`
- ItunesID int64 `json:"itunes_id"`
-}
-
-type ItunesOwner struct {
- ID int64 `json:"id"`
- CreatedAt pgtype.Timestamptz `json:"created_at"`
- UpdatedAt pgtype.Timestamptz `json:"updated_at"`
- DeletedAt pgtype.Timestamptz `json:"deleted_at"`
- Email pgtype.Text `json:"email"`
- Name pgtype.Text `json:"name"`
- ItunesID int64 `json:"itunes_id"`
-}
diff --git a/feeds.go b/feeds.go
index 1d1192b..14c265b 100644
--- a/feeds.go
+++ b/feeds.go
@@ -53,6 +53,15 @@ func AddFeedToDB(feedURL string) error {
log.Printf("Adding Dublin Core to the database")
createFeedDublinCore(ctx, feed, newFeed)
+ // Add iTunes extensions to the database
+ log.Printf("Adding iTunes extensions to the database")
+ itunes, err := createFeedItunes(ctx, feed, newFeed)
+ if err != nil {
+ log.Printf("Error adding iTunes extensions to database: %s", err)
+ }
+ createFeedItunesCategories(ctx, feed, itunes)
+ createFeedItunesOwners(ctx, feed, itunes)
+
log.Printf("Feed added to database")
return nil
}
diff --git a/handlers.go b/handlers.go
index 4bd171d..9f297e6 100644
--- a/handlers.go
+++ b/handlers.go
@@ -33,8 +33,7 @@ func FeedsHandler(w http.ResponseWriter, _ *http.Request) {
Limit: 100,
})
if err != nil {
- http.Error(w, "Error getting feeds", http.StatusInternalServerError)
- return
+ log.Println("Error getting feeds:", err)
}
fb := strings.Builder{}
@@ -45,7 +44,6 @@ func FeedsHandler(w http.ResponseWriter, _ *http.Request) {
})
if err != nil {
http.Error(w, "Error getting authors", http.StatusInternalServerError)
- return
}
extensions, err := DB.GetFeedExtensions(context.Background(), db.GetFeedExtensionsParams{
@@ -53,8 +51,13 @@ func FeedsHandler(w http.ResponseWriter, _ *http.Request) {
Limit: 100,
})
if err != nil {
- http.Error(w, "Error getting extensions", http.StatusInternalServerError)
- return
+ log.Println("Error getting extensions:", err)
+ }
+
+ // Get the itunes extensions
+ itunes, err := DB.GetFeedItunes(context.Background(), feed.ID)
+ if err != nil {
+ log.Println("Error getting itunes extensions:", err)
}
fb.WriteString("
")
@@ -84,13 +87,42 @@ func FeedsHandler(w http.ResponseWriter, _ *http.Request) {
}
}
+ // Itunes extensions
+
+ fb.WriteString("")
+ if itunes.Author.Valid {
+ fb.WriteString("- Itunes Author: " + itunes.Author.String + "
")
+ }
+ if itunes.Block.Valid {
+ fb.WriteString("- Itunes Block: " + itunes.Block.String + "
")
+ }
+ if itunes.Explicit.Valid {
+ fb.WriteString("- Itunes Explicit: " + itunes.Explicit.String + "
")
+ }
+ if itunes.Image.Valid {
+ fb.WriteString("- Itunes Image: " + itunes.Image.String + "
")
+ }
+ if itunes.Keywords.Valid {
+ fb.WriteString("- Itunes Keywords: " + itunes.Keywords.String + "
")
+ }
+ if itunes.Subtitle.Valid {
+ fb.WriteString("- Itunes Subtitle: " + itunes.Subtitle.String + "
")
+ }
+ if itunes.Summary.Valid {
+ fb.WriteString("- Itunes Summary: " + itunes.Summary.String + "
")
+ }
+ if itunes.Type.Valid {
+ fb.WriteString("- Itunes Type: " + itunes.Type.String + "
")
+ }
+ fb.WriteString("
")
+
images, err := DB.GetFeedImages(context.Background(), db.GetFeedImagesParams{
FeedID: feed.ID,
Limit: 100,
})
if err != nil {
- http.Error(w, "Error getting images", http.StatusInternalServerError)
- return
+ log.Println("Error getting images:", err)
+ continue
}
for _, image := range images {
fb.WriteString("
")
@@ -329,8 +361,7 @@ func FeedHandler(w http.ResponseWriter, r *http.Request) {
Limit: 100,
})
if err != nil {
- http.Error(w, "Error getting items", http.StatusInternalServerError)
- return
+ log.Println("Error getting items:", err)
}
// Build the HTML
@@ -408,6 +439,39 @@ func FeedHandler(w http.ResponseWriter, r *http.Request) {
fb.WriteString("")
fb.WriteString("
")
fb.WriteString("")
+
+ // Itunes extensions
+ itunes, err := DB.GetItemItunes(context.Background(), item.ID)
+ if err != nil {
+ log.Println("Error getting itunes extensions:", err)
+ }
+ fb.WriteString("")
+ if itunes.Author.Valid {
+ fb.WriteString("- Itunes Author: " + itunes.Author.String + "
")
+ }
+ if itunes.Block.Valid {
+ fb.WriteString("- Itunes Block: " + itunes.Block.String + "
")
+ }
+ if itunes.Duration.Valid {
+ fb.WriteString("- Itunes Duration: " + itunes.Duration.String + "
")
+ }
+ if itunes.Explicit.Valid {
+ fb.WriteString("- Itunes Explicit: " + itunes.Explicit.String + "
")
+ }
+ if itunes.Image.Valid {
+ fb.WriteString("- Itunes Image: " + itunes.Image.String + "
")
+ }
+ if itunes.Keywords.Valid {
+ fb.WriteString("- Itunes Keywords: " + itunes.Keywords.String + "
")
+ }
+ if itunes.Subtitle.Valid {
+ fb.WriteString("- Itunes Subtitle: " + itunes.Subtitle.String + "
")
+ }
+ if itunes.Summary.Valid {
+ fb.WriteString("- Itunes Summary: " + itunes.Summary.String + "
")
+ }
+ fb.WriteString("
")
+
}
htmlData := HTMLData{
diff --git a/itunes.go b/itunes.go
new file mode 100644
index 0000000..f37cebb
--- /dev/null
+++ b/itunes.go
@@ -0,0 +1,138 @@
+package main
+
+import (
+ "context"
+ "log"
+ "time"
+
+ "github.com/TheLovinator1/FeedVault/db"
+ "github.com/jackc/pgx/v5/pgtype"
+ "github.com/mmcdole/gofeed"
+)
+
+func createFeedItunes(ctx context.Context, feed *gofeed.Feed, newFeed db.Feed) (db.FeedItune, error) {
+ if feed.ITunesExt == nil {
+ log.Printf("No iTunes extensions to add to database")
+ return db.FeedItune{}, nil
+ }
+
+ // Add iTunes extensions to the database
+ itunesID, err := DB.CreateFeedItunes(ctx, db.CreateFeedItunesParams{
+ CreatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
+ UpdatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
+ DeletedAt: pgtype.Timestamptz{Valid: false},
+ Author: pgtype.Text{String: feed.ITunesExt.Author, Valid: feed.ITunesExt.Author != ""},
+ Block: pgtype.Text{String: feed.ITunesExt.Block, Valid: feed.ITunesExt.Block != ""},
+ Explicit: pgtype.Text{String: feed.ITunesExt.Explicit, Valid: feed.ITunesExt.Explicit != ""},
+ Keywords: pgtype.Text{String: feed.ITunesExt.Keywords, Valid: feed.ITunesExt.Keywords != ""},
+ Subtitle: pgtype.Text{String: feed.ITunesExt.Subtitle, Valid: feed.ITunesExt.Subtitle != ""},
+ Summary: pgtype.Text{String: feed.ITunesExt.Summary, Valid: feed.ITunesExt.Summary != ""},
+ Image: pgtype.Text{String: feed.ITunesExt.Image, Valid: feed.ITunesExt.Image != ""},
+ Complete: pgtype.Text{String: feed.ITunesExt.Complete, Valid: feed.ITunesExt.Complete != ""},
+ NewFeedUrl: pgtype.Text{String: feed.ITunesExt.NewFeedURL, Valid: feed.ITunesExt.NewFeedURL != ""},
+ Type: pgtype.Text{String: feed.ITunesExt.Type, Valid: feed.ITunesExt.Type != ""},
+ FeedID: newFeed.ID,
+ })
+ if err != nil {
+ log.Printf("Error adding iTunes extensions to database: %s", err)
+ return db.FeedItune{}, err
+ }
+ log.Printf("iTunes extensions added to database")
+ return itunesID, nil
+}
+
+func createItemItunes(ctx context.Context, item *gofeed.Item, newItem db.Item) (db.ItemItune, error) {
+ if item.ITunesExt == nil {
+ log.Printf("No iTunes extensions to add to database")
+ return db.ItemItune{}, nil
+ }
+
+ // Add iTunes extensions to the database
+ itunesID, err := DB.CreateItemItunes(ctx, db.CreateItemItunesParams{
+ CreatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
+ UpdatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
+ DeletedAt: pgtype.Timestamptz{Valid: false},
+ Author: pgtype.Text{String: item.ITunesExt.Author, Valid: item.ITunesExt.Author != ""},
+ Block: pgtype.Text{String: item.ITunesExt.Block, Valid: item.ITunesExt.Block != ""},
+ Explicit: pgtype.Text{String: item.ITunesExt.Explicit, Valid: item.ITunesExt.Explicit != ""},
+ Keywords: pgtype.Text{String: item.ITunesExt.Keywords, Valid: item.ITunesExt.Keywords != ""},
+ Subtitle: pgtype.Text{String: item.ITunesExt.Subtitle, Valid: item.ITunesExt.Subtitle != ""},
+ Summary: pgtype.Text{String: item.ITunesExt.Summary, Valid: item.ITunesExt.Summary != ""},
+ Image: pgtype.Text{String: item.ITunesExt.Image, Valid: item.ITunesExt.Image != ""},
+ IsClosedCaptioned: pgtype.Text{String: item.ITunesExt.IsClosedCaptioned, Valid: item.ITunesExt.IsClosedCaptioned != ""},
+ Episode: pgtype.Text{String: item.ITunesExt.Episode, Valid: item.ITunesExt.Episode != ""},
+ Season: pgtype.Text{String: item.ITunesExt.Season, Valid: item.ITunesExt.Season != ""},
+ Order: pgtype.Text{String: item.ITunesExt.Order, Valid: item.ITunesExt.Order != ""},
+ EpisodeType: pgtype.Text{String: item.ITunesExt.EpisodeType, Valid: item.ITunesExt.EpisodeType != ""},
+ ItemID: newItem.ID,
+ })
+ if err != nil {
+ log.Printf("Error adding iTunes extensions to database: %s", err)
+ return db.ItemItune{}, err
+ }
+ log.Printf("iTunes extensions added to database")
+ return itunesID, nil
+}
+
+func createFeedItunesCategories(ctx context.Context, feed *gofeed.Feed, itunes db.FeedItune) {
+ if feed.ITunesExt == nil {
+ log.Printf("No iTunes categories to add to database")
+ return
+ }
+ for _, cat := range feed.ITunesExt.Categories {
+ newCat, err := DB.CreateFeedItunesCategory(ctx, db.CreateFeedItunesCategoryParams{
+ CreatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
+ UpdatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
+ DeletedAt: pgtype.Timestamptz{Valid: false},
+ Text: pgtype.Text{String: cat.Text, Valid: cat.Text != ""}, // 🐈 meow
+ ItunesID: itunes.ID,
+ })
+ if err != nil {
+ log.Printf("Error adding iTunes category to database: %s", err)
+ continue
+ }
+ log.Printf("iTunes category added to database: %s", cat.Text)
+
+ // Add subcategories to the database
+ if cat.Subcategory != nil {
+ _, err = DB.CreateFeedItunesCategory(ctx, db.CreateFeedItunesCategoryParams{
+ CreatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
+ UpdatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
+ DeletedAt: pgtype.Timestamptz{Valid: false},
+ Text: pgtype.Text{String: cat.Subcategory.Text, Valid: cat.Subcategory.Text != ""}, // 🐈 meow
+ ItunesID: itunes.ID,
+ Subcategory: pgtype.Int8{Int64: newCat.ID, Valid: true},
+ })
+ if err != nil {
+ log.Printf("Error adding iTunes subcategory to database: %s", err)
+ continue
+ }
+ log.Printf("iTunes subcategory added to database: %s", cat.Text)
+ }
+ }
+}
+
+func createFeedItunesOwners(ctx context.Context, feed *gofeed.Feed, itunes db.FeedItune) {
+ if feed.ITunesExt == nil {
+ log.Printf("No iTunes owners to add to database")
+ return
+ }
+ if feed.ITunesExt.Owner == nil {
+ log.Printf("No iTunes owner to add to database")
+ return
+ }
+
+ _, err := DB.CreateFeedItunesOwner(ctx, db.CreateFeedItunesOwnerParams{
+ CreatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
+ UpdatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
+ DeletedAt: pgtype.Timestamptz{Valid: false},
+ Name: pgtype.Text{String: feed.ITunesExt.Owner.Name, Valid: feed.ITunesExt.Owner.Name != ""},
+ Email: pgtype.Text{String: feed.ITunesExt.Owner.Email, Valid: feed.ITunesExt.Owner.Email != ""},
+ ItunesID: itunes.ID,
+ })
+ if err != nil {
+ log.Printf("Error adding iTunes owner to database: %s", err)
+ return
+ }
+ log.Printf("iTunes owner added to database: %s", feed.ITunesExt.Owner.Name)
+}
diff --git a/sql/queries/feeds.sql b/sql/queries/feeds.sql
index 187c15a..80dd62a 100644
--- a/sql/queries/feeds.sql
+++ b/sql/queries/feeds.sql
@@ -449,3 +449,157 @@ LIMIT
$2
OFFSET
$3;
+
+-- name: CreateFeedItunes :one
+INSERT INTO
+ feed_itunes (
+ created_at,
+ updated_at,
+ deleted_at,
+ author,
+ "block",
+ "explicit",
+ keywords,
+ subtitle,
+ summary,
+ "image",
+ complete,
+ new_feed_url,
+ "type",
+ feed_id
+ )
+VALUES
+ (
+ $1,
+ $2,
+ $3,
+ $4,
+ $5,
+ $6,
+ $7,
+ $8,
+ $9,
+ $10,
+ $11,
+ $12,
+ $13,
+ $14
+ )
+RETURNING
+ *;
+
+-- name: CreateItemItunes :one
+INSERT INTO
+ item_itunes (
+ created_at,
+ updated_at,
+ deleted_at,
+ author,
+ "block",
+ "explicit",
+ keywords,
+ subtitle,
+ summary,
+ "image",
+ is_closed_captioned,
+ episode,
+ season,
+ "order",
+ episode_type,
+ item_id
+ )
+VALUES
+ (
+ $1,
+ $2,
+ $3,
+ $4,
+ $5,
+ $6,
+ $7,
+ $8,
+ $9,
+ $10,
+ $11,
+ $12,
+ $13,
+ $14,
+ $15,
+ $16
+ )
+RETURNING
+ *;
+
+-- name: GetFeedItunes :one
+SELECT
+ *
+FROM
+ feed_itunes
+WHERE
+ feed_id = $1;
+
+-- name: GetItemItunes :one
+SELECT
+ *
+FROM
+ item_itunes
+WHERE
+ item_id = $1;
+
+-- name: CreateFeedItunesCategory :one
+INSERT INTO
+ feed_itunes_categories (
+ created_at,
+ updated_at,
+ deleted_at,
+ "text",
+ subcategory,
+ itunes_id
+ )
+VALUES
+ ($1, $2, $3, $4, $5, $6)
+RETURNING
+ *;
+
+-- name: CreateFeedItunesOwner :one
+INSERT INTO
+ feed_itunes_owners (
+ created_at,
+ updated_at,
+ deleted_at,
+ email,
+ "name",
+ itunes_id
+ )
+VALUES
+ ($1, $2, $3, $4, $5, $6)
+RETURNING
+ *;
+
+-- name: GetFeedItunesCategories :many
+SELECT
+ *
+FROM
+ feed_itunes_categories
+WHERE
+ itunes_id = $1
+ORDER BY
+ created_at DESC
+LIMIT
+ $2
+OFFSET
+ $3;
+
+-- name: GetFeedItunesOwners :many
+SELECT
+ *
+FROM
+ feed_itunes_owners
+WHERE
+ itunes_id = $1
+ORDER BY
+ created_at DESC
+LIMIT
+ $2
+OFFSET
+ $3;
diff --git a/sql/schema/20240214043229_feeds.sql b/sql/schema/20240214043229_feeds.sql
index 6fb80c6..79c1026 100644
--- a/sql/schema/20240214043229_feeds.sql
+++ b/sql/schema/20240214043229_feeds.sql
@@ -66,8 +66,8 @@ CREATE TABLE IF NOT EXISTS items (
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-DROP TABLE IF EXISTS feeds;
+DROP TABLE IF EXISTS feeds CASCADE;
-DROP TABLE IF EXISTS items;
+DROP TABLE IF EXISTS items CASCADE;
-- +goose StatementEnd
diff --git a/sql/schema/20240215232236_extensions.sql b/sql/schema/20240215232236_extensions.sql
index 52a38d3..4d71ab3 100644
--- a/sql/schema/20240215232236_extensions.sql
+++ b/sql/schema/20240215232236_extensions.sql
@@ -37,8 +37,8 @@ CREATE TABLE IF NOT EXISTS item_extensions (
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-DROP TABLE IF EXISTS feed_extensions;
+DROP TABLE IF EXISTS feed_extensions CASCADE;
-DROP TABLE IF EXISTS item_extensions;
+DROP TABLE IF EXISTS item_extensions CASCADE;
-- +goose StatementEnd
diff --git a/sql/schema/20240215232245_authors.sql b/sql/schema/20240215232245_authors.sql
index 71a04c9..3fe2c45 100644
--- a/sql/schema/20240215232245_authors.sql
+++ b/sql/schema/20240215232245_authors.sql
@@ -33,8 +33,8 @@ CREATE TABLE IF NOT EXISTS item_authors (
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-DROP TABLE IF EXISTS feed_authors;
+DROP TABLE IF EXISTS feed_authors CASCADE;
-DROP TABLE IF EXISTS item_authors;
+DROP TABLE IF EXISTS item_authors CASCADE;
-- +goose StatementEnd
diff --git a/sql/schema/20240215232251_images.sql b/sql/schema/20240215232251_images.sql
index 5933861..21e1b98 100644
--- a/sql/schema/20240215232251_images.sql
+++ b/sql/schema/20240215232251_images.sql
@@ -33,8 +33,8 @@ CREATE TABLE IF NOT EXISTS item_images (
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-DROP TABLE IF EXISTS feed_images;
+DROP TABLE IF EXISTS feed_images CASCADE;
-DROP TABLE IF EXISTS item_images;
+DROP TABLE IF EXISTS item_images CASCADE;
-- +goose StatementEnd
diff --git a/sql/schema/20240215232259_dublin_cores.sql b/sql/schema/20240215232259_dublin_cores.sql
index 8f1e251..f1b84fd 100644
--- a/sql/schema/20240215232259_dublin_cores.sql
+++ b/sql/schema/20240215232259_dublin_cores.sql
@@ -61,8 +61,8 @@ CREATE TABLE IF NOT EXISTS item_dublin_cores (
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-DROP TABLE IF EXISTS feed_dublin_cores;
+DROP TABLE IF EXISTS feed_dublin_cores CASCADE;
-DROP TABLE IF EXISTS item_dublin_cores;
+DROP TABLE IF EXISTS item_dublin_cores CASCADE;
-- +goose StatementEnd
diff --git a/sql/schema/20240215232318_itunes.sql b/sql/schema/20240215232318_itunes.sql
index 6bf75eb..5af00a3 100644
--- a/sql/schema/20240215232318_itunes.sql
+++ b/sql/schema/20240215232318_itunes.sql
@@ -10,9 +10,10 @@ CREATE TABLE IF NOT EXISTS feed_itunes (
-- From gofeed:
author TEXT,
"block" TEXT,
+ -- Categories - See feed_itunes_categories
"explicit" TEXT,
keywords TEXT,
- -- Owner
+ -- Owner - See feed_itunes_owners
subtitle TEXT,
summary TEXT,
"image" TEXT,
@@ -50,24 +51,25 @@ CREATE TABLE IF NOT EXISTS item_itunes (
CONSTRAINT fk_item_id FOREIGN KEY (item_id) REFERENCES items (id) ON DELETE CASCADE
);
--- Itunes categories
+-- Itunes categories for feeds
-- https://github.com/mmcdole/gofeed/blob/master/extensions/itunes.go#L39
-CREATE TABLE IF NOT EXISTS itunes_categories (
+CREATE TABLE IF NOT EXISTS feed_itunes_categories (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ DEFAULT NULL,
-- From gofeed:
"text" TEXT,
- subcategory TEXT,
+ subcategory BIGINT,
-- Link to itunes
itunes_id BIGINT NOT NULL,
- CONSTRAINT fk_itunes_id FOREIGN KEY (itunes_id) REFERENCES feed_itunes (id) ON DELETE CASCADE
+ CONSTRAINT fk_itunes_id FOREIGN KEY (itunes_id) REFERENCES feed_itunes (id) ON DELETE CASCADE,
+ CONSTRAINT fk_subcategory_id FOREIGN KEY (subcategory) REFERENCES feed_itunes_categories (id) ON DELETE SET NULL
);
-- Itunes owners
-- https://github.com/mmcdole/gofeed/blob/master/extensions/itunes.go#L45
-CREATE TABLE IF NOT EXISTS itunes_owners (
+CREATE TABLE IF NOT EXISTS feed_itunes_owners (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
@@ -83,12 +85,12 @@ CREATE TABLE IF NOT EXISTS itunes_owners (
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-DROP TABLE IF EXISTS feed_itunes;
+DROP TABLE IF EXISTS feed_itunes CASCADE;
-DROP TABLE IF EXISTS item_itunes;
+DROP TABLE IF EXISTS item_itunes CASCADE;
-DROP TABLE IF EXISTS itunes_categories;
+DROP TABLE IF EXISTS feed_itunes_categories CASCADE;
-DROP TABLE IF EXISTS itunes_owners;
+DROP TABLE IF EXISTS feed_itunes_owners CASCADE;
-- +goose StatementEnd
diff --git a/sql/schema/20240215232334_enclousures.sql b/sql/schema/20240215232334_enclousures.sql
index fce0c36..9fadd0f 100644
--- a/sql/schema/20240215232334_enclousures.sql
+++ b/sql/schema/20240215232334_enclousures.sql
@@ -1,6 +1,6 @@
-- +goose Up
-- +goose StatementBegin
--- Enclosures
+-- Enclosures - Only for items
-- https://github.com/mmcdole/gofeed/blob/master/feed.go#L86
CREATE TABLE IF NOT EXISTS enclosures (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
@@ -19,6 +19,6 @@ CREATE TABLE IF NOT EXISTS enclosures (
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-DROP TABLE IF EXISTS enclosures;
+DROP TABLE IF EXISTS enclosures CASCADE;
-- +goose StatementEnd