mirror of
https://github.com/A-Star100/simpliplay-desktop.git
synced 2025-09-17 22:29:38 +00:00
Add files via upload
This commit is contained in:
parent
89107a79a6
commit
53010237b9
44
simpliplay/forge.config.js
Normal file
44
simpliplay/forge.config.js
Normal 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
BIN
simpliplay/icon.icns
Normal file
Binary file not shown.
BIN
simpliplay/icon.ico
Normal file
BIN
simpliplay/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 272 KiB |
BIN
simpliplay/icon.png
Normal file
BIN
simpliplay/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
428
simpliplay/index.html
Normal file
428
simpliplay/index.html
Normal 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
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
2
simpliplay/lib/hls.js
Normal file
File diff suppressed because one or more lines are too long
45
simpliplay/main.js
Normal file
45
simpliplay/main.js
Normal 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
9149
simpliplay/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
simpliplay/package.json
Normal file
38
simpliplay/package.json
Normal 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
14
simpliplay/preload.js
Normal 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])
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue
Block a user