text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <title>Changelog</title>
    <link
        type="text/css"
        rel="stylesheet"
        href="../../../src/assets/css/styles.css"
    />
    <link type="text/css" rel="stylesheet" href="./changelog.css" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />


text:
const { app } = require('electron').remote
const markdown = require('markdown').markdown
const fetch = require('node-fetch')
const settingsProvider = require('../../providers/settingsProvider')

fetch(`https://api.github.com/repos/ytmdesktop/ytmdesktop/releases`)
    .then((res) => res.json())
    .then((json) => {
        let tag = `v${app.getVersion()}`

        let filtered = json.filter((value) => value.tag_name === tag)
        let changelog = filtered[0]

        let body = changelog.body

        document.getElementById('version').innerHTML = markdown.toHTML(tag)
        document.getElementById('changelog').innerHTML = markdown.toHTML(body)

        settingsProvider.set('has-updated', false)
    })

repo_name: ytmdesktop/ytmdesktop

repo_language: JavaScript

mime_type: text/plain


text:
        <meta charset="UTF-8" />
        <title>YouTube Music</title>
        <link
            type="text/css"
            rel="stylesheet"
            href="../../../src/assets/css/materialize.min.css"
            media="screen,projection"
        />
        <link
            href="../../../src/assets/css/material_icons.css"
            rel="stylesheet"
        />
        <link
            type="text/css"
            rel="stylesheet"
            href="../../../src/assets/css/styles.css"
        />
        <link type="text/css" rel="stylesheet" href="./streamPlayer.css" />

                    drag_indicator

                    launch

                    Title

                    Album

                    Author

                    0:00 /
                    0:00

                        thumb_down

                        skip_previous

                        play_arrow

                        skip_next

                        thumb_up


text:
:root {
    --text-margin-left: 12px;
}

body:hover .player-info,
body:hover .cmd-btn,
body.showinfo .player-info,
body.showinfo .cmd-btn {
    filter: none;
}

.player-info {
    height: 100vh;
    margin-top: 10vh;
}

.cmd-bar {
    margin: 4px 4px 0;
    height: 100vh;
    position: absolute;
    top: 0;
    width: calc(100% - 8px);
}

.cmd-btn {
    width: 20px;
    height: 20px;
}

.cmd-btn i {
    font-size: 12vmin;
    margin-right: -1vmin;
}

#btn-drag {
    opacity: 0;
}

#btn-like,
#btn-dislike,
#btn-next,
#btn-previous,
#btn-play-pause {
    margin-top: 100vmax;
    opacity: 0;
}

.title {
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
    white-space: nowrap;
    font-weight: bold;
    font-size: 22vmin;
    margin-top: -13vmin;
    margin-left: calc(100vh + var(--text-margin-left));
}

.album {
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
    white-space: nowrap;
    font-size: 18vmin;
    margin-top: -6vmin;
    margin-left: calc(100vh + var(--text-margin-left));
}

.author {
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
    white-space: nowrap;
    font-size: 16vmin;
    margin-top: -5vmin;
    margin-left: calc(100vh + var(--text-margin-left));
}

.duration {
    text-align: left;
    font-size: 14vmin;
    margin-top: -4vmin;
    margin-left: calc(100vh + var(--text-margin-left));
}

.progress-bar {
    margin-top: 15vmin;
    height: 5vmin;
    width: 80vmax;
    margin-left: calc(100vh);
    position: absolute;
    bottom: 0;
}

#progress {
    height: 100%;
    width: 0;
    background: #fff;
}

#content,
#secondsEffect {
    position: absolute;
    top: 0;
    width: 100vmax;
    height: 100vmin;
    z-index: 1;
}

#content {
    text-shadow: 0 0 3 #000;
}

#background {
    background-repeat: no-repeat;
    background-size: 100vh 100vh;
    background-position: center;
    position: absolute;
    width: 100vh;
    height: 100vh;
    z-index: 0;
}

#loading {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
    z-index: 1;
}

#secondsEffect {
    display: none;
}

#secondsEffect.left {
    left: -40vmin;
    display: block;
}

#secondsEffect.right {
    right: -40vmin;
    transform: rotate(180deg);
    display: block;
}


text:
body:hover .player-info,
body:hover .cmd-btn,
body.showinfo .player-info,
body.showinfo .cmd-btn {
    filter: none;
}

body:hover .progress-bar,
body.showinfo .progress-bar {
    margin-top: 0;
    height: 2vmin;
}

body:hover #background,
body.showinfo #background {
    filter: blur(3px) brightness(0.7);
}

.player-info {
    filter: opacity(0);
    height: 78vmin;
}

.cmd-bar {
    margin: 4px 4px 0;
    height: 10vmin;
}

#btn-drag {
    filter: none !important;
}

.cmd-btn {
    width: 10vmin;
    height: 100%;
    padding: 1.5vmin;
    filter: opacity(0);
    border-radius: 100%;
}

.cmd-btn i {
    font-size: 7vmin;
}

.title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: bold;
    font-size: 8.5vmin;
    padding: 0 2.5vmin;
    margin-top: calc(10vmin - 4px);
}

/*
    Album is a data call used in the stream player but not in the default.
    Due to the .js file calling this data I maintain parity with these base files.
    I have set the opacity to 0 and the position to absolute to not interfere.
    Just get rid of those settings to add it to the default mini player.
*/
.album {
    opacity: 0;
    position: absolute;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
    white-space: nowrap;
    font-size: 18vmin;
    margin-top: -6vmin;
    margin-left: 22.5vmax;
}

.author {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 7vmin;
    padding: 0 10px;
    margin-top: 0;
}

.duration {
    font-size: 5.5vmin;
    margin-top: 2vmin;
}

.controls {
    display: flex;
    justify-content: center;
    margin-top: 10vmin;
}

.ctrl-btn {
    float: left;
    padding: 1.5vmin;
    margin: 5vmin 1vmin;
    border-radius: 50%;
}

.ctrl-btn i {
    font-size: 12vmin;
}

.ctrl-btn:hover {
    background: rgba(255, 255, 255, 0.1);
}

#btn-play-pause {
    margin: 0;
}

#btn-like,
#btn-dislike {
    margin: 5vmin 2.5vmin;
    width: 100%;
}

#btn-like i,
#btn-dislike i {
    font-size: 7vmin;
    padding: 2.5vmin;
    position: absolute;
}

#btn-play-pause i {
    font-size: 22vmin;
}

.progress-bar {
    margin-top: 1.4vmin;
    height: 0.6vmin;
    width: 100vmin;
}

#progress {
    height: 100%;
    width: 0;
    background: #fff;
    transition: width 0.15s ease-in;
}

#content,
#secondsEffect {
    position: absolute;
    top: 0;
    width: 100vmin;
    height: 100vmin;
    z-index: 1;
}

#content {
    text-shadow: 0 0 3 #000;
}

#background {
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center;
    position: absolute;
    top: -1vmin;
    left: -1vmin;
    width: 102vmin;
    height: 102vmin;
    z-index: 0;
}

#loading {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
    z-index: 1;
}

#secondsEffect {
    display: none;
}

#secondsEffect.left {
    left: -40vmin;
    display: block;
}

#secondsEffect.right {
    right: -40vmin;
    transform: rotate(180deg);
    display: block;
}

/* transitions */
.player-info,
.cmd-btn,
.progress-bar,
.ctrl-btn,
.material-icons {
    transition: filter, margin-top, height, background-color, opacity;
    transition-duration: 0.15s;
    transition-timing-function: ease-out;
}

.show-solid {
    opacity: 1;
}
.hide-solid {
    opacity: 0;
}


text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <title>YouTube Music</title>
    <link
        type="text/css"
        rel="stylesheet"
        href="../../../src/assets/css/materialize.min.css"
        media="screen,projection"
    />
    <link
        type="text/css"
        href="../../../src/assets/css/material_icons.css"
        rel="stylesheet"
    />
    <link
        type="text/css"
        rel="stylesheet"
        href="../../../src/assets/css/styles.css"
    />
    <link type="text/css" rel="stylesheet" href="./miniplayer.css" />

                drag_indicator

                launch

                Title

                Album

                Author

                0:00
                0:00

                    thumb_down

                    thumb_down

                    skip_previous

                    play_arrow

                    skip_next

                    thumb_up

                    thumb_up


text:
const { ipcRenderer } = require('electron')
const settingsProvider = require('../../providers/settingsProvider')

const btnClose = document.getElementById('btn-close')
const btnDislike = document.getElementById('btn-dislike')
const btnPrevious = document.getElementById('btn-previous')
const btnPlayPause = document.getElementById('btn-play-pause')
const btnNext = document.getElementById('btn-next')
const btnLike = document.getElementById('btn-like')

let body = document.getElementsByTagName('body')[0]
let background = document.getElementById('background')
let title = document.getElementById('title')
let author = document.getElementById('author')
let album = document.getElementById('album')

let current = document.getElementById('current')
let duration = document.getElementById('duration')
let progress = document.getElementById('progress')
let secondsEffect = document.getElementById('secondsEffect')

document.addEventListener('DOMContentLoaded', async () => {
    setPlayerInfo(await retrieveAllInfo())

    setInterval(async () => {
        setPlayerInfo(await retrieveAllInfo())
    }, 500)

    document.addEventListener('wheel', function (ev) {
        ev.preventDefault()
        if (ev.deltaY < 0)
            ipcRenderer.send('media-command', { command: 'media-volume-up' })
        else ipcRenderer.send('media-command', { command: 'media-volume-down' })
    })

    document.addEventListener('dblclick', (ev) => {
        if (ev.clientX >= 100) {
            ipcRenderer.send('media-command', {
                command: 'media-seekbar-forward',
            })
            showDbClickAnimation('right')
        } else {
            ipcRenderer.send('media-command', {
                command: 'media-seekbar-rewind',
            })
            showDbClickAnimation('left')
        }
    })

    btnClose.addEventListener('click', () => {
        ipcRenderer.send('window', { command: 'restore-main-window' })
    })

    btnDislike.addEventListener('click', () => {
        ipcRenderer.send('media-command', { command: 'media-vote-down' })
    })

    btnPrevious.addEventListener('click', () => {
        ipcRenderer.send('media-command', { command: 'media-track-previous' })
    })

    btnPlayPause.addEventListener('click', () => {
        ipcRenderer.send('media-command', { command: 'media-play-pause' })
        body.classList.toggle('showinfo')
    })

    btnNext.addEventListener('click', () => {
        ipcRenderer.send('media-command', { command: 'media-track-next' })
    })

    btnLike.addEventListener('click', () => {
        ipcRenderer.send('media-command', { command: 'media-vote-up' })
    })
})

async function retrieveAllInfo() {
    return new Promise((resolve, reject) => {
        ipcRenderer
            .invoke('invoke-all-info')
            .then((result) => resolve(result))
            .catch((_) => reject(false))
    })
}

function setPlayerInfo(data) {
    document.title = `${data.track.title} - ${data.track.author}`
    background.style.backgroundImage = `url(${data.track.cover})`
    title.innerHTML = data.track.title || 'Title'
    author.innerHTML = data.track.author || 'Author'
    album.innerHTML = data.track.album || 'Album'
    current.innerHTML = data.player.seekbarCurrentPositionHuman || '0:00'
    duration.innerHTML = data.track.durationHuman || '0:00'
    progress.style.width = data.player.statePercent * 100 + '%'
    if (data.player.isPaused) {
        btnPlayPause.children.item(0).innerHTML = 'play_arrow'
        body.classList.add('showinfo')
    } else {
        btnPlayPause.children.item(0).innerHTML = 'pause'
        body.classList.remove('showinfo')
    }

    const btnLikeCL = btnLike.children.item(0).classList
    const btnDislikeCL = btnDislike.children.item(0).classList

    switch (data.player.likeStatus) {
        case 'LIKE':
            btnLikeCL.add('show-solid')
            btnLikeCL.remove('hide-solid')
            btnDislikeCL.add('hide-solid')
            btnDislikeCL.remove('show-solid')
            break

        case 'DISLIKE':
            btnLikeCL.add('hide-solid')
            btnLikeCL.remove('show-solid')
            btnDislikeCL.add('show-solid')
            btnDislikeCL.remove('hide-solid')
            break

        case 'INDIFFERENT':
            btnDislikeCL.add('hide-solid')
            btnDislikeCL.remove('show-solid')
            btnLikeCL.add('hide-solid')
            btnLikeCL.remove('show-solid')
            break
    }

    if (settingsProvider.get('settings-miniplayer-paint-controls')) {
        const Vibrant = require('node-vibrant')

        Vibrant.from(data.track.cover)
            .getPalette()
            .then((palette) => {
                body.style.color = palette.LightVibrant.hex
                progress.style.background = palette.LightVibrant.hex
                secondsEffect.style.background = `linear-gradient(to right, ${palette.LightVibrant.hex}, transparent)`
            })
    }

    if (settingsProvider.get('settings-miniplayer-always-show-controls'))
        body.classList.add('showinfo')

    if (data.track.id) {
        document.querySelector('#loading').classList.add('hide')
        document.querySelector('#content').classList.remove('hide')
    }
}

function showDbClickAnimation(side) {
    secondsEffect.classList.add(side)
    setTimeout(() => {
        secondsEffect.classList.remove(side)
    }, 200)
}


text:
const { ipcRenderer } = require('electron')
const fetch = require('node-fetch')
const __ = require('../../providers/translateProvider')
const infoPlayerProvider = require('electron').remote.require(
    './src/providers/infoPlayerProvider'
)
const settingsProvider = require('../../providers/settingsProvider')

const elementLyric = document.getElementById('lyric')
const elementLyricSource = document.getElementById('lyric-source')

let lastId, target, toggled, geniusAuth

loadingLyrics()

document.getElementById('content').addEventListener('dblclick', (_) => {
    document.getElementById('content').scrollTo(0, target)
})

document.getElementById('content').addEventListener('scroll', (_) => {
    const scrollTop = document.getElementById('content').scrollTop
    const differential =
        target > scrollTop ? target - scrollTop : scrollTop - target
    if (differential >= 40) {
        document.getElementById('tips').innerText = __.trans(
            'DOUBLE_CLICK_TO_RESET_POSITION'
        )
        toggled = false
    } else {
        document.getElementById('tips').innerText = ''
        toggled = true
    }
})

setInterval(async () => {
    await setData(await retrieveAllInfo())
}, 1000)

async function setData(data) {
    const scrollHeight = document.getElementById('content').scrollHeight
    target = (scrollHeight * data.player.statePercent) / 1.4
    if (toggled) document.getElementById('content').scrollTo(0, target)

    getLyric(data.track.author, data.track.title, data.track.id)
}

async function retrieveAllInfo() {
    return new Promise((resolve, reject) => {
        ipcRenderer
            .invoke('invoke-all-info')
            .then((result) => resolve(result))
            .catch((_) => reject(false))
    })
}

function getLyric(artist, song, id) {
    if (artist !== undefined && song !== undefined) {
        if (lastId !== id) {
            lastId = id
            toggled = true
            loadingLyrics()

            // Genius will be skipped if not enabled.
            retrieveGeniusData(artist, song)
                .then((success) => setLyrics('Genius', success, true))
                .catch((_) =>
                    retrieveVagalumeData(artist, song)
                        .then((success_) =>
                            setLyrics('Vagalume', success_, true)
                        )
                        .catch((_) =>
                            retrieveKsoftData(artist, song)
                                .then((success) =>
                                    setLyrics('KSoft', success, true)
                                )
                                .catch((_) =>
                                    retrieveOVHData(artist, song)
                                        .then((success) =>
                                            setLyrics('OVH', success, true)
                                        )
                                        .catch((error) => {
                                            elementLyric.innerText = error
                                            setLyrics('-', error, true)
                                        })
                                )
                        )
                )
        }
    } else elementLyric.innerText = __.trans('LABEL_PLAY_MUSIC')
}

function setLyrics(source, lyrics, hasLoaded) {
    if (source === 'Genius') {
        // Lyrics in Genius is an object check here; https://docs.genius.com/#search-h2 "response: { hits: { result: { ..."
        elementLyric.innerText = lyrics.full_title
        const lyricsElementId = `rg_embed_link_${lyrics.id}`

        const node = document.createElement('div')
        node.id = 'overlay'
        document.getElementById('content').appendChild(node)

        const postscribe = require('postscribe')
        postscribe(
            '#lyric',
            `<div id='${lyricsElementId}' class='rg_embed_link' data-song-id='${lyrics.id}'>Read <a href='https://genius.com${lyrics.path}'>${lyrics.full_title}</a> on Genius</div> `,
            {
                done: () =>
                    (document.getElementsByClassName(
                        'rg_embed music'
                    )[0].style.color = 'black'),
            }
        )
    } else {
        elementLyric.innerText = lyrics
        infoPlayerProvider.updateLyrics(source, lyrics, hasLoaded)
    }
    elementLyricSource.innerText = `Lyrics provided by ${source}`
    document.getElementById('content').scrollTop = 0
}

function loadingLyrics() {
    elementLyricSource.innerText = ''
    elementLyric.innerText = __.trans('LABEL_LOADING')
    infoPlayerProvider.updateLyrics('', __.trans('LABEL_LOADING'), false)
}

function removeAccents(strAccents) {
    // TODO: Remove old code
    /*strAccents = strAccents.split('');
    let strAccentsOut = [];
    let strAccentsLen = strAccents.length;

    const accents =
        'ÀÁÂÃÄÅàáâãäåÒÓÔÕÕÖØòóôõöøÈÉÊËèéêëðÇçÐÌÍÎÏìíîïÙÚÛÜùúûüÑñŠšŸÿýŽž?&=';
    const accentsOut = [
        'A',
        'A',
        'A',
        'A',
        'A',
        'A',
        'a',
        'a',
        'a',
        'a',
        'a',
        'a',
        'O',
        'O',
        'O',
        'O',
        'O',
        'O',
        'O',
        'o',
        'o',
        'o',
        'o',
        'o',
        'o',
        'E',
        'E',
        'E',
        'E',
        'e',
        'e',
        'e',
        'e',
        'e',
        'C',
        'c',
        'D',
        'I',
        'I',
        'I',
        'I',
        'i',
        'i',
        'i',
        'i',
        'U',
        'U',
        'U',
        'U',
        'u',
        'u',
        'u',
        'u',
        'N',
        'n',
        'S',
        's',
        'Y',
        'y',
        'y',
        'Z',
        'z',
        '%3F',
        '%26',
        '%3D',
    ];

    for (let y = 0; y < strAccentsLen; y++)
        if (accents.indexOf(strAccents[y]) !== -1)
            strAccentsOut[y] = accentsOut[accents.indexOf(strAccents[y])];
        else strAccentsOut[y] = strAccents[y];

    strAccentsOut = strAccentsOut.join('');

    return strAccentsOut;*/
    return strAccents.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
}

function retrieveKsoftData(artist, track) {
    return new Promise((resolve, reject) => {
        fetch(
            `https://ytmd-lyrics.herokuapp.com/?q=${removeAccents(
                artist
            )} - ${removeAccents(track)}`,
            { timeout: 3000 }
        )
            .then((res) => res.json())
            .then((json) => {
                if (!json.error) resolve(json.result.lyrics)
                else reject(__.trans('LABEL_LYRICS_NOT_FOUND'))
            })
            .catch((_) => reject(__.trans('LABEL_LYRICS_NOT_FOUND')))
    })
}

function retrieveOVHData(artist, track) {
    return new Promise((resolve, reject) => {
        fetch(
            `https://api.lyrics.ovh/v1/${removeAccents(artist)}/${removeAccents(
                track
            )}`,
            { timeout: 3000 }
        )
            .then((res) => res.json())
            .then((json) => {
                if (json.lyrics) resolve(json.lyrics)
                else reject(__.trans('LABEL_LYRICS_NOT_FOUND'))
            })
            .catch((_) => reject(__.trans('LABEL_LYRICS_NOT_FOUND')))
    })
}

function retrieveVagalumeData(artist, track) {
    return new Promise((resolve, reject) => {
        fetch(
            `https://api.vagalume.com.br/search.php?art=${removeAccents(
                artist
            )}&mus=${removeAccents(track)}`,
            { timeout: 3000 }
        )
            .then((res) => res.json())
            .then((json) => {
                if (json.mus) resolve(json.mus[0].text)
                else reject(__.trans('LABEL_LYRICS_NOT_FOUND'))
            })
            .catch((_) => reject(__.trans('LABEL_LYRICS_NOT_FOUND')))
    })
}

function retrieveGeniusData(artist, track) {
    geniusAuth = settingsProvider.get('genius-auth')

    return new Promise(async (resolve, reject) => {
        //first it will check if Genius is enabled and is authorized.
        if (!settingsProvider.get('settings-genius-auth-server')) {
            reject(__.trans('LABEL_LYRICS_NOT_FOUND'))
        } else if (!(geniusAuth.access_token || geniusAuth.token_type)) {
            reject(__.trans('LABEL_LYRICS_GENIUS_AUTH'))
        } else {
            // Documentation: https://docs.genius.com/#search-h2
            await fetch(
                `https://api.genius.com/search?q=${removeAccents(
                    track
                )} - ${removeAccents(artist)}`,
                {
                    timeout: 3000,
                    headers: {
                        Authorization: `${geniusAuth.token_type} ${geniusAuth.access_token}`,
                    },
                }
            )
                .then((res) => res.json())
                .then((json) => {
                    // Just get the first result, should be good for now?
                    if (json && json.response.hits[0]) {
                        resolve(json.response.hits[0].result)
                    } else reject(__.trans('LABEL_LYRICS_NOT_FOUND'))
                })
                .catch((_) => reject(__.trans('LABEL_LYRICS_NOT_FOUND')))
        }
    })
}


text:
html,
body {
    background-color: #1D1D1D;
    color: #fff;

    /* padding: 5px; */
    padding: 0;

    width: 100%;
    height: 100%;
}

.title {
    text-overflow: ellipsis;
    overflow: hidden;
    width: 93%;
    white-space: nowrap;
}

/* width */
::-webkit-scrollbar {
    width: 6px;
}

#tips {
    position: absolute;
    width: 100%;
    text-align: center;
    color: lightgreen;
}

#content {
    overflow: auto;
    width: 100%;
    height: 98%;
    /*margin-top: 34px; */
    padding: 40% 5px 5px 5px;
    transition: .5s ease-in-out all;
    scroll-behavior: smooth;
}

#lyric {
    font-size: 130%;
    text-align: center;
}

#lyric-source {
    font-size: 80%;
    padding: 0 0 0 6px;
    color: #AAA;
}

.hide {
    display: none;
}

.overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 2;
}


text:
<!DOCTYPE html>

    <meta charset="utf-8" />
    <title>Lyrics</title>
    <link
        type="text/css"
        href="../../../src/assets/css/material_icons.css"
        rel="stylesheet"
    />
    <link
        type="text/css"
        rel="stylesheet"
        href="../../../src/assets/css/styles.css"
    />
    <link
        type="text/css"
        rel="stylesheet"
        href="../../../src/assets/css/materialize.min.css"
        media="screen,projection"
    />
    <link type="text/css" rel="stylesheet" href="./lyrics.css" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />


text:
const { remote, ipcRenderer: ipc, shell } = require('electron')
const settingsProvider = require('../../providers/settingsProvider')
const __ = require('../../providers/translateProvider')
const { isLinux, isMac, isWindows } = require('../../utils/systemInfo')
const fs = require('fs')

/*const elementSettingsCompanionApp = document.getElementById(
    'COMPANION_SERVER_INFO'
)*/
const elementRangeZoom = document.getElementById('range-zoom')
const elementBtnAppRelaunch = document.getElementById('btn-relaunch')
const elementBtnOpenPageEditor = document.getElementById(
    'btn-editor-custom-css-page'
)
const elementBtnLastFmLogin = document.getElementById('btn-last-fm-login')
const elementBtnOpenCompanionServer = document.getElementById(
    'btn-open-companion-server'
)
const elementBtnOpenGeniusAuthServer = document.getElementById(
    'btn-open-genius-auth-server'
)

const elementBtnDiscordSettings = document.getElementById('btn-discord-setting')

const elementBtnShortcutButtonsSettings = document.getElementById(
    'btn-shortcut-buttons-setting'
)

const elementRangeSkipTrackShorterThan = document.getElementById(
    'range-skip-track-shorter-than'
)

if (isLinux())
    document
        .getElementById(
            'i18n_LABEL_SETTINGS_TAB_GENERAL_SELECT_TITLEBAR_TYPE_NICE'
        )
        .remove()

const audioOutputSelect = document.querySelector('#settings-app-audio-output')

let settingsAccelerators = settingsProvider.get('settings-accelerators')

let typeAcceleratorSelected, keyBindings

// FIXME: for some reason, this ipc update_audio_devices_callback could not be triggered
// So we put a button there to allow positive refresh
function update_audio_devices_callback(devices) {
    devices = JSON.parse(devices)
    while (audioOutputSelect.firstChild)
        audioOutputSelect.removeChild(audioOutputSelect.firstChild)
    if (devices.length) {
        devices.forEach((deviceInfo) => {
            let option = document.createElement('option')
            option.text =
                deviceInfo.label != null
                    ? deviceInfo.label
                    : `speaker ${audioOutputSelect.length + 1}`
            option.value = deviceInfo.label
            audioOutputSelect.appendChild(option)
        })

        const defaultOuput = devices.find(
            (audio) => audio.deviceId === 'default'
        )
        if (!audioOutputSelect.value.length)
            audioOutputSelect.value = defaultOuput.label
    } else {
        let option = document.createElement('option')
        option.text = __.trans(
            'LABEL_SETTINGS_TAB_GENERAL_AUDIO_NO_DEVICES_FOUND'
        )
        option.value = '0'
        audioOutputSelect.appendChild(option)
        audioOutputSelect.disabled = true
    }
    mInit()
}

ipc.on('update-audio-output-devices', update_audio_devices_callback)

get_audio_output_list = () =>
    ipc.invoke('get-audio-output-list').then((devices) => {
        devices = JSON.parse(devices)
        while (audioOutputSelect.firstChild)
            audioOutputSelect.removeChild(audioOutputSelect.firstChild)
        if (devices.length) {
            devices.forEach((deviceInfo) => {
                let option = document.createElement('option')
                option.text =
                    deviceInfo.label != null
                        ? deviceInfo.label
                        : `speaker ${audioOutputSelect.length + 1}`
                option.value = deviceInfo.label
                audioOutputSelect.appendChild(option)
            })

            initElement('settings-app-audio-output', 'change', () => {
                ipc.send('change-audio-output', audioOutputSelect.value)
            })

            const defaultOuput = devices.find(
                (audio) => audio.deviceId === 'default'
            )
            if (!audioOutputSelect.value.length)
                audioOutputSelect.value = defaultOuput.label
        } else {
            let option = document.createElement('option')
            option.text = __.trans(
                'LABEL_SETTINGS_TAB_GENERAL_AUDIO_NO_DEVICES_FOUND'
            )
            option.value = '0'
            audioOutputSelect.appendChild(option)
            audioOutputSelect.disabled = true
        }

        mInit()
    })

get_audio_output_list()

function checkCompanionStatus() {
    if (settingsProvider.get('settings-companion-server'))
        document
            .getElementById('companion-server-protect')
            .classList.remove('hide')
    else
        document
            .getElementById('companion-server-protect')
            .classList.add('hide')
}

function checkClipboardWatcherStatus() {
    if (settingsProvider.get('settings-clipboard-read'))
        document
            .getElementById('clipboard-always-ask-read')
            .classList.remove('hide')
    else
        document
            .getElementById('clipboard-always-ask-read')
            .classList.add('hide')
}

function checkWindows10ServiceStatus() {
    if (settingsProvider.get('settings-windows10-media-service'))
        document.getElementById('windows-10-show-info').classList.remove('hide')
    else document.getElementById('windows-10-show-info').classList.add('hide')
}

checkCompanionStatus()
checkClipboardWatcherStatus()
checkWindows10ServiceStatus()

document.addEventListener('DOMContentLoaded', () => {
    initElement('settings-keep-background', 'click', null)
    initElement('settings-show-notifications', 'click', null)
    initElement('settings-start-on-boot', 'click', null)
    initElement('settings-start-minimized', 'click', null)
    initElement('settings-lyrics-always-top', 'click', null)
    initElement('settings-companion-server', 'click', checkCompanionStatus)
    initElement('settings-genius-auth-server', 'click', null)
    initElement('settings-companion-server-protect', 'click', null)
    initElement('settings-windows10-media-service', 'click', () => {
        checkWindows10ServiceStatus()
        showRelaunchButton()
    })
    initElement(
        'settings-windows10-media-service-show-info',
        'click',
        showRelaunchButton
    )
    initElement('settings-shiny-tray', 'click', () => {
        ipc.send('update-tray')
    })
    initElement('settings-shiny-tray-song-title-rollable', 'click', () => {
        ipc.send('update-tray')
    })
    initElement('settings-discord-rich-presence', 'click', showRelaunchButton)
    initElement('settings-app-language', 'change', () => {
        ipc.send('language-updated')
        showRelaunchButton()
    })
    initElement('settings-clipboard-read', 'click', () => {
        ipc.send('switch-clipboard-watcher')
        checkClipboardWatcherStatus()
    })
    initElement('titlebar-type', 'change', showRelaunchButton)
    initElement('settings-custom-css-page', 'click', null)
    initElement('settings-last-fm-scrobbler', 'click', () => {
        const login = settingsProvider.get('last-fm-login')
        if (login.username === '')
            ipc.send('window', { command: 'show-last-fm-login' })
    })
    initElement('settings-rainmeter-web-now-playing', 'click', null)
    initElement('settings-enable-double-tapping-show-hide', 'click', null)
    initElement('settings-volume-media-keys', 'click', () => {
        let enableVolumeMediaKeys = settingsProvider.get(
            'settings-volume-media-keys'
        )
        ipc.send('change-accelerator', {
            type: 'media-volume-up',
            oldValue: enableVolumeMediaKeys ? 'disabled' : 'VolumeUp',
            newValue: enableVolumeMediaKeys ? 'VolumeUp' : 'disabled',
        })
        ipc.send('change-accelerator', {
            type: 'media-volume-down',
            oldValue: enableVolumeMediaKeys ? 'disabled' : 'VolumeDown',
            newValue: enableVolumeMediaKeys ? 'VolumeDown' : 'disabled',
        })
    })
    initElement('settings-decibel-volume', 'click', null)
    initElement(
        'settings-disable-hardware-acceleration',
        'click',
        showRelaunchButton
    )

    initElement('settings-miniplayer-always-top', 'click', null)
    initElement('settings-miniplayer-resizable', 'click', null)
    initElement('settings-miniplayer-show-task', 'click', null)
    initElement('settings-miniplayer-always-show-controls', 'click', null)
    initElement('settings-miniplayer-paint-controls', 'click', null)
    initElement('settings-miniplayer-stream-config', 'click', null)
    initElement('settings-enable-taskbar-progressbar', 'click', () => {
        ipc.send('refresh-progress')
    })
    initElement('settings-enable-player-bgcolor', 'click', () => {
        ipc.send('set-accent-enabled-state')
    })

    // initElement('settings-enable-shortcut-buttons', 'click')

    initElement('settings-continue-where-left-of', 'click', null)
    initElement('settings-skip-track-disliked', 'click', null)

    initElement('settings-clipboard-always-ask-read', 'click', null)
    initElement('settings-tray-icon', 'click', showRelaunchButton)
    initElement('settings-pause-on-suspend', 'click', null)
    initElement('settings-disable-analytics', 'click', showRelaunchButton)
    initElement('settings-surround-sound', 'click', showRelaunchButton)

    mInit()

    document.getElementById('content').classList.remove('hide')
})

/*if (elementSettingsCompanionApp) {
    elementSettingsCompanionApp.addEventListener('click', function() {
        window.open(companionUrl, companionWindowTitle, companionWindowSettings)
    })
}*/

if (elementRangeZoom) {
    elementRangeZoom.addEventListener('input', () => {
        document.getElementById('range-zoom-value').innerText =
            elementRangeZoom.value
        settingsProvider.set('settings-page-zoom', elementRangeZoom.value)
    })
}

if (elementRangeSkipTrackShorterThan)
    elementRangeSkipTrackShorterThan.addEventListener('input', () => {
        document.getElementById(
            'range-skip-track-shorter-than-value'
        ).innerText =
            elementRangeSkipTrackShorterThan.value === 0
                ? `(Disabled) ${elementRangeSkipTrackShorterThan.value}`
                : elementRangeSkipTrackShorterThan.value
        settingsProvider.set(
            'settings-skip-track-shorter-than',
            elementRangeSkipTrackShorterThan.value
        )
    })

if (elementBtnOpenPageEditor)
    elementBtnOpenPageEditor.addEventListener('click', () => {
        ipc.send('window', { command: 'show-editor-theme' })
    })

if (elementBtnLastFmLogin)
    elementBtnLastFmLogin.addEventListener('click', () => {
        ipc.send('window', { command: 'show-last-fm-login' })
    })

if (elementBtnDiscordSettings)
    elementBtnDiscordSettings.addEventListener('click', () => {
        ipc.send('window', { command: 'show-discord-settings' })
    })

if (elementBtnShortcutButtonsSettings)
    elementBtnShortcutButtonsSettings.addEventListener('click', () => {
        ipc.send('window', { command: 'show-shortcut-buttons-settings' })
    })

if (elementBtnOpenCompanionServer)
    elementBtnOpenCompanionServer.addEventListener('click', async () => {
        await shell.openExternal(`http://localhost:9863`)
    })

if (elementBtnOpenGeniusAuthServer)
    elementBtnOpenGeniusAuthServer.addEventListener('click', async () => {
        await shell.openExternal(`http://localhost:9864/login`)
    })

if (elementBtnAppRelaunch)
    elementBtnAppRelaunch.addEventListener('click', () => {
        relaunch()
    })

if (!isMac()) {
    const macSpecificNodes = document.getElementsByClassName('macos-specific')
    for (let i = 0; i < macSpecificNodes.length; i++)
        macSpecificNodes.item(i).classList.add('hide')
}

if (!isWindows()) {
    const windowsSpecificNodes = document.getElementsByClassName(
        'windows-specific'
    )
    for (let i = 0; i < windowsSpecificNodes.length; i++)
        windowsSpecificNodes.item(i).classList.add('hide')
}

if (isWindows()) {
    const os = require('os')
    if (!os.release().startsWith('10.')) {
        const windows10SpecificNodes = document.getElementsByClassName(
            'windows10-specific'
        )
        for (let i = 0; i < windows10SpecificNodes.length; i++)
            windows10SpecificNodes.item(i).classList.add('hide')
    }
}

loadSettings()
__.loadi18n()
loadCustomKeys()

function showRelaunchButton() {
    elementBtnAppRelaunch.classList.remove('hide')
}

/**
 * Initialize element and create listener for it
 * @param {*} elementName
 * @param {*} eventType
 * @param {*} fn
 */
function initElement(elementName, eventType, fn) {
    const element = document.getElementById(elementName)

    if (element) {
        loadValue(element, elementName, eventType)
        createListener(element, elementName, eventType, fn)
    }
}

/**
 *
 * @param {*} element
 * @param {*} settingsName
 * @param {*} eventType
 * @param {*} fn
 */
function createListener(element, settingsName, eventType, fn) {
    element.addEventListener(eventType, (e) => {
        switch (eventType) {
            case 'click':
                settingsProvider.set(settingsName, e.target.checked)
                /*ipc.send('settings-value-changed', {
                    key: settingsName,
                    value: this.checked,
                })*/
                break

            case 'change':
                settingsProvider.set(settingsName, e.target.value)
                /*ipc.send('settings-value-changed', {
                    key: settingsName,
                    value: this.value,
                })*/
                break
        }
        fn && fn()
    })
}

function loadValue(element, settingsName, eventType) {
    switch (eventType) {
        case 'click':
            element.checked = settingsProvider.get(settingsName) || false
            break

        case 'change':
            element.value = settingsProvider.get(settingsName)
            break
    }
}

function loadSettings() {
    // readLocales();

    const settingsZoom = settingsProvider.get('settings-page-zoom')
    if (settingsZoom) {
        document.getElementById('range-zoom').value = settingsZoom
        document.getElementById('range-zoom-value').innerText = settingsZoom
    }

    const settingsSkipTrackShorterThan = settingsProvider.get(
        'settings-skip-track-shorter-than'
    )
    if (settingsSkipTrackShorterThan) {
        document.getElementById(
            'range-skip-track-shorter-than'
        ).value = settingsSkipTrackShorterThan
        document.getElementById(
            'range-skip-track-shorter-than-value'
        ).innerText =
            settingsSkipTrackShorterThan === 0
                ? `(Disabled) ${settingsSkipTrackShorterThan}`
                : settingsSkipTrackShorterThan
    }

    document.getElementById('app-version').innerText = remote.app.getVersion()

    document.getElementById(
        'label-settings-companion-server-token'
    ).innerText = settingsProvider.get('settings-companion-server-token')

    // Disable unsupported platforms which may get an API later
    if (!['darwin', 'win32'].includes(process.platform)) {
        const startOnBootEl = document.getElementById('settings-start-on-boot')
        startOnBootEl.checked = false
        startOnBootEl.setAttribute('disabled', 'disabled')
    }
}

function relaunch() {
    remote.app.relaunch()
    remote.app.exit(0)
}

// TODO: Unused function?
function readLocales() {
    fs.readdir(__dirname, (err, files) => {
        console.log(files)
    })
}

function mInit() {
    M.FormSelect.init(document.querySelectorAll('select'), {})
    M.Tabs.init(document.getElementsByClassName('tabs')[0], {})

    const elems = document.querySelectorAll('.modal')
    M.Modal.init(elems, {})
}

function replaceAcceleratorText(text) {
    text = text.replace(/\+/g, ' + ')

    if (text.indexOf('CmdOrCtrl') !== -1)
        if (isMac()) text = text.replace('CmdOrCtrl', 'Cmd')
        else text = text.replace('CmdOrCtrl', 'Ctrl')

    if (text.indexOf('Meta') !== -1 && isWindows())
        text = text.replace('Meta', 'Windows')

    text = text.replace('numadd', '+')

    text = text.replace('numsub', '-')

    text = text.replace('nummult', '*')

    text = text.replace('numdiv', '/')

    return text
}

function validateKey(e) {
    console.log(e)

    if (e.key === ' ') return 'Space'

    if (e.code === 'NumpadEnter') return 'Enter'

    if (e.code === 'NumpadAdd') return 'numadd'

    if (e.code === 'NumpadSubtract') return 'numsub'

    if (e.code === 'NumpadDecimal') return 'numdec'

    if (e.code === 'NumpadMultiply') return 'nummult'

    if (e.code === 'NumpadDivide') return 'numdiv'

    if (e.code === 'ArrowUp') return 'Up'

    if (e.code === 'ArrowDown') return 'Down'

    if (e.code === 'ArrowLeft') return 'Left'

    if (e.code === 'ArrowRight') return 'Right'

    if (e.keyCode >= 65 && e.keyCode <= 90) return e.key.toUpperCase()

    return e.key
}

function preventSpecialKeys(e) {
    return !(
        e.key === 'Meta' ||
        e.key === 'Command' ||
        e.key === 'Control' ||
        e.key === 'Alt' ||
        e.key === 'Shift' ||
        e.key === 'AltGraph' ||
        e.key === 'MediaPlayPause' ||
        e.key === 'MediaTrackPrevious' ||
        e.key === 'MediaTrackNext' ||
        e.key === 'MediaStop'
    )
}

document
    .querySelector('#modalEditAccelerator')
    .addEventListener('keyup', (e) => {
        if (preventSpecialKeys(e)) {
            keyBindings = ''

            if (e.metaKey) keyBindings += 'Meta+'

            if (e.ctrlKey) keyBindings += 'CmdOrCtrl+'

            if (e.altKey) keyBindings += 'Alt+'

            if (e.shiftKey) keyBindings += 'Shift+'

            keyBindings += validateKey(e)
            document.querySelector(
                '#modalEditAcceleratorKeys'
            ).innerText = replaceAcceleratorText(keyBindings)
        }
    })

function loadCustomKeys() {
    document.querySelector(
        '#settings-accelerators_media-play-pause'
    ).innerText = replaceAcceleratorText(
        settingsAccelerators['media-play-pause']
    )
    document.querySelector(
        '#settings-accelerators_media-track-next'
    ).innerText = replaceAcceleratorText(
        settingsAccelerators['media-track-next']
    )
    document.querySelector(
        '#settings-accelerators_media-track-previous'
    ).innerText = replaceAcceleratorText(
        settingsAccelerators['media-track-previous']
    )
    document.querySelector(
        '#settings-accelerators_media-track-like'
    ).innerText = replaceAcceleratorText(
        settingsAccelerators['media-track-like']
    )
    document.querySelector(
        '#settings-accelerators_media-track-dislike'
    ).innerText = replaceAcceleratorText(
        settingsAccelerators['media-track-dislike']
    )

    document.querySelector(
        '#settings-accelerators_media-volume-up'
    ).innerText = replaceAcceleratorText(
        settingsAccelerators['media-volume-up']
    )
    document.querySelector(
        '#settings-accelerators_media-volume-down'
    ).innerText = replaceAcceleratorText(
        settingsAccelerators['media-volume-down']
    )
    document.querySelector(
        '#settings-accelerators_miniplayer-open-close'
    ).innerText = replaceAcceleratorText(
        settingsAccelerators['miniplayer-open-close']
    )
}

function resetAcceleratorsText() {
    document.querySelector('#modalEditAcceleratorKeys').innerText = `${__.trans(
        'LABEL_SETTINGS_TAB_SHORTCUTS_PRESS_ANY_KEYS'
    )}...`
}

document
    .querySelector('#btn-accelerator-media-play-pause')
    .addEventListener('click', () => {
        typeAcceleratorSelected = 'media-play-pause'
        resetAcceleratorsText()
    })

document
    .querySelector('#btn-accelerator-media-track-next')
    .addEventListener('click', () => {
        typeAcceleratorSelected = 'media-track-next'
        resetAcceleratorsText()
    })

document
    .querySelector('#btn-accelerator-media-track-previous')
    .addEventListener('click', () => {
        typeAcceleratorSelected = 'media-track-previous'
        resetAcceleratorsText()
    })

document
    .querySelector('#btn-accelerator-media-track-like')
    .addEventListener('click', () => {
        typeAcceleratorSelected = 'media-track-like'
        resetAcceleratorsText()
    })

document
    .querySelector('#btn-accelerator-media-track-dislike')
    .addEventListener('click', () => {
        typeAcceleratorSelected = 'media-track-dislike'
        resetAcceleratorsText()
    })

document
    .querySelector('#btn-accelerator-media-volume-up')
    .addEventListener('click', () => {
        typeAcceleratorSelected = 'media-volume-up'
        resetAcceleratorsText()
    })

document
    .querySelector('#btn-accelerator-media-volume-down')
    .addEventListener('click', () => {
        typeAcceleratorSelected = 'media-volume-down'
        resetAcceleratorsText()
    })

document
    .querySelector('#btn-accelerator-miniplayer-open-close')
    .addEventListener('click', () => {
        typeAcceleratorSelected = 'miniplayer-open-close'
        resetAcceleratorsText()
    })

document
    .querySelector('#btn-reload-audio-devices')
    .addEventListener('click', () => {
        get_audio_output_list()
    })

document.querySelector('#saveAccelerator').addEventListener('click', () => {
    ipc.send('change-accelerator', {
        type: typeAcceleratorSelected,
        oldValue: settingsAccelerators[typeAcceleratorSelected],
        newValue: keyBindings,
    })

    settingsAccelerators[typeAcceleratorSelected] = keyBindings

    settingsProvider.set('settings-accelerators', settingsAccelerators)

    loadCustomKeys()
})

document.querySelector('#disableAccelerator').addEventListener('click', () => {
    ipc.send('change-accelerator', {
        type: typeAcceleratorSelected,
        oldValue: settingsAccelerators[typeAcceleratorSelected],
        newValue: 'disabled',
    })

    settingsAccelerators[typeAcceleratorSelected] = 'disabled'

    settingsProvider.set('settings-accelerators', settingsAccelerators)

    loadCustomKeys()
})

document.querySelector('#release-notes').addEventListener('click', () => {
    ipc.send('window', { command: 'show-changelog' })
})

document.querySelectorAll('[externalURL]').forEach((element) => {
    var externalURL = element.getAttribute('externalURL')
    element.addEventListener('click', () => {
        shell.openExternal(externalURL)
    })
})


text:
<!DOCTYPE html>

        <meta charset="UTF-8" />
        <title>Settings</title>
        <link
            type="text/css"
            rel="stylesheet"
            href="../../../src/assets/css/materialize.min.css"
            media="screen,projection"
        />
        <link
            href="../../../src/assets/css/material_icons.css"
            rel="stylesheet"
        />
        <link
            type="text/css"
            rel="stylesheet"
            href="../../../src/assets/css/styles.css"
        />
        <link type="text/css" rel="stylesheet" href="./settings.css" />

                                    dashboard
                                    blur_on

                                    personal_video

                                    music_note

                                    picture_in_picture_alt

                                    wifi_tethering
                                    linear_scale

                                    keyboard

                                    info_outline

                                                    <input
                                                        type="checkbox"
                                                        id="settings-keep-background"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-tray-icon"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-show-notifications"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-start-on-boot"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-start-minimized"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-lyrics-always-top"
                                                    />

                                                autorenew

                                                    <input
                                                        type="checkbox"
                                                        id="settings-disable-hardware-acceleration"
                                                    />

                                                autorenew

                                            <select id="settings-app-language">
                                                <option value="id">
                                                    Bahasa Indonesia
                                                </option>
                                                <option value="de">
                                                    Deutsch
                                                </option>
                                                <option value="en">
                                                    English
                                                </option>
                                                <option value="es">
                                                    Español
                                                </option>
                                                <option value="ca">
                                                    Català
                                                </option>
                                                <option value="fr">
                                                    Français
                                                </option>
                                                <option value="it">
                                                    Italiano
                                                </option>
                                                <option value="nl">
                                                    Nederlands
                                                </option>
                                                <option value="no">
                                                    Norsk
                                                </option>
                                                <option value="pl">
                                                    Polski
                                                </option>
                                                <option value="pt">
                                                    Português
                                                </option>
                                                <option value="th">ไทย</option>
                                                <option value="tr">
                                                    Türkçe
                                                </option>
                                                <option value="ru">
                                                    Русский
                                                </option>
                                                <option value="uk">
                                                    Українська
                                                </option>
                                                <option value="ar">
                                                    العربية
                                                </option>
                                                <option value="ja">
                                                    日本語
                                                </option>
                                                <option value="zh">
                                                    简体中文
                                                </option>
                                                <option value="zh_TW">
                                                    正體中文
                                                </option>
                                                <option value="ko">
                                                    한국어
                                                </option>
                                                <option value="kz">
                                                    Қазақша
                                                </option>
                                                <option value="cs">
                                                    Čeština
                                                </option>
                                                <option value="hu">
                                                    Magyar
                                                </option>
                                            </select>

                                                <select
                                                    id="settings-app-audio-output"
                                                ></select>
                                                <button
                                                    class="waves-effect waves-light btn-flat white-text btn-tiny"
                                                    id="btn-reload-audio-devices"
                                                >
                                                    autorenew
                                                </button>

                                                    <input
                                                        type="checkbox"
                                                        id="settings-shiny-tray"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-shiny-tray-song-title-rollable"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-continue-where-left-of"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-enable-taskbar-progressbar"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-skip-track-disliked"
                                                    />

                                            0s

                                                <input
                                                    id="range-skip-track-shorter-than"
                                                    type="range"
                                                    min="0"
                                                    max="120"
                                                />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-decibel-volume"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-pause-on-suspend"
                                                    />

                                                autorenew

                                                    <input
                                                        type="checkbox"
                                                        id="settings-surround-sound"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-miniplayer-always-top"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-miniplayer-always-show-controls"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-miniplayer-paint-controls"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-miniplayer-resizable"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-miniplayer-show-task"
                                                    />

                                                info

                                                    <input
                                                        type="checkbox"
                                                        id="settings-miniplayer-stream-config"
                                                    />

                                            100%

                                                <input
                                                    id="range-zoom"
                                                    type="range"
                                                    min="50"
                                                    max="150"
                                                    step="10"
                                                />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-enable-player-bgcolor"
                                                    />

                                            <select id="titlebar-type">
                                                <option
                                                    id="i18n_LABEL_SETTINGS_TAB_GENERAL_SELECT_TITLEBAR_TYPE_NICE"
                                                    value="nice"
                                                    i18n="i18n_LABEL_SETTINGS_TAB_GENERAL_SELECT_TITLEBAR_TYPE_NICE"
                                                ></option>
                                                <option
                                                    id="i18n_LABEL_SETTINGS_TAB_GENERAL_SELECT_TITLEBAR_TYPE_SYSTEM"
                                                    value="system"
                                                    i18n="i18n_LABEL_SETTINGS_TAB_GENERAL_SELECT_TITLEBAR_TYPE_SYSTEM"
                                                ></option>
                                                <option
                                                    id="i18n_LABEL_SETTINGS_TAB_GENERAL_SELECT_TITLEBAR_TYPE_NONE"
                                                    value="none"
                                                    i18n="i18n_LABEL_SETTINGS_TAB_GENERAL_SELECT_TITLEBAR_TYPE_NONE"
                                                ></option>
                                            </select>

                                                <button
                                                    class="waves-effect waves-light btn-flat white-text btn-tiny"
                                                    id="btn-editor-custom-css-page"
                                                >
                                                    edit
                                                </button>

                                                    <input
                                                        type="checkbox"
                                                        id="settings-custom-css-page"
                                                    />

                                                <button
                                                    class="waves-effect waves-light btn-flat white-text btn-tiny"
                                                    id="btn-shortcut-buttons-setting"
                                                >
                                                    settings
                                                </button>

                                            <button
                                                class="waves-effect waves-light btn-flat white-text btn-tiny"
                                                id="btn-open-companion-server"
                                            >
                                                open_in_new
                                            </button>

                                                    <input
                                                        type="checkbox"
                                                        id="settings-companion-server"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-companion-server-protect"
                                                    />

                                                autorenew

                                                <button
                                                    class="waves-effect waves-light btn-flat white-text btn-tiny"
                                                    id="btn-discord-setting"
                                                >
                                                    settings
                                                </button>

                                                    <input
                                                        type="checkbox"
                                                        id="settings-discord-rich-presence"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-clipboard-read"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-clipboard-always-ask-read"
                                                    />

                                                <button
                                                    class="waves-effect waves-light btn-flat white-text btn-tiny"
                                                    id="btn-last-fm-login"
                                                >
                                                    settings
                                                </button>

                                                    <input
                                                        type="checkbox"
                                                        id="settings-last-fm-scrobbler"
                                                    />

                                                    <input
                                                        type="checkbox"
                                                        id="settings-rainmeter-web-now-playing"
                                                    />

                                                autorenew

                                                    <input
                                                        type="checkbox"
                                                        id="settings-windows10-media-service"
                                                    />

                                                autorenew

                                                    <input
                                                        type="checkbox"
                                                        id="settings-windows10-media-service-show-info"
                                                    />

                                            <button
                                                class="waves-effect waves-light btn-flat white-text btn-tiny"
                                                id="btn-open-genius-auth-server"
                                            >
                                                open_in_new
                                            </button>

                                                    <input
                                                        type="checkbox"
                                                        id="settings-genius-auth-server"
                                                    />

                                                    <button
                                                        id="btn-accelerator-media-play-pause"
                                                        data-target="modalEditAccelerator"
                                                        class="modal-trigger waves-effect waves-light btn-small text-initial black-text white"
                                                    >

                                                        edit
                                                    </button>

                                                        play_arrow/pause

                                                    <button
                                                        id="btn-accelerator-media-track-next"
                                                        data-target="modalEditAccelerator"
                                                        class="modal-trigger waves-effect waves-light btn-small text-initial black-text white"
                                                    >

                                                        edit
                                                    </button>

                                                        skip_next

                                                    <button
                                                        id="btn-accelerator-media-track-previous"
                                                        data-target="modalEditAccelerator"
                                                        class="modal-trigger waves-effect waves-light btn-small text-initial black-text white"
                                                    >

                                                        edit
                                                    </button>

                                                        skip_previous

                                                    <button
                                                        id="btn-accelerator-media-track-like"
                                                        data-target="modalEditAccelerator"
                                                        class="modal-trigger waves-effect waves-light btn-small text-initial black-text white"
                                                    >

                                                        edit
                                                    </button>

                                                    <button
                                                        id="btn-accelerator-media-track-dislike"
                                                        data-target="modalEditAccelerator"
                                                        class="modal-trigger waves-effect waves-light btn-small text-initial black-text white"
                                                    >

                                                        edit
                                                    </button>

                                                    <button
                                                        id="btn-accelerator-media-volume-up"
                                                        data-target="modalEditAccelerator"
                                                        class="modal-trigger waves-effect waves-light btn-small text-initial black-text white"
                                                    >

                                                        edit
                                                    </button>

                                                    <button
                                                        id="btn-accelerator-media-volume-down"
                                                        data-target="modalEditAccelerator"
                                                        class="modal-trigger waves-effect waves-light btn-small text-initial black-text white"
                                                    >

                                                        edit
                                                    </button>

                                                            <input
                                                                type="checkbox"
                                                                id="settings-volume-media-keys"
                                                            />

                                                    <button
                                                        id="btn-accelerator-miniplayer-open-close"
                                                        data-target="modalEditAccelerator"
                                                        class="modal-trigger waves-effect waves-light btn-small text-initial black-text white"
                                                    >

                                                        edit
                                                    </button>

                                                    2x

                                                            <input
                                                                type="checkbox"
                                                                id="settings-enable-double-tapping-show-hide"
                                                            />

                                    v
Commit hash:

                                    <button
                                        id="release-notes"
                                        class="btn waves-effect waves-light red"
                                    >

                                    </button>

                                Made with
                                ❤ by
                                adlerluiz & contributors

                                        https://ytmdesktop.app

                                        View on Github

                                        autorenew

                                        <input
                                            type="checkbox"
                                            id="settings-disable-analytics"
                                        />

            <button
                class="btn red hide"
                id="btn-relaunch"
                style="text-transform: none"
            >
                autorenew
            </button>


text:
body {
    background-color: #1D1D1D;
}

label {
    color: #fff !important;
}

tbody,
td,
th {
    padding: 10px 5px;
}

ul,
li {
    background-color: transparent !important;
}

ul,
li,
a {
    color: #eee !important;
}

.tabs .tab a:focus,
.tabs .tab a:focus.active {
    background-color: rgba(255, 255, 255, 0.1);
}

.indicator {
    background-color: #f44336 !important;
}

.switch label input[type="checkbox"]:checked+.lever {
    background-color: #d32f2f;
}

.switch label input[type="checkbox"]:checked+.lever:after {
    background-color: #f44336;
}

.dropdown-trigger {
    color: #f44336 !important;
    height: 26px !important;
    margin: 0 !important;
    padding: 0 !important;
}

.dropdown-content li>a,
.dropdown-content li>span {
    color: #fff !important;
}

:disabled {
    color: #666 !important;
    border: none !important;
}

.select-dropdown.dropdown-content {
    background-color: black !important;
    max-height: 300px !important;
}

.select-dropdown.dropdown-content li.selected {
    background-color: #f44336 !important;
}

.select-dropdown.dropdown-content li:hover {
    background-color: #f44336 !important;
    color: #fff !important;
}

.select-dropdown.dropdown-content li.selected span {
    color: #fff !important;
}

.select-dropdown.dropdown-content li.selected span:hover {
    color: #fff !important;
}

input[type="range"] {
    border: 1px solid #d32f2f;
}

input[type="range"]::-webkit-slider-thumb {
    background-color: #f44336;
}

input[type="range"]::-moz-range-thumb {
    background-color: #f44336;
}

input[type="range"]::-ms-thumb {
    background-color: #f44336;
}

input[type="range"]+.thumb {
    background-color: #fff;
}

input[type="range"]+.thumb.active .value {
    color: #f44336;
}

input[type="range"]::-webkit-slider-runnable-track {
    background: #d32f2f;
}

.select-wrapper input.select-dropdown {
    text-align: center;
    border: none;
}

.select-wrapper input.select-dropdown:focus {
    border: none;
}

.select-wrapper input.select-dropdown {
    text-align: right;
    right: 14px;
}

.caret {
    display: none;
}

.hide {
    display: none;
}

.divider {
    height: 1px;
    border-top: 1px solid #ddd;
    text-align: center;
}

.media-icons {
    vertical-align: sub;
    font-size: 16px;
}

.tabs-vertical .tabs {
    height: auto;
    -ms-flex-direction: column;
    -webkit-flex-direction: column;
    flex-direction: column;
}

.tabs-vertical .tab {
    width: 100%;
    -webkit-box-flex: 1;
    -webkit-flex-grow: 1;
    -ms-flex-positive: 1;
    flex-grow: 1;
    display: block;
    float: left;
    text-align: left;
    line-height: 48px;
    height: 48px;
    padding: 0;
    margin: 0;
    text-transform: capitalize;
    text-overflow: ellipsis;
}

.tabs-vertical .tab .active {
    -moz-transition: border-color .5s ease;
    -o-transition: border-color .5s ease;
    -webkit-transition: border-color .5s ease;
    transition: border-color .5s ease;
    border-left: 3px solid #d32f2f;
    color: #FFF !important;
    background-color: rgba(255,255,255,0.1) !important;
}

.tabs-vertical .tab a {
    border-left: 3px solid transparent;
    /*color: #3f51b5;*/
    color: #CCC !important;
    display: block;
    width: 100%;
    height: 100%;
    text-overflow: ellipsis;
    overflow: hidden;
    -webkit-transition: color 0.28s ease;
    -moz-transition: color 0.28s ease;
    -o-transition: color 0.28s ease;
    -ms-transition: color 0.28s ease;
    transition: color 0.28s ease;
}

.tabs-vertical .tab a:hover {
    color: #FFF !important;
}

.tabs-vertical .tab.disabled a {
    color: #8591d5;
    cursor: default;
}

.tabs-vertical .indicator {
    display: none;
}

.mt-1 {
    margin-top: 0.7rem;
}

.mt-2 {
    margin-top: 0.9rem;
}

.bottom {
    position: absolute;
    padding: 5px;
    bottom: 0;
    width: 100%;
    background-color: #1D1D1D;
    text-transform: none;
}

.dropdown-trigger {
    min-width: 300px;
    font-size: 13px !important;
}

.btn-tiny {
    padding: 0 2px;
    height: auto;
    line-height: normal;
}

input[type=range]+.thumb {
    display: none;
}

.settings-item-child::before {
    color: #666;
    content: '└ ';
}

.text-disabled {
    text-decoration: line-through;
    text-transform: capitalize;
}

.shortcut-text {
    color: #888;
    text-transform: lowercase;
    padding: 0 7px;
}

.btn-normalize {
    min-width: 70px;
}

.icon-normalize {
    margin-top: 12px;
}

.tooltip {
    position: relative;
    display: inline-block;
    line-height: 15px;
    font-size: 14px;
}

.tooltip .tooltiptext {
    position: absolute;
    width: 200px;
    margin-left: 5px;
    visibility: hidden;
    background-color: dimgray;
    color: black;
    fill: dimgray;
    border-radius: 10px;
    padding: 5px 5px;
}

.tooltip:hover .tooltiptext {
    visibility: visible;
}


text:
const settingsProvider = require('../../../../providers/settingsProvider')
const __ = require('../../../../providers/translateProvider')

var shortcutSettings = null

__.loadi18n()

function loadSettings() {
    shortcutSettings = settingsProvider.get('settings-shortcut-buttons')
    document.getElementById('settings-shortcut-show-miniplayer').checked =
        shortcutSettings.miniplayer
    document.getElementById('settings-shortcut-show-lyrics').checked =
        shortcutSettings.lyrics
    document.getElementById('settings-shortcut-show-add-to-library').checked =
        shortcutSettings['add-to-library']
    document.getElementById('settings-shortcut-show-add-to-playlist').checked =
        shortcutSettings['add-to-playlist']
}

function save() {
    settingsProvider.set('settings-shortcut-buttons', {
        miniplayer: document.getElementById('settings-shortcut-show-miniplayer')
            .checked,
        lyrics: document.getElementById('settings-shortcut-show-lyrics')
            .checked,
        'add-to-library': document.getElementById(
            'settings-shortcut-show-add-to-library'
        ).checked,
        'add-to-playlist': document.getElementById(
            'settings-shortcut-show-add-to-playlist'
        ).checked,
    })
}

document.addEventListener('DOMContentLoaded', function () {
    loadSettings()
})

document.querySelectorAll('input').forEach((input) => {
    input.addEventListener('click', () => save())
})


text:
<!DOCTYPE html>

        <meta charset="UTF-8" />
        <title>Shortcut buttons</title>
        <link
            href="../../../../../src/assets/css/materialize.min.css"
            media="screen,projection"
            rel="stylesheet"
            type="text/css"
        />
        <link
            href="../../../../../src/assets/css/material_icons.css"
            rel="stylesheet"
        />
        <link
            href="../../../../../src/assets/css/styles.css"
            rel="stylesheet"
            type="text/css"
        />
        <link
            href="./shortcut-buttons-settings.css"
            rel="stylesheet"
            type="text/css"
        />

                                                    <input
                                                        id="settings-shortcut-show-miniplayer"
                                                        type="checkbox"
                                                    />

                                                    <input
                                                        id="settings-shortcut-show-lyrics"
                                                        type="checkbox"
                                                    />

                                                    <input
                                                        id="settings-shortcut-show-add-to-library"
                                                        type="checkbox"
                                                    />

                                                    <input
                                                        id="settings-shortcut-show-add-to-playlist"
                                                        type="checkbox"
                                                    />


text:
body {
    background-color: #1D1D1D;
}

label {
    color: #fff !important;
}

tbody,
td,
th {
    padding: 10px 5px;
}

ul,
li {
    background-color: transparent !important;
}

ul,
li,
a {
    color: #eee !important;
}

.tabs .tab a:focus,
.tabs .tab a:focus.active {
    background-color: rgba(255, 255, 255, 0.1);
}

.indicator {
    background-color: #f44336 !important;
}

.switch label input[type="checkbox"]:checked+.lever {
    background-color: #d32f2f;
}

.switch label input[type="checkbox"]:checked+.lever:after {
    background-color: #f44336;
}

.dropdown-trigger {
    color: #f44336 !important;
    height: 26px !important;
    margin: 0 !important;
    padding: 0 !important;
}

.dropdown-content li>a,
.dropdown-content li>span {
    color: #fff !important;
}

:disabled {
    color: #666 !important;
    border: none !important;
}

.select-dropdown.dropdown-content {
    background-color: black !important;
    max-height: 300px !important;
}

.select-dropdown.dropdown-content li.selected {
    background-color: #f44336 !important;
}

.select-dropdown.dropdown-content li:hover {
    background-color: #f44336 !important;
    color: #fff !important;
}

.select-dropdown.dropdown-content li.selected span {
    color: #fff !important;
}

.select-dropdown.dropdown-content li.selected span:hover {
    color: #fff !important;
}

input[type="range"] {
    border: 1px solid #d32f2f;
}

input[type="range"]::-webkit-slider-thumb {
    background-color: #f44336;
}

input[type="range"]::-moz-range-thumb {
    background-color: #f44336;
}

input[type="range"]::-ms-thumb {
    background-color: #f44336;
}

input[type="range"]+.thumb {
    background-color: #fff;
}

input[type="range"]+.thumb.active .value {
    color: #f44336;
}

input[type="range"]::-webkit-slider-runnable-track {
    background: #d32f2f;
}

.select-wrapper input.select-dropdown {
    text-align: center;
    border: none;
}

.select-wrapper input.select-dropdown:focus {
    border: none;
}

.select-wrapper input.select-dropdown {
    text-align: right;
    right: 14px;
}

.caret {
    display: none;
}

.hide {
    display: none;
}

.divider {
    height: 1px;
    border-top: 1px solid #ddd;
    text-align: center;
}

.media-icons {
    vertical-align: sub;
    font-size: 16px;
}

.tabs-vertical .tabs {
    height: auto;
    -ms-flex-direction: column;
    -webkit-flex-direction: column;
    flex-direction: column;
}

.tabs-vertical .tab {
    width: 100%;
    -webkit-box-flex: 1;
    -webkit-flex-grow: 1;
    -ms-flex-positive: 1;
    flex-grow: 1;
    display: block;
    float: left;
    text-align: left;
    line-height: 48px;
    height: 48px;
    padding: 0;
    margin: 0;
    text-transform: capitalize;
    text-overflow: ellipsis;
}

.tabs-vertical .tab .active {
    -moz-transition: border-color .5s ease;
    -o-transition: border-color .5s ease;
    -webkit-transition: border-color .5s ease;
    transition: border-color .5s ease;
    border-left: 3px solid #d32f2f;
    color: #3f51b5;
    background-color: rgba(255,255,255,0.1) !important;
}

.tabs-vertical .tab a {
    border-left: 3px solid transparent;
    color: #3f51b5;
    display: block;
    width: 100%;
    height: 100%;
    text-overflow: ellipsis;
    overflow: hidden;
    -webkit-transition: color 0.28s ease;
    -moz-transition: color 0.28s ease;
    -o-transition: color 0.28s ease;
    -ms-transition: color 0.28s ease;
    transition: color 0.28s ease;
}

.tabs-vertical .tab a:hover {
    color: #8591d5;
}

.tabs-vertical .tab.disabled a {
    color: #8591d5;
    cursor: default;
}

.tabs-vertical .indicator {
    display: none;
}

.mt-1 {
    margin-top: 0.7rem;
}

.mt-2 {
    margin-top: 0.9rem;
}

.bottom {
    position: absolute;
    bottom: 16px;
    right: 12px
}

.dropdown-trigger {
    min-width: 300px;
    font-size: 13px !important;
}

.btn-tiny {
    padding: 0 2px;
    height: auto;
    line-height: normal;
}

input[type=range]+.thumb {
    display: none;
}


text:
const __ = require('../../../../providers/translateProvider')
const scrobbleProvider = require('../../../../providers/scrobblerProvider')
__.loadi18n()

var login = scrobbleProvider.getLogin()

if (login) {
    document.getElementById('username').value = login.username
    document.getElementById('password').value = login.password
}

document.getElementById('btn-save').addEventListener('click', function () {
    var username = document.getElementById('username').value
    var password = document.getElementById('password').value
    scrobbleProvider.setLogin(username, password)

    scrobbleProvider.getToken()
})


text:
        <link
            type="text/css"
            rel="stylesheet"
            href="../../../../../src/assets/css/materialize.min.css"
            media="screen,projection"
        />
        <link
            href="../../../../../src/assets/css/material_icons.css"
            rel="stylesheet"
        />
        <link
            type="text/css"
            rel="stylesheet"
            href="../../../../../src/assets/css/styles.css"
        />

                    <input id="username" type="text" class="white-text" />
                    Username

                    <input id="password" type="password" class="white-text" />
                    Password

                <button
                    class="btn red"
                    id="btn-save"
                    style="text-transform: none;"
                >

                </button>


text:
<!DOCTYPE html>

        <meta charset="UTF-8" />
        <title>Discord Settings</title>
        <link
            href="../../../../../src/assets/css/materialize.min.css"
            media="screen,projection"
            rel="stylesheet"
            type="text/css"
        />
        <link
            href="../../../../../src/assets/css/material_icons.css"
            rel="stylesheet"
        />
        <link
            href="../../../../../src/assets/css/styles.css"
            rel="stylesheet"
            type="text/css"
        />
        <link href="./discord_settings.css" rel="stylesheet" type="text/css" />

                                                    <input
                                                        id="settings-discord-show-title"
                                                        type="checkbox"
                                                    />

                                                    <input
                                                        id="settings-discord-show-artist"
                                                        type="checkbox"
                                                    />

                                                    <input
                                                        id="settings-discord-show-time"
                                                        type="checkbox"
                                                    />

                                                    <input
                                                        id="settings-discord-show-idle"
                                                        type="checkbox"
                                                    />


text:
const settingsProvider = require('../../../../providers/settingsProvider')
const __ = require('../../../../providers/translateProvider')

//var, because we have to re-assign the variable
var discordSettings = null

__.loadi18n()

function loadSettings() {
    discordSettings = settingsProvider.get('discord-presence-settings')
    document.getElementById('settings-discord-show-title').checked =
        discordSettings.details
    document.getElementById('settings-discord-show-artist').checked =
        discordSettings.state
    document.getElementById('settings-discord-show-time').checked =
        discordSettings.time
    document.getElementById('settings-discord-show-idle').checked =
        discordSettings.hideIdle
}

function save() {
    settingsProvider.set('discord-presence-settings', {
        details: document.getElementById('settings-discord-show-title').checked,
        state: document.getElementById('settings-discord-show-artist').checked,
        time: document.getElementById('settings-discord-show-time').checked,
        hideIdle: document.getElementById('settings-discord-show-idle').checked,
    })
}

document.addEventListener('DOMContentLoaded', function () {
    loadSettings()
})

document.querySelectorAll('input').forEach((input) => {
    input.addEventListener('click', () => save())
})


text:
const { app } = require('electron')
const path = require('path')

const systemInfo = require('../utils/systemInfo')

/**
 * Get local asset
 * @param {*} asset
 */
function getLocal(asset) {
    return path.join(app.getAppPath(), 'src/assets', `${asset}`)
}

function getIcon(assetPath) {
    let localAsset = path.join(app.getAppPath(), 'src/assets', `${assetPath}`)

    return systemInfo.isWindows() ? `${localAsset}.ico` : `${localAsset}.png`
}

function getExternal() {}

module.exports = {
    getLocal: getLocal,
    getExternal: getExternal,
    getIcon: getIcon,
}


text:
const { ipcMain, webContents } = require('electron')
const electronStore = require('electron-store')
const store = new electronStore({ watch: true })

/**
 * Get setting value
 * @param {string} settingName
 */
function get(settingName) {
    return store.get(settingName)
}

/**
 * Set setting value
 * @param {string} settingName
 * @param {*} value
 */
function set(settingName, value) {
    store.set(settingName, value)
}

/**
 * Set initial value for setting if it not setted
 * @param {string} settingName
 * @param {*} initialValue
 */
function setInitialValue(settingName, initialValue) {
    if (!store.has(settingName)) set(settingName, initialValue)
}

function onDidChange(key, callback) {
    return store.onDidChange(key, (newValue, oldValue) =>
        callback({ newValue, oldValue })
    )
}

function proxyCallbackToSender(id, key) {
    return ({ newValue, oldValue }) => {
        const sender = webContents.fromId(id)
        if (!sender) return
        sender.send(`SETTINGS_NOTIFY_${key}`, newValue, oldValue)
    }
}

// In the browser process
if (ipcMain) {
    ipcMain.on('SETTINGS_GET', (e, settingName) => {
        e.returnValue = get(settingName)
    })

    const subscriptions = new Map()

    // TODO: Support unsubscribing over IPC, would need to decrement counter
    // no use case yet so left unimplemented
    ipcMain.on('SETTINGS_SUBSCRIBE', (e, settingName) => {
        let existingSubs = subscriptions.get(e.sender.id)
        if (!existingSubs) {
            existingSubs = {}
            subscriptions.set(e.sender.id, existingSubs)
        }
        if (existingSubs[settingName]) existingSubs[settingName]++
        else {
            existingSubs[settingName] = 1
            const unsub = onDidChange(
                settingName,
                proxyCallbackToSender(e.sender.id, settingName)
            )
            e.sender.on('destroyed', unsub)
        }
    })
}

module.exports = {
    onDidChange: onDidChange,
    get: get,
    set: set,
    setInitialValue: setInitialValue,
}