You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
4.0 KiB
156 lines
4.0 KiB
// app/services/episode.ts
|
|
// Copyright (C) 2025 DTP Technologies, LLC
|
|
// All Rights Reserved
|
|
|
|
import mongoose, { Types } from "mongoose";
|
|
|
|
import Episode, { EpisodeStatus, IEpisode } from "../models/episode.js";
|
|
|
|
import {
|
|
DtpPlatform,
|
|
DtpService,
|
|
DtpServiceUpdate,
|
|
WebError,
|
|
WebPaginationParameters,
|
|
} from "../../lib/dtplib.js";
|
|
|
|
import TextService from "./text.js";
|
|
|
|
export interface EpisodeDefinition {
|
|
title: string;
|
|
description: string;
|
|
}
|
|
|
|
export interface EpisodeLibrary {
|
|
episodes: Array<IEpisode>;
|
|
totalEpisodeCount: number;
|
|
}
|
|
|
|
export class EpisodeService extends DtpService {
|
|
populateEpisode: Array<mongoose.PopulateOptions>;
|
|
|
|
static get name() {
|
|
return "EpisodeService";
|
|
}
|
|
static get slug() {
|
|
return "episode";
|
|
}
|
|
|
|
constructor(platform: DtpPlatform) {
|
|
super(platform, EpisodeService);
|
|
this.populateEpisode = [
|
|
{
|
|
path: "show",
|
|
},
|
|
{
|
|
path: "video",
|
|
},
|
|
{
|
|
path: "feedItems",
|
|
},
|
|
];
|
|
}
|
|
|
|
async create(definition: EpisodeDefinition): Promise<IEpisode> {
|
|
const textService = this.getService<TextService>("text");
|
|
const NOW = new Date();
|
|
|
|
const episode = new Episode();
|
|
episode.created = NOW;
|
|
episode.title = textService.filter(definition.title);
|
|
episode.description = textService.filter(definition.description);
|
|
await episode.save();
|
|
|
|
return episode.toObject();
|
|
}
|
|
|
|
async update(
|
|
episode: IEpisode | Types.ObjectId,
|
|
definition: EpisodeDefinition
|
|
): Promise<IEpisode> {
|
|
const textService = this.getService<TextService>("text");
|
|
|
|
const update: DtpServiceUpdate = {};
|
|
update.$set = {};
|
|
|
|
update.$set.title = textService.filter(definition.title);
|
|
update.$set.description = textService.filter(definition.description);
|
|
|
|
const newEpisode = await Episode.findByIdAndUpdate(episode._id, update, {
|
|
new: true,
|
|
populate: this.populateEpisode,
|
|
}).lean();
|
|
if (!newEpisode) {
|
|
throw new WebError(404, "show does not exist");
|
|
}
|
|
return newEpisode;
|
|
}
|
|
|
|
async getById(episodeId: Types.ObjectId): Promise<IEpisode | null> {
|
|
const show = await Episode.findById(episodeId)
|
|
.populate(this.populateEpisode)
|
|
.lean();
|
|
return show;
|
|
}
|
|
|
|
async getLive(pagination: WebPaginationParameters): Promise<EpisodeLibrary> {
|
|
const search = { status: EpisodeStatus.Live };
|
|
|
|
const episodes = await Episode.find(search)
|
|
.sort({ created: -1 })
|
|
.skip(pagination.skip)
|
|
.limit(pagination.cpp)
|
|
.populate(this.populateEpisode)
|
|
.lean();
|
|
|
|
const totalEpisodeCount = await Episode.countDocuments(search);
|
|
return { episodes, totalEpisodeCount };
|
|
}
|
|
|
|
async getAll(pagination: WebPaginationParameters): Promise<EpisodeLibrary> {
|
|
const episodes = await Episode.find()
|
|
.sort({ created: -1 })
|
|
.skip(pagination.skip)
|
|
.limit(pagination.cpp)
|
|
.populate(this.populateEpisode)
|
|
.lean();
|
|
|
|
const totalEpisodeCount = await Episode.estimatedDocumentCount();
|
|
return { episodes, totalEpisodeCount };
|
|
}
|
|
|
|
/**
|
|
* For now just fetches a sample of the live list minus any episodes specified
|
|
* for exclusion (such as the one(s) on the page right now).
|
|
* @param maxCount number The maximum number of episodes to return.
|
|
* @param exclude Array<IEpisode|mongoose.Types.ObjectId> Array of episodes to
|
|
* exclude from the result set.
|
|
* @returns An array of recommended episodes for display.
|
|
*/
|
|
async getRecommended(
|
|
maxCount: number,
|
|
exclude?: Array<IEpisode | Types.ObjectId>
|
|
): Promise<Array<IEpisode>> {
|
|
const pipeline: Array<mongoose.PipelineStage> = [];
|
|
|
|
if (exclude) {
|
|
if (!Array.isArray(exclude)) {
|
|
exclude = [exclude];
|
|
}
|
|
pipeline.push({
|
|
$match: {
|
|
_id: { $nin: exclude.map((show) => show._id) },
|
|
},
|
|
});
|
|
}
|
|
|
|
pipeline.push({
|
|
$sample: { size: maxCount },
|
|
});
|
|
|
|
const shows = await Episode.aggregate(pipeline);
|
|
return await Episode.populate(shows, this.populateEpisode);
|
|
}
|
|
}
|
|
|
|
export default EpisodeService;
|
|
|