feat: initial session management
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
12
backend/services/session/SessionFactory.js
Normal file
12
backend/services/session/SessionFactory.js
Normal 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;
|
||||
21
backend/services/session/SessionHandler.js
Normal file
21
backend/services/session/SessionHandler.js
Normal 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;
|
||||
93
backend/services/session/StreamedSuSession.js
Normal file
93
backend/services/session/StreamedSuSession.js
Normal 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();
|
||||
Reference in New Issue
Block a user