Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f06d804e9 | ||
|
|
fb87b201da | ||
|
|
a58090b334 | ||
|
|
0050bfe36f | ||
|
|
d40233cb1e | ||
|
|
3125f8e7d1 | ||
|
|
0646fb77f2 | ||
|
|
c5a3a17a94 | ||
|
|
e4b1880025 | ||
|
|
c0b74c1605 | ||
|
|
96aed66af2 | ||
|
|
65e23a90a1 | ||
|
|
f3405b5724 | ||
|
|
5bef1e5663 | ||
|
|
5120cee05a | ||
|
|
895e593d02 | ||
|
|
32d8070119 | ||
|
|
46273ecbe5 | ||
|
|
4268099a06 | ||
|
|
921db2f6cb | ||
|
|
f640c45464 | ||
|
|
d4dc7a32a4 | ||
|
|
33e5171742 | ||
|
|
ebb4857782 | ||
|
|
c47d253967 | ||
|
|
c58e7d2ccf | ||
|
|
f3cad86f6f | ||
|
|
a23655de42 | ||
|
|
b7b899ea40 | ||
|
|
89029b1bc3 | ||
|
|
9b4212e00c | ||
|
|
6b158e1e23 | ||
|
|
6cae350ef2 | ||
|
|
3b83738208 | ||
|
|
6b894eadf7 | ||
|
|
13128b6a0f | ||
|
|
4b84238fb4 | ||
|
|
7345ecde7b | ||
|
|
8272cf0ade | ||
|
|
cae3d41af0 | ||
|
|
54061018ea | ||
|
|
0281b3c9f9 | ||
|
|
70c8d3f837 | ||
|
|
19dcd072c7 | ||
|
|
4c5474cb0f | ||
|
|
2c5baf6b87 | ||
|
|
508c2fe5b6 | ||
|
|
200bd1be74 | ||
|
|
29617de259 | ||
|
|
96f1a1153b | ||
|
|
c931fd1737 | ||
|
|
8f8e9a538d | ||
|
|
8dbec207cb | ||
|
|
4ca0fff9d9 | ||
|
|
97378addf5 | ||
|
|
1888243853 | ||
|
|
e0e61f7e7d | ||
|
|
d2b7054d80 | ||
|
|
afbd0a3301 | ||
|
|
5a8c10ba82 | ||
|
|
c1b0aaec2d | ||
|
|
3ac0b252ff | ||
|
|
f199c3051b | ||
|
|
aa575ee267 | ||
|
|
20c9120b3f | ||
|
|
8e17cff31c | ||
|
|
69641c0b93 | ||
|
|
0a39d8622c | ||
|
|
2a8f5c519b | ||
|
|
b53a898f82 | ||
|
|
45582751be | ||
|
|
819d238294 | ||
|
|
76dd0516cd | ||
|
|
1535cbb20d | ||
|
|
69c6ac9fdd | ||
|
|
2f9bc9a2bc | ||
|
|
6125523988 |
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for YT Presence
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
node_modules
|
||||
out
|
||||
.vscode
|
||||
dist
|
||||
54
Extension/connect.css
Normal file
@@ -0,0 +1,54 @@
|
||||
#ytp-connectinfo {
|
||||
position: fixed;
|
||||
top: -50px;
|
||||
right: 0;
|
||||
z-index: 10000;
|
||||
width: 175px;
|
||||
border-bottom-left-radius: 5px;
|
||||
height: 50px;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
user-select: none;
|
||||
animation-name: slideIn;
|
||||
animation-duration: 5s;
|
||||
animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1);
|
||||
color: black;
|
||||
}
|
||||
|
||||
#ytp-connectinfo * {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#ytp-connectinfo img {
|
||||
float: left;
|
||||
margin: 5px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
#ytp-connectinfo h1 {
|
||||
font-size: 20px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
#ytp-connectinfo h2 {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
0% {
|
||||
top: -50px;
|
||||
}
|
||||
|
||||
10% {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
90% {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: -50px;
|
||||
}
|
||||
}
|
||||
BIN
Extension/icon.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
49
Extension/manifest.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "YT Presence",
|
||||
"author": "Timeraa",
|
||||
"version": "1.1",
|
||||
"description": "YT Presence adds Discord Rich Presence integration, Media controls and much more to YouTube/YouTube Music, Twitch etc...",
|
||||
"manifest_version": 2,
|
||||
"icons": {
|
||||
"2024": "icon.png"
|
||||
},
|
||||
"browser_action": {
|
||||
"default_icon": {
|
||||
"2024": "icon.png"
|
||||
}
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"js": ["util/jquery-3.3.1.min.js", "util/socket.io-2.1.1.min.js"],
|
||||
"css": ["connect.css"],
|
||||
"matches": [
|
||||
"https://www.youtube.com/*",
|
||||
"https://music.youtube.com/*",
|
||||
"https://www.netflix.com/*",
|
||||
"https://soundcloud.com/*",
|
||||
"https://www.twitch.tv/*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"js": ["presences/YouTubeMusic.js"],
|
||||
"matches": ["https://music.youtube.com/*"]
|
||||
},
|
||||
{
|
||||
"js": ["presences/YouTube.js"],
|
||||
"matches": ["https://www.youtube.com/*"]
|
||||
},
|
||||
{
|
||||
"js": ["presences/Netflix.js"],
|
||||
"matches": ["https://*.netflix.com/*"]
|
||||
},
|
||||
{
|
||||
"js": ["presences/SoundCloud.js"],
|
||||
"matches": ["https://*.soundcloud.com/*"]
|
||||
},
|
||||
{
|
||||
"js": ["presences/Twitch.js"],
|
||||
"matches": ["https://*.twitch.tv/*"]
|
||||
}
|
||||
],
|
||||
"permissions": ["http://localhost:3000/", "tabs", "storage"]
|
||||
}
|
||||
137
Extension/presences/Netflix.js
Normal file
@@ -0,0 +1,137 @@
|
||||
let playback = true,
|
||||
eventType,
|
||||
playbackNew
|
||||
|
||||
var lastPlaybackState = true
|
||||
setInterval(() => {
|
||||
if($('.VideoContainer div video')[0] != null && $('.VideoContainer div video')[0].paused && lastPlaybackState == true) {
|
||||
handlePlayPause()
|
||||
lastPlaybackState = false;
|
||||
}
|
||||
if($('.VideoContainer div video')[0] != null && !$('.VideoContainer div video')[0].paused && lastPlaybackState == false) {
|
||||
handlePlayPause()
|
||||
lastPlaybackState = true;
|
||||
}
|
||||
}, 500)
|
||||
|
||||
//* Create socket connection to application
|
||||
var socket = io.connect('http://localhost:3020/');
|
||||
|
||||
//* Log when connected
|
||||
socket.on('connect', function () {
|
||||
console.log('YT Presence: %cConnected to Application', "color: green; font-weight: 700")
|
||||
if(sessionStorage['ytpconnected'] == null || sessionStorage['ytpconnected'] == 'false') {
|
||||
$('<div id="ytp-connectinfo"><img draggable="false" src="//github.com/Timeraa/YT-Presence/blob/master/icon.png?raw=true"><h1>YT Presence</h1><h2>Connected</h2></div>').appendTo('body')
|
||||
setTimeout(() => {
|
||||
$('#ytp-connectinfo').remove()
|
||||
}, 5*1000)
|
||||
sessionStorage['ytpconnected'] = 'true'
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
sessionStorage['ytpconnected'] = 'false'
|
||||
$('<div id="ytp-connectinfo"><img draggable="false" src="//github.com/Timeraa/YT-Presence/blob/master/icon.png?raw=true"><h1>YT Presence</h1><h2>Disconnected</h2></div>').appendTo('body')
|
||||
setTimeout(() => {
|
||||
$('#ytp-connectinfo').remove()
|
||||
}, 5*1000)
|
||||
})
|
||||
|
||||
//* When we receive messages from the application
|
||||
socket.on('mediaKeyHandler', function (data) {
|
||||
//* Check if the data is for YTM & if music is running
|
||||
//* Media control buttons
|
||||
if (musicRunning) {
|
||||
//* Switch cases for playback / Clicks corresponding buttons
|
||||
switch (data.playback) {
|
||||
case "pause":
|
||||
$('.VideoContainer div video')[0].paused ? $('.VideoContainer div video')[0].play() : $('.VideoContainer div video')[0].pause()
|
||||
updateData("playPauseVideo")
|
||||
break
|
||||
case "nextTrack":
|
||||
$('.button-nfplayerNextEpisode').click()
|
||||
//* Prevent playback from being paused again
|
||||
playback = true
|
||||
//* Send response back to application
|
||||
updateData("nextVideo")
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
function handlePlayPause() {
|
||||
//* Toggle playback variable
|
||||
if (playback == true) playback = false; else playback = true;
|
||||
//* Send status to application
|
||||
updateData(playback ? "playing" : "paused")
|
||||
}
|
||||
|
||||
function checkPlayChange() {
|
||||
//* Correct playback if out of sync
|
||||
if (playback == false) {
|
||||
//* Check if playbutton on page matches variable
|
||||
if ($('.ytp-play-button svg').prop("outerHTML") == playButton) {
|
||||
//* Update playback variable
|
||||
playback = true
|
||||
//* Pause song
|
||||
$('.ytp-play-button').click()
|
||||
}
|
||||
}
|
||||
|
||||
//* Set musicRunning variable to true if url has /watch or music title not empty
|
||||
if (document.location.pathname.includes("/watch")) musicRunning = true; else musicRunning = false;
|
||||
}
|
||||
|
||||
//* Start interval
|
||||
window.onload = function () {
|
||||
setInterval(updateData, 1000)
|
||||
}
|
||||
|
||||
//* Create needed variables
|
||||
let urlForVideo,
|
||||
songTime,
|
||||
calcDifference = []
|
||||
|
||||
function updateData(playbackChange = false) {
|
||||
if (document.location.pathname.includes("/watch")) musicRunning = true; else musicRunning = false;
|
||||
urlForVideo = document.location.href
|
||||
if ($(".time-remaining__time").html() != "") {
|
||||
let data
|
||||
|
||||
if (playbackChange != false) {
|
||||
var eventType = 'playBackChange'
|
||||
} else {
|
||||
var eventType = 'updateData'
|
||||
}
|
||||
|
||||
var endTime
|
||||
if(musicRunning && $('.VideoContainer div video')[0] != undefined) {
|
||||
var startTime = Date.now();
|
||||
endTime = Math.floor(startTime / 1000) -
|
||||
Math.floor($('.VideoContainer div video')[0].currentTime) +
|
||||
Math.floor($('.VideoContainer div video')[0].duration);
|
||||
|
||||
data = {
|
||||
nflix: {
|
||||
url: urlForVideo,
|
||||
seriesTitle: $('.ellipsize-text').children().html(),
|
||||
season: $($('.ellipsize-text').children().get(1)).html(),
|
||||
episodeTitle: $($('.ellipsize-text').children().get(2)).html(),
|
||||
episodeCurrentTimeSeconds: Math.floor($('.VideoContainer div video')[0].currentTime),
|
||||
episodeEndTimeSeconds: Math.floor($('.VideoContainer div video')[0].duration),
|
||||
episodeCurrentTime: startTime,
|
||||
episodeEndTime: endTime,
|
||||
playback: $('.VideoContainer div video')[0].paused ? "paused" : "playing"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data = {
|
||||
nflix: {
|
||||
playback: false
|
||||
}
|
||||
}
|
||||
}
|
||||
if (socket.connected) socket.emit(eventType, data)
|
||||
}
|
||||
}
|
||||
144
Extension/presences/SoundCloud.js
Normal file
@@ -0,0 +1,144 @@
|
||||
let playback = true,
|
||||
eventType,
|
||||
playbackNew
|
||||
|
||||
var lastPlaybackState = true
|
||||
setInterval(() => {
|
||||
if($('.playControl').hasClass('playing') && lastPlaybackState == true) {
|
||||
handlePlayPause()
|
||||
lastPlaybackState = false;
|
||||
}
|
||||
if(!$('.playControl').hasClass('playing') && lastPlaybackState == false) {
|
||||
handlePlayPause()
|
||||
lastPlaybackState = true;
|
||||
}
|
||||
}, 500)
|
||||
|
||||
//* Create socket connection to application
|
||||
var socket = io.connect('http://localhost:3020/');
|
||||
|
||||
//* Log when connected
|
||||
socket.on('connect', function () {
|
||||
console.log('YT Presence: %cConnected to Application', "color: green; font-weight: 700")
|
||||
if(sessionStorage['ytpconnected'] == null || sessionStorage['ytpconnected'] == 'false') {
|
||||
$('<div id="ytp-connectinfo"><img draggable="false" src="//github.com/Timeraa/YT-Presence/blob/master/icon.png?raw=true"><h1>YT Presence</h1><h2>Connected</h2></div>').appendTo('body')
|
||||
setTimeout(() => {
|
||||
$('#ytp-connectinfo').remove()
|
||||
}, 5*1000)
|
||||
sessionStorage['ytpconnected'] = 'true'
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
sessionStorage['ytpconnected'] = 'false'
|
||||
$('<div id="ytp-connectinfo"><img draggable="false" src="//github.com/Timeraa/YT-Presence/blob/master/icon.png?raw=true"><h1>YT Presence</h1><h2>Disconnected</h2></div>').appendTo('body')
|
||||
setTimeout(() => {
|
||||
$('#ytp-connectinfo').remove()
|
||||
}, 5*1000)
|
||||
})
|
||||
|
||||
//* When we receive messages from the application
|
||||
socket.on('mediaKeyHandler', function (data) {
|
||||
//* Check if the data is for YTM & if music is running
|
||||
//* Media control buttons
|
||||
if ($('.playControl') != undefined) {
|
||||
//* Switch cases for playback / Clicks corresponding buttons
|
||||
switch (data.playback) {
|
||||
case "pause":
|
||||
$('.playControl').click()
|
||||
updateData("playPauseVideo")
|
||||
break
|
||||
case "nextTrack":
|
||||
$('.skipControl__next').click()
|
||||
//* Prevent playback from being paused again
|
||||
playback = true
|
||||
//* Send response back to application
|
||||
updateData("nextVideo")
|
||||
break
|
||||
case "previousTrack":
|
||||
$('.skipControl__previous').click()
|
||||
//* Prevent playback from being paused again
|
||||
playback = true
|
||||
//* Send response back to application
|
||||
updateData("nextVideo")
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
function handlePlayPause() {
|
||||
//* Toggle playback variable
|
||||
if (playback == true) playback = false; else playback = true;
|
||||
//* Send status to application
|
||||
updateData(playback ? "playing" : "paused")
|
||||
}
|
||||
|
||||
function checkPlayChange() {
|
||||
//* Correct playback if out of sync
|
||||
if (playback == false) {
|
||||
//* Check if playbutton on page matches variable
|
||||
if ($('.ytp-play-button svg').prop("outerHTML") == playButton) {
|
||||
//* Update playback variable
|
||||
playback = true
|
||||
//* Pause song
|
||||
$('.ytp-play-button').click()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//* Start interval
|
||||
window.onload = function () {
|
||||
setInterval(updateData, 1000)
|
||||
}
|
||||
|
||||
//* Create needed variables
|
||||
let urlForVideo,
|
||||
songTime,
|
||||
calcDifference = []
|
||||
|
||||
function updateData(playbackChange = false) {
|
||||
urlForVideo = document.location.href
|
||||
if ($(".time-remaining__time").html() != "") {
|
||||
let data
|
||||
|
||||
if (playbackChange != false) {
|
||||
var eventType = 'playBackChange'
|
||||
} else {
|
||||
var eventType = 'updateData'
|
||||
}
|
||||
|
||||
var endTime
|
||||
if($('.playbackSoundBadge__titleContextContainer') != undefined) {
|
||||
var startTime = Date.now();
|
||||
endTime = Math.floor(startTime/1000) -
|
||||
getSeconds($('.playbackTimeline__timePassed').children().get(1).innerHTML) + getSeconds($('.playbackTimeline__duration').children().get(1).innerHTML);
|
||||
data = {
|
||||
scloud: {
|
||||
url: urlForVideo,
|
||||
songTitle: $('.playbackSoundBadge__titleLink').children().get(1).innerHTML,
|
||||
songAuthor: $('.playbackSoundBadge__titleContextContainer').children().get(0).innerHTML,
|
||||
songCurrentTimeSeconds: getSeconds($('.playbackTimeline__timePassed').children().get(1).innerHTML),
|
||||
songEndTimeSeconds: getSeconds($('.playbackTimeline__duration').children().get(1).innerHTML),
|
||||
songCurrentTime: startTime,
|
||||
songEndTime: endTime,
|
||||
playback: $('.playControl').hasClass('playing') ? "playing" : "paused"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data = {
|
||||
scloud: {
|
||||
playback: false
|
||||
}
|
||||
}
|
||||
}
|
||||
if (socket.connected) socket.emit(eventType, data)
|
||||
}
|
||||
}
|
||||
|
||||
function getSeconds(string) {
|
||||
const a = string.split(":")
|
||||
|
||||
const seconds = +a[0] * 60 + +a[1]
|
||||
return seconds
|
||||
}
|
||||
121
Extension/presences/Twitch.js
Normal file
@@ -0,0 +1,121 @@
|
||||
let playback = true,
|
||||
eventType,
|
||||
playbackNew,
|
||||
lastURL = null,
|
||||
startTimeStamp
|
||||
|
||||
var lastPlaybackState = true
|
||||
setInterval(() => {
|
||||
if($('.player-video video')[0] != null && $('.player-video video')[0].paused && lastPlaybackState == true) {
|
||||
handlePlayPause()
|
||||
lastPlaybackState = false;
|
||||
}
|
||||
if($('.player-video video')[0] != null && !$('.player-video video')[0].paused && lastPlaybackState == false) {
|
||||
handlePlayPause()
|
||||
lastPlaybackState = true;
|
||||
}
|
||||
}, 500)
|
||||
|
||||
//* Create socket connection to application
|
||||
var socket = io.connect('http://localhost:3020/');
|
||||
|
||||
//* Log when connected
|
||||
socket.on('connect', function () {
|
||||
console.log('YT Presence: %cConnected to Application', "color: green; font-weight: 700")
|
||||
if(sessionStorage['ytpconnected'] == null || sessionStorage['ytpconnected'] == 'false') {
|
||||
$('<div id="ytp-connectinfo"><img draggable="false" src="//github.com/Timeraa/YT-Presence/blob/master/icon.png?raw=true"><h1>YT Presence</h1><h2>Connected</h2></div>').appendTo('body')
|
||||
setTimeout(() => {
|
||||
$('#ytp-connectinfo').remove()
|
||||
}, 5*1000)
|
||||
sessionStorage['ytpconnected'] = 'true'
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
sessionStorage['ytpconnected'] = 'false'
|
||||
$('<div id="ytp-connectinfo"><img draggable="false" src="//github.com/Timeraa/YT-Presence/blob/master/icon.png?raw=true"><h1>YT Presence</h1><h2>Disconnected</h2></div>').appendTo('body')
|
||||
setTimeout(() => {
|
||||
$('#ytp-connectinfo').remove()
|
||||
}, 5*1000)
|
||||
})
|
||||
|
||||
//* When we receive messages from the application
|
||||
socket.on('mediaKeyHandler', function (data) {
|
||||
//* Check if the data is for YTM & if music is running
|
||||
//* Media control buttons
|
||||
if ($('.player-video video')[0] != null) {
|
||||
//* Switch cases for playback / Clicks corresponding buttons
|
||||
switch (data.playback) {
|
||||
case "pause":
|
||||
$('.player-video video')[0].paused ? $('.qa-pause-play-button').click() : $('.qa-pause-play-button').click()
|
||||
updateData("playPauseVideo")
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
function handlePlayPause() {
|
||||
//* Toggle playback variable
|
||||
if (playback == true) playback = false; else playback = true;
|
||||
//* Send status to application
|
||||
updateData(playback ? "playing" : "paused")
|
||||
}
|
||||
|
||||
function checkPlayChange() {
|
||||
//* Correct playback if out of sync
|
||||
if (playback == false) {
|
||||
//* Check if playbutton on page matches variable
|
||||
if ($('.ytp-play-button svg').prop("outerHTML") == playButton) {
|
||||
//* Update playback variable
|
||||
playback = true
|
||||
//* Pause song
|
||||
$('.ytp-play-button').click()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//* Start interval
|
||||
window.onload = function () {
|
||||
setInterval(updateData, 1000)
|
||||
}
|
||||
|
||||
//* Create needed variables
|
||||
let urlForVideo,
|
||||
songTime,
|
||||
calcDifference = []
|
||||
|
||||
function updateData(playbackChange = false) {
|
||||
urlForVideo = document.location.href
|
||||
let data
|
||||
|
||||
if (playbackChange != false) {
|
||||
var eventType = 'playBackChange'
|
||||
} else {
|
||||
var eventType = 'updateData'
|
||||
}
|
||||
|
||||
if($('.player-video video')[0] != undefined && $('.tw-ellipsis.tw-mg-b-05').children().length > 0) {
|
||||
if(urlForVideo != lastURL) {
|
||||
lastURL = urlForVideo
|
||||
startTimeStamp = Date.now()
|
||||
}
|
||||
|
||||
data = {
|
||||
twitch: {
|
||||
url: urlForVideo,
|
||||
streamTitle: $('.tw-ellipsis.tw-mg-b-05').children().get(0).innerHTML,
|
||||
streamHost: $('.channel-header__user h5').html(),
|
||||
watchingSince: startTimeStamp,
|
||||
playback: $('.player-video video')[0].paused ? "paused" : "playing"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data = {
|
||||
twitch: {
|
||||
playback: false
|
||||
}
|
||||
}
|
||||
}
|
||||
if (socket.connected) socket.emit(eventType, data)
|
||||
}
|
||||
150
Extension/presences/YouTube.js
Normal file
@@ -0,0 +1,150 @@
|
||||
var pauseButton = '<svg height="100%" version="1.1" viewBox="0 0 36 36" width="100%"><use class="ytp-svg-shadow" xlink:href="#ytp-id-91"></use><path class="ytp-svg-fill" d="M 12,26 18.5,22 18.5,14 12,10 z M 18.5,22 25,18 25,18 18.5,14 z" id="ytp-id-91"></path></svg>'
|
||||
var pauseButton = '<svg height="100%" version="1.1" viewBox="0 0 36 36" width="100%"><use class="ytp-svg-shadow" xlink:href="#ytp-id-93"></use><path class="ytp-svg-fill" d="M 12,26 16,26 16,10 12,10 z M 21,26 25,26 25,10 21,10 z" id="ytp-id-93"></path></svg>'
|
||||
|
||||
let playback = true,
|
||||
eventType,
|
||||
playbackNew
|
||||
|
||||
//* Register listeners
|
||||
$('.video-stream').click(handlePlayPause)
|
||||
$('.ytp-play-button').click(handlePlayPause)
|
||||
|
||||
//* Create socket connection to application
|
||||
var socket = io.connect('http://localhost:3020/');
|
||||
|
||||
//* Log when connected
|
||||
socket.on('connect', function () {
|
||||
console.log('YT Presence: %cConnected to Application', "color: green; font-weight: 700")
|
||||
if(sessionStorage['ytpconnected'] == null || sessionStorage['ytpconnected'] == 'false') {
|
||||
$('<div id="ytp-connectinfo"><img draggable="false" src="//github.com/Timeraa/YT-Presence/blob/master/icon.png?raw=true"><h1>YT Presence</h1><h2>Connected</h2></div>').appendTo('body')
|
||||
setTimeout(() => {
|
||||
//$('#ytp-connectinfo').remove()
|
||||
}, 5*1000)
|
||||
sessionStorage['ytpconnected'] = 'true'
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
sessionStorage['ytpconnected'] = 'false'
|
||||
$('<div id="ytp-connectinfo"><img draggable="false" src="//github.com/Timeraa/YT-Presence/blob/master/icon.png?raw=true"><h1>YT Presence</h1><h2>Disconnected</h2></div>').appendTo('body')
|
||||
setTimeout(() => {
|
||||
$('#ytp-connectinfo').remove()
|
||||
}, 5*1000)
|
||||
})
|
||||
|
||||
//* When we receive messages from the application
|
||||
socket.on('mediaKeyHandler', function (data) {
|
||||
//* Check if the data is for YTM & if music is running
|
||||
//* Media control buttons
|
||||
if (musicRunning) {
|
||||
//* Switch cases for playback / Clicks corresponding buttons
|
||||
switch (data.playback) {
|
||||
case "pause":
|
||||
$('.video-stream').click()
|
||||
updateData("playPauseVideo")
|
||||
break
|
||||
case "nextTrack":
|
||||
$('.ytp-next-button')[0].click()
|
||||
//* Prevent playback from being paused again
|
||||
playback = true
|
||||
//* Send response back to application
|
||||
updateData("nextVideo")
|
||||
break
|
||||
case "previousTrack":
|
||||
$('.ytp-prev-button')[0].click()
|
||||
//* Send response back to application
|
||||
updateData("previousVideo")
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
function handlePlayPause() {
|
||||
//* Toggle playback variable
|
||||
if (playback == true) playback = false; else playback = true;
|
||||
//* Send status to application
|
||||
updateData(playback ? "playing" : "paused")
|
||||
}
|
||||
|
||||
function checkPlayChange() {
|
||||
//* Correct playback if out of sync
|
||||
if (playback == false) {
|
||||
//* Check if playbutton on page matches variable
|
||||
if ($('.ytp-play-button svg').prop("outerHTML") == playButton) {
|
||||
//* Update playback variable
|
||||
playback = true
|
||||
//* Pause song
|
||||
$('.ytp-play-button').click()
|
||||
}
|
||||
}
|
||||
|
||||
//* Set musicRunning variable to true if url has /watch or music title not empty
|
||||
if (document.location.pathname.includes("/watch") || $(".title.style-scope.ytmusic-player-bar").html() != "") musicRunning = true; else musicRunning = false;
|
||||
}
|
||||
|
||||
//* Start interval
|
||||
window.onload = function () {
|
||||
setInterval(updateData, 1000)
|
||||
}
|
||||
|
||||
//* Create needed variables
|
||||
let urlForVideo,
|
||||
songTime,
|
||||
calcDifference = []
|
||||
|
||||
function updateData(playbackChange = false) {
|
||||
if (document.location.pathname.includes("/watch")) musicRunning = true; else musicRunning = false;
|
||||
urlForVideo = document.location.href
|
||||
if ($(".ytp-time-current").html() != " " && $('.video-stream')[0] != null) {
|
||||
let data
|
||||
|
||||
if (playbackChange != false) {
|
||||
var playbackNew = playbackChange
|
||||
var eventType = 'playBackChange'
|
||||
} else {
|
||||
var playbackNew = playback ? "playing" : "paused"
|
||||
var eventType = 'updateData'
|
||||
}
|
||||
|
||||
var endTime
|
||||
if(musicRunning) {
|
||||
var startTime = Date.now();
|
||||
endTime = Math.floor(startTime / 1000) -
|
||||
Math.floor($('.video-stream')[0].currentTime) +
|
||||
Math.floor($('.video-stream')[0].duration);
|
||||
|
||||
data = {
|
||||
yt: {
|
||||
url: urlForVideo,
|
||||
videoTitle: $(".title.style-scope.ytd-video-primary-info-renderer")
|
||||
.children().html(),
|
||||
videoAuthor: $("#upload-info .style-scope .ytd-video-owner-renderer")
|
||||
.contents()
|
||||
.first()
|
||||
.html(),
|
||||
videoCurrentTimeSeconds: Math.floor($('.video-stream')[0].currentTime),
|
||||
videoEndTimeSeconds: Math.floor($('.video-stream')[0].duration),
|
||||
videoCurrentTime: startTime,
|
||||
videoEndTime: endTime,
|
||||
playback: playbackNew
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(document.location.pathname.startsWith('www.youtube.com')) console.log("NICE")
|
||||
data = {
|
||||
yt: {
|
||||
playback: false
|
||||
}
|
||||
}
|
||||
}
|
||||
if (socket.connected && !isNaN($('.video-stream')[0].duration)) socket.emit(eventType, data)
|
||||
}
|
||||
}
|
||||
|
||||
function getSeconds(string) {
|
||||
const a = string.split(":")
|
||||
|
||||
const seconds = +a[0] * 60 + +a[1]
|
||||
return seconds
|
||||
}
|
||||
170
Extension/presences/YouTubeMusic.js
Normal file
@@ -0,0 +1,170 @@
|
||||
//* Play Pause buttons
|
||||
var playButton = '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope iron-icon" style="pointer-events: none; display: block; width: 100%; height: 100%;"><g class="style-scope iron-icon"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" class="style-scope iron-icon"></path></g></svg>'
|
||||
var pauseButton = '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope iron-icon" style="pointer-events: none; display: block; width: 100%; height: 100%;"><g class="style-scope iron-icon"><path d="M8 5v14l11-7z" class="style-scope iron-icon"></path></g></svg>'
|
||||
|
||||
//* Create needed variables
|
||||
let splitTime,
|
||||
songCurrentTime,
|
||||
songEndTime,
|
||||
songAuthors = [],
|
||||
playback = true,
|
||||
eventType
|
||||
|
||||
//* Register listeners
|
||||
$('.play-pause-button').click(handlePlayPause)
|
||||
$('.ytmusic-player').click(handlePlayPause)
|
||||
//* Start interval
|
||||
$(document).ready(() => {
|
||||
//* Check Play change
|
||||
setInterval(checkPlayChange, 250)
|
||||
//* Update data and send to application
|
||||
setInterval(updateData, 1000)
|
||||
})
|
||||
|
||||
//* Create socket connection to application
|
||||
var socket = io.connect('http://localhost:3020/');
|
||||
|
||||
//* Log when connected
|
||||
socket.on('connect', function () {
|
||||
console.log('YT Presence: %cConnected to Application', "color: green; font-weight: 700")
|
||||
if(sessionStorage['ytpconnected'] == null || sessionStorage['ytpconnected'] == 'false') {
|
||||
$('<div id="ytp-connectinfo"><img draggable="false" src="//github.com/Timeraa/YT-Presence/blob/master/icon.png?raw=true"><h1>YT Presence</h1><h2>Connected</h2></div>').appendTo('body')
|
||||
setTimeout(() => {
|
||||
$('#ytp-connectinfo').remove()
|
||||
}, 5*1000)
|
||||
sessionStorage['ytpconnected'] = 'true'
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
sessionStorage['ytpconnected'] = 'false'
|
||||
$('<div id="ytp-connectinfo"><img draggable="false" src="//github.com/Timeraa/YT-Presence/blob/master/icon.png?raw=true"><h1>YT Presence</h1><h2>Disconnected</h2></div>').appendTo('body')
|
||||
setTimeout(() => {
|
||||
$('#ytp-connectinfo').remove()
|
||||
}, 5*1000)
|
||||
})
|
||||
|
||||
socket.on('error', (err) => console.log(`Error while connecting... ${err}`))
|
||||
|
||||
//* When we receive messages from the application
|
||||
socket.on('mediaKeyHandler', function (data) {
|
||||
//* Check if the data is for YTM & if music is running
|
||||
//* Media control buttons
|
||||
if (musicRunning) {
|
||||
//* Switch cases for playback / Clicks corresponding buttons
|
||||
switch (data.playback) {
|
||||
case "pause":
|
||||
$('.play-pause-button').click()
|
||||
updateData("playPauseTrack")
|
||||
break
|
||||
case "nextTrack":
|
||||
$('.next-button').click()
|
||||
//* Prevent playback from being paused again
|
||||
playback = true
|
||||
//* Send response back to application
|
||||
updateData("nextTrack")
|
||||
break
|
||||
case "previousTrack":
|
||||
$('.previous-button').click()
|
||||
//* Send response back to application
|
||||
updateData("previousTrack")
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function handlePlayPause() {
|
||||
//* Toggle playback variable
|
||||
if (playback == true) playback = false; else playback = true;
|
||||
//* Send status to application
|
||||
updateData(playback ? "playing" : "paused")
|
||||
}
|
||||
|
||||
function checkPlayChange() {
|
||||
//* Correct playback if out of sync
|
||||
if (playback == false) {
|
||||
//* Check if playbutton on page matches variable
|
||||
if ($(".play-pause-button svg").prop("outerHTML") == playButton) {
|
||||
//* Update playback variable
|
||||
playback = true
|
||||
//* Pause song
|
||||
$('.play-pause-button').click()
|
||||
}
|
||||
}
|
||||
|
||||
//* Set musicRunning variable to true if url has /watch or music title not empty
|
||||
if (document.location.pathname.includes("/watch") || $(".title.style-scope.ytmusic-player-bar").html() != "") musicRunning = true; else musicRunning = false;
|
||||
}
|
||||
|
||||
function updateData(playbackChange = false) {
|
||||
//* Clear author array
|
||||
songAuthors = []
|
||||
|
||||
if ($(".title.style-scope.ytmusic-player-bar").html() != "") {
|
||||
//* Get song Time String (2:10 / 3:21)
|
||||
//* Split to array ["2:10", "3:21"]
|
||||
splitTime = $(".time-info.style-scope.ytmusic-player-bar").html().split(" / ", 2)
|
||||
//* Convert to seconds
|
||||
songCurrentTime = getSeconds(splitTime[0])
|
||||
songEndTime = getSeconds(splitTime[1])
|
||||
|
||||
//* Get all authors
|
||||
$(".byline.ytmusic-player-bar").contents().each(function (index, item) {
|
||||
if (item.classList != undefined) {
|
||||
if (item.classList.contains("yt-simple-endpoint") == true) {
|
||||
songAuthors.push(item.innerHTML)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//* If no authors found in previous method use this
|
||||
if (songAuthors.length == 0 || songAuthors.length == 1) {
|
||||
//* Clear old list
|
||||
songAuthors = []
|
||||
songAuthors.push($(".byline.ytmusic-player-bar").contents().first().text())
|
||||
}
|
||||
|
||||
var startTime = Date.now();
|
||||
var endTime =
|
||||
Math.floor(startTime / 1000) -
|
||||
songCurrentTime +
|
||||
songEndTime;
|
||||
|
||||
if (playbackChange != false) {
|
||||
playbackNew = playbackChange
|
||||
eventType = 'playBackChange'
|
||||
} else {
|
||||
playbackNew = playback ? "playing" : "paused"
|
||||
eventType = 'updateData'
|
||||
}
|
||||
|
||||
var data = {
|
||||
ytm: {
|
||||
songTitle: $(".title.style-scope.ytmusic-player-bar").html(),
|
||||
songAuthors: songAuthors,
|
||||
songCurrentTimeSeconds: songCurrentTime,
|
||||
songCurrentTime: startTime,
|
||||
songEndTimeSeconds: songEndTime,
|
||||
songEndTime: endTime,
|
||||
songCover: $(".image.style-scope.ytmusic-player-bar").attr("src"),
|
||||
playback: playbackNew
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var data = {
|
||||
status: "keepAlive"
|
||||
}
|
||||
eventType = 'updateData'
|
||||
}
|
||||
|
||||
if(socket.connected) socket.emit(eventType, data)
|
||||
}
|
||||
|
||||
//* Used to extract seconds from Syntax
|
||||
//* 1:39 => 99
|
||||
function getSeconds(string) {
|
||||
const s = string.split(":")
|
||||
|
||||
const seconds = +s[0] * 60 + +s[1]
|
||||
return seconds
|
||||
}
|
||||
2
Extension/util/jquery-3.3.1.min.js
vendored
Normal file
7
Extension/util/socket.io-2.1.1.min.js
vendored
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Florian Metz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
115
README.md
Normal file
@@ -0,0 +1,115 @@
|
||||
##  [](https://discord.gg/Kw7WaYn)
|
||||
|
||||
# <img src="icon.png" width="20" draggable="false"><b> </b>YouTube Presence · Watcha watching there? 👀
|
||||
|
||||
_YT Presence Features like **Discord Rich Presence** integration, **Media controls** to **YouTube**/**YouTube Music**, **Twitch**, **SoundCloud**, **Netflix**._
|
||||
|
||||
<a href="https://discord.gg/Kw7WaYn" title="Join our Discord!">
|
||||
<img src="discord-logo.svg" width="200px" draggable="false">
|
||||
</a>
|
||||
|
||||
# Table of contents
|
||||
|
||||
- [Features](#features)
|
||||
- [Support](#support)
|
||||
- [Installation](#installation)
|
||||
- [Installing the Extension](#installing-the-extension)
|
||||
- Chrome
|
||||
- Opera
|
||||
- [Installing the Application](#installing-the-application)
|
||||
- Mac OS
|
||||
- Windows
|
||||
- [FAQ](#faq-·-frequently-asked-questions)
|
||||
- Is this project up to date?
|
||||
- Will this project stay free?
|
||||
|
||||
# Features
|
||||
|
||||
- Discord Rich Presence Integration
|
||||
- Media control keys
|
||||
- Automatically clears Presence after 1 minute of inactivity
|
||||
- _More soon_
|
||||
|
||||
# Support
|
||||
|
||||
### **Operating Systems**
|
||||
|
||||
- Windows
|
||||
- Mac OS
|
||||
|
||||
### **Browsers**
|
||||
|
||||
- Chrome
|
||||
- Opera
|
||||
|
||||
# Installation
|
||||
|
||||
## Extension
|
||||
|
||||
<details>
|
||||
<summary><b><u>Installing the Chrome extension</u></b> (Click to expand)</summary>
|
||||
<h1>Chrome extension installation</h1>
|
||||
<ol>
|
||||
<li>Click <a href="https://chrome.google.com/webstore/detail/yt-presence/agjnjboanicjcpenljmaaigopkgdnihi">this</a> link</li>
|
||||
</li>
|
||||
<li>Click "add to Chrome"</li>
|
||||
<li>Install the <a href="#installing-the-application">application</a></li>
|
||||
</ol>
|
||||
</details>
|
||||
<details>
|
||||
<summary><b><u>Installing the Opera extension</u></b> (Click to expand)</summary>
|
||||
<h1>Opera extension installation</h1>
|
||||
<ol>
|
||||
<li>Download the latest version of the <a href="https://github.com/Timeraa/YT-Presence/releases/latest">extension</a>
|
||||
</li>
|
||||
<li>Extract the downloaded <b>.zip</b> file</li>
|
||||
<li>Open Opera</li>
|
||||
<li>Go to <a href="chrome://extensions/">chrome://extensions/</a></li>
|
||||
<li>Drag and drop the extension's folder on the page<br>
|
||||
<li>Load the extracted <b>Extension</b> folder</li>
|
||||
<li>Install the <a href="#installing-the-application">application</a></li>
|
||||
</ol>
|
||||
</details>
|
||||
|
||||
## Application
|
||||
|
||||
<details>
|
||||
<summary><b><u>Mac OS</u></b> (Click to expand)</summary>
|
||||
<h1>Installation on Mac OS</h1>
|
||||
<ol>
|
||||
<li>Download the latest version of the <a href="https://github.com/Timeraa/YT-Presence/releases/latest">application</a>
|
||||
</li>
|
||||
<li>Open the downloaded <b>.dmg</b> file</li>
|
||||
<li>Drag <b>YT Presence</b> Into your <b>Applications</b> Folder</li>
|
||||
<li>Open your Launchpad</li>
|
||||
<li>Open <b>YT Presence</b></li>
|
||||
<li>Install Extension if not already</li>
|
||||
</ol>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b><u>Windows</u></b> (Click to expand)</summary>
|
||||
<h1>Installation on Windows</h1>
|
||||
<ol>
|
||||
<li>Download the latest installer from <a href="https://github.com/Timeraa/YT-Presence/releases/">here</a></li>
|
||||
<li>Open the downloaded <b>.exe</b> installer</li>
|
||||
<li>If SmartScreen comes up press more informations then press run anyways. (Its not a virus, i promise)</li>
|
||||
<li>YouTube Presence should install itself and start automatically (You can tell by looking at the taskbar)</li>
|
||||
<li>Install Extension if not already</li>
|
||||
</ol>
|
||||
</details>
|
||||
<br>
|
||||
|
||||
# FAQ · Frequently Asked Questions
|
||||
|
||||
> ## Is this project still up to date?<br>
|
||||
|
||||
- Yes! I use and work on this program and extension almost everyday!<br>There are currently no plans on discontinuing this Project.
|
||||
|
||||
> ## Will this project stay free?<br>
|
||||
|
||||
- Of course! Its open source! But i would highly appreciate a little bit of Money it helps me for my Future, like a **Driving license**. <br><br><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZU8Q766ACS2WS&lc=US">Donate here</a>
|
||||
|
||||
---
|
||||
|
||||
### YT Presence V1.1 · By Timeraa
|
||||
BIN
appIcon.icns
Normal file
BIN
appIcon.ico
Normal file
|
After Width: | Height: | Size: 36 KiB |
11
createInstaller.js
Normal file
@@ -0,0 +1,11 @@
|
||||
var electronInstaller = require('electron-winstaller');
|
||||
|
||||
resultPromise = electronInstaller.createWindowsInstaller({
|
||||
appDirectory: './out/YT Presence-win32-x64',
|
||||
outputDirectory: './dist/windows/',
|
||||
exe: './YT Presence.exe',
|
||||
iconUrl: 'https://raw.githubusercontent.com/Timeraa/YT-Presence/master/appIcon.ico',
|
||||
noMsi: true
|
||||
});
|
||||
|
||||
resultPromise.then(() => console.log("It worked!"), (e) => console.log(`No dice: ${e.message}`));
|
||||
BIN
developerMode.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
1
discord-logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 272.1"><style>.st0{fill:#7289DA;}</style><path class="st0" d="M142.8 120.1c-5.7 0-10.2 4.9-10.2 11s4.6 11 10.2 11c5.7 0 10.2-4.9 10.2-11s-4.6-11-10.2-11zM106.3 120.1c-5.7 0-10.2 4.9-10.2 11s4.6 11 10.2 11c5.7 0 10.2-4.9 10.2-11 .1-6.1-4.5-11-10.2-11z"/><path class="st0" d="M191.4 36.9h-134c-11.3 0-20.5 9.2-20.5 20.5v134c0 11.3 9.2 20.5 20.5 20.5h113.4l-5.3-18.3 12.8 11.8 12.1 11.1 21.6 18.7V57.4c-.1-11.3-9.3-20.5-20.6-20.5zm-38.6 129.5s-3.6-4.3-6.6-8c13.1-3.7 18.1-11.8 18.1-11.8-4.1 2.7-8 4.6-11.5 5.9-5 2.1-9.8 3.4-14.5 4.3-9.6 1.8-18.4 1.3-25.9-.1-5.7-1.1-10.6-2.6-14.7-4.3-2.3-.9-4.8-2-7.3-3.4-.3-.2-.6-.3-.9-.5-.2-.1-.3-.2-.4-.2-1.8-1-2.8-1.7-2.8-1.7s4.8 7.9 17.5 11.7c-3 3.8-6.7 8.2-6.7 8.2-22.1-.7-30.5-15.1-30.5-15.1 0-31.9 14.4-57.8 14.4-57.8 14.4-10.7 28-10.4 28-10.4l1 1.2c-18 5.1-26.2 13-26.2 13s2.2-1.2 5.9-2.8c10.7-4.7 19.2-5.9 22.7-6.3.6-.1 1.1-.2 1.7-.2 6.1-.8 13-1 20.2-.2 9.5 1.1 19.7 3.9 30.1 9.5 0 0-7.9-7.5-24.9-12.6l1.4-1.6s13.7-.3 28 10.4c0 0 14.4 25.9 14.4 57.8 0-.1-8.4 14.3-30.5 15zM303.8 79.7h-33.2V117l22.1 19.9v-36.2h11.8c7.5 0 11.2 3.6 11.2 9.4v27.7c0 5.8-3.5 9.7-11.2 9.7h-34v21.1h33.2c17.8.1 34.5-8.8 34.5-29.2v-29.8c.1-20.8-16.6-29.9-34.4-29.9zm174 59.7v-30.6c0-11 19.8-13.5 25.8-2.5l18.3-7.4c-7.2-15.8-20.3-20.4-31.2-20.4-17.8 0-35.4 10.3-35.4 30.3v30.6c0 20.2 17.6 30.3 35 30.3 11.2 0 24.6-5.5 32-19.9l-19.6-9c-4.8 12.3-24.9 9.3-24.9-1.4zM417.3 113c-6.9-1.5-11.5-4-11.8-8.3.4-10.3 16.3-10.7 25.6-.8l14.7-11.3c-9.2-11.2-19.6-14.2-30.3-14.2-16.3 0-32.1 9.2-32.1 26.6 0 16.9 13 26 27.3 28.2 7.3 1 15.4 3.9 15.2 8.9-.6 9.5-20.2 9-29.1-1.8l-14.2 13.3c8.3 10.7 19.6 16.1 30.2 16.1 16.3 0 34.4-9.4 35.1-26.6 1-21.7-14.8-27.2-30.6-30.1zm-67 55.5h22.4V79.7h-22.4v88.8zM728 79.7h-33.2V117l22.1 19.9v-36.2h11.8c7.5 0 11.2 3.6 11.2 9.4v27.7c0 5.8-3.5 9.7-11.2 9.7h-34v21.1H728c17.8.1 34.5-8.8 34.5-29.2v-29.8c0-20.8-16.7-29.9-34.5-29.9zm-162.9-1.2c-18.4 0-36.7 10-36.7 30.5v30.3c0 20.3 18.4 30.5 36.9 30.5 18.4 0 36.7-10.2 36.7-30.5V109c0-20.4-18.5-30.5-36.9-30.5zm14.4 60.8c0 6.4-7.2 9.7-14.3 9.7-7.2 0-14.4-3.1-14.4-9.7V109c0-6.5 7-10 14-10 7.3 0 14.7 3.1 14.7 10v30.3zM682.4 109c-.5-20.8-14.7-29.2-33-29.2h-35.5v88.8h22.7v-28.2h4l20.6 28.2h28L665 138.1c10.7-3.4 17.4-12.7 17.4-29.1zm-32.6 12h-13.2v-20.3h13.2c14.1 0 14.1 20.3 0 20.3z"/></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
BIN
loadUnpackedExtension.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
3832
package-lock.json
generated
65
package.json
@@ -1,61 +1,24 @@
|
||||
{
|
||||
"name": "yt-presence",
|
||||
"name": "yt_presence",
|
||||
"productName": "YT Presence",
|
||||
"version": "0.0.1",
|
||||
"description": "Show your current YouTube Video / Song in your Discord profile.",
|
||||
"main": "src/index.js",
|
||||
"version": "1.0.0",
|
||||
"description": "YT Presence adds cool Features to YouTube/YouTube Music. Like for example Discord Rich Presence integration, Media controls and much more.",
|
||||
"main": "index.js",
|
||||
"repository": "Timeraa/YT-Presence",
|
||||
"scripts": {
|
||||
"start": "electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
"lint": "echo \"No linting configured\""
|
||||
"start": "electron src/.",
|
||||
"pkgmac": "electron-packager ./src/ --out=./out/ --asar --overwrite --icon=./appIcon.ico --osx-sign.identity='Florian Metz'",
|
||||
"pkgwin": "electron-packager ./src/ --out=./out/ --overwrite --icon=./appIcon.ico",
|
||||
"installer-mac": "npm run pkgmac && electron-installer-dmg ./out/YT\\ Presence-darwin-x64/YT\\ Presence.app YT\\ Presence\\ Installer --overwrite --icon=./appIcon.icns --out=./dist",
|
||||
"installer-win": "npm run pkgwin && node createInstaller"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Timeraa",
|
||||
"license": "MIT",
|
||||
"config": {
|
||||
"forge": {
|
||||
"packagerConfig": {},
|
||||
"makers": [
|
||||
{
|
||||
"name": "@electron-forge/maker-squirrel",
|
||||
"config": {
|
||||
"name": "YT Presence"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "@electron-forge/maker-zip",
|
||||
"platforms": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "@electron-forge/maker-deb",
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "@electron-forge/maker-rpm",
|
||||
"config": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^2.4.1",
|
||||
"discord-rpc": "^3.0.0-beta.12",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"express": "^4.16.3",
|
||||
"html-entities": "^1.2.1",
|
||||
"menubar": "^5.2.3",
|
||||
"update-electron-app": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^6.0.0-beta.22",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.22",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.22",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.22",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.22",
|
||||
"electron": "2.0.4"
|
||||
"electron-installer-dmg": "^1.0.0",
|
||||
"electron-installer-windows": "^1.1.0",
|
||||
"electron-packager": "^12.2.0",
|
||||
"electron-winstaller": "^2.7.0"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
src/assets/images/icon.png
Normal file
|
After Width: | Height: | Size: 379 B |
BIN
src/assets/images/icon@2x.png
Normal file
|
After Width: | Height: | Size: 660 B |
BIN
src/assets/images/logo.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
@@ -1,4 +1,9 @@
|
||||
{
|
||||
"yt_client_id": "463097721130188830",
|
||||
"ytm_client_id": "463151177836658699"
|
||||
"ytm_client_id": "463151177836658699",
|
||||
"nflix_client_id": "499981204045430784",
|
||||
"twitch_client_id": "501021996336021504",
|
||||
"scloud_client_id": "501021185887436810",
|
||||
"version": "1.1",
|
||||
"devBuild": false
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
Well hey there!!!
|
||||
</body>
|
||||
</html>
|
||||
165
src/index.js
@@ -1,61 +1,138 @@
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
//* Handle Winstall
|
||||
require('./util/handleWinstall')
|
||||
|
||||
//#region Define constants
|
||||
//* Declare needed constants
|
||||
const {app} = require('electron')
|
||||
|
||||
const AutoLaunch = require('auto-launch')
|
||||
//* Require config
|
||||
const config = require('./config.json');
|
||||
const constants = require('./util/constants')
|
||||
//* Require electron-config
|
||||
const os = require('os')
|
||||
//* Update constant in file
|
||||
constants.platform = os.platform()
|
||||
//* Require Update checker
|
||||
const updater = require('./util/updateChecker')
|
||||
//* Require Needed packages
|
||||
const chalk = require("chalk")
|
||||
//* Setup electron-config
|
||||
const Config = require('electron-config');
|
||||
//#endregion
|
||||
|
||||
let constants = require('./util/constants')
|
||||
//* Required for Windows Push notifications
|
||||
app.setAppUserModelId("eu.Timeraa.yt_presence")
|
||||
|
||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
|
||||
app.quit();
|
||||
global.UPDATEAVAIABLE = ""
|
||||
global.VERSION = config.version
|
||||
if(config.devBuild) {
|
||||
global.VERSIONSTRING = VERSION + "-DEV"
|
||||
} else {
|
||||
global.VERSIONSTRING = VERSION
|
||||
}
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow;
|
||||
global.BROWSERCONNECTIONSTATE = "NOT_CONNECTED"
|
||||
global.EXTENSIONSOCKET = null
|
||||
|
||||
const setup = () => {
|
||||
console.log(constants.consolePrefix + chalk.green("Loading..."))
|
||||
//* YTM global vars
|
||||
global.CURRENTSONGTITLE = ""
|
||||
global.CURRENTSONGAUTHORS = []
|
||||
global.CURRENTSONGAUTHORSSTRING = ""
|
||||
global.CURRENTSONGSTARTTIME = ""
|
||||
global.CURRENTSONGSTARTTIMESECONDS = ""
|
||||
global.CURRENTSONGENDTIME = ""
|
||||
global.CONSOLEPREFIX = chalk.bold(chalk.bgHex('#db0918')(chalk.hex('#000000')(" Y") + chalk.hex('#ffffff')("T "))) + chalk.cyan(" Presence") + chalk.hex('#ffffff')(": ")
|
||||
global.YTRPCREADY = false
|
||||
global.YTMRPCREADY = false
|
||||
global.NFLIXRPCREADY = false
|
||||
global.TWITCHRPCREADY = false
|
||||
global.SCLOUDRPCREADY = false
|
||||
|
||||
// Create the browser window.
|
||||
constants.win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
show: false
|
||||
});
|
||||
global.YTRPC = null
|
||||
global.YTMRPC = null
|
||||
global.NFLIXRPC = null
|
||||
global.TWITCHRPC = null
|
||||
global.SCLOUDRPC = null
|
||||
global.TRAY = null
|
||||
|
||||
// and load the index.html of the app.
|
||||
constants.win.loadURL(`file://${__dirname}/index.html`);
|
||||
|
||||
// Open the DevTools.
|
||||
constants.win.webContents.openDevTools();
|
||||
//* Clear console
|
||||
process.stdout.write("\u001b[2J\u001b[0;0H");
|
||||
|
||||
// Emitted when the window is closed.
|
||||
constants.win.on('closed', () => {
|
||||
//constants.win = null;
|
||||
});
|
||||
//* Single Instance Check
|
||||
var iShouldQuit = app.makeSingleInstance(() => {return true});
|
||||
|
||||
if(iShouldQuit){
|
||||
console.log(CONSOLEPREFIX + chalk.red("App already running, closing current instance..."))
|
||||
app.quit();
|
||||
return;
|
||||
}
|
||||
|
||||
const userSettings = new Config({
|
||||
name: "userSettings"
|
||||
});
|
||||
|
||||
//* Set default values for electon-config userSettings
|
||||
if(userSettings.get('enabled') == undefined) userSettings.set('enabled', true)
|
||||
if(userSettings.get('youTube') == undefined) userSettings.set('youTube', true)
|
||||
if(userSettings.get('youTubeMusic') == undefined) userSettings.set('youTubeMusic', true)
|
||||
if(userSettings.get('netflix') == undefined) userSettings.set('netflix', true)
|
||||
if(userSettings.get('twitch') == undefined) userSettings.set('twitch', true)
|
||||
if(userSettings.get('soundcloud') == undefined) userSettings.set('soundcloud', true)
|
||||
if(userSettings.get('titleMenubar') == undefined) userSettings.set('titleMenubar', true)
|
||||
if(userSettings.get('autoUpdateCheck') == undefined) userSettings.set('autoUpdateCheck', true)
|
||||
|
||||
|
||||
//* Set dock Badge to version
|
||||
if(constants.platform == "darwin") {
|
||||
app.dock.setBadge("V" + VERSION)
|
||||
}
|
||||
|
||||
console.log(CONSOLEPREFIX + chalk.yellow("Loading..."))
|
||||
|
||||
//* App ready
|
||||
const appReady = () => {
|
||||
//* Setup MenuBar
|
||||
require('./tray/createTray').run()
|
||||
//* Require shortcuts
|
||||
require('./util/shortcutHandler')
|
||||
//* Include PresenceHandler
|
||||
require('./presenceHandler.js')
|
||||
};
|
||||
|
||||
//* Automatically check for update
|
||||
if(userSettings.get('autoUpdateCheck') == true)
|
||||
updater.checkForUpdate(true)
|
||||
|
||||
// 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.on('ready', setup);
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
//app.quit();
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
// On OS X 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 (mainWindow === null) {
|
||||
setup();
|
||||
//* hide Dock icon when everything running
|
||||
if(constants.platform == "darwin") {
|
||||
app.dock.hide()
|
||||
}
|
||||
});
|
||||
|
||||
// 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 import them here.
|
||||
if(userSettings.get('autoLaunch') == undefined || userSettings.get('autoLaunch') == true) {
|
||||
userSettings.set('autoLaunch', true)
|
||||
//* Add App to AutoLaunch
|
||||
console.log(CONSOLEPREFIX + chalk.yellow("Adding App to autostart..."))
|
||||
let autoLaunch = new AutoLaunch({
|
||||
name: 'YT Presence',
|
||||
path: app.getPath('exe'),
|
||||
isHidden: true
|
||||
});
|
||||
|
||||
require('./menubar/setup')
|
||||
//* Enable AutoLaunch if disabled
|
||||
autoLaunch.isEnabled().then(async (isEnabled) => {
|
||||
if (!isEnabled) autoLaunch.enable();
|
||||
console.log(CONSOLEPREFIX + chalk.green("Added App to autostart."))
|
||||
})
|
||||
//* Catch error
|
||||
.catch(function(err) {
|
||||
console.log(CONSOLEPREFIX + chalk.red("Error while adding App to autostart."))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//* Register Handler
|
||||
app.on('ready', appReady);
|
||||
|
||||
//* Prevent default behaviour
|
||||
app.on('window-all-closed', () => {});
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,39 +0,0 @@
|
||||
var MenuBar = require('menubar')
|
||||
const {app, Menu, MenuItem} = require('electron')
|
||||
var main = require("../index")
|
||||
var constants = require("../util/constants")
|
||||
|
||||
console.log(process.cwd())
|
||||
|
||||
constants.menuBar = MenuBar({
|
||||
tooltip: "YouTube Presence",
|
||||
icon: "./src/menubar/icon.png"
|
||||
})
|
||||
|
||||
constants.menuBar.on('ready', function() {
|
||||
constants.menuBar.tray.setTitle(" Connecting...")
|
||||
var menu = new Menu();
|
||||
menu.append(new MenuItem({
|
||||
label: "Show Panel",
|
||||
click: function (menuItem, browserWindow, event) {
|
||||
constants.win.show()
|
||||
}
|
||||
}))
|
||||
menu.append(new MenuItem({
|
||||
type: "separator"
|
||||
}))
|
||||
menu.append(new MenuItem({
|
||||
label: "Check for Updates"
|
||||
}))
|
||||
menu.append(new MenuItem({
|
||||
label: "About"
|
||||
}))
|
||||
menu.append(new MenuItem({
|
||||
type: "separator"
|
||||
}))
|
||||
menu.append(new MenuItem({
|
||||
label: "Quit",
|
||||
role: "quit"
|
||||
}))
|
||||
constants.menuBar.tray.setContextMenu(menu)
|
||||
})
|
||||
5961
src/package-lock.json
generated
Normal file
25
src/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "yt_presence",
|
||||
"productName": "YT Presence",
|
||||
"version": "1.0.0",
|
||||
"description": "YT Presence adds cool Features to YouTube/YouTube Music. Like for example Discord Rich Presence integration, Media controls and much more.",
|
||||
"main": "index.js",
|
||||
"repository": "Timeraa/YT-Presence",
|
||||
"keywords": [],
|
||||
"author": "Timeraa",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"auto-launch": "^5.0.5",
|
||||
"chalk": "^2.4.1",
|
||||
"discord-rpc": "^3.0.1",
|
||||
"electron-config": "^1.0.0",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"express": "^4.16.3",
|
||||
"html-entities": "^1.2.1",
|
||||
"request": "^2.88.0",
|
||||
"socket.io": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron-prebuilt-compile": "3.0.2"
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,120 @@
|
||||
var {webContents} = require('electron')
|
||||
|
||||
//* Require needed packages
|
||||
const Entities = require('html-entities').AllHtmlEntities
|
||||
const entities = new Entities()
|
||||
|
||||
const chalk = require("chalk")
|
||||
const { yt_client_id, ytm_client_id } = require("./config")
|
||||
const express = require("express")
|
||||
var constants = require('./util/constants.js')
|
||||
let YTM = require('./presences/handleYTM.js');
|
||||
var extension = express();
|
||||
var http = require('http')
|
||||
var socketServer = http.createServer(extension);
|
||||
var io = require('socket.io')(socketServer);
|
||||
|
||||
//* Create APP
|
||||
const app = express()
|
||||
//* Load Config
|
||||
const Config = require('electron-config');
|
||||
const userSettings = new Config({
|
||||
name: "userSettings"
|
||||
});
|
||||
|
||||
app.use(express.json());
|
||||
//* Define needed variables
|
||||
var keepAliveSwitch = 0,
|
||||
lastKeepAliveSwitch = 0,
|
||||
ytrpcused = false,
|
||||
ytmrpcused = false,
|
||||
nflixrpcused = false,
|
||||
twitchrpcused = false,
|
||||
scloudrpcused = false
|
||||
|
||||
let data
|
||||
//* Keep alive check to automatically remove presence if browser not running/not using YT
|
||||
setInterval(keepAliveCheck, 1000)
|
||||
|
||||
app.post("/", async (request, response) => {
|
||||
data = request.body
|
||||
if(data.connected === true) {
|
||||
//* Check if presence is ready
|
||||
if(constants.chromeConnected == false) {
|
||||
constants.chromeConnected = true;
|
||||
constants.menuBar.tray.setTitle(" Connected!")
|
||||
console.log(constants.consolePrefix + chalk.green("Chrome client connected."))
|
||||
setTimeout(function() {
|
||||
constants.menuBar.tray.setTitle("")
|
||||
}, 5*1000)
|
||||
}
|
||||
if(constants.chromeConnected == true && constants.presenceReady == true) {
|
||||
if(serviceType(data.service) == "ytm") YTM.updatePresence(data)
|
||||
}
|
||||
}
|
||||
return response.sendStatus(200);
|
||||
})
|
||||
|
||||
function serviceType(service) {
|
||||
switch(service) {
|
||||
case "ytm":
|
||||
return "ytm"
|
||||
default:
|
||||
return false
|
||||
function keepAliveCheck() {
|
||||
if (lastKeepAliveSwitch > keepAliveSwitch + 10) {
|
||||
TRAY.setTitle("")
|
||||
if (YTMRPCREADY) YTMRPC.clearActivity()
|
||||
if (YTRPCREADY) YTRPC.clearActivity()
|
||||
if (NFLIXRPCREADY) NFLIXRPC.clearActivity()
|
||||
if (TWITCHRPCREADY) TWITCHRPC.clearActivity()
|
||||
if (SCLOUDRPCREADY) SCLOUDRPC.clearActivity()
|
||||
}
|
||||
lastKeepAliveSwitch += 1
|
||||
}
|
||||
|
||||
//* Listen on port 3000
|
||||
app.listen(3000, () => console.log(constants.consolePrefix + chalk.green("is ready to use!")));
|
||||
//* Listen on port 3020
|
||||
socketServer.listen(3020, () => {
|
||||
console.log(CONSOLEPREFIX + chalk.green("Listening on Port 3020"))
|
||||
});
|
||||
|
||||
//* Socket connection event
|
||||
io.on('connection', function (socket) {
|
||||
global.EXTENSIONSOCKET = socket
|
||||
BROWSERCONNECTIONSTATE = "CONNECTED"
|
||||
|
||||
socket.on('playBackChange', (data) => {
|
||||
updatePresence(data, true)
|
||||
})
|
||||
|
||||
socket.on('updateData', (data) => {
|
||||
updatePresence(data)
|
||||
})
|
||||
})
|
||||
|
||||
//* Updates the presence with the incomming data
|
||||
function updatePresence(data, force = false) {
|
||||
lastKeepAliveSwitch = 0
|
||||
if (!userSettings.get('titleMenubar')) constants.tray.setTitle("")
|
||||
|
||||
if (data.yt != undefined) {
|
||||
ytrpcused = true
|
||||
if (userSettings.get('youTube')) require('./presences/YouTube.js')(data, force); else if (YTRPCREADY) YTRPC.clearActivity()
|
||||
} else if (data.ytm != undefined) {
|
||||
ytmrpcused = true
|
||||
if (userSettings.get('youTubeMusic')) require('./presences/YouTube_Music.js')(data, force); else if (YTMRPCREADY) YTMRPC.clearActivity()
|
||||
} else if(data.nflix != undefined) {
|
||||
nflixrpcused = true
|
||||
if (userSettings.get('netflix')) require('./presences/Netflix.js')(data, force); else if (NFLIXRPCREADY) NFLIXRPC.clearActivity()
|
||||
} else if(data.twitch != undefined) {
|
||||
twitchrpcused = true
|
||||
if (userSettings.get('twitch')) require('./presences/Twitch.js')(data, force); else if (TWITCHRPCREADY) TWITCHRPC.clearActivity()
|
||||
} else if(data.scloud != undefined) {
|
||||
scloudrpcused = true
|
||||
if (userSettings.get('soundcloud')) require('./presences/SoundCloud.js')(data, force); else if(SCLOUDRPCREADY) SCLOUDRPC.clearActivity()
|
||||
}
|
||||
|
||||
if (data.yt == undefined && YTRPCREADY) {
|
||||
if (ytrpcused == true) {
|
||||
ytrpcused = false
|
||||
YTRPC.clearActivity()
|
||||
TRAY.setTitle("")
|
||||
}
|
||||
}
|
||||
|
||||
if (data.ytm == undefined && YTMRPCREADY) {
|
||||
if (ytmrpcused == true) {
|
||||
ytmrpcused = false
|
||||
YTMRPC.clearActivity()
|
||||
TRAY.setTitle("")
|
||||
}
|
||||
}
|
||||
|
||||
if (data.nflix == undefined && NFLIXRPCREADY) {
|
||||
if (nflixrpcused == true) {
|
||||
nflixrpcused = false
|
||||
NFLIXRPC.clearActivity()
|
||||
TRAY.setTitle("")
|
||||
}
|
||||
}
|
||||
|
||||
if (data.twitch == undefined && TWITCHRPCREADY) {
|
||||
if (twitchrpcused == true) {
|
||||
twitchrpcused = false
|
||||
TWITCHRPC.clearActivity()
|
||||
TRAY.setTitle("")
|
||||
}
|
||||
}
|
||||
|
||||
if (data.scloud == undefined && SCLOUDRPCREADY) {
|
||||
if (scloudrpcused == true) {
|
||||
scloudrpcused = false
|
||||
SCLOUDRPC.clearActivity()
|
||||
TRAY.setTitle("")
|
||||
}
|
||||
}
|
||||
}
|
||||
93
src/presences/Netflix.js
Normal file
@@ -0,0 +1,93 @@
|
||||
const DiscordRPC = require('discord-rpc')
|
||||
|
||||
const Entities = require("html-entities").AllHtmlEntities;
|
||||
const entities = new Entities();
|
||||
const { nflix_client_id } = require("../config.json");
|
||||
const Config = require('electron-config');
|
||||
const userSettings = new Config({
|
||||
name: 'userSettings'
|
||||
})
|
||||
|
||||
let lastEndTime = 0,
|
||||
pauseRPCChange = 0,
|
||||
lastVideoTitle = "",
|
||||
videoTitle,
|
||||
videoAuthor,
|
||||
videoStartTime,
|
||||
videoEndTime,
|
||||
seriesTitle
|
||||
|
||||
|
||||
tryLogin()
|
||||
var retryRPCLogin = setInterval(tryLogin, 10 * 1000)
|
||||
|
||||
module.exports = (data, force) => {
|
||||
if (force) {
|
||||
if (data.nflix.playback == "paused")
|
||||
updatePresence(seriesTitle, videoAuthor, "pause", "Paused")
|
||||
else if (data.nflix.playback == "playing")
|
||||
updatePresence(seriesTitle, videoAuthor, "play", "Watching", videoStartTime, videoEndTime)
|
||||
} else {
|
||||
updateData(data)
|
||||
|
||||
if ((data.nflix.playback == "playing" && NFLIXRPCREADY) && (videoEndTime != lastEndTime || videoTitle != lastVideoTitle)) {
|
||||
lastVideoTitle = videoTitle
|
||||
lastEndTime = videoEndTime
|
||||
updatePresence(seriesTitle, videoAuthor, "play", "Watching", videoStartTime, videoEndTime)
|
||||
}
|
||||
|
||||
//* Clear Presence after 1 minute if playback == pause
|
||||
if(pauseRPCChange >= 60 && pauseRPCChange <= 60) NFLIXRPC.clearActivity()
|
||||
if(data.nflix.playback == "paused") pauseRPCChange++; else pauseRPCChange = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to login to RPC until connected
|
||||
*/
|
||||
async function tryLogin() {
|
||||
NFLIXRPC = new DiscordRPC.Client({ transport: "ipc" });
|
||||
NFLIXRPC.login({ clientId: nflix_client_id })
|
||||
.catch(err => console.log(`${CONSOLEPREFIX}NFLIXRPC: ${err.message}`))
|
||||
NFLIXRPC.on("ready", () => {
|
||||
clearInterval(retryRPCLogin)
|
||||
NFLIXRPCREADY = true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Presence on Discord
|
||||
* @param {String} details Details to show
|
||||
* @param {String} state State to show
|
||||
* @param {String} smallImageKey Small image name
|
||||
* @param {String} smallImageText Small image text
|
||||
* @param {Number} startTimestamp Start timestamp
|
||||
* @param {Number} endTimestamp End timerstamp
|
||||
*/
|
||||
function updatePresence(details, state, smallImageKey, smallImageText, startTimestamp = null, endTimestamp = null) {
|
||||
if(startTimestamp != null && userSettings.get('titleMenubar')) TRAY.setTitle(entities.decode(details)); else TRAY.setTitle("");
|
||||
NFLIXRPC.setActivity({
|
||||
details: entities.decode(details),
|
||||
state: entities.decode(state),
|
||||
largeImageKey: "nflix_lg",
|
||||
largeImageText: "YT Presence " + VERSIONSTRING,
|
||||
smallImageKey: smallImageKey,
|
||||
smallImageText: smallImageText,
|
||||
startTimestamp: startTimestamp,
|
||||
endTimestamp: endTimestamp,
|
||||
instance: true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the data used for the Presence
|
||||
* @param {Object} data Data received from Extension
|
||||
*/
|
||||
function updateData(data) {
|
||||
videoTitle = data.nflix.episodeTitle
|
||||
videoAuthor = data.nflix.season + " - " + data.nflix.episodeTitle
|
||||
videoStartTime = Math.floor(data.nflix.episodeCurrentTime / 1000);
|
||||
videoEndTime = data.nflix.episodeEndTime;
|
||||
seriesTitle = data.nflix.seriesTitle
|
||||
}
|
||||
91
src/presences/SoundCloud.js
Normal file
@@ -0,0 +1,91 @@
|
||||
const DiscordRPC = require('discord-rpc')
|
||||
|
||||
const Entities = require("html-entities").AllHtmlEntities;
|
||||
const entities = new Entities();
|
||||
const { scloud_client_id } = require("../config.json");
|
||||
const Config = require('electron-config');
|
||||
const userSettings = new Config({
|
||||
name: 'userSettings'
|
||||
})
|
||||
|
||||
let lastEndTime = 0,
|
||||
pauseRPCChange = 0,
|
||||
lastSongTitle = "",
|
||||
songAuthor,
|
||||
songStartTime,
|
||||
songEndTime,
|
||||
songTitle
|
||||
|
||||
|
||||
tryLogin()
|
||||
var retryRPCLogin = setInterval(tryLogin, 10 * 1000)
|
||||
|
||||
module.exports = (data, force) => {
|
||||
if (force) {
|
||||
if (data.scloud.playback == "paused")
|
||||
updatePresence(songTitle, songAuthor, "pause", "Paused")
|
||||
else if (data.scloud.playback == "playing")
|
||||
updatePresence(songTitle, songAuthor, "play", "Playing back.", songStartTime, songEndTime)
|
||||
} else {
|
||||
updateData(data)
|
||||
|
||||
if ((data.scloud.playback == "playing" && SCLOUDRPCREADY) && (songEndTime != lastEndTime || songTitle != lastSongTitle)) {
|
||||
lastSongTitle = songTitle
|
||||
lastEndTime = songEndTime
|
||||
updatePresence(songTitle, songAuthor, "play", "Playing back", songStartTime, songEndTime)
|
||||
}
|
||||
|
||||
//* Clear Presence after 1 minute if playback == pause
|
||||
if(pauseRPCChange >= 60 && pauseRPCChange <= 60) SCLOUDRPC.clearActivity()
|
||||
if(data.scloud.playback == "paused") pauseRPCChange++; else pauseRPCChange = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to login to RPC until connected
|
||||
*/
|
||||
async function tryLogin() {
|
||||
SCLOUDRPC = new DiscordRPC.Client({ transport: "ipc" });
|
||||
SCLOUDRPC.login({ clientId: scloud_client_id })
|
||||
.catch(err => console.log(`${CONSOLEPREFIX}SCLOUDRPC: ${err.message}`))
|
||||
SCLOUDRPC.on("ready", () => {
|
||||
clearInterval(retryRPCLogin)
|
||||
SCLOUDRPCREADY = true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Presence on Discord
|
||||
* @param {String} details Details to show
|
||||
* @param {String} state State to show
|
||||
* @param {String} smallImageKey Small image name
|
||||
* @param {String} smallImageText Small image text
|
||||
* @param {Number} startTimestamp Start timestamp
|
||||
* @param {Number} endTimestamp End timerstamp
|
||||
*/
|
||||
function updatePresence(details, state, smallImageKey, smallImageText, startTimestamp = null, endTimestamp = null) {
|
||||
if(startTimestamp != null && userSettings.get('titleMenubar')) TRAY.setTitle(entities.decode(details)); else TRAY.setTitle("");
|
||||
SCLOUDRPC.setActivity({
|
||||
details: entities.decode(details),
|
||||
state: entities.decode(state),
|
||||
largeImageKey: "scloud_lg",
|
||||
largeImageText: "YT Presence " + VERSIONSTRING,
|
||||
smallImageKey: smallImageKey,
|
||||
smallImageText: smallImageText,
|
||||
startTimestamp: startTimestamp,
|
||||
endTimestamp: endTimestamp,
|
||||
instance: true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the data used for the Presence
|
||||
* @param {Object} data Data received from Extension
|
||||
*/
|
||||
function updateData(data) {
|
||||
songAuthor = data.scloud.songAuthor
|
||||
songStartTime = Math.floor(data.scloud.songCurrentTime / 1000);
|
||||
songEndTime = data.scloud.songEndTime;
|
||||
songTitle = data.scloud.songTitle
|
||||
}
|
||||
88
src/presences/Twitch.js
Normal file
@@ -0,0 +1,88 @@
|
||||
const DiscordRPC = require('discord-rpc')
|
||||
|
||||
const Entities = require("html-entities").AllHtmlEntities;
|
||||
const entities = new Entities();
|
||||
const { twitch_client_id } = require("../config.json");
|
||||
const Config = require('electron-config');
|
||||
const userSettings = new Config({
|
||||
name: 'userSettings'
|
||||
})
|
||||
|
||||
let lastEndTime = 0,
|
||||
pauseRPCChange = 0,
|
||||
laststreamTitle = "",
|
||||
streamTitle,
|
||||
streamHost,
|
||||
videoStartTime
|
||||
|
||||
|
||||
|
||||
tryLogin()
|
||||
var retryRPCLogin = setInterval(tryLogin, 10 * 1000)
|
||||
|
||||
module.exports = (data, force) => {
|
||||
if (force) {
|
||||
if (data.twitch.playback == "paused")
|
||||
updatePresence(streamTitle, streamHost, "pause", "Paused")
|
||||
else if (data.twitch.playback == "playing")
|
||||
updatePresence(streamTitle, streamHost, "play", "Watching", videoStartTime)
|
||||
} else {
|
||||
updateData(data)
|
||||
|
||||
if ((data.twitch.playback == "playing" && TWITCHRPCREADY) && streamTitle != laststreamTitle) {
|
||||
laststreamTitle = streamTitle
|
||||
updatePresence(streamTitle, streamHost, "play", "Watching", videoStartTime)
|
||||
}
|
||||
|
||||
//* Clear Presence after 1 minute if playback == pause
|
||||
if(pauseRPCChange >= 60 && pauseRPCChange <= 60) TWITCHRPC.clearActivity()
|
||||
if(data.twitch.playback == "paused") pauseRPCChange++; else pauseRPCChange = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to login to RPC until connected
|
||||
*/
|
||||
async function tryLogin() {
|
||||
TWITCHRPC = new DiscordRPC.Client({ transport: "ipc" });
|
||||
TWITCHRPC.login({ clientId: twitch_client_id })
|
||||
.catch(err => console.log(`${CONSOLEPREFIX}TWITCHRPC: ${err.message}`))
|
||||
TWITCHRPC.on("ready", () => {
|
||||
clearInterval(retryRPCLogin)
|
||||
TWITCHRPCREADY = true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Presence on Discord
|
||||
* @param {String} details Details to show
|
||||
* @param {String} state State to show
|
||||
* @param {String} smallImageKey Small image name
|
||||
* @param {String} smallImageText Small image text
|
||||
* @param {Number} startTimestamp Start timestamp
|
||||
* @param {Number} endTimestamp End timerstamp
|
||||
*/
|
||||
function updatePresence(details, state, smallImageKey, smallImageText, startTimestamp = null) {
|
||||
if(startTimestamp != null && userSettings.get('titleMenubar')) TRAY.setTitle(entities.decode(details)); else TRAY.setTitle("");
|
||||
TWITCHRPC.setActivity({
|
||||
details: entities.decode(details),
|
||||
state: entities.decode(state),
|
||||
largeImageKey: "twitch_lg",
|
||||
largeImageText: "YT Presence " + VERSIONSTRING,
|
||||
smallImageKey: smallImageKey,
|
||||
smallImageText: smallImageText,
|
||||
startTimestamp: startTimestamp,
|
||||
instance: true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the data used for the Presence
|
||||
* @param {Object} data Data received from Extension
|
||||
*/
|
||||
function updateData(data) {
|
||||
streamTitle = data.twitch.streamTitle
|
||||
streamHost = data.twitch.streamHost
|
||||
videoStartTime = Math.floor(data.twitch.watchingSince / 1000);
|
||||
}
|
||||
91
src/presences/YouTube.js
Normal file
@@ -0,0 +1,91 @@
|
||||
const DiscordRPC = require('discord-rpc')
|
||||
|
||||
const Entities = require("html-entities").AllHtmlEntities;
|
||||
const entities = new Entities();
|
||||
const { yt_client_id } = require("../config.json");
|
||||
const Config = require('electron-config');
|
||||
const userSettings = new Config({
|
||||
name: 'userSettings'
|
||||
})
|
||||
|
||||
let lastEndTime = 0,
|
||||
pauseRPCChange = 0,
|
||||
lastVideoTitle = "",
|
||||
videoTitle,
|
||||
videoAuthor,
|
||||
videoStartTime,
|
||||
videoEndTime
|
||||
|
||||
|
||||
tryLogin()
|
||||
var retryRPCLogin = setInterval(tryLogin, 10 * 1000)
|
||||
|
||||
module.exports = (data, force) => {
|
||||
if (force) {
|
||||
if (data.yt.playback == "paused")
|
||||
updatePresence(videoTitle, videoAuthor, "pause", "Playback paused.")
|
||||
else if (data.yt.playback == "playing")
|
||||
updatePresence(videoTitle, videoAuthor, "play", "Playing back.", videoStartTime, videoEndTime)
|
||||
} else {
|
||||
updateData(data)
|
||||
|
||||
if ((data.yt.playback == "playing" && YTRPCREADY) && (videoEndTime != lastEndTime || videoTitle != lastVideoTitle)) {
|
||||
lastVideoTitle = videoTitle
|
||||
lastEndTime = videoEndTime
|
||||
updatePresence(videoTitle, videoAuthor, "play", "Playing back.", videoStartTime, videoEndTime)
|
||||
}
|
||||
|
||||
//* Clear Presence after 1 minute if playback == pause
|
||||
if(pauseRPCChange >= 60 && pauseRPCChange <= 60) YTRPC.clearActivity()
|
||||
if(data.yt.playback == "paused") pauseRPCChange++; else pauseRPCChange = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to login to RPC until connected
|
||||
*/
|
||||
async function tryLogin() {
|
||||
YTRPC = new DiscordRPC.Client({ transport: "ipc" });
|
||||
YTRPC.login({ clientId: yt_client_id })
|
||||
.catch(err => console.log(`${CONSOLEPREFIX}YTRPC: ${err.message}`))
|
||||
YTRPC.on("ready", () => {
|
||||
clearInterval(retryRPCLogin)
|
||||
YTRPCREADY = true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Presence on Discord
|
||||
* @param {String} details Details to show
|
||||
* @param {String} state State to show
|
||||
* @param {String} smallImageKey Small image name
|
||||
* @param {String} smallImageText Small image text
|
||||
* @param {Number} startTimestamp Start timestamp
|
||||
* @param {Number} endTimestamp End timerstamp
|
||||
*/
|
||||
function updatePresence(details, state, smallImageKey, smallImageText, startTimestamp = null, endTimestamp = null) {
|
||||
if(startTimestamp != null && userSettings.get('titleMenubar')) TRAY.setTitle(entities.decode(details)); else TRAY.setTitle("");
|
||||
YTRPC.setActivity({
|
||||
details: entities.decode(details),
|
||||
state: entities.decode(state),
|
||||
largeImageKey: "yt_lg",
|
||||
largeImageText: "YT Presence " + VERSIONSTRING,
|
||||
smallImageKey: smallImageKey,
|
||||
smallImageText: smallImageText,
|
||||
startTimestamp: startTimestamp,
|
||||
endTimestamp: endTimestamp,
|
||||
instance: true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the data used for the Presence
|
||||
* @param {Object} data Data received from Extension
|
||||
*/
|
||||
function updateData(data) {
|
||||
videoTitle = data.yt.videoTitle
|
||||
videoAuthor = data.yt.videoAuthor
|
||||
videoStartTime = Math.floor(data.yt.videoCurrentTime / 1000);
|
||||
videoEndTime = data.yt.videoEndTime;
|
||||
}
|
||||
111
src/presences/YouTube_Music.js
Normal file
@@ -0,0 +1,111 @@
|
||||
const DiscordRPC = require("discord-rpc")
|
||||
|
||||
const Entities = require("html-entities").AllHtmlEntities;
|
||||
const entities = new Entities();
|
||||
|
||||
const { ytm_client_id } = require("../config.json");
|
||||
|
||||
const Config = require('electron-config');
|
||||
const userSettings = new Config({name: 'userSettings'})
|
||||
|
||||
//* Define needed variables
|
||||
let lastEndTime = 0,
|
||||
pauseRPCChange = 0,
|
||||
songTitle,
|
||||
songAuthorString,
|
||||
songEndTime,
|
||||
songStartTime
|
||||
|
||||
//* Try to connect RPC
|
||||
tryLogin()
|
||||
var retryRPCLogin = setInterval(tryLogin, 10 * 1000)
|
||||
|
||||
module.exports = (data, force) => {
|
||||
//* If YTM RPC Ready
|
||||
if(YTMRPCREADY) {
|
||||
//* Is update forced?
|
||||
if (force) {
|
||||
//* Check what playback state and set its Presence
|
||||
if (data.ytm.playback == "paused")
|
||||
updatePresence(songTitle, songAuthorString, "pause", "Playback paused.");
|
||||
else if (data.ytm.playback == "playing")
|
||||
updatePresence(songTitle, songAuthorString, "play", "Playing back.", songStartTime, songEndTime);
|
||||
} else {
|
||||
//* Update Presence data
|
||||
updateData(data)
|
||||
|
||||
//* Only update Presence if time changed
|
||||
if (data.ytm.playback == "playing" && songEndTime != lastEndTime) {
|
||||
lastEndTime = songEndTime
|
||||
updatePresence(songTitle, songAuthorString, "play", "Playing back.", songStartTime, songEndTime)
|
||||
}
|
||||
|
||||
//* Clear Presence after 1 minute if playback == pause
|
||||
if(pauseRPCChange >= 60 && pauseRPCChange <= 60) {
|
||||
YTMRPC.clearActivity()
|
||||
TRAY.setTitle("")
|
||||
}
|
||||
|
||||
//* Increase pauseChange if paused
|
||||
if(data.ytm.playback == "paused") pauseRPCChange++; else pauseRPCChange = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to login to RPC until connected
|
||||
*/
|
||||
async function tryLogin() {
|
||||
YTMRPC = new DiscordRPC.Client({ transport: "ipc" });
|
||||
YTMRPC.login({ clientId: ytm_client_id })
|
||||
.catch(err => console.log(`${CONSOLEPREFIX}YTMRPC: ${err.message}`))
|
||||
YTMRPC.on("ready", () => {
|
||||
clearInterval(retryRPCLogin)
|
||||
YTMRPCREADY = true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Presence on Discord
|
||||
* @param {String} details Details to show
|
||||
* @param {String} state State to show
|
||||
* @param {String} smallImageKey Small image name
|
||||
* @param {String} smallImageText Small image text
|
||||
* @param {Number} startTimestamp Start timestamp
|
||||
* @param {Number} endTimestamp End timerstamp
|
||||
*/
|
||||
function updatePresence(details, state, smallImageKey, smallImageText, startTimestamp = null, endTimestamp = null) {
|
||||
if(startTimestamp != null && userSettings.get('titleMenubar')) TRAY.setTitle(entities.decode(songTitle)); else TRAY.setTitle("");
|
||||
YTMRPC.setActivity({
|
||||
details: entities.decode(details),
|
||||
state: entities.decode(state),
|
||||
largeImageKey: "ytm_lg",
|
||||
largeImageText: "YT Presence " + VERSIONSTRING,
|
||||
smallImageKey: smallImageKey,
|
||||
smallImageText: smallImageText,
|
||||
startTimestamp: startTimestamp,
|
||||
endTimestamp: endTimestamp,
|
||||
instance: true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the data used for the Presence
|
||||
* @param {Object} data Data received from Extension
|
||||
*/
|
||||
function updateData(data) {
|
||||
songTitle = data.ytm.songTitle
|
||||
var songAuthors = data.ytm.songAuthors
|
||||
songStartTime = Math.floor(data.ytm.songCurrentTime / 1000);
|
||||
songEndTime = data.ytm.songEndTime;
|
||||
|
||||
//* Create author string from author array
|
||||
songAuthors.forEach((author, index, authors) => {
|
||||
if (index == 0)
|
||||
songAuthorString = author;
|
||||
else if (index < authors.length - 2)
|
||||
songAuthorString = songAuthorString + ", " + author;
|
||||
else if (index < authors.length - 1) songAuthorString = songAuthorString + " and " + author;
|
||||
else songAuthorString = songAuthorString + " • " + author;
|
||||
});
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
const DiscordRPC = require("discord-rpc");
|
||||
const constants = require("../util/constants.js");
|
||||
const { ytm_client_id } = require("../config.json");
|
||||
const Entities = require("html-entities").AllHtmlEntities;
|
||||
const entities = new Entities();
|
||||
|
||||
const rpc = new DiscordRPC.Client({ transport: "ipc" });
|
||||
|
||||
let presence,
|
||||
startTime = new Date(),
|
||||
endTime = new Date(),
|
||||
lastTitle,
|
||||
lastStartingTime
|
||||
|
||||
async function updatePresence(data) {
|
||||
if (data.currentSongStartTime == lastStartingTime) {
|
||||
if ((data.currentSongAuthor && data.currentSongTitle) != undefined) {
|
||||
constants.menuBar.tray.setTitle("")
|
||||
rpc.setActivity({
|
||||
details: entities.decode(data.currentSongTitle),
|
||||
state: entities.decode(data.currentSongAuthor),
|
||||
smallImageKey: "pause",
|
||||
smallImageText: "Playback paused.",
|
||||
largeImageKey: "ytm_lg",
|
||||
instance: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
startTime = new Date();
|
||||
endTime =
|
||||
Math.floor(startTime / 1000) -
|
||||
data.currentSongStartTime +
|
||||
data.currentSongEndTime;
|
||||
lastTitle = data.currentSongTitle;
|
||||
lastStartingTime = data.currentSongStartTime;
|
||||
|
||||
if (data.url.includes("watch?v=")) {
|
||||
if ((data.currentSongAuthor && data.currentSongTitle) != undefined) {
|
||||
constants.menuBar.tray.setTitle(" " + data.currentSongTitle)
|
||||
rpc.setActivity({
|
||||
details: entities.decode(data.currentSongTitle),
|
||||
state: entities.decode(data.currentSongAuthor),
|
||||
smallImageKey: "play",
|
||||
smallImageText: "Playing back.",
|
||||
largeImageKey: "ytm_lg",
|
||||
startTimestamp: startTime,
|
||||
endTimestamp: endTime,
|
||||
instance: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpc.on("ready", () => {
|
||||
constants.presenceReady = true;
|
||||
});
|
||||
|
||||
exports.updatePresence = updatePresence;
|
||||
|
||||
rpc.login(ytm_client_id).catch(console.error);
|
||||
32
src/tray/createTray.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const path = require('path')
|
||||
const constants = require("../util/constants")
|
||||
const {Tray, Menu, MenuItem} = require('electron')
|
||||
|
||||
exports.run = () => {
|
||||
TRAY = new Tray(path.join(__dirname, "../assets/images/icon.png"))
|
||||
TRAY.setToolTip(`YT Presence V${VERSIONSTRING}`)
|
||||
constants.menuBarMenu = new Menu()
|
||||
constants.menuBarMenu.append(new MenuItem({
|
||||
label: `YT Presence | V${VERSIONSTRING}`,
|
||||
enabled: false,
|
||||
icon: path.join(__dirname, "../assets/images/icon.png")
|
||||
}))
|
||||
constants.menuBarMenu.append(new MenuItem({type: "separator"}))
|
||||
constants.menuBarMenu.append(new MenuItem({
|
||||
click: require('./showAbout').run,
|
||||
label: "About"
|
||||
}))
|
||||
constants.menuBarMenu.append(new MenuItem({
|
||||
click: cfu,
|
||||
label: "Check for update"
|
||||
}))
|
||||
constants.menuBarMenu.append(new MenuItem({
|
||||
click: require('./showPreferences').run,
|
||||
label: "Preferences"
|
||||
}))
|
||||
constants.menuBarMenu.append(new MenuItem({type: "separator"}))
|
||||
constants.menuBarMenu.append(new MenuItem({role: "quit"}))
|
||||
TRAY.setContextMenu(constants.menuBarMenu)
|
||||
}
|
||||
|
||||
cfu = () => require('../util/updateChecker').checkForUpdate(true, true)
|
||||
22
src/tray/showAbout.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const path = require('path')
|
||||
const {BrowserWindow} = require('electron')
|
||||
|
||||
exports.run = () => {
|
||||
var aboutWindow = new BrowserWindow({
|
||||
center: true,
|
||||
maximizable: false,
|
||||
minimizable: false,
|
||||
resizable: false,
|
||||
width: 400,
|
||||
height: 600,
|
||||
alwaysOnTop: true
|
||||
})
|
||||
|
||||
aboutWindow.setMenu(null)
|
||||
|
||||
aboutWindow.loadURL("file://" + path.join(__dirname, "../windows/about.html"))
|
||||
|
||||
aboutWindow.on('close', () => {
|
||||
aboutWindow = null;
|
||||
})
|
||||
}
|
||||
35
src/tray/showPreferences.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const path = require('path')
|
||||
const {BrowserWindow} = require('electron')
|
||||
const os = require('os');
|
||||
let menuBarHeight
|
||||
|
||||
exports.run = () => {
|
||||
switch(os.platform()) {
|
||||
case "darwin":
|
||||
menuBarHeight = 440;
|
||||
break;
|
||||
default:
|
||||
menuBarHeight = 410;
|
||||
break;
|
||||
}
|
||||
|
||||
var settingsWindow = new BrowserWindow({
|
||||
center: true,
|
||||
maximizable: false,
|
||||
resizable: false,
|
||||
height: menuBarHeight,
|
||||
width: 400
|
||||
})
|
||||
|
||||
settingsWindow.setMenu(null)
|
||||
|
||||
settingsWindow.loadURL("file://" + path.join(__dirname, "../windows/preferences.html"))
|
||||
|
||||
settingsWindow.on('blur', () => {
|
||||
settingsWindow.close()
|
||||
})
|
||||
|
||||
settingsWindow.on('close', () => {
|
||||
settingsWindow = null;
|
||||
})
|
||||
}
|
||||
@@ -1,19 +1,14 @@
|
||||
const chalk = require("chalk")
|
||||
|
||||
let consolePrefix, win, menuBar, menuBarMenu, chromeConnected, presenceReady, presence, setup;
|
||||
|
||||
module.exports = {
|
||||
consolePrefix: chalk.bold(chalk.gray("<[ ") + chalk.bgRed(chalk.black(" Y") + chalk.white("T ")) + chalk.cyan(" Presence") + chalk.gray(" ]> ")),
|
||||
app: "",
|
||||
win: "",
|
||||
menuBar: "",
|
||||
menuBarMenu: "",
|
||||
chromeConnected: false,
|
||||
presenceReady: false,
|
||||
setup: false,
|
||||
presence: {
|
||||
details: "Waiting for music",
|
||||
state: "to start playing...",
|
||||
largeImageKey: "ytm_lg",
|
||||
instance: true
|
||||
}
|
||||
newVersion: false,
|
||||
introRan: false,
|
||||
lastResponse: false,
|
||||
platform: "",
|
||||
data: ""
|
||||
};
|
||||
|
||||
60
src/util/handleWinstall.js
Normal file
@@ -0,0 +1,60 @@
|
||||
if (require('electron-squirrel-startup')) return;
|
||||
|
||||
const {app} = require('electron');
|
||||
|
||||
// this should be placed at top of main.js to handle setup events quickly
|
||||
if (handleSquirrelEvent()) {
|
||||
// squirrel event handled and app will exit in 1000ms, so don't do anything else
|
||||
return;
|
||||
}
|
||||
|
||||
function handleSquirrelEvent() {
|
||||
if (process.argv.length === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ChildProcess = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
const appFolder = path.resolve(process.execPath, '..');
|
||||
const rootAtomFolder = path.resolve(appFolder, '..');
|
||||
const updateDotExe = path.resolve(path.join(rootAtomFolder, 'Update.exe'));
|
||||
const exeName = path.basename(process.execPath);
|
||||
|
||||
const spawn = function(command, args) {
|
||||
let spawnedProcess, error;
|
||||
|
||||
try {
|
||||
spawnedProcess = ChildProcess.spawn(command, args, {detached: true});
|
||||
} catch (error) {}
|
||||
|
||||
return spawnedProcess;
|
||||
};
|
||||
|
||||
const spawnUpdate = function(args) {
|
||||
return spawn(updateDotExe, args);
|
||||
};
|
||||
|
||||
const squirrelEvent = process.argv[1];
|
||||
switch (squirrelEvent) {
|
||||
case '--squirrel-install':
|
||||
case '--squirrel-updated':
|
||||
|
||||
// Install desktop and start menu shortcuts
|
||||
spawnUpdate(['--createShortcut', exeName]);
|
||||
|
||||
setTimeout(app.quit, 1000);
|
||||
return true;
|
||||
|
||||
case '--squirrel-uninstall':
|
||||
// Remove desktop and start menu shortcuts
|
||||
spawnUpdate(['--removeShortcut', exeName]);
|
||||
|
||||
setTimeout(app.quit, 1000);
|
||||
return true;
|
||||
|
||||
case '--squirrel-obsolete':
|
||||
app.quit();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
18
src/util/shortcutHandler.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const chalk = require('chalk')
|
||||
var { globalShortcut } = require('electron')
|
||||
|
||||
console.log(CONSOLEPREFIX + chalk.yellow("Registering keyboard shortcuts..."))
|
||||
|
||||
globalShortcut.register('medianexttrack', () => {
|
||||
if (EXTENSIONSOCKET != null) EXTENSIONSOCKET.emit('mediaKeyHandler', { playback: "nextTrack" })
|
||||
})
|
||||
|
||||
globalShortcut.register('mediaplaypause', () => {
|
||||
if (EXTENSIONSOCKET != null) EXTENSIONSOCKET.emit('mediaKeyHandler', { playback: "pause" })
|
||||
})
|
||||
|
||||
globalShortcut.register('mediaprevioustrack', () => {
|
||||
if (EXTENSIONSOCKET != null) EXTENSIONSOCKET.emit('mediaKeyHandler', { playback: "previousTrack" })
|
||||
})
|
||||
|
||||
console.log(CONSOLEPREFIX + chalk.green("Successfully registered keyboard shortcuts."))
|
||||
69
src/util/updateChecker.js
Normal file
@@ -0,0 +1,69 @@
|
||||
const { Notification, BrowserWindow } = require('electron');
|
||||
|
||||
const path = require('path')
|
||||
|
||||
const request = require("request")
|
||||
const chalk = require("chalk")
|
||||
let constants = require('./constants')
|
||||
|
||||
function checkForUpdate(sendNotification = false, sendNoUpdateInfo = false) {
|
||||
|
||||
console.log(CONSOLEPREFIX + chalk.cyan("Checking for update..."))
|
||||
|
||||
request({
|
||||
url: "https://api.github.com/repos/Timeraa/YT-Presence/releases/latest",
|
||||
json: true,
|
||||
headers: {'user-agent': 'node.js'}
|
||||
}, function (error, response, body) {
|
||||
if(error) {
|
||||
console.log(CONSOLEPREFIX + chalk.red("Error while checking for update. " + error))
|
||||
return
|
||||
}
|
||||
//* Remove v from version
|
||||
var gitVersion = body.tag_name.replace('v', '')
|
||||
//* Compare version
|
||||
if(gitVersion > VERSION) {
|
||||
global.UPDATEAVAIABLE = gitVersion
|
||||
constants.newVersion = gitVersion
|
||||
|
||||
console.log(CONSOLEPREFIX + chalk.cyan("New version avaiable: ") + chalk.red(`V${VERSION}`) + chalk.blue(' > ') + chalk.yellow(`V${gitVersion}`))
|
||||
|
||||
var updateWindow = new BrowserWindow({
|
||||
center: true,
|
||||
maximizable: false,
|
||||
minimizable: false,
|
||||
height: 500,
|
||||
width: 400,
|
||||
alwaysOnTop: true
|
||||
})
|
||||
|
||||
updateWindow.setMenu(null)
|
||||
|
||||
updateWindow.loadURL("file://" + path.join(__dirname, "../windows/update.html"))
|
||||
updateWindow.webContents.on('did-finish-load', () => {
|
||||
updateWindow.webContents.send('updateData', body);
|
||||
});
|
||||
|
||||
updateWindow.on('close', () => {
|
||||
updateWindow = null;
|
||||
})
|
||||
|
||||
} else {
|
||||
global.UPDATEAVAIABLE = false
|
||||
console.log(CONSOLEPREFIX + chalk.cyan("Up to date! ") + chalk.yellow(`V${VERSION}`))
|
||||
if(sendNoUpdateInfo) {
|
||||
const noUpdateAvaiableNotification = new Notification({
|
||||
title: 'YT Presence',
|
||||
body: `You are up to date! (V${VERSION})`,
|
||||
silent: true
|
||||
})
|
||||
|
||||
noUpdateAvaiableNotification.show()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return UPDATEAVAIABLE
|
||||
}
|
||||
|
||||
module.exports.checkForUpdate = checkForUpdate
|
||||
131
src/windows/about.html
Normal file
@@ -0,0 +1,131 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>About YouTube Presence</title>
|
||||
<link rel="stylesheet" href="css/reset.css">
|
||||
<link rel="stylesheet" href="css/about.css">
|
||||
<link href="css/ripple.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 id="hello">Hi there!</h1>
|
||||
<img src="https://avatars2.githubusercontent.com/u/29104008?s=150">
|
||||
<div id="background"></div>
|
||||
<h1 id="name">Florian Metz</h1>
|
||||
<div id="nickname">
|
||||
<p>T</p>
|
||||
<p>i</p>
|
||||
<p>m</p>
|
||||
<p>e</p>
|
||||
<p>r</p>
|
||||
<p>a</p>
|
||||
<p>a</p>
|
||||
</div>
|
||||
<div id="aboutMe">
|
||||
<p>
|
||||
My name is <a href="https://github.com/Timeraa">Timeraa (Florian Metz)</a>. I am a Web developer, master a lot of
|
||||
known languages and i love the TV series
|
||||
called<br>
|
||||
<a href="https://en.wikipedia.org/wiki/My_Little_Pony:_Friendship_Is_Magic">My little Pony: Friendship is Magic</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div id="aboutYTP">
|
||||
<p>
|
||||
I started this project because i recently switched to <a href="https://music.youtube.com/">YouTube Music</a>, now
|
||||
i wanted to have <b>Rich Presence
|
||||
Integration</b> for <a href="https://discordapp.com/">Discord</a>, so i just started to create this. And now
|
||||
you can use it. :P
|
||||
</p>
|
||||
</div>
|
||||
<button id="supportMe">
|
||||
Support me!
|
||||
<div class="rippleJS"></div>
|
||||
</button>
|
||||
<script>if (typeof module === 'object') { window.module = module; module = undefined; }</script>
|
||||
|
||||
<script src="js/jquery-3.3.1.min.js"></script>
|
||||
<script src="js/anime.min.js"></script>
|
||||
<script src="js/preferences.js"></script>
|
||||
<script src="js/ripple.min.js"></script>
|
||||
|
||||
<script>if (window.module) module = window.module;</script>
|
||||
<script>
|
||||
var shell = require('electron').shell;
|
||||
//open links externally by default
|
||||
$(document).on('click', 'a[href^="http"]', function (event) {
|
||||
event.preventDefault();
|
||||
shell.openExternal(this.href);
|
||||
});
|
||||
|
||||
$('#supportMe').click(() => {
|
||||
shell.openExternal("https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZU8Q766ACS2WS&lc=US");
|
||||
})
|
||||
|
||||
$(window).on("load", () => {
|
||||
$('html').addClass("ready")
|
||||
|
||||
anime.timeline({
|
||||
targets: 'img',
|
||||
scale: [
|
||||
{ value: 0, duration: 0 },
|
||||
{ value: 1.25, duration: 1000 },
|
||||
{ value: 1, duration: 500 }
|
||||
],
|
||||
easing: 'easeInOutExpo',
|
||||
}).add({
|
||||
targets: '#background',
|
||||
scale: [0, 20],
|
||||
duration: 750,
|
||||
offset: 900
|
||||
}).add({
|
||||
targets: '#hello',
|
||||
translateY: [75, -25, 0],
|
||||
opacity: [0, 1],
|
||||
scale: [0, 1],
|
||||
duration: 2000,
|
||||
offset: 750
|
||||
}).add({
|
||||
targets: '#name',
|
||||
translateY: [0, 25],
|
||||
opacity: [0, 1],
|
||||
scale: [0, 1],
|
||||
duration: 2000,
|
||||
offset: 900
|
||||
}).add({
|
||||
targets: '#nickname p',
|
||||
opacity: [0, 1],
|
||||
duration: 500,
|
||||
color: ['rgba(0,0,0,0)', 'rgb(0, 150, 200)', '#fff'],
|
||||
delay: function (el, i) {
|
||||
return 2500 + 75 * i
|
||||
},
|
||||
offset: 0
|
||||
}).add({
|
||||
targets: '#aboutMe',
|
||||
translateY: [-25, 0],
|
||||
opacity: [0, 1],
|
||||
scale: [0.75, 1],
|
||||
easing: 'easeInOutSine',
|
||||
duration: 1500,
|
||||
offset: 3250
|
||||
}).add({
|
||||
targets: '#aboutYTP',
|
||||
translateY: [-25, 0],
|
||||
opacity: [0, 1],
|
||||
scale: [0.75, 1],
|
||||
easing: 'easeInOutSine',
|
||||
duration: 1500,
|
||||
offset: 4500
|
||||
}).add({
|
||||
targets: '#supportMe',
|
||||
opacity: [0, 1],
|
||||
scale: [0, 1],
|
||||
duration: 1500,
|
||||
offset: 5000
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
99
src/windows/css/about.css
Normal file
@@ -0,0 +1,99 @@
|
||||
* {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
html:not(.ready) * {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#background {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-left: -75px;
|
||||
background-color: #1d1d1d;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border-radius: 50%;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 35px;
|
||||
}
|
||||
|
||||
#name {
|
||||
margin-top: 150px;
|
||||
}
|
||||
|
||||
#nickname p {
|
||||
position: relative;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-left: -75px;
|
||||
width: 150px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
#aboutMe,
|
||||
#aboutYTP {
|
||||
width: 380px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
color: rgb(0, 150, 255);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -1px;
|
||||
height: 2px;
|
||||
width: 0;
|
||||
transition: width 0.25s ease-out;
|
||||
background-color: rgb(0, 125, 255);
|
||||
}
|
||||
|
||||
a:hover:after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
button {
|
||||
transition: 0.25s all ease-out;
|
||||
cursor: pointer;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
background-color: rgb(0, 160, 255);
|
||||
color: white;
|
||||
width: 150px;
|
||||
height: 35px;
|
||||
border: none;
|
||||
border-radius: 15px;
|
||||
outline: none;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: rgb(0, 175, 255);
|
||||
box-shadow: 0 0 5px 3px rgba(0, 174, 255, 0.5);
|
||||
}
|
||||
78
src/windows/css/inputs.css
Normal file
@@ -0,0 +1,78 @@
|
||||
/* The switch - the box around the slider */
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 35px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
/* Hide default HTML checkbox */
|
||||
|
||||
.switch input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* The slider */
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: 0.25s;
|
||||
transition: 0.25s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 17px;
|
||||
width: 17px;
|
||||
left: -2.5px;
|
||||
top: -2.5px;
|
||||
background-color: white;
|
||||
-webkit-transition: 0.25s;
|
||||
transition: 0.25s cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #2196f3;
|
||||
}
|
||||
|
||||
input:disabled + .slider {
|
||||
background-color: rgb(160, 160, 160);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
input:disabled + .slider:before {
|
||||
background-color: lightgray;
|
||||
border: 1px solid rgb(160, 160, 160);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px #2196f3;
|
||||
}
|
||||
|
||||
input:hover + .slider {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
79
src/windows/css/preferences.css
Normal file
@@ -0,0 +1,79 @@
|
||||
html {
|
||||
transition: 0.5s background-color ease-out;
|
||||
}
|
||||
|
||||
#header {
|
||||
background-color: #ce1d2a;
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
#header img {
|
||||
margin: 5px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
top: 10px;
|
||||
position: absolute;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
#options {
|
||||
border-collapse: collapse;
|
||||
width: 90%;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#options th:nth-child(1) {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
#options th:nth-child(2) {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
#options tbody tr {
|
||||
transition: 1s color ease-in-out, 0.75s border ease-out;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
#options tbody tr:not(:last-child) {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
#footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
background-color: #ce1d2a;
|
||||
color: white;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#footer #close-btn {
|
||||
position: relative;
|
||||
transition: background-color 0.2s ease-out;
|
||||
margin: 5px;
|
||||
appearance: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
color: white;
|
||||
width: 100px;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#footer #close-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
47
src/windows/css/reset.css
Normal file
@@ -0,0 +1,47 @@
|
||||
@font-face {
|
||||
font-family: "Montserrat";
|
||||
src: url("../fonts/Montserrat/Montserrat-Regular.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Montserrat";
|
||||
src: url("../fonts/Montserrat/Montserrat-Medium.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Montserrat";
|
||||
src: url("../fonts/Montserrat/Montserrat-Bold.ttf") format("truetype");
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Montserrat";
|
||||
src: url("../fonts/Montserrat/Montserrat-Black.ttf") format("truetype");
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Montserrat";
|
||||
src: url("../fonts/Montserrat/Montserrat-BlackItalic.ttf") format("truetype");
|
||||
font-weight: 900;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: "Montserrat", sans-serif;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
67
src/windows/css/ripple.css
Normal file
@@ -0,0 +1,67 @@
|
||||
/** NOTE: When this is updated, refresh ripple.js' minified version. */
|
||||
|
||||
/**
|
||||
* contains a ripple. Will normally grow to fixed size (200px/200px), not
|
||||
* related to the holder itself.
|
||||
*/
|
||||
.rippleJS {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
border-radius: inherit;
|
||||
|
||||
/** Forces webkit to properly contain content within border-radius. */
|
||||
-webkit-mask-image: -webkit-radial-gradient(circle, white, black);
|
||||
}
|
||||
|
||||
/** adds default border-radius */
|
||||
.rippleJS.fill::after { /** allows webkit/blink to tap on corners */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
content: "";
|
||||
}
|
||||
.rippleJS.fill {
|
||||
border-radius: 1000000px; /** "large" number, but not 100% */
|
||||
}
|
||||
|
||||
.rippleJS .ripple {
|
||||
position: absolute;
|
||||
border-radius: 100%;
|
||||
background: currentColor;
|
||||
opacity: 0.2;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
/** only animate transform and opacity */
|
||||
-webkit-transition: -webkit-transform 0.4s ease-out, opacity 0.4s ease-out;
|
||||
transition: transform 0.4s ease-out, opacity 0.4s ease-out;
|
||||
|
||||
/** initially hidden */
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
|
||||
pointer-events: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.rippleJS .ripple.held {
|
||||
opacity: 0.4;
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.rippleJS .ripple.done {
|
||||
opacity: 0.0;
|
||||
}
|
||||
76
src/windows/css/update.css
Normal file
@@ -0,0 +1,76 @@
|
||||
#header {
|
||||
background-color: #ce1d2a;
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
#header img {
|
||||
margin: 5px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
top: 10px;
|
||||
position: absolute;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin: 5px;
|
||||
margin-bottom: 65px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
background-color: #ce1d2a;
|
||||
color: white;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#footer #update {
|
||||
position: relative;
|
||||
transition: background-color 0.2s ease-out;
|
||||
margin: 15px;
|
||||
appearance: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
background-color: rgb(0, 160, 255);
|
||||
color: white;
|
||||
width: 100px;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#footer #update:hover {
|
||||
background-color: rgb(0, 175, 255);
|
||||
}
|
||||
|
||||
#footer #later {
|
||||
position: relative;
|
||||
transition: background-color 0.2s ease-out;
|
||||
float: right;
|
||||
margin: 15px;
|
||||
appearance: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
color: white;
|
||||
width: 100px;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#footer #later:hover {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
BIN
src/windows/fonts/Montserrat/Montserrat-Black.ttf
Executable file
BIN
src/windows/fonts/Montserrat/Montserrat-BlackItalic.ttf
Executable file
BIN
src/windows/fonts/Montserrat/Montserrat-Bold.ttf
Executable file
BIN
src/windows/fonts/Montserrat/Montserrat-Medium.ttf
Executable file
BIN
src/windows/fonts/Montserrat/Montserrat-Regular.ttf
Executable file
33
src/windows/js/anime.min.js
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
2017 Julian Garnier
|
||||
Released under the MIT license
|
||||
*/
|
||||
var $jscomp={scope:{}};$jscomp.defineProperty="function"==typeof Object.defineProperties?Object.defineProperty:function(e,r,p){if(p.get||p.set)throw new TypeError("ES3 does not support getters and setters.");e!=Array.prototype&&e!=Object.prototype&&(e[r]=p.value)};$jscomp.getGlobal=function(e){return"undefined"!=typeof window&&window===e?e:"undefined"!=typeof global&&null!=global?global:e};$jscomp.global=$jscomp.getGlobal(this);$jscomp.SYMBOL_PREFIX="jscomp_symbol_";
|
||||
$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.symbolCounter_=0;$jscomp.Symbol=function(e){return $jscomp.SYMBOL_PREFIX+(e||"")+$jscomp.symbolCounter_++};
|
||||
$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var e=$jscomp.global.Symbol.iterator;e||(e=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[e]&&$jscomp.defineProperty(Array.prototype,e,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(e){var r=0;return $jscomp.iteratorPrototype(function(){return r<e.length?{done:!1,value:e[r++]}:{done:!0}})};
|
||||
$jscomp.iteratorPrototype=function(e){$jscomp.initSymbolIterator();e={next:e};e[$jscomp.global.Symbol.iterator]=function(){return this};return e};$jscomp.array=$jscomp.array||{};$jscomp.iteratorFromArray=function(e,r){$jscomp.initSymbolIterator();e instanceof String&&(e+="");var p=0,m={next:function(){if(p<e.length){var u=p++;return{value:r(u,e[u]),done:!1}}m.next=function(){return{done:!0,value:void 0}};return m.next()}};m[Symbol.iterator]=function(){return m};return m};
|
||||
$jscomp.polyfill=function(e,r,p,m){if(r){p=$jscomp.global;e=e.split(".");for(m=0;m<e.length-1;m++){var u=e[m];u in p||(p[u]={});p=p[u]}e=e[e.length-1];m=p[e];r=r(m);r!=m&&null!=r&&$jscomp.defineProperty(p,e,{configurable:!0,writable:!0,value:r})}};$jscomp.polyfill("Array.prototype.keys",function(e){return e?e:function(){return $jscomp.iteratorFromArray(this,function(e){return e})}},"es6-impl","es3");var $jscomp$this=this;
|
||||
(function(e,r){"function"===typeof define&&define.amd?define([],r):"object"===typeof module&&module.exports?module.exports=r():e.anime=r()})(this,function(){function e(a){if(!h.col(a))try{return document.querySelectorAll(a)}catch(c){}}function r(a,c){for(var d=a.length,b=2<=arguments.length?arguments[1]:void 0,f=[],n=0;n<d;n++)if(n in a){var k=a[n];c.call(b,k,n,a)&&f.push(k)}return f}function p(a){return a.reduce(function(a,d){return a.concat(h.arr(d)?p(d):d)},[])}function m(a){if(h.arr(a))return a;
|
||||
h.str(a)&&(a=e(a)||a);return a instanceof NodeList||a instanceof HTMLCollection?[].slice.call(a):[a]}function u(a,c){return a.some(function(a){return a===c})}function C(a){var c={},d;for(d in a)c[d]=a[d];return c}function D(a,c){var d=C(a),b;for(b in a)d[b]=c.hasOwnProperty(b)?c[b]:a[b];return d}function z(a,c){var d=C(a),b;for(b in c)d[b]=h.und(a[b])?c[b]:a[b];return d}function T(a){a=a.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,function(a,c,d,k){return c+c+d+d+k+k});var c=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(a);
|
||||
a=parseInt(c[1],16);var d=parseInt(c[2],16),c=parseInt(c[3],16);return"rgba("+a+","+d+","+c+",1)"}function U(a){function c(a,c,b){0>b&&(b+=1);1<b&&--b;return b<1/6?a+6*(c-a)*b:.5>b?c:b<2/3?a+(c-a)*(2/3-b)*6:a}var d=/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(a)||/hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(a);a=parseInt(d[1])/360;var b=parseInt(d[2])/100,f=parseInt(d[3])/100,d=d[4]||1;if(0==b)f=b=a=f;else{var n=.5>f?f*(1+b):f+b-f*b,k=2*f-n,f=c(k,n,a+1/3),b=c(k,n,a);a=c(k,n,a-1/3)}return"rgba("+
|
||||
255*f+","+255*b+","+255*a+","+d+")"}function y(a){if(a=/([\+\-]?[0-9#\.]+)(%|px|pt|em|rem|in|cm|mm|ex|ch|pc|vw|vh|vmin|vmax|deg|rad|turn)?$/.exec(a))return a[2]}function V(a){if(-1<a.indexOf("translate")||"perspective"===a)return"px";if(-1<a.indexOf("rotate")||-1<a.indexOf("skew"))return"deg"}function I(a,c){return h.fnc(a)?a(c.target,c.id,c.total):a}function E(a,c){if(c in a.style)return getComputedStyle(a).getPropertyValue(c.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase())||"0"}function J(a,c){if(h.dom(a)&&
|
||||
u(W,c))return"transform";if(h.dom(a)&&(a.getAttribute(c)||h.svg(a)&&a[c]))return"attribute";if(h.dom(a)&&"transform"!==c&&E(a,c))return"css";if(null!=a[c])return"object"}function X(a,c){var d=V(c),d=-1<c.indexOf("scale")?1:0+d;a=a.style.transform;if(!a)return d;for(var b=[],f=[],n=[],k=/(\w+)\((.+?)\)/g;b=k.exec(a);)f.push(b[1]),n.push(b[2]);a=r(n,function(a,b){return f[b]===c});return a.length?a[0]:d}function K(a,c){switch(J(a,c)){case "transform":return X(a,c);case "css":return E(a,c);case "attribute":return a.getAttribute(c)}return a[c]||
|
||||
0}function L(a,c){var d=/^(\*=|\+=|-=)/.exec(a);if(!d)return a;var b=y(a)||0;c=parseFloat(c);a=parseFloat(a.replace(d[0],""));switch(d[0][0]){case "+":return c+a+b;case "-":return c-a+b;case "*":return c*a+b}}function F(a,c){return Math.sqrt(Math.pow(c.x-a.x,2)+Math.pow(c.y-a.y,2))}function M(a){a=a.points;for(var c=0,d,b=0;b<a.numberOfItems;b++){var f=a.getItem(b);0<b&&(c+=F(d,f));d=f}return c}function N(a){if(a.getTotalLength)return a.getTotalLength();switch(a.tagName.toLowerCase()){case "circle":return 2*
|
||||
Math.PI*a.getAttribute("r");case "rect":return 2*a.getAttribute("width")+2*a.getAttribute("height");case "line":return F({x:a.getAttribute("x1"),y:a.getAttribute("y1")},{x:a.getAttribute("x2"),y:a.getAttribute("y2")});case "polyline":return M(a);case "polygon":var c=a.points;return M(a)+F(c.getItem(c.numberOfItems-1),c.getItem(0))}}function Y(a,c){function d(b){b=void 0===b?0:b;return a.el.getPointAtLength(1<=c+b?c+b:0)}var b=d(),f=d(-1),n=d(1);switch(a.property){case "x":return b.x;case "y":return b.y;
|
||||
case "angle":return 180*Math.atan2(n.y-f.y,n.x-f.x)/Math.PI}}function O(a,c){var d=/-?\d*\.?\d+/g,b;b=h.pth(a)?a.totalLength:a;if(h.col(b))if(h.rgb(b)){var f=/rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g.exec(b);b=f?"rgba("+f[1]+",1)":b}else b=h.hex(b)?T(b):h.hsl(b)?U(b):void 0;else f=(f=y(b))?b.substr(0,b.length-f.length):b,b=c&&!/\s/g.test(b)?f+c:f;b+="";return{original:b,numbers:b.match(d)?b.match(d).map(Number):[0],strings:h.str(a)||c?b.split(d):[]}}function P(a){a=a?p(h.arr(a)?a.map(m):m(a)):[];return r(a,
|
||||
function(a,d,b){return b.indexOf(a)===d})}function Z(a){var c=P(a);return c.map(function(a,b){return{target:a,id:b,total:c.length}})}function aa(a,c){var d=C(c);if(h.arr(a)){var b=a.length;2!==b||h.obj(a[0])?h.fnc(c.duration)||(d.duration=c.duration/b):a={value:a}}return m(a).map(function(a,b){b=b?0:c.delay;a=h.obj(a)&&!h.pth(a)?a:{value:a};h.und(a.delay)&&(a.delay=b);return a}).map(function(a){return z(a,d)})}function ba(a,c){var d={},b;for(b in a){var f=I(a[b],c);h.arr(f)&&(f=f.map(function(a){return I(a,
|
||||
c)}),1===f.length&&(f=f[0]));d[b]=f}d.duration=parseFloat(d.duration);d.delay=parseFloat(d.delay);return d}function ca(a){return h.arr(a)?A.apply(this,a):Q[a]}function da(a,c){var d;return a.tweens.map(function(b){b=ba(b,c);var f=b.value,e=K(c.target,a.name),k=d?d.to.original:e,k=h.arr(f)?f[0]:k,w=L(h.arr(f)?f[1]:f,k),e=y(w)||y(k)||y(e);b.from=O(k,e);b.to=O(w,e);b.start=d?d.end:a.offset;b.end=b.start+b.delay+b.duration;b.easing=ca(b.easing);b.elasticity=(1E3-Math.min(Math.max(b.elasticity,1),999))/
|
||||
1E3;b.isPath=h.pth(f);b.isColor=h.col(b.from.original);b.isColor&&(b.round=1);return d=b})}function ea(a,c){return r(p(a.map(function(a){return c.map(function(b){var c=J(a.target,b.name);if(c){var d=da(b,a);b={type:c,property:b.name,animatable:a,tweens:d,duration:d[d.length-1].end,delay:d[0].delay}}else b=void 0;return b})})),function(a){return!h.und(a)})}function R(a,c,d,b){var f="delay"===a;return c.length?(f?Math.min:Math.max).apply(Math,c.map(function(b){return b[a]})):f?b.delay:d.offset+b.delay+
|
||||
b.duration}function fa(a){var c=D(ga,a),d=D(S,a),b=Z(a.targets),f=[],e=z(c,d),k;for(k in a)e.hasOwnProperty(k)||"targets"===k||f.push({name:k,offset:e.offset,tweens:aa(a[k],d)});a=ea(b,f);return z(c,{children:[],animatables:b,animations:a,duration:R("duration",a,c,d),delay:R("delay",a,c,d)})}function q(a){function c(){return window.Promise&&new Promise(function(a){return p=a})}function d(a){return g.reversed?g.duration-a:a}function b(a){for(var b=0,c={},d=g.animations,f=d.length;b<f;){var e=d[b],
|
||||
k=e.animatable,h=e.tweens,n=h.length-1,l=h[n];n&&(l=r(h,function(b){return a<b.end})[0]||l);for(var h=Math.min(Math.max(a-l.start-l.delay,0),l.duration)/l.duration,w=isNaN(h)?1:l.easing(h,l.elasticity),h=l.to.strings,p=l.round,n=[],m=void 0,m=l.to.numbers.length,t=0;t<m;t++){var x=void 0,x=l.to.numbers[t],q=l.from.numbers[t],x=l.isPath?Y(l.value,w*x):q+w*(x-q);p&&(l.isColor&&2<t||(x=Math.round(x*p)/p));n.push(x)}if(l=h.length)for(m=h[0],w=0;w<l;w++)p=h[w+1],t=n[w],isNaN(t)||(m=p?m+(t+p):m+(t+" "));
|
||||
else m=n[0];ha[e.type](k.target,e.property,m,c,k.id);e.currentValue=m;b++}if(b=Object.keys(c).length)for(d=0;d<b;d++)H||(H=E(document.body,"transform")?"transform":"-webkit-transform"),g.animatables[d].target.style[H]=c[d].join(" ");g.currentTime=a;g.progress=a/g.duration*100}function f(a){if(g[a])g[a](g)}function e(){g.remaining&&!0!==g.remaining&&g.remaining--}function k(a){var k=g.duration,n=g.offset,w=n+g.delay,r=g.currentTime,x=g.reversed,q=d(a);if(g.children.length){var u=g.children,v=u.length;
|
||||
if(q>=g.currentTime)for(var G=0;G<v;G++)u[G].seek(q);else for(;v--;)u[v].seek(q)}if(q>=w||!k)g.began||(g.began=!0,f("begin")),f("run");if(q>n&&q<k)b(q);else if(q<=n&&0!==r&&(b(0),x&&e()),q>=k&&r!==k||!k)b(k),x||e();f("update");a>=k&&(g.remaining?(t=h,"alternate"===g.direction&&(g.reversed=!g.reversed)):(g.pause(),g.completed||(g.completed=!0,f("complete"),"Promise"in window&&(p(),m=c()))),l=0)}a=void 0===a?{}:a;var h,t,l=0,p=null,m=c(),g=fa(a);g.reset=function(){var a=g.direction,c=g.loop;g.currentTime=
|
||||
0;g.progress=0;g.paused=!0;g.began=!1;g.completed=!1;g.reversed="reverse"===a;g.remaining="alternate"===a&&1===c?2:c;b(0);for(a=g.children.length;a--;)g.children[a].reset()};g.tick=function(a){h=a;t||(t=h);k((l+h-t)*q.speed)};g.seek=function(a){k(d(a))};g.pause=function(){var a=v.indexOf(g);-1<a&&v.splice(a,1);g.paused=!0};g.play=function(){g.paused&&(g.paused=!1,t=0,l=d(g.currentTime),v.push(g),B||ia())};g.reverse=function(){g.reversed=!g.reversed;t=0;l=d(g.currentTime)};g.restart=function(){g.pause();
|
||||
g.reset();g.play()};g.finished=m;g.reset();g.autoplay&&g.play();return g}var ga={update:void 0,begin:void 0,run:void 0,complete:void 0,loop:1,direction:"normal",autoplay:!0,offset:0},S={duration:1E3,delay:0,easing:"easeOutElastic",elasticity:500,round:0},W="translateX translateY translateZ rotate rotateX rotateY rotateZ scale scaleX scaleY scaleZ skewX skewY perspective".split(" "),H,h={arr:function(a){return Array.isArray(a)},obj:function(a){return-1<Object.prototype.toString.call(a).indexOf("Object")},
|
||||
pth:function(a){return h.obj(a)&&a.hasOwnProperty("totalLength")},svg:function(a){return a instanceof SVGElement},dom:function(a){return a.nodeType||h.svg(a)},str:function(a){return"string"===typeof a},fnc:function(a){return"function"===typeof a},und:function(a){return"undefined"===typeof a},hex:function(a){return/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a)},rgb:function(a){return/^rgb/.test(a)},hsl:function(a){return/^hsl/.test(a)},col:function(a){return h.hex(a)||h.rgb(a)||h.hsl(a)}},A=function(){function a(a,
|
||||
d,b){return(((1-3*b+3*d)*a+(3*b-6*d))*a+3*d)*a}return function(c,d,b,f){if(0<=c&&1>=c&&0<=b&&1>=b){var e=new Float32Array(11);if(c!==d||b!==f)for(var k=0;11>k;++k)e[k]=a(.1*k,c,b);return function(k){if(c===d&&b===f)return k;if(0===k)return 0;if(1===k)return 1;for(var h=0,l=1;10!==l&&e[l]<=k;++l)h+=.1;--l;var l=h+(k-e[l])/(e[l+1]-e[l])*.1,n=3*(1-3*b+3*c)*l*l+2*(3*b-6*c)*l+3*c;if(.001<=n){for(h=0;4>h;++h){n=3*(1-3*b+3*c)*l*l+2*(3*b-6*c)*l+3*c;if(0===n)break;var m=a(l,c,b)-k,l=l-m/n}k=l}else if(0===
|
||||
n)k=l;else{var l=h,h=h+.1,g=0;do m=l+(h-l)/2,n=a(m,c,b)-k,0<n?h=m:l=m;while(1e-7<Math.abs(n)&&10>++g);k=m}return a(k,d,f)}}}}(),Q=function(){function a(a,b){return 0===a||1===a?a:-Math.pow(2,10*(a-1))*Math.sin(2*(a-1-b/(2*Math.PI)*Math.asin(1))*Math.PI/b)}var c="Quad Cubic Quart Quint Sine Expo Circ Back Elastic".split(" "),d={In:[[.55,.085,.68,.53],[.55,.055,.675,.19],[.895,.03,.685,.22],[.755,.05,.855,.06],[.47,0,.745,.715],[.95,.05,.795,.035],[.6,.04,.98,.335],[.6,-.28,.735,.045],a],Out:[[.25,
|
||||
.46,.45,.94],[.215,.61,.355,1],[.165,.84,.44,1],[.23,1,.32,1],[.39,.575,.565,1],[.19,1,.22,1],[.075,.82,.165,1],[.175,.885,.32,1.275],function(b,c){return 1-a(1-b,c)}],InOut:[[.455,.03,.515,.955],[.645,.045,.355,1],[.77,0,.175,1],[.86,0,.07,1],[.445,.05,.55,.95],[1,0,0,1],[.785,.135,.15,.86],[.68,-.55,.265,1.55],function(b,c){return.5>b?a(2*b,c)/2:1-a(-2*b+2,c)/2}]},b={linear:A(.25,.25,.75,.75)},f={},e;for(e in d)f.type=e,d[f.type].forEach(function(a){return function(d,f){b["ease"+a.type+c[f]]=h.fnc(d)?
|
||||
d:A.apply($jscomp$this,d)}}(f)),f={type:f.type};return b}(),ha={css:function(a,c,d){return a.style[c]=d},attribute:function(a,c,d){return a.setAttribute(c,d)},object:function(a,c,d){return a[c]=d},transform:function(a,c,d,b,f){b[f]||(b[f]=[]);b[f].push(c+"("+d+")")}},v=[],B=0,ia=function(){function a(){B=requestAnimationFrame(c)}function c(c){var b=v.length;if(b){for(var d=0;d<b;)v[d]&&v[d].tick(c),d++;a()}else cancelAnimationFrame(B),B=0}return a}();q.version="2.2.0";q.speed=1;q.running=v;q.remove=
|
||||
function(a){a=P(a);for(var c=v.length;c--;)for(var d=v[c],b=d.animations,f=b.length;f--;)u(a,b[f].animatable.target)&&(b.splice(f,1),b.length||d.pause())};q.getValue=K;q.path=function(a,c){var d=h.str(a)?e(a)[0]:a,b=c||100;return function(a){return{el:d,property:a,totalLength:N(d)*(b/100)}}};q.setDashoffset=function(a){var c=N(a);a.setAttribute("stroke-dasharray",c);return c};q.bezier=A;q.easings=Q;q.timeline=function(a){var c=q(a);c.pause();c.duration=0;c.add=function(d){c.children.forEach(function(a){a.began=
|
||||
!0;a.completed=!0});m(d).forEach(function(b){var d=z(b,D(S,a||{}));d.targets=d.targets||a.targets;b=c.duration;var e=d.offset;d.autoplay=!1;d.direction=c.direction;d.offset=h.und(e)?b:L(e,b);c.began=!0;c.completed=!0;c.seek(d.offset);d=q(d);d.began=!0;d.completed=!0;d.duration>b&&(c.duration=d.duration);c.children.push(d)});c.seek(0);c.reset();c.autoplay&&c.restart();return c};return c};q.random=function(a,c){return Math.floor(Math.random()*(c-a+1))+a};return q});
|
||||
2
src/windows/js/jquery-3.3.1.min.js
vendored
Normal file
231
src/windows/js/preferences.js
Normal file
@@ -0,0 +1,231 @@
|
||||
//* Declare needed constants
|
||||
const remote = require('electron').remote
|
||||
const app = remote.app
|
||||
const AutoLaunch = require('auto-launch');
|
||||
const Config = require('electron-config')
|
||||
const userSettings = new Config({
|
||||
name: "userSettings"
|
||||
})
|
||||
|
||||
var togglePresence,
|
||||
toggleYouTube,
|
||||
toggleYouTubeMusic,
|
||||
toggleTitleMenubar;
|
||||
|
||||
$(() => {
|
||||
|
||||
var os = require('os')
|
||||
if (os.platform() == "darwin") {
|
||||
$('<tr><td class="noselect">Title menubar</td><td class="right"><label class="switch"><input class="toggleTitleMenubar" type="checkbox"><span class="slider round"></span></label></td></tr>').insertAfter('.soundcloudToggle')
|
||||
}
|
||||
|
||||
togglePresence = $(".togglePresence"),
|
||||
toggleYouTube = $(".toggleYouTube"),
|
||||
toggleYouTubeMusic = $(".toggleYouTubeMusic");
|
||||
toggleNetflix = $(".toggleNetflix");
|
||||
toggleTwitch = $(".toggleTwitch");
|
||||
toggleSoundCloud = $(".toggleSoundCloud");
|
||||
toggleTitleMenubar = $(".toggleTitleMenubar");
|
||||
toggleAutoLaunch = $(".toggleAutoLaunch");
|
||||
toggleAutoUpdate = $(".toggleAutoUpdate");
|
||||
|
||||
togglePresence.change(switchPresence);
|
||||
toggleYouTube.change(switchYouTube);
|
||||
toggleYouTubeMusic.change(switchYouTubeMusic);
|
||||
toggleTitleMenubar.change(switchTitleMenubar);
|
||||
toggleAutoLaunch.change(switchAutomaticStartup);
|
||||
toggleAutoUpdate.change(switchAutoUpdate);
|
||||
toggleNetflix.change(switchNetflix);
|
||||
toggleTwitch.change(switchTwitch);
|
||||
toggleSoundCloud.change(switchSoundCloud);
|
||||
|
||||
if (userSettings.get('enabled') == true) togglePresence.prop("checked", true)
|
||||
else {
|
||||
togglePresence.prop("checked", false)
|
||||
toggleYouTube.prop("checked", false)
|
||||
toggleYouTubeMusic.prop("checked", false)
|
||||
toggleNetflix.prop("checked", false)
|
||||
toggleTwitch.prop("checked", false)
|
||||
toggleSoundCloud.prop("checked", false)
|
||||
|
||||
toggleYouTube.attr("disabled", "disabled")
|
||||
toggleYouTubeMusic.attr("disabled", "disabled")
|
||||
toggleNetflix.attr("disabled", "disabled")
|
||||
toggleTwitch.attr("disabled", "disabled")
|
||||
toggleSoundCloud.attr("disabled", "disabled")
|
||||
toggleTitleMenubar.attr("disabled", "disabled")
|
||||
}
|
||||
|
||||
if (userSettings.get('youTube') == true) {
|
||||
toggleYouTube.prop("checked", true)
|
||||
toggleYouTube.removeAttr("disabled")
|
||||
} else toggleYouTube.prop("checked", false)
|
||||
|
||||
if (userSettings.get('youTubeMusic') == true) {
|
||||
toggleYouTubeMusic.prop("checked", true)
|
||||
toggleYouTubeMusic.removeAttr("disabled")
|
||||
} else toggleYouTubeMusic.prop("checked", false)
|
||||
|
||||
if (userSettings.get('netflix') == true) {
|
||||
toggleNetflix.prop("checked", true)
|
||||
toggleNetflix.removeAttr("disabled")
|
||||
} else toggleNetflix.prop("checked", false)
|
||||
|
||||
if (userSettings.get('twitch') == true) {
|
||||
toggleTwitch.prop("checked", true)
|
||||
toggleTwitch.removeAttr("disabled")
|
||||
} else toggleTwitch.prop("checked", false)
|
||||
|
||||
if (userSettings.get('soundcloud') == true) {
|
||||
toggleSoundCloud.prop("checked", true)
|
||||
toggleSoundCloud.removeAttr("disabled")
|
||||
} else toggleSoundCloud.prop("checked", false)
|
||||
|
||||
if (userSettings.get('titleMenubar') == true) {
|
||||
toggleTitleMenubar.prop("checked", true)
|
||||
toggleTitleMenubar.removeAttr("disabled")
|
||||
} else {
|
||||
toggleTitleMenubar.prop("checked", false)
|
||||
}
|
||||
|
||||
if (userSettings.get('autoLaunch') == true) {
|
||||
toggleAutoLaunch.prop("checked", true)
|
||||
toggleAutoLaunch.removeAttr("disabled")
|
||||
} else toggleAutoLaunch.prop("checked", false)
|
||||
|
||||
if (userSettings.get('autoUpdateCheck') == true) {
|
||||
toggleAutoUpdate.prop("checked", true)
|
||||
toggleAutoUpdate.removeAttr("disabled")
|
||||
} else toggleAutoUpdate.prop("checked", false)
|
||||
})
|
||||
|
||||
function switchPresence() {
|
||||
if (userSettings.get('enabled') == true) {
|
||||
userSettings.set('enabled', false);
|
||||
userSettings.set('youTube', false);
|
||||
userSettings.set('youTubeMusic', false);
|
||||
userSettings.set('netflix', false);
|
||||
userSettings.set('soundcloud', false);
|
||||
|
||||
toggleYouTube.prop("checked", false)
|
||||
toggleYouTube.attr("disabled", "disabled")
|
||||
|
||||
toggleYouTubeMusic.prop("checked", false)
|
||||
toggleYouTubeMusic.attr("disabled", "disabled")
|
||||
|
||||
toggleNetflix.prop("checked", false)
|
||||
toggleNetflix.attr("disabled", "disabled")
|
||||
|
||||
toggleTwitch.prop("checked", false)
|
||||
toggleTwitch.attr("disabled", "disabled")
|
||||
|
||||
toggleSoundCloud.prop("checked", false)
|
||||
toggleSoundCloud.attr("disabled", "disabled")
|
||||
} else {
|
||||
userSettings.set('enabled', true);
|
||||
toggleYouTube.removeAttr("disabled")
|
||||
toggleYouTubeMusic.removeAttr("disabled")
|
||||
toggleNetflix.removeAttr("disabled")
|
||||
toggleTwitch.removeAttr("disabled")
|
||||
toggleSoundCloud.removeAttr("disabled")
|
||||
}
|
||||
}
|
||||
|
||||
function switchYouTube() {
|
||||
if (userSettings.get('youTube') == true) {
|
||||
userSettings.set('youTube', false)
|
||||
toggleYouTube.prop("checked", false)
|
||||
} else {
|
||||
userSettings.set('youTube', true)
|
||||
toggleYouTube.prop("checked", true)
|
||||
}
|
||||
}
|
||||
|
||||
function switchYouTubeMusic() {
|
||||
if (userSettings.get('youTubeMusic') == true) {
|
||||
userSettings.set('youTubeMusic', false);
|
||||
toggleYouTubeMusic.prop("checked", false)
|
||||
} else {
|
||||
userSettings.set('youTubeMusic', true);
|
||||
toggleYouTubeMusic.prop("checked", true)
|
||||
}
|
||||
}
|
||||
|
||||
function switchNetflix() {
|
||||
if (userSettings.get('netflix') == true) {
|
||||
userSettings.set('netflix', false);
|
||||
toggleNetflix.prop("checked", false)
|
||||
} else {
|
||||
userSettings.set('netflix', true);
|
||||
toggleNetflix.prop("checked", true)
|
||||
}
|
||||
}
|
||||
|
||||
function switchTwitch() {
|
||||
if (userSettings.get('twitch') == true) {
|
||||
userSettings.set('twitch', false);
|
||||
toggleTwitch.prop("checked", false)
|
||||
} else {
|
||||
userSettings.set('twitch', true);
|
||||
toggleTwitch.prop("checked", true)
|
||||
}
|
||||
}
|
||||
|
||||
function switchSoundCloud() {
|
||||
if (userSettings.get('soundcloud') == true) {
|
||||
userSettings.set('soundcloud', false);
|
||||
toggleSoundCloud.prop("checked", false)
|
||||
} else {
|
||||
userSettings.set('soundcloud', true);
|
||||
toggleSoundCloud.prop("checked", true)
|
||||
}
|
||||
}
|
||||
|
||||
function switchTitleMenubar() {
|
||||
if (userSettings.get('titleMenubar') == true) {
|
||||
userSettings.set('titleMenubar', false);
|
||||
toggleTitleMenubar.prop("checked", false)
|
||||
} else {
|
||||
userSettings.set('titleMenubar', true);
|
||||
toggleTitleMenubar.prop("checked", true)
|
||||
}
|
||||
}
|
||||
|
||||
function switchAutomaticStartup() {
|
||||
//* Add App to AutoLaunch
|
||||
let autoLaunch = new AutoLaunch({
|
||||
name: 'YT Presence',
|
||||
path: app.getPath('exe'),
|
||||
isHidden: true
|
||||
});
|
||||
|
||||
if (userSettings.get('autoLaunch') == true) {
|
||||
autoLaunch.isEnabled().then((isEnabled) => {
|
||||
if(isEnabled) autoLaunch.disable()
|
||||
}).catch((err) => {
|
||||
console.log("Error while adding App to autostart.")
|
||||
})
|
||||
|
||||
userSettings.set('autoLaunch', false);
|
||||
toggleAutoLaunch.prop("checked", false)
|
||||
} else {
|
||||
autoLaunch.isEnabled().then((isEnabled) => {
|
||||
if(!isEnabled) autoLaunch.enable()
|
||||
}).catch((err) => {
|
||||
console.log("Error while adding App to autostart.")
|
||||
})
|
||||
|
||||
userSettings.set('autoLaunch', true);
|
||||
toggleAutoLaunch.prop("checked", true)
|
||||
}
|
||||
}
|
||||
|
||||
function switchAutoUpdate() {
|
||||
if (userSettings.get('autoUpdateCheck') == true) {
|
||||
userSettings.set('autoUpdateCheck', false);
|
||||
toggleAutoUpdate.prop("checked", false)
|
||||
} else {
|
||||
userSettings.set('autoUpdateCheck', true);
|
||||
toggleAutoUpdate.prop("checked", true)
|
||||
}
|
||||
}
|
||||
5
src/windows/js/ripple.min.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
(function(){function h(c){c=c.target;var a=c.childNodes.length;if("button"!==c.localName||!a)return c.classList.contains("rippleJS")?c:null;for(var b=0;b<a;++b){var g=c.childNodes[b],e=g.classList;if(e&&e.contains("rippleJS"))return g}return null}
|
||||
function n(c,a){var b=h(a);if(b){var g=b.classList,e=b.getAttribute("data-event");if(!e||e===c){b.setAttribute("data-event",c);var d=b.getBoundingClientRect();e=a.offsetX;void 0!==e?a=a.offsetY:(e=a.clientX-d.left,a=a.clientY-d.top);var f=document.createElement("div");d=d.width===d.height?1.412*d.width:Math.sqrt(d.width*d.width+d.height*d.height);var k=2*d+"px";f.style.width=k;f.style.height=k;f.style.marginLeft=-d+e+"px";f.style.marginTop=-d+a+"px";f.className="ripple";b.appendChild(f);window.setTimeout(function(){f.classList.add("held")},
|
||||
0);var l="mousedown"===c?"mouseup":"touchend",m=function(){document.removeEventListener(l,m);f.classList.add("done");window.setTimeout(function(){b.removeChild(f);b.children.length||(g.remove("active"),b.removeAttribute("data-event"))},650)};document.addEventListener(l,m)}}}
|
||||
function p(){var c=c||document;c.addEventListener("mousedown",function(a){0===a.button&&n(a.type,a)},{passive:!0});c.addEventListener("touchstart",function(a){for(var b=0;b<a.changedTouches.length;++b)n(a.type,a.changedTouches[b])},{passive:!0})};(function(){function c(){var a=document.createElement("div");a.className="rippleJS";document.body.appendChild(a);var b="absolute"===window.getComputedStyle(a).position;document.body.removeChild(a);b||(a=document.createElement("style"),a.textContent='/*rippleJS*/.rippleJS,.rippleJS.fill::after{position:absolute;top:0;left:0;right:0;bottom:0}.rippleJS{display:block;overflow:hidden;border-radius:inherit;-webkit-mask-image:-webkit-radial-gradient(circle,#fff,#000)}.rippleJS.fill::after{content:""}.rippleJS.fill{border-radius:1000000px}.rippleJS .ripple{position:absolute;border-radius:100%;background:currentColor;opacity:.2;width:0;height:0;-webkit-transition:-webkit-transform .4s ease-out,opacity .4s ease-out;transition:transform .4s ease-out,opacity .4s ease-out;-webkit-transform:scale(0);transform:scale(0);pointer-events:none;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.rippleJS .ripple.held{opacity:.4;-webkit-transform:scale(1);transform:scale(1)}.rippleJS .ripple.done{opacity:0}',
|
||||
document.head.insertBefore(a,document.head.firstChild));p()}"complete"===document.readyState?c():window.addEventListener("load",c)})();}())
|
||||
3
src/windows/js/showdown.min.js
vendored
Executable file
122
src/windows/preferences.html
Normal file
@@ -0,0 +1,122 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Preferences • YT Presence</title>
|
||||
<link href="css/reset.css" rel="stylesheet">
|
||||
<link href="css/preferences.css" rel="stylesheet">
|
||||
<link href="css/inputs.css" rel="stylesheet">
|
||||
<link href="css/ripple.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="header">
|
||||
<img draggable="false" src="../assets/images/logo.png">
|
||||
<h1>YT Presence</h1>
|
||||
</div>
|
||||
<div id="content">
|
||||
<table id="options">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Enabled</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input class="togglePresence" type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>YouTube</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input class="toggleYouTube" type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ytmToggle">
|
||||
<td>YouTube Music</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input class="toggleYouTubeMusic" type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="nflixToggle">
|
||||
<td>Netflix</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input class="toggleNetflix" type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="twitchToggle">
|
||||
<td>Twitch</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input class="toggleTwitch" type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="soundcloudToggle">
|
||||
<td>SoundCloud</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input class="toggleSoundCloud" type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Launch on system startup</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input class="toggleAutoLaunch" type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Automatically check for updates</td>
|
||||
<td>
|
||||
<label class="switch">
|
||||
<input class="toggleAutoUpdate" type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<button id="close-btn">
|
||||
Close
|
||||
<div class="rippleJS"></div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>if (typeof module === 'object') { window.module = module; module = undefined; }</script>
|
||||
|
||||
<script src="js/jquery-3.3.1.min.js"></script>
|
||||
<script src="js/preferences.js"></script>
|
||||
<script src="js/ripple.min.js"></script>
|
||||
|
||||
<script>if (window.module) module = window.module;</script>
|
||||
<script>
|
||||
document.getElementById("close-btn").addEventListener("click", function (e) {
|
||||
var window = remote.getCurrentWindow();
|
||||
window.close();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
60
src/windows/update.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>YT Presence • Update avaiable!</title>
|
||||
<link rel="stylesheet" href="css/reset.css">
|
||||
<link rel="stylesheet" href="css/update.css">
|
||||
<link href="css/ripple.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="header">
|
||||
<img draggable="false" src="../assets/images/logo.png">
|
||||
<h1>YT Presence</h1>
|
||||
</div>
|
||||
<div id="content">
|
||||
<p id="description"></p>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<button id="update">
|
||||
Update
|
||||
<div class="rippleJS"></div>
|
||||
</button>
|
||||
<button id="later">
|
||||
Later
|
||||
<div class="rippleJS"></div>
|
||||
</button>
|
||||
</div>
|
||||
<script>if (typeof module === 'object') { window.module = module; module = undefined; }</script>
|
||||
|
||||
<script src="js/jquery-3.3.1.min.js"></script>
|
||||
<script src="js/showdown.min.js"></script>
|
||||
<script src="js/ripple.min.js"></script>
|
||||
|
||||
<script>if (window.module) module = window.module;</script>
|
||||
<script>
|
||||
var converter = new showdown.Converter()
|
||||
|
||||
const ipc = require('electron').ipcRenderer;
|
||||
const remote = require('electron').remote
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var shell = require('electron').shell;
|
||||
ipc.on('updateData', (event, message) => {
|
||||
$('#description').html(converter.makeHtml(message.body))
|
||||
$('#update').click(() => {
|
||||
console.log(message)
|
||||
shell.openExternal(`https://github.com/Timeraa/YT-Presence/releases/tag/${message.tag_name}`);
|
||||
remote.getCurrentWindow().close()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
$('#later').click(e => {
|
||||
remote.getCurrentWindow().close()
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||