mirror of
https://github.com/A-Star100/simpliplay-desktop.git
synced 2025-09-17 22:29:38 +00:00
Compare commits
42 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 |
@ -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))
|
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
|
## Mirrors
|
||||||
Self-hosted mirror available [here](https://git.anirudhsevugan.me/simpliplay/simpliplay-desktop-mirror).
|
Self-hosted mirror available [here](https://new-git.anirudhsevugan.me/anirudh/simpliplay-desktop/src/branch/main/).
|
||||||
**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)**.
|
**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');
|
const { checkForUpdate } = require('./updateChecker');
|
||||||
let gpuAccel = "";
|
let gpuAccel = "";
|
||||||
let didRegisterShortcuts = false;
|
let didRegisterShortcuts = false;
|
||||||
let version = "2.0.6.0"
|
let version = "2.1.3.0"
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
if (process.argv.includes('--use-gl')) {
|
if (process.argv.includes('--use-gl')) {
|
||||||
|
@ -6,7 +6,7 @@ const { pathToFileURL } = require("url");
|
|||||||
const { checkForUpdate } = require('./updateChecker');
|
const { checkForUpdate } = require('./updateChecker');
|
||||||
let gpuAccel = "";
|
let gpuAccel = "";
|
||||||
let didRegisterShortcuts = false;
|
let didRegisterShortcuts = false;
|
||||||
let version = "2.0.6.0"
|
let version = "2.1.4.0"
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
if (process.argv.includes('--use-gl')) {
|
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:
|
// Add the Add-ons menu only once here:
|
||||||
newMenuItems.push(new MenuItem({ label: 'Add-ons', submenu: addonsMenu }));
|
newMenuItems.push(new MenuItem({ label: 'Add-ons', submenu: addonsMenu }));
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "SimpliPlay",
|
"name": "SimpliPlay",
|
||||||
"version": "2.0.6",
|
"version": "2.1.4",
|
||||||
"description": "SimpliPlay - The mission to make media playback accessible on every device, anywhere, anytime.",
|
"description": "SimpliPlay - The mission to make media playback accessible on every device, anywhere, anytime.",
|
||||||
"main": "./main.js",
|
"main": "./main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -92,6 +92,11 @@
|
|||||||
"name": "FLAC Audio",
|
"name": "FLAC Audio",
|
||||||
"role": "Viewer"
|
"role": "Viewer"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ext": "opus",
|
||||||
|
"name": "Opus Audio",
|
||||||
|
"role": "Viewer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ext": "mkv",
|
"ext": "mkv",
|
||||||
"name": "MKV Video",
|
"name": "MKV Video",
|
||||||
@ -113,7 +118,7 @@
|
|||||||
"allowToChangeInstallationDirectory": true
|
"allowToChangeInstallationDirectory": true
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^37.3.1",
|
"electron": "^38.1.0",
|
||||||
"electron-builder": "^26.0.12"
|
"electron-builder": "^26.0.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "SimpliPlay",
|
"name": "SimpliPlay",
|
||||||
"version": "2.0.6",
|
"version": "2.1.4",
|
||||||
"description": "The mission to make media playback accessible on every platform, anywhere, anytime.",
|
"description": "The mission to make media playback accessible on every platform, anywhere, anytime.",
|
||||||
"main": "./main.js",
|
"main": "./main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -23,6 +23,13 @@
|
|||||||
"summary": "A cross-platform, simple media player.",
|
"summary": "A cross-platform, simple media player.",
|
||||||
"description": "The mission to make media playback accessible on every platform, anywhere, anytime."
|
"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": {
|
"mac": {
|
||||||
"target": [
|
"target": [
|
||||||
"dmg"
|
"dmg"
|
||||||
@ -98,6 +105,11 @@
|
|||||||
"name": "FLAC Audio",
|
"name": "FLAC Audio",
|
||||||
"role": "Viewer"
|
"role": "Viewer"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ext": "opus",
|
||||||
|
"name": "Opus Audio",
|
||||||
|
"role": "Viewer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ext": "mkv",
|
"ext": "mkv",
|
||||||
"name": "MKV Video",
|
"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": {
|
"devDependencies": {
|
||||||
"electron": "^37.3.1",
|
"electron": "^38.1.0",
|
||||||
"electron-builder": "^26.0.12"
|
"electron-builder": "^26.0.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,9 +66,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function addSubtitles(url) {
|
async function addSubtitles(url) {
|
||||||
// Remove existing tracks
|
// Remove existing tracks and revoke any previous blob URLs
|
||||||
const existingTracks = mediaPlayer.getElementsByTagName('track');
|
const existingTracks = mediaPlayer.getElementsByTagName('track');
|
||||||
for (let track of existingTracks) track.remove();
|
for (let i = existingTracks.length - 1; i >= 0; i--) {
|
||||||
|
const track = existingTracks[i];
|
||||||
|
if (track.src.startsWith('blob:')) URL.revokeObjectURL(track.src);
|
||||||
|
track.remove();
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch subtitle content
|
// Fetch subtitle content
|
||||||
let text = '';
|
let text = '';
|
||||||
@ -84,26 +88,28 @@ async function addSubtitles(url) {
|
|||||||
const firstLine = text.split(/\r?\n/)[0].trim();
|
const firstLine = text.split(/\r?\n/)[0].trim();
|
||||||
const format = firstLine.startsWith('WEBVTT') || url.toLowerCase().endsWith('.vtt') ? 'vtt' : 'srt';
|
const format = firstLine.startsWith('WEBVTT') || url.toLowerCase().endsWith('.vtt') ? 'vtt' : 'srt';
|
||||||
|
|
||||||
// Convert SRT → VTT if needed
|
// Determine track source
|
||||||
|
let trackSrc = url;
|
||||||
if (format === 'srt') {
|
if (format === 'srt') {
|
||||||
|
// Convert SRT → VTT
|
||||||
text = 'WEBVTT\n\n' + text
|
text = 'WEBVTT\n\n' + text
|
||||||
.replace(/\r+/g, '')
|
.replace(/\r+/g, '')
|
||||||
.replace(/^\s+|\s+$/g, '')
|
.replace(/^\s+|\s+$/g, '')
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map(line => line.replace(/(\d+):(\d+):(\d+),(\d+)/g, '$1:$2:$3.$4'))
|
.map(line => line.replace(/(\d+):(\d+):(\d+),(\d+)/g, '$1:$2:$3.$4'))
|
||||||
.join('\n');
|
.join('\n');
|
||||||
}
|
|
||||||
|
|
||||||
// Create Blob URL
|
// Create Blob URL for converted subtitles
|
||||||
const blob = new Blob([text], { type: 'text/vtt' });
|
const blob = new Blob([text], { type: 'text/vtt' });
|
||||||
const blobUrl = URL.createObjectURL(blob);
|
trackSrc = URL.createObjectURL(blob);
|
||||||
|
}
|
||||||
|
|
||||||
// Create and append track
|
// Create and append track
|
||||||
const track = document.createElement('track');
|
const track = document.createElement('track');
|
||||||
track.kind = 'subtitles';
|
track.kind = 'subtitles';
|
||||||
track.label = 'English';
|
track.label = 'English';
|
||||||
track.srclang = 'en';
|
track.srclang = 'en';
|
||||||
track.src = blobUrl;
|
track.src = trackSrc;
|
||||||
track.default = true;
|
track.default = true;
|
||||||
mediaPlayer.appendChild(track);
|
mediaPlayer.appendChild(track);
|
||||||
|
|
||||||
@ -111,10 +117,11 @@ async function addSubtitles(url) {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
for (let t of mediaPlayer.textTracks) t.mode = 'disabled';
|
for (let t of mediaPlayer.textTracks) t.mode = 'disabled';
|
||||||
track.track.mode = 'showing';
|
track.track.mode = 'showing';
|
||||||
}, 50); // slight delay ensures the browser registers it
|
}, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*// Handle submit subtitle URL
|
/*// Handle submit subtitle URL
|
||||||
function clearSubtitles() {
|
function clearSubtitles() {
|
||||||
const tracks = mediaPlayer.getElementsByTagName('track');
|
const tracks = mediaPlayer.getElementsByTagName('track');
|
||||||
@ -123,14 +130,18 @@ function clearSubtitles() {
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// Handle submit subtitle URL
|
|
||||||
function clearSubtitles() {
|
function clearSubtitles() {
|
||||||
const tracks = mediaPlayer.getElementsByTagName('track');
|
const tracks = mediaPlayer.getElementsByTagName('track');
|
||||||
for (let i = tracks.length - 1; i >= 0; i--) {
|
for (let i = tracks.length - 1; i >= 0; i--) {
|
||||||
tracks[i].remove();
|
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
|
// Use this function when a new video is loaded
|
||||||
|
|
||||||
submitSubtitlesBtn.addEventListener('click', () => {
|
submitSubtitlesBtn.addEventListener('click', () => {
|
||||||
@ -261,6 +272,7 @@ const { isHLS, isDASH } = await detectStreamType(url);
|
|||||||
|
|
||||||
|
|
||||||
let previousObjectURL = null; // Store the last Object URL
|
let previousObjectURL = null; // Store the last Object URL
|
||||||
|
window.objectURL = previousObjectURL
|
||||||
|
|
||||||
fileInput.addEventListener('change', (event) => {
|
fileInput.addEventListener('change', (event) => {
|
||||||
if (hls !== null) {
|
if (hls !== null) {
|
||||||
@ -281,6 +293,12 @@ const { isHLS, isDASH } = await detectStreamType(url);
|
|||||||
// Revoke the previous Object URL if it exists
|
// Revoke the previous Object URL if it exists
|
||||||
if (previousObjectURL) {
|
if (previousObjectURL) {
|
||||||
URL.revokeObjectURL(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
|
// Create a new Object URL for the selected file
|
||||||
@ -293,6 +311,7 @@ const { isHLS, isDASH } = await detectStreamType(url);
|
|||||||
|
|
||||||
// Store the new Object URL for future cleanup
|
// Store the new Object URL for future cleanup
|
||||||
previousObjectURL = fileURL;
|
previousObjectURL = fileURL;
|
||||||
|
fileInput.value = "";
|
||||||
|
|
||||||
// Hide dialog after selecting a file
|
// Hide dialog after selecting a file
|
||||||
dialogOverlay.style.display = 'none';
|
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
|
// Handle submit subtitle URL
|
||||||
function clearSubtitles() {
|
function clearSubtitles() {
|
||||||
const tracks = mediaElement.getElementsByTagName('track');
|
const tracks = mediaElement.getElementsByTagName('track');
|
||||||
@ -97,3 +194,7 @@ window.electron.receive("unload-addon", (fileURL) => {
|
|||||||
unloadAddon(fileURL);
|
unloadAddon(fileURL);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user