Merge pull request 'main' (#5) from RemixDev/deemix-webui:main into main

Reviewed-on: https://codeberg.org/m3troux/deemix-webui/pulls/5
This commit is contained in:
m3troux 2020-08-12 18:19:51 +02:00
commit 9a5168d994
18 changed files with 306 additions and 215 deletions

View File

@ -4,36 +4,34 @@ This is just the WebUI for deemix, it should be used with deemix-pyweb or someth
## What's left to do? ## What's left to do?
- Use Vue as much as possible - [ ] Use Vue app-wide
- First step: rewrite the app in Single File Components way ✅ - First step: rewrite the app in Single File Components way ✅
- Second step: Implement custom contextmenu ⚒ - Second step: Implement routing for the whole app using Vue Router ⚒
- Implemented a first version - Third step: Remove jQuery
- Need heavy testing and more features - [ ] Implement custom contextmenu ⚒
- Third step: Implement routing for the whole app using Vue Router - Copy and paste functions ✅
- Fourth step: Remove jQuery - Copy Link where possible ✅
- Make i18n async (https://kazupon.github.io/vue-i18n/guide/lazy-loading.html) - Download Quality ✅
- Copy Image URL where possible ✅
- Resolve problem when positioning out of window (e.g. clicking on the bottom of the window)
- [ ] Make i18n async (https://kazupon.github.io/vue-i18n/guide/lazy-loading.html)
- Use ES2020 async imports, if possible - Use ES2020 async imports, if possible
- Make the UI look coherent - [ ] Make the UI look coherent
- Style buttons - Style buttons
- Style text inputs - Style text inputs
- Style checkboxes - Style checkboxes
- Search tab - [ ] Search tab
- Better placeholer before search - Better placeholer before search
- Link Analyzer - [ ] Link Analyzer
- Better placeholer before analyzing and error feedback - Better placeholer before analyzing and error feedback
- Settings tab - [ ] Settings tab
- Maybe tabbing the section for easy navigation - Maybe tabbing the section for easy navigation
- Could use a carousel, but it's not worth adding a new dep - Could use a carousel, but it's not worth adding a new dep
- Variable selector near template inputs - Variable selector near template inputs
- Add Custom Context menu to objects - [ ] Block selection where it's not needed (keep only titles artists albums labels and useful data)
- Copy Link where possible
- Copy Image URL where possible
- Context menu for pywebview (Context menu is blocked in pywebview)
- Copy and paste functions
- Block selection where it's not needed (keep only titles artists albums labels and useful data)
- There's a SASS mixin for this. Need to use it in the proper classes - There's a SASS mixin for this. Need to use it in the proper classes
- Better feedback for socket.io possible errors - [ ] Better feedback for socket.io possible errors
- Remove images size limit and add warning if > 1200 - [ ] Remove images size limit and add warning if > 1200
- ? - ?
# License # License

File diff suppressed because one or more lines are too long

View File

@ -33,6 +33,7 @@ function initClient() {
window.clientMode = true window.clientMode = true
document.querySelector(`#open_downloads_folder`).classList.remove('hide') document.querySelector(`#open_downloads_folder`).classList.remove('hide')
document.querySelector(`#select_downloads_folder`).classList.remove('hide') document.querySelector(`#select_downloads_folder`).classList.remove('hide')
document.querySelector(`#settings_btn_applogin`).classList.remove('hide')
} }
document.addEventListener('DOMContentLoaded', startApp) document.addEventListener('DOMContentLoaded', startApp)

View File

@ -8,14 +8,7 @@
}" }"
> >
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
<div <div role="button" aria-label="download" @click.stop="addToQueue" :data-link="link" class="fab right">
role="button"
aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue"
:data-link="link"
class="fab right"
>
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i> <i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
</div> </div>
</header> </header>
@ -64,18 +57,13 @@
explicit explicit
</i> </i>
{{ release.title }} {{ release.title }}
<i v-if="checkNewRelease(release.release_date)" class="material-icons" style="color:#FF7300;"> <i v-if="checkNewRelease(release.release_date)" class="material-icons" style="color: #ff7300;">
fiber_new fiber_new
</i> </i>
</td> </td>
<td>{{ release.release_date }}</td> <td>{{ release.release_date }}</td>
<td>{{ release.nb_song }}</td> <td>{{ release.nb_song }}</td>
<td <td @click.stop="addToQueue" :data-link="release.link" class="clickable">
@click.stop="addToQueue"
@contextmenu.prevent="openQualityModal"
:data-link="release.link"
class="clickable"
>
<i class="material-icons" :title="$t('globals.download_hint')"> <i class="material-icons" :title="$t('globals.download_hint')">
file_download file_download
</i> </i>
@ -129,9 +117,6 @@ export default {
e.stopPropagation() e.stopPropagation()
Downloads.sendAddToQueue(e.currentTarget.dataset.link) Downloads.sendAddToQueue(e.currentTarget.dataset.link)
}, },
openQualityModal(e) {
this.$root.$emit('QualityModal:open', e.currentTarget.dataset.link)
},
sortBy(key) { sortBy(key) {
if (key == this.sortKey) { if (key == this.sortKey) {
this.sortOrder = this.sortOrder == 'asc' ? 'desc' : 'asc' this.sortOrder = this.sortOrder == 'asc' ? 'desc' : 'asc'
@ -184,11 +169,15 @@ export default {
showTable() { showTable() {
if (this.body) { if (this.body) {
if (this.sortKey == 'nb_song') if (this.sortKey == 'nb_song')
return orderBy(this.body[this.currentTab], function (o) { return new Number(o.nb_song); }, this.sortOrder) return orderBy(
else this.body[this.currentTab],
return orderBy(this.body[this.currentTab], this.sortKey, this.sortOrder) function (o) {
} return new Number(o.nb_song)
else return [] },
this.sortOrder
)
else return orderBy(this.body[this.currentTab], this.sortKey, this.sortOrder)
} else return []
} }
}, },
mounted() { mounted() {

View File

@ -36,11 +36,7 @@
</div> </div>
<div v-else id="charts_table"> <div v-else id="charts_table">
<button @click="changeCountry">{{ $t('charts.changeCountry') }}</button> <button @click="changeCountry">{{ $t('charts.changeCountry') }}</button>
<button <button @click.stop="addToQueue" :data-link="'https://www.deezer.com/playlist/' + id">
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue"
:data-link="'https://www.deezer.com/playlist/' + id"
>
{{ $t('charts.download') }} {{ $t('charts.download') }}
</button> </button>
<table class="table table--charts"> <table class="table table--charts">
@ -72,9 +68,7 @@
<td class="table__cell--large breakline"> <td class="table__cell--large breakline">
{{ {{
track.title + track.title +
(track.title_version && track.title.indexOf(track.title_version) == -1 (track.title_version && track.title.indexOf(track.title_version) == -1 ? ' ' + track.title_version : '')
? ' ' + track.title_version
: '')
}} }}
</td> </td>
<td <td
@ -96,7 +90,6 @@
</td> </td>
<td <td
class="table__cell--download" class="table__cell--download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="track.link" :data-link="track.link"
role="button" role="button"
@ -146,9 +139,6 @@ export default {
e.stopPropagation() e.stopPropagation()
Downloads.sendAddToQueue(e.currentTarget.dataset.link) Downloads.sendAddToQueue(e.currentTarget.dataset.link)
}, },
openQualityModal(e) {
this.$root.$emit('QualityModal:open', e.currentTarget.dataset.link)
},
getTrackList(event) { getTrackList(event) {
document.getElementById('content').scrollTo(0, 0) document.getElementById('content').scrollTo(0, 0)

View File

@ -2,7 +2,7 @@
<div class="context-menu" v-show="menuOpen" ref="contextMenu" :style="{ top: yPos, left: xPos }"> <div class="context-menu" v-show="menuOpen" ref="contextMenu" :style="{ top: yPos, left: xPos }">
<button <button
class="menu-option" class="menu-option"
v-for="option of options" v-for="option of sortedOptions"
:key="option.label" :key="option.label"
v-show="option.show" v-show="option.show"
@click.prevent="option.action" @click.prevent="option.action"
@ -13,118 +13,185 @@
</template> </template>
<script> <script>
import Downloads from '@/utils/downloads'
import downloadQualities from '@js/qualities'
import { generatePath } from '@/utils/utils'
export default { export default {
data: () => ({ data() {
return {
menuOpen: false, menuOpen: false,
xPos: 0, xPos: 0,
yPos: 0, yPos: 0,
currentHref: '', deezerHref: '',
options: [ generalHref: '',
{ imgSrc: ''
label: 'Cut', }
show: true, },
computed: {
options() {
// In the action property:
// Use arrow functions to keep the Vue instance in 'this' // Use arrow functions to keep the Vue instance in 'this'
// Use normal functions to keep the object instance in 'this' // Use normal functions to keep the object instance in 'this'
const options = {
cut: {
label: this.$t('globals.cut'),
show: true,
position: 1,
action: () => { action: () => {
document.execCommand('Cut') document.execCommand('Cut')
} }
}, },
{ copy: {
label: 'Copy', label: this.$t('globals.copy'),
show: true, show: true,
position: 2,
action: () => { action: () => {
document.execCommand('Copy') document.execCommand('Copy')
} }
}, },
{ copyLink: {
label: 'Copy Link', label: this.$t('globals.copyLink'),
show: false, show: false,
action: null position: 3,
action: () => {
navigator.clipboard.writeText(this.generalHref).catch(err => {
console.error('Link copying failed', err)
})
}
}, },
{ copyImageLink: {
label: 'Copy Deezer Link', label: this.$t('globals.copyImageLink'),
show: false, show: false,
action: null position: 4,
action: () => {
navigator.clipboard.writeText(this.imgSrc).catch(err => {
console.error('Image copying failed', err)
})
}
}, },
{ copyDeezerLink: {
label: 'Paste', label: this.$t('globals.copyDeezerLink'),
show: false,
position: 5,
action: () => {
navigator.clipboard.writeText(this.generalHref).catch(err => {
console.error('Deezer link copying failed', err)
})
}
},
paste: {
label: this.$t('globals.paste'),
show: true, show: true,
position: 6,
action: () => { action: () => {
navigator.clipboard.readText().then(text => { navigator.clipboard.readText().then(text => {
document.execCommand('insertText', undefined, text) document.execCommand('insertText', undefined, text)
}) })
} }
} }
] }
}),
let nextValuePosition = Object.values(options).length + 1
downloadQualities.forEach((quality, index) => {
options[quality.objName] = {
label: `${this.$t('globals.download', [quality.label])}`,
show: false,
position: nextValuePosition + index,
action: this.tryToDownloadTrack.bind(null, quality.value)
}
})
return options
},
// This computed property is used for rendering the options in the wanted order
// while keeping the options computed property an Object to make the properties
// accessible via property name (es this.options.copyLink)
sortedOptions() {
return Object.values(this.options).sort((first, second) => {
return first.position < second.position ? -1 : 1
})
}
},
mounted() { mounted() {
document.body.addEventListener('contextmenu', this.showMenu) document.body.addEventListener('contextmenu', this.showMenu)
document.body.addEventListener('click', this.hideMenu)
document.body.addEventListener('click', () => {
// Finish all operations before closing (may be not necessary)
this.$nextTick().then(() => {
this.menuOpen = false
this.options[2].show = false
this.options[3].show = false
})
})
}, },
methods: { methods: {
showMenu(contextMenuEvent) { showMenu(contextMenuEvent) {
contextMenuEvent.preventDefault() contextMenuEvent.preventDefault()
const { const { pageX, pageY, target: elementClicked } = contextMenuEvent
pageX,
pageY, const path = generatePath(elementClicked)
path,
path: [elementClicked]
} = contextMenuEvent
this.positionMenu(pageX, pageY) this.positionMenu(pageX, pageY)
// Show 'Copy Link' option // Show 'Copy Link' option
if (elementClicked.matches('a')) { if (elementClicked.matches('a')) {
this.showCopyLink(elementClicked.href) this.generalHref = elementClicked.href
this.options.copyLink.show = true
} }
let link = null // Show 'Copy Image Link' option
if (elementClicked.matches('img')) {
this.imgSrc = elementClicked.src
this.options.copyImageLink.show = true
}
let deezerLink = null
for (let i = 0; i < path.length; i++) { for (let i = 0; i < path.length; i++) {
if (path[i] == document) break if (path[i] == document) break
if (path[i].matches('[data-link]')) { if (path[i].matches('[data-link]')) {
link = path[i].dataset.link deezerLink = path[i].dataset.link
break break
} }
} }
// Show 'Copy Deezer Link' option // Show 'Copy Deezer Link' option
if (link) { if (deezerLink) {
this.showCopyDeezerLink(link) this.deezerHref = deezerLink
this.showDeezerOptions()
} }
this.menuOpen = true this.menuOpen = true
}, },
hideMenu() {
if (!this.menuOpen) return
// Finish all operations before closing (may be not necessary)
this.$nextTick()
.then(() => {
this.menuOpen = false
this.options.copyLink.show = false
this.options.copyDeezerLink.show = false
this.options.copyImageLink.show = false
downloadQualities.forEach(quality => {
this.options[quality.objName].show = false
})
})
.catch(err => {
console.error(err)
})
},
positionMenu(newX, newY) { positionMenu(newX, newY) {
this.xPos = `${newX}px` this.xPos = `${newX}px`
this.yPos = `${newY}px` this.yPos = `${newY}px`
}, },
showCopyLink(href) { showDeezerOptions() {
this.options[2].show = true this.options.copyDeezerLink.show = true
this.options[2].action = () => {
navigator.clipboard.writeText(href).catch(err => { downloadQualities.forEach(quality => {
console.error('Link copying failed', err) this.options[quality.objName].show = true
}) })
}
}, },
showCopyDeezerLink(link) { tryToDownloadTrack(qualityValue) {
this.options[3].show = true Downloads.sendAddToQueue(this.deezerHref, qualityValue)
this.options[3].action = () => {
navigator.clipboard.writeText(link).catch(err => {
console.error('Download link copying failed', err)
})
}
} }
} }
} }
@ -164,6 +231,10 @@ export default {
background: var(--table-highlight); background: var(--table-highlight);
filter: brightness(150%); filter: brightness(150%);
} }
&__text {
text-transform: capitalize;
}
} }
// Resetting buttons only for this component (because the style is scoped) // Resetting buttons only for this component (because the style is scoped)

View File

@ -38,7 +38,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="release.link" :data-link="release.link"
class="download_overlay" class="download_overlay"
@ -48,9 +47,7 @@
</div> </div>
<p class="primary-text">{{ release.title }}</p> <p class="primary-text">{{ release.title }}</p>
<p class="secondary-text"> <p class="secondary-text">
{{ {{ `${$t('globals.by', [release.creator.name])} - ${$tc('globals.listTabs.trackN', release.nb_tracks)}` }}
`${$t('globals.by', [release.creator.name])} - ${$tc('globals.listTabs.trackN', release.nb_tracks)}`
}}
</p> </p>
</div> </div>
<div <div
@ -64,7 +61,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="release.link" :data-link="release.link"
class="download_overlay" class="download_overlay"
@ -74,12 +70,7 @@
</div> </div>
<p class="primary-text">{{ release.title }}</p> <p class="primary-text">{{ release.title }}</p>
<p class="secondary-text"> <p class="secondary-text">
{{ {{ `${$t('globals.by', [release.creator.name])} - ${$tc('globals.listTabs.trackN', release.nb_tracks)}` }}
`${$t('globals.by', [release.creator.name])} - ${$tc(
'globals.listTabs.trackN',
release.nb_tracks
)}`
}}
</p> </p>
</div> </div>
</div> </div>
@ -95,7 +86,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="release.link" :data-link="release.link"
class="download_overlay" class="download_overlay"
@ -119,7 +109,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="release.link" :data-link="release.link"
class="download_overlay" class="download_overlay"
@ -185,7 +174,6 @@
</td> </td>
<td <td
class="table__cell--download clickable" class="table__cell--download clickable"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="track.link" :data-link="track.link"
role="button" role="button"
@ -267,9 +255,6 @@ export default {
e.stopPropagation() e.stopPropagation()
Downloads.sendAddToQueue(e.currentTarget.dataset.link) Downloads.sendAddToQueue(e.currentTarget.dataset.link)
}, },
openQualityModal(e) {
this.$root.$emit('QualityModal:open', e.currentTarget.dataset.link)
},
updated_userSpotifyPlaylists(data) { updated_userSpotifyPlaylists(data) {
this.spotifyPlaylists = data this.spotifyPlaylists = data
}, },

View File

@ -20,7 +20,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="release.link" :data-link="release.link"
class="download_overlay" class="download_overlay"
@ -50,7 +49,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="release.link" :data-link="release.link"
class="download_overlay" class="download_overlay"
@ -89,9 +87,6 @@ export default {
addToQueue(e) { addToQueue(e) {
Downloads.sendAddToQueue(e.currentTarget.dataset.link) Downloads.sendAddToQueue(e.currentTarget.dataset.link)
}, },
openQualityModal(e) {
this.$root.$emit('QualityModal:open', e.currentTarget.dataset.link)
},
initHome(data) { initHome(data) {
const { const {
playlists: { data: playlistData }, playlists: { data: playlistData },

View File

@ -9,8 +9,12 @@
<li class="section-tabs__tab search_tablinks" id="search_all_tab">{{ $t('globals.listTabs.all') }}</li> <li class="section-tabs__tab search_tablinks" id="search_all_tab">{{ $t('globals.listTabs.all') }}</li>
<li class="section-tabs__tab search_tablinks" id="search_track_tab">{{ $tc('globals.listTabs.track', 2) }}</li> <li class="section-tabs__tab search_tablinks" id="search_track_tab">{{ $tc('globals.listTabs.track', 2) }}</li>
<li class="section-tabs__tab search_tablinks" id="search_album_tab">{{ $tc('globals.listTabs.album', 2) }}</li> <li class="section-tabs__tab search_tablinks" id="search_album_tab">{{ $tc('globals.listTabs.album', 2) }}</li>
<li class="section-tabs__tab search_tablinks" id="search_artist_tab">{{ $tc('globals.listTabs.artist', 2) }}</li> <li class="section-tabs__tab search_tablinks" id="search_artist_tab">
<li class="section-tabs__tab search_tablinks" id="search_playlist_tab">{{ $tc('globals.listTabs.playlist', 2) }}</li> {{ $tc('globals.listTabs.artist', 2) }}
</li>
<li class="section-tabs__tab search_tablinks" id="search_playlist_tab">
{{ $tc('globals.listTabs.playlist', 2) }}
</li>
</ul> </ul>
<div id="search_tab_content"> <div id="search_tab_content">
<!-- ### Main Search Tab ### --> <!-- ### Main Search Tab ### -->
@ -46,7 +50,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="results.allTab.TOP_RESULT[0].link" :data-link="results.allTab.TOP_RESULT[0].link"
class="download_overlay" class="download_overlay"
@ -65,9 +68,7 @@
$tc('globals.listTabs.trackN', results.allTab.TOP_RESULT[0].nb_song) $tc('globals.listTabs.trackN', results.allTab.TOP_RESULT[0].nb_song)
}} }}
</p> </p>
<span class="tag">{{ <span class="tag">{{ $tc(`globals.listTabs.${results.allTab.TOP_RESULT[0].type}`, 1) }}</span>
$tc(`globals.listTabs.${results.allTab.TOP_RESULT[0].type}`, 1)
}}</span>
</div> </div>
</div> </div>
<div v-else-if="section == 'TRACK'"> <div v-else-if="section == 'TRACK'">
@ -113,7 +114,6 @@
</td> </td>
<td <td
class="table__cell--download table__cell--center clickable" class="table__cell--download table__cell--center clickable"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="'https://www.deezer.com/track/' + track.SNG_ID" :data-link="'https://www.deezer.com/track/' + track.SNG_ID"
role="button" role="button"
@ -147,7 +147,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="'https://deezer.com/artist/' + release.ART_ID" :data-link="'https://deezer.com/artist/' + release.ART_ID"
class="download_overlay" class="download_overlay"
@ -179,7 +178,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="'https://deezer.com/album/' + release.ALB_ID" :data-link="'https://deezer.com/album/' + release.ALB_ID"
class="download_overlay" class="download_overlay"
@ -195,7 +193,9 @@
> >
{{ release.ALB_TITLE }} {{ release.ALB_TITLE }}
</p> </p>
<p class="secondary-text">{{ release.ART_NAME + ' - ' + $tc('globals.listTabs.trackN', release.NUMBER_TRACK) }}</p> <p class="secondary-text">
{{ release.ART_NAME + ' - ' + $tc('globals.listTabs.trackN', release.NUMBER_TRACK) }}
</p>
</div> </div>
</div> </div>
<div v-else-if="section == 'PLAYLIST'" class="release_grid firstrow_only"> <div v-else-if="section == 'PLAYLIST'" class="release_grid firstrow_only">
@ -220,7 +220,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="'https://deezer.com/playlist/' + release.PLAYLIST_ID" :data-link="'https://deezer.com/playlist/' + release.PLAYLIST_ID"
class="download_overlay" class="download_overlay"
@ -317,7 +316,6 @@
</td> </td>
<td <td
class="table__cell--download table__cell--center clickable" class="table__cell--download table__cell--center clickable"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="track.link" :data-link="track.link"
role="button" role="button"
@ -349,7 +347,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="release.link" :data-link="release.link"
class="download_overlay" class="download_overlay"
@ -361,7 +358,11 @@
<i v-if="release.explicit_lyrics" class="material-icons explicit_icon">explicit</i> <i v-if="release.explicit_lyrics" class="material-icons explicit_icon">explicit</i>
{{ release.title }} {{ release.title }}
</p> </p>
<p class="secondary-text">{{ $t('globals.by', [release.artist.name]) + ' - ' + $tc('globals.listTabs.trackN', release.nb_tracks) }}</p> <p class="secondary-text">
{{
$t('globals.by', [release.artist.name]) + ' - ' + $tc('globals.listTabs.trackN', release.nb_tracks)
}}
</p>
</div> </div>
</div> </div>
</div> </div>
@ -383,7 +384,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="release.link" :data-link="release.link"
class="download_overlay" class="download_overlay"
@ -414,7 +414,6 @@
<div <div
role="button" role="button"
aria-label="download" aria-label="download"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue" @click.stop="addToQueue"
:data-link="release.link" :data-link="release.link"
class="download_overlay" class="download_overlay"
@ -424,9 +423,7 @@
</div> </div>
<p class="primary-text">{{ release.title }}</p> <p class="primary-text">{{ release.title }}</p>
<p class="secondary-text"> <p class="secondary-text">
{{ {{ `${$t('globals.by', [release.user.name])} - ${$tc('globals.listTabs.trackN', release.nb_tracks)}` }}
`${$t('globals.by', [release.user.name])} - ${$tc('globals.listTabs.trackN', release.nb_tracks)}`
}}
</p> </p>
</div> </div>
</div> </div>
@ -613,9 +610,6 @@ export default {
addToQueue(e) { addToQueue(e) {
Downloads.sendAddToQueue(e.currentTarget.dataset.link) Downloads.sendAddToQueue(e.currentTarget.dataset.link)
}, },
openQualityModal(e) {
this.$root.$emit('QualityModal:open', e.currentTarget.dataset.link)
},
numberWithDots: Utils.numberWithDots, numberWithDots: Utils.numberWithDots,
convertDuration: Utils.convertDuration, convertDuration: Utils.convertDuration,
search(type) { search(type) {

View File

@ -53,7 +53,6 @@ export default {
}), }),
mounted() { mounted() {
this.$root.$on('QualityModal:open', this.openModal) this.$root.$on('QualityModal:open', this.openModal)
this.$refs.modal.addEventListener('webkitAnimationEnd', this.handleAnimationEnd) this.$refs.modal.addEventListener('webkitAnimationEnd', this.handleAnimationEnd)
}, },
methods: { methods: {

View File

@ -26,6 +26,9 @@
<a href="https://codeberg.org/RemixDev/deemix/wiki/Getting-your-own-ARL" target="_blank"> <a href="https://codeberg.org/RemixDev/deemix/wiki/Getting-your-own-ARL" target="_blank">
{{ $t('settings.login.arl.question') }} {{ $t('settings.login.arl.question') }}
</a> </a>
<a id="settings_btn_applogin" class="hide" href="#" @click="applogin">
Automated login
</a>
<button id="settings_btn_updateArl" @click="login" style="width: 100%;"> <button id="settings_btn_updateArl" @click="login" style="width: 100%;">
{{ $t('settings.login.arl.update') }} {{ $t('settings.login.arl.update') }}
</button> </button>
@ -678,6 +681,7 @@ export default {
socket.on('accountChanged', this.accountChanged) socket.on('accountChanged', this.accountChanged)
socket.on('familyAccounts', this.initAccounts) socket.on('familyAccounts', this.initAccounts)
socket.on('downloadFolderSelected', this.downloadFolderSelected) socket.on('downloadFolderSelected', this.downloadFolderSelected)
socket.on('applogin_arl', this.setArl)
}, },
methods: { methods: {
revertSettings() { revertSettings() {
@ -742,6 +746,14 @@ export default {
socket.emit('login', arl, true, this.accountNum) socket.emit('login', arl, true, this.accountNum)
} }
}, },
applogin(e) {
e.preventDefault()
if (window.clientMode) socket.emit('applogin')
},
setArl(arl) {
this.$refs.loginInput.value = arl
this.login()
},
changeAccount() { changeAccount() {
socket.emit('changeAccount', this.accountNum) socket.emit('changeAccount', this.accountNum)
}, },

View File

@ -133,15 +133,10 @@
</table> </table>
<span v-if="label" style="opacity: 0.4; margin-top: 8px; display: inline-block; font-size: 13px;">{{ label }}</span> <span v-if="label" style="opacity: 0.4; margin-top: 8px; display: inline-block; font-size: 13px;">{{ label }}</span>
<footer> <footer>
<button @contextmenu.prevent="openQualityModal" @click.stop="addToQueue" :data-link="link"> <button @click.stop="addToQueue" :data-link="link">
{{ `${$t('globals.download', [$tc(`globals.listTabs.${type}`, 1)])}` }} {{ `${$t('globals.download', [$tc(`globals.listTabs.${type}`, 1)])}` }}
</button> </button>
<button <button class="with_icon" @click.stop="addToQueue" :data-link="selectedLinks()">
class="with_icon"
@contextmenu.prevent="openQualityModal"
@click.stop="addToQueue"
:data-link="selectedLinks()"
>
{{ $t('tracklist.downloadSelection') }}<i class="material-icons">file_download</i> {{ $t('tracklist.downloadSelection') }}<i class="material-icons">file_download</i>
</button> </button>
<button class="back-button">{{ $t('globals.back') }}</button> <button class="back-button">{{ $t('globals.back') }}</button>
@ -189,9 +184,6 @@ export default {
addToQueue(e) { addToQueue(e) {
Downloads.sendAddToQueue(e.currentTarget.dataset.link) Downloads.sendAddToQueue(e.currentTarget.dataset.link)
}, },
openQualityModal(e) {
this.$root.$emit('QualityModal:open', e.currentTarget.dataset.link)
},
toggleAll(e) { toggleAll(e) {
this.body.forEach(item => { this.body.forEach(item => {
if (item.type == 'track') { if (item.type == 'track') {

32
src/js/qualities.js Normal file
View File

@ -0,0 +1,32 @@
export default [
{
objName: 'flac',
label: 'FLAC',
value: 9
},
{
objName: '320kbps',
label: 'MP3 320kbps',
value: 3
},
{
objName: '128kbps',
label: 'MP3 128kbps',
value: 1
},
{
objName: 'realityAudioHQ',
label: '360 Reality Audio [HQ]',
value: 15
},
{
objName: 'realityAudioMQ',
label: '360 Reality Audio [MQ]',
value: 14
},
{
objName: 'realityAudioLQ',
label: '360 Reality Audio [LQ]',
value: 13
}
]

View File

@ -211,7 +211,9 @@ const de = {
title: 'Soll ich die Dateien überchreiben?', title: 'Soll ich die Dateien überchreiben?',
y: 'Ja überschreibe die Dateien', y: 'Ja überschreibe die Dateien',
n: 'Nein überschreibe die Dateien nicht', n: 'Nein überschreibe die Dateien nicht',
t: 'Überschreibe nur die Tags' t: 'Überschreibe nur die Tags',
b: 'Nein, behalte beide Dateien und füge der Kopie eine Nummer hinzu'
}, },
fallbackBitrate: 'Falls gewünschte Bitrate nicht verfügbar, auf niedrigere Bitrate zurückgreifen', fallbackBitrate: 'Falls gewünschte Bitrate nicht verfügbar, auf niedrigere Bitrate zurückgreifen',
fallbackSearch: 'Zur Suche zurückkehren, wenn der Song nicht verfügbar ist', fallbackSearch: 'Zur Suche zurückkehren, wenn der Song nicht verfügbar ist',

View File

@ -11,6 +11,12 @@ const en = {
toggle_download_tab_hint: 'Expand/Collapse', toggle_download_tab_hint: 'Expand/Collapse',
clean_queue_hint: 'Clear Finished', clean_queue_hint: 'Clear Finished',
cancel_queue_hint: 'Cancel All', cancel_queue_hint: 'Cancel All',
cut: 'cut',
copy: 'copy',
copyLink: 'copy link',
copyImageLink: 'copy image link',
copyDeezerLink: 'copy deezer link',
paste: 'paste',
listTabs: { listTabs: {
empty: '', empty: '',
all: 'all', all: 'all',

View File

@ -11,6 +11,12 @@ const it = {
toggle_download_tab_hint: 'Espandi/Riduci', toggle_download_tab_hint: 'Espandi/Riduci',
clean_queue_hint: 'Pulisci Lista', clean_queue_hint: 'Pulisci Lista',
cancel_queue_hint: 'Cancella tutti i download', cancel_queue_hint: 'Cancella tutti i download',
cut: 'taglia',
copy: 'copia',
copyLink: 'copia link',
copyImageLink: 'copia link immagine',
copyDeezerLink: 'copia link deezer',
paste: 'incolla',
listTabs: { listTabs: {
all: 'tutto', all: 'tutto',
top_result: 'miglior risultato', top_result: 'miglior risultato',

View File

@ -1,9 +1,9 @@
import { socket } from '@/utils/socket' import { socket } from '@/utils/socket'
function sendAddToQueue(url, bitrate = null) { function sendAddToQueue(url, bitrate = null) {
if (url != '') { if (!url) return
socket.emit('addToQueue', { url: url, bitrate: bitrate }, () => {})
} socket.emit('addToQueue', { url, bitrate }, () => {})
} }
export default { export default {

View File

@ -1,3 +1,22 @@
/**
* Climbs the DOM until the root is reached, storing every node passed.
* @param {HTMLElement} el
* @return {Array} Contains all the nodes between el and the root
*/
export function generatePath(el) {
if (!el) {
throw new Error('No element passed to the generatePath function!')
}
let path = [el]
while ((el = el.parentNode) && el !== document) {
path.push(el)
}
return path
}
export function isValidURL(text) { export function isValidURL(text) {
let lowerCaseText = text.toLowerCase() let lowerCaseText = text.toLowerCase()