mirror of
https://github.com/A-Star100/simpliplay-desktop.git
synced 2025-09-18 06:39:44 +00:00
Compare commits
47 Commits
release-2.
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a26e1448ab | ||
![]() |
59a8d5c577 | ||
![]() |
ef5e34331e | ||
![]() |
cc5da53f23 | ||
![]() |
5d0128e2af | ||
![]() |
101f14c4d6 | ||
![]() |
49939dd963 | ||
![]() |
257218c5a1 | ||
![]() |
808c275167 | ||
![]() |
5d49c97662 | ||
![]() |
16311d2eff | ||
![]() |
4a23d31b03 | ||
![]() |
97088fb943 | ||
![]() |
b987f539f8 | ||
![]() |
117d2950ea | ||
![]() |
0aa0e06194 | ||
![]() |
2ddce17442 | ||
![]() |
ae127fb837 | ||
![]() |
f7fa4bf081 | ||
![]() |
9cdbc5e328 | ||
![]() |
778cc0ec74 | ||
![]() |
451313f414 | ||
![]() |
2c6b048721 | ||
![]() |
adb0647fdc | ||
![]() |
f06f2cc9fc | ||
![]() |
8fd5de8b75 | ||
![]() |
20f5d7e250 | ||
![]() |
6125553e40 | ||
![]() |
ec3332a248 | ||
![]() |
7a7cb4d8fd | ||
![]() |
a815b4c431 | ||
![]() |
f20c520123 | ||
![]() |
4675073530 | ||
![]() |
ad979d6b86 | ||
![]() |
1abe50b40a | ||
![]() |
d03513ea3d | ||
![]() |
2560bd4315 | ||
![]() |
99303f82e7 | ||
![]() |
1d38e90b2b | ||
![]() |
0513fa7b23 | ||
![]() |
fde5bc7fe2 | ||
![]() |
6e4f9d0459 | ||
![]() |
2176853993 | ||
![]() |
32645bb3cf | ||
![]() |
47f7753b99 | ||
![]() |
6a0d9cb53a | ||
![]() |
97c7b4d4dd |
@ -26,5 +26,5 @@ ___
|
||||
Read the docs ([Electron - Tutorial](https://www.electronjs.org/docs/latest/tutorial/tutorial-prerequisites), [Neutralinojs - Your First App](https://neutralino.js.org/docs/getting-started/your-first-neutralinojs-app), [NW.js - Getting Started (For Users)](https://nwjs.readthedocs.io/en/latest/For%20Users/Getting%20Started/#write-nwjs-app))
|
||||
|
||||
## Mirrors
|
||||
Self-hosted mirror available [here](https://git.anirudhsevugan.me/simpliplay/simpliplay-desktop-mirror).
|
||||
**The server may not always be up. You'll see a screen saying "Git is down" if the server is down, with a link to contact me (the webmaster)**.
|
||||
Self-hosted mirror available [here](https://new-git.anirudhsevugan.me/anirudh/simpliplay-desktop/src/branch/main/).
|
||||
**The server may not always be up. If it isn't, you can contact me at: (personal at anirudhsevugan dot me)**.
|
||||
|
@ -1 +1,11 @@
|
||||
Source code for SimpliPlay.
|
||||
|
||||
--BUILD STUFF--
|
||||
SimpliPlay uses Electron Builder for desktop builds, but includes an example Electron Forge config that will work as expected but does not support native actions such as file picking from the explorer; the package.json uses electron-builder specific actions for that.
|
||||
|
||||
--CI/CD--
|
||||
We now use CI/CD (specifically GitHub Actions, at one point CircleCI) for builds, but Linux ARM64 snaps in particular were annoying to get started with. After multiple attempts, it finally worked as expected (with a few really stupid workarounds required that shouldn't even be needed).
|
||||
|
||||
--DEPENDENCIES--
|
||||
Other than Electron and Electron Builder, there is one more dependency called "Nothing".
|
||||
Its name is very self explanatory, isn't it?
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 76 KiB |
@ -6,7 +6,7 @@ const { pathToFileURL } = require("url");
|
||||
const { checkForUpdate } = require('./updateChecker');
|
||||
let gpuAccel = "";
|
||||
let didRegisterShortcuts = false;
|
||||
let version = "2.0.4.5"
|
||||
let version = "2.1.3.0"
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
if (process.argv.includes('--use-gl')) {
|
||||
|
@ -6,7 +6,7 @@ const { pathToFileURL } = require("url");
|
||||
const { checkForUpdate } = require('./updateChecker');
|
||||
let gpuAccel = "";
|
||||
let didRegisterShortcuts = false;
|
||||
let version = "2.0.5.0"
|
||||
let version = "2.1.4.0"
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
if (process.argv.includes('--use-gl')) {
|
||||
@ -321,6 +321,14 @@ addonsMenu.append(new MenuItem({
|
||||
}
|
||||
}));
|
||||
|
||||
// "Store" menu item (info dialog version)
|
||||
addonsMenu.append(new MenuItem({
|
||||
label: 'Store',
|
||||
click: () => {
|
||||
shell.openExternal("https://simpliplay.netlify.app/addons/")
|
||||
}
|
||||
}));
|
||||
|
||||
// Add the Add-ons menu only once here:
|
||||
newMenuItems.push(new MenuItem({ label: 'Add-ons', submenu: addonsMenu }));
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "SimpliPlay",
|
||||
"version": "2.0.5",
|
||||
"version": "2.1.4",
|
||||
"description": "SimpliPlay - The mission to make media playback accessible on every device, anywhere, anytime.",
|
||||
"main": "./main.js",
|
||||
"scripts": {
|
||||
@ -92,6 +92,11 @@
|
||||
"name": "FLAC Audio",
|
||||
"role": "Viewer"
|
||||
},
|
||||
{
|
||||
"ext": "opus",
|
||||
"name": "Opus Audio",
|
||||
"role": "Viewer"
|
||||
},
|
||||
{
|
||||
"ext": "mkv",
|
||||
"name": "MKV Video",
|
||||
@ -113,7 +118,7 @@
|
||||
"allowToChangeInstallationDirectory": true
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^37.3.1",
|
||||
"electron": "^38.1.0",
|
||||
"electron-builder": "^26.0.12"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "SimpliPlay",
|
||||
"version": "2.0.5",
|
||||
"version": "2.1.4",
|
||||
"description": "The mission to make media playback accessible on every platform, anywhere, anytime.",
|
||||
"main": "./main.js",
|
||||
"scripts": {
|
||||
@ -23,6 +23,13 @@
|
||||
"summary": "A cross-platform, simple media player.",
|
||||
"description": "The mission to make media playback accessible on every platform, anywhere, anytime."
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"installerIcon": "icon.ico",
|
||||
"uninstallerIcon": "icon.ico",
|
||||
"uninstallDisplayName": "simpliplay-uninstaller",
|
||||
"allowToChangeInstallationDirectory": true
|
||||
},
|
||||
"mac": {
|
||||
"target": [
|
||||
"dmg"
|
||||
@ -98,6 +105,11 @@
|
||||
"name": "FLAC Audio",
|
||||
"role": "Viewer"
|
||||
},
|
||||
{
|
||||
"ext": "opus",
|
||||
"name": "Opus Audio",
|
||||
"role": "Viewer"
|
||||
},
|
||||
{
|
||||
"ext": "mkv",
|
||||
"name": "MKV Video",
|
||||
@ -110,16 +122,8 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"installerIcon": "icon.ico",
|
||||
"uninstallerIcon": "icon.ico",
|
||||
"uninstallDisplayName": "simpliplay-uninstaller",
|
||||
"license": "LICENSE.md",
|
||||
"allowToChangeInstallationDirectory": true
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^37.3.1",
|
||||
"electron": "^38.1.0",
|
||||
"electron-builder": "^26.0.12"
|
||||
}
|
||||
}
|
||||
|
@ -65,36 +65,83 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Function to add subtitles dynamically (e.g., after URL input)
|
||||
function addSubtitles(url) {
|
||||
// Remove any existing subtitle tracks
|
||||
async function addSubtitles(url) {
|
||||
// Remove existing tracks and revoke any previous blob URLs
|
||||
const existingTracks = mediaPlayer.getElementsByTagName('track');
|
||||
for (let track of existingTracks) {
|
||||
for (let i = existingTracks.length - 1; i >= 0; i--) {
|
||||
const track = existingTracks[i];
|
||||
if (track.src.startsWith('blob:')) URL.revokeObjectURL(track.src);
|
||||
track.remove();
|
||||
}
|
||||
|
||||
// Create a new track for subtitles
|
||||
// Fetch subtitle content
|
||||
let text = '';
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
text = await res.text();
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch subtitles:', err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect format
|
||||
const firstLine = text.split(/\r?\n/)[0].trim();
|
||||
const format = firstLine.startsWith('WEBVTT') || url.toLowerCase().endsWith('.vtt') ? 'vtt' : 'srt';
|
||||
|
||||
// Determine track source
|
||||
let trackSrc = url;
|
||||
if (format === 'srt') {
|
||||
// Convert SRT → VTT
|
||||
text = 'WEBVTT\n\n' + text
|
||||
.replace(/\r+/g, '')
|
||||
.replace(/^\s+|\s+$/g, '')
|
||||
.split('\n')
|
||||
.map(line => line.replace(/(\d+):(\d+):(\d+),(\d+)/g, '$1:$2:$3.$4'))
|
||||
.join('\n');
|
||||
|
||||
// Create Blob URL for converted subtitles
|
||||
const blob = new Blob([text], { type: 'text/vtt' });
|
||||
trackSrc = URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
// Create and append track
|
||||
const track = document.createElement('track');
|
||||
track.kind = 'subtitles';
|
||||
track.label = 'English';
|
||||
track.srclang = 'en';
|
||||
track.src = url;
|
||||
|
||||
// Append the new track
|
||||
track.src = trackSrc;
|
||||
track.default = true;
|
||||
mediaPlayer.appendChild(track);
|
||||
|
||||
// Optionally, enable subtitles by default
|
||||
track.track.mode = 'showing'; // Enable subtitles by default
|
||||
// Force enable immediately
|
||||
setTimeout(() => {
|
||||
for (let t of mediaPlayer.textTracks) t.mode = 'disabled';
|
||||
track.track.mode = 'showing';
|
||||
}, 50);
|
||||
}
|
||||
|
||||
// Handle submit subtitle URL
|
||||
|
||||
|
||||
/*// Handle submit subtitle URL
|
||||
function clearSubtitles() {
|
||||
const tracks = mediaPlayer.getElementsByTagName('track');
|
||||
for (let i = tracks.length - 1; i >= 0; i--) {
|
||||
tracks[i].remove();
|
||||
}
|
||||
}*/
|
||||
|
||||
function clearSubtitles() {
|
||||
const tracks = mediaPlayer.getElementsByTagName('track');
|
||||
for (let i = tracks.length - 1; i >= 0; i--) {
|
||||
const track = tracks[i];
|
||||
if (track.src.startsWith('blob:')) {
|
||||
URL.revokeObjectURL(track.src); // free memory leaks from SRT to VTT converted subtitles
|
||||
}
|
||||
track.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Use this function when a new video is loaded
|
||||
|
||||
submitSubtitlesBtn.addEventListener('click', () => {
|
||||
@ -225,6 +272,7 @@ const { isHLS, isDASH } = await detectStreamType(url);
|
||||
|
||||
|
||||
let previousObjectURL = null; // Store the last Object URL
|
||||
window.objectURL = previousObjectURL
|
||||
|
||||
fileInput.addEventListener('change', (event) => {
|
||||
if (hls !== null) {
|
||||
@ -245,8 +293,14 @@ const { isHLS, isDASH } = await detectStreamType(url);
|
||||
// Revoke the previous Object URL if it exists
|
||||
if (previousObjectURL) {
|
||||
URL.revokeObjectURL(previousObjectURL);
|
||||
window.objectURL = previousObjectURL
|
||||
}
|
||||
|
||||
// Revoke previous file picker Object URL
|
||||
if (window.previousDropURL) {
|
||||
URL.revokeObjectURL(window.previousDropURL);
|
||||
}
|
||||
|
||||
// Create a new Object URL for the selected file
|
||||
const fileURL = URL.createObjectURL(file);
|
||||
mediaPlayer.src = fileURL;
|
||||
@ -257,6 +311,7 @@ const { isHLS, isDASH } = await detectStreamType(url);
|
||||
|
||||
// Store the new Object URL for future cleanup
|
||||
previousObjectURL = fileURL;
|
||||
fileInput.value = "";
|
||||
|
||||
// Hide dialog after selecting a file
|
||||
dialogOverlay.style.display = 'none';
|
||||
|
@ -16,6 +16,103 @@ function loadMedia(fileURL) {
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const dropArea = document.createElement('div');
|
||||
dropArea.style.position = 'fixed';
|
||||
dropArea.style.top = '0';
|
||||
dropArea.style.left = '0';
|
||||
dropArea.style.width = '100vw';
|
||||
dropArea.style.height = '100vh';
|
||||
dropArea.style.display = 'none'; // hidden by default
|
||||
dropArea.style.zIndex = '0'; // behind everything
|
||||
dropArea.style.opacity = '0'; // invisible
|
||||
document.body.appendChild(dropArea);
|
||||
|
||||
let dragCounter = 0; // track nested dragenter/dragleave events
|
||||
|
||||
|
||||
window.addEventListener('dragenter', (e) => {
|
||||
if (e.dataTransfer.types.includes('Files')) {
|
||||
dragCounter++;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('dragleave', (e) => {
|
||||
if (e.dataTransfer.types.includes('Files')) {
|
||||
dragCounter--;
|
||||
if (dragCounter <= 0) dragCounter = 0;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('dragover', (e) => {
|
||||
if (e.dataTransfer.types.includes('Files')) {
|
||||
e.preventDefault(); // allow drop
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let previousDropURL = null; // Store last Object URL
|
||||
window.previousDropURL = previousDropURL
|
||||
|
||||
window.addEventListener('drop', e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (!file) return;
|
||||
|
||||
// Destroy existing HLS/DASH instances if they exist
|
||||
if (window.hls) {
|
||||
window.hls.destroy();
|
||||
window.hls = null;
|
||||
}
|
||||
if (window.dash) {
|
||||
window.dash.reset();
|
||||
window.dash = null;
|
||||
}
|
||||
|
||||
// Clear previously loaded subtitles
|
||||
const tracks = mediaElement.getElementsByTagName('track');
|
||||
for (let i = tracks.length - 1; i >= 0; i--) {
|
||||
tracks[i].remove();
|
||||
}
|
||||
|
||||
// Revoke previous Object URL
|
||||
if (previousDropURL) {
|
||||
URL.revokeObjectURL(previousDropURL);
|
||||
window.previousDropURL = previousDropURL;
|
||||
}
|
||||
|
||||
// Revoke previous file picker Object URL
|
||||
if (window.objectURL) {
|
||||
URL.revokeObjectURL(window.objectURL);
|
||||
}
|
||||
|
||||
// Create a new Object URL
|
||||
const fileURL = URL.createObjectURL(file);
|
||||
mediaElement.src = fileURL;
|
||||
|
||||
mediaElement.load();
|
||||
// Autoplay if checkbox is checked
|
||||
if (autoplayCheckbox.checked) {
|
||||
mediaElement.play().catch(err => console.warn(err));
|
||||
}
|
||||
|
||||
// Store for future cleanup
|
||||
previousDropURL = fileURL;
|
||||
|
||||
// Hide file dialog if applicable
|
||||
if (dialogOverlay) dialogOverlay.style.display = 'none';
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
// Handle submit subtitle URL
|
||||
function clearSubtitles() {
|
||||
const tracks = mediaElement.getElementsByTagName('track');
|
||||
@ -97,3 +194,7 @@ window.electron.receive("unload-addon", (fileURL) => {
|
||||
unloadAddon(fileURL);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user