Skip to content

Commit

Permalink
feat: adding R2 video source, chapters, & captions in preparation for…
Browse files Browse the repository at this point in the history
… Cloudflare R2 testing
  • Loading branch information
tomgobich committed Dec 28, 2024
1 parent 66edad6 commit 9fec009
Show file tree
Hide file tree
Showing 20 changed files with 582 additions and 8 deletions.
3 changes: 3 additions & 0 deletions app/actions/posts/destroy_post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export default class DestroyPost {
await post.related('collections').detach()
await post.related('taxonomies').detach()

await post.related('chapters').query().delete()
await post.related('captions').query().delete()

await this.#destroyComments(post, trx)
await this.#destroyHistory(post)
await this.#removeFromWatchlists(post)
Expand Down
3 changes: 3 additions & 0 deletions app/actions/posts/get_post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ export default class GetPost {
.where({ id })
.preload('thumbnails', (query) => query.orderBy('sort_order'))
.preload('covers', (query) => query.orderBy('sort_order'))
.preload('chapters', (query) => query.orderBy('sort_order'))
.preload('captions', (query) => query.orderBy('sort_order'))
.preload('taxonomies')

.firstOrFail()
}
}
7 changes: 6 additions & 1 deletion app/actions/posts/store_post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import db from '@adonisjs/lucid/services/db'
import { Infer } from '@vinejs/vine/types'
import SyncPostAsset from './sync_post_assets.js'
import SyncTaxonomies from '#actions/taxonomies/sync_taxonomies'
import SyncCaptions from './sync_captions.js'
import SyncChapters from './sync_chapters.js'

type Data = Infer<typeof postValidator>

Expand All @@ -20,7 +22,8 @@ export default class StorePost {
if (!data.stateId) data.stateId = States.DRAFT
if (!data.postTypeId) data.postTypeId = PostTypes.LESSON

const { thumbnail, publishAtDate, publishAtTime, taxonomyIds, ...store } = data
const { thumbnail, publishAtDate, publishAtTime, taxonomyIds, captions, chapters, ...store } =
data

return db.transaction(async (trx) => {
const post = await Post.create(store, { client: trx })
Expand All @@ -29,6 +32,8 @@ export default class StorePost {

await SyncTaxonomies.handle({ resource: post, ids: taxonomyIds })
await SyncPostAsset.handle({ post, asset: thumbnail }, trx)
await SyncCaptions.handle({ post, captions }, trx)
await SyncChapters.handle({ post, chapters }, trx)

return post
})
Expand Down
36 changes: 36 additions & 0 deletions app/actions/posts/sync_captions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Post from '#models/post'
import { postValidator } from '#validators/post'
import { TransactionClientContract } from '@adonisjs/lucid/types/database'
import { Infer } from '@vinejs/vine/types'

type Params = {
post: Post
captions: Infer<typeof postValidator>['captions']
}

export default class SyncCaptions {
static async handle({ post, captions }: Params, trx: TransactionClientContract) {
if (!captions?.length) {
await post.related('captions').query().delete()
return
}

const promises = captions.map(async (caption, index) => {
if (!caption.id) {
return post.related('captions').create(caption, { client: trx })
}

const row = await post.related('captions').query().where('id', caption.id).firstOrFail()

row.useTransaction(trx)
row.merge({
sortOrder: index,
...caption,
})

return row.save()
})

await Promise.all(promises)
}
}
36 changes: 36 additions & 0 deletions app/actions/posts/sync_chapters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Post from '#models/post'
import { postValidator } from '#validators/post'
import { TransactionClientContract } from '@adonisjs/lucid/types/database'
import { Infer } from '@vinejs/vine/types'

type Params = {
post: Post
chapters: Infer<typeof postValidator>['chapters']
}

export default class SyncChapters {
static async handle({ post, chapters }: Params, trx: TransactionClientContract) {
if (!chapters?.length) {
await post.related('chapters').query().delete()
return
}

const promises = chapters.map(async (chapter, index) => {
if (!chapter.id) {
return post.related('chapters').create(chapter, { client: trx })
}

const row = await post.related('chapters').query().where('id', chapter.id).firstOrFail()

row.useTransaction(trx)
row.merge({
sortOrder: index,
...chapter,
})

return row.save()
})

await Promise.all(promises)
}
}
7 changes: 6 additions & 1 deletion app/actions/posts/update_post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import db from '@adonisjs/lucid/services/db'
import { Infer } from '@vinejs/vine/types'
import SyncPostAsset from './sync_post_assets.js'
import SyncTaxonomies from '#actions/taxonomies/sync_taxonomies'
import SyncCaptions from './sync_captions.js'
import SyncChapters from './sync_chapters.js'

type Data = Infer<typeof postValidator>

Expand All @@ -25,7 +27,8 @@ export default class UpdatePost {
if (!data.stateId) data.stateId = States.DRAFT
if (!data.postTypeId) data.postTypeId = PostTypes.LESSON

const { thumbnail, publishAtDate, publishAtTime, taxonomyIds, ...update } = data
const { thumbnail, publishAtDate, publishAtTime, taxonomyIds, captions, chapters, ...update } =
data

post.merge(update)

Expand All @@ -39,6 +42,8 @@ export default class UpdatePost {
await post.save()
await SyncTaxonomies.handle({ resource: post, ids: taxonomyIds })
await SyncPostAsset.handle({ post, asset: thumbnail }, trx)
await SyncCaptions.handle({ post, captions }, trx)
await SyncChapters.handle({ post, chapters }, trx)

return post
})
Expand Down
6 changes: 6 additions & 0 deletions app/dtos/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import VideoTypes from '#enums/video_types'
import States from '#enums/states'
import PaywallTypes from '#enums/paywall_types'
import PostTypes from '#enums/post_types'
import PostChapterDto from './post_chapter.js'
import PostCaptionDto from './post_caption.js'

export default class PostDto extends BaseModelDto {
declare id: number
Expand Down Expand Up @@ -69,6 +71,8 @@ export default class PostDto extends BaseModelDto {
declare viewHistory: HistoryDto[]
declare progressionHistory: ProgressDto[]
declare watchlist: WatchlistDto[]
declare chapters: PostChapterDto[]
declare captions: PostCaptionDto[]

declare publishAtISO: string | null
declare publishAtDisplay: string
Expand Down Expand Up @@ -146,6 +150,8 @@ export default class PostDto extends BaseModelDto {
this.viewHistory = HistoryDto.fromArray(post.viewHistory)
this.progressionHistory = ProgressDto.fromArray(post.progressionHistory)
this.watchlist = WatchlistDto.fromArray(post.watchlist)
this.chapters = PostChapterDto.fromArray(post.chapters)
this.captions = PostCaptionDto.fromArray(post.captions)

this.publishAtISO = post.publishAtISO
this.publishAtDisplay = post.publishAtDisplay
Expand Down
34 changes: 34 additions & 0 deletions app/dtos/post_caption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { BaseModelDto } from '@adocasts.com/dto/base'
import PostCaption from '#models/post_caption'
import CaptionTypes from '#enums/caption_types'
import CaptionLanguages from '#enums/caption_languages'
import PostDto from '#dtos/post'

export default class PostCaptionDto extends BaseModelDto {
declare id: number
declare postId: number
declare type: CaptionTypes
declare label: string
declare language: CaptionLanguages
declare filename: string
declare sortOrder: number
declare createdAt: string
declare updatedAt: string
declare post: PostDto | null

constructor(postCaption?: PostCaption) {
super()

if (!postCaption) return
this.id = postCaption.id
this.postId = postCaption.postId
this.type = postCaption.type
this.label = postCaption.label
this.language = postCaption.language
this.filename = postCaption.filename
this.sortOrder = postCaption.sortOrder
this.createdAt = postCaption.createdAt.toISO()!
this.updatedAt = postCaption.updatedAt.toISO()!
this.post = postCaption.post && new PostDto(postCaption.post)
}
}
34 changes: 34 additions & 0 deletions app/dtos/post_chapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { BaseModelDto } from '@adocasts.com/dto/base'
import PostChapter from '#models/post_chapter'
import PostDto from '#dtos/post'

export default class PostChapterDto extends BaseModelDto {
declare id: number
declare postId: number
declare start: string
declare end: string
declare text: string
declare sortOrder: number
declare createdAt: string
declare updatedAt: string
declare post: PostDto | null
declare startSeconds: number
declare endSeconds: number

constructor(postChapter?: PostChapter) {
super()

if (!postChapter) return
this.id = postChapter.id
this.postId = postChapter.postId
this.start = postChapter.start
this.end = postChapter.end
this.text = postChapter.text
this.sortOrder = postChapter.sortOrder
this.createdAt = postChapter.createdAt.toISO()!
this.updatedAt = postChapter.updatedAt.toISO()!
this.post = postChapter.post && new PostDto(postChapter.post)
this.startSeconds = postChapter.startSeconds
this.endSeconds = postChapter.endSeconds
}
}
36 changes: 36 additions & 0 deletions app/dtos/post_form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ import VideoTypes from '#enums/video_types'
import States from '#enums/states'
import PaywallTypes from '#enums/paywall_types'
import PostTypes from '#enums/post_types'
import CaptionTypes from '#enums/caption_types'
import CaptionLanguages from '#enums/caption_languages'

type Caption = {
id?: number
type: CaptionTypes
language: CaptionLanguages
label: string
}

type Chapter = {
id?: number
start: string
end: string
text: string
}

export default class PostFormDto extends BaseModelDto {
declare id: number
Expand Down Expand Up @@ -38,6 +54,8 @@ export default class PostFormDto extends BaseModelDto {
declare thumbnail: AssetDto | null
declare cover: AssetDto | null
declare taxonomyIds: number[]
declare captions: Caption[] | null
declare chapters: Chapter[] | null

constructor(post?: Post) {
super()
Expand Down Expand Up @@ -74,5 +92,23 @@ export default class PostFormDto extends BaseModelDto {
this.thumbnail = post.thumbnails.length ? new AssetDto(post.thumbnails[0]) : null
this.cover = post.covers.length ? new AssetDto(post.covers[0]) : null
this.taxonomyIds = post.taxonomies?.map((row) => row.id) ?? []

if (post.captions?.length) {
this.captions = post.captions.map((row) => ({
id: row.id,
type: row.type,
language: row.language,
label: row.label,
}))
}

if (post.chapters?.length) {
this.chapters = post.chapters.map((row) => ({
id: row.id,
start: row.start,
end: row.end,
text: row.text,
}))
}
}
}
17 changes: 17 additions & 0 deletions app/enums/caption_languages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
enum CaptionLanguages {
ENGLISH = 'en',
SPANISH = 'es',
FRENCH = 'fr',
GERMAN = 'de',
PORTUGUESE = 'pt',
}

export const CaptionLanguageDesc: Record<CaptionLanguages, string> = {
[CaptionLanguages.ENGLISH]: 'English',
[CaptionLanguages.SPANISH]: 'Spanish',
[CaptionLanguages.FRENCH]: 'French',
[CaptionLanguages.GERMAN]: 'German',
[CaptionLanguages.PORTUGUESE]: 'Portuguese',
}

export default CaptionLanguages
11 changes: 11 additions & 0 deletions app/enums/caption_types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
enum CaptionTypes {
SRT = 'srt',
VTT = 'vtt',
}

export const CaptionTypeDesc: Record<CaptionTypes, string> = {
[CaptionTypes.SRT]: 'SRT',
[CaptionTypes.VTT]: 'VTT',
}

export default CaptionTypes
8 changes: 8 additions & 0 deletions app/models/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import SlugService from '#services/slug_service'
import router from '@adonisjs/core/services/router'
import Progress from './progress.js'
import PostTypes from '#enums/post_types'
import PostCaption from './post_caption.js'
import PostChapter from './post_chapter.js'

export default class Post extends AppBaseModel {
serializeExtras = true
Expand Down Expand Up @@ -164,6 +166,12 @@ export default class Post extends AppBaseModel {
@hasMany(() => Comment)
declare comments: HasMany<typeof Comment>

@hasMany(() => PostCaption)
declare captions: HasMany<typeof PostCaption>

@hasMany(() => PostChapter)
declare chapters: HasMany<typeof PostChapter>

@manyToMany(() => User, {
pivotTable: 'author_posts',
pivotColumns: ['author_type_id'],
Expand Down
Loading

0 comments on commit 9fec009

Please sign in to comment.