From 855343dfc3d004b09b1683d37ff7017c398966b7 Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Fri, 29 May 2026 07:18:06 -0400 Subject: [PATCH] Started work on archived media collection. --- src/app/channel/media/archivedMedia.js | 113 ++++++++++++++++++ .../channel/media/queueBroadcastManager.js | 2 +- .../channel/media/archivedMediaSchema.js | 80 +++++++++++++ 3 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 src/app/channel/media/archivedMedia.js create mode 100644 src/schemas/channel/media/archivedMediaSchema.js diff --git a/src/app/channel/media/archivedMedia.js b/src/app/channel/media/archivedMedia.js new file mode 100644 index 0000000..1f5df94 --- /dev/null +++ b/src/app/channel/media/archivedMedia.js @@ -0,0 +1,113 @@ +/*Canopy - The next generation of stoner streaming software +Copyright (C) 2024-2025 Rainbownapkin and the TTN Community + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see .*/ + +//Local Imports +const media = require('./media'); +const queuedMedia = require('./queuedMedia'); + +/** + * Class extending media which represents a queued piece of media + * @extends media + */ +class archivedMedia extends queuedMedia{ + /** + * Creates a new queued media object + * @param {String} channel - Channel where object was queued + */ + constructor(title, fileName, url, id, type, duration, rawLink, startTime, startTimeStamp = 0, earlyEnd, uuid, channel){ + //Call derived constructor + super(title, fileName, url, id, type, duration, rawLink, startTime, startTimeStamp, earlyEnd, uuid); + + /** + * Channel media was queued + */ + this.channel = channel; + + /** + * Media status type + */ + this.status = 'archived'; + } + + //statics + /** + * Creates a archivedMedia object from a media object + * @param {String} channel - Channel where object was queued + * @returns {archivedMedia} queuedMedia object created from given media object + */ + static fromMedia(media, startTime, startTimeStamp, channel){ + //Create and return queuedMedia object from given media object and arguments + return new this( + media.title, + media.fileName, + media.url, + media.id, + media.type, + media.duration, + media.rawLink, + startTime, + startTimeStamp, + null, + null, + channel); + } + + /** + * Converts array of media objects into array of archivedMedia objects + * @param {String} channel - Channel where object was queued + * @returns Array of converted queued media objects + */ + static fromMediaArray(mediaList, start, channel){ + //Queued Media List + const archivedMediaList = []; + //Start Time Offset + let startOffset = 0; + + for(let media of mediaList){ + //Convert mediaObj to queuedMedia and push to the back of the list + archivedMediaList.push(this.fromMedia(media, start + startOffset, 0, channel)); + + //Set start offset to end of the current item + startOffset += (media.duration * 1000) + 5; + } + + return archivedMediaList; + } + + //methods + /** + * Generates a unique clone of a given media object + * @returns unique clone of media object + */ + clone(){ + return new archivedMedia( + this.title, + this.fileName, + this.url, + this.id, + this.type, + this.duration, + this.rawLink, + this.startTime, + this.startTimeStamp, + this.earlyEnd, + null, + this.channel + ); + } +} + +module.exports = archivedMedia; \ No newline at end of file diff --git a/src/app/channel/media/queueBroadcastManager.js b/src/app/channel/media/queueBroadcastManager.js index 61e0497..7e07d95 100644 --- a/src/app/channel/media/queueBroadcastManager.js +++ b/src/app/channel/media/queueBroadcastManager.js @@ -21,7 +21,7 @@ const loggerUtils = require("../../../utils/loggerUtils"); const channelModel = require("../../../schemas/channel/channelSchema"); /** - * Class containg global server-side private message relay logic + * Class containg per-channel server-side queue broadcasting logic * * Exists to make broadcasting channel queues to groups of authenticated users with the 'read-queue' perm as painless as possible, * reducing DB call/perm checks to just connection time, and not requireing any out-of-library user iteration at broadcast time. diff --git a/src/schemas/channel/media/archivedMediaSchema.js b/src/schemas/channel/media/archivedMediaSchema.js new file mode 100644 index 0000000..115d45d --- /dev/null +++ b/src/schemas/channel/media/archivedMediaSchema.js @@ -0,0 +1,80 @@ +/*Canopy - The next generation of stoner streaming software +Copyright (C) 2024-2025 Rainbownapkin and the TTN Community + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see .*/ + +//NPM Imports +const {mongoose} = require('mongoose'); + +//Local Imports +const mediaSchema = require('./mediaSchema'); +const archivedMedia = require('../../../app/channel/media/archivedMedia'); + +/** + * DB Schema for documents representing a queued media object + */ +const archivedProperties = new mongoose.Schema({ + channel: { + type: mongoose.SchemaTypes.String, + required: true, + }, + startTime: { + type: mongoose.SchemaTypes.Number, + required: true, + }, + startTimeStamp: { + type: mongoose.SchemaTypes.Number, + required: false, + }, + earlyEnd: { + type: mongoose.SchemaTypes.Number, + required: false, + }, + uuid: { + type: mongoose.SchemaTypes.UUID, + required: true, + }, +}, +{ + discriminatorKey: 'status' +}); + +//Methods +/** + * Rehydrate to a full phat archived media object + * @returns {archivedMedia} A full phat archived media object, re-hydrated from the DB + */ +archivedProperties.methods.rehydrate = function(){ + return new archivedMedia( + this.title, + this.fileName, + this.url, + this.id, + this.type, + this.duration, + //We don't save raw links that are stored seperate from the standard URL as they tend to expire. + undefined, + this.startTime, + this.startTimeStamp, + this.earlyEnd, + this.uuid.toString(), + this.channel + ); +} + +//Create 'archivedMediaSchema' as descriminator of mediaSchema +var archivedMediaSchema = mediaSchema.discriminator('archived', archivedProperties); + +//Export mongoose model based on archivedSchema +module.exports = mongoose.model("archivedMedia", archivedMediaSchema); \ No newline at end of file