Browse Source

Episode updates

develop
Rob Colbert 2 months ago
parent
commit
fbd6cd8ff5
  1. 72
      src/app/controllers/episode.ts
  2. 10
      src/app/models/episode.ts
  3. 33
      src/app/services/episode.ts

72
src/app/controllers/episode.ts

@ -0,0 +1,72 @@
// app/controllers/episode.ts
// Copyright (C) 2025 DTP Technologies, LLC
// All Rights Reserved
import { NextFunction, Request, Response } from "express";
import { WebServer, WebController } from "../../lib/dtplib.js";
import { EpisodeService } from "../services/episode.js";
import { SidebarService } from "../services/sidebar.js";
export class EpisodeController extends WebController {
static get name(): string {
return "EpisodeController";
}
static get slug(): string {
return "episode";
}
constructor(server: WebServer) {
super(server, EpisodeController);
}
get route(): string {
return "/episode";
}
async start(): Promise<void> {
const sidebarService = this.getService<SidebarService>("sidebar");
const sidebarMiddleware = sidebarService.middleware();
this.router.get("/:episodeId", sidebarMiddleware, this.getView.bind(this));
this.router.get("/", sidebarMiddleware, this.getHome.bind(this));
}
async getView(
_req: Request,
res: Response,
next: NextFunction
): Promise<void> {
const episodeService = this.getService<EpisodeService>("episode");
try {
res.locals.recommended = await episodeService.getRecommended(3, [
res.locals.episode,
]);
res.render("episode/view");
} catch (error) {
this.log.error("failed to present the view", { error });
return next(error);
}
}
async getHome(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
const episodeService = this.getService<EpisodeService>("episode");
try {
res.locals.pagination = this.getPaginationParameters(req, 20);
res.locals.episodeLibrary = await episodeService.getLive(
res.locals.pagination
);
return res.render("episode/home");
} catch (error) {
this.log.error("failed to present the Home view", { error });
return next(error);
}
}
}
export default EpisodeController;

10
src/app/models/episode.ts

@ -1,4 +1,4 @@
// app/models/feed.ts
// app/models/episode.ts
// Copyright (C) 2025 DTP Technologies, LLC
// All Rights Reserved
@ -15,6 +15,10 @@ export enum EpisodeStatus {
Offline = "offline",
Removed = "removed",
}
/**
* Defines an Episode in DTP Newsroom.
*/
export interface IEpisode {
_id: Types.ObjectId; // MongoDB concern
__v: number; // MongoDB concern
@ -25,8 +29,8 @@ export interface IEpisode {
title: string;
description: string;
video?: IVideo | Types.ObjectId;
feedItems?: Array<IFeedItem | Types.ObjectId>;
video?: IVideo | Types.ObjectId;
}
export const EpisodeSchema = new Schema<IEpisode>({
@ -46,8 +50,8 @@ export const EpisodeSchema = new Schema<IEpisode>({
},
title: { type: String, required: true },
description: { type: String, required: true },
video: { type: Schema.ObjectId, ref: "Video" },
feedItems: { type: [Schema.ObjectId], ref: "FeedItem" },
video: { type: Schema.ObjectId, ref: "Video" },
});
export const Episode = model<IEpisode>("Episode", EpisodeSchema);

33
src/app/services/episode.ts

@ -118,6 +118,39 @@ export class EpisodeService extends DtpService {
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;

Loading…
Cancel
Save