Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add itunesSeason, itunesEpisode, and itunesEpisodeType columns #664

Merged
merged 7 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions migrations/0048_seasons.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ALTER TABLE ONLY public."episodes"
ADD COLUMN "itunesEpisode" integer;

ALTER TABLE ONLY public."episodes"
ADD COLUMN "itunesEpisodeType" varchar;

ALTER TABLE ONLY public."episodes"
ADD COLUMN "itunesSeason" integer;

CREATE INDEX CONCURRENTLY "episodes_itunesEpisode" ON "episodes" ("itunesEpisode");

CREATE INDEX CONCURRENTLY "episodes_itunesEpisodeType" ON "episodes" ("itunesEpisodeType");

CREATE INDEX CONCURRENTLY "episodes_itunesSeason" ON "episodes" ("itunesSeason");
7 changes: 7 additions & 0 deletions migrations/0049_podcast_itunesFeedType_hasSeasons.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TABLE ONLY public."podcasts"
ADD COLUMN "itunesFeedType" varchar;

CREATE INDEX CONCURRENTLY "podcasts_itunesFeedType" ON "podcasts" ("itunesFeedType");

ALTER TABLE ONLY public."podcasts"
ADD COLUMN "hasSeasons" boolean DEFAULT false NOT NULL;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@
"paypal-rest-sdk": "2.0.0-rc.2",
"pg": "8.7.3",
"podcast-partytime": "4.6.2",
"podverse-shared": "^4.12.10",
"podverse-shared": "^4.13.14",
"reflect-metadata": "0.1.13",
"request": "^2.88.2",
"request-promise-native": "1.0.8",
Expand Down
63 changes: 61 additions & 2 deletions src/controllers/episode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { validateSearchQueryString } from '~/lib/utility/validation'
import { manticoreWildcardSpecialCharacters, searchApi } from '~/services/manticore'
import { liveItemStatuses } from './liveItem'
import { createMediaRef, updateMediaRef } from './mediaRef'
import { getPodcast } from './podcast'
const createError = require('http-errors')
const SqlString = require('sqlstring')
const { superUserId } = config
Expand All @@ -21,6 +22,8 @@ const relations = [
'podcast.categories'
]

const maxResultsEpisodes = 5000

const getEpisode = async (id) => {
const repository = getRepository(Episode)
const episode = await repository.findOne(
Expand Down Expand Up @@ -118,6 +121,9 @@ const addSelectsToQueryBuilder = (qb) => {
.addSelect('episode.imageUrl')
.addSelect('episode.isExplicit')
.addSelect('episode.isPublic')
.addSelect('episode.itunesEpisode')
.addSelect('episode.itunesEpisodeType')
.addSelect('episode.itunesSeason')
.addSelect('episode.linkUrl')
.addSelect('episode.mediaFilesize')
.addSelect('episode.mediaType')
Expand Down Expand Up @@ -317,6 +323,47 @@ const getEpisodesByPodcastId = async (query, qb, podcastIds) => {
return handleGetEpisodesWithOrdering({ maxResults, qb, query, skip, sort, take }, allowRandom, shouldLimitCount)
}

// When a podcast has seasons, we always return all the episodes,
// and in the order the podcaster intended.
// If the podcast has serial type, then return in chronological order
// instead of the default most-recent order.
const getEpisodesByPodcastIdWithSeasons = async ({
searchTitle,
sincePubDate,
hasVideo,
itunesFeedType,
podcastId
}) => {
const includePodcast = false
const shouldUseEpisodesMostRecent = false
const liveItemStatus = null
const qb = generateEpisodeSelects(
includePodcast,
searchTitle,
sincePubDate,
hasVideo,
shouldUseEpisodesMostRecent,
liveItemStatus
)

const isSerial = itunesFeedType === 'serial'
const sort = isSerial ? 'oldest' : 'most-recent'
const seasonsQuery = {
podcastId,
maxResults: maxResultsEpisodes,
searchTitle,
sort
}

const resultsArray = await getEpisodesByPodcastId(seasonsQuery, qb, [podcastId])
resultsArray.push({
hasSeasons: true,
isSerial
})

return resultsArray
}

const getEpisodesByPodcastIds = async (query) => {
const {
hasVideo,
Expand All @@ -343,7 +390,19 @@ const getEpisodesByPodcastIds = async (query) => {
)

if (podcastIds.length === 1) {
return getEpisodesByPodcastId(query, qb, podcastIds)
const id = podcastIds[0]
const podcast = await getPodcast(id)
if (podcast?.hasSeasons) {
return getEpisodesByPodcastIdWithSeasons({
searchTitle,
sincePubDate,
hasVideo,
itunesFeedType: podcast.itunesFeedType,
podcastId
})
} else {
return getEpisodesByPodcastId(query, qb, podcastIds)
}
}

qb.andWhere('episode.podcastId IN(:...podcastIds)', { podcastIds })
Expand All @@ -363,7 +422,7 @@ const handleGetEpisodesWithOrdering = async (
totalOverride?
) => {
const { maxResults, skip, sort, take } = obj
const finalTake = maxResults ? 5000 : take
const finalTake = maxResults ? maxResultsEpisodes : take

let { qb } = obj
qb.offset(skip)
Expand Down
2 changes: 2 additions & 0 deletions src/controllers/podcast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,12 @@ const getPodcasts = async (query, countOverride?, isFromManticoreSearch?) => {
.addSelect('podcast.feedLastUpdated')
.addSelect('podcast.funding')
.addSelect('podcast.hasLiveItem')
.addSelect('podcast.hasSeasons')
.addSelect('podcast.hasVideo')
.addSelect('podcast.hideDynamicAdsWarning')
.addSelect('podcast.imageUrl')
.addSelect('podcast.isExplicit')
.addSelect('podcast.itunesFeedType')
.addSelect('podcast.lastEpisodePubDate')
.addSelect('podcast.lastEpisodeTitle')
.addSelect('podcast.lastFoundInPodcastIndex')
Expand Down
12 changes: 11 additions & 1 deletion src/controllers/userHistoryItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ export const generateGetUserItemsQuery = (table, tableName, loggedInUserId) => {
.addSelect('episode.funding', 'episodeFunding')
.addSelect('episode.guid', 'episodeGuid')
.addSelect('episode.imageUrl', 'episodeImageUrl')
.addSelect('episode.itunesEpisode', 'episodeItunesEpisode')
.addSelect('episode.itunesEpisodeType', 'episodeItunesEpisodeType')
.addSelect('episode.itunesSeason', 'episodeItunesSeason')
.addSelect('episode.mediaType', 'episodeMediaType')
.addSelect('episode.mediaUrl', 'episodeMediaUrl')
.addSelect('episode.pubDate', 'episodePubDate')
Expand All @@ -127,10 +130,12 @@ export const generateGetUserItemsQuery = (table, tableName, loggedInUserId) => {
.addSelect('episode.transcript', 'episodeTranscript')
.addSelect('episode.value', 'episodeValue')
.addSelect('podcast.funding', 'podcastFunding')
.addSelect('podcast.hasSeasons', 'podcastHasSeasons')
.addSelect('podcast.id', 'podcastId')
.addSelect('podcast.imageUrl', 'podcastImageUrl')
.addSelect('podcast.podcastIndexId', 'podcastPodcastIndexId')
.addSelect('podcast.podcastGuid', 'podcastGuid')
.addSelect('podcast.itunesFeedType', 'podcastItunesFeedType')
.addSelect('podcast.shrunkImageUrl', 'podcastShrunkImageUrl')
.addSelect('podcast.title', 'podcastTitle')
.addSelect('podcast.value', 'podcastValue')
Expand All @@ -143,6 +148,9 @@ export const generateGetUserItemsQuery = (table, tableName, loggedInUserId) => {
.addSelect('clipEpisode.funding', 'clipEpisodeFunding')
.addSelect('clipEpisode.guid', 'clipEpisodeGuid')
.addSelect('clipEpisode.imageUrl', 'clipEpisodeImageUrl')
.addSelect('clipEpisode.itunesEpisode', 'clipEpisodeItunesEpisode')
.addSelect('clipEpisode.itunesEpisodeType', 'clipEpisodeItunesEpisodeType')
.addSelect('clipEpisode.itunesSeason', 'clipEpisodeItunesSeason')
.addSelect('clipEpisode.mediaType', 'clipEpisodeMediaType')
.addSelect('clipEpisode.mediaUrl', 'clipEpisodeMediaUrl')
.addSelect('clipEpisode.pubDate', 'clipEpisodePubDate')
Expand All @@ -153,9 +161,11 @@ export const generateGetUserItemsQuery = (table, tableName, loggedInUserId) => {
.addSelect('clipEpisode.value', 'clipEpisodeValue')
.addSelect('clipPodcast.id', 'clipPodcastId')
.addSelect('clipPodcast.funding', 'clipPodcastFunding')
.addSelect('clipPodcast.hasSeasons', 'clipPodcastHasSeasons')
.addSelect('clipPodcast.imageUrl', 'clipPodcastImageUrl')
.addSelect('clipPodcast.podcastIndexId', 'clipPodcastIndexId')
.addSelect('clipPodcast.itunesFeedType', 'clipPodcastItunesFeedType')
.addSelect('clipPodcast.podcastGuid', 'clipPodcastGuid')
.addSelect('clipPodcast.podcastIndexId', 'clipPodcastIndexId')
.addSelect('clipPodcast.shrunkImageUrl', 'clipPodcastShrunkImageUrl')
.addSelect('clipPodcast.title', 'clipPodcastTitle')
.addSelect('clipPodcast.value', 'clipPodcastValue')
Expand Down
12 changes: 12 additions & 0 deletions src/entities/episode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,18 @@ export class Episode {
@Column({ default: false })
isPublic: boolean

@Index()
@Column({ nullable: true })
itunesEpisode?: number

@Index()
@Column({ nullable: true })
itunesEpisodeType?: string

@Index()
@Column({ nullable: true })
itunesSeason?: number

@ValidateIf((a) => a.linkUrl != null)
@IsUrl()
@Column({ nullable: true })
Expand Down
8 changes: 7 additions & 1 deletion src/entities/episodes_most_recent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@ export class EpisodeMostRecent {
credentialsRequired?: boolean

@ViewColumn()
description?: string
itunesEpisode?: number

@ViewColumn()
itunesEpisodeType?: string

@ViewColumn()
itunesSeason?: number

@ViewColumn()
duration?: number
Expand Down
8 changes: 8 additions & 0 deletions src/entities/podcast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ type ValueRecipient = {
type: string
}

export const podcastItunesTypeDefaultValue = 'episodic'

@Index(['hasVideo', 'pastAllTimeTotalUniquePageviews'])
@Index(['hasVideo', 'pastHourTotalUniquePageviews'])
@Index(['hasVideo', 'pastDayTotalUniquePageviews'])
Expand Down Expand Up @@ -110,6 +112,9 @@ export class Podcast {
@Column({ default: false })
hasPodcastIndexValueTag?: boolean

@Column({ default: false })
hasSeasons: boolean

@Column({ default: false })
hasVideo: boolean

Expand All @@ -128,6 +133,9 @@ export class Podcast {
@Column({ default: false })
isPublic: boolean

@Column({ default: podcastItunesTypeDefaultValue })
itunesFeedType: string

@Column({ nullable: true })
language?: string

Expand Down
32 changes: 32 additions & 0 deletions src/services/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { config } from '~/config'
import { updateSoundBites } from '~/controllers/mediaRef'
import { getPodcast } from '~/controllers/podcast'
import { Author, Category, Episode, FeedUrl, LiveItem, Podcast } from '~/entities'
import { podcastItunesTypeDefaultValue } from '~/entities/podcast'
import type { Value } from '~/entities/podcast'
import {
_logEnd,
Expand Down Expand Up @@ -193,6 +194,7 @@ export const parseFeedUrl = async (feedUrl, forceReparsing = false, cacheBust =
generator: feed.generator,
guid: feed.guid,
imageURL: feed.itunesImage || feed.image?.url,
itunesType: feed.itunesType || podcastItunesTypeDefaultValue,
language: feed.language,
lastBuildDate: feed.lastBuildDate,
link: feed.link,
Expand Down Expand Up @@ -222,6 +224,9 @@ export const parseFeedUrl = async (feedUrl, forceReparsing = false, cacheBust =
// funding: Array.isArray(episode.podcastFunding) ? episode.podcastFunding?.map((f) => fundingCompat(f)) : [],
guid: episode.guid,
imageURL: episode.image,
itunesEpisode: episode.itunesEpisode,
itunesEpisodeType: episode.itunesEpisodeType,
itunesSeason: episode.itunesSeason,
link: episode.link,
pubDate: episode.pubDate,
socialInteraction: episode.podcastSocialInteraction ?? [],
Expand Down Expand Up @@ -372,6 +377,7 @@ export const parseFeedUrl = async (feedUrl, forceReparsing = false, cacheBust =

podcast.isExplicit = meta.explicit
podcast.isPublic = true
podcast.itunesFeedType = meta.itunesType
podcast.language = meta.language

/*
Expand Down Expand Up @@ -415,6 +421,7 @@ export const parseFeedUrl = async (feedUrl, forceReparsing = false, cacheBust =
logPerformance('findOrGenerateParsedLiveItems', _logEnd)

podcast.hasLiveItem = hasLiveItem
podcast.hasSeasons = episodesResults.hasSeasons || liveItemsResults.hasSeasons
podcast.hasVideo = episodesResults.hasVideo || liveItemsResults.hasVideo

newEpisodes = episodesResults.newEpisodes
Expand Down Expand Up @@ -942,6 +949,11 @@ const assignParsedEpisodeData = async (
episode.guid = parsedEpisode.guid || parsedEpisode.enclosure.url
episode.imageUrl = parsedEpisode.imageURL
episode.isExplicit = parsedEpisode.explicit

episode.itunesEpisode = parsedEpisode.itunesEpisode
episode.itunesEpisodeType = parsedEpisode.itunesEpisodeType
episode.itunesSeason = parsedEpisode.itunesSeason

episode.linkUrl = parsedEpisode.link

episode.mediaType = parsedEpisode.enclosure.type
Expand Down Expand Up @@ -1043,6 +1055,7 @@ const findOrGenerateParsedLiveItems = async (parsedLiveItems, podcast, pvEpisode
/* If a feed has more video episodes than audio episodes, mark it as a hasVideo podcast. */
let videoCount = 0
let audioCount = 0
let hasSeasons = false

// If episode is already saved, then merge the matching episode found in
// the parsed object with what is already saved.
Expand All @@ -1058,6 +1071,10 @@ const findOrGenerateParsedLiveItems = async (parsedLiveItems, podcast, pvEpisode
pvEpisodesValueTagsByGuid
)

if (parsedLiveItem.itunesSeason) {
hasSeasons = true
}

if (parsedLiveItem.mediaType && checkIfVideoMediaType(parsedLiveItem.mediaType)) {
videoCount++
} else {
Expand All @@ -1075,6 +1092,10 @@ const findOrGenerateParsedLiveItems = async (parsedLiveItems, podcast, pvEpisode
let episode = new Episode() as ExtendedEpisode
episode = await assignParsedEpisodeData(episode, newParsedLiveItem, podcast, pvEpisodesValueTagsByGuid)

if (newParsedLiveItem.itunesSeason) {
hasSeasons = true
}

if (newParsedLiveItem.mediaType && checkIfVideoMediaType(newParsedLiveItem.mediaType)) {
videoCount++
} else {
Expand Down Expand Up @@ -1116,6 +1137,7 @@ const findOrGenerateParsedLiveItems = async (parsedLiveItems, podcast, pvEpisode
return {
newLiveItems,
updatedSavedLiveItems,
hasSeasons,
hasVideo: videoCount > audioCount,
liveItemNotificationsData
}
Expand Down Expand Up @@ -1186,13 +1208,18 @@ const findOrGenerateParsedEpisodes = async (parsedEpisodes, podcast, pvEpisodesV
/* If a feed has more video episodes than audio episodes, mark it as a hasVideo podcast. */
let videoCount = 0
let audioCount = 0
let hasSeasons = false

// If episode is already saved, then merge the matching episode found in
// the parsed object with what is already saved.
for (let existingEpisode of savedEpisodes) {
const parsedEpisode = validParsedEpisodes.find((x) => x.guid === existingEpisode.guid)
existingEpisode = await assignParsedEpisodeData(existingEpisode, parsedEpisode, podcast, pvEpisodesValueTagsByGuid)

if (existingEpisode.itunesSeason) {
hasSeasons = true
}

if (existingEpisode.mediaType && checkIfVideoMediaType(existingEpisode.mediaType)) {
videoCount++
} else {
Expand All @@ -1210,6 +1237,10 @@ const findOrGenerateParsedEpisodes = async (parsedEpisodes, podcast, pvEpisodesV
let episode = new Episode() as ExtendedEpisode
episode = await assignParsedEpisodeData(episode, newParsedEpisode, podcast, pvEpisodesValueTagsByGuid)

if (newParsedEpisode.itunesSeason) {
hasSeasons = true
}

if (newParsedEpisode.mediaType && checkIfVideoMediaType(newParsedEpisode.mediaType)) {
videoCount++
} else {
Expand All @@ -1224,6 +1255,7 @@ const findOrGenerateParsedEpisodes = async (parsedEpisodes, podcast, pvEpisodesV
return {
newEpisodes,
updatedSavedEpisodes,
hasSeasons,
hasVideo: videoCount > audioCount
}
}
Expand Down
Loading