Merge remote-tracking branch 'swiftfork/main'

# Conflicts:
#	src/renderer/views/components/add-to-playlist.ejs
This commit is contained in:
Swiftzerr 2022-01-29 21:57:17 -05:00
commit bde33c4324
175 changed files with 35181 additions and 23240 deletions

View file

@ -0,0 +1,5 @@
{
"js": {
"beautify.ignore": "src/renderer/index.js"
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -27,6 +27,7 @@
.md-option-segment.md-option-segment_auto {
width: auto;
white-space: nowrap;
}
.md-option-container .md-option-line:not(:last-child) {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-mic"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="23"></line><line x1="8" y1="23" x2="16" y2="23"></line></svg>

After

Width:  |  Height:  |  Size: 418 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg>

After

Width:  |  Height:  |  Size: 344 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-video"><polygon points="23 7 16 12 23 17 23 7"></polygon><rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect></svg>

After

Width:  |  Height:  |  Size: 329 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-volume-2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path></svg>

After

Width:  |  Height:  |  Size: 354 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-volume"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon></svg>

After

Width:  |  Height:  |  Size: 275 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x-circle"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" fill="white"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M471.1 96C405 96 353.3 137.3 320 174.6 286.7 137.3 235 96 168.9 96 75.8 96 0 167.8 0 256s75.8 160 168.9 160c66.1 0 117.8-41.3 151.1-78.6 33.3 37.3 85 78.6 151.1 78.6 93.1 0 168.9-71.8 168.9-160S564.2 96 471.1 96zM168.9 320c-40.2 0-72.9-28.7-72.9-64s32.7-64 72.9-64c38.2 0 73.4 36.1 94 64-20.4 27.6-55.9 64-94 64zm302.2 0c-38.2 0-73.4-36.1-94-64 20.4-27.6 55.9-64 94-64 40.2 0 72.9 28.7 72.9 64s-32.7 64-72.9 64z"/></svg>

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
fill="white" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path id="XMLID_11_" d="M418.5,139.4H232.4v139.8h186.1V139.4z M464.8,46.7H46.3C20.5,46.7,0,68.1,0,93.1v325.9
c0,25.8,21.4,46.3,46.3,46.3h419.4c25.8,0,46.3-20.5,46.3-46.3V93.1C512,67.2,490.6,46.7,464.8,46.7z M464.8,418.9H46.3V92.2h419.4
v326.8H464.8z"/>
</svg>

After

Width:  |  Height:  |  Size: 627 B

View file

@ -0,0 +1,33 @@
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill="white" version="1.1">
<g>
<title>Layer 1</title>
<g id="svg_1">
<g id="svg_2">
<g id="svg_3">
<path id="svg_4" d="m482.197,374.266l-78.717,-45.448c-15.89,-9.174 -35.829,2.308 -35.829,20.686l0,27.587l-216.593,0c-63.597,-0.001 -115.337,-51.74 -115.337,-115.338c0,-9.864 -7.997,-17.86 -17.86,-17.86c-9.864,0 -17.86,7.997 -17.86,17.86c0,83.294 67.765,151.058 151.058,151.058l216.591,0l0,27.587c0,18.347 19.913,29.876 35.829,20.686l78.717,-45.447c15.89,-9.172 15.917,-32.181 0.001,-41.371z"/>
<path id="svg_5" d="m360.942,99.189l-216.593,0l0,-27.588c0,-18.347 -19.913,-29.876 -35.829,-20.686l-78.717,45.447c-15.889,9.173 -15.917,32.182 0,41.372l78.717,45.448c15.89,9.174 35.829,-2.309 35.829,-20.686l0,-27.587l216.593,0c63.598,0 115.337,51.739 115.337,115.337c0,9.864 7.997,17.86 17.86,17.86c9.864,0 17.86,-7.997 17.86,-17.86c0.001,-83.293 -67.764,-151.057 -151.057,-151.057z"/>
</g>
</g>
</g>
<g id="svg_6"/>
<g id="svg_7"/>
<g id="svg_8"/>
<g id="svg_9"/>
<g id="svg_10"/>
<g id="svg_11"/>
<g id="svg_12"/>
<g id="svg_13"/>
<g id="svg_14"/>
<g id="svg_15"/>
<g id="svg_16"/>
<g id="svg_17"/>
<g id="svg_18"/>
<g id="svg_19"/>
<g id="svg_20"/>
<g id="svg_24">
<path id="svg_21" d="m118,511.5c-65.19337,0 -118,-52.80663 -118,-118c0,-65.19337 52.80663,-118 118,-118c65.19337,0 118,52.80663 118,118c0,65.19337 -52.80663,118 -118,118z" opacity="undefined" stroke-width="0" stroke="#000" fill="#fff"/>
<text font-weight="bold" xml:space="preserve" text-anchor="start" font-family="Noto Sans JP" font-size="250" id="svg_23" y="470" x="54.64063" stroke-width="0" stroke="#000" fill="#000000">1</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path fill="white" d="M215.03 72.04L126.06 161H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V89.02c0-21.47-25.96-31.98-40.97-16.98zm123.2 108.08c-11.58-6.33-26.19-2.16-32.61 9.45-6.39 11.61-2.16 26.2 9.45 32.61C327.98 229.28 336 242.62 336 257c0 14.38-8.02 27.72-20.92 34.81-11.61 6.41-15.84 21-9.45 32.61 6.43 11.66 21.05 15.8 32.61 9.45 28.23-15.55 45.77-45 45.77-76.88s-17.54-61.32-45.78-76.87z"/></svg>

Before

Width:  |  Height:  |  Size: 710 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path fill="white" d="M215.03 71.05L126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97zm233.32-51.08c-11.17-7.33-26.18-4.24-33.51 6.95-7.34 11.17-4.22 26.18 6.95 33.51 66.27 43.49 105.82 116.6 105.82 195.58 0 78.98-39.55 152.09-105.82 195.58-11.17 7.32-14.29 22.34-6.95 33.5 7.04 10.71 21.93 14.56 33.51 6.95C528.27 439.58 576 351.33 576 256S528.27 72.43 448.35 19.97zM480 256c0-63.53-32.06-121.94-85.77-156.24-11.19-7.14-26.03-3.82-33.12 7.46s-3.78 26.21 7.41 33.36C408.27 165.97 432 209.11 432 256s-23.73 90.03-63.48 115.42c-11.19 7.14-14.5 22.07-7.41 33.36 6.51 10.36 21.12 15.14 33.12 7.46C447.94 377.94 480 319.54 480 256zm-141.77-76.87c-11.58-6.33-26.19-2.16-32.61 9.45-6.39 11.61-2.16 26.2 9.45 32.61C327.98 228.28 336 241.63 336 256c0 14.38-8.02 27.72-20.92 34.81-11.61 6.41-15.84 21-9.45 32.61 6.43 11.66 21.05 15.8 32.61 9.45 28.23-15.55 45.77-45 45.77-76.88s-17.54-61.32-45.78-76.86z"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -5,6 +5,8 @@ var CiderAudio = {
gainNode : null,
spatialNode : null,
spatialInput: null,
audioBands : null,
preampNode : null,
},
init: function (cb = function () { }) {
//AudioOutputs.fInit = true;
@ -21,8 +23,14 @@ var CiderAudio = {
},
off: function(){
try{
CiderAudio.audioNodes.gainNode.disconnect();
CiderAudio.audioNodes.spatialNode.disconnect();
try{
CiderAudio.audioNodes.gainNode.disconnect(); } catch(e){}
try{ CiderAudio.audioNodes.spatialNode.disconnect();} catch(e){}
try{
CiderAudio.audioNodes.preampNode.disconnect();
CiderAudio.audioNodes.audioBands[0].disconnect();
CiderAudio.audioNodes.audioBands[9].disconnect();
} catch(e){}
CiderAudio.source.connect(CiderAudio.context.destination);} catch(e){}
},
connectContext: function (mediaElem){
@ -42,6 +50,7 @@ var CiderAudio = {
if (app.cfg.audio.spatial){
CiderAudio.spatialOn()
}
CiderAudio.equalizer()
},
normalizerOn: function (){},
normalizerOff: function (){
@ -49,7 +58,7 @@ var CiderAudio = {
},
spatialOn: function (){
try{
CiderAudio.audioNodes.gainNode.connect(CiderAudio.context.destination);} catch(e){}
CiderAudio.audioNodes.gainNode.disconnect(CiderAudio.context.destination);} catch(e){}
CiderAudio.audioNodes.spatialNode = new ResonanceAudio(CiderAudio.context);
CiderAudio.audioNodes.spatialNode.output.connect(CiderAudio.context.destination);
let roomDimensions = {
@ -90,6 +99,42 @@ var CiderAudio = {
}
);
}
},
equalizer: function (){
let BANDS = app.cfg.audio.equalizer.frequencies;
let GAIN = app.cfg.audio.equalizer.gain;
let Q = app.cfg.audio.equalizer.Q;
CiderAudio.audioNodes.audioBands = [];
for (i = 0; i < BANDS.length; i++) {
CiderAudio.audioNodes.audioBands[i] = CiderAudio.context.createBiquadFilter();
CiderAudio.audioNodes.audioBands[i].type = 'peaking'; // 'peaking';
CiderAudio.audioNodes.audioBands[i].frequency.value = BANDS[i];
CiderAudio.audioNodes.audioBands[i].Q.value = Q[i];
CiderAudio.audioNodes.audioBands[i].gain.value = GAIN[i] * app.cfg.audio.equalizer.mix;
}
CiderAudio.audioNodes.preampNode = CiderAudio.context.createBiquadFilter();
CiderAudio.audioNodes.preampNode.type = 'highshelf';
CiderAudio.audioNodes.preampNode.frequency.value = 0; // allow all
CiderAudio.audioNodes.preampNode.gain.value = app.cfg.audio.equalizer.preamp;
if (app.cfg.audio.spatial) {
try{
CiderAudio.audioNodes.spatialNode.output.disconnect(CiderAudio.context.destination); } catch(e){}
CiderAudio.audioNodes.spatialNode.output.connect(CiderAudio.audioNodes.preampNode);
} else {
try{
CiderAudio.audioNodes.gainNode.disconnect(CiderAudio.context.destination);} catch(e){}
CiderAudio.audioNodes.gainNode.connect(CiderAudio.audioNodes.preampNode);
}
CiderAudio.audioNodes.preampNode.connect(CiderAudio.audioNodes.audioBands[0]);
for (i = 1; i < BANDS.length; i ++) {
CiderAudio.audioNodes.audioBands[i-1].connect(CiderAudio.audioNodes.audioBands[i]);
}
CiderAudio.audioNodes.audioBands[BANDS.length-1].connect(CiderAudio.context.destination);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,108 @@
const wsapi = {
cache: {playParams: {id: 0}, status: null, remainingTime: 0},
playbackCache: {status: null, time: Date.now()},
search(term, limit) {
MusicKit.getInstance().api.search(term, {limit: limit, types: 'songs,artists,albums,playlists'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearch', JSON.stringify(results))
})
},
searchLibrary(term, limit) {
MusicKit.getInstance().api.library.search(term, {limit: limit, types: 'library-songs,library-artists,library-albums,library-playlists'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearchLibrary', JSON.stringify(results))
})
},
getAttributes: function () {
const mk = MusicKit.getInstance();
const nowPlayingItem = mk.nowPlayingItem;
const isPlayingExport = mk.isPlaying;
const remainingTimeExport = mk.currentPlaybackTimeRemaining;
const attributes = (nowPlayingItem != null ? nowPlayingItem.attributes : {});
attributes.status = isPlayingExport ? isPlayingExport : false;
attributes.name = attributes.name ? attributes.name : 'No Title Found';
attributes.artwork = attributes.artwork ? attributes.artwork : {url: ''};
attributes.artwork.url = attributes.artwork.url ? attributes.artwork.url : '';
attributes.playParams = attributes.playParams ? attributes.playParams : {id: 'no-id-found'};
attributes.playParams.id = attributes.playParams.id ? attributes.playParams.id : 'no-id-found';
attributes.albumName = attributes.albumName ? attributes.albumName : '';
attributes.artistName = attributes.artistName ? attributes.artistName : '';
attributes.genreNames = attributes.genreNames ? attributes.genreNames : [];
attributes.remainingTime = remainingTimeExport ? (remainingTimeExport * 1000) : 0;
attributes.durationInMillis = attributes.durationInMillis ? attributes.durationInMillis : 0;
attributes.startTime = Date.now();
attributes.endTime = attributes.endTime ? attributes.endTime : Date.now();
attributes.volume = mk.volume;
attributes.shuffleMode = mk.shuffleMode;
attributes.repeatMode = mk.repeatMode;
attributes.autoplayEnabled = mk.autoplayEnabled;
return attributes
},
moveQueueItem(oldPosition, newPosition) {
MusicKit.getInstance().queue._queueItems.splice(newPosition,0,MusicKit.getInstance().queue._queueItems.splice(oldPosition,1)[0])
MusicKit.getInstance().queue._reindex()
},
setAutoplay(value) {
MusicKit.getInstance().autoplayEnabled = value
},
returnDynamic(data, type) {
ipcRenderer.send('wsapi-returnDynamic', JSON.stringify(data), type)
},
musickitApi(method, id, params, library = false) {
if (library) {
MusicKit.getInstance().api.library[method](id, params).then((results)=>{
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
})
} else {
MusicKit.getInstance().api[method](id, params).then((results)=>{
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
})
}
},
getPlaybackState () {
ipcRenderer.send('wsapi-updatePlaybackState', MusicKitInterop.getAttributes());
},
getLyrics() {
ipcRenderer.send('wsapi-returnLyrics',JSON.stringify(app.lyrics));
},
getQueue() {
ipcRenderer.send('wsapi-returnQueue', JSON.stringify(MusicKit.getInstance().queue))
},
playNext(type, id) {
var request = {}
request[type] = id
MusicKit.getInstance().playNext(request)
},
playLater(type, id) {
var request = {}
request[type] = id
MusicKit.getInstance().playLater(request)
},
love() {
},
playTrackById(id, kind = "song") {
MusicKit.getInstance().setQueue({ [kind]: id }).then(function (queue) {
MusicKit.getInstance().play()
})
},
quickPlay(term) {
// Quick play by song name
MusicKit.getInstance().api.search(term, { limit: 2, types: 'songs' }).then(function (data) {
MusicKit.getInstance().setQueue({ song: data["songs"][0]["id"] }).then(function (queue) {
MusicKit.getInstance().play()
})
})
},
toggleShuffle() {
MusicKit.getInstance().shuffleMode = MusicKit.getInstance().shuffleMode === 0 ? 1 : 0
},
toggleRepeat() {
if(MusicKit.getInstance().repeatMode == 0) {
MusicKit.getInstance().repeatMode = 1
}else if(MusicKit.getInstance().repeatMode == 1){
MusicKit.getInstance().repeatMode = 2
}else{
MusicKit.getInstance().repeatMode = 0
}
}
}

1
src/renderer/js/bootbox.min.js vendored Normal file

File diff suppressed because one or more lines are too long

7
src/renderer/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
src/renderer/js/notyf.min.js vendored Normal file

File diff suppressed because one or more lines are too long

5
src/renderer/js/popper.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1107
src/renderer/less/bootstrap.less vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -45,7 +45,7 @@
}
@media (max-width: 951px) {
.content-inner {
#app-content {
zoom: 0.8;
}
}
@ -53,7 +53,7 @@
// if page width is less than 951px
@media (max-width: 951px) {
.content-inner {
#app-content {
zoom: 0.8;
}
}

View file

@ -0,0 +1,370 @@
@-webkit-keyframes notyf-fadeinup {
0% {
opacity: 0;
transform: translateY(25%)
}
to {
opacity: 1;
transform: translateY(0)
}
}
@keyframes notyf-fadeinup {
0% {
opacity: 0;
transform: translateY(25%)
}
to {
opacity: 1;
transform: translateY(0)
}
}
@-webkit-keyframes notyf-fadeinleft {
0% {
opacity: 0;
transform: translateX(25%)
}
to {
opacity: 1;
transform: translateX(0)
}
}
@keyframes notyf-fadeinleft {
0% {
opacity: 0;
transform: translateX(25%)
}
to {
opacity: 1;
transform: translateX(0)
}
}
@-webkit-keyframes notyf-fadeoutright {
0% {
opacity: 1;
transform: translateX(0)
}
to {
opacity: 0;
transform: translateX(25%)
}
}
@keyframes notyf-fadeoutright {
0% {
opacity: 1;
transform: translateX(0)
}
to {
opacity: 0;
transform: translateX(25%)
}
}
@-webkit-keyframes notyf-fadeoutdown {
0% {
opacity: 1;
transform: translateY(0)
}
to {
opacity: 0;
transform: translateY(25%)
}
}
@keyframes notyf-fadeoutdown {
0% {
opacity: 1;
transform: translateY(0)
}
to {
opacity: 0;
transform: translateY(25%)
}
}
@-webkit-keyframes ripple {
0% {
transform: scale(0) translateY(-45%) translateX(13%)
}
to {
transform: scale(1) translateY(-45%) translateX(13%)
}
}
@keyframes ripple {
0% {
transform: scale(0) translateY(-45%) translateX(13%)
}
to {
transform: scale(1) translateY(-45%) translateX(13%)
}
}
.notyf {
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
color: #fff;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: flex-end;
pointer-events: none;
box-sizing: border-box;
padding: 20px
}
.notyf__icon--error,
.notyf__icon--success {
height: 21px;
width: 21px;
background: #fff;
border-radius: 50%;
display: block;
margin: 0 auto;
position: relative
}
.notyf__icon--error:after,
.notyf__icon--error:before {
content: "";
background: currentColor;
display: block;
position: absolute;
width: 3px;
border-radius: 3px;
left: 9px;
height: 12px;
top: 5px
}
.notyf__icon--error:after {
transform: rotate(-45deg)
}
.notyf__icon--error:before {
transform: rotate(45deg)
}
.notyf__icon--success:after,
.notyf__icon--success:before {
content: "";
background: currentColor;
display: block;
position: absolute;
width: 3px;
border-radius: 3px
}
.notyf__icon--success:after {
height: 6px;
transform: rotate(-45deg);
top: 9px;
left: 6px
}
.notyf__icon--success:before {
height: 11px;
transform: rotate(45deg);
top: 5px;
left: 10px
}
.notyf__toast {
display: block;
overflow: hidden;
pointer-events: auto;
-webkit-animation: notyf-fadeinup .3s ease-in forwards;
animation: notyf-fadeinup .3s ease-in forwards;
box-shadow: 0 3px 7px 0 rgba(0, 0, 0, .25);
position: relative;
padding: 0 15px;
border-radius: 2px;
max-width: 300px;
transform: translateY(25%);
box-sizing: border-box;
flex-shrink: 0
}
.notyf__toast--disappear {
transform: translateY(0);
-webkit-animation: notyf-fadeoutdown .3s forwards;
animation: notyf-fadeoutdown .3s forwards;
-webkit-animation-delay: .25s;
animation-delay: .25s
}
.notyf__toast--disappear .notyf__icon,
.notyf__toast--disappear .notyf__message {
-webkit-animation: notyf-fadeoutdown .3s forwards;
animation: notyf-fadeoutdown .3s forwards;
opacity: 1;
transform: translateY(0)
}
.notyf__toast--disappear .notyf__dismiss {
-webkit-animation: notyf-fadeoutright .3s forwards;
animation: notyf-fadeoutright .3s forwards;
opacity: 1;
transform: translateX(0)
}
.notyf__toast--disappear .notyf__message {
-webkit-animation-delay: .05s;
animation-delay: .05s
}
.notyf__toast--upper {
margin-bottom: 20px
}
.notyf__toast--lower {
margin-top: 20px
}
.notyf__toast--dismissible .notyf__wrapper {
padding-right: 30px
}
.notyf__ripple {
height: 400px;
width: 400px;
position: absolute;
transform-origin: bottom right;
right: 0;
top: 0;
border-radius: 50%;
transform: scale(0) translateY(-51%) translateX(13%);
z-index: 5;
-webkit-animation: ripple .4s ease-out forwards;
animation: ripple .4s ease-out forwards
}
.notyf__wrapper {
display: flex;
align-items: center;
padding-top: 17px;
padding-bottom: 17px;
padding-right: 15px;
border-radius: 3px;
position: relative;
z-index: 10
}
.notyf__icon {
width: 22px;
text-align: center;
font-size: 1.3em;
opacity: 0;
-webkit-animation: notyf-fadeinup .3s forwards;
animation: notyf-fadeinup .3s forwards;
-webkit-animation-delay: .3s;
animation-delay: .3s;
margin-right: 13px
}
.notyf__dismiss {
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 26px;
margin-right: -15px;
-webkit-animation: notyf-fadeinleft .3s forwards;
animation: notyf-fadeinleft .3s forwards;
-webkit-animation-delay: .35s;
animation-delay: .35s;
opacity: 0
}
.notyf__dismiss-btn {
background-color: rgba(0, 0, 0, .25);
border: none;
cursor: pointer;
transition: opacity .2s ease, background-color .2s ease;
outline: none;
opacity: .35;
height: 100%;
width: 100%
}
.notyf__dismiss-btn:after,
.notyf__dismiss-btn:before {
content: "";
background: #fff;
height: 12px;
width: 2px;
border-radius: 3px;
position: absolute;
left: calc(50% - 1px);
top: calc(50% - 5px)
}
.notyf__dismiss-btn:after {
transform: rotate(-45deg)
}
.notyf__dismiss-btn:before {
transform: rotate(45deg)
}
.notyf__dismiss-btn:hover {
opacity: .7;
background-color: rgba(0, 0, 0, .15)
}
.notyf__dismiss-btn:active {
opacity: .8
}
.notyf__message {
vertical-align: middle;
position: relative;
opacity: 0;
-webkit-animation: notyf-fadeinup .3s forwards;
animation: notyf-fadeinup .3s forwards;
-webkit-animation-delay: .25s;
animation-delay: .25s;
line-height: 1.5em
}
@media only screen and (max-width:480px) {
.notyf {
padding: 0
}
.notyf__ripple {
height: 600px;
width: 600px;
-webkit-animation-duration: .5s;
animation-duration: .5s
}
.notyf__toast {
max-width: none;
border-radius: 0;
box-shadow: 0 -2px 7px 0 rgba(0, 0, 0, .13);
width: 100%
}
.notyf__dismiss {
width: 56px
}
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
<script type="text/x-template" id="artwork-material">
<div class="artworkMaterial">
<img :src="src" v-for="image in images"/>
</div>
</script>
<script>
Vue.component('artwork-material', {
template: '#artwork-material',
data: function () {
return {
src: ""
}
},
mounted() {
this.src = app.getMediaItemArtwork(this.url, this.size)
},
props: {
url: {
type: String,
required: true
},
size: {
type: [String, Number],
required: false,
default: '32'
},
images: {
type: [String, Number],
required: false,
default: '2'
}
},
methods: {
}
});
</script>

View file

@ -0,0 +1,82 @@
<script type="text/x-template" id="add-to-playlist">
<template>
<div class="modal-fullscreen modal-generic" @click.self="app.resetState()" @contextmenu.self="app.resetState()">
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{app.getLz('action.addToLibrary')}}</div>
<button class="close-btn" @click="app.resetState()"></button>
</div>
<div class="modal-content">
<button class="playlist-item"
:class="{ focused: playlist.id == focused }"
@click="addToPlaylist(playlist.id)" style="width:100%;" v-for="playlist in playlistSorted" v-if="playlist.attributes.canEdit && playlist.type != 'library-playlist-folders'">
<div class="icon"><%- include("../svg/playlist.svg") %></div>
<div class="name">{{ playlist.attributes.name }}</div>
</button>
</div>
<div class="modal-search">
<div class="search-input-container" style="width:100%;margin: 16px 0;">
<div class="search-input--icon"></div>
<input type="search"
ref="searchInput"
style="width:100%;"
spellcheck="false"
:placeholder="app.getLz('term.search') + '...'"
v-model="searchQuery"
@input="search()"
class="search-input">
</div>
</div>
</div>
</div>
</template>
</script>
<script>
Vue.component('add-to-playlist', {
template: '#add-to-playlist',
data: function () {
return {
playlistSorted: [],
searchQuery: "",
focused: "",
app: this.$root,
}
},
props: {
playlists: {
type: Array,
required: true
}
},
mounted() {
this.search()
this.$refs.searchInput.focus()
this.$refs.searchInput.addEventListener('keydown', (e) => {
if (e.keyCode == 13) {
if (this.focused != "") {
this.addToPlaylist(this.focused)
}
}
})
},
methods: {
addToPlaylist(id) {
app.addSelectedToPlaylist(id)
},
search() {
this.focused = ""
if (this.searchQuery == "") {
this.playlistSorted = this.playlists
} else {
this.playlistSorted = this.playlists.filter(playlist => {
return playlist.attributes.name.toLowerCase().indexOf(this.searchQuery.toLowerCase()) > -1
})
if (this.playlistSorted.length == 1) {
this.focused = this.playlistSorted[0].id
}
}
},
}
});
</script>

View file

@ -0,0 +1,390 @@
<script type="text/x-template" id="eq-view">
<div class="modal-fullscreen equalizer-panel">
<div class="modal-window" >
<div class="modal-header">
<div class="modal-title">{{$root.getLz('term.equalizer')}}</div>
<button class="close-btn" @click="close()"></button>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select" style="width:220px;text-align:center;margin-right:16.75em" v-model="$root.cfg.audio.equalizer.preset" v-on:change="changePreset($root.cfg.audio.equalizer.preset)">
<optgroup label="User Presets">
<option v-for="preset in $root.cfg.audio.equalizer.presets" :value="preset.preset">{{preset.name}}</option>
</optgroup>
<optgroup label="Default Presets">
<option v-for="preset in defaultPresets" :value="preset.preset">{{preset.name}}</option>
</optgroup>
</select>
</div>
</div>
<div class="modal-content">
<!-- BANDS = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000]; -->
<div class="inputs-container">
<div class="input-container mini">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.preamp" @change="changePreamp()">
<input tabindex="0" type="range" class="eq-slider mini" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.preamp" @change="changePreamp()">
Preamp
</div>
<div class="input-container mini">
{{$root.cfg.audio.equalizer.mix}}
<input tabindex="0" type="range" class="eq-slider mini" orient="vertical" min="0" max="2" step="0.1" v-model="$root.cfg.audio.equalizer.mix" @change="changeMix()">
Mix
</div>
<div class="input-container header mini">
Gain
<input type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" >
<div class="freq-header">Freq</div>
<div>Q</div>
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[0]" @change="changeGain(0)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[0]" @change="changeGain(0)">
<input type="number" class="eq-freq" orient="vertical" min="22" max="44" step="2" v-model="$root.cfg.audio.equalizer.frequencies[0]" @change="changeFreq(0)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[0]" @change="changeQ(0)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[1]" @change="changeGain(1)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[1]" @change="changeGain(1)">
<input type="number" class="eq-freq" orient="vertical" min="44" max="88" step="4" v-model="$root.cfg.audio.equalizer.frequencies[1]" @change="changeFreq(1)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[1]" @change="changeQ(1)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[2]" @change="changeGain(2)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[2]" @change="changeGain(2)">
<input type="number" class="eq-freq" orient="vertical" min="88" max="177" step="8" v-model="$root.cfg.audio.equalizer.frequencies[2]" @change="changeFreq(2)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[2]" @change="changeQ(2)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[3]" @change="changeGain(3)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[3]" @change="changeGain(3)">
<input type="number" class="eq-freq" orient="vertical" min="177" max="355" step="16" v-model="$root.cfg.audio.equalizer.frequencies[3]" @change="changeFreq(3)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[3]" @change="changeQ(3)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[4]" @change="changeGain(4)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[4]" @change="changeGain(4)">
<input type="number" class="eq-freq" orient="vertical" min="355" max="710" step="32" v-model="$root.cfg.audio.equalizer.frequencies[4]" @change="changeFreq(4)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[4]" @change="changeQ(4)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[5]" @change="changeGain(5)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[5]" @change="changeGain(5)">
<input type="number" class="eq-freq" orient="vertical" min="710" max="1420" step="64" v-model="$root.cfg.audio.equalizer.frequencies[5]" @change="changeFreq(5)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[5]" @change="changeQ(5)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[6]" @change="changeGain(6)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[6]" @change="changeGain(6)">
<input type="number" class="eq-freq" orient="vertical" min="1420" max="2840" step="128" v-model="$root.cfg.audio.equalizer.frequencies[6]" @change="changeFreq(6)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[6]" @change="changeQ(6)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[7]" @change="changeGain(7)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[7]" @change="changeGain(7)">
<input type="number" class="eq-freq" orient="vertical" min="2840" max="5680" step="256" v-model="$root.cfg.audio.equalizer.frequencies[7]" @change="changeFreq(7)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[7]" @change="changeQ(7)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[8]" @change="changeGain(8)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[8]" @change="changeGain(8)">
<input type="number" class="eq-freq" orient="vertical" min="5680" max="11360" step="512" v-model="$root.cfg.audio.equalizer.frequencies[8]" @change="changeFreq(8)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[8]" @change="changeQ(8)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[9]" @change="changeGain(9)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[9]" @change="changeGain(9)">
<input type="number" class="eq-freq" orient="vertical" min="11360" max="22720" step="1024" v-model="$root.cfg.audio.equalizer.frequencies[9]" @change="changeFreq(9)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[9]" @change="changeQ(9)">
</div>
</div>
</div>
<div class="modal-lowercontent">
<div class="row">
<div class="col">
<button class="md-btn" style="width:100%" @click="resetGain()">{{$root.getLz('term.reset')}}</button>
</div>
<div class="col">
<button class="md-btn" style="width:100%" @click="presetOptions($event)">{{$root.getLz('term.menu')}}</button>
</div>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('eq-view', {
template: '#eq-view',
data: function () {
return {
// app: this.$root,
eqPreset: function () {
this.preset = uuidv4()
this.name = ""
this.frequencies = []
this.gain = []
this.Q = []
this.preamp = 0
this.mix = 1
this.userGenerated = true
},
defaultPresets: [{
'preset': 'warmth',
'name': 'Warmth',
'frequencies': [32, 75, 125, 197, 500, 1000, 2000, 3040, 8000, 16000],
'gain': [0, 2.1, 0, 0.8, 0, 0, 0, -1.5, 0, 0],
'Q': [1, 0.7, 1, 1.5, 1, 1, 1, 2, 1, 1],
'preamp': 0,
'mix': 1,
'userGenerated': false
}, {
'preset': 'boostBrightness',
'name': 'Boost Brightness',
'frequencies': [32, 63, 125, 250, 466, 1000, 2000, 4000, 8000, 20000],
'gain': [0, 0, 0, 0, -2, 0, 0, 0, 0, 10],
'Q': [1, 1, 1, 1, 0.6, 1, 1, 1, 1, 0.1],
'preamp': 0,
'mix': 1,
'userGenerated': false
}, {
'preset': 'acoustic',
'name': 'Acoustic',
'frequencies': [32, 75, 125, 220, 700, 1000, 2000, 4000, 10000, 16000],
'gain': [0, -8, 0, -0.1, -3, 0, 0, 0, 4, 0],
'Q': [1, 0.2, 1, 2.0, 1.4, 1, 1, 1, 0.1, 1],
'preamp': 0,
'mix': 1,
'userGenerated': false
}, {
'preset': 'clearVocal',
'name': 'Clear Vocal',
'frequencies': [20, 63, 125, 250, 400, 1000, 2000, 4000, 8000, 20000],
'gain': [-22, 0, 0, 0, -3, 0, 1.8, 0, 0, 3.5],
'Q': [0.3, 1, 1, 1, 2.0, 1, 0.7, 1, 1, 0.8],
'preamp': 0,
'mix': 1,
'userGenerated': false
}, {
'preset': 'instrumentClarity',
'name': 'Instrument Clarity',
'frequencies': [20, 63, 155, 250, 500, 1000, 2000, 5000, 11000, 16000],
'gain': [-15, 0, -3, 0, 0, 0, 0, 3.1, 0, 0],
'Q': [0.5, 1, 2, 1, 1, 1, 1, 1.5, 0.1, 1],
'preamp': 0,
'mix': 1,
'userGenerated': false
}, {
'preset': 'reduceHarshness',
'name': 'Reduce Harshness',
'frequencies': [32, 63, 125, 250, 500, 1128, 2000, 4057, 8000, 16000],
'gain': [0, 0, 0, 0, 0, 2, 0, -6.4, 0, 0],
'Q': [1, 1, 1, 1, 1, 2, 1, 1, 1, 1],
'preamp': 0,
'mix': 1,
'userGenerated': false
}, {
'preset': 'smileyFace',
'name': 'Smiley Face',
'frequencies': [35, 63, 125, 250, 500, 800, 2000, 4000, 8000, 20000],
'gain': [5, 0, 0, 0, 0, -5, 0, 0, 0, 5],
'Q': [0.1, 1, 1, 1, 1, 0.6, 1, 1, 1, 0.2],
'preamp': 0,
'mix': 1,
'userGenerated': false
},
{
'preset': 'bassBoostCrystal',
'name': 'Crystal Bass Boost',
'frequencies': [45.53,88.06,116.18,161.3,247.05,295.6,365.79,495.13,716.85,960.76],
'gain': [-0.36,4.07,-1.3,1.92,0.77,-0.53,-1.33,0.44,0.46,-0.5],
'Q': [1.768,0.625,5,8.409,10,16.82,5.946,7.071,20,10],
'preamp': -2,
'mix': 1,
'userGenerated': false
},
{
'preset': 'bassBoostSurgical',
'name': 'Surgical Bass Boost',
'frequencies': [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
'gain': [2.7, 2.2, 1.6, 1.4, 0.6, 0, 0, 0, 0, 0],
'Q': [1.4, 1.4, 1.4, 1.4, 1.4, 1, 1, 1, 1, 1],
'preamp': 0,
'mix': 1,
'userGenerated': false
}, {
'preset': 'bassBoostClassic',
'name': 'Classic Bass Boost',
'frequencies': [32, 63, 160, 250, 500, 1000, 2000, 3500, 8000, 20000],
'gain': [2.7, 2.2, 1.6, 1.4, 0.6, 0, 0, 0, 0, 0],
'Q': [0.7, 0.7, 0.7, 0.7, 0.7, 1, 1, 1, 1, 1],
'preamp': 0,
'mix': 1,
'userGenerated': false
}]
}
},
props: ["src", "url"],
mounted() {
},
methods: {
presetOptions(event) {
let menu = {
items: {
"new": {
"icon": "./assets/feather/plus.svg",
name: "New Preset...",
action: () => {
this.addPreset()
}
},
"delete": {
"icon": "./assets/feather/x-circle.svg",
name: "Delete Preset",
action: () => {
this.deletePreset()
}
},
"import": {
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.import'),
"action": function () {
notyf.error("Not implemented yet")
}
},
"export": {
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.export'),
"action": function () {
notyf.error("Not implemented yet")
}
},
}
}
if(!this.$root.cfg.audio.equalizer.userGenerated) {
delete menu.items.delete
}
app.showMenuPanel(menu, event)
},
sharePreset(event) {
let menu = {
items: [
{
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.import'),
"action": function () {
notyf.error("Not implemented yet")
}
},
{
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.export'),
"action": function () {
notyf.error("Not implemented yet")
}
},
]
}
app.showMenuPanel(menu, event)
},
deletePreset() {
let presets = this.$root.cfg.audio.equalizer.presets
bootbox.confirm("Are you sure you want to delete this preset?", (result) => {
if (result) {
this.changePreset("default")
// find the preset by id (preset) and remove it
let index = presets.findIndex(p => p.preset == this.preset)
presets.splice(index, 1)
notyf.success("Removed preset")
}
})
},
close() {
app.resetState()
},
changePreamp() {
CiderAudio.audioNodes.preampNode.gain.value = app.cfg.audio.equalizer.preamp;
},
changeMix() {
for (var i = 0; i < 10; i++) {
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
}
},
changeGain(i) {
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
},
changeFreq(i) {
CiderAudio.audioNodes.audioBands[i].frequency.value = app.cfg.audio.equalizer.frequencies[i]
},
changeQ(i) {
CiderAudio.audioNodes.audioBands[i].Q.value = app.cfg.audio.equalizer.Q[i]
},
resetGain() {
this.applyPreset({
'frequencies': [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
'gain': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'Q': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
'preamp': 0,
'mix': 1,
})
if (app.cfg.audio.equalizer.userGenerated) {
this.saveSelectedPreset()
}
},
addPreset() {
let self = this
bootbox.prompt("New EQ Preset Name", (res) => {
if (res) {
let eqSettings = Clone(app.cfg.audio.equalizer)
let newPreset = new self.eqPreset()
newPreset.name = res
newPreset.frequencies = eqSettings.frequencies
newPreset.gain = eqSettings.gain
newPreset.Q = eqSettings.Q
newPreset.preamp = eqSettings.preamp
newPreset.mix = eqSettings.mix
app.cfg.audio.equalizer.presets.push(newPreset)
notyf.success("Added Preset")
self.changePreset(newPreset.preset)
}
})
},
saveSelectedPreset() {
// Save the current settings to the selected preset
let self = this
//let preset = app.cfg.audio.equalizer.presets[app.cfg.audio.equalizer.preset]
// find the preset by its id (preset)
let preset = app.cfg.audio.equalizer.presets.find(p => p.preset == app.cfg.audio.equalizer.preset)
preset.frequencies = app.cfg.audio.equalizer.frequencies
preset.gain = app.cfg.audio.equalizer.gain
preset.Q = app.cfg.audio.equalizer.Q
preset.preamp = app.cfg.audio.equalizer.preamp
preset.mix = app.cfg.audio.equalizer.mix
notyf.success("Saved Preset")
},
applyPreset(preset) {
Object.assign(this.$root.cfg.audio.equalizer, preset)
this.changePreamp()
for (var i = 0; i < 10; i++) {
this.changeGain(i)
this.changeFreq(i)
this.changeQ(i)
}
},
changePreset(id) {
let userPresets = app.cfg.audio.equalizer.presets
let defaultPresets = Clone(this.defaultPresets)
let presets = defaultPresets.concat(userPresets)
console.log(presets)
let preset = presets.find(p => p.preset == id)
console.log(preset)
if (preset) {
this.applyPreset(preset)
}
}
}
});
</script>

View file

@ -26,7 +26,7 @@
{{ app.mk.nowPlayingItem["attributes"]["name"] }}
</div>
<div
style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap; margin-top: 0.25vh;">
style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap; margin-top: 0.25vh; overflow: hidden;">
<div class="item-navigate song-artist" style="display: inline-block;"
@click="app.getNowPlayingItemDetailed(`artist`)">
{{ app.mk.nowPlayingItem["attributes"]["artistName"] }}
@ -71,17 +71,17 @@
<div class="app-chrome-item">
<button class="playback-button--small repeat" v-if="app.mk.repeatMode == 0"
@click="app.mk.repeatMode = 1"></button>
<button class="playback-button--small repeat active" @click="app.mk.repeatMode = 2"
<button class="playback-button--small repeat repeatOne" @click="app.mk.repeatMode = 2"
v-else-if="app.mk.repeatMode == 1"></button>
<button class="playback-button--small repeat repeatOne" @click="app.mk.repeatMode = 0"
<button class="playback-button--small repeat active" @click="app.mk.repeatMode = 0"
v-else-if="app.mk.repeatMode == 2"></button>
</div>
</div>
<div class="app-chrome-item volume display--large">
<div class="app-chrome-item volume-icon"></div>
<div class="input-container">
<input type="range" class="slider" @wheel="app.volumeWheel" step="0.01" min="0" max="1" v-model="app.mk.volume"
v-if="typeof app.mk.volume != 'undefined'">
<button class="volume-button--small volume" @click="app.muteButtonPressed()" :class="{'active': app.cfg.audio.volume == 0}"></button>
<input type="range" class="slider" @wheel="app.volumeWheel" step="0.01" min="0" :max="$root.cfg.audio.maxVolume" v-model="app.mk.volume"
v-if="typeof app.mk.volume != 'undefined'" @change="app.checkMuteChange()">
</div>
</div>
</div>

View file

@ -2,28 +2,20 @@
<div v-observe-visibility="{callback: visibilityChanged}"
@click="select"
class="cd-mediaitem-list-item"
:class="{'mediaitem-selected': app.select_hasMediaItem(guid)}">
:class="{'mediaitem-selected': app.select_hasMediaItem(guid)}"
@contextmenu="contextMenu">
<template v-if="isVisible">
<div class="artwork" v-if="showArtwork == true">
<mediaitem-artwork
:url="getArtwork()"
size="50"
:type="item.type"></mediaitem-artwork>
<button class="overlay-play" @click="select"></button>
</div>
<div class="info-rect" :style="{'padding-left': (showArtwork ? '' : '16px')}"
@dblclick="app.routeView(item)">
<div class="title text-overflow-elipsis">
{{ item.attributes.name }}
</div>
<div class="subtitle text-overflow-elipsis" style="-webkit-box-orient: horizontal;">
<template v-if="item.attributes.name">
<div class="artist item-navigate text-overflow-elipsis"
@click="select">
{{ item.attributes.artistName }}
</div>
</template>
</div>
</div>
</template>
</div>
@ -62,22 +54,19 @@
return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
},
getDataType() {
if (this.item.attributes.playParams.isLibrary) {
return this.item.type
} else {
return this.item.attributes.playParams.kind
}
return this.item.type
},
async select(e) {
let u = this.item
let u1 = await app.mk.api.library.artistRelationship(u.id,"albums",
{platform: "web",
"include[library-albums]": "artists,tracks",
"include[library-artists]": "catalog",
"fields[artists]": "url",
"includeOnly": "catalog,artists"}
)
app.showCollection({data : Object.assign({},u1)}, u.attributes.name?? '', '');
let u1 = await app.mk.api.v3.music(`/v1/me/library/artists/${u.id}/albums`, {
"platform": "web",
"include[library-albums]": "artists,tracks",
"include[library-artists]": "catalog",
"fields[artists]": "url",
"includeOnly": "catalog,artists"
})
app.showCollection({data : Object.assign({},u1.data.data)}, u.attributes.name?? '', '');
app.select_selectMediaItem(u.id, this.getDataType(), this.index, this.guid, true)
},
getArtwork(){
let u = ""
@ -87,80 +76,38 @@
return u;
},
contextMenu(event) {
let self = this
let data_type = this.getDataType()
let item_id = this.item.attributes.playParams.id ?? this.item.id
let isLibrary = this.item.attributes.playParams.isLibrary ?? false
let item = self.item
item.attributes.artistName = item.attributes.name;
let useMenu = "normal"
if (app.selectedMediaItems.length <= 1) {
app.selectedMediaItems = []
app.select_selectMediaItem(item_id, data_type, this.index, this.guid, isLibrary)
app.select_selectMediaItem(this.item.id, data_type, this.index, this.guid, true)
} else {
useMenu = "multiple"
}
let menus = {
multiple: {
items: [
{
"name": "Add to Playlist...",
"action": function () {
app.promptAddToPlaylist()
}
},
{
name: `Play ${app.selectedMediaItems.length} tracks next`,
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
if (!itemsToPlay[item.kind]) {
itemsToPlay[item.kind] = []
}
itemsToPlay[item.kind].push(item.id)
})
// loop through itemsToPlay
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playNext({[kind + "s"]: itemsToPlay[kind]})
}
}
console.log(itemsToPlay)
app.selectedMediaItems = []
}
},
{
name: `Play ${app.selectedMediaItems.length} tracks later`,
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
if (!itemsToPlay[item.kind]) {
itemsToPlay[item.kind] = []
}
itemsToPlay[item.kind].push(item.id)
})
// loop through itemsToPlay
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playLater({[kind + "s"]: itemsToPlay[kind]})
}
}
app.selectedMediaItems = []
}
},
]
items: [] //
},
normal: {
items: [
{
"name": "Add to Playlist...",
"name": app.getLz('action.goToArtist'),
"icon": "./assets/feather/user.svg",
"action": function () {
app.promptAddToPlaylist()
app.searchAndNavigate(self.item, 'artist')
console.log(self.item)
}
},
{
"name": "Start Radio",
"icon": "./assets/feather/radio.svg",
"name": app.getLz('action.startRadio'),
"action": function () {
app.mk.setStationQueue({song: self.item.attributes.playParams.id ?? self.item.id}).then(() => {
app.mk.play()
@ -169,31 +116,15 @@
}
},
{
"name": "Play Next",
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.share'),
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
app.selectedMediaItems = []
}
},
{
"name": "Play Later",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
app.selectedMediaItems = []
}
},
{
"name": "Go to Artist",
"action": function () {
app.searchAndNavigate(self.item, 'artist')
}
},
{
"name": "Go to Album",
"action": function () {
app.searchAndNavigate(self.item, 'album')
if (!self.item.attributes.url && self.item.relationships){
if (self.item.relationships.catalog){
app.mkapi(self.item.attributes.playParams.kind, false, self.item.relationships.catalog.data[0].id).then(u => {self.app.copyToClipboard((u.data.data.length && u.data.data.length > 0)? u.data.data[0].attributes.url : u.data.data.attributes.url)})
}
} else {
self.app.copyToClipboard(self.item.attributes.url)}
}
},
]
@ -208,7 +139,9 @@
menus.multiple.items = menus.multiple.items.concat(this.contextExt.multiple)
}
}
CiderContextMenu.Create(event, menus[useMenu])
//CiderContextMenu.Create(event, menus[useMenu]); // Depreciated Context Menu
app.showMenuPanel(menus[useMenu], event);
},
visibilityChanged: function (isVisible, entry) {
this.isVisible = isVisible

View file

@ -6,7 +6,7 @@
<h3>{{ recom.attributes.title ? recom.attributes.title.stringForDisplay : ""}}</h3>
</div>
<div class="col-auto flex-center" v-if="recom.relationships.contents.data.length >= 10">
<button class="cd-btn-seeall" @click="app.showCollection(recom.relationships.contents, recom.attributes.title ? recom.attributes.title.stringForDisplay : '', 'listen_now')" >See All</button>
<button class="cd-btn-seeall" @click="app.showCollection(recom.relationships.contents, recom.attributes.title ? recom.attributes.title.stringForDisplay : '', 'listen_now')" >{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<template v-if="recom.attributes.display.kind == 'MusicCoverShelf'">

View file

@ -35,7 +35,7 @@
</template>
<template v-else>
<div class="no-lyrics">
Loading... / Lyrics not found./ Instrumental.</div>
{{app.getLz('term.noLyrics')}}</div>
</template>
</div>
</script>

View file

@ -1,10 +1,10 @@
<script type="text/x-template" id="mediaitem-artwork">
<div class="mediaitem-artwork" :class="[{'rounded': (type == 'artists')}, classes]" :key="url"
v-observe-visibility="{callback: visibilityChanged}">
<div class="mediaitem-artwork" :class="[{'rounded': (type == 'artists')}, classes]" :key="url">
<img :src="app.getMediaItemArtwork(url, size, width)"
decoding="async" loading="lazy"
decoding="async"
:style="{background: bgcolor}"
class="mediaitem-artwork--img">
<div v-if="video && isVisible && getVideoPriority()" class="animatedartwork-view-box">
<div v-if="video && getVideoPriority()" class="animatedartwork-view-box">
<animatedartwork-view :priority="getVideoPriority()" :video="video"></animatedartwork-view>
</div>
</div>
@ -22,6 +22,10 @@
type: [String, Number],
required: false
},
bgcolor: {
type: String,
default: ''
},
url: {
type: String,
default: ''
@ -88,9 +92,6 @@
width: this.size + 'px',
height: this.size + 'px'
};
},
visibilityChanged: function (isVisible, entry) {
this.isVisible = isVisible
}
}
});

View file

@ -14,17 +14,26 @@
@mouseleave="showInLibrary = false"
:class="[{'mediaitem-selected': app.select_hasMediaItem(guid)}, addClasses]">
<template v-if="isVisible">
<div class="isLibrary" :style="{opacity: (showInLibrary ? 1 : 0)}" v-if="showLibraryStatus == true">
<button @click="addToLibrary()"
v-if="!addedToLibrary">
<div class="svg-icon" :style="{'--color': 'var(--keyColor)', '--url': 'url(./assets/feather/plus.svg)'}"></div>
</button>
<button v-else style="opacity:0;">❤️</button>
<div class="isLibrary"" v-if="showLibraryStatus == true">
<div v-if="showInLibrary" :style="{display: (showInLibrary ? 'block' : 'none'), 'margin-left':'11px'}">
<button @click="addToLibrary()" v-if="!addedToLibrary">
<div class="svg-icon" :style="{'--color': 'var(--keyColor)', '--url': 'url(./assets/feather/plus.svg)'}"></div>
</button>
</div>
<div v-if="!(app.mk.isPlaying && (((app.mk.nowPlayingItem._songId ?? app.mk.nowPlayingItem.id ) == item.attributes.playParams.id) || (app.mk.nowPlayingItem.id == item.id ))) && showIndex" :style="{display: ((showIndex && !showInLibrary) ? 'block' : 'none'), 'margin-left':'11px'}">
<div>
<div>{{ (item.attributes && !showIndexPlaylist) ? (item.attributes.trackNumber ?? '') : ((index * 1 + 1 ) ?? '')}}</div>
</div>
</div>
<div v-if="app.mk.isPlaying && (((app.mk.nowPlayingItem._songId ?? app.mk.nowPlayingItem.id ) == item.attributes.playParams.id) || (app.mk.nowPlayingItem.id == item.id))" :style="{display: (showInLibrary ? 'none' : 'block')}">
<div class="loadbar-sound"></div>
</div>
</div>
<div class="artwork" v-if="showArtwork == true">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
:size="48"
:bgcolor="getBgColor()"
:type="item.type"></mediaitem-artwork>
<button class="overlay-play" @click="playTrack()"><%- include("../svg/play.svg") %></button>
</div>
@ -88,6 +97,8 @@
'show-library-status': {type: Boolean, default: true},
'show-meta-data': {type: Boolean, default: false},
'show-duration': {type: Boolean, default: true},
'showIndex': {type: Boolean, required: false},
'showIndexPlaylist': {type: Boolean, required: false},
'contextExt': {type: Object, required: false},
'class-list': {type: String, required: false, default: ""},
},
@ -99,6 +110,10 @@
this.getClasses()
},
methods: {
getBgColor() {
let color = `#${(this.item.attributes.artwork != null && this.item.attributes.artwork.bgColor != null) ? (this.item.attributes.artwork.bgColor) : ``}`
return color
},
async checkLibrary() {
if(this.addedToLibrary) {return this.addedToLibrary}
if(this.item.type.includes("library-playlists") || this.item.type.includes("station")) {
@ -223,14 +238,14 @@
multiple: {
items: [
{
"name": "Add to Playlist...",
"name": app.getLz('action.addToPlaylist'),
"icon": "./assets/feather/plus.svg",
"action": function () {
app.promptAddToPlaylist()
}
},
{
name: `Play ${app.selectedMediaItems.length} tracks next`,
name: app.getLz('action.playTracksNext').replace("${app.selectedMediaItems.length}", app.selectedMediaItems.length),
"icon": "./assets/arrow-bend-up.svg",
action: () => {
let itemsToPlay = {}
@ -252,7 +267,7 @@
}
},
{
name: `Play ${app.selectedMediaItems.length} tracks later`,
name: app.getLz('action.playTracksLater').replace("${app.selectedMediaItems.length}", app.selectedMediaItems.length),
"icon": "./assets/arrow-bend-down.svg",
action: () => {
let itemsToPlay = {}
@ -279,7 +294,7 @@
{
"icon": "./assets/feather/heart.svg",
"id": "love",
"name": "Love",
"name": this.app.getLz('action.love'),
"hidden": false,
"disabled": true,
"action": function () {
@ -290,7 +305,7 @@
"icon": "./assets/feather/heart.svg",
"id": "unlove",
"active": true,
"name": "Unlove",
"name": this.app.getLz('action.unload'),
"hidden": true,
"action": function () {
app.unlove(self.item)
@ -299,7 +314,7 @@
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "dislike",
"name": "Dislike",
"name": this.app.getLz('action.dislike'),
"hidden": false,
"disabled": true,
"action": function () {
@ -309,7 +324,7 @@
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "undo_dislike",
"name": "Undo dislike",
"name": this.app.getLz('action.undoDislike'),
"active": true,
"hidden": true,
"action": function () {
@ -321,7 +336,7 @@
{
"id": "addToLibrary",
"icon": "./assets/feather/plus.svg",
"name": "Add to library",
"name": this.app.getLz('action.addToLibrary'),
"hidden": false,
"disabled": true,
"action": function () {
@ -331,7 +346,7 @@
{
"id": "removeFromLibrary",
"icon": "./assets/feather/x-circle.svg",
"name": "Remove from library",
"name": app.getLz('action.removeFromLibrary'),
"hidden": true,
"action": function () {
self.removeFromLibrary()
@ -339,13 +354,13 @@
},
{
"icon": "./assets/feather/list.svg",
"name": "Add to Playlist...",
"name": app.getLz('action.addToPlaylist'),
"action": function () {
app.promptAddToPlaylist()
}
},
{
"name": "Play Next",
"name": app.getLz('action.playNext'),
"icon": "./assets/arrow-bend-up.svg",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
@ -354,7 +369,7 @@
}
},
{
"name": "Play Later",
"name": app.getLz('action.playLater'),
"icon": "./assets/arrow-bend-down.svg",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
@ -364,7 +379,7 @@
},
{
"icon": "./assets/feather/radio.svg",
"name": "Start Radio",
"name": app.getLz('action.startRadio'),
"action": function () {
app.mk.setStationQueue({song: self.item.attributes.playParams.id ?? self.item.id}).then(() => {
app.mk.play()
@ -374,25 +389,25 @@
},
{
"icon": "./assets/feather/user.svg",
"name": "Go to Artist",
"name": app.getLz('action.goToArtist'),
"action": function () {
app.searchAndNavigate(self.item, 'artist')
}
},
{
"icon": "./assets/feather/disc.svg",
"name": "Go to Album",
"name": app.getLz('action.goToAlbum'),
"action": function () {
app.searchAndNavigate(self.item, 'album')
}
},
{
"icon": "./assets/feather/share.svg",
"name": "Share",
"name": app.getLz('action.share'),
"action": function () {
if (!self.item.attributes.url && self.item.relationships){
if (self.item.relationships.catalog){
app.mkapi(self.item.attributes.playParams.kind, false, self.item.relationships.catalog.data[0].id).then(u => {self.app.copyToClipboard((u.length && u.length > 0)? u[0].attributes.url : u.attributes.url)})
app.mkapi(self.item.attributes.playParams.kind, false, self.item.relationships.catalog.data[0].id).then(u => {self.app.copyToClipboard((u.data.data.length && u.data.data.length > 0)? u.data.data[0].attributes.url : u.data.data.attributes.url)})
}
}else {
self.app.copyToClipboard(self.item.attributes.url)}
@ -502,22 +517,21 @@
app.mk.setQueue({[truekind]: [item.attributes.playParams.id ?? item.id]}).then(function () {
app.mk.play().then(function (){
var playlistId = id
function getPlaylist(id, params, isLibrary){
function getPlaylist(id, isLibrary){
if (isLibrary){
return app.mk.api.library.playlist(id, params)
} else { return app.mk.api.playlist(id, params)}
return this.app.mk.api.v3.music(`/v1/me/library/playlists/${id}`)
} else { return this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/playlists/${id}`)}
}
try {
getPlaylist(id, params, isLibrary).then(res => {
getPlaylist(id, isLibrary).then(res => {
//let query = res.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
//if (app.mk.shuffleMode == 1){shuffleArray(query); }
// console.log(query)
// app.mk.queue.append(query)
if (!res.relationships.tracks.next) {
if (!res.data.relationships.tracks.next) {
return
} else {
getPlaylistTracks(res.relationships.tracks.next)
getPlaylistTracks(res.data.relationships.tracks.next)
}
function getPlaylistTracks(next) {

View file

@ -1,6 +1,7 @@
<script type="text/x-template" id="mediaitem-scroller-horizontal">
<template>
<div class="cd-hmedia-scroller" :class="kind">
<slot></slot>
<mediaitem-square :kind="kind" :item="item"
v-for="item in items"></mediaitem-square>
</div>
@ -13,7 +14,7 @@
props: {
'items': {
type: Array,
required: true
required: false
},
'kind': {
type: String,

View file

@ -122,7 +122,11 @@
}
let kind = this.item.attributes.playParams.kind ?? this.item.type ?? '';
var truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.library.remove({[truekind]: id})
app.mk.api.v3.music(`v1/me/library/${truekind}/${id.toString()}`,{},
{
fetchOptions: {
method: "DELETE"
}})
this.addedToLibrary = true
},
async contextMenu(event) {
@ -140,7 +144,7 @@
multiple: {
items: [
{
name: `Play ${app.selectedMediaItems.length} tracks next`,
name: this.$root.getLz('action.playTracksNext').replace("${app.selectedMediaItems.length}", app.selectedMediaItems.length),
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
@ -161,7 +165,7 @@
}
},
{
name: `Play ${app.selectedMediaItems.length} tracks later`,
name: app.getLz('action.playTracksLater').replace("${app.selectedMediaItems.length}", app.selectedMediaItems.length),
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
@ -225,7 +229,7 @@
}
},
{
"name": "Share",
"name": this.$root.getLz('term.share'),
"action": function () {
self.app.copyToClipboard(self.item.attributes.url)
}

View file

@ -96,6 +96,7 @@
if (this.item.type && !this.item.type.includes("library")) {
var params = {"fields[playlists]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library", "extend": this.revisedRandId()}
var res = await app.mkapi(this.item.attributes.playParams.kind ?? this.item.type, this.item.attributes.playParams.isLibrary ?? false, this.item.attributes.playParams.id ?? this.item.id, params);
res = res.data.data[0]
this.addedToLibrary = (res && res.attributes && res.attributes.inLibrary) ? res.attributes.inLibrary : false
} else {
this.addedToLibrary = true
@ -105,12 +106,17 @@
var params = {"fields[playlists]": "inLibrary","fields[songs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library", "extend": this.revisedRandId()}
var id = this.item.id ?? this.item.attributes.playParams.id
var res = await app.mkapi(this.item.attributes.playParams.kind ?? this.item.type, this.item.attributes.playParams.isLibrary ?? false, this.item.attributes.playParams.id ?? this.item.id, params);
res = res.data.data[0]
if (res && res.relationships && res.relationships.library && res.relationships.library.data && res.relationships.library.data.length > 0) {
id = res.relationships.library.data[0].id
}
let kind = this.item.attributes.playParams.kind ?? this.item.type ?? '';
var truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.library.remove({[truekind]: id})
app.mk.api.v3.music(`v1/me/library/${truekind}/${id.toString()}`,{},
{
fetchOptions: {
method: "DELETE"
}})
this.addedToLibrary = true
},
subtitleSearchNavigate(item) {
@ -152,7 +158,7 @@
multiple: {
items: [
{
name: `Play ${app.selectedMediaItems.length} tracks next`,
name: app.getLz('action.playTracksNext').replace("${app.selectedMediaItems.length}", app.selectedMediaItems.length),
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
@ -173,7 +179,7 @@
}
},
{
name: `Play ${app.selectedMediaItems.length} tracks later`,
name: app.getLz('action.playTracksLater').replace("${app.selectedMediaItems.length}", app.selectedMediaItems.length),
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {

View file

@ -11,6 +11,7 @@
:video="(item.attributes != null && item.attributes.editorialVideo != null) ? (item.attributes.editorialVideo.motionDetailSquare ? item.attributes.editorialVideo.motionDetailSquare.video : (item.attributes.editorialVideo.motionSquareVideo1x1 ? item.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' "
:size="size"
shadow="subtle"
:bgcolor="getBgColor()"
:type="item.type"></mediaitem-artwork>
</div>
<button class="menu-btn" v-if="!nomenu.includes(item.type)"
@ -75,22 +76,8 @@
},
methods: {
getBgColor() {
let color = `#${(this.item.attributes.artwork != null && this.item.attributes.artwork.bgColor != null) ? (this.item.attributes.artwork.bgColor) : `333333`}`
let c = color.substring(1); // strip #
var rgb = parseInt(c, 16); // convert rrggbb to decimal
var r = (rgb >> 16) & 0xff; // extract red
var g = (rgb >> 8) & 0xff; // extract green
var b = (rgb >> 0) & 0xff; // extract blue
var luma = 0.2126 * r + 0.7152 * g + 0.0722 * b; // per ITU-R BT.709
if (luma > 140) {
return "#aaaaaa"
}else{
return color
}
let color = `#${(this.item.attributes.artwork != null && this.item.attributes.artwork.bgColor != null) ? (this.item.attributes.artwork.bgColor) : ``}`
return color
},
getSubtitle() {
if(this.kind == 'card') {
@ -145,8 +132,8 @@
let friends = this.badges[id]
if (friends) {
friends.forEach(function (friend) {
self.app.mk.api.socialProfile(friend).then(data => {
self.itemBadges.push(data)
self.app.mk.api.v3.music(`/v1/social/${app.mk.storefrontId}/social-profiles/${friend}`).then(data => {
self.itemBadges.push(data.data.data[0])
})
})
}
@ -164,6 +151,7 @@
"extend": this.revisedRandId()
}
var res = await app.mkapi(this.item.attributes.playParams.kind ?? this.item.type, this.item.attributes.playParams.isLibrary ?? false, this.item.attributes.playParams.id ?? this.item.id, params);
res = res.data.data[0]
this.addedToLibrary = (res && res.attributes && res.attributes.inLibrary) ? res.attributes.inLibrary : false
} else {
this.addedToLibrary = true
@ -179,12 +167,17 @@
}
var id = this.item.id ?? this.item.attributes.playParams.id
var res = await app.mkapi(this.item.attributes.playParams.kind ?? this.item.type, this.item.attributes.playParams.isLibrary ?? false, this.item.attributes.playParams.id ?? this.item.id, params);
res= res.data.data[0]
if (res && res.relationships && res.relationships.library && res.relationships.library.data && res.relationships.library.data.length > 0) {
id = res.relationships.library.data[0].id
}
let kind = this.item.attributes.playParams.kind ?? this.item.type ?? '';
var truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.library.remove({[truekind]: id})
app.mk.api.v3.music(`v1/me/library/${truekind}/${id.toString()}`,{},
{
fetchOptions: {
method: "DELETE"
}})
this.addedToLibrary = true
},
uuidv4() {
@ -229,6 +222,7 @@
case "music-videos":
case "uploadedVideo":
case "uploaded-videos":
case "library-music-videos":
return "mediaitem-video";
break;
}
@ -257,7 +251,7 @@
multiple: {
items: [
{
name: `Play ${app.selectedMediaItems.length} tracks next`,
name: app.getLz('action.playTracksNext').replace("${app.selectedMediaItems.length}", app.selectedMediaItems.length),
"icon": "./assets/arrow-bend-up.svg",
action: () => {
let itemsToPlay = {}
@ -279,7 +273,7 @@
}
},
{
name: `Play ${app.selectedMediaItems.length} tracks later`,
name: app.getLz('action.playTracksLater').replace("${app.selectedMediaItems.length}", app.selectedMediaItems.length),
"icon": "./assets/arrow-bend-down.svg",
action: () => {
let itemsToPlay = {}
@ -306,7 +300,7 @@
{
"icon": "./assets/feather/heart.svg",
"id": "love",
"name": "Love",
"name": app.getLz('action.love'),
"hidden": false,
"disabled": true,
"action": function () {
@ -317,7 +311,7 @@
"icon": "./assets/feather/heart.svg",
"id": "unlove",
"active": true,
"name": "Unlove",
"name": app.getLz('action.unlove'),
"hidden": true,
"action": function () {
app.unlove(self.item)
@ -326,7 +320,7 @@
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "dislike",
"name": "Dislike",
"name": app.getLz('action.dislike'),
"hidden": false,
"disabled": true,
"action": function () {
@ -336,7 +330,7 @@
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "undo_dislike",
"name": "Undo dislike",
"name": app.getLz('action.undoDislike'),
"active": true,
"hidden": true,
"action": function () {
@ -348,7 +342,7 @@
{
"icon": "./assets/feather/list.svg",
"id": "addToPlaylist",
"name": "Add to Playlist...",
"name": app.getLz('action.addToPlaylist'),
"action": function () {
app.promptAddToPlaylist()
}
@ -356,7 +350,7 @@
{
"id": "addToLibrary",
"icon": "./assets/feather/plus.svg",
"name": "Add to library",
"name": app.getLz('action.addToLibrary'),
"hidden": false,
"disabled": true,
"action": function () {
@ -369,7 +363,7 @@
{
"id": "removeFromLibrary",
"icon": "./assets/feather/x-circle.svg",
"name": "Remove from library",
"name": app.getLz('action.removeFromLibrary'),
"hidden": true,
"action": async function () {
console.log("remove");
@ -380,7 +374,7 @@
}
},
{
"name": "Play Next",
"name": app.getLz('action.playNext'),
"icon": "./assets/arrow-bend-up.svg",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
@ -389,7 +383,7 @@
}
},
{
"name": "Play Later",
"name": app.getLz('action.playLater'),
"icon": "./assets/arrow-bend-down.svg",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
@ -399,9 +393,14 @@
},
{
"icon": "./assets/feather/share.svg",
"name": "Share",
"name": app.getLz('action.share'),
"action": function () {
self.app.copyToClipboard(self.item.attributes.url)
if (!self.item.attributes.url && self.item.relationships){
if (self.item.relationships.catalog){
app.mkapi(self.item.attributes.playParams.kind, false, self.item.relationships.catalog.data[0].id).then(u => {self.app.copyToClipboard((u.data.data.length && u.data.data.length > 0)? u.data.data[0].attributes.url : u.data.data.attributes.url)})
}
}else {
self.app.copyToClipboard(self.item.attributes.url)}
}
}
]

View file

@ -0,0 +1,163 @@
<script type="text/x-template" id="mini-view">
<div class="mini-view" tabindex="0">
<div class="background">
</div>
<div class="player-pin" title="Pin to Top" @click="app.pinMiniPlayer()">
<span id="mini-pin">📌</span>
</div>
<div class="player-exit" title="Close" @click="app.miniPlayer(false)">
<svg fill="#323232e3" xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21"
aria-role="presentation" focusable="false">
<path
d="M10.5 21C4.724 21 0 16.275 0 10.5S4.724 0 10.5 0 21 4.725 21 10.5 16.276 21 10.5 21zm-3.543-5.967a.96.96 0 00.693-.295l2.837-2.842 2.85 2.842c.167.167.41.295.693.295.552 0 1.001-.461 1.001-1.012 0-.281-.115-.512-.295-.704L11.899 10.5l2.85-2.855a.875.875 0 00.295-.68c0-.55-.45-.998-1.001-.998a.871.871 0 00-.668.295l-2.888 2.855-2.862-2.843a.891.891 0 00-.668-.281.99.99 0 00-1.001.986c0 .269.116.512.295.678L9.088 10.5l-2.837 2.843a.926.926 0 00-.295.678c0 .551.45 1.012 1.001 1.012z"
fill-rule="nonzero"/>
</svg>
</div>
<div class="col artwork-col">
<div class="artwork" @click="app.miniPlayer(false)">
<mediaitem-artwork
:size="600"
:url="image ?? ''"
></mediaitem-artwork>
</div>
<div class="controls-parents">
<template v-if="app.mkReady()">
<div class="app-playback-controls" @mouseover="app.chrome.progresshover = true"
@mouseleave="app.chrome.progresshover = false" @contextmenu="app.nowPlayingContextMenu">
<div class="playback-info">
<div class="song-name">
{{ app.mk.nowPlayingItem["attributes"]["name"] }}
</div>
<div
style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap; margin-top: 0.25vh; overflow: hidden; margin-bottom: 5px;">
<div class="item-navigate song-artist" style="display: inline-block;"
@click="app.getNowPlayingItemDetailed(`artist`)">
{{ app.mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-artist item-navigate" style="display: inline-block;"
@click="app.getNowPlayingItemDetailed('album')">
{{ (app.mk.nowPlayingItem["attributes"]["albumName"]) ? (" — " +
app.mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div>
</div>
<div class="song-progress">
<div class="song-duration" style="justify-content: space-between; height: 1px; margin-bottom: 1px;"
:style="[app.chrome.progresshover ? {'display': 'flex'} : {'display' : 'none'} ]">
<p style="width: auto">{{ app.convertToMins(app.getSongProgress()) }}</p>
<p style="width: auto">{{ app.convertToMins(app.mk.currentPlaybackDuration) }}</p>
</div>
<input type="range" step="0.01" min="0" :style="app.progressBarStyle()"
@input="app.playerLCD.desiredDuration = $event.target.value;app.playerLCD.userInteraction = true"
@mouseup="app.mk.seekToTime($event.target.value);app.playerLCD.desiredDuration = 0;app.playerLCD.userInteraction = false"
:max="app.mk.currentPlaybackDuration" :value="app.getSongProgress()">
</div>
</div>
<div class="control-buttons">
<div class="app-chrome-item">
<button class="playback-button--small shuffle" v-if="app.mk.shuffleMode == 0"
@click="app.mk.shuffleMode = 1"></button>
<button class="playback-button--small shuffle active" v-else
@click="app.mk.shuffleMode = 0"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button previous" @click="app.prevButton()"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button pause" @click="app.mk.pause()" v-if="app.mk.isPlaying"></button>
<button class="playback-button play" @click="app.mk.play()" v-else></button>
</div>
<div class="app-chrome-item">
<button class="playback-button next" @click="app.mk.skipToNextItem()"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button--small repeat" v-if="app.mk.repeatMode == 0"
@click="app.mk.repeatMode = 1"></button>
<button class="playback-button--small repeat repeatOne" @click="app.mk.repeatMode = 2"
v-else-if="app.mk.repeatMode == 1"></button>
<button class="playback-button--small repeat active" @click="app.mk.repeatMode = 0"
v-else-if="app.mk.repeatMode == 2"></button>
</div>
</div>
<div class="app-chrome-item volume display--large">
<div class="input-container">
<button class="volume-button--small volume" @click="app.muteButtonPressed()" :class="{'active': app.cfg.audio.volume == 0}"></button>
<input type="range" class="slider" @wheel="app.volumeWheel" step="0.01" min="0" max="1" v-model="app.mk.volume"
v-if="typeof app.mk.volume != 'undefined'" @change="app.checkMuteChange()">
</div>
</div>
</div>
</template>
</div>
</div>
<!-- <div class="row fs-row">
<div class="col right-col" v-if="tabMode != ''">
<div class="fs-info">
<div>Name</div>
<div>Name</div>
<div>Name</div>
</div>
<div class="lyrics-col" v-if="tabMode == 'lyrics'">
<lyrics-view :yoffset="120" :time="time" :lyrics="lyrics"
:richlyrics="richlyrics"></lyrics-view>
</div>
<div class="queue-col" v-if="tabMode == 'queue'">
<cider-queue v-if="tabMode == 'queue'" ref="queue" ></cider-queue>
</div>
</div>
</div> -->
<!-- <div class="tab-toggles">
<div class="lyrics" :class="{active: tabMode == 'lyrics'}" @click="tabMode = (tabMode == 'lyrics') ? '' : 'lyrics'"></div>
<div class="queue" :class="{active: tabMode == 'queue'}" @click="tabMode = (tabMode == 'queue') ? '' :'queue'"></div>
</div> -->
</div>
</script>
<script>
Vue.component('mini-view', {
template: '#mini-view',
props: {
time: {
type: Number,
required: false
},
lyrics: {
type: Array,
required: false
},
richlyrics: {
type: Array,
required: false
},
image: {
type: String,
required: false
},
},
data: function () {
return {
app: this.$root,
tabMode: "",
}
},
beforeMount() {
window.addEventListener('keyup', this.onEscapeKeyUp);
},
beforeDestroy() {
window.removeEventListener('keyup', this.onEscapeKeyUp)
},
methods: {
onEscapeKeyUp(event) {
if (event.which === 27) {
app.miniPlayer(false);
console.log('js')
}
},
}
});
</script>

View file

@ -0,0 +1,34 @@
<script type="text/x-template" id="qrcode-modal">
<div class="modal-fullscreen spatialproperties-panel">
<div class="modal-window" >
<div class="modal-header">
<div class="modal-title">{{`Web Remote QR : ` + url }}</div>
<button class="close-btn" @click="close()"></button>
</div>
<div class="modal-content">
<img class="qrimg" :src="src"/>
</div>
</div>
</div>
</script>
<script>
Vue.component('qrcode-modal', {
template: '#qrcode-modal',
data: function () {
return {
app: this.$root,
}
},
props: ["src","url"],
mounted() {
},
methods: {
close() {
app.resetState()
},
}
});
</script>

View file

@ -48,7 +48,7 @@
let self = this
CiderContextMenu.Create(event, {
items: [{
"name": "Remove from queue",
"name": $root.getLz('action.removeFromQueue'),
"action": function () {
}

View file

@ -2,10 +2,10 @@
<div class="queue-panel">
<div class="row">
<div class="col">
<h3 class="queue-header-text">Queue</h3>
<h3 class="queue-header-text">{{app.getLz('term.queue')}}</h3>
</div>
<div class="col-auto flex-center">
<button class="autoplay" :style="{'background': app.mk.autoplayEnabled ? 'var(--keyColor)' : ''}" @click="app.mk.autoplayEnabled = !app.mk.autoplayEnabled"></button>
<button class="autoplay" :style="{'background': app.mk.autoplayEnabled ? 'var(--keyColor)' : ''}" @click="app.mk.autoplayEnabled = !app.mk.autoplayEnabled"> <img class="infinity"></button>
</div>
</div>
<div class="queue-body">
@ -33,7 +33,7 @@
</draggable>
</div>
<div class="queue-footer">
<button class="md-btn" style="width:100%;" v-if="queueItems.length > 1" @click="app.mk.clearQueue();updateQueue()">Clear All</button>
<button class="md-btn" style="width:100%;" v-if="queueItems.length > 1" @click="app.mk.clearQueue();updateQueue()">{{app.getLz('term.clearAll')}}</button>
</div>
</div>
</script>
@ -76,7 +76,7 @@
let menus = {
single: {
items: [{
"name": "Remove from queue",
"name": app.getLz('action.removeFromQueue'),
"action": function () {
self.queueItems.splice(position, 1)
app.mk.queue._queueItems = self.queueItems;
@ -84,7 +84,7 @@
}
},
{
"name": "Start Radio",
"name": app.getLz('action.startRadio'),
"action": function () {
app.mk.setStationQueue({
song: item.attributes.playParams.id ?? item.id
@ -97,7 +97,7 @@
},
multiple: {
items: [{
"name": `Remove ${self.selectedItems.length} tracks from queue`,
"name": app.getLz('action.removeTracks'),
"action": function () {
// add property to items to be removed
self.selectedItems.forEach(function (item) {

View file

@ -7,7 +7,7 @@
@dragover="dragOver"
@drop="onDrop"
:href="item.href"
@click='item.type != "library-playlist-folders" ? openPlaylist(item) : getPlaylistChildren(item)'>
@click='clickEvent()'>
<template v-if="!renaming">
<div class="sidebar-icon" v-html="icon"></div> {{ item.attributes.name }}
</template>
@ -15,7 +15,7 @@
</button>
<div class="folder-body" v-if="item.type === 'library-playlist-folders' && folderOpened">
<template v-if="children.length != 0">
<sidebar-playlist v-for="item in children" :item="item" :key="item.id"></sidebar-playlist>
<sidebar-playlist v-for="item in children" :playlist-select="playlistSelect" :item="item" :key="item.id"></sidebar-playlist>
</template>
<template v-else>
<div class="spinner"></div>
@ -31,6 +31,10 @@
item: {
type: Object,
required: true
},
playlistSelect: {
type: Function,
required: false
}
},
data: function () {
@ -50,6 +54,17 @@
}
},
methods: {
clickEvent() {
if(this.item.type != "library-playlist-folders") {
if(this.playlistSelect) {
this.playlistSelect(this.item)
}else{
this.openPlaylist(this.item)
}
}else{
this.getPlaylistChildren(this.item)
}
},
rename() {
this.renaming = false
@ -105,7 +120,7 @@
let menu = {
items: {
"moveToParent": {
name: "Move to top",
name: this.$root.getLz('action.moveToTop'),
action: () => {
let self = this
this.move(this.item, {
@ -116,7 +131,7 @@
}
},
"rename": {
name: "Rename",
name: this.$root.getLz('action.rename'),
action: () => {
this.renaming = true
setTimeout(()=>{
@ -126,13 +141,13 @@
}
},
"deleteFromPlaylist": {
name: "Delete from library",
name: this.$root.getLz('action.removeFromLibrary'),
action: () => {
this.$root.deletePlaylist(playlist_id)
}
},
"addToFavorites": {
name: "Add to favorites",
name: this.$root.getLz('action.addToFavorites'),
disabled: true,
hidden: true,
action: () => {
@ -181,7 +196,9 @@
this.children = []
this.getChildren()
this.toggleFolder()
this.$root.mk.api.library.playlistFolderChildren(item.id).then(children => {
this.$root.mk.api.v3.music(`v1/me/library/playlist-folders/${item.id}/children`).then(data => {
let children = data.data.data;
children.forEach(child => {
if(!self.$root.playlists.listing.find(listing => listing.id == child.id)) {
child.parent = self.item.id

View file

@ -2,15 +2,15 @@
<div class="modal-fullscreen spatialproperties-panel">
<div class="modal-window" v-if="ready">
<div class="modal-header">
<div class="modal-title">Spatial Properties</div>
<div class="modal-title">{{$root.getLz('spatial.spatialProperties')}}</div>
<button class="close-btn" @click="close()"></button>
</div>
<div class="modal-content">
<template v-if="roomEditType == 'dimensions'">
<div class="row">
<div class="col"><h3>Room Dimensions</h3></div>
<div class="col"><h3>{{$root.getLz('spatial.roomDimensions')}}</h3></div>
<div class="col-auto flex-center">
<button class="md-btn" @click="roomEditType = 'positions'">Set Positions</button>
<button class="md-btn" @click="roomEditType = 'positions'">{{$root.getLz('spatial.setPositions')}}</button>
</div>
</div>
<div class="row">
@ -18,7 +18,7 @@
<div class="row">
<div class="col-3 flex-center">
Width
{{$root.getLz('spatial.width')}}
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
@ -32,7 +32,7 @@
<div class="row">
<div class="col-3 flex-center">
Height
{{$root.getLz('spatial.height')}}
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
@ -46,7 +46,7 @@
<div class="row">
<div class="col-3 flex-center">
Depth
{{$root.getLz('spatial.depth')}}
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
@ -58,7 +58,7 @@
</div>
</div>
<label v-if="!app.cfg.audio.normalization">
Gain
{{$root.getLz('spatial.gain')}}
<input type="number" min="0" @change="setRoom()" style="width: 100%;"
v-model="app.cfg.audio.spatial_properties.gain" step="0.1"/>
</label>
@ -73,9 +73,9 @@
</template>
<template v-if="roomEditType == 'positions'">
<div class="row">
<div class="col"><h3>Room Positions</h3></div>
<div class="col"><h3>{{$root.getLz('spatial.roomPositions')}}</h3></div>
<div class="col-auto flex-center">
<button class="md-btn" @click="roomEditType = 'dimensions'">Set Dimensions</button>
<button class="md-btn" @click="roomEditType = 'dimensions'">{{$root.getLz('spatial.setDimensions')}}</button>
</div>
</div>
<div class="row">
@ -83,7 +83,7 @@
<div class="row">
<div class="col-3 flex-center">
X (Listener)
X ({{$root.getLz('spatial.listener')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
@ -97,7 +97,7 @@
<div class="row">
<div class="col-3 flex-center">
Y (Listener)
Y ({{$root.getLz('spatial.listener')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
@ -111,7 +111,7 @@
<div class="row">
<div class="col-3 flex-center">
Z (Listener)
Z ({{$root.getLz('spatial.listener')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
@ -125,7 +125,7 @@
<div class="row">
<div class="col-3 flex-center">
X (Audio Source)
X ({{$root.getLz('spatial.audioSource')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
@ -139,7 +139,7 @@
<div class="row">
<div class="col-3 flex-center">
Y (Audio Source)
Y ({{$root.getLz('spatial.audioSource')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
@ -153,7 +153,7 @@
<div class="row">
<div class="col-3 flex-center">
Z (Audio Source)
Z ({{$root.getLz('spatial.audioSource')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
@ -179,13 +179,13 @@
</template>
<div class="row">
<div class="col"><h3>Room Materials</h3></div>
<div class="col"><h3>{{$root.getLz('spatial.roomMaterials')}}</h3></div>
</div>
<div class="row">
<div class="col"></div>
<div class="col flex-center">
<label>
Up
{{$root.getLz('spatial.up')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.up">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>
@ -197,7 +197,7 @@
<div class="row">
<div class="col flex-center">
<label>
Left
{{$root.getLz('spatial.left')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.left">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>
@ -206,14 +206,14 @@
</div>
<div class="col flex-center">
<label>
Front
{{$root.getLz('spatial.front')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.front">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>
</select>
</label>
<label>
Back
{{$root.getLz('spatial.back')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.back">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>
@ -222,7 +222,7 @@
</div>
<div class="col flex-center">
<label>
Right
{{$root.getLz('spatial.right')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.right">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>
@ -234,7 +234,7 @@
<div class="col"></div>
<div class="col flex-center">
<label>
Down
{{$root.getLz('spatial.down')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.down">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>

View file

@ -16,17 +16,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Cider</title>
<link rel="stylesheet/less" type="text/css" href="style.less"/>
<script src="less.js"></script>
<script src="<%- (env.dev ? "vue.js" : "vue.dev.js") %>"></script>
<script src="vuex.min.js"></script>
<script src="sortable.min.js"></script>
<script src="vue-observe-visibility.min.js"></script>
<script src="sortable.min.js"></script>
<script src="vibrant.min.js"></script>
<script src="vuedraggable.umd.min.js"></script>
<script src="./js/less.js"></script>
<script src="<%- (env.dev ? "./js/vue.js" : "./js/vue.dev.js") %>"></script>
<script src="./js/vuex.min.js"></script>
<script src="./js/sortable.min.js"></script>
<script src="./js/vue-observe-visibility.min.js"></script>
<script src="./js/vuedraggable.umd.min.js"></script>
<link rel="manifest" href="./manifest.json?v=2">
<script src="https://js-cdn.music.apple.com/hls.js/2.141.0/hls.js/hls.js"></script>
<script src="hlscider.js"></script>
<script src="./js/jquery-3.2.1.slim.min.js"></script>
<script src="./js/popper.min.js"></script>
<script src="./js/bootstrap.min.js"></script>
<script src="./js/bootbox.min.js"></script>
<script src="./js/notyf.min.js"></script>
</head>
<body oncontextmenu="return false;" loading="1" platform="<%= env.platform %>">
@ -38,9 +41,10 @@
<div class="app-chrome--left">
<div class="app-chrome-item full-height" v-if="chrome.windowControlPosition == 'left'">
<div class="window-controls">
<div class="close" @click="ipcRenderer.send('close')"></div>
<div class="close" @click="closeWindow()"></div>
<div class="minimize" @click="ipcRenderer.send('minimize')"></div>
<div class="minmax restore" v-if="chrome.maximized" @click="ipcRenderer.send('maximize')">
<div class="minmax restore" v-if="chrome.maximized"
@click="ipcRenderer.send('maximize')">
</div>
<div class="minmax" v-else @click="ipcRenderer.send('maximize')"></div>
</div>
@ -67,9 +71,9 @@
<div class="app-chrome-item display--large">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
@click="mk.repeatMode = 1"></button>
<button class="playback-button--small repeat active" @click="mk.repeatMode = 2"
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2"
v-else-if="mk.repeatMode == 1"></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 0"
<button class="playback-button--small repeat active" @click="mk.repeatMode = 0"
v-else-if="mk.repeatMode == 2"></button>
</div>
</div>
@ -83,29 +87,38 @@
</div>
<div class="playback-info">
<div class="song-name" style="-webkit-box-orient: horizontal;"
:style="[mk.nowPlayingItem['attributes']['contentRating'] == 'explicit' ? {'margin-left' : '23px'} : {'margin-left' : '0px'} ]">
:class="[isElementOverflowing('#app-main > div.app-chrome > div.app-chrome--center > div > div > div.playback-info > div.song-name') ? 'marquee' : '']"
:style="[mk.nowPlayingItem['attributes']['contentRating'] == 'explicit' ? {'margin-left' : '23px'} : {'margin-left' : '0px'} ]">
{{ mk.nowPlayingItem["attributes"]["name"] }}
<div class="explicit-icon" v-if="mk.nowPlayingItem['attributes']['contentRating'] == 'explicit'" style="display: inline-block"></div>
<div class="explicit-icon"
v-if="mk.nowPlayingItem['attributes']['contentRating'] == 'explicit'"
style="display: inline-block"></div>
</div>
<div class="song-artist"
<div class="song-artist-album">
<div class="song-artist-album-content"
:class="[isElementOverflowing('#app-main > .app-chrome .app-chrome-item > .app-playback-controls > div >.song-artist-album > .song-artist-album-content') ? 'marquee' : '']"
style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap;">
<div class="item-navigate song-artist" style="display: inline-block;"
<div class="item-navigate song-artist" style="display: inline-block"
@click="getNowPlayingItemDetailed(`artist`)">
{{ mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-artist item-navigate" style="display: inline-block;"
@click="getNowPlayingItemDetailed('album')" v-if="mk.nowPlayingItem['attributes']['albumName'] != ''">
<div class="song-artist item-navigate" style="display: inline-block"
@click="getNowPlayingItemDetailed('album')"
v-if="mk.nowPlayingItem['attributes']['albumName'] != ''">
<div class="separator" style="display: inline-block;">{{"—"}}</div>
{{(mk.nowPlayingItem["attributes"]["albumName"]) ?
(mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div>
</div>
</div>
<div class="song-progress">
<div class="song-duration" style="justify-content: space-between; height: 1px;"
<div class="song-duration"
style="justify-content: space-between; height: 1px;"
:style="[chrome.progresshover ? {'display': 'flex'} : {'display' : 'none'} ]">
<p style="width: auto">{{ convertToMins(getSongProgress()) }}</p>
<p style="width: auto">{{ convertToMins(mk.currentPlaybackDuration) }}</p>
<p style="width: auto">{{ convertToMins(mk.currentPlaybackDuration) }}
</p>
</div>
<input type="range" step="0.01" min="0" :style="progressBarStyle()"
@ -115,11 +128,11 @@
</div>
</div>
<template v-if="mk.nowPlayingItem['attributes']['playParams']">
<div class="actions"
v-if="isInLibrary(mk.nowPlayingItem['attributes']['playParams'])">
❤️
<div class="actions">
<button class="lcdMenu" @click="nowPlayingContextMenu">
<div class="svg-icon"></div>
</button>
</div>
<div class="actions" v-else>🖤</div>
</template>
</div>
@ -129,34 +142,40 @@
</div>
<div class="app-chrome--right">
<div class="app-chrome-item volume display--large">
<div class="app-chrome-item volume-icon"></div>
<input type="range" class="" @wheel="volumeWheel" step="0.01" min="0" max="1"
v-model="mk.volume"
v-if="typeof mk.volume != 'undefined'">
<button class="volume-button--small volume" @click="muteButtonPressed()"
:class="{'active': this.cfg.audio.volume == 0}"></button>
<input type="range" class="" @wheel="volumeWheel" step="0.01" min="0" :max="cfg.audio.maxVolume"
v-model="mk.volume" v-if="typeof mk.volume != 'undefined'" @change="checkMuteChange()">
</div>
<div class="app-chrome-item generic" v-if="false">
<button class="playback-button--small">
<%- include("svg/cast.svg") %>
</button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small miniplayer"
@click="drawer.open = false; miniPlayer(true)"></button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small queue" :class="{'active': drawer.panel == 'queue'}"
@click="invokeDrawer('queue')"></button>
</div>
<div class="app-chrome-item generic">
<template v-if="lyrics && lyrics != [] && lyrics.length > 0">
<button class="playback-button--small lyrics" :class="{'active': drawer.panel == 'lyrics'}"
@click="invokeDrawer('lyrics')"></button>
</template>
<button class="playback-button--small lyrics"
:class="{'active': drawer.panel == 'lyrics'}"
@click="invokeDrawer('lyrics')"></button>
</template>
</div>
<div class="app-chrome-item full-height" v-if="chrome.windowControlPosition == 'right'">
<div class="window-controls">
<div class="minimize" @click="ipcRenderer.send('minimize')"></div>
<div class="minmax restore" v-if="chrome.maximized" @click="ipcRenderer.send('maximize')">
<div class="minmax restore" v-if="chrome.maximized"
@click="ipcRenderer.send('maximize')">
</div>
<div class="minmax" v-else @click="ipcRenderer.send('maximize')"></div>
<div class="close" @click="ipcRenderer.send('close')"></div>
<div class="close" @click="closeWindow()"></div>
</div>
</div>
</div>
@ -169,11 +188,9 @@
<input type="search" spellcheck="false" @click="showSearch()"
@focus="search.showHints = true"
@blur="setTimeout(()=>{search.showHints = false}, 300)"
v-on:keyup.enter="searchQuery();search.showHints = false"
@change="showSearch();" @input="getSearchHints()" placeholder="Search..."
v-model="search.term"
ref="searchInput"
class="search-input">
v-on:keyup.enter="searchQuery();search.showHints = false" @change="showSearch();"
@input="getSearchHints()" :placeholder="$root.getLz('term.search') + '...'" v-model="search.term"
ref="searchInput" class="search-input">
</div>
</div>
<div class="search-hints-container" v-if="search.showHints && search.hints.length != 0">
@ -186,40 +203,53 @@
</div>
<div class="app-sidebar-content">
<div class="app-sidebar-header-text">
Apple Music
Cider
</div>
<sidebar-library-item name="Home" svg-icon="./assets/feather/home.svg"
page="home"></sidebar-library-item>
<sidebar-library-item name="Listen Now" svg-icon="./assets/feather/play-circle.svg"
page="listen_now"></sidebar-library-item>
<sidebar-library-item name="Browse" svg-icon="./assets/feather/globe.svg"
page="browse"></sidebar-library-item>
<sidebar-library-item name="Radio" svg-icon="./assets/feather/radio.svg"
page="radio"></sidebar-library-item>
<sidebar-library-item :name="$root.getLz('home.title')" svg-icon="./assets/feather/home.svg" page="home">
</sidebar-library-item>
<div class="app-sidebar-header-text">
Library
{{$root.getLz('term.appleMusic')}}
</div>
<sidebar-library-item name="Recently Added" svg-icon="./assets/feather/plus-circle.svg"
<sidebar-library-item :name="$root.getLz('term.listenNow')" svg-icon="./assets/feather/play-circle.svg"
page="listen_now"></sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.browse')" svg-icon="./assets/feather/globe.svg" page="browse">
</sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.radio')" svg-icon="./assets/feather/radio.svg" page="radio">
</sidebar-library-item>
<div class="app-sidebar-header-text">
{{$root.getLz('term.library')}}
</div>
<sidebar-library-item :name="$root.getLz('term.recentlyAdded')" svg-icon="./assets/feather/plus-circle.svg"
page="library-recentlyadded"></sidebar-library-item>
<sidebar-library-item name="Songs" svg-icon="./assets/feather/music.svg"
<sidebar-library-item :name="$root.getLz('term.songs')" svg-icon="./assets/feather/music.svg"
page="library-songs"></sidebar-library-item>
<sidebar-library-item name="Albums" svg-icon="./assets/feather/disc.svg"
<sidebar-library-item :name="$root.getLz('term.albums')" svg-icon="./assets/feather/disc.svg"
page="library-albums"></sidebar-library-item>
<sidebar-library-item name="Artists" svg-icon="./assets/feather/user.svg"
<sidebar-library-item :name="$root.getLz('term.artists')" svg-icon="./assets/feather/user.svg"
page="library-artists"></sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.videos')" svg-icon="./assets/feather/video.svg" page="library-videos"></sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.podcasts')" svg-icon="./assets/feather/mic.svg" page="podcasts">
</sidebar-library-item>
<div class="app-sidebar-header-text" @contextmenu="playlistHeaderContextMenu">
Playlists
{{ $root.getLz('term.playlists') }}
</div>
<sidebar-playlist v-for="item in getPlaylistFolderChildren('p.playlistsroot')"
:item="item"></sidebar-playlist>
<sidebar-playlist v-for="item in getPlaylistFolderChildren('p.playlistsroot')" :item="item">
</sidebar-playlist>
</div>
<transition name="wpfade">
<div class="usermenu-container" v-if="chrome.menuOpened">
<div class="usermenu-body">
<button class="usermenu-item" @click="chrome.hideUserInfo = !chrome.hideUserInfo">
<button class="usermenu-item" @click="showWebRemoteQR()">
<div class="row nopadding">
<div class="col nopadding">
Show Personal Info
{{$root.getLz('action.showWebRemoteQR')}}
</div>
</div>
</button>
<button class="usermenu-item" @click="toggleHideUserInfo()">
<div class="row nopadding">
<div class="col nopadding">
{{$root.getLz('settings.option.visual.showPersonalInfo')}}
</div>
<div class="col-auto nopadding" v-if="!chrome.hideUserInfo">
✔️
@ -229,31 +259,35 @@
<button class="usermenu-item" @click="mk.privateEnabled = !mk.privateEnabled">
<div class="row nopadding">
<div class="col nopadding">
Private Session
{{$root.getLz('term.privateSession')}}
</div>
<div class="col-auto nopadding" v-if="mk.privateEnabled">
✔️
</div>
</div>
</button>
<button class="usermenu-item" v-if="cfg.advanced.AudioContext"
@click="modals.equalizer = true">
{{$root.getLz('term.equalizer')}}
</button>
<button class="usermenu-item" v-if="cfg.advanced.AudioContext && cfg.audio.spatial"
@click="modals.spatialProperties = true">
Spatialized Audio Settings
{{$root.getLz('term.spacializedAudioSetting')}}
</button>
<button class="usermenu-item" @click="appRoute('apple-account-settings')">
Account Settings
{{$root.getLz('term.accountSettings')}}
</button>
<button class="usermenu-item" @click="appRoute('about')">
About
{{$root.getLz('term.about')}}
</button>
<button class="usermenu-item" @click="window.open('https://discord.gg/applemusic')">
Discord
{{$root.getLz('term.discord')}}
</button>
<button class="usermenu-item" @click="appRoute('settings')">
Settings
{{$root.getLz('term.settings')}}
</button>
<button class="usermenu-item" @click="mk.unauthorize()">
Sign Out
<button class="usermenu-item" @click="unauthorize()">
{{$root.getLz('term.logout')}}
</button>
</div>
</div>
@ -283,23 +317,23 @@
<div class="app-chrome-item">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
@click="mk.repeatMode = 1"></button>
<button class="playback-button--small repeat active" @click="mk.repeatMode = 2"
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2"
v-else-if="mk.repeatMode == 1"></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 0"
v-else-if="mk.repeatMode == 2"></button>
<button class="playback-button--small repeat active"
@click="mk.repeatMode = 0" v-else-if="mk.repeatMode == 2"></button>
</div>
</div>
<div class="app-chrome-item volume">
<div class="input-container">
<div class="app-chrome-item volume-icon"></div>
<input type="range" class="" @wheel="volumeWheel" step="0.01" min="0" max="1"
v-model="mk.volume"
v-if="typeof mk.volume != 'undefined'">
<button class="volume-button--small volume" @click="muteButtonPressed()"
:class="{'active': this.cfg.audio.volume == 0}"></button>
<input type="range" class="" @wheel="volumeWheel" step="0.01" min="0" :max="cfg.audio.maxVolume"
v-model="mk.volume" v-if="typeof mk.volume != 'undefined'"
@change="checkMuteChange()">
</div>
</div>
</div>
<button class="app-sidebar-button" style="width:100%"
:class="{active: chrome.menuOpened}"
<button class="app-sidebar-button" style="width:100%" :class="{active: chrome.menuOpened}"
@blur="setTimeout(()=>{chrome.menuOpened = false}, 100)"
@click="(chrome.userinfo.id) ? chrome.menuOpened = !chrome.menuOpened : false">
@ -308,9 +342,11 @@
<div class="sidebar-user-text" v-if="!chrome.hideUserInfo">
<template v-if="chrome.userinfo.id">
<div class="fullname text-overflow-elipsis">{{ chrome.userinfo.attributes.name }}
<div class="fullname text-overflow-elipsis">{{ chrome.userinfo.attributes.name
}}
</div>
<div class="handle-text text-overflow-elipsis">@{{ chrome.userinfo.attributes.handle
<div class="handle-text text-overflow-elipsis">{{
chrome.userinfo.attributes.handle
}}
</div>
</template>
@ -325,18 +361,36 @@
</div>
</button>
</div>
<div class="app-sidebar-notification libraryNotification" v-if="library.downloadNotification.show">
<div class="message">{{ library.downloadNotification.message }} ({{
library.downloadNotification.progress }} / {{ library.downloadNotification.total }})
<div class="app-sidebar-notification backgroundNotification"
v-if="library.backgroundNotification.show">
<div class="message">{{ library.backgroundNotification.message }} ({{
library.backgroundNotification.progress }} / {{ library.backgroundNotification.total }})
</div>
</div>
</div>
<div id="app-content">
<div id="navigation-bar">
<button class="nav-item" @click="navigateBack()"><%- include('svg/chevron-left.svg') %></button>
<button class="nav-item"
@click="navigateForward()"><%- include('svg/chevron-right.svg') %></button>
<button class="nav-item" @click="navigateBack()">
<%- include('svg/chevron-left.svg') %>
</button>
<button class="nav-item" @click="navigateForward()">
<%- include('svg/chevron-right.svg') %>
</button>
</div>
<!-- Podcasts -->
<transition name="wpfade">
<template v-if="page == 'podcasts'">
<apple-podcasts></apple-podcasts>
</template>
</transition>
<!-- Library - Library Videos -->
<transition name="wpfade" >
<template v-if="page == 'library-videos'">
<cider-library-videos></cider-library-videos>
</template>
</transition>
<!-- Apple Setings Page -->
<transition name="wpfade">
<template v-if="page == 'apple-account-settings'">
@ -407,53 +461,53 @@
<template v-if="page == 'browse'">
<!-- <div class="content-inner">
<button id="apple-music-authorize" class="md-btn md-btn-primary" @click="init()">Start
MusicKit
</button>
<button id="apple-music-unauthorize" class="md-btn md-btn-primary"
@click="unauthorize()">
Stop
MusicKit
</button>
<button id="apple-music-authorize" class="md-btn md-btn-primary" @click="init()">Start
MusicKit
</button>
<button id="apple-music-unauthorize" class="md-btn md-btn-primary"
@click="unauthorize()">
Stop
MusicKit
</button>
<br>
<template v-if="mk.nowPlayingItem">
currentPlaybackProgress: {{ app.mk.currentPlaybackProgress }}
<br>
<template v-if="mk.nowPlayingItem">
currentPlaybackProgress: {{ app.mk.currentPlaybackProgress }}
<br>
currentPlaybackDuration: {{ app.mk.currentPlaybackDuration }}
</template>
<div><input type="text" v-model="quickPlayQuery">
<button @click="quickPlay(quickPlayQuery)">Play</button>
</div>
<h1 class="header-text">Browse</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, urna eu
tincidunt
consectetur, nisl nunc euismod nisi, eu porttitor nisl nisi euismod nisi.
</p>
<div class="media-item--small">
<div class="artwork">
currentPlaybackDuration: {{ app.mk.currentPlaybackDuration }}
</template>
<div><input type="text" v-model="quickPlayQuery">
<button @click="quickPlay(quickPlayQuery)">Play</button>
</div>
<h1 class="header-text">{{$root.getLz('term.browse')}}</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, urna eu
tincidunt
consectetur, nisl nunc euismod nisi, eu porttitor nisl nisi euismod nisi.
</p>
<div class="media-item--small">
<div class="artwork">
</div>
<div class="text">
Text
</div>
<div class="subtext">
Subtext
</div>
</div>
<br>
<br>
<h1 class="header-text">Listen Now</h1>
<div class="winbox">
<div class="fancy">990kbps</div>
<div class="">
<button class="md-btn md-btn-primary">Audio Quality Settings</button>
</div>
<div class="text">
Text
</div>
<button class="md-btn" @click="drawertest = !drawertest">Toggle Drawer</button>
<button class="md-btn">Button</button>
<button class="md-btn md-btn-primary">Button</button>
</div> -->
<div class="subtext">
Subtext
</div>
</div>
<br>
<br>
<h1 class="header-text">{{$root.getLz('term.listenNow')}}</h1>
<div class="winbox">
<div class="fancy">990kbps</div>
<div class="">
<button class="md-btn md-btn-primary">Audio Quality Settings</button>
</div>
</div>
<button class="md-btn" @click="drawertest = !drawertest">Toggle Drawer</button>
<button class="md-btn">Button</button>
<button class="md-btn md-btn-primary">Button</button>
</div> -->
<cider-browse :data="browsepage"></cider-browse>
</template>
</transition>
@ -467,8 +521,8 @@
<transition v-on:enter="getRadioStations()" name="wpfade">
<template v-if="page == 'radio'" @created="console.log('radio')">
<div class="content-inner">
<h1 class="header-text">Radio</h1>
<h3>Recent Stations</h3>
<h1 class="header-text">{{$root.getLz('term.radio')}}</h1>
<h3>{{$root.getLz('term.recentStations')}}</h3>
<mediaitem-square :item="item" v-for="item in radio.personal"></mediaitem-square>
</div>
</template>
@ -522,7 +576,8 @@
</div>
<transition name="drawertransition">
<div class="app-drawer" v-if="drawer.open && drawer.panel == 'lyrics' && lyrics && lyrics != [] && lyrics.length > 0">
<div class="app-drawer"
v-if="drawer.open && drawer.panel == 'lyrics' && lyrics && lyrics != [] && lyrics.length > 0">
<div class="bgArtworkMaterial">
<div class="bg-artwork-container">
<img class="bg-artwork a" :src="$store.state.artwork.playerLCD">
@ -533,7 +588,7 @@
:richlyrics="richlyrics"></lyrics-view>
<div v-if="drawer.panel == 'lyrics'" class="lyric-footer">
<button class="md-btn" @click="modularUITest(!fullscreenLyrics)">{{fullscreenLyrics ?
"Default View":'Fullscreen View'}}
$root.getLz('term.defaultView'): $root.getLz('term.fullscreenView')}}
</button>
</div>
</div>
@ -543,25 +598,28 @@
<cider-queue ref="queue" v-if="drawer.panel == 'queue'"></cider-queue>
</div>
</transition>
<cider-menu-panel v-if="menuPanel.visible">
</cider-menu-panel>
</div>
</div>
</transition>
<cider-menu-panel v-if="menuPanel.visible">
</cider-menu-panel>
<transition name="fsModeSwitch">
<div class="fullscreen-view-container" v-if="appMode == 'fullscreen'">
<fullscreen-view :image="currentArtUrl.replace('50x50', '600x600')" :time="lyriccurrenttime"
:lyrics="lyrics" :richlyrics="richlyrics"></fullscreen-view>
</div>
</transition>
<transition name="fsModeSwitch">
<div class="fullscreen-view-container" v-if="appMode == 'mini'">
<mini-view :image="currentArtUrl.replace('50x50', '600x600')" :time="lyriccurrenttime"
:lyrics="lyrics" :richlyrics="richlyrics"></mini-view>
</div>
</transition>
<transition name="wpfade">
<div class="bg-artwork-container"
<div class="bg-artwork-container" v-if="cfg.visual.window_background_style == 'artwork'"
:class="{noanimation: (!cfg.visual.bg_artwork_rotation || !animateBackground)}">
<img @load="chrome.artworkReady = true" class="bg-artwork a "
>
<img class="bg-artwork b"
>
<img @load="chrome.artworkReady = true" class="bg-artwork a ">
<img class="bg-artwork b">
</div>
</transition>
<transition name="wpfade">
@ -573,6 +631,12 @@
<transition name="modal">
<spatial-properties v-if="modals.spatialProperties"></spatial-properties>
</transition>
<transition name="modal">
<eq-view v-if="modals.equalizer"></eq-view>
</transition>
<transition name="modal">
<qrcode-modal v-if="modals.qrcode" :src="webremoteqr" :url="webremoteurl"></qrcode-modal>
</transition>
<div id="apple-music-video-container">
<div id="apple-music-video-player-controls">
<div id="player-exit" title="Close" @click="exitMV()">
@ -585,9 +649,11 @@
</div>
<div id="captions">{{((lyricon) ? ((lyrics.length > 0 && lyrics[currentLyricsLine] &&
lyrics[currentLyricsLine].line ) ?
lyrics[currentLyricsLine].line.replace('lrcInstrumental','') : "") : '') + ((lyricon) ? ((lyrics.length
lyrics[currentLyricsLine].line.replace('lrcInstrumental','') : "") : '') + ((lyricon) ?
((lyrics.length
> 0 && lyrics[currentLyricsLine] && lyrics[currentLyricsLine].line ) ?
(lyrics[currentLyricsLine].translation ? ('\n\r' + lyrics[currentLyricsLine].translation) : ""): "") :
(lyrics[currentLyricsLine].translation ? ('\n\r' + lyrics[currentLyricsLine].translation) : ""): "")
:
'')}}
</div>
<div id="player-pip"
@ -605,6 +671,9 @@
</div>
</div>
<!-- Apple Settings Page -->
<%- include('pages/podcasts') %>
<!-- Apple Settings Page -->
<%- include('pages/apple-account-settings') %>
<!-- Library - Songs -->
@ -648,12 +717,17 @@
<!-- About -->
<%- include('pages/about') %>
<script type="text/x-template" id="am-musiccovershelf">
<%- include('pages/library-videos') %>
<script type="text/x-template"
id="am-musiccovershelf">
<h1>{{ component.attributes.title.stringForDisplay }}</h1>
</script>
<!-- Sidebar Item -->
<script type="text/x-template" id="sidebar-library-item">
<script type="text/x-template"
id="sidebar-library-item">
<button class="app-sidebar-item"
:class="$parent.getSidebarItemClass(page)" @click="$root.appRoute(page)">
<div class="sidebar-icon" v-html="svgIconData" v-if="svgIconData != ''"></div>
@ -661,56 +735,95 @@
</button>
</script>
<!-- Artwork Material -->
<%- include('components/artwork-material') %>
<!-- Menu Panel -->
<%- include('components/menu-panel') %>
<!-- Playlist Listing -->
<%- include('components/sidebar-playlist') %>
<%- include('components/sidebar-playlist')
%>
<!-- Spatial Properties -->
<%- include('components/spatial-properties') %>
<%- include('components/spatial-properties')
%>
<!-- QRCode Modal -->
<%- include('components/qrcode-modal')
%>
<!-- Equalizer -->
<%- include('components/equalizer')
%>
<!-- Add to playlist -->
<%- include('components/add-to-playlist') %>
<%- include('components/add-to-playlist')
%>
<!-- Queue -->
<%- include('components/queue') %>
<%- include('components/queue')
%>
<!-- Queue Item -->
<%- include('components/queue-item') %>
<%- include('components/queue-item')
%>
<!-- Horizontal MediaItem Scroller -->
<%- include('components/mediaitem-scroller-horizontal') %>
<%- include('components/mediaitem-scroller-horizontal')
%>
<!-- Horizontal MediaItem Scroller (Large) -->
<%- include('components/mediaitem-scroller-horizontal-large') %>
<%- include('components/mediaitem-scroller-horizontal-large')
%>
<!-- Horizontal MediaItem Scroller (SP : Special) -->
<%- include('components/mediaitem-scroller-horizontal-sp') %>
<%- include('components/mediaitem-scroller-horizontal-sp')
%>
<!-- Horizontal MediaItem Scroller (MV) -->
<%- include('components/mediaitem-scroller-horizontal-mvview') %>
<%- include('components/mediaitem-scroller-horizontal-mvview')
%>
<!-- MediaItem List Item -->
<%- include('components/mediaitem-list-item') %>
<%- include('components/mediaitem-list-item')
%>
<!-- MediaItem Horizontal Rectangle -->
<%- include('components/mediaitem-hrect') %>
<%- include('components/mediaitem-hrect')
%>
<!-- MediaItem Square -->
<%- include('components/mediaitem-square') %>
<%- include('components/mediaitem-square')
%>
<!-- MediaItem Square SP -->
<%- include('components/mediaitem-square-sp') %>
<%- include('components/mediaitem-square-sp')
%>
<!-- MediaItem MusicVideo -->
<%- include('components/mediaitem-mvview') %>
<%- include('components/mediaitem-mvview')
%>
<!-- MediaItem MusicVideo -->
<%- include('components/libraryartist-item') %>
<%- include('components/listennow-child') %>
<%- include('components/libraryartist-item')
%>
<%- include('components/listennow-child')
%>
<!-- MediaItem MusicVideo SP -->
<%- include('components/mediaitem-mvview-sp') %>
<%- include('components/mediaitem-mvview-sp')
%>
<!-- Animated Artwork View -->
<%- include('components/animatedartwork-view') %>
<%- include('components/animatedartwork-view')
%>
<!-- Lyrics View -->
<%- include('components/lyrics-view') %>
<%- include('components/lyrics-view')
%>
<!-- Fullscreen View -->
<%- include('components/fullscreen') %>
<%- include('components/fullscreen')
%>
<script src="musickit.js?v=1"></script>
<!-- Miniplayer View -->
<%- include('components/miniplayer')
%>
<script
src="musickit.js?v=1"></script>
<script>
if (typeof MusicKit == 'undefined') {
document.write(unescape("%3Cscript src='https://js-cdn.music.apple.com/musickit/v2/amp/musickit.js' type='text/javascript'%3E%3C/script%3E"));
}
</script>
<script src="index.js?v=1"></script>
<script src="https://cdn.jsdelivr.net/npm/resonance-audio/build/resonance-audio.min.js"></script>
<script src="/audio/audio.js?v=1"></script>
<script
src="index.js?v=1"></script>
<script
src="https://cdn.jsdelivr.net/npm/resonance-audio/build/resonance-audio.min.js"></script>
<script
src="/audio/audio.js?v=1"></script>
<script
src="./js/WSAPI_Interop.js"></script>
</body>
</html>

View file

@ -3,15 +3,14 @@
<div class="row">
<div class="col">
<img src="assets/banner.png" alt="Cider Logo" style="display:block;margin:0 auto;width: 500px;">
<p style="text-align: center">Major thanks to the Cider Collective Team and all of our contributors.</p>
<p style="text-align: center"> {{$root.getLz('about.thanks')}} </p>
<p style="text-align: center">"Apple Music" - Copyright © 2021 <a href="https://www.apple.com/" class="dt-footer__link"
<p style="text-align: center">"{{$root.getLz('term.appleMusic')}}" - {{$root.getLz('term.copyright')}} © 2022 <a href="https://www.apple.com/" class="dt-footer__link"
target="_blank"
rel="noopener" data-dt-link-to-exclude="">Apple Inc.</a>
All Rights
Reserved.</p>
rel="noopener" data-dt-link-to-exclude="">{{$root.getLz('term.appleInc')}}</a>
{{$root.getLz('term.rightsReserved')}}</p>
<hr>
<h3>Sponsor this project</h3>
<h3>{{$root.getLz('term.sponsor')}}</h3>
<button onclick="window.open('https://ko-fi.com/cryptofyre')" class="md-btn sponsorBtn"><img src="./assets/ko_fi.svg"/>Ko-fi</button>
<button onclick="window.open('https://opencollective.com/ciderapp')" class="md-btn sponsorBtn"><img src="./assets/open_collective.svg"/>Open Collective</button>
@ -19,7 +18,7 @@
<div class="col">
<div class="row">
<div class="col">
<h3>Cider Team</h3>
<h3>{{$root.getLz('term.ciderTeam')}}</h3>
<button class="md-btn teamBtn" @click="window.open(member.link)" v-for="member in team">
<img :src="member.avatar"/>
<div class="row" style="width:100%;">
@ -36,7 +35,7 @@
<div class="row">
<div class="col">
<h3></h3>
<h3>Contributors</h3>
<h3>{{$root.getLz('term.contributors')}}</h3>
<img class="md-contributors"
style="cursor:pointer;width:100%;"
onclick="window.open('https://github.com/ciderapp/Cider/graphs/contributors')"
@ -58,43 +57,43 @@
{
name: 'cryptofyre',
link: 'https://github.com/cryptofyre',
role: 'Developer',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/33162551?v=4'
},
{
name: 'Core',
link: 'https://github.com/coredev-uk',
role: 'Developer',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/64542347?v=4'
},
{
name: 'Quacksire',
link: 'https://github.com/quacksire',
role: 'Developer',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/19170969?v=4'
},
{
name: 'booploops',
link: 'https://github.com/booploops',
role: 'Developer',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/49113086?v=4'
},
{
name: 'vapormusic',
link: 'https://github.com/vapormusic',
role: 'Developer',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/27716185?v=4'
},
{
name: 'Void',
link: 'https://twitter.com/MoonyVoid',
role: 'Social Team',
role: app.getLz('term.socialTeam'),
avatar: 'https://pbs.twimg.com/profile_images/1226463559472816129/8LScNYED_400x400.jpg'
},
{
name: 'NoseySG',
link: 'https://twitter.com/noah_grose',
role: 'Social Team',
role: app.getLz('term.socialTeam'),
avatar: 'https://pbs.twimg.com/profile_images/1422541289837535239/qg-aaoP9_400x400.jpg'
}
]

View file

@ -8,7 +8,7 @@
<h3>{{ recom.attributes.name ?? ""}}</h3>
</div>
<div class="col-auto flex-center" v-if="index != 0 && recom.relationships && ((recom.relationships.children && recom.relationships.children.data.length > 10) || (recom.relationships.contents && recom.relationships.contents.data.length > 10))">
<button class="cd-btn-seeall" @click="app.showCollection(recom.relationships.children ? recom.relationships.children : recom.relationships.contents, recom.attributes.name ?? '', 'listen_now')" >See All</button>
<button class="cd-btn-seeall" @click="app.showCollection(recom.relationships.children ? recom.relationships.children : recom.relationships.contents, recom.attributes.name ?? '', 'listen_now')" >{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<template v-if="recom.relationships && ((recom.relationships.children && recom.relationships.children.data) || (recom.relationships.contents && recom.relationships.contents.data))">

View file

@ -5,7 +5,32 @@
<div class="col">
<div class="row nopadding">
<div class="col nopadding">
<h3>Your Artists Feed</h3>
<h3>{{app.getLz('home.followedArtists')}}</h3>
</div>
</div>
<div class="well">
<mediaitem-scroller-horizontal>
<div v-for="artist in artists" style="margin: 6px;">
<mediaitem-square :item="artist" kind="small"></mediaitem-square>
<button @click="unfollow(artist.id)" class="md-btn md-btn-glyph" style="display:flex;">
<div class="sidebar-icon">
<div class="svg-icon" :style="{'--url': 'url(./assets/feather/x-circle.svg)'}"></div>
</div> {{app.getLz('action.unfollow')}}
</button>
</div>
</mediaitem-scroller-horizontal>
</div>
</div>
</div>
</div>
<div>
<div class="row">
<div class="col">
<div class="row nopadding">
<div class="col nopadding">
<h3>{{app.getLz('home.artistsFeed')}}</h3>
</div>
</div>
<div class="well" style="margin-top:0;">
@ -30,6 +55,7 @@
app: this.$root,
followedArtists: this.$root.cfg.home.followedArtists,
artistFeed: [],
artists: []
}
},
async mounted() {
@ -37,19 +63,26 @@
await this.getArtistFeed()
},
methods: {
unfollow(id) {
let index = this.followedArtists.indexOf(id)
if (index > -1) {
this.followedArtists.splice(index, 1)
}
let artist = this.artists.find(a => a.id == id)
let index2 = this.artists.indexOf(artist)
if (index2 > -1) {
this.artists.splice(index2, 1)
}
this.getArtistFeed()
},
async getArtistFeed() {
let artists = this.followedArtists
let self = this
this.app.mk.api.artists(artists, {
"views": "featured-release,full-albums,appears-on-albums,featured-albums,featured-on-albums,singles,compilation-albums,live-albums,latest-release,top-music-videos,similar-artists,top-songs,playlists,more-to-hear,more-to-see",
"extend": "artistBio,bornOrFormed,editorialArtwork,editorialVideo,isGroup,origin,hero",
"extend[playlists]": "trackCount",
"include[songs]": "albums",
"fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount",
"limit[artists:top-songs]": 20,
"art[url]": "f"
}, {includeResponseMeta: !0}).then(artistData => {
artistData.data.forEach(item => {
this.artists = []
this.artistFeed = []
this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists?ids=${artists.toString()}&views=latest-release&include[songs]=albums&fields[albums]=artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount&limit[artists:top-songs]=2&art[url]=f`).then(artistData => {
artistData.data.data.forEach(item => {
self.artists.push(item)
if (item.views["latest-release"].data.length != 0) {
self.artistFeed.push(item.views["latest-release"].data[0])
}

View file

@ -1,49 +1,66 @@
<script type="text/x-template" id="cider-artist">
<div class="content-inner artist-page">
<div class="artist-header" :style="getArtistPalette(data)" :key="data.id">
<div class="content-inner artist-page" :class="[data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9) ? 'animated' : '']">
<div class="artist-header" :key="data.id" v-observe-visibility="{callback: isHeaderVisible}">
<animatedartwork-view
:priority="true"
v-if="data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9)"
:video="data.attributes.editorialVideo.motionArtistWide16x9.video ?? (data.attributes.editorialVideo.motionArtistFullscreen16x9.video ?? '')">
</animatedartwork-view>
<div class="row">
<div class="col-sm" style="width: auto;">
<div class="artist-image" v-if="!(data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9))">
<mediaitem-artwork
shadow="large"
:url="data.attributes.artwork ? data.attributes.artwork.url : ''"
size="190" type="artists"></mediaitem-artwork>
<button class="overlay-play" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
<div class="header-content" style="pointer-events: all;">
<div class="row">
<div class="col-sm" style="width: auto;">
<div class="artist-image" v-if="!(data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9))">
<mediaitem-artwork
shadow="large"
:url="data.attributes.artwork ? data.attributes.artwork.url : ''"
size="190" type="artists"></mediaitem-artwork>
<button class="overlay-play" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
app.mk.play()
})">
<%- include("../svg/play.svg") %>
</button>
<%- include("../svg/play.svg") %>
</button>
</div>
</div>
</div>
<div class="col flex-center artist-title"
:class="{'artist-animation-on': (data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9)) }"
>
<button class="artist-play" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
<div class="col flex-center artist-title"
:class="{'artist-animation-on': (data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9)) }"
>
<button class="artist-play" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
app.mk.play()
})"><%- include("../svg/play.svg") %></button>
<h1>{{ data.attributes.name }}</h1>
<h1>{{ data.attributes.name }}</h1>
</div>
</div>
<button class="more-btn-round" @click="artistMenu" style="pointer-events: all;">
<div class="svg-icon"></div>
</button>
</div>
<div class="artworkContainer" v-if="!(data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9))">
<artwork-material :url="data.attributes.artwork.url" size="190" images="1"></artwork-material>
</div>
</div>
<div class="floating-header" :style="{opacity: (headerVisible ? 0 : 1),'pointer-events': (headerVisible ? 'none' : '')}">
<div class="row">
<div class="col-auto flex-center">
<button class="artist-play" style="display:block;" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
app.mk.play()
})"><%- include("../svg/play.svg") %></button>
</div>
<div class="col">
<h3>{{ data.attributes.name }}</h3>
</div>
<div class="col-auto flex-center">
<button class="more-btn-round" @click="artistMenu">
<div class="svg-icon"></div>
</button>
</div>
</div>
<button class="artist-more" @click="artistMenu">
<div style=" margin-top: -1px;
margin-left: -6px;
width: 36px;
height: 36px;">
<%- include("../svg/more.svg") %>
</div>
</button>
</div>
<div class="artist-body">
<div class="row well">
<div class="col">
<div class="row">
<div class="col-auto" v-if="data.views['latest-release'].data.length != 0">
<h3>Latest Release</h3>
<h3>{{app.getLz('term.latestReleases')}}</h3>
<div style="width: auto;margin: 0 auto;">
<mediaitem-square kind="card" v-for="song in data.views['latest-release'].data"
:item="song">
@ -53,25 +70,20 @@
<div class="col" v-if="data.views['top-songs']">
<div class="row">
<div class="col" style="padding:0;">
<h3>Top Songs</h3>
<h3>{{app.getLz('term.topSongs')}}</h3>
</div>
<div class="col-auto flex-center" v-if="data.views['top-songs'].data.length >= 10" style="padding:0;">
<button class="cd-btn-seeall" @click="app.showArtistView(data.id, data.attributes.name + ' - Top Songs', 'top-songs')">See All</button>
<div class="col-auto flex-center" v-if="data.views['top-songs'].data.length >= 16" style="padding:0;">
<button class="cd-btn-seeall" @click="app.showArtistView(data.id, data.attributes.name + ' - Top Songs', 'top-songs')">{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<mediaitem-list-item
v-for="(song, index) in data.views['top-songs'].data.limit(topSongsExpanded ? 10 : 5)"
<div class="mediaitem-list-item__grid">
<div class="grid-body">
<mediaitem-list-item
v-for="(song, index) in data.views['top-songs'].data.limit(16)"
:index="index"
:item="song"></mediaitem-list-item>
<button class="showmoreless"
@click="topSongsExpanded = !topSongsExpanded">
<template v-if="!topSongsExpanded">
Show more
</template>
<template v-else>
Show less
</template>
</button>
</div>
</div>
</div>
</div>
</div>
@ -87,7 +99,7 @@
</h3>
</div>
<div class="col-auto flex-center" v-if="data.views[view].data.length >= 10">
<button class="cd-btn-seeall" @click="app.showArtistView(data.id, data.attributes.name + ' - ' + data.views[view].attributes.title, view)">See All</button>
<button class="cd-btn-seeall" @click="app.showArtistView(data.id, data.attributes.name + ' - ' + data.views[view].attributes.title, view)">{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<template v-if="!((data.views[view].attributes.title ?
@ -103,7 +115,7 @@
</template>
<div class="row">
<div class="col" v-if="data.attributes.artistBio">
<h3>About {{ data.attributes.name }}</h3>
<h3>{{ app.getLz('term.about') }} {{ data.attributes.name }}</h3>
<p v-html="data.attributes.artistBio"></p>
</div>
<div class="col">
@ -116,7 +128,7 @@
{{ data.attributes.bornOrFormed }}
</div>
<div v-if="data.attributes.genreNames">
<h3>Genre</h3>
<h3>{{app.getLz('term.sortBy.genre')}}</h3>
{{ data.attributes.genreNames.join(', ') }}
</div>
</div>
@ -133,24 +145,28 @@
data: function () {
return {
topSongsExpanded: false,
app: this.$root
app: this.$root,
headerVisible: true
}
},
methods: {
isHeaderVisible(visible) {
this.headerVisible = visible
},
artistMenu (event) {
let self = this
let followAction = "follow"
let followActions = {
follow: {
icon: "./assets/feather/plus-circle.svg",
name: "Follow Artist",
name: app.getLz('action.follow'),
action: ()=>{
self.app.cfg.home.followedArtists.push(self.data.id)
}
},
unfollow: {
icon: "./assets/feather/x-circle.svg",
name: "Unfollow Artist",
name: app.getLz('action.unfollow'),
action: ()=>{
let index = self.app.cfg.home.followedArtists.indexOf(self.data.id)
if (index > -1) {
@ -166,7 +182,7 @@
items: [
{
icon: "./assets/feather/play.svg",
name: "Play Artist Radio",
name: app.getLz('action.startRadio'),
action: ()=>{
app.mk.setStationQueue({artist:self.data.id}).then(()=>{
app.mk.play()
@ -176,7 +192,7 @@
followActions[followAction],
{
icon: "./assets/feather/share.svg",
name: "Share",
name: app.getLz('term.share'),
action: ()=>{
self.app.copyToClipboard(self.data.attributes.url)
}

View file

@ -1,6 +1,6 @@
<script type="text/x-template" id="cider-browse">
<div class="content-inner">
<h1 class="header-text">Browse</h1>
<h1 class="header-text">{{$root.getLz("term.browse")}}</h1>
<template v-if="data.relationships && data.relationships.tabs">
<template v-for="(recom,index) in data.relationships.tabs.data[0].relationships.children.data">
<div class="row">
@ -8,7 +8,7 @@
<h3>{{ recom.attributes.name ?? ""}}</h3>
</div>
<div class="col-auto flex-center" v-if="index != 0 && recom.relationships && ((recom.relationships.children && recom.relationships.children.data.length > 10) || (recom.relationships.contents && recom.relationships.contents.data.length > 10))">
<button class="cd-btn-seeall" @click="app.showCollection(recom.relationships.children ? recom.relationships.children : recom.relationships.contents, recom.attributes.name ?? '', 'listen_now')" >See All</button>
<button class="cd-btn-seeall" @click="app.showCollection(recom.relationships.children ? recom.relationships.children : recom.relationships.contents, recom.attributes.name ?? '', 'listen_now')" >{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<template v-if="recom.relationships && ((recom.relationships.children && recom.relationships.children.data) || (recom.relationships.contents && recom.relationships.contents.data))">

View file

@ -7,81 +7,118 @@
</div>
</template>
<template v-if="app.playlists.loadingState == 1">
<div class="playlist-display row"
<div class="playlist-display"
:style="{
background: (data.attributes.artwork != null && data.attributes.artwork['bgColor'] != null) ? ('#' + data.attributes.artwork.bgColor) : '',
color: (data.attributes.artwork != null && data.attributes.artwork['textColor1'] != null) ? ('#' + data.attributes.artwork.textColor1) : ''
'--bgColor': (data.attributes.artwork != null && data.attributes.artwork['bgColor'] != null) ? ('#' + data.attributes.artwork.bgColor) : '',
'--textColor': (data.attributes.artwork != null && data.attributes.artwork['textColor1'] != null) ? ('#' + data.attributes.artwork.textColor1) : ''
}">
<div class="col-auto flex-center">
<div style="width: 260px;height:260px;">
<mediaitem-artwork
:video-priority="true"
:url="(data.attributes != null && data.attributes.artwork != null) ? data.attributes.artwork.url : ((data.relationships != null && data.relationships.tracks.data.length > 0 && data.relationships.tracks.data[0].attributes != null) ? ((data.relationships.tracks.data[0].attributes.artwork != null)? data.relationships.tracks.data[0].attributes.artwork.url : ''):'')"
:video="(data.attributes != null && data.attributes.editorialVideo != null) ? (data.attributes.editorialVideo.motionDetailSquare ? data.attributes.editorialVideo.motionDetailSquare.video : (data.attributes.editorialVideo.motionSquareVideo1x1 ? data.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' "
size="260"
></mediaitem-artwork>
</div>
</div>
<div class="col playlist-info">
<template v-if="!editorialNotesExpanded">
<div>
<div class="playlist-name" @click="editPlaylistName()" v-show="!nameEditing">
{{data.attributes ? (data.attributes.name ??
(data.attributes.title ?? '') ?? '') : ''}}
<div class="playlistInfo">
<div class="row">
<div class="col-auto flex-center">
<div style="width: 260px;height:260px;">
<mediaitem-artwork
shadow="large"
:video-priority="true"
:url="(data.attributes != null && data.attributes.artwork != null) ? data.attributes.artwork.url : ((data.relationships != null && data.relationships.tracks.data.length > 0 && data.relationships.tracks.data[0].attributes != null) ? ((data.relationships.tracks.data[0].attributes.artwork != null)? data.relationships.tracks.data[0].attributes.artwork.url : ''):'')"
:video="(data.attributes != null && data.attributes.editorialVideo != null) ? (data.attributes.editorialVideo.motionDetailSquare ? data.attributes.editorialVideo.motionDetailSquare.video : (data.attributes.editorialVideo.motionSquareVideo1x1 ? data.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' "
size="260"
></mediaitem-artwork>
</div>
<div class="playlist-name" v-show="nameEditing"><input type="text" spellcheck="false"
class="nameEdit"
v-model="data.attributes.name"
@blur="editPlaylist"
@change="editPlaylist"
@keydown.enter="editPlaylist"/></div>
<div class="playlist-artist item-navigate"
v-if="getArtistName(data) != ''"
@click="data.attributes && data.attributes.artistName ? app.searchAndNavigate(data,'artist') : ''">
{{getArtistName(data)}}
</div>
<div class="playlist-desc" v-if="data.attributes.description && (data.attributes.description.standard || data.attributes.description.short)">
<div v-if="data.attributes.description.short" class="content" v-html="data.attributes.description.short"></div>
<div v-else-if="data.attributes.description.standard" class="content" v-html="data.attributes.description.standard"></div>
<button v-if="data.attributes.description.short" class="more-btn"
@click="editorialNotesExpanded = !editorialNotesExpanded">
More
</div>
<div class="col playlist-info">
<template v-if="!editorialNotesExpanded">
<div>
<div class="playlist-name" @click="editPlaylistName()" v-show="!nameEditing">
{{data.attributes ? (data.attributes.name ??
(data.attributes.title ?? '') ?? '') : ''}}
</div>
<div class="playlist-name" v-show="nameEditing"><input type="text" spellcheck="false"
class="nameEdit"
v-model="data.attributes.name"
@blur="editPlaylist"
@change="editPlaylist"
@keydown.enter="editPlaylist"/></div>
<div class="playlist-artist item-navigate"
v-if="getArtistName(data) != ''"
@click="data.attributes && data.attributes.artistName ? app.searchAndNavigate(data,'artist') : ''">
{{getArtistName(data)}}
</div>
<div class="playlist-desc" v-if="data.attributes.description && (data.attributes.description.standard || data.attributes.description.short)">
<div v-if="data.attributes.description.short" class="content" v-html="data.attributes.description.short"></div>
<div v-else-if="data.attributes.description.standard" class="content" v-html="data.attributes.description.standard"></div>
<button v-if="data.attributes.description.short" class="more-btn"
@click="editorialNotesExpanded = !editorialNotesExpanded">
{{app.getLz('term.showMore')}}
</button>
</div>
</div>
</template>
<template v-if="editorialNotesExpanded">
<div class="playlist-desc-expanded">
<div class="content"
v-html="((data.attributes.editorialNotes) ? (data.attributes.editorialNotes.standard ?? (data.attributes.editorialNotes.short ?? '') ) : (data.attributes.description ? (data.attributes.description.standard ?? (data.attributes.description.short ?? '')) : ''))"></div>
<button class="more-btn" @click="editorialNotesExpanded = !editorialNotesExpanded">{{app.getLz('term.showLess')}}
</button>
</div>
</template>
<div class="playlist-controls" v-observe-visibility="{callback: isHeaderVisible}">
<button class="md-btn md-btn-primary md-btn-icon" style="min-width: 100px;"
@click="app.mk.shuffleMode = 0; play()"> <img class="md-ico-play">
{{app.getLz('term.play')}}
</button>
<button class="md-btn md-btn-primary md-btn-icon" style="min-width: 100px;"
@click="app.mk.shuffleMode = 1;play()"> <img class="md-ico-shuffle">
{{app.getLz('term.shuffle')}}
</button>
<button class="md-btn md-btn-icon" style="min-width: 180px;" v-if="inLibrary!=null && confirm!=true"
@click="confirmButton()"> <img :class="(!inLibrary) ? 'md-ico-add' : 'md-ico-remove'">
{{ (!inLibrary) ? app.getLz('action.addToLibrary') : app.getLz("action.removeFromLibrary") }}
</button>
<button class="md-btn md-btn-icon" style="min-width: 180px;" v-if="confirm==true"
@click="(!inLibrary) ? addToLibrary(data.attributes.playParams.id.toString()) : removeFromLibrary(data.attributes.playParams.id.toString()) "> <img :class="(!inLibrary) ? 'md-ico-add' : 'md-ico-remove'">
{{app.getLz('term.confirm')}}
</button>
<button class="more-btn-round" style="float:right;" @click="menu">
<div class="svg-icon"></div>
</button>
</div>
</div>
</template>
<template v-if="editorialNotesExpanded">
<div class="playlist-desc-expanded">
<div class="content"
v-html="((data.attributes.editorialNotes) ? (data.attributes.editorialNotes.standard ?? (data.attributes.editorialNotes.short ?? '') ) : (data.attributes.description ? (data.attributes.description.standard ?? (data.attributes.description.short ?? '')) : ''))"></div>
<button class="more-btn" @click="editorialNotesExpanded = !editorialNotesExpanded">Less
</div>
</div>
<div class="artworkContainer" v-if="data.attributes.artwork != null">
<artwork-material :url="data.attributes.artwork.url" size="260" images="1"></artwork-material>
</div>
</div>
<div class="floating-header" :style="{opacity: (headerVisible ? 0 : 1),'pointer-events': (headerVisible ? 'none' : '')}">
<div class="row">
<div class="col">
<h3>{{data.attributes ? (data.attributes.name ??
(data.attributes.title ?? '') ?? '') : ''}}</h3>
</div>
<div class="col-auto flex-center">
<div>
<button class="md-btn md-btn-primary md-btn-icon" style="min-width: 100px;"
@click="app.mk.shuffleMode = 0; play()"> <img class="md-ico-play">
{{app.getLz('term.play')}}
</button>
<button class="md-btn md-btn-primary md-btn-icon" style="min-width: 100px;"
@click="app.mk.shuffleMode = 1;play()"> <img class="md-ico-shuffle">
{{app.getLz('term.shuffle')}}
</button>
<button class="md-btn md-btn-icon" style="min-width: 180px;" v-if="inLibrary!=null && confirm!=true"
@click="confirmButton()"> <img :class="(!inLibrary) ? 'md-ico-add' : 'md-ico-remove'">
{{ (!inLibrary) ? app.getLz('action.addToLibrary') : app.getLz("action.removeFromLibrary") }}
</button>
<button class="md-btn md-btn-icon" style="min-width: 180px;" v-if="confirm==true"
@click="(!inLibrary) ? addToLibrary(data.attributes.playParams.id.toString()) : removeFromLibrary(data.attributes.playParams.id.toString()) "> <img :class="(!inLibrary) ? 'md-ico-add' : 'md-ico-remove'">
{{app.getLz('term.confirm')}}
</button>
</div>
</template>
<div class="playlist-controls">
<button class="md-btn" style="min-width: 120px;"
@click="app.mk.shuffleMode = 0; play()">
Play
</button>
<button class="md-btn" style="min-width: 120px;"
@click="app.mk.shuffleMode = 1;play()">
Shuffle
</button>
<button class="md-btn" style="min-width: 120px;" v-if="inLibrary!=null && confirm!=true"
@click="confirmButton()">
{{ (!inLibrary) ? "Add to Library" : "Remove from Library" }}
</button>
<button class="md-btn" style="min-width: 120px;" v-if="confirm==true"
@click="(!inLibrary) ? addToLibrary(data.attributes.playParams.id.toString()) : removeFromLibrary(data.attributes.playParams.id.toString()) ">
Confirm?
</button>
<button class="playlist-more" @click="menu">
<div style=" margin-top: -1px;
margin-left: -5px;
width: 36px;
height: 36px;">
<%- include("../svg/more.svg") %>
</div>
</div>
<div class="col-auto flex-center">
<button class="more-btn-round" style="float:right;" @click="menu">
<div class="svg-icon"></div>
</button>
</div>
</div>
@ -91,7 +128,7 @@
<div style="width:100%">
<draggable :sort="data.attributes.canEdit && data.type == 'library-playlists'"
v-model="data.relationships.tracks.data" @start="drag=true" @end="drag=false;put()">
<mediaitem-list-item :item="item" :parent="getItemParent(data)" :index="index"
<mediaitem-list-item :item="item" :parent="getItemParent(data)" :index="index" :showIndex="true" :showIndexPlaylist="(data.attributes.playParams.kind ?? data.type ?? '').includes('playlist')"
:context-ext="buildContextMenu()"
v-for="(item,index) in data.relationships.tracks.data"></mediaitem-list-item>
</draggable>
@ -118,6 +155,25 @@
style="width: 50%;">
{{data.attributes.copyright}}
</div>
<template v-if="(data.attributes?.playParams?.kind ?? data.type ?? '').includes('album') && data.relationships.catalog != null && data.relationships.catalog != null && data.relationships.catalog.data.length > 0">
<div class="playlist-time showExtended item-navigate" style="color:#fa586a; font-weight: bold" @click="app.routeView(data.relationships.catalog.data[0])">
{{$root.getLz("term.showAlbum")}}
</div>
</template>
<hr>
<template v-if="typeof data.meta != 'undefined'">
<div v-for="view in data.meta.views.order" v-if="data.views[view].data.length != 0">
<div class="row" >
<div class="col">
<h3>{{ data.views[view].attributes.title }}</h3>
</div>
</div>
<div>
<mediaitem-scroller-horizontal :items="data.views[view].data"></mediaitem-scroller-horizontal>
</div>
</div>
</template>
</div>
</template>
</div>
@ -138,7 +194,8 @@
confirm: false,
app: this.$root,
itemBadges: [],
badgesRequested: false
badgesRequested: false,
headerVisible: true
}
},
mounted: function () {
@ -153,6 +210,9 @@
}
},
methods: {
isHeaderVisible(visible) {
this.headerVisible = visible
},
getBadges() {
return
if (this.badgesRequested) {
@ -171,8 +231,8 @@
let friends = badges[id]
if (friends) {
friends.forEach(function (friend) {
self.app.mk.api.socialProfile(friend).then(data => {
self.itemBadges.push(data)
self.app.mk.api.v3.music(`/v1/social/${app.mk.storefrontId}/social-profiles/${friend}`).then(data => {
self.itemBadges.push(data.data.data[0])
})
})
}
@ -205,7 +265,7 @@
"relate": "library"
};
const res = await app.mkapi(this.data.attributes.playParams.kind ?? this.data.type, this.data.attributes.playParams.isLibrary ?? false, this.data.attributes.playParams.id ?? this.data.id, params);
this.inLibrary = (res && res.attributes && res.attributes.inLibrary) ? res.attributes.inLibrary : false
this.inLibrary = (res.data.data[0] && res.data.data[0].attributes && res.data.data[0].attributes.inLibrary) ? res.data.data[0].attributes.inLibrary : false
console.log(res)
} else {
this.inLibrary = true
@ -229,12 +289,16 @@
const params = {"fields[somgs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library"};
var id = this.data.id ?? this.data.attributes.playParams.id
const res = await app.mkapi(this.data.attributes.playParams.kind ?? this.data.type, this.data.attributes.playParams.isLibrary ?? false, this.data.attributes.playParams.id ?? this.data.id, params);
if (res && res.relationships && res.relationships.library && res.relationships.library.data && res.relationships.library.data.length > 0) {
id = res.relationships.library.data[0].id
if (res.data.data[0] && res.data.data[0].relationships && res.data.data[0].relationships.library && res.data.data[0].relationships.library.data && res.data.data[0].relationships.library.data.length > 0) {
id = res.data.data[0].relationships.library.data[0].id
}
let kind = this.data.attributes.playParams.kind ?? this.data.type ?? '';
const truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.library.remove({[truekind]: id})
app.mk.api.v3.music(`v1/me/library/${truekind}/${id.toString()}`,{},
{
fetchOptions: {
method: "DELETE"
}})
this.inLibrary = false
this.confirm = false
},
@ -254,7 +318,7 @@
return {
normal: [
{
name: 'Remove from Playlist',
name: app.getLz('action.removeFromPlaylist'),
action: () => {
self.remove()
}
@ -262,7 +326,7 @@
],
multiple: [
{
name: 'Remove selected tracks from Playlist',
name: app.getLz('action.removeFromPlaylist'),
action: () => {
self.remove()
}
@ -274,7 +338,19 @@
if (!this.data.attributes.canEdit) {
return
}
await app.mk.api.library.putPlaylistTracklisting(this.data.attributes.playParams.id, this.convert())
console.log('sds',this.convert())
await app.mk.api.v3.music(
`/v1/me/library/playlists/${this.data.attributes.playParams.id}/tracks`,
{},
{
fetchOptions: {
method: "PUT",
body: JSON.stringify({
data: this.convert()
})
}
}
)
},
async remove() {
if (!this.data.attributes.canEdit) {
@ -312,7 +388,7 @@
app.showMenuPanel({
items: {
"share": {
name: "Share",
name: app.getLz('term.share'),
icon: "./assets/feather/share.svg",
action: () => {
let route = ""
@ -353,24 +429,32 @@
if (date == null || date === "") return "";
switch (date) {
case this.data.attributes.releaseDate:
prefix = 'Released '
prefix = this.app.getLz('term.time.released')+ ' '
break;
case this.data.attributes.lastModifiedDate:
prefix = 'Updated '
prefix = this.app.getLz('term.time.updated')+ ' '
break;
case this.data.attributes.dateAdded:
prefix = 'Added '
prefix = this.app.getLz('term.time.added')+ ' '
break;
}
let month, year;
try {
const releaseDate = new Date(date);
console.log(date, releaseDate)
month = new Intl.DateTimeFormat('en-US', {month: 'long'}).format(releaseDate);
date = releaseDate.getDate();
year = releaseDate.getFullYear();
return prefix + date + " " + month + " " + year;
// month = new Intl.DateTimeFormat(this.app.cfg.general.language.replace('_','-'), {month: 'long'}).format(releaseDate);
// date = releaseDate.getDate();
// year = releaseDate.getFullYear();
let formatted = ''
try {formatted = new Intl.DateTimeFormat(this.app.cfg.general.language?.replace('_','-') ?? 'en-US', {day:'numeric',month: 'long', year: 'numeric'}).format(releaseDate);}
catch(e){
// use the format in json instead
if (this.app.getLz('date.format') != null){
formatted = new this.app.getLz('date.format').replace("${d}", releaseDate.getDate()).replace("${m}", releaseDate.getMonth()).replace("${y}", releaseDate.getFullYear());
} else {
formatted = new Intl.DateTimeFormat('en-US', {day:'numeric',month: 'long', year: 'numeric'}).format(releaseDate);
}
}
return prefix + formatted
} catch (e) {
return ""
}
@ -411,4 +495,4 @@
}
})
</script>
</script>

View file

@ -14,13 +14,16 @@
<mediaitem-square v-else :item="item" :type="getKind(item)"></mediaitem-square>
</template>
</template>
<button v-if="triggerEnabled" style="opacity:0;height: 32px;" v-observe-visibility="{callback: visibilityChanged}">Show More</button>
<button v-if="triggerEnabled" style="opacity:0;height: 32px;"
v-observe-visibility="{callback: visibilityChanged}">{{this.app.getLz('term.showMore')}}
</button>
</div>
<transition name="fabfade">
<button class="top-fab" v-show="showFab" @click="scrollToTop()">
<%- include("../svg/arrow-up.svg") %>
</button>
</transition>
<div class="well" v-show="loading"><div class="spinner"></div></div>
</div>
</script>
<script>
@ -47,16 +50,18 @@
canSeeTrigger: false,
showFab: false,
commonKind: "song",
api: this.$root.mk.api
api: this.$root.mk.api,
loading: false,
app: this.$root,
}
},
methods: {
getKind(item) {
if(typeof item.kind != "undefined") {
if (typeof item.kind != "undefined") {
this.commonKind = item.kind;
return item.kind
}
if(typeof item.attributes.playParams != "undefined") {
if (typeof item.attributes.playParams != "undefined") {
this.commonKind = item.attributes.playParams.kind
return item.attributes.playParams.kind
}
@ -71,73 +76,48 @@
})
},
getNext() {
// if this.data.next is not null, then we can run this.data.next() and concat to this.data.data to get the next page
switch(this.type) {
default:
case "artists":
if (this.data.next && this.triggerEnabled) {
this.triggerEnabled = false;
let nextFn = (data => {
console.log(data);
this.data.next = data.next;
this.data.data = this.data.data.concat(data.data);
this.triggerEnabled = true;
});
if(typeof this.data.next == "function") {
this.data.next().then(data => nextFn(data));
}else{
this.api.v3.music(this.data.next).then(data => nextFn(data));
}
}else{
console.log("No next page");
this.triggerEnabled = false;
}
break;
case "search":
if (this.data.next && this.triggerEnabled) {
this.triggerEnabled = false;
this.data.next().then(data => {
console.log(data);
this.data.next = data[this.data.groups].next;
this.data.data = this.data.data.concat(data[this.data.groups].data.data);
this.triggerEnabled = true;
});
}else{
console.log("No next page");
this.triggerEnabled = false;
}
break;
case "listen_now":
case "curator":
if (this.data.next && this.triggerEnabled) {
this.triggerEnabled = false;
app.mk.api.v3.music(this.data.next).then(data => {
console.log(data);
this.data.next = data.data.next;
this.data.data = this.data.data.concat(data.data.data);
this.triggerEnabled = true;
});
}else{
console.log("No next page");
this.triggerEnabled = false;
}
break;
let self = this
this.triggerEnabled = false;
if (typeof this.data.next == "undefined") {
return
}
this.loading = true
this.api.v3.music(this.data.next, app.collectionList.requestBody).then((response) => {
console.log(response)
if (!app.collectionList.response.groups) {
this.data.data = this.data.data.concat(response.data.data);
if (response.data.next) {
this.data.next = response.data.next;
this.triggerEnabled = true;
}
this.loading = false
}else{
if(!response.data.results[app.collectionList.response.groups]) {
this.loading = false
return
}
this.data.data = this.data.data.concat(response.data.results[app.collectionList.response.groups].data);
if (response.data.results[app.collectionList.response.groups].next) {
this.data.next = response.data.results[app.collectionList.response.groups].next;
this.triggerEnabled = true;
this.loading = false
}
}
})
},
headerVisibility: function (isVisible, entry) {
if(isVisible) {
if (isVisible) {
this.showFab = false;
}else{
} else {
this.showFab = true;
}
},
visibilityChanged: function (isVisible, entry) {
if(isVisible) {
if (isVisible) {
this.canSeeTrigger = true;
this.getNext();
}else{
} else {
this.canSeeTrigger = false;
}
}

View file

@ -3,7 +3,7 @@
<div v-if="page == 'main'">
<div class="row">
<div class="col">
<h3>Recently Played</h3>
<h3>{{app.getLz('home.recentlyPlayed')}}</h3>
<div class="well artistfeed-well">
<template v-if="isSectionReady('recentlyPlayed')">
<mediaitem-list-item v-for="item in recentlyPlayed.limit(6)"
@ -15,10 +15,10 @@
<div class="col">
<div class="row nopadding">
<div class="col nopadding">
<h3>Your Artists Feed</h3>
<h3>{{app.getLz('home.artistsFeed')}}</h3>
</div>
<div class="col-auto nopadding flex-center">
<button class="cd-btn-seeall" @click="app.appRoute('artist-feed')">See All</button>
<button class="cd-btn-seeall" @click="app.appRoute('artist-feed')">{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<div class="well artistfeed-well" style="margin-top:0;">
@ -26,7 +26,7 @@
<mediaitem-list-item v-for="item in artistFeed.limit(6)" :item="item"></mediaitem-list-item>
</template>
<div class="spinner" v-else-if="followedArtists.length > 0"></div>
<div class="no-artist" v-else> Follow some artists first and their latest releases will be here</div>
<div class="no-artist" v-else> {{app.getLz('home.artistsFeed.noArtist')}}</div>
</div>
</div>
</div>
@ -44,7 +44,7 @@
<!-- </div>-->
<div class="row">
<div class="col">
<h3>Made For You</h3>
<h3>{{app.getLz('home.madeForYou')}}</h3>
<div class="well">
<template v-if="isSectionReady('madeForYou')">
<mediaitem-square kind="small" v-for="item in madeForYou" :item="item"></mediaitem-square>
@ -57,10 +57,10 @@
<div class="col">
<div class="row">
<div class="col nopadding">
<h3>Friends Listening To</h3>
<h3>{{app.getLz('home.friendsListeningTo')}}</h3>
</div>
<div class="col-auto nopadding flex-center">
<button class="cd-btn-seeall" @click="app.showSocialListeningTo()">See All</button>
<button class="cd-btn-seeall" @click="app.showSocialListeningTo()">{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<div class="well">
@ -131,32 +131,22 @@
playlists.push(item.id)
}
}
if (playlists.length != 0) {
this.app.mk.api.playlists(playlists).then(playlistsData => {
self.favorites.push(...playlistsData)
if (playlists.length != 0) {
this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/playlists/${playlists.toString()}`).then(playlistsData => {
self.favorites.push(...playlistsData.data)
})
}
if (libraryPlaylists.length != 0) {
this.app.mk.api.library.playlists(libraryPlaylists).then(playlistsData => {
self.favorites.push(...playlistsData)
if (libraryPlaylists.length != 0) {
this.app.mk.api.v3.music(`v1/me/library/playlists/${playlists.toString()}`).then(playlistsData => {
self.favorites.push(...playlistsData.data)
})
}
},
async getArtistFeed() {
let artists = this.followedArtists
let self = this
this.app.mk.api.artists(artists, {
"views": "featured-release,full-albums,appears-on-albums,featured-albums,featured-on-albums,singles,compilation-albums,live-albums,latest-release,top-music-videos,similar-artists,top-songs,playlists,more-to-hear,more-to-see",
"extend": "artistBio,bornOrFormed,editorialArtwork,editorialVideo,isGroup,origin,hero",
"extend[playlists]": "trackCount",
"include[songs]": "albums",
"fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount",
"limit[artists:top-songs]": 20,
"art[url]": "f"
}, {
includeResponseMeta: !0
}).then(artistData => {
artistData.data.forEach(item => {
this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists?ids=${artists.toString()}&views=latest-release&include[songs]=albums&fields[albums]=artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount&limit[artists:top-songs]=2&art[url]=f`).then(artistData => {
artistData.data.data.forEach(item => {
if (item.views["latest-release"].data.length != 0) {
self.artistFeed.push(item.views["latest-release"].data[0])
}
@ -176,35 +166,10 @@
},
async getListenNowData() {
let self = this
this.app.mk.api.personalRecommendations("", {
name: "listen-now",
with: "friendsMix,library,social",
"art[social-profiles:url]": "c",
"art[url]": "c,f",
"omit[resource]": "autos",
"relate[editorial-items]": "contents",
extend: ["editorialCard", "editorialVideo"],
"extend[albums]": ["artistUrl"],
"extend[library-albums]": ["artistUrl", "editorialVideo"],
"extend[playlists]": ["artistNames", "editorialArtwork", "editorialVideo"],
"extend[library-playlists]": ["artistNames", "editorialArtwork", "editorialVideo"],
"extend[social-profiles]": "topGenreNames",
"include[albums]": "artists",
"include[songs]": "artists",
"include[music-videos]": "artists",
"fields[albums]": ["artistName", "artistUrl", "artwork", "contentRating", "editorialArtwork", "editorialVideo", "name", "playParams", "releaseDate", "url"],
"fields[artists]": ["name", "url"],
"extend[stations]": ["airDate", "supportsAirTimeUpdates"],
"meta[stations]": "inflectionPoints",
types: "artists,albums,editorial-items,library-albums,library-playlists,music-movies,music-videos,playlists,stations,uploaded-audios,uploaded-videos,activities,apple-curators,curators,tv-shows,social-profiles,social-upsells",
platform: "web"
}, {
includeResponseMeta: !0,
reload: !0
}).then((data) => {
console.log(data.data[1])
this.app.mk.api.v3.music(`/v1/me/recommendations?timezone=${encodeURIComponent(app.formatTimezoneOffset())}&name=listen-now&with=friendsMix,library,social&art[social-profiles:url]=c&art[url]=c,f&omit[resource]=autos&relate[editorial-items]=contents&extend=editorialCard,editorialVideo&extend[albums]=artistUrl&extend[library-albums]=artistUrl,editorialVideo&extend[playlists]=artistNames,editorialArtwork,editorialVideo&extend[library-playlists]=artistNames,editorialArtwork,editorialVideo&extend[social-profiles]=topGenreNames&include[albums]=artists&include[songs]=artists&include[music-videos]=artists&fields[albums]=artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url&fields[artists]=name,url&extend[stations]=airDate,supportsAirTimeUpdates&meta[stations]=inflectionPoints&types=artists,albums,editorial-items,library-albums,library-playlists,music-movies,music-videos,playlists,stations,uploaded-audios,uploaded-videos,activities,apple-curators,curators,tv-shows,social-upsells&platform=web`).then((data) => {
console.log(data.data.data[1])
try {
self.madeForYou = data.data.filter(section => {
self.madeForYou = data.data.data.filter(section => {
if (section.meta.metrics.moduleType == "6") {
return section
};
@ -213,8 +178,8 @@
self.sectionsReady.push("madeForYou")
try {
self.recentlyPlayed = data.data[1].relationships.contents.data
self.friendsListeningTo = data.data.filter(section => {
self.recentlyPlayed = data.data.data[1].relationships.contents.data
self.friendsListeningTo = data.data.data.filter(section => {
if (section.meta.metrics.moduleType == "11") {
return section
};
@ -228,7 +193,7 @@
self.profile = response.data.data[0]
})
}
}
}
});
</script>

View file

@ -2,7 +2,7 @@
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0;">
<h1 class="header-text">Albums</h1>
<h1 class="header-text">{{$root.getLz('term.albums')}}</h1>
</div>
<div class="col-auto">
<button v-if="library.albums.downloadState == 2" @click="getLibraryAlbumsFull(true, 1)" class="reload-btn"><%- include('../svg/redo.svg') %></button>
@ -15,7 +15,7 @@
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
:placeholder="$root.getLz('term.search') + '...'"
@input="searchLibraryAlbums"
v-model="library.albums.search" class="search-input">
</div>
@ -24,24 +24,24 @@
<div class="row">
<div class="col">
<select class="md-select" v-model="library.albums.sorting[1]" @change="searchLibraryAlbums(1)">
<optgroup label="Sort By">
<optgroup :label="$root.getLz('term.sortBy')">
<option v-for="(sort, index) in library.albums.sortingOptions" :value="index">{{ sort }}</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="library.albums.sortOrder[1]" @change="searchLibraryAlbums(1)">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
<optgroup :label="$root.getLz('term.sortOrder')">
<option value="asc">{{$root.getLz('term.sortOrder.ascending')}}</option>
<option value="desc">{{$root.getLz('term.sortOrder.descending')}}</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="library.albums.viewAs">
<optgroup label="View As">
<option value="covers">Cover Art</option>
<option value="list">List</option>
<optgroup :label="$root.getLz('term.viewAs')">
<option value="covers">{{$root.getLz('term.viewAs.coverArt')}}</option>
<option value="list">{{$root.getLz('term.viewAs.list')}}</option>
</optgroup>
</select>
</div>

View file

@ -1,7 +1,7 @@
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0;">
<h1 class="header-text">Artists</h1>
<h1 class="header-text">{{$root.getLz('term.artists')}}</h1>
</div>
</div>
@ -12,7 +12,7 @@
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
:placeholder="$root.getLz('term.search') + '...'"
@input="searchLibraryArtists"
v-model="library.artists.search" class="search-input">
</div>
@ -28,9 +28,9 @@
</div> -->
<div class="col">
<select class="md-select" v-model="library.artists.sortOrder[1]" @change="searchLibraryArtists(1)">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
<optgroup :label="$root.getLz('term.sortOrder')">
<option value="asc">{{$root.getLz('term.sortOrder.ascending')}}</option>
<option value="desc">{{$root.getLz('term.sortOrder.descending')}}</option>
</optgroup>
</select>
</div>

View file

@ -2,7 +2,7 @@
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0;">
<h1 class="header-text">Recently Added</h1>
<h1 class="header-text">{{$root.getLz('term.recentlyAdded')}}</h1>
</div>
<div class="col-auto">
<button v-if="library.albums.downloadState == 2" @click="getLibraryAlbumsFull(true, 0)"
@ -16,7 +16,7 @@
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
:placeholder="$root.getLz('term.search') + '...'"
@input="searchLibraryAlbums"
v-model="library.albums.search" class="search-input">
</div>
@ -26,17 +26,17 @@
<div class="col">
<select class="md-select" v-model="library.albums.sortOrder[0]"
@change="searchLibraryAlbums(0)">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
<optgroup :label="$root.getLz('term.sortOrder')">
<option value="asc">{{$root.getLz('term.sortOrder.ascending')}}</option>
<option value="desc">{{$root.getLz('term.sortOrder.descending')}}</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="library.albums.viewAs">
<optgroup label="View As">
<option value="covers">Cover Art</option>
<option value="list">List</option>
<optgroup :label="$root.getLz('term.viewAs')">
<option value="covers">{{$root.getLz('term.viewAs.coverArt')}}</option>
<option value="list">{{$root.getLz('term.viewAs.list')}}</option>
</optgroup>
</select>
</div>

View file

@ -10,7 +10,7 @@
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
:placeholder="app.getLz('term.search') + '...'"
@input="$root.searchLibrarySongs"
v-model="library.songs.search" class="search-input">
</div>
@ -19,24 +19,24 @@
<div class="row">
<div class="col">
<select class="md-select" v-model="prefs.sort" @change="$root.searchLibrarySongs()">
<optgroup label="Sort By">
<optgroup :label="app.getLz('term.sortBy')">
<option v-for="(sort, index) in library.songs.sortingOptions" :value="index">{{ sort }}</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="prefs.sortOrder" @change="$root.searchLibrarySongs()">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
<optgroup :label="app.getLz('term.sortOrder')">
<option value="asc">{{app.getLz('term.sortOrder.ascending')}}</option>
<option value="desc">{{app.getLz('term.sortOrder.descending')}}</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="prefs.size" @change="$root.searchLibrarySongs()">
<optgroup label="Size">
<option value="normal">Normal</option>
<option value="compact">Compact</option>
<optgroup :label="app.getLz('term.size')">
<option value="normal">{{app.getLz('term.size.normal')}}</option>
<option value="compact">{{app.getLz('term.size.compact')}}</option>
</optgroup>
</select>
</div>
@ -68,7 +68,8 @@
return {
library: this.$root.library,
mediaItemSize: "compact",
prefs: this.$root.cfg.libraryPrefs.songs
prefs: this.$root.cfg.libraryPrefs.songs,
app : this.$root
}
},
methods: {

View file

@ -0,0 +1,32 @@
<script type="text/x-template" id="cider-library-videos">
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0">
<h1 class="header-text">{{$root.getLz('term.videos')}}</h1>
</div>
</div>
<div class="madeforyou-body">
<mediaitem-square :size="300" :item="item" v-for="item in videos">
</mediaitem-square>
</div>
</div>
</script>
<script>
Vue.component('cider-library-videos', {
template: "#cider-library-videos",
props: ["data"],
data: function(){
return {
videos: [],
}
},
mounted() {
this.$nextTick(async function () {
if (this.$data.videos == null || this.$data.videos.length == 0)
this.$data.videos = (await this.$root.mk.api.v3.music('/v1/me/library/music-videos')).data?.data ?? []
})
}
})
</script>

View file

@ -1,6 +1,6 @@
<script type="text/x-template" id="cider-listen-now">
<div class="content-inner">
<h1 class="header-text">Listen Now</h1>
<h1 class="header-text">{{app.getLz('term.listenNow')}}</h1>
<template v-for="recom in data.data">
<listennow-child :recom="recom"></listennow-child>
</template>
@ -10,6 +10,11 @@
<script>
Vue.component('cider-listen-now', {
template: "#cider-listen-now",
props: ["data"]
props: ["data"],
data: function(){
return {
app : this.$root
}
}
})
</script>

View file

@ -1,7 +1,7 @@
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0">
<h1 class="header-text">Made For You</h1>
<h1 class="header-text">{{$root.getLz('home.madeForYou')}}</h1>
</div>
</div>
<div class="madeforyou-body">

View file

@ -0,0 +1,249 @@
<script type="text/x-template" id="apple-podcasts">
<div class="content-inner podcasts-page">
<div class="podcasts-list">
<div class="podcasts-search">
<div class="search-input-container" style="width:100%;">
<div class="search-input--icon"></div>
<input type="search"
style="width:100%;"
spellcheck="false"
:placeholder="$root.getLz('term.search') + '...'"
@change="searchPodcasts();librarySearch()"
v-model="search.term" class="search-input">
</div>
</div>
<div v-if="search.term == ''">
<div class="podcast-list-header" v-if="ciderPodcasts.length != 0">
{{$root.getLz('podcast.followedOnCider')}}
</div>
<div class="podcast-list-header" v-if="podcasts.length != 0">
{{$root.getLz('podcast.subscribedOnItunes')}}
</div>
<podcast-tab :isselected="podcastSelected.id == podcast.id" @click.native="selectPodcast(podcast)" v-for="podcast in podcasts" :item="podcast"></podcast-tab>
</div>
<div v-else>
<div class="podcast-list-header" v-if="podcasts.length != 0">
{{$root.getLz('term.library')}}
</div>
<podcast-tab :isselected="podcastSelected.id == podcast.id" @click.native="selectPodcast(podcast)" v-for="podcast in search.resultsLibrary" :item="podcast"></podcast-tab>
<div class="podcast-list-header" v-if="podcasts.length != 0">
{{$root.getLz('podcast.itunesStore')}}
</div>
<podcast-tab :isselected="podcastSelected.id == podcast.id" @click.native="selectPodcast(podcast)" v-for="podcast in search.results" :item="podcast"></podcast-tab>
</div>
</div>
<div class="episodes-list">
<div v-if="podcastSelected.id != -1" class="episodes-inline-info">
<div class="row">
<div class="col-auto flex-center">
<div class="podcast-artwork">
<mediaitem-artwork shadow="large" :url="podcastSelected.attributes.artwork.url" size="300"></mediaitem-artwork>
</div>
</div>
<div class="col podcast-show-info">
<h1>{{ podcastSelected.attributes.name }}</h1>
<small>{{ podcastSelected.attributes.releaseFrequency }}</small>
<small>Created: {{ new Date(podcastSelected.attributes.createdDate).toLocaleDateString() }}</small>
</div>
</div>
<div class="well podcast-show-description">{{ podcastSelected.attributes.description.standard }}</div>
<div class="row" v-if="!isSubscribed(podcastSelected.id)">
<div class="col">
<button class="md-btn md-btn-block">{{$root.getLz('podcast.followOnCider')}}</button>
</div>
<div class="col">
<button class="md-btn md-btn-block">{{$root.getLz('podcast.subscribeOnItunes')}}</button>
</div>
</div>
<h3>{{$root.getLz('podcast.episodes')}}</h3>
</div>
<div v-if="this.search.results.length == 0 && podcastSelected.id == -1" class="podcast-no-search-results">
<h3>{{$root.getLz('error.noResults')}}</h3>
<p>{{$root.getLz('error.noResults.description')}}</p>
</div>
<podcast-episode :isselected="selected.id == episode.id" @dblclick.native="playEpisode(episode)" @click.native="selectEpisode(episode)" :item="episode"
v-for="episode in episodes"></podcast-episode>
</div>
<transition name="wpfade">
<div class="podcasts-details" v-if="selected.id != -1">
<div class="podcasts-details-header">
<button class="close-btn" @click="selected.id = -1"></button>
</div>
<div class="podcast-artwork">
<mediaitem-artwork shadow="large" :url="selected.attributes.artwork.url" size="300"></mediaitem-artwork>
</div>
<h3 class="podcast-header">{{ selected.attributes.name }}</h3>
<button @click="playEpisode(selected)" class="md-btn podcast-play-btn">{{$root.getLz('podcast.playEpisode')}}</button>
<div class="podcast-genre">
{{ selected.attributes.genreNames[0] }}
</div>
<div class="podcast-metainfo">
{{ msToMinSec(selected.attributes.durationInMilliseconds) }} • {{ new Date(selected.attributes.releaseDateTime).toLocaleString() }}
</div>
<div class="well podcast-description" v-if="selected.attributes.description.standard">{{ selected.attributes.description.standard }}</div>
<div class="row">
<div class="col">
<button class="md-btn md-btn-block meta-btn" @click="openUrl(selected.attributes.websiteUrl)">{{$root.getLz('podcast.website')}}</button>
</div>
<div class="col">
<button class="md-btn md-btn-block meta-btn">{{$root.getLz('action.share')}}</button>
</div>
</div>
</div>
</transition>
</div>
</script>
<script type="text/x-template" id="podcast-tab">
<div class="cd-mediaitem-list-item list-flat" :class="{'mediaitem-selected': isselected}">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork.url"
size="50"
type="podcast"></mediaitem-artwork>
</div>
<div class="info-rect">
<div class="title text-overflow-elipsis">
{{ item.attributes.name }}
</div>
</div>
</div>
</script>
<script type="text/x-template" id="podcast-episode">
<div class="cd-mediaitem-list-item list-flat" :class="{'mediaitem-selected': isselected}">
<div class="info-rect" :style="{'padding-left':'16px'}">
<div class="title text-overflow-elipsis">
{{ item.attributes.name }}
</div>
<div class="subtitle text-overflow-elipsis">
{{ item.attributes.description.standard }}
</div>
<div class="subtitle text-overflow-elipsis">
{{ msToMinSec(item.attributes.durationInMilliseconds) }} • {{ new Date(item.attributes.releaseDateTime).toLocaleString() }}
</div>
</div>
</div>
</script>
<script>
Vue.component('podcast-episode', {
template: '#podcast-episode',
props: ['item', 'isselected'],
methods: {
msToMinSec(ms) {
var minutes = Math.floor(ms / 60000);
var seconds = ((ms % 60000) / 1000).toFixed(0);
return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
}
}
});
Vue.component('podcast-tab', {
template: '#podcast-tab',
props: ['item', 'isselected'],
methods: {}
});
Vue.component('apple-podcasts', {
template: '#apple-podcasts',
data: function () {
return {
ciderPodcasts: [],
podcasts: [],
episodes: [],
search: {
term: "",
loading: false,
results: [],
resultsLibrary: [],
next: ""
},
podcastSelected: {
id: -1
},
selected: {
id: -1
}
}
},
async mounted() {
let podcastShow = await app.mk.api.v3.podcasts(`/v1/me/library/podcasts?include=episodes`)
this.podcasts = podcastShow.data.data
if (podcastShow.data.next) {
await this.getNext(podcastShow.data.next)
}
// this.episodes = podcastShow.data.data[0].relationships.episodes.data
},
methods: {
searchTriggerVis(visible) {
},
librarySearch() {
this.search.resultsLibrary = []
if (this.search.term.length > 2) {
this.search.resultsLibrary = this.podcasts.filter(podcast => podcast.attributes.name.toLowerCase().includes(this.search.term.toLowerCase()))
}
},
isSubscribed(id) {
return this.podcasts.filter(podcast => podcast.id == id).length > 0
},
searchPodcasts() {
let self = this
if(this.search.term == "") {
return
}
app.mk.api.v3.podcasts("/v1/catalog/us/search", {term: this.search.term, types: ["podcasts"], limit: 25}).then(response => {
console.log(response)
self.search.results = response.data.results.podcasts.data
})
},
openUrl(url) {
window.open(url)
},
msToMinSec(ms) {
var minutes = Math.floor(ms / 60000);
var seconds = ((ms % 60000) / 1000).toFixed(0);
return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
},
playEpisode(episode) {
app.mk.setQueue({'episode': episode.id}).then(() => {app.mk.play()})
},
selectPodcast(podcast) {
this.podcastSelected = podcast
this.getEpisodes(podcast)
},
selectEpisode(episode) {
this.selected = Clone(episode)
},
async getEpisodes(podcast) {
this.episodes = []
let eps = await app.mk.api.v3.podcasts(`/v1/catalog/${app.mk.storefrontId}/podcasts/${podcast.id}?include=episodes`)
eps.data.data[0].relationships.episodes.data.forEach(ep => {
this.episodes.push(ep)
})
if (eps.data.data[0].relationships.episodes.next) {
await this.getNextEpisodes(eps.data.data[0].relationships.episodes.next, podcast.id)
}
},
async getNextEpisodes(next, podcastId) {
let podcastShow = await app.mk.api.v3.podcasts(next)
if(podcastId != this.podcastSelected.id) {
return
}
podcastShow.data.data.forEach(ep => {
this.episodes.push(ep)
})
if (podcastShow.data.next) {
await this.getNextEpisodes(podcastShow.data.next, podcastId)
}
},
async getNext(next) {
let podcastShow = await app.mk.api.v3.podcasts(next)
this.podcasts = this.podcasts.concat(podcastShow.data.data)
if (podcastShow.data.next) {
await this.getNext(podcastShow.data.next)
}
}
}
});
</script>

View file

@ -33,7 +33,7 @@
<h3>{{ data.views["latest-releases"].attributes.title ?? ""}}</h3>
</div>
<div class="col-auto flex-center" v-if="data.views['latest-releases'].data.length >= 10">
<button class="cd-btn-seeall" @click="app.showRecordLabelView(data.id, data.attributes.name + ' - Latest Releases', 'latest-releases')">See All</button>
<button class="cd-btn-seeall" @click="app.showRecordLabelView(data.id, data.attributes.name + ' - Latest Releases', 'latest-releases')">{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<mediaitem-square :item="item" v-for="item in data.views['latest-releases'].data">
@ -45,7 +45,7 @@
<h3>{{ data.views["top-releases"].attributes.title ?? ""}}</h3>
</div>
<div class="col-auto flex-center" v-if="data.views['top-releases'].data.length >= 10">
<button class="cd-btn-seeall" @click="app.showRecordLabelView(data.id, data.attributes.name + ' - Top Releases', 'top-releases')">See All</button>
<button class="cd-btn-seeall" @click="app.showRecordLabelView(data.id, data.attributes.name + ' - Top Releases', 'top-releases')">{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<mediaitem-square :item="item" v-for="item in data.views['top-releases'].data">
@ -57,7 +57,7 @@
<h3>Playlists</h3>
</div>
<div class="col-auto flex-center" v-if="data.relationships.playlists.data.length >= 5">
<button class="cd-btn-seeall" @click="app.showCollection(data.relationships.playlists, data.attributes.name + ' - Playlists', 'curator')">See All</button>
<button class="cd-btn-seeall" @click="app.showCollection(data.relationships.playlists, data.attributes.name + ' - Playlists', 'curator')">{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<mediaitem-square :item="item" v-for="item in data.relationships.playlists.data.limit(5)">

View file

@ -1,86 +1,103 @@
<script type="text/x-template" id="cider-search">
<div class="content-inner">
<div class="row">
<div class="col-sm" style="width: auto;" v-if="getTopResult()">
<template>
<h3>Top Result</h3>
<mediaitem-square :item="getTopResult()"></mediaitem-square>
</template>
</div>
<div v-else style="text-align: center">
<h3>No Results</h3>
<p>Try a new search.</p>
</div>
<div class="col" v-if="search.results.song">
<div class="row">
<div class="col">
<h3>Songs</h3>
<div v-if="search != null && search != [] && search.term != ''">
<div class="row">
<div class="col-sm" style="width: auto;" v-if="getTopResult()">
<template>
<h3>Top Result</h3>
<mediaitem-square :item="getTopResult()"></mediaitem-square>
</template>
</div>
<div v-else style="text-align: center">
<h3>{{app.getLz('error.noResults')}}</h3>
<p>{{app.getLz('error.noResults.description')}}</p>
</div>
<div class="col" v-if="search.results.song">
<div class="row">
<div class="col">
<h3>Songs</h3>
</div>
<div class="col-auto flex-center"
@click="app.showSearchView(app.search.term, 'song', app.friendlyTypes('song'))"
v-if="search.results.song.data.length >= 12">
<button class="cd-btn-seeall">{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<div class="col-auto flex-center"
@click="app.showSearchView(app.search.term, 'song', app.friendlyTypes('song'))"
v-if="search.results.song.data.length >= 6">
<button class="cd-btn-seeall">See All</button>
<div class="mediaitem-list-item__grid">
<div class="grid-body">
<mediaitem-list-item :item="item" :index="index"
v-for="(item, index) in search.results.song.data.limit(12)"></mediaitem-list-item>
</div>
</div>
</div>
<div>
<mediaitem-list-item :item="item" :index="index"
v-for="(item, index) in search.results.song.data.limit(6)"></mediaitem-list-item>
</div>
</div>
</div>
<template v-if="search.results['meta']">
<template
v-for="section in search.results.meta.results.order" v-if="section != 'song' && section != 'top'">
<template v-if="search.results['meta'] != null">
<template
v-for="section in search.results.meta.results.order" v-if="section != 'song' && section != 'top'">
<div class="row">
<div class="col">
<h3>{{ app.friendlyTypes(section) }}</h3>
</div>
<div class="col-auto flex-center" v-if="search.results[section].data.length >= 10">
<button class="cd-btn-seeall"
@click="app.showSearchView(app.search.term, section, app.friendlyTypes(section))">See
All
</button>
</div>
</div>
<template v-if="!app.friendlyTypes(section).includes('Video')">
<mediaitem-scroller-horizontal-large
:items="search.results[section].data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
<template v-else>
<mediaitem-scroller-horizontal-mvview
:items="search.results[section].data.limit(10)"></mediaitem-scroller-horizontal-mvview>
</template>
</template>
</template>
<template v-if="search.resultsSocial.playlist">
<div class="row">
<div class="col">
<h3>{{ app.friendlyTypes(section) }}</h3>
<h3>Shared Playlists</h3>
</div>
<div class="col-auto flex-center" v-if="search.results[section].data.length >= 10">
<div class="col-auto flex-center" v-if="search.resultsSocial.playlist.data.length >= 10">
<button class="cd-btn-seeall"
@click="app.showSearchView(app.search.term, section, app.friendlyTypes(section))">See
All
@click="app.showCollection(search.resultsSocial.playlist, 'Shared Playlists', 'default')">{{app.getLz('term.seeAll')}}
</button>
</div>
</div>
<template v-if="!app.friendlyTypes(section).includes('Video')">
<mediaitem-scroller-horizontal-large
:items="search.results[section].data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
<template v-else>
<mediaitem-scroller-horizontal-mvview
:items="search.results[section].data.limit(10)"></mediaitem-scroller-horizontal-mvview>
</template>
<mediaitem-scroller-horizontal-large
:items="search.resultsSocial.playlist.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
</template>
<template v-if="search.resultsSocial.playlist">
<div class="row">
<div class="col">
<h3>Shared Playlists</h3>
<template v-if="search.resultsSocial.profile">
<div class="row">
<div class="col">
<h3>People</h3>
</div>
<div class="col-auto flex-center" v-if="search.resultsSocial.profile.data.length >= 10">
<button class="cd-btn-seeall"
@click="app.showCollection(search.resultsSocial.profile, 'People', 'default')">{{app.getLz('term.seeAll')}}
</button>
</div>
</div>
<div class="col-auto flex-center" v-if="search.resultsSocial.playlist.data.data.length >= 10">
<button class="cd-btn-seeall"
@click="app.showCollection(search.resultsSocial.playlist.data, 'Shared Playlists', 'default')">See All
</button>
<mediaitem-scroller-horizontal-large
:items="search.resultsSocial.profile.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
</div>
<div v-else>
<div v-if="categoriesReady || getCategories()">
<div>
<div class="col" v-if="categoriesView != null && categoriesView != [] && categoriesView[0].attributes != null && categoriesView[0].attributes.title != null">
<h3>{{categoriesView[0].attributes.title.stringForDisplay ?? ""}}</h3>
</div>
<mediaitem-square :kind="'385'" size="600"
:item="item ? (item.attributes.kind ? item : ((item.relationships && item.relationships.contents ) ? item.relationships.contents.data[0] : item)) : []"
:imagesize="800"
v-for="item in categoriesView[1].relationships.contents.data.filter(item => item.type != 'editorial-items')">
</div>
</div>
<mediaitem-scroller-horizontal-large
:items="search.resultsSocial.playlist.data.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
<template v-if="search.resultsSocial.profile">
<div class="row">
<div class="col">
<h3>People</h3>
</div>
<div class="col-auto flex-center" v-if="search.resultsSocial.profile.data.data.length >= 10">
<button class="cd-btn-seeall"
@click="app.showCollection(search.resultsSocial.profile.data, 'People', 'default')">See All
</button>
</div>
</div>
<mediaitem-scroller-horizontal-large
:items="search.resultsSocial.profile.data.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
</div>
</div>
</div>
</script>
@ -90,16 +107,26 @@
props: ['search'],
data: function () {
return {
app: this.$root
app: this.$root,
categoriesView: [],
categoriesReady: false,
}
},
methods: {
getTopResult() {
try {
return this.search.results[this.search.results.meta.results.order[0]]["data"][0]
} catch( error ) {
} catch (error) {
return false
}
},
async getCategories() {
if (this.categoriesView != [] && this.categoriesView.length > 0) { this.categoriesReady = true; return await true; } else {
let response = await this.app.mk.api.v3.music(`/v1/recommendations/${this.app.mk.storefrontId}?timezone=${encodeURIComponent(this.app.formatTimezoneOffset())}&name=search-landing&platform=web&extend=editorialArtwork&art%5Burl%5D=f%2Cc&types=editorial-items%2Capple-curators%2Cactivities`);
this.categoriesView = response.data.data;
this.categoriesReady = true;
return await true;
}
}
}
})

File diff suppressed because it is too large Load diff

View file

@ -5,5 +5,8 @@
{{ $store.state.test }}
<div class="spinner"></div>
<button class="md-btn">Cider Button</button>
<div style="position: relative;width: 300px;height: 300px;">
<artwork-material url="https://is3-ssl.mzstatic.com/image/thumb/Music126/v4/13/41/13/1341133b-560f-1aee-461f-c4b32ec049b4/cover.jpg/{w}x{h}bb.jpg"></artwork-material>
</div>
</div>
</template>