Add files via upload

This commit is contained in:
Anirudh Sevugan 2025-01-23 18:07:49 -06:00 committed by GitHub
parent 89107a79a6
commit 53010237b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 9723 additions and 0 deletions

View File

@ -0,0 +1,44 @@
const { FusesPlugin } = require('@electron-forge/plugin-fuses');
const { FuseV1Options, FuseVersion } = require('@electron/fuses');
module.exports = {
packagerConfig: {
asar: true,
},
rebuildConfig: {},
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {},
},
{
name: '@electron-forge/maker-zip',
platforms: ['darwin'],
},
{
name: '@electron-forge/maker-deb',
config: {},
},
{
name: '@electron-forge/maker-rpm',
config: {},
},
],
plugins: [
{
name: '@electron-forge/plugin-auto-unpack-natives',
config: {},
},
// Fuses are used to enable/disable various Electron functionality
// at package time, before code signing the application
new FusesPlugin({
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false,
[FuseV1Options.EnableCookieEncryption]: true,
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
[FuseV1Options.EnableNodeCliInspectArguments]: false,
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
[FuseV1Options.OnlyLoadAppFromAsar]: true,
}),
],
};

BIN
simpliplay/icon.icns Normal file

Binary file not shown.

BIN
simpliplay/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

BIN
simpliplay/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

428
simpliplay/index.html Normal file
View File

@ -0,0 +1,428 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="lib/dash.js"></script>
<script src="lib/hls.js"></script>
<title>SimpliPlay</title>
<style>
body {
font-family: "Inter", sans-serif;
text-align: center;
padding: 20px;
margin: 0;
background: linear-gradient(135deg, #083358, #1a73e8);
color: white;
}
#saveSettingsBtn {
margin: 10px 5px;
padding: 10px 20px;
border: none;
border-radius: 5px;
background: #1a73e8;
color: white;
font-size: 16px;
cursor: pointer;
}
.dialog-overlay,
.subtitles-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 9999;
}
.dialog,
.subtitles-dialog {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #ffffff;
color: #333;
padding: 20px;
border-radius: 10px;
width: 90%;
max-width: 400px;
text-align: center;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
.dialog input[type="url"],
.subtitles-dialog input[type="url"] {
width: 80%;
padding: 10px;
font-size: 16px;
border-radius: 5px;
border: 1px solid #ccc;
margin-bottom: 10px;
}
.dialog button,
.subtitles-dialog button {
margin: 10px 5px;
padding: 10px 20px;
border: none;
border-radius: 5px;
background: #1a73e8;
color: white;
font-size: 16px;
cursor: pointer;
}
.dialog button:hover,
.subtitles-dialog button:hover {
background: #0c63d9;
}
#customControls {
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
gap: 10px;
}
#customControls button,
input[type="range"] {
padding: 10px;
border: none;
border-radius: 5px;
font-size: 14px;
cursor: pointer;
}
#customControls button {
background: #1a73e8;
color: white;
}
#customControls button:hover {
background: #0c63d9;
}
input[type="range"] {
flex-grow: 1;
background: transparent;
outline: none;
cursor: pointer;
}
video {
display: flex;
margin: 20px auto;
background: black;
width: 80vw;
height: 80vh;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
.gap-box {
height: 20px;
}
#showDialogBtn {
margin: 10px 5px;
padding: 10px 20px;
border: none;
border-radius: 5px;
background: #1a73e8;
color: white;
font-size: 16px;
cursor: pointer;
}
#settingsBtn {
background: #1a73e8;
font-size: 18px;
border-radius: 50%;
width: 40px;
height: 40px;
cursor: pointer;
margin-top: 20px;
}
#settingsBtn:hover {
background: #0c63d9;
}
#settingsPanel {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #ffffff;
color: black;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
z-index: 10000;
text-align: center;
}
</style>
</head>
<body>
<h1>SimpliPlay</h1>
<div class="dialog-overlay" id="dialogOverlay">
<div class="dialog">
<p>Select how you want to load media:</p>
<button id="chooseFileBtn">Choose a File</button>
<button id="enterUrlBtn">Enter a URL <small>(Supports MPEG-DASH and HLS)</small></button>
<button id="hideDialogBtn">Go back</button>
<input type="file" id="fileInput" accept="video/*,audio/*" style="display: none;">
</div>
</div>
<div class="dialog-overlay" id="urlDialogOverlay">
<div class="dialog">
<p>Enter the media URL:</p>
<input type="url" id="urlInput" placeholder="Enter URL here">
<button id="submitUrlBtn">Submit</button>
<button id="cancelUrlBtn">Cancel</button>
</div>
</div>
<div class="subtitles-overlay" id="subtitlesOverlay">
<div class="subtitles-dialog">
<p>Enter subtitle URL:</p>
<input type="url" id="subtitlesInput" placeholder="Enter subtitle URL here">
<button id="submitSubtitlesBtn">Submit</button>
<button id="cancelSubtitlesBtn">Cancel</button>
</div>
</div>
<video id="mediaPlayer" autoplay controls></video>
<div id="customControls">
<button id="playPauseBtn">Pause</button>
<input type="range" id="seekBar" min="0" value="0" step="0.1">
<span id="timeDisplay">00:00 / 00:00</span>
<button id="volumeBtn">🔊</button>
<input type="range" id="volumeBar" min="0" max="1" step="0.1" value="1">
<button id="fullscreenBtn"></button>
<button id="ccBtn">Add CC</button> <!-- CC button -->
</div>
<div class="gap-box"></div>
<button id="showDialogBtn">Play more media</button>
<!-- Settings gear button -->
<button id="settingsBtn">⚙️</button>
<!-- Settings panel -->
<div id="settingsPanel">
<label for="autoplayCheckbox">Autoplay:</label>
<input type="checkbox" id="autoplayCheckbox" checked><br>
<label for="loopCheckbox">Loop:</label>
<input type="checkbox" id="loopCheckbox"><br>
<button id="saveSettingsBtn">Save Settings</button>
</div>
<script>
const dialogOverlay = document.getElementById('dialogOverlay');
const chooseFileBtn = document.getElementById('chooseFileBtn');
const enterUrlBtn = document.getElementById('enterUrlBtn');
const fileInput = document.getElementById('fileInput');
const mediaPlayer = document.getElementById('mediaPlayer');
const showDialogBtn = document.getElementById('showDialogBtn');
const hideDialogBtn = document.getElementById('hideDialogBtn');
const playPauseBtn = document.getElementById('playPauseBtn');
const seekBar = document.getElementById('seekBar');
const timeDisplay = document.getElementById('timeDisplay');
const volumeBar = document.getElementById('volumeBar');
const settingsBtn = document.getElementById('settingsBtn');
const settingsPanel = document.getElementById('settingsPanel');
const autoplayCheckbox = document.getElementById('autoplayCheckbox');
const loopCheckbox = document.getElementById('loopCheckbox');
const saveSettingsBtn = document.getElementById('saveSettingsBtn');
const fullscreenBtn = document.getElementById('fullscreenBtn');
const urlDialogOverlay = document.getElementById('urlDialogOverlay');
const urlInput = document.getElementById('urlInput');
const submitUrlBtn = document.getElementById('submitUrlBtn');
const cancelUrlBtn = document.getElementById('cancelUrlBtn');
const ccBtn = document.getElementById('ccBtn'); // CC button
const subtitlesOverlay = document.getElementById('subtitlesOverlay');
const subtitlesInput = document.getElementById('subtitlesInput');
const submitSubtitlesBtn = document.getElementById('submitSubtitlesBtn');
const cancelSubtitlesBtn = document.getElementById('cancelSubtitlesBtn');
// Handle submit subtitle URL
submitSubtitlesBtn.addEventListener('click', () => {
const subtitleUrl = subtitlesInput.value;
if (subtitleUrl) {
const track = document.createElement('track');
track.kind = 'subtitles';
track.label = 'English';
track.srclang = 'en';
track.src = subtitleUrl;
mediaPlayer.appendChild(track);
subtitlesOverlay.style.display = 'none';
}
});
// Function to add subtitles dynamically (e.g., after URL input)
function addSubtitles(url) {
subtitleTrack.src = url;
subtitleTrack.track.mode = 'showing'; // Enable subtitles by default
subtitleBtn.textContent = 'Subtitles On'; // Set button text to indicate subtitles are on
}
let autoplayEnabled = true;
let loopEnabled = false;
// Handle submit URL button in custom dialog
submitUrlBtn.addEventListener('click', () => {
const url = urlInput.value;
if (url) {
if (url.includes('.m3u8')) {
// HLS stream
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(url);
hls.attachMedia(mediaPlayer);
hls.on(Hls.Events.MANIFEST_PARSED, function() {
mediaPlayer.play();
});
} else {
alert('HLS not supported on your browser.');
}
} else if (url.includes('.mpd')) {
// MPEG-DASH stream
const player = dashjs.MediaPlayer().create();
player.initialize(mediaPlayer, url, true);
} else {
mediaPlayer.src = url;
mediaPlayer.play();
}
urlDialogOverlay.style.display = 'none';
dialogOverlay.style.display = 'none';
}
});
// Handle CC button to show subtitle modal
ccBtn.addEventListener('click', () => {
subtitlesOverlay.style.display = 'block';
});
// Handle cancel subtitle modal
cancelSubtitlesBtn.addEventListener('click', () => {
subtitlesOverlay.style.display = 'none';
});
// Show the dialog on page load
window.onload = function () {
dialogOverlay.style.display = 'block';
};
// Handle "Choose a File" button
chooseFileBtn.addEventListener('click', () => {
fileInput.click();
});
// Handle file input
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
const fileURL = URL.createObjectURL(file);
mediaPlayer.src = fileURL;
dialogOverlay.style.display = 'none';
}
});
// Handle "Enter a URL" button
enterUrlBtn.addEventListener('click', () => {
urlDialogOverlay.style.display = 'block';
});
// Handle cancel button in URL dialog
cancelUrlBtn.addEventListener('click', () => {
urlDialogOverlay.style.display = 'none';
});
// Handle custom play/pause button
playPauseBtn.addEventListener('click', () => {
if (mediaPlayer.paused) {
mediaPlayer.play();
playPauseBtn.textContent = 'Pause';
} else {
mediaPlayer.pause();
playPauseBtn.textContent = 'Play';
}
});
// Update seek bar and time display
mediaPlayer.addEventListener('timeupdate', () => {
seekBar.max = mediaPlayer.duration || 0;
seekBar.value = mediaPlayer.currentTime;
const current = formatTime(mediaPlayer.currentTime);
const total = formatTime(mediaPlayer.duration);
timeDisplay.textContent = `${current} / ${total}`;
});
// Seek media
seekBar.addEventListener('input', () => {
mediaPlayer.currentTime = seekBar.value;
});
// Handle volume
volumeBar.addEventListener('input', () => {
mediaPlayer.volume = volumeBar.value;
});
// Show settings panel
settingsBtn.addEventListener('click', () => {
settingsPanel.style.display = 'block';
});
// Save settings
saveSettingsBtn.addEventListener('click', () => {
autoplayEnabled = autoplayCheckbox.checked;
loopEnabled = loopCheckbox.checked;
mediaPlayer.autoplay = autoplayEnabled;
mediaPlayer.loop = loopEnabled;
settingsPanel.style.display = 'none';
});
// Format time
function formatTime(time) {
const minutes = Math.floor(time / 60) || 0;
const seconds = Math.floor(time % 60) || 0;
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
// Show dialog
showDialogBtn.addEventListener('click', () => {
dialogOverlay.style.display = 'block';
});
// Hide dialog
hideDialogBtn.addEventListener('click', () => {
dialogOverlay.style.display = 'none';
});
// Fullscreen functionality
fullscreenBtn.addEventListener('click', () => {
if (!document.fullscreenElement) {
mediaPlayer.requestFullscreen();
} else {
document.exitFullscreen();
}
});
</script>
</body>
</html>

3
simpliplay/lib/dash.js Normal file

File diff suppressed because one or more lines are too long

2
simpliplay/lib/hls.js Normal file

File diff suppressed because one or more lines are too long

45
simpliplay/main.js Normal file
View File

@ -0,0 +1,45 @@
// main.js
// Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron')
const path = require('node:path')
const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 1920,
height: 1080,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// Open the DevTools.
// mainWindow.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

9149
simpliplay/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

38
simpliplay/package.json Normal file
View File

@ -0,0 +1,38 @@
{
"name": "SimpliPlay",
"version": "1.0.0",
"description": "The mission to make media playback accessible on every device",
"main": "main.js",
"scripts": {
"test": "echo Hello World!!!",
"start": "electron-forge start",
"package": "electron-builder --dir",
"make": "electron-builder"
},
"author": "Anirudh Sevugan",
"build": {
"appId": "com.anirudhsevugan.simpliPlay",
"productName": "SimpliPlay",
"directories": {
"output": "dist"
},
"mac": {
"target": [
"dmg"
],
"icon": "icon.icns"
},
"win": {
"target": "nsis",
"icon": "icon.ico"
},
"linux": {
"target": "AppImage",
"icon": "icon.png"
}
},
"devDependencies": {
"electron": "^34.0.1",
"electron-builder": "^25.1.8"
}
}

14
simpliplay/preload.js Normal file
View File

@ -0,0 +1,14 @@
// preload.js
// All the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency])
}
})