From f73a36c1c895071c4e417e61ff1b25034e4d4c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Hells=C3=A9n?= Date: Sun, 18 Feb 2024 05:07:30 +0100 Subject: [PATCH] Add support for Itunes --- .vscode/settings.json | 1 + add_item.go | 6 + db/feeds.sql.go | 458 +++++++++++++++++++++ db/models.go | 40 +- feeds.go | 9 + handlers.go | 82 +++- itunes.go | 138 +++++++ sql/queries/feeds.sql | 154 +++++++ sql/schema/20240214043229_feeds.sql | 4 +- sql/schema/20240215232236_extensions.sql | 4 +- sql/schema/20240215232245_authors.sql | 4 +- sql/schema/20240215232251_images.sql | 4 +- sql/schema/20240215232259_dublin_cores.sql | 4 +- sql/schema/20240215232318_itunes.sql | 22 +- sql/schema/20240215232334_enclousures.sql | 4 +- 15 files changed, 883 insertions(+), 51 deletions(-) create mode 100644 itunes.go 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("") + 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("
  • \"Feed
  • ") @@ -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("") + } 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