feat: initial session management

This commit is contained in:
Ante Brähler
2024-12-26 21:20:19 +01:00
parent edb6c53a0f
commit 672890974d
8 changed files with 154 additions and 8 deletions

View File

@@ -4,6 +4,7 @@ class Channel {
this.id = Channel.nextId++;
this.name = name;
this.url = url;
this.sessionProvider = null;
this.avatar = avatar;
this.mode = mode;
this.headers = headers;

View File

@@ -38,7 +38,7 @@ const PORT = 5000;
const server = app.listen(PORT, () => {
console.log(`Server listening on Port ${PORT}`);
if (ChannelService.getCurrentChannel().restream()) {
streamController.start(process.env.DEFAULT_CHANNEL_URL);
streamController.start(ChannelService.getCurrentChannel());
}
});

View File

@@ -55,11 +55,11 @@ class ChannelService {
if (this.currentChannel !== nextChannel) {
if (nextChannel.restream()) {
streamController.stop(this.currentChannel.id);
streamController.stop(nextChannel.id);
streamController.stop(this.currentChannel);
streamController.stop(nextChannel);
streamController.start(nextChannel);
} else {
streamController.stop(this.currentChannel.id);
streamController.stop(this.currentChannel);
}
this.currentChannel = nextChannel;
}
@@ -84,7 +84,7 @@ class ChannelService {
if (this.currentChannel.id === id) {
if (deletedChannel.restream()) {
streamController.stop(deletedChannel.id);
streamController.stop(deletedChannel);
}
this.currentChannel = this.channels.length > 0 ? this.channels[0] : null;
@@ -112,7 +112,7 @@ class ChannelService {
if (this.currentChannel.id == id) {
if (streamChanged) {
streamController.stop(channel.id);
streamController.stop(channel);
if (channel.restream()) {
streamController.start(channel);
}

View File

@@ -13,6 +13,13 @@ function startFFmpeg(nextChannel) {
}
const channelUrl = nextChannel.url;
if(nextChannel.sessionProvider) {
const sessionQuery = nextChannel.sessionProvider.getSessionQuery();
const querySeparator = channelUrl.includes('?') ? '&' : '?';
channelUrl += `${querySeparator}${sessionQuery}`;
}
currentChannelId = nextChannel.id;
const headers = nextChannel.headers;

View File

@@ -1,19 +1,31 @@
const ffmpegService = require('./FFmpegService');
const storageService = require('./StorageService');
const SessionFactory = require('../session/SessionFactory');
function start(nextChannel) {
storageService.createChannelStorage(nextChannel.id);
if (!ffmpegService.isFFmpegRunning()) {
nextChannel.sessionProvider = SessionFactory.getSessionProvider(nextChannel.url);
if(nextChannel.sessionProvider) {
nextChannel.sessionProvider.createSession(nextChannel);
}
ffmpegService.startFFmpeg(nextChannel);
}
}
function stop(channelId) {
function stop(channel) {
if (ffmpegService.isFFmpegRunning()) {
ffmpegService.stopFFmpeg();
}
storageService.deleteChannelStorage(channelId);
if (channel.sessionProvider) {
channel.sessionProvider.destroySession();
nextChannel.sessionProvider = null;
}
storageService.deleteChannelStorage(channel.id);
}
module.exports = {

View File

@@ -0,0 +1,12 @@
class SessionFactory {
static getSessionProvider(channelDomain) {
switch (true) {
case channelDomain.includes('vipstreams.in'): //StreamedSU
return new StreamedSuSession('https://secure.embedme.top');
default:
return null;
}
}
}
module.exports = new SessionFactory;

View File

@@ -0,0 +1,21 @@
class SessionHandler {
constructor() {
if (this.constructor === SessionHandler) {
throw new Error("Abstract class cannot be instantiated");
}
}
async createSession(url, interval) {
throw new Error("Method 'startSession()' must be implemented");
}
destroySession() {
throw new Error("Method 'destroySession()' must be implemented");
}
getSessionQuery() {
throw new Error("Method 'getSessionQuery()' must be implemented");
}
}
module.exports = SessionHandler;

View File

@@ -0,0 +1,93 @@
class StreamedSuSession extends SessionHandler {
constructor(baseUrl) {
super();
this.baseUrl = baseUrl;
this.checkInterval = null;
this.sessionData = null;
}
async #initSession(url) {
try {
const response = await fetch(`${this.baseUrl}/init-session`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
path: new URL(url).pathname,
})
});
if (!response.ok) {
throw new Error('Failed to initialize session');
}
this.sessionData = await response.json();
return this.sessionData;
} catch (error) {
console.error('Session initialization failed:', error);
throw error;
}
}
async #checkSession() {
if (!this.sessionData?.id) {
return false;
}
try {
const response = await fetch(`${this.baseUrl}/check/${this.sessionData.id}`);
return response.status === 200;
} catch (error) {
console.error('Session check failed:', error);
return false;
}
}
#startAutoCheck(interval = 15000) {
if (this.checkInterval) {
this.stopAutoCheck();
}
this.checkInterval = setInterval(async () => {
const isValid = await this.#checkSession();
if (!isValid) {
window.location.reload();
}
}, interval);
}
#stopAutoCheck() {
if (this.checkInterval) {
clearInterval(this.checkInterval);
this.checkInterval = null;
}
}
// Public Methods
async createSession(url, interval = 15000) {
if (!this.sessionData) {
await this.#initSession(url);
}
this.#startAutoCheck(interval);
return this.getSessionQuery();
}
destroySession() {
this.#stopAutoCheck();
this.sessionData = null;
return true;
}
getSessionQuery() {
if (!this.sessionData?.id) {
throw new Error('No active session');
}
return `id=${this.sessionData.id}`;
}
}
module.exports = new StreamedSuSession();